面向对象的陷阱——构造器的陷阱
来源:互联网 发布:原始原素 知乎 编辑:程序博客网 时间:2024/05/21 09:48
2、构造器的陷阱
构造器是Java每个类都会提供的一个“特殊方法”。构造器负责对Java对象执行初始化操作,不管是定义实例变量时指定的初始值,还是在非静态初始化块中所执行的操作,实际上都会被提取到构造器中来执行。
2、1 构造器返回类型
构造器不能声明返回值类型,也不能使用void声明构造器没有返回值。当为构造器声明添加任何返回值类型声明,或者添加void声明该构造器没有返回值时,编译器不会提示这个构造器有错误,只是把这个所谓的“构造器”当成普通方法处理。
2、2 创建对象
实际上构造器并不会创建Java对象,构造器只负责执行初始化,在构造器执行之前,Java对象所需要的内存空间,应该是由new关键字申请的。绝大部分时候,程序使用new关键字为一个Java对象申请空间之后,都需要使用构造器为这个对象执行初始化。但在某些时候,程序创建Java对象无须调用构造器,以下两种方式创建java对象无须使用构造器。
- 使用反序列化的方式恢复Java对象。
- 使用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对象创建完成
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
true
上面程序为Singleton类提供了readResolve()方法,当JVM反序列化回复一个对象时,系统会自动调用该方法,从而保证系统通过序列化机制不会产生多个Java对象。
除了可以使用反序列化机制恢复Java对象无须构造器之外,使用clone()方法复制Java对象也无须调用构造器。如果希望某个Java类的实例是可复制的,则对该Java类有如下两个要求。
- 让该Java类实现Cloneable接口。
- 为该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
false
通过clone()方法复制出来的Dog对象和原来的Dog对象具有完全相同的实例变量值,但系统中将会产生两个Dog对象。
注意:
- 尽量不要在定义实例变量时指定实例变量的值为当前类的实例。
- 尽量不要在初始化块中创建当前类的实例。
- 尽量不要在构造器内调用本构造器创建Java对象。
1 0
- 面向对象的陷阱——构造器的陷阱
- 面向对象的陷阱——native方法的陷阱
- 27、面向对象的陷阱
- 面向对象的陷阱——instanceof运算符的陷阱
- 面向对象的陷阱——非静态内部类的陷阱
- 第7篇 面向对象的陷阱
- static native面向对象的陷阱
- java 之 面向对象的陷阱
- 拷贝构造函数的陷阱
- 构造函数的小陷阱
- copy构造隐藏的陷阱
- 拷贝构造函数的陷阱
- 拷贝构造函数的陷阱
- 疯狂Java笔记之面向对象的陷阱
- Java表达式的陷阱——关于字符串的陷阱
- Java表达式的陷阱——表达式类型的陷阱
- Java表达式的陷阱——正则表达式的陷阱
- Java表达式的陷阱——多线程的陷阱
- Qt之图形(QPainter的基本绘图)
- Java多线程/并发08、中断线程 interrupt()
- python调用c函数参数类型问题
- 来自知乎 python十分钟教程
- 详解匿名内部类
- 面向对象的陷阱——构造器的陷阱
- ANDROID LOG信息分析
- NYOJ123 士兵杀敌(四)(树状数组,插线问点)
- 获取屏幕旋转事件
- 数据存储之SQLite与LitePal<一>
- 4724: [POI2017]Podzielno
- 一个简单的爬虫开发
- Differences and Similarities Between Index Coalesce and Shrink Space
- linux中使用apt-get安装的文件位置