《Effective Java》(9~11)阅读笔记
来源:互联网 发布:淘宝视频剪辑收费 编辑:程序博客网 时间:2024/05/19 01:10
接着上期继续看本书高质量编码建议9、10、11条的阅读笔记
9.覆盖equals时总要覆盖hashcode方法
如果这个类仅仅是重写了equals方法而没有重写hashCode,那么这个类和基于散列的集合类一起工作时就会出现问题。
首先明确一个概念,两个对象使用equals返回true,则它们的hashCode也一定相等;如果两个对象的hashCode相等,则它们的equals则不一定相等。
如何实现hashCode,当然你可以使hashCode返回一个固定的数值,任何对象的hashCode都是一个固定的数值,这没有问题。但当它与基于散列的集合类一起工作时,这些元素将具有相同的散列码,进而使得所有对象都被映射到统一散列桶中,使得散列表退化为链表。10.始终要覆盖toString
这条建议我在实际当中遇到过,因为当时几乎并没有人去重写toString方法,使得我不得不在后来去将几乎所有的POJO类的toString方法都重写了。原因在于在有的场景下会打印一条日志,日志的内容就是POJO类的属性字段值,这个时候toString的意义很明显的就体现出来了,好在eclipse能按照一定的格式自动生成toString方法。有的类是自己已经重新实现了toString方法例如集合类。
11.谨慎的覆盖clone
按照书中的话来讲,能不重写clone就不要去重写,因为它带来的问题太多了。我们暂且不讨论这里面的陷阱有多少,只从对Java基础知识的掌握程度来说明什么是clone,以及什么是“深拷贝”和“浅拷贝”。
首先观察以下代码,并思考对象在内存中的分配以及引用的变化:
public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } 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 class Main { public static void main(String[] args) throws Exception{ Student stu = new Student("kevin", 23); Student stu2 = stu; stu2.setAge(0); System.out.println(stu.getAge()); }}
这是一段很简单的代码,Student对象实例stu、stu2在内存中的分配及引用分别如下图所示:
所以代码中出现修改stu2实例的age字段时,stu中的age字段也被修改了,原因很简单因为它们的引用指向的都是同一个对象实例。
那如果我们想在实例化一个name=”kevin”,age=23的Student实例怎么办呢?当然可以再写一段Student stu2 = new Student(“kevin”, 23);如果再重新构造一个对象实例很复杂,能不能直接复制呢?显然,使Student实现Cloneable接口并重写clone方法即可,注意此时的重写clone方法在里面仅有一句代码即是即调用父类的clone方法,而不是自定义实现:
public class Student implements Cloneable{ private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } 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; } @Override protected Student clone() throws CloneNotSupportedException { return (Student)super.clone(); }}public class Main { public static void main(String[] args) throws Exception{ Student stu = new Student("kevin", 23); Student stu2 = stu.clone(); stu2.setAge(0); System.out.println(stu.getAge()); }}调用clone方法产生的对象实例并不是之前的实例,而是在堆上重新实例化了一个各个参数类型值都相同的实例,所以此时修改stu2的age字段并不会影响到stu,看起来clone就是一个构造器的作用 -- 创建实例。
上面我们仅仅是说明了什么是clone,接下来我们接着来讲解什么是“深拷贝”和“浅拷贝”。
在上面的例子Student类中,我们新增一个引用型变量Test类:
public class Student implements Cloneable{ private String name; private int age; private Test test; public Student(String name, int age) { this.name = name; this.age = age; } 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 getTest() { return test; } public void setTest(Test test) { this.test= test; } @Override protected Student clone() throws CloneNotSupportedException { return (Student)super.clone(); }}
public class Main { public static void main(String[] args) throws Exception{ Student stu = new Student("kevin", 23); Student stu2 = stu.clone(); stu2.setAge(0); System.out.println(stu.getAge()); }}实际上测试这段代码可知,clone出来的stu2确实和stu是两个对象实例,但它们的成员变量实际上确是指向的同一个引用(通过比较hashCode可知),这也就是所谓的“浅拷贝”。对应的“深拷贝”则是所有的成员变量都会真正的做一份拷贝。怎么做到“深拷贝”,则是要求将类中的所有引用型变量都要clone。
/** * 深拷贝 * */public class Student implements Cloneable{ private String name; private int age; private Test test; public Student(String name, int age) { this.name = name; this.age = age; } 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 Test getTest() { return test; } public void setTest(Test test) { this.test = test; } @Override protected Object clone() throws CloneNotSupportedException { Student stu = (Student)super.clone(); stu.test = test.clone(); //Test类也要继承Cloneable return stu; }}
书中是不建议自定义重写clone方法的,如果非要重写书中总结为一句话:clone方法就是一个构造器,你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。
再说一个与本条目无关的点,查看Cloneable接口实际上可以发现里面什么方法都没有,clone方法却来自Object类,继承了Cloneable接口为什么就能重写clone方法了呢?原因在于clone方法在Object类中的修饰符是protected,而Cloneable接口和Object处于同一个包下,熟悉修饰符的都知道protected的权限限定在同一个包下或者其子类。Cloneable和Object同属于一个包,Cloneable自然能继承clone方法,继承了Cloneable接口的成为了它的子类同样也就继承了clone方法。
- 《Effective Java》(9~11)阅读笔记
- Effective Java 阅读笔记
- Effective Java阅读笔记
- 《Effective Java》阅读笔记(一)
- 《Effective Java》阅读笔记(二)
- 《Effective Java》阅读笔记(三)
- 《Effective Java》(1~2)阅读笔记
- 《Effective Java》(3~4)阅读笔记
- 《Effective Java》(5~6)阅读笔记
- 《Effective Java》(7~8)阅读笔记
- 《Effective Java》(12~16)阅读笔记
- 《Effective Java》(17~22)阅读笔记
- 《Effective Java》(23~25)阅读笔记
- 《Effective Java》(26~28)阅读笔记
- 《Effective C++》阅读笔记
- Effective C++阅读笔记
- effective stl 阅读笔记
- 《Effective C++》阅读笔记
- Java动态代理机制介绍(jdk和cglib的区别)
- 线代
- 1010. 一元多项式求导 (25)
- 简述Spring框架的Ioc
- cocoscreator实现微信内置浏览器点击图片识别图中二维码
- 《Effective Java》(9~11)阅读笔记
- 记录华为、魅族手机无法打印 Log 日志的问题
- 单链表的逆置
- hdu 1274 展开字符串
- Vue之style的用法
- 杭电oj1010题:Tempter of the Bone
- C中enum用法
- 2017.9.23 新Nim游戏 失败总结
- 索引在文件和内存中的数据结构(搜索阶段)