HeadFirst Java
本人有C语言基础,通过阅读Java廖雪峰网站,简单速成了java,但对其中一些入门概念有所疏漏,阅读本书以弥补。
第一章 Java入门
第二章 面向对象
第三章 变量
第四章 方法操作实例变量
第五章 程序实战
第六章 Java函数库
第七章 继承与多态
第八章 深入多态
第九章 构造器与垃圾收集器
第十章 数字与静态
第十一章 异常处理
第十二章 GUI(内部类)
第十三章 Swing
第十四章 保存对象
第十五章 网络与线程
第十六章 数据结构
前言
上一小节,我们着重介绍了ArrayList,以及使用sort方法排序歌单。遇到了编译错误的问题,引入了泛型的介绍,以及comparable与comparator两个接口解决 排序的问题。这一小节继续介绍其他的集合。
泛型的理解不用太扣,就是泛型的类,E会被替代为实际类型, 什么时候替代,一般是创建实例 和 实现接口时 会用到具体的类。
书中对于什么时候替代并没有具体指出,这里是我自己的理解了。
当歌单数据有重复时,我们并不需要记录重复歌名,怎么办呢?
`
对象相等
引用相等
指向同一个对象的引用相等,使用”==“,根据引用的bits pattern(位模式)进行比较,指向同一对象的 引用的 bits 相等。
equals() 默认执行”==“操作
references to two different objects will contain a diffterent bits pattern
hasCode(),获取对象的哈希值,因为指向同一对象,所以相等
hasCode()默认返回对象特有的序号(哈希值),这个值根据内存位置计算,因此每个对象的哈希值都不相同
对象相等
堆上的两个不同对象 在意义上相同
必须覆盖继承Object的hasCode() 和 equls()
覆写hasCode,比如标题相等,hashcode就返回相等值
覆写equals,比如标题相等,equls就返回true
if (foo.equals(bar) && foo.hasCode() == bar.hasCode())
HashSet如何检查重复
先根据对象的哈希值放入Set,同时会比较Set中其他对象的哈希值
所以必须覆写hashCode(),否则不可能有相同哈希值的对象
如果hashCode()找到相同hascode的两个对象,会进一步调用equals()比较是否在意义上相等(等效)。
总结eqauls()与hasCode()
两个对象相等
- 哈希值 ,hasCode()返回相等
- a.equals(b),b.equals(a)相等
equals即是对象相等
哈希值相等 不能推 对象相等,对象相等 必推 哈希值相等
—— 若equals被覆盖,hasCode也必须覆盖
——a.equals(b) 为true,则 a.hasCode == b.hasCode 为true。反之无
为什么不同对象 有相同hashCode的情况?
HashSet通过hashCode加快查询对象的速度,ArrayList则是从头遍历。
答:这与hashCode的计算算法有关,兼顾效率的同时,尽可能让不同对象的hashcode不同,但总可能会有相同情况。这时hashSet则会再根据equals进一步验证。可以理解为,hashCode是初步缩小查找范围。
TreeSet
保持有序,工作原理与sort()一样,具体回忆上一小节
1.元素的类实现Comparable接口
2.传入Comparator对象参数
Map
回到泛型
普通数组工作方式
使用多态参数与泛型
思考:ArrayList参数可以接受ArrayList对象吗?
普通数组Animal[]可以传入Dog[]
大家动手试试的话,显然编译会报错?那么为啥呢,明明数组是可以的,集合咋就G了
——不妨反证,如果可以会怎样?
public void takeAnimal(ArrayList<Animal> animals) {animals.add(new Cat());
}
如上,cat加入ArrayList当然合理,但是你传入ArrayList给该方法,那么Cat就会混进去Dog的集合,编译器当然不会允许这种情况
结论:形式参数声明为ArrayList 只会取用 ArrayList 具体对象参数。
这时候就更奇怪了,那同理数组我也可以传个cat给dog数组
public void test() {Dog[] dogs = {new Dog()};takeAnimals(dogs);
}
public void takeAnimal(Animal[] ani) {ani[0] = new Cat();
}
确实可以通过编译,但运行期JVM会发现并丢出异常
结论:数组类型在JVM运行期间检查,集合的类型检查只在编译期间
泛型的万用字符
那么到底如何多态化集合参数呢?像普通数组那样传入猫狗猪,且能eat()——万用字符 ?
public void takeAnimal(ArrayList<? extends Animal> animals) {for (Animal a:animals) //可以通过a.eat();animals.add(new Cat());//不能通过编译,一旦使用万用字符,编译器阻止任何破坏 引用参数集合 的操作
}
使用万用字符,编译器只允许操作集合,不允许新增元素,避免 将猫加入狗集合 问题,保障执行期间安全
相同功能的语法:上一节提到,泛型方法 使用 未定义在泛型类的 形式参数
public <T extends Animal> void take(ArrayList<T> list)
一般如何选择呢?通常使用万用字符,除非如下更有效率
练习