JAVA核心技术I卷
第五章
5.1 类、超类和子类
5.1.4 阻止继承:final类和方法
final类里面,所有的方法默认都是final类型的,但是成员变量不是这样。
5.1.5 强制类型转换
在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。因为子类拥有比父类更多的属性、更强的功能,所以父类转换为子类需要强制。那么,是不是只要是父类转换为子类就会成功呢?其实不然,他们之间的强制类型转换是有条件的。
当我们用一个类型的构造器构造出一个对象时,这个对象的类型就已经确定的,也就说它的本质是不会再发生变化了。在Java中我们可以通过继承、向上转型的关系使用父类类型来引用它,这个时候我们是使用功能较弱的类型引用功能较强的对象,这是可行的。但是将功能较弱的类型强制转功能较强的对象时,就不一定可以行了。
举个例子来说明。比如系统中存在Father、Son两个对象。首先我们先构造一个Son对象,然后用一个Father类型变量引用它:
Father father = new Son();
在这里Son 对象实例被向上转型为father了,但是请注意这个Son对象实例在内存中的本质还是Son类型的,只不过它的能力临时被消弱了而已,如果我们想变强怎么办?将其对象类型还原!
Son son = (Son)father;
这条语句是可行的,其实father引用仍然是Father类型的,只不过是将它的能力加强了,将其加强后转交给son引用了,Son对象实例在son的变量的引用下,恢复真身,可以使用全部功能了。
前面提到父类强制转换成子类并不是总是成功,那么在什么情况下它会失效呢?
当引用类型的真实身份是父类本身的类型时,强制类型转换就会产生错误。例如:
Father father = new Father();
Son son = (Son) father;
这个系统会抛出`ClassCastException异常信息。`
所以编译器在编译时只会检查类型之间是否存在继承关系,有则通过;而在运行时就会检查它的真实类型,是则通过,否则抛出ClassCastException异常。
所以在继承中,子类可以自动转型为父类,但是父类强制转换为子类时只有当引用类型真正的身份为子类时才会强制转换成功,否则失败。
所以在将超类转换为子类之前,应该使用instanceof进行检查.
5.1.6抽象类
抽象类里面不全是抽象方法,也可以有非抽象方法。
5.2 Object:所有类的超类
5.2.1 equals方法
1.equals():
超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等。使用Object的equals()方法是比较两个对象的内存地址是否相等,即若object1.equals(object2)为true,则表示equals1和equals2实际上是引用同一个对象。但是我们必须要清楚我们很大部分时间都是进行两个对象的比较,这个时候Object的equals()方法就不可以了,实际上JDK中,String、Math等封装类都对equals()方法进行了重写。如String的equals()方法是进行内容比较,而不是引用比较。
2.在equals()中使用getClass进行类型判断:
我们在覆写equals()方法时,一般都是推荐使用getClass来进行类型判断,不是使用instanceof。
3.equals()与==
- 使用==比较原生类型如:boolean、int、char等等,使用equals()比较对象。
- ==返回true如果两个引用指向相同的对象,equals()的返回结果依赖于具体业务实现。
- 字符串的对比使用equals()。
5.2.2 相等测试与继承
如何判断两个object相等,参见文中这章节。
5.5参数数量可变的方法
…表示可以接收任意数量的参数,如printf(String fmt,Object… args),这里的Object…相当于Object[]。
5.7 反射
第六章 接口和内部类
6.1 接口
接口中的方法默认都是public的,可以包含常量,被自动设为public static final。
Arrays类中的sort方法可以对对象数组进行排序,但对象所属的类必须实现了Comparable接口。
6.2 对象克隆
当拷贝一个变量时,原始变量和拷贝变量引用同一个对象。
默认的拷贝是浅拷贝,如果是基本类型则拷贝没有问题,但它并没有克隆包含在对象中的内部对象。如果在对象中包含了子对象的引用,拷贝的结果会使得两个域引用同一个子对象。要实现深拷贝,则要实现cloneable接口。
6.3 接口和回调
6.4 内部类
为什么使用内部类?
1.内部类可以访问该类所在作用域的数据,包括私有的数据;
2.可以对同一个包中的其他类隐藏起来;
3.匿名内部类实现回调。
6.4.5 由外部方法访问final变量
当某个变量为final,但又不得不进行修改时,可以使用一个数组。参见本节例子。
6.5 代理
第12章 泛型程序设计。。。
第13章 集合
13.2 具体的集合
LinkedList使用ListIterator来遍历。虽然可以通过get(index)来随机获取元素,但是效率非常低,每次都要从头开始遍历到指定位置。所有如果需要随机访问,建议数组或arrayList。
第14章 多线程
14.3 线程状态
14.3.4 被终止的线程
join():等待指定线程的中止。参见浅析 Java Thread.join()
14.4 线程属性
14.4.2 守护线程
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程) 。用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。
定义:守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。
优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为
守护线程的方式是在 线程对象创建之前用线程对象的setDaemon方法。
example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
生命周期:守护进程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。
14.4.3 未捕获异常处理器