考虑用静态方法代替构造器
优点:
便于理解,每个静态方法命名都是根据含义来的,而构造方法是跟类名相同,只有通过构造方法参数区别,每 个构造方法的作用
不必每次创建实例,可以使用预先生成的实例,或者缓存的实例
可以返回原返回类型的子类型
构造参数化实例时,构造函数必须写上参数,如果参数比较多,久会很冗长,但是静态方法不同
缺点:
无公有构造方法,则不能被子类化(因祸得福,程序估计复合,而不是继承)遇到多个构造器参数时建议使用构建器
静态方法和构造器相同的缺点:不能很好的扩展到大量的可选参数
那对于大量的可选参数的时候,如何构造实例呢,有以下3种方式
设置多个构造器,参数由少变多,依次调用下个构造器(重叠构造器)
可行,但是遇到大量参数时,代码不易写,阅读起来有难度,再则如果后期添加参数,则需要添加新的构造器, 以及遇到一大堆相同类型的参数时,构造实例时参数值传错,编译器不会报错,问题难找
public class Human {
private Long id;
private String name;
private int age;
private String city;
private String address;
private String email;
private String phone;
public Human(Long id) {
this( id, null);
}
public Human(Long id, String name) {
this( id, name, 0);
}
public Human(Long id, String name, int age) {
this( id, name, age, null);
}
public Human(Long id, String name, int age, String city) {
this( id, name, age, city, null);
}
public Human(Long id, String name, int age, String city, String address) {
this( id, name, age, city, address, null);
}
public Human(Long id, String name, int age, String city, String address, String email) {
this( id, name, age, city, address, email,null);
}
public Human(Long id, String name, int age, String city, String address, String email, String phone) {
this.id = id;
this.name = name;
this.age = age;
this.city = city;
this.address = address;
this.email = email;
this.phone = phone;
}
}
使用默认无参构造器
创建实例后,在使用set方法设置value
构造过程可能处于不同状态, 可能导致javabeans不一致的状态,(A线程处理Human,设置参数时,B线程也在处理,获取参数,则B线程获取的数据会处问题)并且类不能校验正确性
public class Human {
private Long id;
private String name;
private int age;
private String city;
private String address;
private String email;
private String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Builder模式,在要构造多个构造方法的类中,创造一个静态构建类,然后将要动态设置参数的属性,放进这个构建类种,然后通过javabeans或则静态方法,创建构建类,再设置属性值,即让原有类不直接生成实例
public class Human {
private Long id;
private String name;
private int age;
private String city;
private String address;
private String email;
private String phone;
public Human(Builder builder) {
this.id = builder.getId();
this.name = builder.getName();
this.age = builder.getAge();
this.city = builder.getCity();
this.address = builder.getAddress();
this.email = builder.getEmail();
this.phone = builder.getPhone();
}
public static class Builder{
private Long id;
private String name;
private int age;
private String city;
private String address;
private String email;
private String phone;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Human build(){
return new Human(this);
}
}
}
使用私有构造方法或枚举创建单例属性
jdk1.5前 私有构造方法,要导出一个公有的静态属性public class Human { public static final Human human = new Human(); private Human(){ } }
public class Human { private static final Human human = new Human(); private Human(){ } public Human getInstance(){ return human; } }
上面2种方法,都可以创建单例对象,但是享有特权的客服端,如果使用反射的方式,调用私有构造器,创建实例,则便不是单列对象,所以要防止2次创建实例,给予提示,并抛出异常;
jdk1.5后,可采用枚举创建,那么对象只会是单例的将不允许继承的类的构造方法,私有化
有些类中,只包含静态方法和静态属性,其实可以直接类名调用,但是有些人懒用这些类,但其实这些类又有其存在的价值,比如java.util.Arrays,其实这样的类是不需要使用则创建实例,以及是不允许继承的,那么这样的类设置其构造器为私有的避免创建不必要的对象
大家都知道,创建对象时,栈和堆都将开辟新空间,如果不断的创建对象,则会导致堆或栈溢出,特别是在循环和迭代的情况下,所以在不需要创建对象的时候,就不需要,尽量可以是一次创建多次使用消除过期的引用对象
最典型得就是栈,大家都知道,栈是一个先进后出的数据格式,当压入一个数据后,再取出一个数据时,其实其数组长度没有变,取出的数据,则不会被垃圾回收站给回收了,因为此时数组还在引用,这样整个栈都取出后,数组也不会变,即引用的对象也一直存在,所以要在每次取出栈中数据的时候,elements[size] = null;清空数组当时的引用的数据值,使其回收
public class Stack {
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(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;//显式地清空引用
return result;
}
private void ensureCapacity() {
if (elements.length == size)
elements = Arrays.copyOf( elements, 2 * size + 1 );
}
}
避免使用终结方法
finalize()基本概念
所谓的终结方法其实是指finalize()。终结方法finalizer通常是不可预测的,也是很危险的。一般情况下是不必要的。使用终结方法会导致行为不稳定,降低性能,以及可移植性问题。根据经验,应避免使用终结方法。
finalize()的执行过程
当对象不可达时,GC会判断该对象是否重写了finalize()方法,如没有重写则直接将其回收,否则,若对象未执行过finalize()方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize()方法。执行finalize()方法完后,GC会再次判断该对象是否可达,若不可达则进行回收。否则对象“复活”。
finalize()的缺点
在java中,当对象不可达时,垃圾回收会回收与之相关的存储控件,不需要专门做,但Java的垃圾回收机制只负责内存相关清理,其他连接资源,比如DB连接,文件流释放都需要手动完成,如果滥用finalize()方法来释放资源,有可能文件还会在打开状态(.Java语言规范不仅不能保证finalize()方法会被及时的执行,而且根本就不保证他们会执行),所以再次打开时可能会出错
不同jvm虚拟机对finalize算法不同,在本地测试通过,在客户机上根本无法运行,也是有可能的
性能问题,某测试数据:创建和销毁一个简单对象的时间大约为5.6ns。增加一个finalize()使时间增加到了2400ns。换句话说,用终结方法创建和销毁对象慢了大约430倍。
finalize()的优点
1.当对象的所有者忘记调用之前建议的显示终止方法时,finalize()可以充当安全网,safety net。虽然这种做并不能保证finalize()会被及时的调用,但是在客户端无法通过调用显示的finalize()来正确结束操作的情况下(希望这种情形尽可能的少发生),迟一点释放关键资源总比永远不释放要好。如果正考虑编写这样的安全网终结方法,就要考虑清楚这种额外的保护是否值得付出额外的代价。
显示finalize()模式的实例中所示的四个类FileInputStream,FileOutputStream,Timer和Connection,都具有终结方法。当他们的显示的终止方法未能被调用的情况下,这些finalize()充当了安全网。
2.finalize()的第二种合理用于与对象的本地对等体native peer有关。本地对等体是一个本地对象native object,普通对象通过本地方法native method委托给一个本地对象。因为本地对等体不是一个普通对象,所以垃圾回收器不会知道它。当它的Java对等体被回收的时候,它不会被回收。在本地对等体并不拥有关键资源的前提下(注是否因为关键资源可被本地其他进程使用),finalize()正是执行任务的最合适的工具。如果本地对等体用于必须被及时终止的资源, 那么该类就应该具有一个显示的终止方法,如前所述。终止方法应该完成所有必要的工作以便释放关键的资源。终止方法可以使本地方法或者也可以调用本地方法。
五、使用finalize()的建议
尽量少用finalize,最好由系统管理,我们可以定义其他的方法来释放非内存资源。
除非是作为安全网或者是为了终止非关键的本地资源,否则请不要使用终结方法。