
Effective_Java.pptx
- Количество слайдов: 45
Effective Java Луговский Владимир Java developer System Development 1 www. itransition. com Здесь и сейчас!
Жаль, что у меня не было этой книги лет 10 назад. Некоторые могут подумать, что мне не нужны книги по Java, но эта мне точно нужна. Джеймс Гослинг, вице президент Sun Microsystems, Inc. www. itransition. com Стр. 2
Книга • Java best practices, накопленные за годы существования языка • Объясняет как правильно использовать нововведения Java 5 • Автор – Джошуа Блох – разрабатывал collections framework, java. math и другое © 2006 Itransition. Все права защищены. Стр. 3
equals & hash. Code © 2006 Itransition. Все права защищены. Стр. 4
equals & hash. Code Для переопределения должна быть причина Нельзя переопределять по одному методу www. itransition. com Стр. 5
Когде же переопределять equals? Переопределять Класс имеет понятие логического равенства, которое отличается от стандартного равенства двух объектов value class – класс, который представляет какое-либо значение: Integer, Date… www. itransition. com Не переопределять Не нужно Каждый экземпляр класса по существу уникален Суперкласс уже переопределил equals и его результат подходит для текущего класса Класс с видимостью private или package private Стр. 6
Требования equals Рефлексивность Симметричность Equals x. equals(x) == true x. equals(y) => y. equals(x) Транзитивность x. equals(y) && y. equals(z) => x. equals(z) Постоянство Последовательные вызовы equals возвращают одинаковый реультат x. equals(null) == false www. itransition. com Стр. 7
Equals высокого качества Проверить является ли параметр ссылкой к текущему объекту Проверить тип с помощью instanceof Присвоить параметру тип Для каждого значимого поля в классе, проверить соответствие в параметре: (field == o. field || (field != null && field. equals(o. field))) Задуматься удовлетворяет ли полученный метод требованиям equals www. itransition. com Стр. 8
Equals высокого качества: пример public final class Phone. Number { private final short area. Code; private final short prefix; private final short line. Number; public Phone. Number(int area. Code, int prefix, int line. Number) {. . . } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Phone. Number)) { return false; } Phone. Number pn = (Phone. Number) o; return pn. line. Number == line. Number && pn. prefix == prefix && pn. area. Code == area. Code; } } www. itransition. com Стр. 9
Теперь о hash. Code ВСЕГДА переопределять hash. Code, когда переопределяется и наоборот Если x. equals(y), то x. hash. Code() == y. hash. Code() Эти принципы важны в хеш-контейнерах: Hash. Map, Hash. Set и т. д. hash. Code по-умолчанию System. identity. Hash. Code() www. itransition. com Стр. 10
Что если не переопределить class No. Hash. Code { String some. Field; No. Hash. Code(String some. Field) { this. some. Field = some. Field; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || get. Class() != o. get. Class()) return false; No. Hash. Code that = (No. Hash. Code) o; if (some. Field != null ? !some. Field. equals(that. some. Field) : that. some. Field != null) return false; return true; } } public class Hash. Code. Equals. Example { public static void main(String[] args) { Map
Плохой и хороший hash. Code Плохой Хороший @Override public int hash. Code() { return 42; } @Override public int hash. Code() { int result = 17; result = 31 * result + area. Code; result = 31 * result + prefix; result = 31 * result + line. Number; return result; } www. itransition. com Стр. 12
Enums © 2006 Itransition. Все права защищены. Стр. 13
Enums vs int constants Int constants не позволяют ослеживать смысловую правильность передачи той или иной константы компилятором public class Enums. VSInt. Constants { public static final int FONT_STYLE_BOLD = 1; public static final int FONT_STYLE_ITALIC = 2; public static final int FRUIT_APPLE = 1; public static final int FRUIT_ORANGE = 2; public static void print. Fruit. Name(int fruit. Type) { if (fruit. Type == FRUIT_ORANGE) System. out. println("Orange"); else if (fruit. Type == FRUIT_APPLE) System. out. println("Apple"); } public static void main(String[] args) { print. Fruit. Name(FONT_STYLE_BOLD); // Prints "Apple"!!! } } www. itransition. com - По хорошему данный код не должен компилироваться Стр. 14
Enums vs int constants Enum’ы намного понятнее В плане производительности небольшой overhead только при инициализации enum’а. Но это ничто! public class Enums. VSInt. Constants { enum Fruit. Type { APPLE, ORANGE } enum Font. Style { BOLD, ITALIC } // public static void main(String[] args) { print. Fruit. Type(Fruit. Type. APPLE); // Prints "Apple" - good print. Fruit. Type(Font. Style. BOLD); // Does not compile - good } public static void print. Fruit. Type(Fruit. Type fruit. Type) { if (fruit. Type == Fruit. Type. ORANGE) System. out. println("Orange"); else if (fruit. Type == Fruit. Type. APPLE) System. out. println("Apple"); } } www. itransition. com - Flawless victory! Стр. 15
А что еще есть у enum’ов Можно проитерироваться по всем элементам enum’а используя метод values(), который по умолчанию есть у всех enum’ов Метод value. Of() Параметризированные enum’ы Abstract methods www. itransition. com Стр. 16
Параметризированные enum’ы public class Parametrized. Enum. Example { enum Earth. And. Moon { EARTH(600), // * 10^22 kg MOON(7); // * 10^22 kg private int weight; Earth. And. Moon(int weight) { this. weight = weight; } public int get. Weight() { return weight; } } } www. itransition. com Стр. 17
Abstract methods в enum’ах public class Abstract. Enum. Method. Example { enum Operation { PLUS { @Override public int apply(int x, int y) { return x + y; } }, MINUS { @Override public int apply(int x, int y) { return x - y; } }, MULT { @Override public int apply(int x, int y) { return x * y; } }, DIVIDE { @Override public int apply(int x, int y) { return x / y; } }; public abstract int apply(int x, int y); } } www. itransition. com Стр. 18
Создание объектов © 2006 Itransition. Все права защищены. Стр. 19
Static factory method Имеют имена в отличие от конструкторов Могут кешировать данные, следовательно не всегда создавать новые объекты Могут возвращать экземпляр класса наследника Enum. Set – не имеет паблик конструкторов Regular. Enum. Set – возвращается, если меньше 64 элемента Jumbo. Enumset – возвращается, если больше 64 элемента Factory of type inference www. itransition. com Стр. 20
Factory of type inference - пример Map
Создание объектов Telescoping constructor Java. Bean-style Builder pattern www. itransition. com Стр. 22
Создание объектов – telescoping constructor Добавлять по параметру к каждому последующему конструктору, формируя все допустимые комбинации public class Telescoping. Constructor { private int a; private int b; private int c; public Telescoping. Constructor(int a, int b, int c) { this. a = a; this. b = b; this. c = c; } public Telescoping. Constructor(int a, int b) { this. a = a; this. b = b; Недостатки: • Нужно передавать default значения если мы не хотим инициализировать некоторые поля • Возникает путаница, которая приводит к ошибкам } public Telescoping. Constructor(int a) { this. a = a; } public Telescoping. Constructor() { } } www. itransition. com Стр. 23
Создание объектов – java bean style Использование сеттеров public class Java. Bean. Style { private int x; private int y; private int z; public void set. X(int x) { this. x = x; } public void set. Y(int y) { this. y = y; } public void set. Z(int z) { this. z = z; } Недостаток: • Создание объекта разнесено на несколько вызовов public static void main(String[] args) { Java. Bean. Style example = new Java. Bean. Style(); example. set. X(1); example. set. Y(2); example. set. Z(3); } } www. itransition. com Стр. 24
Создание объектов - Builder public class Example { private int x; private int y; private int z; public static class Example. Builder { private int x; private int y; private int z; public void set. X(int x) { this. x = x; } public void set. Y(int y) { this. y = y; } public void set. Z(int z) { this. z = z; } Достоинства: • Создание объекта происходит в одном месте • Перед вызовом build можно вставить валидацию • Есть возможность реализовать fluent interface public Example build() { Example example = new Example(); example. x = this. x; example. y = this. y; example. z = this. z; return example; } } public static void main(String[] args) { Example. Builder example. Builder = new Example. Builder(); example. Builder. set. X(1); example. Builder. set. Y(2); example. Builder. set. Z(3); Example result = example. Builder. build(); } } www. itransition. com Стр. 25
Builder – пример fluent interface public class Example { private int x; private int y; private int z; public static class Example. Builder { private int x; private int y; private int z; public Example. Builder set. X(int x) { this. x = x; return this; } public Example. Builder set. Y(int y) { this. y = y; return this; } public Example. Builder set. Z(int z) { this. z = z; return this; } public Example build() { Example example = new Example(); example. x = this. x; example. y = this. y; example. z = this. z; return example; } } public static void main(String[] args) { Example. Builder example. Builder = new Example. Builder(); example. Builder. set. X(1). set. Y(2). set. Z(3); Example result = example. Builder. build(); } } www. itransition. com Стр. 26
Более общее best practices © 2006 Itransition. Все права защищены. Стр. 27
Возвращать empty List, вместо null Уменьшает количество клиентского когда Если возвращать null, полюбому когда-нибудь упадет Null. Pointer. Exception public class Empty. List. Example { public static List
Использовать исключения по назначению НЕЛЬЗЯ использовать в качестве управляющих конструкций НЕЛЬЗЯ оставлять пустые блоки catch public class Exceptions. Wrong. Use { // DON'T EVER DO THIS! public static void main(String[] args) { String [] iterable = new String [] { "Hello", "world", "!" }; try { for (int i = 0; ; i++) { System. out. print(iterable[i]); } } catch (Array. Index. Out. Of. Bounds. Exception e) { // Done with iteration } } } www. itransition. com Стр. 29
Создавать private конструкторы для утилитных классов и singletone классов Это – защита Также следует делать данные классы final’ами public final class Utility. Class { private Utility. Class() { // Utility class. Prevent instantiation. } public static int add. Numbers(int x, int y) { return x + y; } } www. itransition. com Стр. 30
Избегать finalize Не надежно: не известно, когда выполнится Проблемы с производительность Решение: финализаторы в finally public class Finalizer. Method { public static void main(String[] args) { Buffered. Reader reader = null; try { reader = new Buffered. Reader(new File. Reader("input. txt")); while (!reader. ready()) { System. out. println(reader. read. Line()); } } catch (Exception e) { System. out. println(e); } finally { if (reader != null) { try { reader. close(); } catch (IOException e 1) { System. out. println("Error closing file. "); } } } www. itransition. com Стр. 31
Переопределение to. String() Полезно если потребуется вывести отладочную информацию Может пригодиться, если потребуется сериализация Следует переопределять в объектах домена www. itransition. com public class To. String. Override. Example { public static class Phone. Number { String area; String number; public Phone. Number(String area, String number) { this. area = area; this. number = number; } } public static class Phone. Number 2 { String area; String number; public Phone. Number 2(String area, String number) { this. area = area; this. number = number; } @Override public String to. String() { String. Builder number. Builder = new String. Builder(). append(area). append('-'). append(number); return number. Builder. to. String(); } } public static void main(String[] args) { System. out. println(new Phone. Number("029", "111 -11111")); // prints To. String. Override. Example$Phone. Number@3 ebfc 8 e 0 System. out. println(new Phone. Number 2("029", "111 -111 -11")); // prints 029 -111 -11 } } Стр. 32
Функциональный подход Изменение состояния – ключ к большинству ошибок Статические переменные – огромное зло Данный подход взят за основу в классах Big. Integer, Big. Decimal, а также в некоторых современных языках программирования Scala, Haskell www. itransition. com public final class Complex { private final double re; private final double im; public Complex(double re, double im) { this. re = re; this. im = im; } // Accessors with no corresponding mutators public double real. Part() { return re; } public double imaginary. Part() { return im; } public Complex add(Complex c) { return new Complex(re + c. re, im + c. im); } public Complex subtract(Complex c) { return new Complex(re - c. re, im - c. im); } public Complex multiply(Complex c) { return new Complex(re * c. re - im * c. im, re * c. im + im * c. re); } public Complex divide(Complex c) { double tmp = c. re * c. re + c. im * c. im; return new Complex((re * c. re + im * c. im) / tmp, (im * c. re - re * c. im) / tmp); } } Стр. 33
Composition over Inheritance Наследование нужно использовать только тогда, когда это нужно Наследование нужно использовать тогда, когда отношение между классами is-a и когда класс наследник дожен наследовать интерфейс базовго класса например: Cat is an Animal (Cat extends Animal), Rose is a Flower (Rose extends Flower) Во всех других случаях нужно использовать композицию www. itransition. com Стр. 34
Отдавать предпочтение интерфейсам нежели абстрактным классам Существующие классы могут легко быть расширены путем добавления к ним нового интерфейса Унаследоваться можно одил раз www. itransition. com Стр. 35
Не использовать tagged классы Нет Да // Tagged class - vastly inferior to a class hierarchy! class Figure { enum Shape { RECTANGLE, CIRCLE }; final Shape shape; double length; double width; double radius; Figure(double radius) { shape = Shape. CIRCLE; this. radius = radius; } Figure(double length, double width) { shape = Shape. RECTANGLE; this. length = length; this. width = width; } double area() { switch(shape) { case RECTANGLE: return length * width; case CIRCLE: return Math. PI * (radius * radius); default: throw new Assertion. Error(); } } } // Class hierarchy replacement for a tagged class abstract class Figure 1 { abstract double area(); } class Circle extends Figure 1 { final double radius; Circle(double radius) { this. radius = radius; } double area() { return Math. PI * (radius * radius); } } www. itransition. com class Rectangle extends Figure 1 { final double length; final double width; Rectangle(double length, double width) { this. length = length; this. width = width; } double area() { return length * width; } } Стр. 36
Предпочитать static nested классы Если nested классу не требуется доступ к полям окружающего класса, он всегда должен быть static Иначе, memory and performance leak public class Static. Nested. Class { public static class Good. Nested. Class { String message = "I am good"; } public class Bad. Nested. Class { String message = "Bad to the bone"; } } www. itransition. com Стр. 37
По максимуму использовать generic типы Generics - Да Расширяемость Проверка компилятором // Parameterized collection type - typesafe private final Collection
List > Array public class Array. Vs. List { public static void main(String[] args) { // Fails at runtime! Object[] object. Array = new Long[1]; object. Array[0] = "I don't fit in"; // Throws Array. Store. Exception // Won't compile! List
Defensive copies Нужно делать при возвращении mutable объектов, которые не должны изменяться (Date, List, Array и т. д. ) class Date. Container { private static Calendar calendar = Calendar. get. Instance(); static { calendar. set(2012, Calendar. DECEMBER, 21); } private static final Date NEW_ERA = calendar. get. Time(); public static Date get. New. Era() { return NEW_ERA; } } public class Date. Defensive. Copies { public static void main(String[] args) { System. out. println(Date. Container. get. New. Era()); // Prints Fri Dec 21 03: 01: 20 VET 2012 Date. Container. get. New. Era(). set. Time(11111); System. out. println(Date. Container. get. New. Era()); // Prints Wed Dec 31 20: 00: 11 VET 1969 } } www. itransition. com class Date. Container { private static Calendar calendar = Calendar. get. Instance(); static { calendar. set(2012, Calendar. DECEMBER, 21); } private static final Date NEW_ERA = calendar. get. Time(); public static Date get. New. Era() { return new Date(NEW_ERA. get. Time()); } } public class Date. Defensive. Copies { public static void main(String[] args) { System. out. println(Date. Container. get. New. Era()); // Prints Fri Dec 21 03: 01: 20 VET 2012 Date. Container. get. New. Era(). set. Time(11111); System. out. println(Date. Container. get. New. Era()); // Prints Fri Dec 21 03: 01: 20 VET 2012 } } Стр. 40
Переопределять методы с умом Что напечатает следующая программа? public class Collection. Classifier { public static String classify(Set > s) { return "Set"; } public static String classify(List > lst) { return "List"; } public static String classify(Collection > c) { return "Unknown Collection"; } public static void main(String[] args) { Collection >[] collections = { new Hash. Set
Foraech > for Использование итераторов и индексов перегружает код и заставляет программиста руками осуществлять контроль итерации, что приводит к ошибкам for (Element e : elements) { do. Something(e); } www. itransition. com Стр. 42
Использовать примитивы, вместо их boxed аналогов Использование смеси boxed и примитивов зачастую приводит к пробемам с производительность Ошибка сравнивать Boxed примитивы через == Например, программа ниже выполняется во много раз медленнее, чем могла бы public static void main(String[] args) { Long sum = 0 L; for (long i = 0; i < Integer. MAX_VALUE; i++) { sum += i; } System. out. println(sum); } www. itransition. com Стр. 43
String concatenation – использовать String. Builder Программа ниже имеет проблемы с производительностью public String statement() { String result = ""; for (int i = 0; i < num. Items(); i++) result += line. For. Item(i); // String concatenation return result; } www. itransition. com Стр. 44
КОНЕЦ © 2006 Itransition. Все права защищены. Стр. 45