面向对象的陷阱——构造器的陷阱

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Never_Blue/article/details/70918571

2、构造器的陷阱

       构造器是Java每个类都会提供的一个“特殊方法”。构造器负责对Java对象执行初始化操作,不管是定义实例变量时指定的初始值,还是在非静态初始化块中所执行的操作,实际上都会被提取到构造器中来执行。

2、1 构造器返回类型

       构造器不能声明返回值类型,也不能使用void声明构造器没有返回值。当为构造器声明添加任何返回值类型声明,或者添加void声明该构造器没有返回值时,编译器不会提示这个构造器有错误,只是把这个所谓的“构造器”当成普通方法处理。

2、2 创建对象

       实际上构造器并不会创建Java对象,构造器只负责执行初始化,在构造器执行之前,Java对象所需要的内存空间,应该是由new关键字申请的。绝大部分时候,程序使用new关键字为一个Java对象申请空间之后,都需要使用构造器为这个对象执行初始化。但在某些时候,程序创建Java对象无须调用构造器,以下两种方式创建java对象无须使用构造器。
  1. 使用反序列化的方式恢复Java对象。
  2. 使用clone方法复制Java对象。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

class Person implements Serializable{
	private String name;
	
	public Person(String name) {
		System.out.println("调用有参数的构造器");
		this.name = name;
	}
	
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj.getClass() == Person.class) {
			Person target = (Person)obj;
			return target.name.equals(this.name);
		}
		return false;
	}
	
	public int hashCode() {
		return name.hashCode();
	}
}

public class SerializebleTest{
	public static void main(String[] args) throws Exception {
		Person p = new Person("张三");
		System.out.println("Person对象创建完成");
		Person p1 = null;
		try (
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\CSDN博客\\a.txt"));
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\CSDN博客\\a.txt"));
		)
		{
			oos.writeObject(p);
			oos.flush();
			p1 = (Person)ois.readObject();
			System.out.println(p.equals(p1));
			System.out.println(p == p1);
		}
	}
}
输出结果为:
调用有参数的构造器
Person对象创建完成
true
false
       当创建Person对象时,程序调用了相应的构造器来对该对象执行初始化;当程序通过反序列化机制恢复Java对象时,系统无须再调用构造器来执行初始化。通过反序列化机制恢复出来的Person对象当然和原来的Person对象具有完全相同的实例变量值,但系统会产生两个Person对象。
注意 :程序完全可以通过反序列化机制破坏单例类的规则。如果真的想要保证反序列化也不会产生多个Java实例,则应该为单例类提供readResolve()方法,该方法保证反序列化时得到已有的Java实例。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

class Singleton implements Serializable {
	private static Singleton instance;
	private String name;
	
	private Singleton(String name) {
		System.out.println("调用有参数的构造器");
		this.name = name;
	}
	
	public static Singleton getInstance(String name) {
		if (instance == null) {
			instance = new Singleton(name);
		}
		return instance;
	}
	
	private Object readResolve() throws ObjectStreamException {
		return instance;
	}
}

public class SingletonTest {
	public static void main(String[] args) throws Exception{
		Singleton s = Singleton.getInstance("张三");
		Singleton s1 = null;
		try (
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\CSDN博客\\b.txt"));
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\CSDN博客\\b.txt"));
		) {
			oos.writeObject(s);
			oos.flush();
			s1 = (Singleton)ois.readObject();
			System.out.println(s == s1);
		}
	}
}
输出结果为:
调用有参数的构造器
true
       上面程序为Singleton类提供了readResolve()方法,当JVM反序列化回复一个对象时,系统会自动调用该方法,从而保证系统通过序列化机制不会产生多个Java对象。

       除了可以使用反序列化机制恢复Java对象无须构造器之外,使用clone()方法复制Java对象也无须调用构造器。如果希望某个Java类的实例是可复制的,则对该Java类有如下两个要求。
  1. 让该Java类实现Cloneable接口。
  2. 为该Java类提供clone()方法,则方法负责进行复制。
class Dog implements Cloneable {
	private String name;
	private double weight;
	
	public Dog(String name , double weight) {
		this.name = name;
		this.weight = weight;
	}
	
	public Object clone() {
		Dog dog = null;
		try {
			dog = (Dog)super.clone();
		}
		catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return dog;
	}
	
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj.getClass() == Dog.class) {
			Dog target = (Dog)obj;
			return target.name.equals(this.name) && target.weight == this.weight;
		}
		return false;
	}
}

public class CloneTest {
	public static void main(String[] args) {
		Dog dog = new Dog("Blue" , 22.5);
		Dog dog1 = (Dog)dog.clone();
		System.out.println(dog1.equals(dog));
		System.out.println(dog1 == dog);
	}
}
输出结果为:
true
false
       通过clone()方法复制出来的Dog对象和原来的Dog对象具有完全相同的实例变量值,但系统中将会产生两个Dog对象。

注意
  1. 尽量不要在定义实例变量时指定实例变量的值为当前类的实例。
  2. 尽量不要在初始化块中创建当前类的实例。
  3. 尽量不要在构造器内调用本构造器创建Java对象。  






猜你喜欢

转载自blog.csdn.net/Never_Blue/article/details/70918571