В программировании Java некоторые знания не могут быть изучены только с помощью языковых спецификаций или стандартной документации API. В этой статье я постараюсь собрать некоторые из наиболее часто используемых идиомов, особенно тех, которые трудно догадаться.
Я поместил весь код в этой статье в общественных местах. Вы можете скопировать и изменить любой фрагмент кода в соответствии с вашими предпочтениями, без каких -либо учетных данных.
Реализация equals ()
класс Человек {Строка Имя; int birthyyear; байт [] сырой; public boolean equals (Object obj) {if (! obj exanceOf person) вернуть false; Человек другой = (человек) obj; return name.equals (shrel.name) && birthdayyear == Другое. Birthyear && arrays.equals (raw, orheme.raw); } public int hashcode () {...}} Параметр должен иметь объект типа, а не периферический класс.
foo.equals (null) должен вернуть ложь и не может бросить NullpointerException. (Обратите внимание, что нулевой экземпляр любого класса всегда возвращает false, поэтому приведенный выше код можно запустить.)
Сравнение доменов базового типа (например, int) используется ==, а сравнение доменов массива основных типов используется Arrays.equals ().
Когда перезаписание равно (), не забудьте перезаписать HashCode () соответственно, чтобы соответствовать equals ().
Ссылка: java.lang.object.equals (объект).
Реализовать hashcode ()
класс человека {строка a; Объект B; байт C; int [] d; public int hashcode () {return a.hashcode () + b.hashcode () + c + arrays.hashcode (d); } public boolean equals (Object o) {...}} Когда объекты x и y имеют x.equals (y) == true, вы должны убедиться, что x.hashcode () == y.hashcode ().
В соответствии с обратным предложением, если x.hashcode ()! = Y.hashcode (), то x.equals (y) == false должен быть истинно.
Вам не нужно гарантировать, что x.hashcode ()! = Y.hashcode (), когда x.equals (y) == false. Тем не менее, это улучшает производительность хэш -таблицы, если вы можете сделать это как можно дольше.
Самая простая юридическая реализация hashcode () - просто вернуть 0; Хотя эта реализация верна, это приведет к тому, что структуры данных, такие как HashMap, работают очень медленно.
Реализовать сравнение ()
Класс Человек реализует сопоставимый <derss> {string firstname; String Lastname; int день рождения; // Сравнение FirstName, Break Skies By Lastname, наконец -то нарушайте связи с помощью даты рождения public int compareto (человек другой) {if (firstname.compareto (shrel.firstname)! = 0) вернуть FirstName.comPareto (ore.firstName); else if (lastname.compareto (shrel.lastname)! = 0) return lastname.compareto (shrel.lastname); иначе, если (дата рождения <Другое. BirthDate) возврат -1; иначе, если (дата рождения> Другое. Бартхдат) возврат 1; иначе вернуть 0; }} Всегда реализуйте общую версию, сопоставимую вместо примитивного типа, сопоставимого. Потому что это может сохранить объем кода и уменьшить ненужные хлопоты.
Просто заботитесь о знаках (отрицательных/нулевых/положительных), которые возвращают результат, их размер не имеет значения.
Реализация Comporator.comPare () аналогична этой.
Реализуйте клон ()
Значения класса реализуют клонируемые {String abc; двойной foo; int [] bars; Дата нанята; public values clone () {try {values result = (values) super.clone (); result.bars = result.bars.clone (); result.hied = result.hired.clone (); результат возврата; } catch (clonenotsupportedException e) {// Невозможно бросить новое утверждение иррор (e); }}} Используйте Super.clone (), чтобы сделать класс объекта ответственным за создание новых объектов.
Основные домены типа были скопированы правильно. Опять же, нам не нужно клонировать неизменные типы, такие как String и Biginteger.
Вручную выполните глубокое копирование всех непримитивых полей типов (объекты и массивы).
Клонируемый класс реализован, и метод клона () никогда не должен бросать клоненотсупорноэкспений. Поэтому вам нужно поймать это исключение и проигнорировать его, или обернуть его неконтролируемым исключением.
Это нормально и законно вручную реализовать метод Clone () без использования метода object.clone ().
Используйте StringBuilder или StringBuffer
// join (["a", "b", "c"]) -> "a и b и c" string join (list <string> strs) {stringBuilder sb = new StringBuilder (); Boolean First = true; for (string s: strs) {if (first) first = false; else sb.append ("и"); SB.Append (ы); } return sb.toString ();} Не используйте дублирующую конкатенацию строки, как это: s += элемент, потому что его эффективность времени o (n^2).
При использовании StringBuilder или StringBuffer вы можете использовать метод Append () для добавления текста и использовать метод ToString () для получения подключенного всего текста.
Приоритет дается StringBuilder, поскольку он быстрее. Все методы StringBuffer синхронизированы, и вам обычно не нужен синхронизированный метод.
Генерировать случайные целые числа в диапазоне
Случайный rand = new Random (); // между 1 и 6, включая diceroll () {return rand.nextint (6) + 1;} Всегда используйте метод Java API, чтобы генерировать случайное число в диапазоне целых чисел.
Не пытайтесь использовать math.abs (rand.nextint ()) %n для этих неопределенных применений, потому что ее результаты смещены. Кроме того, его значение результата может быть отрицательным, например, когда rand.nextint () == integer.min_value.
Используйте iterator.remove ()
void filter (list <string> list) {for (iterator <string> iter = list.iterator (); iter.hasnext ();) {string item = iter.next (); if (...) iter.remove (); }}Метод remove () действует при недавно возвращенном вводе метода следующего (). Каждая запись может использовать метод remove () только один раз.
Вернуть строку
String reverse (string s) {return new StringBuilder (s) .reverse (). ToString ();}Этот метод, вероятно, должен быть добавлен в стандартную библиотеку Java.
Запустить ветку
Следующие три примера делают то же самое по -разному.
Как реализовать runnnable:
void startAThRead0 () {new Thread (new MyRunnable ()). start ();} класс MyRunnable реализует runnable {public void run () {...}}Как унаследовать нить:
void startAThread1 () {new MyThread (). Start ();} класс MyThread Extends Thread {public void run () {...}}Как унаследовать нить анонимно:
void startAThread2 () {new Thread () {public void run () {...}} .start ();}Не вызывайте метод run () напрямую. Метод Thread.Start () всегда вызывается, который создает новый поток и приводит к тому, что недавно созданный поток вызовет run ().
Используйте Try-Finally
Пример потока ввода/вывода:
void writeStuff () бросает ioException {outputStream out = new FileOutputStream (...); попробуйте {out.write (...); } наконец {out.close (); }}Пример блокировки:
void dowithlock (lock lock) {lock.acquire (); try {...} наконец {lock.release (); }} Если оператор перед попыткой не выполняется, и исключение будет отменено, то блок оператора, наконец, не будет выполнен. Но независимо от того, что, в этом примере, нет необходимости беспокоиться об освобождении ресурсов.
Если оператор в блоке оператора TRY выбрасывает исключение, запуск программы перейдет к блоку оператора, чтобы выполнить как можно больше операторов, а затем выпрыгнуть из этого метода (если только этот метод не имеет другого периферического блока операторов, наконец,).
Читать данные байтов из входного потока
InputStream in = (...); try {while (true) {int b = in.read (); if (b == -1) разрыв; (... процесс B ...)}} наконец {in.close ();}Метод Read () либо возвращает следующее число байтов, считываемых из потока (от 0 до 255, включая 0 и 255), либо возвращает -1, когда достигнут конец потока.
Прочитать данные блока из входного потока
InputStream in = (...); try {byte [] buf = new Byte [100]; while (true) {int n = in.read (buf); if (n == -1) разрыв; (... процесс BUF с Offset = 0 и Length = n ...)}} наконец {in.close ();}Помните, что метод read () не обязательно заполняет весь BUF, поэтому вы должны рассмотреть длину возврата в логике обработки.
Прочитать текст из файла
BufferedReader in = new BufferedReader (new InputStreamReader (new FileInputStream (...), "UTF-8")); try {while (true) {String line = in.readline (); if (line == null) перерыв; (... Линия процесса ...)}} наконец {in.close ();} Создание объекта BufferedReader кажется очень многословным. Это связано с тем, что Java рассматривает байты и символов как к двум разным понятиям (это отличается от C).
Вы можете использовать любой тип inputstream вместо FileInputStream, такой как сокет.
BufferedReader.Readline () возвращает NULL, когда достигнут конец потока.
Чтобы читать один символ за раз, используйте метод reader.read ().
Вы можете использовать другие кодировки персонажа без UTF-8, но лучше не делать этого.
Записать текст в файл
Printwriter out = new PrintWriter (новый outputstreamwriter (new FileOutputStream (...), "UTF-8")); try {out.print ("hello"); Out.print (42); out.println ("world!");} наконец {out.close ();} Создание объектов PrintWriter кажется очень многословным. Это связано с тем, что Java рассматривает байты и символов как к двум разным понятиям (это отличается от C).
Как и System.out, вы можете использовать Print () и println () для печати нескольких типов значений.
Вы можете использовать другие кодировки персонажа без UTF-8, но лучше не делать этого.
Защитная проверка значения
int informorial (int n) {if (n <0) бросить новый allosalargumentException ("undefined"); иначе if (n> = 13) бросить новое арифметикексапс ("переполнение результата"); иначе if (n == 0) вернуть 1; else return n * факториал (n - 1);} Не думайте, что входные значения являются положительными, достаточно малыми и т. Д. Для явного обнаружения этих условий.
Хорошо разработанная функция должна быть в состоянии правильно выполнять для всех возможных входных значений. Убедитесь, что все ситуации учитываются и что не существует неправильного вывода (такого как переполнение).
Объекты профилактического тестирования
int findIndex (list <string> list, string target) {if (list == null || target == null) бросить новый NullPointerException (); ...}Не думайте, что параметры объекта не будут нулевыми. Чтобы явно обнаружить это условие.
Индекс массива профилактического обнаружения
void frob (byte [] b, int index) {if (b == null) бросить новый NullPointerException (); if (index <0 || index> = b.length) бросить новый indexoutofboundsexception (); ...}Не думайте, что указанный индекс массива не будет пересекать границы. Чтобы обнаружить это явно.
Профилактический интервал массива
void frob (byte [] b, int off, int len) {if (b == null) бросить новый nullpointerexception (); if (off <0 || off> b.length || len <0 || b.length - off <len) бросить новый indexoutofboundsexception (); ...}Не думайте, что заданный интервал массива (например, начиная от выхода, чтение элементов LEN) не выходит за рамки границ. Чтобы обнаружить это явно.
Заполнить элементы массива
Используя петли:
// заполнить каждый элемент массива 'a' с 123byte [] a = (...); для (int i = 0; i <a.length; i ++) a [i] = 123;
(Преимущественные) методы использования стандартной библиотеки:
Arrays.fill (a, (байт) 123);
Скопируйте элемент массива в диапазоне
Используя петли:
// копирование 8 элементов из массива 'a', начиная с смещения 3 // до массива 'b', начиная с смещения 6, // Предполагая, что 'a' и 'b' являются отдельными arrysbybe [] a = (...); byte [] b = (...); для (int i = 0; i <8; i ++) b [6 + i] = a [3 + i];
(Преимущественные) методы использования стандартной библиотеки:
System.ArrayCopy (A, 3, B, 6, 8);
Изменить размер массива
Используйте петли (масштабирование):
// Сделать массив 'A' больше до Newlenbyte [] a = (...); byte [] b = new Byte [newlen]; for (int i = 0; i <a.length; i ++) // поднимается до длины a b [i] = a [i]; a = b;
Используйте петли (уменьшите размер):
// Сделать массив 'A' меньше до Newlenbyte [] a = (...); Byte [] b = новый байт [newlen]; для (int i = 0; i <b.length; i ++) // поднимается до длины b b [i] = a [i]; a = b;
(Преимущественные) методы использования стандартной библиотеки:
a = arrays.copyof (a, newlen);
Упаковка 4 байта в int
int packbigendian (byte [] b) {return (b [0] & 0xff) << 24 | (B [1] & 0xff) << 16 | (B [2] & 0xff) << 8 | (b [3] & 0xff) << 0;} int packlittleendian (byte [] b) {return (b [0] & 0xff) << 0 | (B [1] & 0xff) << 8 | (B [2] & 0xff) << 16 | (B [3] & 0xff) << 24;}Разложить Int на 4 байта
byte [] unpackbigendian (int x) {return new byte [] {(byte) (x >>> 24), (byte) (x >>> 16), (byte) (x >>> 8), (байт) (x >>> 0)};} byte [] unpacklitleendian (int x) {return new byte [] {(byte) (>> x) {(>> x) {(>> (>> (>> (>> (>> (>> (>> (>> (>> (>> (>> (>> (>> (>> (>> x) (x >> x). (байт) (x >>> 8), (байт) (x >>> 16), (byte) (x >>> 24)};}Всегда используйте оператор правого смены без знака (>>>), чтобы обернуть биты, не используйте арифметический оператор правого сдвига (>>).