Effective Java 2.1——考虑用静态工厂方法替代构造器

Effective Java系列学习笔记每礼拜更新一次,每次视书本内容长度更新1~2条条目方便大家消化,大家感兴趣的可以关注一下呦~(大家觉得学的不过瘾的话可以看看我其他的学习笔记相信也能为大家带来一些帮助的)
由于第一章是引言,所以我们就直接从第二章开始。Effective Java被称为Java学习资料里面的“神书”,这也是我第一次看这本书,把个人的学习笔记贴出来一方面增加了我对知识的理解,另一方面希望能给大家一些帮助。话不多说,开啃!!!

第一条:考虑用静态工厂方法替代构造器

构造器和静态方法的区别

大家看到这个标题可能会思考什么叫做静态工厂,什么叫做构造器。下面用代码跟大家说明一下:
构造器:

class A
{
    int a;
    public A(int a)//构造器
    {
        this.a = a;
    }
}

public class Main
{
    public static void main(String args[])
    {
        A aa = new A(1);//实例化方法
        System.out.println(aa.a);
    }
}

输出为:1
所谓的构造器也就是我们平时所说的构造方法,C++里面的构造函数。一般来说构造器的特点是访问修饰符为:public。因为构造函数的作用就是在其他地方来实例化我这个类的对象嘛。
那存不存在一种写法,使构造函数的访问修饰符为:private呢?
静态工厂:

class A
{
    int a;
    private A(int a)//构造器
    {
        this.a = a;
    }
    public static A getA(int a) {//静态工厂
        return new A(a);
    }
}
public class Main
{
    public static void main(String args[])
    {
        A aa = A.getA(1);//实例化方法
        System.out.println(aa.a);
    }
}

输出值为:1
所谓的静态工厂其实也就是在类里面定义一个静态方法,方法的本质其实也就是调用本类的构造方法然后返回一个A对象。
小tips:大家应该能看出来,这里我把构造器的访问权限定为:private。那也就是说我们在外面是只能使用静态方法来实例化A类。(当然这里也可以用public,但是尽量考虑一下类的封装性,对外的接口越少越好。这样可以避免一些奇奇怪怪的错误。)

静态工厂相比于构造器的优势

1、 静态工厂相比于构造器可以有很多很多的名字

考虑一种情况,一个类有很多很多的变量,那么构造函数的目的就是去初始化这些变量,我们可以这么写:

class A
{
    int a;
    int aa;
    int aaa;
    public A(int a)//构造器1
    {
        this.a = a;
    }
    public A(int a,int aa)//构造器2
    {
        this(a);//调用构造器1
        this.aa = aa;
    }
    public A(int a,int aa,int aaa)//构造器3
    {
        this(a,aa);//调用构造器2
        this.aaa = aaa;
    }
}

我们可以看到三个构造器的名字都是一样的-A,这也是构造器的要求即名字必须与类名相同。但是这样一来我们区别构造器的方法只能通过里面参数的不同。
下面看静态工厂的实现:

class A
{
    int a;
    int aa;
    int aaa;
    public static A initOne(int a)//初始化一个
    {
        return new A(a);
    }
    public static A initTwo(int a,int aa) {//初始化两个
        return new A(a,aa);
    }
    public static A initThree(int a,int aa,int aaa)//初始化三个
    {
        return new A(a,aa,aaa);
    }
    private A(int a)//构造器1
    {
        this.a = a;
    }
    private A(int a,int aa)//构造器2
    {
        this(a);
        this.aa = aa;
    }
    private A(int a,int aa,int aaa)//构造器3
    {
        this(a,aa);
        this.aaa = aaa;
    }

}

我们把三个构造器变为private,然后写了三个静态工厂,选择了3个命名突出了三个静态工厂的区别。(大家在选择名称的时候要慎重呦~)

2、 不必每次调用静态工厂方法时都创建一个新对象

看下面一段代码:

class A
{
    int a;
    private static final A Singleton = new A(1);
    public static A getInstanceA()//返回唯一实例
    {
        return Singleton;
    }
    private A(int a)//构造器
    {
        this.a = a;
    }
}

我们在类里面定义了一个常对象,也就是单例对象(只存在一个),而且我们的构造器是private,也就是说A类只能存在一个已经被我们预先实例化好的对象(所谓的单例(SingleTon))。那么无论我们调用多少次getInstance(),返回的都是Singleton。在一些情况下我们完全没必要构建很多的无用对象,使用静态工厂会比构造器大大节省空间。

3、 静态工厂可以返回原返回类型的任何子类型对象

相信大家看到这句话的时候也会有这种感觉(你TM在BB什么玩意儿,什么叫返回原返回类型的任何子类型的对象???),(⊙﹏⊙)b这句话是作者原话,我也看了好久没看懂。
下面是就我的理解写出来的一段代码,大家看看是不是作者想要表达的意思:

class A
{
    private A(){};
    int a;
    private C c = new B();
    public static C getC()
    {
        return c;
    }
}

interface C{
    void doSth();
}
class B implements C
{
    public void doSth(){
        System.out.println("bbb");
    }
}
public class Main
{
    public static void main(String args[])
    {
        C c = A.getC();
        c.doSth();
    }
}

输出结果为:bbb。
代码里面我们定义了A类,作为静态工厂类。然后定义一个接口C,B类实现C接口。B类的作用是实现C里面的抽象方法。因为B实现了C,所以静态工厂里面可以return B类的对象(向上转型)。我们在执行这句话的时候C c = A.getC();,相当于C c = new B();
那么在这里使用静态工厂有什么好处呢?两个字:隐藏。我们在main函数里面完全没有B类的任何痕迹,也就是说如果我们是编写api的人员,我们完全没有必要告诉用户B类的实现。只需要告诉getC()这个方法做了什么事情就好了。这样一来假设C下面有1000个实现类(即类似于B的子类),我们只需要编写A类的api(使用不同的静态方法返回不同实现类的对象)。这个从另一方面也体现了封装性。

4、 在创建参数化类型实例的时候,静态工厂能使代码变的简洁。

上代码:

class A<A1,A2,A3>
{
    A1 a;
    A2 aa;
    A3 aaa;
    public static <A1,A2,A3> A<String, String, String> getStringInstance(String s,String ss,String sss)//获得String类型的对象
    {
        return  new A<String, String, String>(s,ss,sss);
    }
    public A(A1 a,A2 aa,A3 aaa)//构造器
    {

        this.a = a;
        this.aa = aa;
        this.aaa = aaa;
    }
}
public class Main
{
    public static void main(String args[])
    {
        A<String,String,String> a1 = A.getStringInstance("a","aa","aaa");//静态工厂
        System.out.println(a1.a+" "+a1.aa+" "+a1.aaa);
        A<String, String, String> a2 = new A<String,String,String>("aaa","aa","a");//构造器
        System.out.println(a2.a+" "+a2.aa+" "+a2.aaa);
    }
}

输出结果为:a aa aaa
aaa aa a
相当于省去了后面一长串的代码new A

静态工厂的缺点

1. 如果类里面没有protected或者public的构造器,那么这个类不能子类化


这个大家仔细想一下就明白了。因为子类的构造方法会自动调用父类的构造器。如果父类没有可供子类调用的构造器则会报错。

2. 静态工厂与其他静态方法本质上没有任何区别

长得一样,功能略有不同而已。而且大家看api的时候也会发现,只有构造器是单独一块列出来的,剩下的都是方法挤在一起。

下面是静态工厂的一些常用名称:

  • valueOf:作用类似于类型转换。传进去什么参数返回的就是那个参数的值的对象。
  • Of:valueOf的简称。
  • getInstance:字面意思,返回一个实例。(可以是Singleton,或者自己通过传进去参数定制的实例)。
  • newInstance:返回一个与其他实例不同的实例。
  • getType:类似于getInstance,但得到返回的对象的类型。
  • newType:类似于newInstance,但得到返回的对象的类型。

本条总结

  1. 静态工厂是什么?
  2. 优点:名字多,返回同一实例,隐藏实现类,使泛型类的代码变简洁。
  3. 缺点:不能子类化(需要public或者protected的构造器),api中没有单独标出,与其他静态方法无明显区别。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值