Статьи

Java-мемы, которые отказываются умирать

Также под названием; Мой питомец ненавидит Java-кодирование.

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

Использование StringBuffer вместо StringBuilder

Javadoc для StringBuffer с 2004 года заявляет

Начиная с выпуска JDK 5 этот класс был дополнен эквивалентным классом, предназначенным для использования одним потоком, StringBuilder. Класс StringBuilder, как правило, следует использовать предпочтительнее этого, поскольку он поддерживает все те же операции, но он быстрее, так как он не выполняет синхронизацию.

StringBuilder не только лучший выбор, но и случаи, когда вы могли бы использовать синхронизированный StringBuffer, настолько редки, что, в отличие от него, это никогда не было хорошей идеей.

Скажи, у тебя был код

1
2
// run in two threads
sb.append(key).append("=").append(value).append(", ");

Каждое дополнение является потокобезопасным, но блокировка может быть снята в любой момент, что означает, что вы можете получить

1
2
3
key1=value1, key2=value2,
key1=key2value1=, value2,
key1key2==value1value2, ,

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

Использование DataInputStream для чтения текста

Другой распространенный мем — использование DataInputStream при чтении текста в следующем шаблоне (три строки с двумя считывателями на одной строке). Я подозреваю, что существует один оригинальный код, который копируется вокруг.

1
2
3
FileInputStream fstream = new FileInputStream("filename.txt"); 
DataInputStream in = new DataInputStream(fstream); 
BufferedReader br = new BufferedReader(new InputStreamReader(in)); 

Это плохо по трем причинам

  • Вы можете испытать желание использовать in для чтения двоичного файла, который не будет работать из-за буферизированной природы BufferedReader. (Я видел это попробовал)
  • Точно так же вы можете полагать, что DataInputStream делает что-то полезное здесь, когда это не
  • Существует гораздо более короткий путь, который является правильным.
1
2
3
4
5
BufferedReader br = new BufferedReader(new FileReader("filename.txt"));
// or with Java 7.
try (BufferedReader br = new BufferedReader(new FileReader("filename.txt")) {
    // use br
}

Использование двойной проверки блокировки для создания синглтона

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
// Singleton with double-checked locking:
public class Singleton {
    private volatile static Singleton instance;
 
    private Singleton() { }
 
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Проблема заключалась в том, что до Java 5.0 это обычно работало, но не было гарантировано в модели памяти. Был более простой вариант, который был безопасным и не требовал явной блокировки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
// suggested by Bill Pugh
public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() { }
 
    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance()
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder {
        public static final Singleton INSTANCE = new Singleton();
    }
 
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

Это было все еще многословно, но это работало и не требовало явной блокировки, чтобы это могло быть быстрее.

В Java 5.0, когда они исправили модель памяти для безопасной обработки двойной блокировки, они также представили перечисления, которые дали вам гораздо более простое решение.

Во втором издании своей книги «Эффективная Java» Джошуа Блох утверждает, что «одноэлементный тип перечисления является лучшим способом реализации синглтона»

С перечислением код выглядит следующим образом.

1
2
3
public enum Singleton {
    INSTANCE;
}

Это лениво загруженный, потокобезопасный, без явных блокировок и намного более простой.

Ссылка: Java-мемы, которые отказываются умирать от нашего партнера JCG Питера Лоури из блога Vanilla Java .