Java - общие Gotchas
В том же духе других платформ было логично следить за этим вопросом: какие распространенные неочевидные ошибки в Java? Кажется, что они должны работать, но не надо.
Я не буду давать рекомендации относительно того, как структурировать ответы, или что "слишком легко" можно считать "добычей", поскольку для этого важно голосование.
См. также:
Ответы
Ответ 1
Сравнение равенства объектов с использованием ==
вместо .equals()
- которое ведет себя совершенно по-разному для примитивов.
Эта информация гарантирует, что новички будут путаться при "foo" == "foo"
, но new String("foo") != new String("foo")
.
Ответ 2
"a,b,c,d,,,".split(",").length
возвращает 4, не 7, как вы могли бы (и я конечно же) ожидал. split
игнорирует все возвращаемые возвращаемые строки. Это означает:
",,,a,b,c,d".split(",").length
возвращает 7! Чтобы получить то, что я думаю о "наименее удивительном" поведении, вам нужно сделать что-то весьма удивительное:
"a,b,c,d,,,".split(",",-1).length
чтобы получить 7.
Ответ 3
Переопределить equals(), но не hashCode()
Он может иметь действительно неожиданные результаты при использовании карт, наборов или списков.
Ответ 4
Я думаю, что очень подлый метод String.substring
. Это повторно использует тот же базовый массив char[]
, что и исходная строка с разными offset
и length
.
Это может привести к очень труднодоступным проблемам памяти. Например, вы можете обрабатывать чрезвычайно большие файлы (возможно, XML
) для нескольких маленьких бит. Если вы преобразовали весь файл в String
(вместо того, чтобы использовать Reader
для "ходьбы" над файлом) и используйте substring
, чтобы захватить нужные вам биты, вы по-прежнему несете полный размер файла char[]
массив за кулисами. Я видел это много раз, и это может быть очень сложно определить.
На самом деле это прекрасный пример того, почему интерфейс никогда не может быть полностью отделен от реализации. И это было прекрасное введение (для меня) несколько лет назад относительно того, почему вы должны быть подозрительно к качеству стороннего кода.
Ответ 5
SimpleDateFormat не является потокобезопасным.
Ответ 6
Есть два, которые меня очень раздражают.
Дата/Календарь
Во-первых, классы Java Date и Calendar серьезно перепутаны. Я знаю, что есть предложения по их исправлению, я просто надеюсь, что они преуспеют.
Calendar.get(Calendar.DAY_OF_MONTH) - 1-й вариант
Calendar.get(Calendar.MONTH) - 0
Авто-бокс, предотвращающий мышление
Другой - Integer vs int (это относится к любой примитивной версии объекта). Это, в частности, раздражение, вызванное неправильным пониманием Integer как отличного от int (поскольку вы можете относиться к ним столько же времени из-за автоматического бокса).
int x = 5;
int y = 5;
Integer z = new Integer(5);
Integer t = new Integer(5);
System.out.println(5 == x); // Prints true
System.out.println(x == y); // Prints true
System.out.println(x == z); // Prints true (auto-boxing can be so nice)
System.out.println(5 == z); // Prints true
System.out.println(z == t); // Prints SOMETHING
Так как z и t - объекты, даже если они имеют одинаковое значение, они (скорее всего) разные объекты. Что вы на самом деле имеете в виду:
System.out.println(z.equals(t)); // Prints true
Это может быть болью для отслеживания. Вы отлаживаете что-то, все выглядит хорошо, и вы, наконец, в конечном итоге обнаруживаете, что ваша проблема в том, что 5!= 5, когда оба являются объектами.
Возможность сказать
List<Integer> stuff = new ArrayList<Integer>();
stuff.add(5);
так приятно. Это сделало Java гораздо более пригодным для использования, чтобы не перекладывать все эти "новые целые числа (5)" и "((Integer) list.get(3)). IntValue()" по всему месту. Но эти преимущества приходят с этой выгодой.
Ответ 7
Попробуйте прочитать Java Puzzlers, который полон страшных вещей, даже если большая часть из них не является чем-то другим, с которым вы сталкиваетесь каждый день. Но это сильно разрушит ваше доверие к языку.
Ответ 8
List<Integer> list = new java.util.ArrayList<Integer>();
list.add(1);
list.remove(1); // throws...
Старые API не были разработаны с учетом бокса, поэтому перегрузка с примитивами и объектами.
Ответ 9
Этот я только что наткнулся:
double[] aList = new double[400];
List l = Arrays.asList(aList);
//do intense stuff with l
Кто-нибудь видит проблему?
Что происходит, Arrays.asList()
ожидает массив типов объектов (например, Double []). Было бы неплохо, если бы он просто выбросил ошибку для предыдущего ocde. Однако asList()
также может принимать такие аргументы:
Arrays.asList(1, 9, 4, 4, 20);
Итак, что делает код, создайте List
с одним элементом - a double[]
.
Я должен был подумать, когда понадобилось 0ms для сортировки массива элементов массива 750000...
Ответ 10
Поплавки
Я не знаю много раз, я видел
floata == floatb
где "правильный" тест должен быть
Math.abs(floata - floatb) < 0.001
Я действительно хочу, чтобы BigDecimal с литеральным синтаксисом был десятичным типом по умолчанию...
Ответ 11
этот меня несколько раз сбивал с толку, и я слышал немало опытных разработчиков java, которые тратили много времени.
ClassNotFoundException --- вы знаете, что класс находится в пути к классам, но вы НЕ уверены, почему класс НЕ загружается.
Собственно, этот класс имеет статический блок. В статическом блоке было исключение, и кто-то съел исключение. они НЕ должны. Они должны бросать ExceptionInInitializerError. Итак, всегда ищите статические блоки, чтобы путешествовать. Это также помогает перемещать любой код в статических блоках, чтобы перейти в статические методы, чтобы отладка метода была намного проще с отладчиком.
Ответ 12
Не очень специфичен для Java, поскольку многие (но не все) языки реализуют его таким образом, но оператор %
не является истинным модульным оператором, так как он работает с отрицательными числами. Это делает его оператором остатка и может привести к неожиданностям, если вы не знаете об этом.
Появится следующий код: "четный" или "нечетный", но это не так.
public static void main(String[] args)
{
String a = null;
int n = "number".hashCode();
switch( n % 2 ) {
case 0:
a = "even";
break;
case 1:
a = "odd";
break;
}
System.out.println( a );
}
Проблема заключается в том, что хеш-код для "числа" отрицательный, поэтому операция n % 2
в коммутаторе также отрицательна. Поскольку в коммутаторе нет случая для решения отрицательного результата, переменная a
никогда не будет установлена. Программа выводит null
.
Убедитесь, что вы знаете, как работает оператор %
с отрицательными номерами, независимо от того, на каком языке вы работаете.
Ответ 13
Я думаю, что я получил большую версию, которая всегда меня преследовала бы, когда я был молодым программистом, был исключение параллельной модификации при удалении из массива, который вы повторяли:
List list = new ArrayList();
Iterator it = list.iterator();
while(it.hasNext()){
//some code that does some stuff
list.remove(0); //BOOM!
}
Ответ 14
Манипуляция компонентами Swing извне потока отправки событий может привести к ошибкам, которые чрезвычайно трудно найти. Это даже то, что мы (как опытные программисты с 3-мя соответствующими 6-летним опытом Java) часто забываем! Иногда эти ошибки прорываются после написания кода справа и рефакторинга небрежно после этого...
Смотрите tutorial, почему вы должны.
Ответ 15
Целочисленное деление
1/2 == 0 not 0.5
Ответ 16
Неизменяемые строки, что означает, что определенные методы не изменяют исходный объект, а вместо этого возвращают копию измененного объекта. Когда я начинал с Java, я все время забывал об этом и задавался вопросом, почему метод замены не работал на моем строковом объекте.
String text = "foobar";
text.replace("foo", "super");
System.out.print(text); // still prints "foobar" instead of "superbar"
Ответ 17
если у вас есть метод, который имеет то же имя, что и конструктор, но имеет тип возврата. Хотя этот метод выглядит как конструктор (для noob), он НЕ.
передача аргументов основному методу - для использования noobs требуется некоторое время.
. как аргумент classpath для выполнения программы в текущем каталоге.
Понимая, что имя массива строк не очевидно
hashCode и равно: многие разработчики java с более чем 5-летним опытом не совсем поняли.
Установить vs List
До JDK 6 у Java не было NavigableSets, чтобы вы могли легко выполнять итерацию с помощью Set and Map.
Ответ 18
Хэш по умолчанию не является детерминированным, поэтому, если он используется для объектов в HashMap, упорядочение записей на этой карте может меняться от прогона до запуска.
В качестве простой демонстрации следующая программа может давать разные результаты в зависимости от того, как она выполняется:
public static void main(String[] args) {
System.out.println(new Object().hashCode());
}
Сколько памяти выделено куче, или вы используете ее в отладчике, можно изменить результат.
Ответ 19
(un) Бокс и длинная/длинная путаница. Вопреки опыту pre-Java 5, вы можете получить исключение NullPointerException во второй строке ниже.
Long msec = getSleepMsec();
Thread.sleep(msec);
Если getSleepTime() возвращает нулевые, распаковывающие броски.
Ответ 20
Когда вы создаете duplicate
или slice
для ByteBuffer
, он не наследует значение свойства order
из родительского буфера, поэтому такой код не будет делать то, что вы ожидаете:
ByteBuffer buffer1 = ByteBuffer.allocate(8);
buffer1.order(ByteOrder.LITTLE_ENDIAN);
buffer1.putInt(2, 1234);
ByteBuffer buffer2 = buffer1.duplicate();
System.out.println(buffer2.getInt(2));
// Output is "-771489792", not "1234" as expected
Ответ 21
Использование подстановочного файла ?
generics.
Люди видят это и думают, что им нужно, например. используйте List<?>
, когда они хотят, чтобы List
они могли добавить что-нибудь, не останавливаясь, чтобы думать, что List<Object>
уже делает это. Затем они задаются вопросом, почему компилятор не позволит им использовать add()
, потому что List<?>
действительно означает "список определенного типа, который я не знаю", поэтому единственное, что вы можете сделать с этим List
, - это получить от него Object
экземпляры.
Ответ 22
Среди распространенных ловушек, хорошо известных, но по-прежнему кусающих иногда программистов, есть классический if (a = b)
, который встречается на всех языках, подобных C.
В Java он может работать, только если a и b являются логическими, конечно. Но я слишком часто вижу тесты новичков, такие как if (a == true)
(в то время как if (a)
короче, читабельнее и безопаснее...) и иногда пишу по ошибке if (a = true)
, задаваясь вопросом, почему тест не работает.
Для тех, кто этого не получает: последний оператор сначала назначает true
на a
, а затем выполняет тест, который всегда преуспевает!
-
Тот, кто кусает много новичков, и даже некоторые отвлеченные более опытные программисты (нашли его в нашем коде), if (str == "foo")
. Обратите внимание, что я всегда задавался вопросом, почему Sun переопределяет знак + для строк, но не ==, хотя бы для простых случаев (с учетом регистра).
Для новичков: ==
сравнивает ссылки, а не содержимое строк. Вы можете иметь две строки одного и того же содержимого, хранящиеся в разных объектах (разные ссылки), поэтому ==
будет ложным.
Простой пример:
final String F = "Foo";
String a = F;
String b = F;
assert a == b; // Works! They refer to the same object
String c = "F" + F.substring(1); // Still "Foo"
assert c.equals(a); // Works
assert c == a; // Fails
-
И я также видел if (a == b & c == d)
или что-то в этом роде. Он работает (любопытно), но мы потеряли логический ярлык оператора (не пытайтесь писать: if (r != null & r.isSomething())
!).
Для новичков: при оценке a && b
Java не оценивает b, если a является ложным. В a & b
Java оценивает обе части, а затем выполняет операцию; но вторая часть может потерпеть неудачу.
[EDIT] Хорошее предложение от J Coombs, я обновил свой ответ.
Ответ 23
Не унифицированная система типов противоречит идее объектной ориентации. Несмотря на то, что все не обязательно должно быть выделено в виде кучи, программисту должно быть разрешено обрабатывать примитивные типы, вызывая методы на них.
Реализация системного типа с типом стиранием ужасна и бросает большинство студентов, когда они узнают о генериках для первого в Java: почему нам все еще нужно прибегать к типу, если параметр типа уже предоставлен? Да, они обеспечивали обратную совместимость, но при довольно глупой стоимости.
Ответ 24
Идти первым, здесь я поймал сегодня. Это связано с путаницей Long
/Long
.
public void foo(Object obj) {
if (grass.isGreen()) {
Long id = grass.getId();
foo(id);
}
}
private void foo(long id) {
Lawn lawn = bar.getLawn(id);
if (lawn == null) {
throw new IllegalStateException("grass should be associated with a lawn");
}
}
Очевидно, имена были изменены для защиты невинных:)
Ответ 25
Еще один, который я хотел бы отметить, - это (слишком распространенный) диск для создания общих API. Использование хорошо продуманного общего кода в порядке. Разработка вашего проекта сложна. Очень сложно!
Посмотрите на функцию сортировки/фильтрации в новом Swing JTable
. Это полный кошмар. Очевидно, что вы, вероятно, захотите подключить фильтры в реальной жизни, но я счел невозможным это сделать, просто используя необработанную типизированную версию предоставленных классов.
Ответ 26
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("Asia/Hong_Kong")).getTime());
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("America/Jamaica")).getTime());
Вывод одинаков.
Ответ 27
ИМХО
1. Использование vector.add(Collection) вместо vector.addall(Collection). Первый добавляет объект коллекции к вектору, а второй добавляет содержимое коллекции.
2. Хотя это точно не связано с программированием, использование парсеров xml, которые поступают из нескольких источников, таких как xerces, jdom. Опираясь на разные парсеры и имея свои банки в пути класса, это кошмар.
Ответ 28
У меня была некоторая забавная отладка TreeSet один раз, поскольку я не знал об этой информации из API:
Обратите внимание, что порядок, поддерживаемый набором (будь то явный компаратор), должен быть согласован с равными, если он правильно реализует интерфейс Set. (См. Comparable или Comparator для точного определения соответствия с равными.) Это происходит потому, что интерфейс Set определяется с помощью операции equals, но экземпляр TreeSet выполняет все сопоставления ключей, используя метод compareTo (или compare), поэтому два ключи, которые по этому методу считаются равными, равны, с точки зрения множества. Поведение множества хорошо определено, даже если его упорядочение не соответствует равным; он просто не подчиняется генеральному контракту интерфейса Set. http://download.oracle.com/javase/1.4.2/docs/api/java/util/TreeSet.html
Объекты с правильными реализациями equals/hashcode добавлялись и никогда больше не видели, поскольку реализация compareTo была несовместима с равными.