1) switch реализован совершенно непонятно. От использования его без break и размещения default не в конце лучше воздержаться.
switch (1) {
case 2: System.out.print("y");
case 3: System.out.print("икраткое");
default: System.out.print("x");
} //вполне ожидаемый х
switch (1) {
default: System.out.print("x");
case 2: System.out.print("y");
case 3: System.out.print("икраткое");
} //внезапное xyикраткое
switch (1) {
case 2: System.out.print("y");
default: System.out.print("x");
case 3: System.out.print("икраткое");
} // печальное xикраткое
Написать case с переменными невозможно – все варианты должны быть разрешены в момент компиляции.
2) Куча запутанных преобразований типов. Но всех переплюнуло уменьшение разрядности – неявное не работает, а явное наоборот, вместо того, что бы выкинуть ошибку сделает большое отрицательное число
int i = Short.MAX_VALUE - 10;
short s = i; //Ошибка компилятора: incompatible types: possible lossy conversion from int to short
short s = (short) (Short.MAX_VALUE + 1);
System.out.print("s=" + s + "; MAX_VALUE+1=" + (Short.MAX_VALUE+1));//s=-32768; MAX_VALUE+1=32768
3) Еще преобразование типов. Приведенный ниже пример конечно не компилируется
short s1 = 0;
short s2 = 1;
short s3 = s1 + s2;
//Error:(6, 15) java: incompatible types: possible lossy conversion from int to short
потому что внезапно
System.out.println(((Object)(s1+s2)).getClass().getName());
//возвращает java.lang.Integer
4) Присваивание это еще и функция, которая возвращает правую часть. Не ошибись в if, не пропусти второе =
boolean b = false;
if (b = true) System.out.print("На самом деле я уже true, посмотри сам " + b);//На самом деле я уже true, посмотри сам true
5) Сравнение строк и StringBuilder – отдельная боль
вот, например, строки одинаковые, а конструкторы вызваны были разные
String s1 = "Я томат";
String s2 = new String("Я томат");
if (s1 == s2)
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' равны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ равны");
Рано печалилсь, для StringBuilder это не работает уже из коробки :)
StringBuilder s1 = new StringBuilder("Я томат");
StringBuilder s2 = new StringBuilder("Я томат");
if (s1 == s2)
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' равны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ равны");
А вот ударили монтировкой по голове – шнурки развязались
StringBuilder s1 = new StringBuilder("Я томат");
StringBuilder s2 = s1;
s2.append(" очень рад");
if (s1 == s2)
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' равны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ равны");
// Мы случайно поменяли и первую строку: По мнению джавы строки 'Я томат очень рад' и 'Я томат очень рад' равны.
В общем сравнение со строками не совместимо, надо использовать equals
String s1 = "Я томат";
String s2 = " Я томат".trim();
if (s1 == s2)
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' равны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ равны");
//С томатами явная проблема: По мнению джавы строки 'Я томат' и 'Я томат' НЕ равны
if (s1.equals(s2))
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' эквальны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ эквальны");
//Хоть с эквальностью проблем нету)): По мнению джавы строки 'Я томат' и 'Я томат' эквальны
Но wtf, если вместо строк случайно оказался StringBuilder
StringBuilder s1 = new StringBuilder("Я томат");
StringBuilder s2 = new StringBuilder("Я томат");
if (s1.equals(s2))
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' эквальны");
else
System.out.print("По мнению джавы строки '" + s1 + "' и '" + s2 + "' НЕ эквальны");
//По мнению джавы строки 'Я томат' и 'Я томат' НЕ эквальны
оказывается разрабочтики метод не реализовали (facepalm)
6) Унарный инкремент и декремент давно пора бы убрать. Польза только в синтаксисе for-цикла, а непонятности вносит много. Вот, например что выведет?
int i = 0;
while (i <= 3) {
i = i++;
System.out.println(i);
}
7) Реализация substring по-моему чудовищная, хотя с этих можно смириться.
8) Инициализация переменных. Она зависит от контекста
public class Main {
float gf;
int gi;
public static void main(String[] xxx) {
int li;
float lf;
Main c = new Main();
System.out.println("Global float default: " + c.gf + "; Global integer default " + c.gi);
// Global float default: 0.0; Global integer default 0
System.out.println("Local float default: " + lf + "; Local integer default " + li);
// не компилируется, т.к. переменные не инициализированы
}
}
9) Игра Угадай-ка
public class Simple {
public Simple() {/*Угадай, кто из нас конструктор*/}
public void Simple() {/*Угадай, кто из нас конструктор*/}
public static void main(String[] args) {}
}
10) Дурацкие типы для литералов
long x = 123;//Компилируется
System.out.println(Integer.MAX_VALUE);//Выводит 2147483647
long x = 2147483999; //не компилируется Error:(6, 10) java: integer number too large: 2147483999
long x = 2147483999L;//Должно быть так
все потому что литерал по-умолчанию имеет тип int
11) “Чудесно” работающая процедура поиска по несортированным массивам
int numbers[] = new int[] {3,2,1};
System.out.println(Arrays.binarySearch(numbers, 2)); //правильное 1
System.out.println(Arrays.binarySearch(numbers, 3)); //странное -4
12) Protected access – вообще отдельная песня. Кажется, что понять скомпилируется код или нет можно уже только постфактум. Примера не привожу ибо длинно и непонятно.
13) Использование пустых ссылок не всегда приводят к ошибкам:
public class Main {
static int x;
public static void main(String[] xxx) {
Main m = new Main();
System.out.println("Static var = " + m.x);//0, инициализирована которым по-умолчанию
m = null;
System.out.println("Static var via null link = " + m.x);//и тут 0
}
14) By reference or by value? Или все таки By reference? Вроде как заявляется, что параметры передаются By Value, т.е. изменение параметра внутри функции не влияет на переменную, которая была между скобками в вызывающем коде. Никаких спец конструкций, вроде NOCOPY или by reference в Java нет.
Вот небольшой примерчик:
static void byValueOrByReference(StringBuilder s1, String s2, String s3, String[] many_s) {// а попробуй угадай, что тут по ссылке, а что по значению :)?
s1.append("но");
s2 = s1 + "но";
s3 = s2 + "но";
for(String s: many_s) {
s = s + "нейшество";
}
}
public static void main(String[] xxx) {
StringBuilder s1 = new StringBuilder("Гов");
String s2 = "гов";
String s3 = new String("New гов");
String[] sarr = {"гов", "нище"};
byValueOrByReference(s1, s2, s3, sarr);
System.out.println(s1);//Говно
System.out.println(s2);//гов
System.out.println(s3);//New гов -- не смотря на то, что они обещали обычный объект
System.out.println(sarr[0] + "-" + sarr[1]);//гов-нище
}
Допустим объяснение, что примитивы должны вести себя как-будто их передают по значению, а объекты по ссылке. Но что же со строками, которые объявлены с new и просто? Они по разному вели себя в сравнении, а тут опять выдают себя за примитивы? Непоследовательно как-то…
15) Цепочки периодов работаю немного по разному
public static void main(String[] xxx) {
LocalDate d = LocalDate.of(2017, 1, 1);
Period p1 = Period.of(0,0,0);
p1 = p1.plusYears(1).plusMonths(1).plusDays(1);
Period p2 = Period.ofYears(1).ofMonths(1).ofDays(1);
System.out.println(d.plus(p1));//2018-02-02 - как и просили добавился день, месяц и год
System.out.println(d.plus(p2));//2017-01-02 полнейшая печаль
}
В первом случае создаем пусто период и пользуемся функциями plus* - все работает как часы. А вот во втором, более коротком варианте добавляется только последний вызов. Лучше бы исключение кидали.
16) C StringBuilder.substring нельзя делать цепочки вызовов
StringBuilder s = new StringBuilder("опаньки");
System.out.println(s.insert(0,"ж").substring(0,2).append("пища"));// не компилируется
System.out.println(s.insert(0,"ж").append("пища"));// компилируется
17) Волшебная перезагрузка статических методов, вызов которых работает совсем не так, как в перегружегнных обычных.
В примере ниже: из дочернего объекта вызываем процедуру печати. В первом случае перегруженную, во втором родную.
Процедура печати почти одинаковая – дергает статический и нестатический метод из ?своего? класса. Смотрим на результаты
class Parent {
static String stat() {return "СТАТИЧЕСКИЙ Parent";}
String nonStat(){return "Parent";}
void printInherited(){
System.out.println("Вызов статической процедуры из Parent вызвал: " + stat());
System.out.println("Вызов НЕстатической процедуры из Parent вызвал: " + nonStat());
}
}
public class Main extends Parent{
static String stat() {return "СТАТИЧЕСКИЙ Child";}
String nonStat(){return "Child";}
void printLocal(){
System.out.println("Вызов статической процедуры из Child вызвал: " + stat());
System.out.println("Вызов нестатической процедуры из Child вызвал: " + nonStat());
}
public static void main(String... args) {
Main m = new Main();
System.out.println("Вызываем процедуру печати из родительского класса. Она наследовалась и не перегружалась");
m.printInherited();
System.out.println("Вызываем процедуру печати из дочернего класса");
m.printLocal();
}
}
Вызываем процедуру печати из родительского класса. Она наследовалась и не перегружалась
Вызов статической процедуры из Parent вызвал: СТАТИЧЕСКИЙ Parent
Вызов нестатической процедуры из Parent вызвал: Child
Вызываем процедуру печати из дочернего класса
Вызов статической процедуры из Child вызвал: СТАТИЧЕСКИЙ Child
Вызов нестатической процедуры из Child вызвал: Child
Нижняя часть с вызовом для дочернего класса прошла без неожиданностей – обе процедуры дернулись из самого дочернего класса. В верхней части волшебство – поведение то разное. Статический метод вызвался из одного места, а обычный из другого…
18) Немного об абстракных классах и станном компиляторе. Сначала о странном компиляторе: недостижимый код в if(false) и while(false) по-разному недостижимый
Integer i = 1;
Short x = 1;
if(b == x) {System.out.println("Equal");} //Не компилируется, разные типы же
while(false) {System.out.println("False");} //Не компилируется, недостижимый код
if(false){System.out.println("False");} //Компилируется ))
Компилятор на столько умен, что даже запрещает сравнивать переменные разных типов
А что-же он будет делать, когда мы пытаемся объявить и использовать переменную абстрактного класса? (вопрос подчерпнут из тестов для подготовки к экзаменам)
public abstract class Main{
abstract void calculate();
public static void main(String[] args) {
System.out.println("calculating");
Main x = null;
x.calculate();
}
}
Думаете компилятор ругается на создание переменной абстрактного класса (это наверное слишком полезная функциональность)? Хрен, результат прогона такой
calculating
Exception in thread "main" java.lang.NullPointerException
at Main.main(Main.java:10)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
19) Добавим немного полиморфизма :). Вот переменная и метод, возвращающий эту переменную:
public abstract class Main{
public static void main(String[] args) {
A o = new B( );
System.out.println(o.m1( ) );//20 -- метод вызывается из класса справа от равно и возвращает переменную из класса справа от равно
System.out.println(o.i ); //10 -- переменная возвращается из класса слева от равно
}
}
class A { int i = 10; int m1( ) { return i; } }
class B extends A { int i = 20; int m1() { return i; } }
20) null…
char[] myCharArr = {'x', 'y'};
String newStr = null;
for(char ch : myCharArr){
newStr = newStr + ch;
System.out.println(newStr);
}
Выводит
nullx
nullxy
StringBuilder sb = new StringBuilder("12345");
CharSequence cs = null;
System.out.println(sb.append(cs));//пишет 12345null
no comments
21) Инициализация примитивов и типов-оберток. Говорить о последовательности разработчиков вообще не приходится - тут работает, тут перестало, а тут мы рыбу заворачивали. Ну а инициализация типов-оберток - вообще страшно написана.
//Почти правильная и естественная инициализация. Целочисленные типы без точки, с плавающей точкой через 1.0
double d = 1.0;
float f = 1.0; // из этого блока не работает только это волшебным образом
float f11 = 1.0f; // вот так работает
long l = 1;
int i = 1;
short s = 1;
byte b = 1;
char c = 1;
//неправильная инициализация
double d2 = 1; //это работает
int i2 = 1.0; // а это уже нет
//Инициализация объектов. Конструктор? Какой конструктор?
//И перестал работать Long внезапно
Double d3 = 1.0;
Float f3 = 1.0; //не осилил преобразовать константу
Float f31 = 1.0f;
Long l3 = 1; // Полнейшее рукалицо -- не преобразовал целочисленный в целочисленный
Long l31 = 1L; // Так надо
Integer i3 = 1;
Short s3 = 1;
Byte b3 = 1;
Character c3 = 1;
//неправильная инициализация -- сломалось уже всё
Double d4 = 1; //не работает
Integer i4 = 1.0; //не работает
//Инициализация враперов через конструкторы
//Как всегда непоследовательно -- на этот раз float работает, а вот типы ниже int нет
Double d5 = new Double(1.0);
Float f5 = new Float(1.0);//а про этот в этот раз не забыли -- он работает
Float f51 = new Float(1.0f);
Long l5 = new Long(1);
Integer i5 = new Integer(1);
Short s5 = new Short(1);//забыли написать конструкторы
Short s51 = new Short((short) 1);//надо так
Byte b5 = new Byte(1);//забыли написать конструкторы
Byte b51 = new Byte((byte) 1);//надо так
Character c5 = new Character(1);//забыли написать конструкторы
Character c51 = new Character((char) 1);//надо так
Character c52 = new Character('x');//или так
To be continued…