第6章 面向对象入门
本章读者可以学到如下实例:
- 实例043 自定义图书类
- 实例044 温度单位转换工具
- 实例045 成员变量的默认初始化值
- 实例046 单例模式的应用
- 实例047 汉诺塔问题求解
- 实例048 编写同名的方法
- 实例049 构造方法的应用
- 实例050 统计图书的销售量
- 实例051 两只完全相同的宠物
- 实例052 重新计算对象的哈希码
- 实例053 使用字符串输出对象
- 实例054 对象的假克隆
- 实例055 Java对象的浅克隆
- 实例056 Java对象的深克隆
- 实例057 序列化与对象克隆
- 实例058 深克隆效率的比较
实例044 温度单位转换工具
实例说明
目前有两种广泛使用的温度单位,即摄氏度和华氏度。在标准大气压下,沸腾的水可以表示成100摄氏度和212华氏度。
本实例将编写一个简单的温度单位转换工具,它可以将用户输入的摄氏度转换成华氏度。
实现过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.mingrisoft;
import java.util.Scanner;
public class CelsiusConverter {
public double celsiusToFahrenheit(double celsius) { double fahrenheit = 1.8 * celsius + 32; return fahrenheit; }
private double fahrenheitToCelsius(double fahrenheit) { double celsius = (fahrenheit - 32) / 1.8; return celsius; }
public static void main(String[] args) { CelsiusConverter converter = new CelsiusConverter(); System.out.print("请输入 摄氏度:"); Scanner in = new Scanner(System.in); double celsius = in.nextDouble(); System.out.println(celsius + "摄氏度=" + converter.celsiusToFahrenheit(celsius) + "华氏度"); System.out.print("请输入 华氏度:"); double fahrenheit = in.nextDouble(); System.out.println(fahrenheit + "华氏度=" + converter.fahrenheitToCelsius(fahrenheit) + "摄氏度"); in.close(); } }
|
技术要点
实例046 单例模式的应用
单例类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.mingrisoft;
public class Emperor { private static Emperor emperor = null;
private Emperor() { }
public static Emperor getInstance() { if (emperor == null) { emperor = new Emperor(); } return emperor; } }
|
测试类:
1 2 3 4 5 6 7 8 9
| package com.mingrisoft;
public class Test { public static void main(String[] args) { Emperor emperor1 = Emperor.getInstance(); Emperor emperor2 = Emperor.getInstance(); System.out.println(emperor1 == emperor2); } }
|
技术要点
单例模式的特点在于仅能获得一个对象。为了防止其他用户创建对象,需要将构造方法设置成private
的,然后提供一个静态方法,该方法返回这个类的对象。
实例047 汉诺塔问题求解
实例说明
汉诺塔问题的描述如下:有A、B和C 3根柱子,在A上从下往上按照从小到大的顺序放着64个圆盘,以B为中介,把盘子全部移动到C上。移动过程中,要求任意盘子的下面要么没有盘子,要么只能有比它大的盘子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.mingrisoft;
public class HanoiTower { public static void moveDish(int level, char from, char inter, char to) { if (level == 1) { System.out.println("从 " + from + " 移动盘子 " + level + " 号到 " + to); } else { moveDish(level - 1, from, to, inter); System.out.println("从 " + from + " 移动盘子 " + level + " 号到 " + to); moveDish(level - 1, inter, from, to); } }
public static void main(String[] args) { int nDisks = 3; moveDish(nDisks, 'A', 'B', 'C'); } }
|
运行效果
1 2 3 4 5 6 7
| 从 A 移动盘子 1 号到 C 从 A 移动盘子 2 号到 B 从 C 移动盘子 1 号到 B 从 A 移动盘子 3 号到 C 从 B 移动盘子 1 号到 A 从 B 移动盘子 2 号到 C 从 A 移动盘子 1 号到 C
|
技术要点
为了将N个盘子从A移动到C,需要先将第N个盘子上面的N-1
个盘子移动到B上,这样才能将第N个盘子移动到C上。
同理,为了将第N-1
个盘子从B移动到C上,需要将N-2
个盘子移动到A上,这样才能将第N-1
个盘子移动到C上。
通过递归就可以实现汉诺塔问题的求解。
实例051 两只完全相同的宠物
实例说明
由于生命的复杂性,寻找两只完全相同的宠物(类的对象)是不可能的。在Java
中,可以通过比较对象的成员变量来判断对象是否相同。本实例将创建3只宠物猫,通过比较它们的名字、年龄、重量和颜色属性来看它们是否相同。
实现过程
Cat.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| package com.mingrisoft;
import java.awt.Color;
public class Cat { private String name; private int age; private double weight; private Color color;
public Cat(String name, int age, double weight, Color color) { this.name = name; this.age = age; this.weight = weight; this.color = color; }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Cat cat = (Cat) obj; return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color)); }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("名字:" + name + ",\t"); sb.append("年龄:" + age + ",\t"); sb.append("重量:" + weight + ",\t"); sb.append("颜色:" + color + ""); return sb.toString(); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.mingrisoft;
import java.awt.Color;
public class Test { public static void main(String[] args) { Cat cat1 = new Cat("Java", 12, 21, Color.BLACK); Cat cat2 = new Cat("C++", 12, 21, Color.WHITE); Cat cat3 = new Cat("Java", 12, 21, Color.BLACK); System.out.println("猫咪1号:" + cat1); System.out.println("猫咪2号:" + cat2); System.out.println("猫咪3号:" + cat3); System.out.println("猫咪1号是否与猫咪2号相同:" + cat1.equals(cat2)); System.out.println("猫咪1号是否与猫咪3号相同:" + cat1.equals(cat3)); }
}
|
运行效果
1 2 3 4 5
| 猫咪1号:名字:Java, 年龄:12, 重量:21.0, 颜色:java.awt.Color[r=0,g=0,b=0] 猫咪2号:名字:C++, 年龄:12, 重量:21.0, 颜色:java.awt.Color[r=255,g=255,b=255] 猫咪3号:名字:Java, 年龄:12, 重量:21.0, 颜色:java.awt.Color[r=0,g=0,b=0] 猫咪1号是否与猫咪2号相同:false 猫咪1号是否与猫咪3号相同:true
|
技术要点
Java
中的类都是Object
类的直接或间接子类。在Object
类中定义了equals
方法用于比较类的两个对象是否相同。该方法的默认实现仅能比较两个对象是否是同一个对象。通常在定义类时推荐重写这个方法。
重点 equals方法的规范定义描述
- 1.如果是同一个对象,则相同
- 2.如果有一个为null,则不同
- 3.如果类型不同,则不同
- 4.强制类型转换,比较属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Cat cat = (Cat) obj; return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color)); }
|
实例052 重新计算对象的哈希码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package com.mingrisoft;
import java.awt.Color;
public class Cat { private String name; private int age; private double weight; private Color color;
public Cat(String name, int age, double weight, Color color) { this.name = name; this.age = age; this.weight = weight; this.color = color; }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Cat cat = (Cat) obj; return name.equals(cat.name) && (age == cat.age) && (weight == cat.weight) && (color.equals(cat.color)); }
@Override public int hashCode() { return 7 * name.hashCode() + 11 * Integer.valueOf(age).hashCode() + 13 * Double.valueOf(weight).hashCode() + 17 * color.hashCode(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("名字:" + name + ",\t"); sb.append("年龄:" + age + ",\t"); sb.append("重量:" + weight + ",\t"); sb.append("颜色:" + color + ""); return sb.toString(); } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package com.mingrisoft;
import java.awt.Color;
public class Test { public static void main(String[] args) { Cat cat1 = new Cat("Java", 12, 21, Color.BLACK); Cat cat2 = new Cat("C++", 12, 21, Color.WHITE); Cat cat3 = new Cat("Java", 12, 21, Color.BLACK); System.out.println("猫咪1号的哈希码:" + cat1.hashCode()); System.out.println("猫咪2号的哈希码:" + cat2.hashCode()); System.out.println("猫咪3号的哈希码:" + cat3.hashCode()); System.out.println("猫咪1号是否与猫咪2号相同:" + cat1.equals(cat2)); System.out.println("猫咪1号是否与猫咪3号相同:" + cat1.equals(cat3)); }
}
|
运行效果
1 2 3 4 5
| 猫咪1号的哈希码:849794130 猫咪2号的哈希码:1119356584 猫咪3号的哈希码:849794130 猫咪1号是否与猫咪2号相同:false 猫咪1号是否与猫咪3号相同:true
|
简单的计算哈希码的方式
一种简单的计算哈希码的方式是:
将重写equals
方法时使用到的成员变量,乘以不同的质数然后求和,以此作为新的哈希码
实例054 对象的假克隆
实例说明
对象的克隆是Java
中的一项高级技术,它可以根据给定的对象,获得与其完全相同的另个对象。在克隆过程中,有些注意事项。本实例将演示常见的错误,其运行效果如图6.12所示。
技术要点
Java
中,对于基本类型可以使用“==
”来进行克隆,此时两个变量除了相等是没有任何关系的。而对于引用类型却不能简单地使用“==”进行克隆,这与Java
的内存空间使用有关。Java
将内存空间分成两块,即栈和堆。
对于引用变量而言,使用“==”将修改引用,而不是复制堆中的对象。此时两个引用变量将指向同一个对象。因此,如果一个变量对其进行修改则会改变另一个变量。
实例055 Java对象的浅克隆
克隆相关内容
实例说明
在克隆对象时,
- 如果对象的成员变量是基本类型,则使用浅克隆即可完成。
- 如果对象的成员变量包括可变引用类型,则需要使用深克隆。
本实例将演示如何使用浅克隆
实现过程
Address.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package com.mingrisoft;
public class Address { private String state; private String province; private String city;
public Address(String state, String province, String city) { this.state = state; this.province = province; this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("国家:" + state + ", "); sb.append("省:" + province + ", "); sb.append("市:" + city); return sb.toString(); } }
|
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| package com.mingrisoft;
public class Employee implements Cloneable { private String name; private int age; private Address address;
public Employee(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; }
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 Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("姓名:" + name + ", "); sb.append("年龄:" + age + "\n"); sb.append("地址:" + address); return sb.toString(); }
@Override public Employee clone() { Employee employee = null; try { employee = (Employee) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return employee; } }
|
测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package com.mingrisoft;
public class Test { public static void main(String[] args) { System.out.println("---------------------克隆之前--------------------------"); Address address = new Address("中国", "吉林", "长春"); Employee employee1 = new Employee("张三", 30, address); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("---------------------克隆之前--------------------------"); Employee employee2 = employee1.clone(); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); employee2.setName("李四"); employee2.setAge(24); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("员工2的信息:"); System.out.println(employee2); } }
|
运行效果:
1 2 3 4 5 6 7 8 9 10 11
| ---------------------克隆之前-------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 ---------------------克隆之前-------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:四川, 市:成都 员工2的信息: 姓名:李四, 年龄:24 地址:国家:中国, 省:四川, 市:成都
|
从运行结果上看,使用浅克隆得来的对象和原来的对象之间的
- 基本类型的属性和不可变引用类型的属性不会相互影响
- 可变引用类型的属性会相互影响
多学两招
如果引用类型是不可变的,如String
类的对象,则不必进行深克隆。
技术要点
当需要克隆对象时,需要使用clone()
方法,该方法的声明如下:
需要注意的是,该方法是一个受保护的方法,通常需要重写该方法并将访问权限限定符改成public
。该方法将类中各个域进行复制,如果对于引用类型的域,这种操作就会有问题,因此称作浅克隆。
提供克隆功能的类需要实现Cloneable
接口,否则使用cloned
方法时会抛出CloneNotSupportedException
异常。
实例056 Java对象的深克隆
实例说明
如果类的成员变量中包括可变引用类型,则在克隆时就需要使用深克隆。本实例将演示如何使用深克隆
实现过程
Address.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| package com.mingrisoft;
public class Address implements Cloneable { private String state; private String province; private String city;
public Address(String state, String province, String city) { this.state = state; this.province = province; this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("国家:" + state + ", "); sb.append("省:" + province + ", "); sb.append("市:" + city); return sb.toString(); }
@Override protected Address clone() { Address address = null; try { address = (Address) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return address; } }
|
脚下留神:Address
类的域不是基本类型就是不可变类型,所以可以直接使用浅克隆
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| package com.mingrisoft;
public class Employee implements Cloneable { private String name; private int age; private Address address;
public Employee(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; }
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 Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("姓名:" + name + ", "); sb.append("年龄:" + age + "\n"); sb.append("地址:" + address); return sb.toString(); }
@Override public Employee clone() { Employee employee = null; try { employee = (Employee) super.clone(); employee.address = address.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return employee; } }
|
多学两招:在Java5.0
版中,支持重写方法时返回协变类型,因此可以返回Employee
对象。
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.mingrisoft;
public class Test { public static void main(String[] args) { System.out.println("---------------------克隆之前--------------------------"); Address address = new Address("中国", "吉林", "长春"); Employee employee1 = new Employee("张三", 30, address); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("---------------------克隆之后--------------------------"); Employee employee2 = employee1.clone(); employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); employee2.setName("李四"); employee2.setAge(24); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("员工2的信息:"); System.out.println(employee2); }
}
|
运行效果
1 2 3 4 5 6 7 8 9 10 11
| ---------------------克隆之前-------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 ---------------------克隆之后-------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 员工2的信息: 姓名:李四, 年龄:24 地址:国家:中国, 省:四川, 市:成都
|
从运行结果可以看出,拷贝得来的员工2不再和员工1共享同一个地址对象了,修改员工2的地址,不会影响到员工1的地址.
技术要点
通常情况下,需要克隆对象时都需要使用深克隆。但需要注意的是,如果引用类型中还有可变的引用类型成员变量,则它也需要进行克隆。例如本实例中Address
类如果增加了一个Date
成员变量表示开始在此居住的时间,则其也需要被克隆。
实例057 序列化与对象克隆
实例说明
如果类的成员变量比较复杂,例如使用了多个可变引用类型,使用clone()
方法来克隆是非常麻烦的。此时,也可以考虑使用序列化的方式完成克隆。本实例将演示其用法,实例的运行效果如图6.15所示。
实现过程
Address.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| package com.mingrisoft;
import java.io.Serializable;
public class Address implements Serializable { private static final long serialVersionUID = 4983187287403615604L; private String state; private String province; private String city;
public Address(String state, String province, String city) { this.state = state; this.province = province; this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public String getProvince() { return province; }
public void setProvince(String province) { this.province = province; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public static long getSerialversionuid() { return serialVersionUID; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("国家:" + state + ", "); sb.append("省:" + province + ", "); sb.append("市:" + city); return sb.toString(); }
}
|
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| package com.mingrisoft;
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;
public class Employee implements Serializable { private static final long serialVersionUID = 3049633059823371192L; private String name; private int age; private Address address;
public Employee(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; }
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 Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(" 姓名:" + name + ", "); sb.append("年龄:" + age + "\n"); sb.append(" 地址:" + address); return sb.toString(); }
public static Employee readEmployee() { Employee employee = null; ObjectInputStream in = null; try { in = new ObjectInputStream(new FileInputStream("employee.dat")); employee = (Employee) in.readObject(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return employee; }
public static void writeEmployee(Employee employee) { ObjectOutputStream out = null; try { out = new ObjectOutputStream(new FileOutputStream("employee.dat")); out.writeObject(employee); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
|
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package com.mingrisoft;
public class Test { public static void main(String[] args) { System.out.println("----------------------------序列化之前----------------------------"); Address address = new Address("中国", "吉林", "长春"); Employee employee1 = new Employee("张三", 30, address); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("----------------------------序列化之后----------------------------"); Employee.writeEmployee(employee1); Employee employee2 = Employee.readEmployee(); System.out.println("员工2的信息:"); System.out.println(employee2); System.out.println("----------------------------员工2修改之后----------------------------"); if (employee2 != null) { employee2.getAddress().setState("中国"); employee2.getAddress().setProvince("四川"); employee2.getAddress().setCity("成都"); employee2.setName("李四"); employee2.setAge(24); System.out.println("员工1的信息:"); System.out.println(employee1); System.out.println("员工2的信息:"); System.out.println(employee2); } } }
|
运行效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| ----------------------------序列化之前---------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 ----------------------------序列化之后---------------------------- 员工2的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 ----------------------------员工2修改之后---------------------------- 员工1的信息: 姓名:张三, 年龄:30 地址:国家:中国, 省:吉林, 市:长春 员工2的信息: 姓名:李四, 年龄:24 地址:国家:中国, 省:四川, 市:成都
|
从运行结果可以看出,修改员工2的成员变量,不会影响到员工1的成员变量.
技术要点
进行序列化的类需要实现Serializable
接口,该接口中并没有定义任何方法,是一个标识接口。如果类中有可变的引用类型成员变量,则该变量也需要实现Serializable
接口,依此类推。
本实例中采用将对象写入本地文件的方式完成序列化,读者也可以将其写入到内存中再读取。
实例058 深克隆效率的比较
实例说明前面介绍了两种实现深克隆的方式,本实例将分别使用这两种方式克隆10000个对象,并输出花费的时间,这样读者可以对它们的效率差异有个直观的认识。
实现过程
Employee.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package com.mingrisoft;
import java.io.Serializable;
public class Employee implements Cloneable, Serializable { private static final long serialVersionUID = 5022956767440380940L; private String name; private int age;
public Employee(String name, int age) { this.name = name; this.age = age; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("姓名:" + name + ", "); sb.append("年龄:" + age + "\n"); return sb.toString(); }
@Override protected Employee clone() { Employee employee = null; try { employee = (Employee) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return employee; } }
|
Test.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| package com.mingrisoft;
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List;
public class Test {
public static void main(String[] args) { Employee employee = new Employee("李四", 25); long currentTime = System.currentTimeMillis(); List<Employee> employeeList1 = new ArrayList<Employee>(); int times = 100000; for (int i = 0; i < times; i++) { Employee employee2 = employee.clone(); employeeList1.add(employee2); } System.out.println("克隆花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒"); currentTime = System.currentTimeMillis(); List<Employee> employeeList2 = new ArrayList<Employee>(); for (int i = 0; i < times; i++) { ByteArrayOutputStream byteArrayOut = witeEmployee(employee); Employee employee2 = readEmployee(byteArrayOut); employeeList2.add(employee2); } System.out.println("序列化花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒"); }
private static Employee readEmployee(ByteArrayOutputStream byteArrayOut) { Employee employee = null; ByteArrayInputStream bais = new ByteArrayInputStream(byteArrayOut.toByteArray()); ObjectInputStream in = null; try { in = new ObjectInputStream(bais); employee = (Employee) in.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } return employee; }
private static ByteArrayOutputStream witeEmployee(Employee employee) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = null; try { out = new ObjectOutputStream(baos); out.writeObject(employee); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } return baos; } }
|
运行效果
1 2
| 克隆花费时间:13毫秒 序列化花费时间:1992毫秒
|
可以看到使用序列化实现的深拷贝所需要的时间比深拷贝的时间要长的多
技术要点
使用ByteArrayOutputStream
和ByteArrayInputStream
可以将对象保存在内存中,这样就不必产生一个本地文件来完成序列化的功能。