对象创建销毁那些事儿(一)

这是我参与更文挑战的第15天,活动详情查看: 更文挑战

一、前言

在之前的设计模式中,我们完整的学完了设计模式中的创建型模式,这种模式是专注于对象的创建方式。想回顾的可以看:

浅谈单例模式

浅谈工厂模式

浅谈原型模式

浅谈建造者模式

为了对这种创建型模式的扩展,今天开始,我们来讨论下对象是如何创建和销毁的这个话题。

几个疑问:

  • 1、何时以及如何创建对象?
  • 2、何时以及如何避免创建对象?
  • 3、如何确保这些对象能够适时销毁掉?
  • 4、对象销毁前需要做哪些清理工作?

二、用静态工厂方法代替构造器

什么是构造器?对于类而言,都有一个默认的公有的构造方法,我们可以调用它生成一个新的类,这就是我们常说的new 一个对象,也就是构造器。

什么是静态工厂方法? 这里的静态工厂方法与我们在设计模式中讲的工厂方法不同,但也类似,它是通过一个静态的方法返回类的实例,Boolean类型就是这是典型的静态工厂方法运用:

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
复制代码

也就是用静态工厂方法代替构造器,这样做有什么好处呢?

2.1 优势

2.1.1 静态工厂方法有方法名

我们知道Java的构造器中往往会传入多个参数构造成不同的对象。也就是重载构造方法,如果参数类型、数目又比较相似,就很容易出错,如果没有参考文档,往往不知所措。

2.1.2 不必每次调用的时候都创建一个对象

对于不可变类,可以提前创建好实例,或者将实例缓存起来,进行重复利用,就可以避免创建不必要的对象,例如单例模式的创建,一次加载,后续调用即可。

2.1.3 可与返回原返回类型的子类

这样在选择返回对象的类时就更加灵活,也应对着设计模式的基本原则—-里氏替换原则。即子类应该能替代父类。

灵活的应用就是调用API返回对象。

在java.util.Collections 中,它的内部有很多Collections的非共有实现类(包括不可修改的集合,同步集合),这些类都可与通过静态工厂方法导出,返回的也是它的子类。好处就是减少了API的数量

public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }

 static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
        private static final long serialVersionUID = 1820017752578914078L;

        final Collection<? extends E> c;

        UnmodifiableCollection(Collection<? extends E> c) {
            if (c==null)
                throw new NullPointerException();
            this.c = c;
        }
}
复制代码

简单举的例子:

public class Animal{
	publict static Animal getInstance(){
		return new Dog()
	}
}
public class Dog extends Animal(){
}
复制代码

调用父类可以生成返回子类。

扩展:

java8 开始,接口中允许有静态方法

java9 开始,接口中允许有私有静态方法

2.1.4 创建参数型对象的时候,代码更加简洁

在源码里面,静态模式的参数值也是会随着版本而发生变化的。例如EnumSet 中它的具体实现就依据着底层枚举类型的带线啊哦,如果它的元素小于等于64,就发RegalarEnumSet实例,如果大于64,则返回JumboEnumSet方法,对于用户来说是不知道的。

当然,这是源码里的一个例子,实际里面好像还没遇到,可以在一个对象需要多个固定调用参数的时候,就用一个方法来封装它,调用这个方法即可,就不需要传入很多参数。当然,这种要求就比较苛刻了。

2.1.5 方法返回对象的类可以不存在

就是使用静态工厂方法,我们只需传入参数,就可以获得一个不存在的类的实例。

回顾下JDBC驱动:

Class.forName(driverClass);
//加载MySql驱动
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb", "root", "root"); 
复制代码

由Drivermanager.getConnection() 返回具体的实现类对象

2.2 劣势

2.2.1 类如果不含公有的或者受保护的构造器,就不能被子类化。

继承一个类的时候,那么这个父类的构造方法就不能私有化。

2.2.2 与其他的静态方法实际上没有任何区别

一般很难方法哪里使用了静态工厂方法,所以感觉不到它与其他静态方法的区别。因此,可以从一些惯用命名来找出静态工厂方法的使用地方:

静态工厂方法给方法起了更多有意义的名字,让代码阅读和编写更加清晰,常见的命名方式如下:

from——- 类型转换方法,它只有单个参数,例如Data d=Date.from(instance)

valueOf——不太严格的讲,该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。
of——valueOf的一种更为简洁的替代,在EnumSet中使用并流行起来。
getInstance——返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对Singleton来说,该方法没有参数,并返回唯一的实例。
create或newInstance——像getInstance一样,但newInstance能够确保返回的每个实例都与所有其他实例不同。
getType——像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
newType——像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。

三、小结

总而言之,静态工厂方法的优点很多,在天天使用构造器方法的时候,可以不妨想想静态工厂方法。

当然,上面的问题在这篇文章种还是没有得到解决,接着后面的文章分析,我们再来对他们一探究竟。

参考资料:
《Effective Java (第3版)》

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享