Подобно этот вопрос ...
Какие наихудшие практики вы на самом деле обнаружили в Java-коде?
Мои:
Использование реализаций Collection вместо интерфейсов также не является ошибкой. Это ошибка только в том случае, если конкретная реализация интерфейса не имеет отношения к клиенту.




Излишняя абстракция объектно-ориентированного дизайна (удалена только для 10k).
Тот же ответ в аналогичной теме (относится ко всем языкам, допускающим объектно-ориентированное проектирование).
Не относится строго к Java, но вызывает дорогостоящую функцию снова и снова вместо сохранения результата, когда вы знаете, что он не изменится. Пример:
if (expensiveFunction() > aVar)
aVar = expensiveFunction();
for (int i=0; i < expensiveFunction(); ++i)
System.out.println(expensiveFunction());
Все зависит от того, что вы подразумеваете под словом «дорогое». Мартин Фаулер в своей книге о рефакторинге на самом деле рекомендует писать такой код, так как его легче реорганизовать.
как это проще, чем использовать переменную со значением, возвращенным этой функцией? то есть одна строка против X строк ...
Вы должны быть полностью уверены, что результат метода не меняется. Если метод меняется и, следовательно, результат меняется чаще, тогда подумайте при написании зависимого метода, ваш код ломается, не зная почему. Если вы хотите кешировать, сделайте это в вызываемом дорогом методе. Там вы узнаете, меняется ли результат.
А программисты на Haskell смеются ...
Подобно вашему, но сделал еще один шаг вперед:
Использование переменных класса (статических), когда переменная с областью действия запроса была правильным действием в действии Struts. : O
Фактически это было развернуто в производственной среде в течение нескольких месяцев, и никто ничего не заметил, пока я однажды не просмотрел код.
У нас была такая проблема, которая приводила к ситуации, когда только один пользователь мог одновременно нажимать кнопку «Отправить», не получая трассировки стека. К счастью, пользователи были внутренними, и в конце концов кто-то из команды разработчиков спросил у профессиональных сервисных центров, почему они продолжают кричать через стену куба: «Пожар в дыре!»
Ненавижу, когда люди создают интерфейсы только для того, чтобы вешать набор констант:
public interface InterfaceAntiPattern {
boolean BAD_IDEA = true;
int THIS_SUCKS = 1;
}
- Интерфейсы предназначены для определения поведенческих контрактов, а не удобного механизма для включения констант.
Думаю, вам нравится статический импорт в Java 6?
Статический импорт был введен в Java 5, и я не понимаю, какое отношение они имеют к этому моменту.
Позволяет импортировать, например, Math.cos и называйте его просто cos. См. java.sun.com/j2se/1.5.0/docs/guide/language/static-import.ht мл
Math.cos не является постоянным. Math.PI был бы лучшим примером.
Math - это конкретный класс, а не интерфейс, поэтому Math.PI не является примером этого антипаттерна.
Что общего у статического импорта с этим моментом, так это то, что он позволяет вам просто сказать if (BAD_IDEA), а не if (ConstantsClassPattern.BAD_IDEA), без необходимости расширения InterfaceAntiPattern.
Это постоянный антипаттерн интерфейса и одна из причин, по которым в Java были добавлены перечисления - en.wikipedia.org/wiki/Constant_interface
Какую альтернативу вы предлагаете? 1) Использование классов хуже, потому что вам нужно написать public static final для каждой константы (что не нужно внутри интерфейса). 2) Для констант, которые должны иметь определенные произвольные значения (например, int MY_INT_CONSTANT = 5423;), могут использоваться перечисления Java 5, но они также требуют большего количества кода. Итак, IMO, использование интерфейса часто является лучшим выбором.
@Rogerio Я бы порекомендовал вам прочитать пункт 19 в Эффективной Java. «Если константы сильно привязаны к существующему классу или интерфейсу, вы должны добавить их к этому классу или интерфейсу. Если константы лучше всего рассматривать как члены перечислимого типа, вы должны экспортировать их с типом перечисления. В противном случае вам следует экспортировать константы с помощью неподготовленного служебного класса. Таким образом, интерфейсы следует использовать только для определения типов. Их не следует использовать для экспорта констант.. "
@John Да, я знаком с правилом 19: «Используйте интерфейсы только для определения типов». Это один из очень немногих пунктов в той замечательной книге, с которой я не согласен. В основном это происходит из прошлого опыта в проекте, где у нас были сотни интерфейсов только для констант; почти все эти интерфейсы были вложены в классы сущностей предметной области (и у некоторых сущностей было несколько таких интерфейсов); добавление констант напрямую было бы ужасным, потому что мы потеряли бы имя интерфейса, которое было описанием определенной группы констант. Я согласен, это не идеально, но альтернативы (перечисления / классы) хуже.
Аргументация в пункте 19 Эффективной Java (страницы 98-99) фактически направлена против классов, которые воплощать в жизнь являются константным интерфейсом, чтобы использовать свои константы. Я их не использую. Итак, в моем случае никогда не было «утечки в экспортируемый API класса» просто потому, что у меня никогда не было других классов, реализующих постоянный интерфейс. В этом отношении полностью согласен с п.19.
@ Роджерио, я понимаю, откуда ты. Интересный крайний случай.
Создание подклассов, когда вы не должны этого делать, например вместо использования композиции, агрегирования и т. д.
Обновлено: это особый случай молот.
Абстрагирование функциональности в библиотечный класс который никогда не будет использоваться повторно, поскольку он настолько специфичен для решаемой исходной проблемы.. Таким образом, мы получаем огромное количество библиотечных классов, которые никто никогда не будет использовать и которые полностью заслоняют две полезные утилиты, которые у вас есть на самом деле делать (то есть CollectionUtils и IOUtils).
... паузы для вздоха ...
Почему это так плохо? Это может быть ненужным, но не похоже, чтобы он сильно болит. Также это обеспечивает разделение ответственности, даже если код никогда не будет использоваться повторно.
Мне пришлось поддерживать java-код, где большая часть обработки исключений была такой:
catch( Exception e ) {}
Людей, которые «выбрасывают» исключения, нужно наказывать!
Люди тоже так делают: catch (Exception e) {// не будет} :)
К сожалению, мой текущий проект оказался хуже: catch(Throwable th) { logger.log("something went wrong"); }
Эй, это тоже довольно часто встречается в коде C#.
Я признаю, что при использовании Oracle JDBC я написал catch (SQLException ex) {/ * Что здесь могло быть брошено? * /} при закрытии соединения.
Был один из них. Работал хорошо, пока не было 1200 одновременных пользователей, потом все начало ломаться. Уф.
Иногда я просто хочу проигнорировать исключение. Но это редкость В самом деле.
Java довольно часто требует, чтобы вы перехватили исключение, которое никогда не может произойти. Например, чтение исключения IOException из ByteArrayInputStream - при условии, что вы контролируете создание указанного ByteArrayInputStream.
Это откроет целую дискуссию "отмеченные и непроверенные исключения"! :)
или еще хуже - catch (Throwable t) {}
Тихо перехватить исключение и ничего не сделать с ним - худший грех. В какой-то момент я нашел один с платформы Java. Конечно, после интенсивной отладки. Могу сказать, что меня это не радовало.
@Alan Обратите внимание, что все, что вам нужно сделать, это изменить его на ..went wrong", th);.
Так неудобно вставлять их вручную, в Visual Basic вы можете просто поставить вверху «следующее возобновление ошибки», и тогда вам больше не придется беспокоиться об ошибках. застрял в праведном молнии
Если исключение никогда не должно произойти, выполните catch (Throwable t) /* or whatever class you want */ { throw new AssertionError(t); }.
Моим личным фаворитом было то, что когда я увидел код catch (Exception e){ throw new DuplicateKeyException(); }, я потратил 2 часа, чтобы найти свой дублирующий ключ в своих данных, даже я посмотрел на наше определение дублирования, потому что я не мог найти ничего подозрительного. Это был простой NPE, который был пойман.
Наш стажер использовал модификатор static для сохранения текущего пользователя в приложении Seam.
class Identity{
...
public static User user;
...
}
class foo{
void bar(){
someEntity.setCreator(Identity.user);
}
}
Конечно сработало, когда он его тестировал :)
Стажеры должны учиться, так что я думаю, это простительно. Надеюсь, он больше этого не сделает!
Я сделал это! Единственное, что меня спасает, это то, что я был студентом, и хотя люди, оценивающие мою работу, не заметили моей ошибки, я все же понял ее в своем следующем проекте.
Худшая практика Java, которая охватывает почти все остальные: Глобальное изменяемое состояние.
или любое изменяемое состояние, если бы вы могли использовать неизменное состояние
Нелепая объектно-ориентированная мания с иерархией классов глубиной 10+ уровней.
Отсюда и названия типа DefaultConcreteMutableAbstractWhizzBangImpl. Просто попробуйте отладить такой код - вы часами будете метаться вверх и вниз по дереву классов.
@madlep Совершенно верно! Часть сообщества Java действительно переборщила с экстремальными абстракциями и безумно глубокой иерархией классов. Пару лет назад у Стива Йегге был хороший пост в блоге: Казнь в царстве существительных.
if{
if{
if{
if{
if{
if{
if{
if{
....
}}} еще { ...
В связи с этим, использование ifs вместо if-elses: if (i == 1) {} if (i == 2) {} вместо if (i == 1) {} else if (i == 2) {}
Однажды мне пришлось исследовать веб-приложение, в котором ВСЕ состояние сохранялось на веб-странице, отправляемой клиенту, а не состояние на веб-сервере.
Хотя хорошо масштабируется :)
Для получения бонусных баллов также отправьте в браузер учетные данные пользователя.
Шесть действительно плохих примеров;
System.exit без предупреждения. например, if (properties.size()>10000) System.exit(0); похоронен глубоко в
библиотека.synchronized("one") { }.synchronized(object) { object = ...; }.try { Integer i = null; i.intValue(); } catch(NullPointerException e) { e.printStackTrace(); }.new Integer(text).intValue() or worse new Integer(0).getClass()Я до сих пор не знаю, что плохого в 10000 свойствах. ;)
Ого, строковые константы в качестве блокировок - это здорово ... :)
Я должен признать, что иногда, когда я отлаживаю, я выбрасываю исключения, чтобы получить трассировку стека, но я никогда не проверял это! Если я чувствую себя глупо, я создам класс ExpectedException.
@CaptainMan это быстро грязный, как система выводит операторы println. Я бы подумал, что это нормально, если вы не выпустите код таким образом.
Мой любимый алгоритм сортировки, любезно предоставленный бригадой седобородых:
List needsToBeSorted = new List ();
...blah blah blah...
Set sorted = new TreeSet ();
for (int i = 0; i < needsToBeSorted; i++)
sorted.add (needsToBeSorted.get (i));
needsToBeSorted.clear ();
for (Iterator i = sorted.iterator (); i.hasNext ();)
needsToBeSorted.add (i.next ());
По общему признанию, это сработало, но в конце концов я убедил его, что, возможно, Collections.sort будет намного проще.
Как только я обнаружил исключение singleton:
class Singletons {
public static final MyException myException = new MyException();
}
class Test {
public void doSomething() throws MyException {
throw Singletons.myException;
}
}
Тот же случай исключения генерировался каждый раз ... с точно такой же трассировкой стека, которая не имела ничего общего с реальным потоком кода :-(
Это было недавно рассмотрено в ходе дискуссии, в которой я участвовал. (И я поморщился.): O
Интересно, является ли это объяснением того, как работают исключения .NET?
@finnw: можешь уточнить? Я ничего не знаю об исключениях .net, но теперь мне любопытно, что вы имеете в виду.
Я имел в виду тот факт, что трассировка стека заполняется при возникновении исключения, а не при его создании.
@finnw: перечитывая это через год, как это будет работать, если исключение будет выдано несколькими разными потоками одновременно?
API, требующий от вызывающего:
Foobar f = new Foobar(foobar_id);
f = f.retrieve();
Лучше было бы любое из следующего:
Foobar f = Foobar.retrieve(foobar_id);
или же
Foobar f = new Foobar(foobar_id); // implicit retrieve
или же
Foobar f = new Foobar();
f.retrieve(foobar_id); // but not f = ...
Не закрывать соединения с базой данных, дескрипторы файлов и т. д. В finally {}
А затем попробуйте удалить файл, пока программа все еще работает ...
Создание аксессоров и мутаторов для частных переменных все, не задумываясь, иногда автоматически.
Инкапсуляция была изобретена неспроста.
Тебе нравится Ломбок?
@Saphyra Похоже, компромисс, который может быть очень хорошим в большинстве случаев. Он раскрывает все, но весь этот код является неявным, поэтому он стоит намного меньше. Никто не будет платить за чтение его снова и снова. (Никогда об этом не слышал, смотрел демо сейчас, не пробовал, не программировал на Java в течение многих лет). projectlombok.org
Ошибка молодых программистов: без необходимости использовать переменные-члены вместо локальных.
Пример Java EE:
Запуск потоков в сервлетах или EJB (например, для запуска задач асинхронной обработки).
Это нарушает масштабируемость вашего приложения Java EE. Вы не должны возиться с потоками в компонентах Java EE, потому что сервер приложений должен управлять этим за вас.
Мы реорганизовали это, поместив сообщение в очередь JMS и написав управляемый сообщениями компонент для обработки задачи асинхронной обработки.
Excesive фокусируется на повторном использовании объектов, что приводит к статическим вещам повсюду. (Указанное повторное использование может быть очень полезным в некоторых ситуациях).
В Java есть сборка мусора, если вам нужен объект, создайте новый.
Определение логики с использованием исключений, где будет достаточно цикла for или любой формы цикла.
Пример:
while(i < MAX_VALUE)
{
try
{
while(true)
{
array[j] = //some operation on the array;
j++;
}
}
catch(Exception e)
{
j = 0;
}
}
Серьезно, я знаю парня, который написал этот код. Я просмотрел и поправил код :)
великолепный, хорошо найденный у него
Пару минут назад я видел эту строчку:
Short result = new Short(new Integer(new Double(d).intValue()).shortValue());
Думаю, этот для меня должен быть рекордом. Класс используется для построения сложной модели данных для внешнего интерфейса, включающей фильтрацию. поэтому метод, возвращающий список объектов, выглядит примерно так:
public DataObjectList (GenerateList (massive signature involving 14 parameters, three of which are collections and one is a collection of collections)
try {
250 lines of code to retrieve the data which calls a stored proc that parses some of it and basically contains GUI logic
} catch (Exception e) {
return new DataObjectList(e, filterFields);
}
Итак, я попал сюда, потому что мне было интересно, почему следующий метод вызова дал сбой, и я не мог видеть, где было установлено поле Exception.
DataObjectList dataObjectList= EntireSystemObject.getDataObjectList Generator().generateDataObjectList (viewAsUserCode, processedDataRowHandler, folderQuery, pageNumber, listCount, sortColumns, sortDirections, groupField, filters, firstRun, false, false, resetView);
dataObjectList.setData(processedDataRowHandler.getData());
if (dataObjectList.getErrorException() == null) {
do stuff for GUI, I think, put lots of things into maps ... 250 lines or so
}
return dataObjectList;
} else {
put a blank version into the GUI and then
throw new DWRErrorException("List failed due to list generation error", "list failed due to list generation error for folder: " + folderID + ", column: " + columnID, List.getErrorException(), ListInfo);
}
Все пока со мной?
Ну, по крайней мере, они сказали нам во внешнем интерфейсе, что что-то действительно пошло не так!
AbstractSpringBeanFactoryFactoryFacadeMutatorBeanFactory. Я терпеть не могу эту надуманную, непонятную чушь. Бенджи Смит делает это немного элегантнее.
Вот обрезанный образец реального апплета, который я должен был поддерживать, мне потребовалась целая вечность, чтобы понять, что происходит.
int sval, eval, stepv, i;
double d;
if (/*someCondition*/)
{
sval = 360;//(all values multiplied by 20)
eval = -271;
stepv = -10;
}
else if (/*someCondition*/)
{
sval = 320;
eval = -601;
stepv = -10;
}
else if (/*someCondition*/)
{
sval = 0;
eval = -311;
stepv = -10;
}
else
{
sval = 360;
eval = -601;
stepv = -10;
}
for (i = sval; i > eval; i = i + stepv)
{
d = i;
d = d / 20.0;
//do some more stuff in loop
}
оказывается, он хотел повторить цикл на 0,5 и это не ошибка вставки, это схема отступов
Убей его огнем !!!
В файловом вводе-выводе: некорректное использование блока try-catch.
try {
/* open file */
}
catch(Exception e) {
e.printStackTrace();
}
try {
/* read file content */
}
catch (Exception e) {
e.printStackTrace();
}
try {
/* close the file */
}
catch (Exception e) {
e.printStackTrace();
}
видел что-то вроде этого:
public static boolean isNull(int value) {
Integer integer = new Integer(value);
if (integer == null) {
return true;
} else {
return false;
}
}
У них был аналогичный метод для лонгов.
Я предполагаю, что они изначально сделали что-то вроде
if (value == null) {
и получил ошибку компиляции и все еще не понимал, что примитивные значения не могут быть нулевыми.
Мне сейчас очень стыдно, но когда я начал программировать, я сделал такой метод: public boolean isNull(){ return this == null }
Я не вижу случая, когда этот метод возвращает true, всегда вызывается new. : /
После длительного воздействия Java делает это с некоторыми людьми.
Почему? Я считаю, что это потому, что есть слишком много Intellisense и никакого смысла. Он позволяет делать глупые вещи так быстро, что люди не перестают думать.
Пример 1:
boolean negate( boolean shouldNegate, boolean value ) {
return (shouldNegate?(!value):value;
}
который, конечно, совпадает со значением ^ shouldNegate, простым XOR.
Пример 2: (клянусь, я не выдумываю)
boolean isNotNull( Object o ) {
return o != null;
}
Оба с дополнительными 4-6 строками Javadoc, объясняя, что эти методы сделали.
Пример 3:
/**
*
*
*/
Пустой документ Javadoc, чтобы убрать эти предупреждения раздражающий Eclipse об отсутствии Javadoc.
Пример 3b:
/**
* A constructor. Takes no parameters and creates a new instance of MyClass.
*/
public MyClass() {
}
Преобразование простого XML-сообщения (без XSD / пространств имен) в DataObject:
DataObject operation_file = boFactory....
try{operation_file.setString("file_name", Constants.getTagValue("file_name", eElementOp));}catch (Exception e){operation_file.setString("file_name","");}
try{operation_file.setDate("proposed_execution_date", sdf.parse(Constants.getTagValue("proposed_execution_date", eElementOp)));}catch (Exception e){operation_file.setString("proposed_execution_date",null);}
try{operation_file.setString("instructions", Constants.getTagValue("instructions", eElementOp));}catch (Exception e){operation_file.setString("instructions","");}
try{operation_file.setString("description", Constants.getTagValue("description", eElementOp));}catch (Exception e){operation_file.setString("description","");}
Я назвал это программированием, ориентированным на попытки отлова (tcop) ...
Огромные имена классов. Помню: AbstractTableComponentListeningBehaviourPanel.java и другие подобные монстры.
Хуже всего то, что хотя соглашение об именах было безумно подробным, мне все равно пришлось разбирать файлы, чтобы понять их назначение.
Простое использование переменных экземпляра в сервлетах - это нет ошибка или плохая практика. Изменение переменных после инициализации сервлета может быть ошибкой (это зависит от того, что представляют эти переменные) и является плохой практикой, но просто использование переменных экземпляра в сервлетах - неплохая практика.