第二章 创建和销毁对象
1.用静态工厂方法代替构造器
Service Provider 框架
样例代码
接口
1 2 3 public interface Animal { AnimalFactory newService () ; }
静态工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class AnimalFactory { private static final Map<String, Animal> animals = new ConcurrentHashMap <String, Animal>(); public static final String DEFAULT_ANMIAL_NAME = "<def>" ; public static void DefaultAnimal (Animal a) { registerProvider(DEFAULT_ANMIAL_NAME, a); } public static void registerProvider (String name, Animal p) { animals.put(name,p); } public static AnimalFactory newInstance () throws IllegalAccessException { return newInstance(DEFAULT_ANMIAL_NAME); } public static AnimalFactory newInstance (String name) throws IllegalAccessException { Animal a = animals.get(name); if (a == null ) throw new IllegalAccessException ( "No provider registered with name: " + name); return a.newService(); } }
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class TestMain { public static void main (String[] args) throws IllegalAccessException { AnimalFactory.registerProvider("Cat" , Cat); AnimalFactory.registerProvider("Dog" , Dog); AnimalFactory Cat = AnimalFactory.newInstance("Cat" ); AnimalFactory Dog = AnimalFactory.newInstance("Dog" ); System.out.println(Cat); System.out.println(Dog); } private static final Animal Cat = new Animal () { public AnimalFactory newService () { return new AnimalFactory () { @Override public String toString () { return "This is Cat" ; } }; } }; private static final Animal Dog = new Animal () { @Override public AnimalFactory newService () { return new AnimalFactory () { @Override public String toString () { return "This is Dog" ; } }; } }; }
2.遇到多个构造器参数用构造器
JavaBeans模式
Builder模式
适用于类层次结构
创建Builder对象引入额外开销
Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public abstract class Person { public final String mName; public int mAge; public String mLocation; public String mGender; public String mJob; public static abstract class Builder <T extends Builder <T>> { private final String mName; private int mAge; private String mLocation; private String mGender; private String mJob; protected Builder (String mName) { this .mName = mName; } public abstract Person build () ; protected abstract T self () ; public T SetmAge (int mAge) { this .mAge = mAge; return self(); } public T SetmLocation (String mLocation) { this .mLocation = mLocation; return self(); } public T SetmGender (String mGender) { this .mGender = mGender; return self(); } public T SetmJob (String mJob) { this .mJob = mJob; return self(); } } Person(Builder<?> builder) { this .mAge = builder.mAge; this .mName = builder.mName; this .mGender = builder.mGender; this .mLocation = builder.mLocation; this .mJob = builder.mJob; } }
Hight类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hight extends Person { public double mHight; public static class Builder extends Person .Builder<Builder>{ private double mHight; protected Builder (String mName) { super (mName); } @Override public Hight build () { return new Hight (this ); } @Override protected Builder self () { return this ; } public Builder SetmHight (int mHight) { this .mHight = mHight; return self(); } } private Hight (Builder builder) { super (builder); mHight = builder.mHight; } @Override public String toString () { return "名字:" + mName + "\n" + "性别:" + mGender + "\n" + "年龄:" + mAge + "\n" + "身高:" + mHight + "cm" + "\n" + "职业:" + mJob + "\n" + "地址:" + mLocation + "\n" ; } }
Weight类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Weight extends Person { public double mWeight; public static class Builder extends Person .Builder<Builder>{ private double mWeight; protected Builder (String mName) { super (mName); } @Override public Weight build () { return new Weight (this ); } @Override protected Builder self () { return this ; } public Builder SetmWeight (int mWeight) { this .mWeight = mWeight; return self(); } } private Weight (Builder builder) { super (builder); mWeight = builder.mWeight; } @Override public String toString () { return "名字:" + mName + "\n" + "性别:" + mGender + "\n" + "年龄:" + mAge + "\n" + "体重:" + mWeight + "kg" + "\n" + "职业:" + mJob + "\n" + "地址:" + mLocation + "\n" ; } }
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TestMain { public static void main (String[] args) { Hight h = new Hight .Builder("张三" ). SetmHight(175 ).SetmAge(24 ).SetmLocation("深圳" ). SetmJob("学生" ).SetmGender("男" ).build(); Weight w = new Weight .Builder("李四" ). SetmWeight(80 ).SetmAge(24 ).SetmLocation("广州" ). SetmJob("学生" ).SetmGender("男" ).build(); System.out.println(h); System.out.println(w); } }
3.用私有构造器或枚举类型强化Singleton属性
Singleton:只实例化一次
实现Singleton
1 2 3 4 5 6 public class Emperor { public static final Emperor INSTANCE = new Emperor (); private Emperor () { System.out.println("新建一个皇帝类" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class TestMain { public static void main (String[] args) { Emperor emperor = Emperor.INSTANCE; Emperor emperor1 = Emperor.INSTANCE; if (emperor1.equals(emperor)) { System.out.println("具有单例属性" ); } else { System.out.println("不具有单例属性" ); } } }
公有成员是静态工厂方法
灵活性
泛型Singleton工厂
通过方法引用作为提供者
包含单个元素的枚举类型
4.通过私有构造器强化不可实例化的能力
工具类
无需实例化
不可为抽象类,可被子类化,子类可被实例化
5.优先考虑依赖注入来引用资源
某些类会依赖底层资源
6.避免创建不必要的对象
同时提供了静态工厂方法和构造器的不可变类
静态工厂方法优先
避免无意识的装箱
重用已知不会被修改的可变对象
7.消除过期对象引用
Java:垃圾回收功能(过期引用)
第三章 对所有类都通用的方法
10.覆盖equals方法的通用约定
equals:判断两个示例是否(逻辑)相等
object中的equals方法:判断是否为同一个实例
无需覆盖
类的每个实例确实是唯一的,例如Thread
无需关心实例之间是否逻辑相等
超类已覆盖
类或包是私有的
需要覆盖
覆盖约定
自反性:对于非空x,有x.equals(x)返回true
对称性
传递性
一致性:对于非空x,y,当内容不变时,x.euqals(y)不变
非空性:对于非空x,x.equals(null),返回false
高质量equals方法:
==判断是否为该对象的引用
instanceof判断类型是否正确
转换参数
对域和对象校验是否匹配
满足对称性,传递性,一致性
11.覆盖equals时一定要覆盖hashcode
得以使用HashMap,HashSet,HashTable
对于不可变类缓存HashCode
12.始终要覆盖toString
13.谨慎覆盖clone方法
cloneable接口决定了Object中受保护的clone方法实现的行为
一个类实现了cloneable接口,Object的clone方法返回该对象的逐域拷贝,否则抛出异常
改变了超类中受保护的方法的行为
14.考虑实现Comparable接口
满足equals约定:自反性,对称性,传递性
或者使用Comparator
1 2 3 4 5 6 7 8 private static final Comparator < PhoneNumber > COMPARATOR = comparingInt((PhoneNumber pn) - > pn.areaCode) .thenComparingInt(pn - > pn.prefix) .thenComparingInt(pn - > pn.lineNum); public int compareTo (PhoneNumber pn) { return COMPARATOR.compare(this , pn); }
代码样例:
Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Person { public String id; public String name; int age; public String job; public Person (String id, String name, int age, String job) { this .id = id; this .name = name; this .age = age; this .job = job; } @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Person person = (Person) o; return age = = person.age && id.equals(person.id) && name.equals(person.name) && job.equals(person.job); } @Override public int hashCode () { int result = id.hashCode(); result = 31 * result + name.hashCode(); result = 31 * result + Integer.hashCode(age); result = 31 * result + job.hashCode(); return result; } @Override public String toString () { return "Person{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", age=" + age + ", job='" + job + '\'' + '}' ; } }
Phone类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Phone implements Cloneable { private Screen screen; private static class Screen { double length; double weight; Screen(double length, double weight) { this .length = length; this .weight = weight; } Screen deepcopy () { return new Screen (this .length, this .weight); } } @Override public Phone clone () throws CloneNotSupportedException { Phone result = (Phone)super .clone(); result.screen = this .screen.deepcopy(); return result; } public int comparaTo (Phone p) { return Double.compare(this .screen.length*this .screen.weight, p.screen.length*p.screen.weight); } public Phone (Phone phone) { this .screen.weight = phone.screen.weight; this .screen.length = phone.screen.length; } }
主函数
1 2 3 4 5 6 7 public class TestMain { public static void main (String[] args) { Person person = new Person ("123456" , "张三" , 24 , "学生" ); System.out.println(person); System.out.format("0x%x\n" , person.hashCode()); } }
第四章 类与接口
15.使类和成员的可访问性最小化
信息隐藏/封装
信息隐藏的好处
解耦
各个模块可以独立开发测试优化
提高模块的可重用性
尽可能使每个类或成员不被外界访问
顶层类和接口
包级私有
公有(public)
Public修饰符
包导出的API的一部分
后续版本需要对其进行支持
若某顶层类(或接口)A只在另一个类B的内部用到,应使A成为B的私有嵌套类
降低不必要公有类的可访问性更重要
成员(域,方法,嵌套类,嵌套接口)
私有的(private)
包级私有的(package-private)
受保护的(protected)
声明该成员的类的子类可访问
包内部的任何类都可访问
公有的(public)
尽量减少包级私有(相对于私有)
受保护的成员尽量少用(相对于私有)
实例域不能是公有的
静态final常量可以为公有,但不能为可变对象的引用
16.在公有类中使用访问方法而非公有域
17.使可变性最小化
18.复合优先于继承
包的内部可以使用,在同一个程序员的控制下
专门为继承而设计
复合
20.接口优于抽象类
接口(interface)
抽象类(abstract class)
接口可以方便的实现混合类型,并构造非层次结构的类型框架
结合接口和抽象类的优点:骨架实现
接口负责定义类型,或提供一些缺省方法
骨架类实现其余方法
定义接口
1 2 3 public interface Machine { void setPrice (int price) ; }
1 2 3 4 public interface Movable { int start () ; int stop () ; }
1 2 3 public interface Vehicle extends Machine , Movable{ int run () ; }
Bus实现骨架类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Bus implements Vehicle { private int isRun; public int price; @Override public void setPrice (int price) { this .price = price; } @Override public int start () { if (isRun == 0 ) { isRun = 1 ; return 1 ; } else { return 0 ; } } @Override public int stop () { if (isRun == 1 ) { isRun = 0 ; return 1 ; } else { return 0 ; } } @Override public int run () { if (start() != 0 ) { System.out.println("启动成功" ); return 1 ; } else { System.out.println("已启动" ); return 0 ; } } }
21.为后代设计接口
22.接口只用于定义类型
工具类
1 2 3 4 5 6 7 8 9 10 11 12 public class PhysicalConstants { private PhysicalConstants () { } public static final double AVOGADROS_NUMBER = 6.02214199e23 ; public static final double BOLTZMANN_CONSTANT = 1.3806503e-23 ; public static final double ELECTRON_MASS = 9.10938188e-31 ; } public class Test { double atoms (double mols) { return AVOGADROS_NUMBER * mols; }
23.类层次优于标签类
类层次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 abstract class Figure { abstract double area () ; } class Circle extends Figure { final double radius; Circle(double radius) { this .radius = radius; } double area () { return Math.PI * (radius * radius); } } class Rectangle extends Figure { final double length; final double width; Rectangle(double length, double width) { this .length = length; this .width = width; } double area () { return length * width; } } class Square extends Rectangle { Square(double side) { super (side, side); } }
24.优先考虑静态成员类
嵌套类
匿名类
在使用的同时被声明和实例化
可以出现在任何允许出现表达式的地方
没有静态成员
无法实现多个接口,或者同时扩展类并实现接口
保持简短,否则影响可读性
创建函数对象
第二章 创建和销毁对象
用静态工厂方法代替构造器
Service Provider 框架
样例代码
接口
1 2 3 public interface Animal { AnimalFactory newService () ; }
静态工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class AnimalFactory { private static final Map<String, Animal> animals = new ConcurrentHashMap <String, Animal>(); public static final String DEFAULT_ANMIAL_NAME = "<def>" ; public static void DefaultAnimal (Animal a) { registerProvider(DEFAULT_ANMIAL_NAME, a); } public static void registerProvider (String name, Animal p) { animals.put(name,p); } public static AnimalFactory newInstance () throws IllegalAccessException { return newInstance(DEFAULT_ANMIAL_NAME); } public static AnimalFactory newInstance (String name) throws IllegalAccessException { Animal a = animals.get(name); if (a == null ) throw new IllegalAccessException ( "No provider registered with name: " + name); return a.newService(); } }
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class TestMain { public static void main (String[] args) throws IllegalAccessException { AnimalFactory.registerProvider("Cat" , Cat); AnimalFactory.registerProvider("Dog" , Dog); AnimalFactory Cat = AnimalFactory.newInstance("Cat" ); AnimalFactory Dog = AnimalFactory.newInstance("Dog" ); System.out.println(Cat); System.out.println(Dog); } private static final Animal Cat = new Animal () { public AnimalFactory newService () { return new AnimalFactory () { @Override public String toString () { return "This is Cat" ; } }; } }; private static final Animal Dog = new Animal () { @Override public AnimalFactory newService () { return new AnimalFactory () { @Override public String toString () { return "This is Dog" ; } }; } }; }
遇到多个构造器参数用构造器
JavaBeans模式
Builder模式
适用于类层次结构
创建Builder对象引入额外开销
Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public abstract class Person { public final String mName; public int mAge; public String mLocation; public String mGender; public String mJob; public static abstract class Builder <T extends Builder <T>> { private final String mName; private int mAge; private String mLocation; private String mGender; private String mJob; protected Builder (String mName) { this .mName = mName; } public abstract Person build () ; protected abstract T self () ; public T SetmAge (int mAge) { this .mAge = mAge; return self(); } public T SetmLocation (String mLocation) { this .mLocation = mLocation; return self(); } public T SetmGender (String mGender) { this .mGender = mGender; return self(); } public T SetmJob (String mJob) { this .mJob = mJob; return self(); } } Person(Builder<?> builder) { this .mAge = builder.mAge; this .mName = builder.mName; this .mGender = builder.mGender; this .mLocation = builder.mLocation; this .mJob = builder.mJob; } }
Hight类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Hight extends Person { public double mHight; public static class Builder extends Person .Builder<Builder>{ private double mHight; protected Builder (String mName) { super (mName); } @Override public Hight build () { return new Hight (this ); } @Override protected Builder self () { return this ; } public Builder SetmHight (int mHight) { this .mHight = mHight; return self(); } } private Hight (Builder builder) { super (builder); mHight = builder.mHight; } @Override public String toString () { return "名字:" + mName + "\n" + "性别:" + mGender + "\n" + "年龄:" + mAge + "\n" + "身高:" + mHight + "cm" + "\n" + "职业:" + mJob + "\n" + "地址:" + mLocation + "\n" ; } }
Weight类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Weight extends Person { public double mWeight; public static class Builder extends Person .Builder<Builder>{ private double mWeight; protected Builder (String mName) { super (mName); } @Override public Weight build () { return new Weight (this ); } @Override protected Builder self () { return this ; } public Builder SetmWeight (int mWeight) { this .mWeight = mWeight; return self(); } } private Weight (Builder builder) { super (builder); mWeight = builder.mWeight; } @Override public String toString () { return "名字:" + mName + "\n" + "性别:" + mGender + "\n" + "年龄:" + mAge + "\n" + "体重:" + mWeight + "kg" + "\n" + "职业:" + mJob + "\n" + "地址:" + mLocation + "\n" ; } }
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class TestMain { public static void main (String[] args) { Hight h = new Hight .Builder("张三" ). SetmHight(175 ).SetmAge(24 ).SetmLocation("深圳" ). SetmJob("学生" ).SetmGender("男" ).build(); Weight w = new Weight .Builder("李四" ). SetmWeight(80 ).SetmAge(24 ).SetmLocation("广州" ). SetmJob("学生" ).SetmGender("男" ).build(); System.out.println(h); System.out.println(w); } }
用私有构造器或枚举类型强化Singleton属性
Singleton:只实例化一次
实现Singleton
1 2 3 4 5 6 public class Emperor { public static final Emperor INSTANCE = new Emperor (); private Emperor () { System.out.println("新建一个皇帝类" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class TestMain { public static void main (String[] args) { Emperor emperor = Emperor.INSTANCE; Emperor emperor1 = Emperor.INSTANCE; if (emperor1.equals(emperor)) { System.out.println("具有单例属性" ); } else { System.out.println("不具有单例属性" ); } } }
公有成员是静态工厂方法
灵活性
泛型Singleton工厂
通过方法引用作为提供者
包含单个元素的枚举类型
通过私有构造器强化不可实例化的能力
工具类
无需实例化
不可为抽象类,可被子类化,子类可被实例化
优先考虑依赖注入来引用资源
某些类会依赖底层资源
避免创建不必要的对象
同时提供了静态工厂方法和构造器的不可变类
静态工厂方法优先
避免无意识的装箱
重用已知不会被修改的可变对象
7.消除过期对象引用
Java:垃圾回收功能(过期引用)
第三章 对所有类都通用的方法
10.覆盖equals方法的通用约定
equals:判断两个示例是否(逻辑)相等
object中的equals方法:判断是否为同一个实例
无需覆盖
类的每个实例确实是唯一的,例如Thread
无需关心实例之间是否逻辑相等
超类已覆盖
类或包是私有的
需要覆盖
覆盖约定
自反性:对于非空x,有x.equals(x)返回true
对称性
传递性
一致性:对于非空x,y,当内容不变时,x.euqals(y)不变
非空性:对于非空x,x.equals(null),返回false
高质量equals方法:
==判断是否为该对象的引用
instanceof判断类型是否正确
转换参数
对域和对象校验是否匹配
满足对称性,传递性,一致性
11.覆盖equals时一定要覆盖hashcode
得以使用HashMap,HashSet,HashTable
对于不可变类缓存HashCode
12.始终要覆盖toString
13.谨慎覆盖clone方法
cloneable接口决定了Object中受保护的clone方法实现的行为
一个类实现了cloneable接口,Object的clone方法返回该对象的逐域拷贝,否则抛出异常
改变了超类中受保护的方法的行为
14.考虑实现Comparable接口
满足equals约定:自反性,对称性,传递性
或者使用Comparator
1 2 3 4 5 6 7 8 private static final Comparator < PhoneNumber > COMPARATOR = comparingInt((PhoneNumber pn) - > pn.areaCode) .thenComparingInt(pn - > pn.prefix) .thenComparingInt(pn - > pn.lineNum); public int compareTo (PhoneNumber pn) { return COMPARATOR.compare(this , pn); }
代码样例:
Person类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Person { public String id; public String name; int age; public String job; public Person (String id, String name, int age, String job) { this .id = id; this .name = name; this .age = age; this .job = job; } @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Person person = (Person) o; return age = = person.age && id.equals(person.id) && name.equals(person.name) && job.equals(person.job); } @Override public int hashCode () { int result = id.hashCode(); result = 31 * result + name.hashCode(); result = 31 * result + Integer.hashCode(age); result = 31 * result + job.hashCode(); return result; } @Override public String toString () { return "Person{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", age=" + age + ", job='" + job + '\'' + '}' ; } }
Phone类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Phone implements Cloneable { private Screen screen; private static class Screen { double length; double weight; Screen(double length, double weight) { this .length = length; this .weight = weight; } Screen deepcopy () { return new Screen (this .length, this .weight); } } @Override public Phone clone () throws CloneNotSupportedException { Phone result = (Phone)super .clone(); result.screen = this .screen.deepcopy(); return result; } public int comparaTo (Phone p) { return Double.compare(this .screen.length*this .screen.weight, p.screen.length*p.screen.weight); } public Phone (Phone phone) { this .screen.weight = phone.screen.weight; this .screen.length = phone.screen.length; } }
主函数
1 2 3 4 5 6 7 public class TestMain { public static void main (String[] args) { Person person = new Person ("123456" , "张三" , 24 , "学生" ); System.out.println(person); System.out.format("0x%x\n" , person.hashCode()); } }
第四章 类与接口
15.使类和成员的可访问性最小化
信息隐藏/封装
信息隐藏的好处
解耦
各个模块可以独立开发测试优化
提高模块的可重用性
尽可能使每个类或成员不被外界访问
顶层类和接口
包级私有
公有(public)
Public修饰符
包导出的API的一部分
后续版本需要对其进行支持
若某顶层类(或接口)A只在另一个类B的内部用到,应使A成为B的私有嵌套类
降低不必要公有类的可访问性更重要
成员(域,方法,嵌套类,嵌套接口)
私有的(private)
包级私有的(package-private)
受保护的(protected)
声明该成员的类的子类可访问
包内部的任何类都可访问
公有的(public)
尽量减少包级私有(相对于私有)
受保护的成员尽量少用(相对于私有)
实例域不能是公有的
静态final常量可以为公有,但不能为可变对象的引用
16.在公有类中使用访问方法而非公有域
17.使可变性最小化
18.复合优先于继承
包的内部可以使用,在同一个程序员的控制下
专门为继承而设计
复合
20.接口优于抽象类
接口(interface)
抽象类(abstract class)
接口可以方便的实现混合类型,并构造非层次结构的类型框架
结合接口和抽象类的优点:骨架实现
接口负责定义类型,或提供一些缺省方法
骨架类实现其余方法
定义接口
1 2 3 public interface Machine { void setPrice (int price) ; }
1 2 3 4 public interface Movable { int start () ; int stop () ; }
1 2 3 public interface Vehicle extends Machine , Movable{ int run () ; }
Bus实现骨架类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Bus implements Vehicle { private int isRun; public int price; @Override public void setPrice (int price) { this .price = price; } @Override public int start () { if (isRun == 0 ) { isRun = 1 ; return 1 ; } else { return 0 ; } } @Override public int stop () { if (isRun == 1 ) { isRun = 0 ; return 1 ; } else { return 0 ; } } @Override public int run () { if (start() != 0 ) { System.out.println("启动成功" ); return 1 ; } else { System.out.println("已启动" ); return 0 ; } } }
21.为后代设计接口
22.接口只用于定义类型
工具类
1 2 3 4 5 6 7 8 9 10 11 12 public class PhysicalConstants { private PhysicalConstants () { } public static final double AVOGADROS_NUMBER = 6.02214199e23 ; public static final double BOLTZMANN_CONSTANT = 1.3806503e-23 ; public static final double ELECTRON_MASS = 9.10938188e-31 ; } public class Test { double atoms (double mols) { return AVOGADROS_NUMBER * mols; }
23.类层次优于标签类
类层次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 abstract class Figure { abstract double area () ; } class Circle extends Figure { final double radius; Circle(double radius) { this .radius = radius; } double area () { return Math.PI * (radius * radius); } } class Rectangle extends Figure { final double length; final double width; Rectangle(double length, double width) { this .length = length; this .width = width; } double area () { return length * width; } } class Square extends Rectangle { Square(double side) { super (side, side); } }
24.优先考虑静态成员类
嵌套类
匿名类
在使用的同时被声明和实例化
可以出现在任何允许出现表达式的地方
没有静态成员
无法实现多个接口,或者同时扩展类并实现接口
保持简短,否则影响可读性
创建函数对象
局部类
在可以声明局部变量的地方声明
有名字,可被重复使用
没有静态成员
保持简短,否则影响可读性
25.限制源文件为单个顶级类
第五章 泛型
为什么引入泛型
与C++ template类似,但目的有区别
减少程序运行错误的发生
减少方法的重载
命名规则
E - Element (通常代表集合类中的元素)
K - Key
N - Number
V - Value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Util { public static <K, V> boolean compare (Pair<K,V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey())&& p1.getValue().equals(p2.getValue()); } } Pair<Integer,String> p1 = new Pair <>(1 , "apple" ); Pair<Integer,String> p2 = new Pair <>(2 , "pear" ); boolean same = Util.<Integer, String>compare(p1, p2); boolean same2= Util. compare(p1, p2);
限制类型参数在某个范围内
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Box <T extends Number >{ private T t; public Box (T t) { this .t = t; } public void print () { System.out.println(t.getClass().getName()); } public static void main (String[] args) { Box<Integer> box1 = new Box <Integer>(new Integer (2 )); box1.print(); Box<Double> box2 = new Box <Double>(new Double (1.2 )); box2.print(); Box<String> box2 = new Box <String>(new String ("abc" )); box2.print();
1 2 3 4 class Box <T extends Number & Cloneable & Comparable >{ …… }
泛型类的继承
1 2 3 4 5 6 7 8 9 10 11 12 13 Object someObject = new Object ();Integer someInteger = new Integer (10 );someObject = someInteger; Box < Number > box = new Box < Number > (); box.add(new Integer (10 )); box.add(new Double (10.1 )); public void boxTest (Box < Number > n) { ……} boxTest(Box < Integer > ); boxTest(Box < Double > );
通配符
1 2 3 4 5 6 7 8 9 10 public void boxTest (Box < Number > n) { ……} boxTest(Box < Integer > ); boxTest(Box < Double > ); public void boxTest (Box < ? extends Number > n) { }
26.不要使用原生态类型
原生态类型(raw type)
不带任何实际类型参数的泛型名称
每个泛型都定一个原生态类型
List<E>的原生态类型是List
27.消除非受检警告
改写代码
@SuppressWarnings(“unchecked”)
无法通过修改代码消除警告
证明代码是安全的
在尽可能小得范围内使用
添加对应注释
28.列表优先数组
数组与泛型的不同
数组是协变的,泛型是不可变的
数组是具体化的(reified),泛型是通过擦除(erasure)来实现的
数组和泛型不能很好的混合使用
无法创建泛型(List<E>[])、参数化类型(List<String>[])或类型参数(E[])的数组
优先利用集合类型list<E>,而不是数组类型E[]
29.优先考虑泛型
不能创建不可具体化类型的数组
方法1:创建Object数组,转换为泛型数组类型
方法2:将elements域的类型从E[]改为Object[]。获取元素时进行类型转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Stack <E> { private Object[] elements; private int size = 0 ; private static final int DEFAULT_INITIAL_CAPACITY = 16 ; public Stack () { elements = new Object [DEFAULT_INITIAL_CAPACITY]; } public void push (E e) { ensureCapacity(); elements[size++] = e; } public E pop () { if (size == 0 ) throw new EmptyStackException (); @SuppressWarnings("unchecked") E result = (E) elements[--size]; elements[size] = null ; return result; }
30.优先考虑泛型方法
编写泛型方法与编写泛型类型类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static < E > Set < E > union(Set < E > s1, Set < E > s2) { Set < E > result = new HashSet < > (s1); result.addAll(s2); return result; } public static void main (String[] args) { Set < String > guys = Set.of("Tom" , "Dick" , "Harry" ); Set < String > stooges = Set.of("Larry" , "Moe" , "Curly" ); Set < String > aflCio = union(guys, stooges); System.out.println(aflCio); }
泛型单例工厂
递归类型限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class RecursiveTypeBound { public static <E extends Comparable <E>> E max (Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException ("Empty collection" ); E result = null ; for (E e : c) if (result == null || e.compareTo(result) > 0 ) result = Objects.requireNonNull(e); return result; } public static void main (String[] args) { List<String> argList = Arrays.asList(args); System.out.println(max(argList)); } }
31.利用有限制的通配符来提升API的灵活性
33.优先考虑类型安全的异构容器
容器需要更多的灵活性
将键(key)参数化,而不是将容器(container)参数化
第六章 枚举和注解
34.用enum代替int常量
枚举类型
提供编译时的类型安全
有命名空间,可以有同名常量
可以添加任意的方法和域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public enum Operation { PLUS("+" ) { public double apply (double x, double y) { return x + y; } }, MINUS("-" ) { public double apply (double x, double y) { return x - y; } }, TIMES("*" ) { public double apply (double x, double y) { return x * y; } }, DIVIDE("/" ) { public double apply (double x, double y) { return x / y; } }; private final String symbol; Operation(String symbol) { this .symbol = symbol; } @Override public String toString () { return symbol; } public abstract double apply (double x, double y) ; }
1 2 3 4 5 public class Welcome { public static void main (String[] args) { System.out.println(Operation.PLUS.apply(1 ,2 )); } }
35.用实例域代替序数
1 2 3 4 5 6 7 8 9 10 11 12 public enum Ensemble { SOLO(1 ), DUET(2 ), TRIO(3 ), QUARTET(4 ), QUINTET(5 ), SEXTET(6 ), SEPTET(7 ), OCTET(8 ), DOUBLE_QUARTET(8 ), NONET(9 ), DECTET(10 ), TRIPLE_QUARTET(12 ); private final int numberOfMusicians; Ensemble(int size) { this .numberOfMusicians = size; } public int numberOfMusicians () { return numberOfMusicians; } }
36.用EnumSet代替位域
1 2 3 4 5 6 7 8 9 public class Text { public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH } public void applyStyles (Set < Style > styles) { ... } } text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
37.用EnumMap代替序数索引
枚举类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class MixColor { public enum Color { Red("Red" ), Blue("Blue" ), Green("Green" ), Cyan("Cyan" ), Purple("Purple" ), White("White" ), Yellow("Yellow" ); private final String symbol; Color(String symbol) { this .symbol = symbol; } @Override public String toString () { return symbol; } } public static final Map<EnumSet<Color>, Color> m = new HashMap <>(); public static void applyStyles () { m.put(EnumSet.of(Color.Red), Color.Red); m.put(EnumSet.of(Color.Blue), Color.Blue); m.put(EnumSet.of(Color.Green), Color.Green); m.put(EnumSet.of(Color.Red, Color.Green), Color.Yellow); m.put(EnumSet.of(Color.Green, Color.Blue), Color.Cyan); m.put(EnumSet.of(Color.Red, Color.Blue), Color.Purple); m.put(EnumSet.of(Color.Red, Color.Blue, Color.Green), Color.White); } MixColor() { applyStyles(); } }
主函数
1 2 3 4 5 6 public class Main { public static void main (String[] args) { MixColor mixColor = new MixColor (); System.out.println(MixColor.m.get(EnumSet.of(MixColor.Color.Blue, MixColor.Color.Red))); } }
38.用接口模拟可伸缩的枚举类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface Operation { double apply (double x, double y) ; } public enum BasicOperation implements Operation { PLUS("+" ) { public double apply (double x, double y) { return x + y; } }, MINUS("-" ) { public double apply (double x, double y) { return x - y; } }, TIMES("*" ) { public double apply (double x, double y) { return x * y; } }, DIVIDE("/" ) { public double apply (double x, double y) { return x / y; } }; private final String symbol; BasicOperation(String symbol) { this .symbol = symbol; } @Override public String toString () { return symbol; } }
39.注解优先于命名模式
40.坚持使用Override注解
第七章 Lamda和Stream
42.Lambda优先于匿名类
Lambda表达式
代码简短
无需名称和文档
不可创建或继承抽象类的实例
不适用于创建具有多个抽象方法的接口实例
1 2 3 4 5 6 7 8 9 10 11 Collections.sort(words, new Comparator <String>() { public int compare (String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }); Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
43.方法引用优先于Lambda
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Freq { public static void main (String[] args) { Map<String, Integer> frequencyTable = new TreeMap <>(); for (String s : args) frequencyTable.merge(s, 1 , (count, incr) -> count + incr); System.out.println(frequencyTable); frequencyTable.clear(); for (String s : args) frequencyTable.merge(s, 1 , Integer::sum); System.out.println(frequencyTable); } } default V merge (K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); V oldValue = get(key); V newValue = (oldValue == null ) ? value : remappingFunction.apply(oldValue, value); if (newValue == null ) { remove(key); } else { put(key, newValue); } return newValue; }
44.优先使用标准函数接口
函数对象:实现了特定函数的对象
45.谨慎使用Stream
Stream是元素的集合,支持对元素进行顺序或者并行操作
foreach:
filter:
map:
sorted:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Random random = new Random ();random.ints().limit(10 ).forEach(System.out::println); List<String>strings = Arrays.asList("abc" , "" , "bc" , "efg" , "abcd" ,"" , "jkl" ); long count = strings.stream().filter(string -> string.isEmpty()).count();List<Integer> numbers = Arrays.asList(3 , 2 , 2 , 3 , 7 , 3 , 5 ); List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList()); Random random = new Random ();random.ints().limit(10 ).sorted().forEach(System.out::println);
避免使用Stream处理Char
46.优先选择Stream中无副作用的函数
第八章 方法
49.检查参数的有效性
对参数值进行限制
50.必要时进行保护性拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public final class RaceTime { private final List<Date> times; public RaceTime (Date... end) { times = new ArrayList <>(); for (Date date : end) { Date tmp = new Date (date.getTime()); times.add(tmp); } } public Date retTime (int i) { if (i > times.size() || i < 0 ) { throw new IllegalArgumentException ( "out of index" ); } return new Date (times.get(i).getTime()); } }
51.谨慎设计方法签名
谨慎选择方法的名称
不要过于追求提供便利的方法
避免过长的参数列表
52.明智地使用重载
在java中,具体调用的重载方法是编译时决定的
重载方法的选择是静态的,被覆盖的方法的选择是动态的
请勿写出具有相同参数数目的重载方法
53.明智地使用可变参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static int min (int ... args) { if (args.length == 0 ) throw new IllegalArgumentException ("Too few arguments" ); int min = args[0 ]; for (int i = 1 ; i < args.length; i++) if (args[i] < min) min = args[i]; return min; } static int min (int firstArg, int ... remainingArgs) { int min = firstArg; for (int arg : remainingArgs) if (arg < min) min = arg; return min; }
54.返回零长度的数组或者集合而不是null
1 2 3 4 5 6 private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese [0 ];public Cheese[] getCheeses() { return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY); }
55.明智地返回optional
方法在某些时候无法返回任何值
抛出异常
返回null
返回Optional<T>
1 2 3 4 5 6 7 8 9 10 public static <E extends Comparable <E>> E max (Collection<E> c) { if (c.isEmpty()) throw new IllegalArgumentException ("Empty collection" ); E result = null ; for (E e : c) if (result == null || e.compareTo(result) > 0 ) result = Objects.requireNonNull(e); return result; }
1 2 3 4 5 6 7 8 9 10 11 public static <E extends Comparable <E>> Optional<E> max (Collection<E> c) { if (c.isEmpty()) return Optional.empty(); E result = null ; for (E e : c) if (result == null || e.compareTo(result) > 0 ) result = Objects.requireNonNull(e); return Optional.of(result); }
Stream的终止操作返回Optional
1 2 3 4 public static <E extends Comparable <E>> Optional<E> max (Collection<E> c) { return c.stream().max(Comparator.naturalOrder()); }
第九章 通用编程
57.将局部变量的作用域最小化
与“使类和成员的可访问性最小化”本质上类似
在第一次使用局部变量的地方进行声明
局部变量的声明尽量包含一个初始化表达式
58.for-each循环优于传统循环
for-each循环可遍历任何实现Iterable接口的对象
无法使用for-each循环的情况
解构过滤:遍历集合,删除特定元素
转换:遍历集合,替换特定元素
并行迭代:并行的遍历多个集合,同步前进
59.了解和使用类库
使用标准类库的好处
其实现经过检验,且会不断更新
无需花费时间在底层细节上
性能往往会逐渐提高
方便其他人阅读,重用
总之,不要重新发明轮子
60.如果需要精确的答案,避免使用float和double
float和double并没有提供完全精确的结果,使用BigDecimal
61.基本类型优先于装箱基本类型
基本类型:int,double,Boolean
引用类型:String,List
装箱基本类型:Integer,Double,Boolean
自动装箱,自动拆箱
基本类型和装箱类型的区别
基本类型只有值比较,装箱基本类型具有同一性比较(identity comparison)
基本类型只有功能值,装箱基本类型有null
基本类型比装箱基本类型节省时间和空间
何时应采用装箱基本类型
作为集合中的元素、键和值
参数化类型中,必须使用装箱基本类型作为类型参数
进行反射的方法调用,必须使用装箱基本类型
62.若其他类型更适合,尽量避免使用字符串
63.注意字符串连接的性能
64.通过接口引用对象
65.接口优先于反射机制
核心反射机制:通过程序来访问关于已装载的类的信息
执行反射访问所需要的代码非常笨拙和冗长
应用:frida
66.谨慎地使用本地方法
本地方法:用本地程序设计语言(如C或C++)编写的方法
本地方法可方便完成一些特殊的任务,并可能提高性能
当Java本身的性能得到提升时,程序无法得到改善
67.谨慎地进行优化
68.遵守普遍接受的命名惯例