前言
《Effective Java》中文第三版,是一本关于Java基础的书,这本书不止一次有人推荐我看。其中包括我很喜欢的博客园博主五月的仓颉,他曾在自己的博文《给Java程序猿们推荐一些值得一看的好书》中也推荐过。加深自己的记忆,同时向优秀的人看齐,决定在看完每一章之后,都写一篇随笔。如果有写的不对的地方、表述的不清楚的地方、或者其他建议,希望您能够留言指正,谢谢。
《Effective Java》中文第三版在线阅读链接:https://github.com/sjsdfg/effective-java-3rd-chinese/tree/master/docs/notes
什么是构造方法?
定义:
- 一个在创建对象时自动被调用的方法。
特点:
- 构造方法的名称和类同名。
- 没有返回值类型,即不能使用 return 关键字。
- 普通方法不能以任何形式调用构造方法(构造方法中可以调用普通方法)。
注意:
- 当类中没有定义构造方法时,系统会默认添加一个无参的构造方法,当在类中定义构造方法的时候,默认的构造方法会消失。
实例:
public class Cat {
//名字
private String name;
//颜色
private String color;
//年龄
private Integer age;
//构造方法的名称和类同名
public Cat(){
}
//带一个参数的构造方法
public Cat(String name){
this.name = name;
//普通方法不能以任何形式调用构造方法,构造方法中可以调用普通方法
commonMethod();
}
//有两个参数的构造方法
public Cat(String name, String color){
this.name = name;
this.color = color;
}
public void commonMethod(){
}
}
什么是静态工厂方法?
定义:
- 在类中提供一个公共的静态方法,通过这个静态方法,对外提供自身实例。
实例:
public class Cat {
//名字
private String name;
//颜色
private String color;
//年龄
private Integer age;
//创建只有名字的猫,带有一个构造参数
public static Cat createCatWithName(String name){
Cat cat = new Cat();
cat.name = name;
return cat;
}
//创建只有颜色的猫,带有一个构造参数
public static Cat createCatWithColor(String color){
Cat cat = new Cat();
cat.color = color;
return cat;
}
//创建有名字、颜色、年龄的猫
public static Cat createCatWithAllParam(String name, String color, Integer age){
Cat cat = new Cat();
cat.age = age;
cat.name = name;
cat.color = color;
return cat;
}
}
为什么考虑使用静态工厂方法替代构造方法?(静态工厂方法的优点)
- 与构造方法不同,静态工厂方法可以有多个入参数量相同,但名称不同的方法。
实例:请参考上方的两个实例
- 与构造方法不同,静态工厂方法都是有名字的。生成的代码更易于阅读,也能突出它们的差异。
实例:
public class Test02 {
private String s1;
private String s2;
private String s3;
private Integer i1;
public Test02(){
}
public Test02(String s1){
this.s1 = s1;
}
public Test02(String s1, String s2){
this.s1 = s1;
this.s2 = s2;
}
public Test02(String s1, Integer i1){
this.s1 = s1;
this.i1 = i1;
}
public Test02(String s1, String s2, String s3){
this.s1 = s1;
this.s2 = s2;
this.s3 = s3;
}
public static Test02 createTest02InitAParam(String s1){
Test02 result = new Test02();
result.s1 = s1;
return result;
}
public static Test02 createTest02InitTwoParam(String s1, String s2){
Test02 result = new Test02();
result.s1 = s1;
result.s2 = s2;
return result;
}
public static Test02 createTest02InitThreeParam(String s1, String s2, String s3){
Test02 result = new Test02();
result.s1 = s1;
result.s2 = s2;
result.s3 = s3;
return result;
}
public static void main(String[] args) {
//构造方法创建对象
//前面我们说过,构造方法的名称和类同名,这导致构造函数的名称不够灵活,不能准确的描述返回值。
//如果参数类型、数目比较相似的话,更加不容易找到合适的构造函数。例如test02与test04。
Test02 test0 = new Test02();
Test02 test01 = new Test02("A");
Test02 test02 = new Test02("A","B");
Test02 test03 = new Test02("A","B","C");
Test02 test04 = new Test02("A", 1);
//静态工厂方法创建对象
//这让程序员更好的记得调用哪个方法创建适合自己需要的实例。注意仔细选择名称来突出它们的差异,这里我只是随便取的名字。
Test02 aParam = Test02.createTest02InitAParam("A");
Test02 twoParam = Test02.createTest02InitTwoParam("A", "B");
Test02 threeParam = Test02.createTest02InitThreeParam("A", "B", "C");
}
}
- 与构造方法不同,静态工厂方法不用每次调用时都创建一个新对象。使用预先构建的实例,反复分配它们避免了不必要的重复。这个特点在创建新对象的代价非常昂贵的情况下,是可以极大地提高性能的。
实例:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
- 与构造方法不同,静态工厂方法可以返回原返回类型的子类。例如一个应用是API,这让它可以返回对象而不需要公开它的类,这种方式隐藏类会使得API非常紧凑。
实例:
Class Person {
public static Person getInstance(){
return new Person();
// return new Son01() / Son02()
}
}
Class Son01 extends Person{
}
Class Son02 extends Person{
}
- 与构造方法不同,静态工厂方法可以入参值的范围。作为类的提供者,类入参的范围越大,调用者就越容易出错。下方实例中,调用者只用传入type即可调用。当提供者期望传入构造函数的值是事先定好的值时(下方TYPE_A、TYPE_B),但如果不是,就很容易导致程序错误。避免这种错误,我们可以使用静态工厂模式。调用方无须知道也无需制定type值,这样就能控制type赋值的范围。
实例:
public class Test03 {
public static final int TYPE_A = 1;
public static final int TYPE_B = 2;
public static final int TYPE_C = 3;
int type;
private Test03(int type) {
this.type = type;
}
public static Test03 newA() {
return new Test03(TYPE_A);
}
public static Test03 newB() {
return new Test03(TYPE_B);
}
public static Test03 newC() {
return new Test03(TYPE_C);
}
public static void main(String[] args) {
Test03 test03 = newA();
}
}
静态工厂方法的缺点
- 类如果不含有public或protected的构造器,就不能被子类化。原因是父类缺少公有的构造方法,而子类无法调用父类的私有构造方法,导致子类无法生成构造方法。
实例:
public class Person {
private String name;
private Integer age;
private Person(String name, Integer age){
this.name = name;
this.age = age;
}
public static Person getSon01(String name, Integer age){
return new Person(name, age);
}
public static Person getSon02(String name, Integer age){
return new Person(name, age);
}
//此时Student无法创建,因为Person没有公共的构造方法
public class Son extends Person{
}
}
总结
静态工厂方法,语法层面:
- 能够有自己的名字
- 用子类使代码更加紧凑。
性能的层面:
- 避免创建大量等价的实例对象。
最重要的是,当我们作为类的提供者,能够更好的控制调用者的具体行为,减少调用方出错的行为。我认为这也是对自己代码负责的体现。
来源:https://www.cnblogs.com/gongguowei01/p/12147738.html