Худшая практика Java из вашего опыта?

Подобно этот вопрос ...

Какие наихудшие практики вы на самом деле обнаружили в Java-коде?

Мои:

  • использование переменных экземпляра в сервлетах (на самом деле это не просто плохая практика, а ошибка)
  • используя реализации Collection, такие как HashMap, и не используя соответствующие интерфейсы
  • с использованием кажущихся загадочными именами классов, таких как SmsMaker (SmsFactory) или CommEnvironment (CommunicationContext)

Простое использование переменных экземпляра в сервлетах - это нет ошибка или плохая практика. Изменение переменных после инициализации сервлета может быть ошибкой (это зависит от того, что представляют эти переменные) и является плохой практикой, но просто использование переменных экземпляра в сервлетах - неплохая практика.

MetroidFan2002 26.10.2008 21:22

Использование реализаций Collection вместо интерфейсов также не является ошибкой. Это ошибка только в том случае, если конкретная реализация интерфейса не имеет отношения к клиенту.

Steve B. 28.06.2013 17:57
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
29
2
18 607
31
Перейти к ответу Данный вопрос помечен как решенный

Ответы 31

Излишняя абстракция объектно-ориентированного дизайна (удалена только для 10k).

Тот же ответ в аналогичной теме (относится ко всем языкам, допускающим объектно-ориентированное проектирование).

Не относится строго к Java, но вызывает дорогостоящую функцию снова и снова вместо сохранения результата, когда вы знаете, что он не изменится. Пример:

if (expensiveFunction() > aVar)
    aVar = expensiveFunction();
for (int i=0; i < expensiveFunction(); ++i)
    System.out.println(expensiveFunction());

Все зависит от того, что вы подразумеваете под словом «дорогое». Мартин Фаулер в своей книге о рефакторинге на самом деле рекомендует писать такой код, так как его легче реорганизовать.

oxbow_lakes 26.10.2008 20:17

как это проще, чем использовать переменную со значением, возвращенным этой функцией? то есть одна строка против X строк ...

dusoft 02.08.2009 20:19

Вы должны быть полностью уверены, что результат метода не меняется. Если метод меняется и, следовательно, результат меняется чаще, тогда подумайте при написании зависимого метода, ваш код ломается, не зная почему. Если вы хотите кешировать, сделайте это в вызываемом дорогом методе. Там вы узнаете, меняется ли результат.

Janusz 02.08.2009 22:29

А программисты на Haskell смеются ...

Mechanical snail 19.08.2012 12:43

Подобно вашему, но сделал еще один шаг вперед:

Использование переменных класса (статических), когда переменная с областью действия запроса была правильным действием в действии Struts. : O

Фактически это было развернуто в производственной среде в течение нескольких месяцев, и никто ничего не заметил, пока я однажды не просмотрел код.

У нас была такая проблема, которая приводила к ситуации, когда только один пользователь мог одновременно нажимать кнопку «Отправить», не получая трассировки стека. К счастью, пользователи были внутренними, и в конце концов кто-то из команды разработчиков спросил у профессиональных сервисных центров, почему они продолжают кричать через стену куба: «Пожар в дыре!»

David Moles 04.08.2009 13:00

Ненавижу, когда люди создают интерфейсы только для того, чтобы вешать набор констант:

public interface InterfaceAntiPattern {
  boolean BAD_IDEA = true;
  int THIS_SUCKS = 1;
}

- Интерфейсы предназначены для определения поведенческих контрактов, а не удобного механизма для включения констант.

Думаю, вам нравится статический импорт в Java 6?

Thorbjørn Ravn Andersen 02.08.2009 20:15

Статический импорт был введен в Java 5, и я не понимаю, какое отношение они имеют к этому моменту.

John Topley 02.08.2009 20:41

Позволяет импортировать, например, Math.cos и называйте его просто cos. См. java.sun.com/j2se/1.5.0/docs/guide/language/static-import.ht‌ мл

Thorbjørn Ravn Andersen 02.08.2009 22:18

Math.cos не является постоянным. Math.PI был бы лучшим примером.

finnw 03.08.2009 10:54

Math - это конкретный класс, а не интерфейс, поэтому Math.PI не является примером этого антипаттерна.

John Topley 03.08.2009 15:11

Что общего у статического импорта с этим моментом, так это то, что он позволяет вам просто сказать if (BAD_IDEA), а не if (ConstantsClassPattern.BAD_IDEA), без необходимости расширения InterfaceAntiPattern.

David Moles 04.08.2009 12:57

Это постоянный антипаттерн интерфейса и одна из причин, по которым в Java были добавлены перечисления - en.wikipedia.org/wiki/Constant_interface

Jesper 01.10.2009 14:53

Какую альтернативу вы предлагаете? 1) Использование классов хуже, потому что вам нужно написать public static final для каждой константы (что не нужно внутри интерфейса). 2) Для констант, которые должны иметь определенные произвольные значения (например, int MY_INT_CONSTANT = 5423;), могут использоваться перечисления Java 5, но они также требуют большего количества кода. Итак, IMO, использование интерфейса часто является лучшим выбором.

Rogério 03.02.2010 17:50

@Rogerio Я бы порекомендовал вам прочитать пункт 19 в Эффективной Java. «Если константы сильно привязаны к существующему классу или интерфейсу, вы должны добавить их к этому классу или интерфейсу. Если константы лучше всего рассматривать как члены перечислимого типа, вы должны экспортировать их с типом перечисления. В противном случае вам следует экспортировать константы с помощью неподготовленного служебного класса. Таким образом, интерфейсы следует использовать только для определения типов. Их не следует использовать для экспорта констант.. "

John Topley 03.02.2010 17:57

@John Да, я знаком с правилом 19: «Используйте интерфейсы только для определения типов». Это один из очень немногих пунктов в той замечательной книге, с которой я не согласен. В основном это происходит из прошлого опыта в проекте, где у нас были сотни интерфейсов только для констант; почти все эти интерфейсы были вложены в классы сущностей предметной области (и у некоторых сущностей было несколько таких интерфейсов); добавление констант напрямую было бы ужасным, потому что мы потеряли бы имя интерфейса, которое было описанием определенной группы констант. Я согласен, это не идеально, но альтернативы (перечисления / классы) хуже.

Rogério 04.02.2010 18:50

Аргументация в пункте 19 Эффективной Java (страницы 98-99) фактически направлена ​​против классов, которые воплощать в жизнь являются константным интерфейсом, чтобы использовать свои константы. Я их не использую. Итак, в моем случае никогда не было «утечки в экспортируемый API класса» просто потому, что у меня никогда не было других классов, реализующих постоянный интерфейс. В этом отношении полностью согласен с п.19.

Rogério 04.02.2010 19:01

@ Роджерио, я понимаю, откуда ты. Интересный крайний случай.

John Topley 04.02.2010 19:15

Создание подклассов, когда вы не должны этого делать, например вместо использования композиции, агрегирования и т. д.

Обновлено: это особый случай молот.

Абстрагирование функциональности в библиотечный класс который никогда не будет использоваться повторно, поскольку он настолько специфичен для решаемой исходной проблемы.. Таким образом, мы получаем огромное количество библиотечных классов, которые никто никогда не будет использовать и которые полностью заслоняют две полезные утилиты, которые у вас есть на самом деле делать (то есть CollectionUtils и IOUtils).

... паузы для вздоха ...

Почему это так плохо? Это может быть ненужным, но не похоже, чтобы он сильно болит. Также это обеспечивает разделение ответственности, даже если код никогда не будет использоваться повторно.

Mechanical snail 19.08.2012 12:45
Ответ принят как подходящий

Мне пришлось поддерживать java-код, где большая часть обработки исключений была такой:

catch( Exception e ) {}

Людей, которые «выбрасывают» исключения, нужно наказывать!

TM. 26.10.2008 21:27

Люди тоже так делают: catch (Exception e) {// не будет} :)

jb. 26.10.2008 23:08

К сожалению, мой текущий проект оказался хуже: catch(Throwable th) { logger.log("something went wrong"); }

Alan 27.10.2008 04:23

Эй, это тоже довольно часто встречается в коде C#.

Malik Daud Ahmad Khokhar 28.10.2008 09:09

Я признаю, что при использовании Oracle JDBC я написал catch (SQLException ex) {/ * Что здесь могло быть брошено? * /} при закрытии соединения.

Powerlord 29.10.2008 21:44

Был один из них. Работал хорошо, пока не было 1200 одновременных пользователей, потом все начало ломаться. Уф.

Thorbjørn Ravn Andersen 02.08.2009 20:15

Иногда я просто хочу проигнорировать исключение. Но это редкость В самом деле.

Isaac Waller 02.08.2009 22:30

Java довольно часто требует, чтобы вы перехватили исключение, которое никогда не может произойти. Например, чтение исключения IOException из ByteArrayInputStream - при условии, что вы контролируете создание указанного ByteArrayInputStream.

slim 01.10.2009 14:19

Это откроет целую дискуссию "отмеченные и непроверенные исключения"! :)

dogbane 01.10.2009 14:24

или еще хуже - catch (Throwable t) {}

Chii 01.10.2009 14:47

Тихо перехватить исключение и ничего не сделать с ним - худший грех. В какой-то момент я нашел один с платформы Java. Конечно, после интенсивной отладки. Могу сказать, что меня это не радовало.

Carlos 25.10.2009 04:21

@Alan Обратите внимание, что все, что вам нужно сделать, это изменить его на ..went wrong", th);.

Thorbjørn Ravn Andersen 16.02.2012 16:29

Так неудобно вставлять их вручную, в Visual Basic вы можете просто поставить вверху «следующее возобновление ошибки», и тогда вам больше не придется беспокоиться об ошибках. застрял в праведном молнии

Richard Tingle 01.11.2013 18:29

Если исключение никогда не должно произойти, выполните catch (Throwable t) /* or whatever class you want */ { throw new AssertionError(t); }.

Demi 09.08.2015 04:21

Моим личным фаворитом было то, что когда я увидел код catch (Exception e){ throw new DuplicateKeyException(); }, я потратил 2 часа, чтобы найти свой дублирующий ключ в своих данных, даже я посмотрел на наше определение дублирования, потому что я не мог найти ничего подозрительного. Это был простой NPE, который был пойман.

CsBalazsHungary 17.02.2017 19:50

Наш стажер использовал модификатор static для сохранения текущего пользователя в приложении Seam.

 class Identity{
    ...
    public static User user; 
    ...
 }

 class foo{

    void bar(){
       someEntity.setCreator(Identity.user); 
    }

 }

Конечно сработало, когда он его тестировал :)

Стажеры должны учиться, так что я думаю, это простительно. Надеюсь, он больше этого не сделает!

TM. 26.10.2008 21:26

Я сделал это! Единственное, что меня спасает, это то, что я был студентом, и хотя люди, оценивающие мою работу, не заметили моей ошибки, я все же понял ее в своем следующем проекте.

Athena 27.10.2008 04:05

Худшая практика Java, которая охватывает почти все остальные: Глобальное изменяемое состояние.

или любое изменяемое состояние, если бы вы могли использовать неизменное состояние

RAY 06.01.2011 09:21

Нелепая объектно-ориентированная мания с иерархией классов глубиной 10+ уровней.

Отсюда и названия типа DefaultConcreteMutableAbstractWhizzBangImpl. Просто попробуйте отладить такой код - вы часами будете метаться вверх и вниз по дереву классов.

@madlep Совершенно верно! Часть сообщества Java действительно переборщила с экстремальными абстракциями и безумно глубокой иерархией классов. Пару лет назад у Стива Йегге был хороший пост в блоге: Казнь в царстве существительных.

if{
 if{
  if{
   if{
    if{
     if{
      if{
       if{
         ....

}}} еще { ...

user85421 03.08.2009 19:03

В связи с этим, использование ifs вместо if-elses: if (i == 1) {} if (i == 2) {} вместо if (i == 1) {} else if (i == 2) {}

dogbane 01.10.2009 14:35

Однажды мне пришлось исследовать веб-приложение, в котором ВСЕ состояние сохранялось на веб-странице, отправляемой клиенту, а не состояние на веб-сервере.

Хотя хорошо масштабируется :)

Для получения бонусных баллов также отправьте в браузер учетные данные пользователя.

finnw 03.08.2009 10:58

Шесть действительно плохих примеров;

  • Вместо сообщения об ошибках просто 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 свойствах. ;)

Peter Lawrey 05.08.2009 22:35

Ого, строковые константы в качестве блокировок - это здорово ... :)

thenickdude 01.10.2009 17:31

Я должен признать, что иногда, когда я отлаживаю, я выбрасываю исключения, чтобы получить трассировку стека, но я никогда не проверял это! Если я чувствую себя глупо, я создам класс ExpectedException.

Captain Man 28.04.2015 05:41

@CaptainMan это быстро грязный, как система выводит операторы println. Я бы подумал, что это нормально, если вы не выпустите код таким образом.

Peter Lawrey 28.04.2015 08:32
stackoverflow.com/questions/9622315/…
user2418306 28.05.2016 14:53

Мой любимый алгоритм сортировки, любезно предоставленный бригадой седобородых:

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

Sam Harwell 03.08.2009 10:50

Интересно, является ли это объяснением того, как работают исключения .NET?

finnw 20.01.2011 03:15

@finnw: можешь уточнить? Я ничего не знаю об исключениях .net, но теперь мне любопытно, что вы имеете в виду.

Peter Štibraný 20.01.2011 13:27

Я имел в виду тот факт, что трассировка стека заполняется при возникновении исключения, а не при его создании.

finnw 20.01.2011 14:01

@finnw: перечитывая это через год, как это будет работать, если исключение будет выдано несколькими разными потоками одновременно?

Peter Štibraný 11.01.2012 17:54

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 04.06.2019 17:36

Создание аксессоров и мутаторов для частных переменных все, не задумываясь, иногда автоматически.

Инкапсуляция была изобретена неспроста.

Тебе нравится Ломбок?

Saphyra 04.06.2019 17:37

@Saphyra Похоже, компромисс, который может быть очень хорошим в большинстве случаев. Он раскрывает все, но весь этот код является неявным, поэтому он стоит намного меньше. Никто не будет платить за чтение его снова и снова. (Никогда об этом не слышал, смотрел демо сейчас, не пробовал, не программировал на Java в течение многих лет). projectlombok.org

Emilio M Bumachar 04.06.2019 19:21

Ошибка молодых программистов: без необходимости использовать переменные-члены вместо локальных.

Пример 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;
   }
}

Серьезно, я знаю парня, который написал этот код. Я просмотрел и поправил код :)

великолепный, хорошо найденный у него

maazza 02.09.2012 12:57

Пару минут назад я видел эту строчку:

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 и это не ошибка вставки, это схема отступов

Убей его огнем !!!

Joshua Blevins 07.06.2018 14:55

В файловом вводе-выводе: некорректное использование блока 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 }

CsBalazsHungary 08.01.2016 17:47

Я не вижу случая, когда этот метод возвращает true, всегда вызывается new. : /

Spasoje Petronijević 15.11.2017 17:24

Не думать так, как следовало бы программисту.

После длительного воздействия 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 и другие подобные монстры.

Хуже всего то, что хотя соглашение об именах было безумно подробным, мне все равно пришлось разбирать файлы, чтобы понять их назначение.

Другие вопросы по теме