第7章 泛型和反射
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?--> 1 获取一个泛型的实际泛型类型
class Utils {
// clz一定是子类的class,并且子类一定明确了父类的泛型T
public static Class getTClass(Class clz) {
// 获取clz父类的泛型type
Type type = clz.getGenericSuperclass();
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
// 得到泛型列表
Type[] types = pt.getActualTypeArguments();
if (types.length > 0 && types[0] instanceof Class) {
return (Class) types[0];
}
}
return (Class) Object.class;
}
}
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?--> 2 Class.forNmae不适合加载数组,建议使用Array.newInstance的方式
String[] strs = (String[]) Array.newInstance(String.class,8)
int[][] ints = (int[][]) Array.newInsgtance(int.class,2,3)
3 3 反射中的Accessible:用来判断是否需要安全检查,
在大量使用反射的情况下,设置Accessible=true(表示不需要安全检查),可以提高性能20部以上)
第8章:异常
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?--> 1 不要在finally块中处理返回值
原因: (1) 覆盖了try块中的return返回值
(2)
屏蔽异常:
如果try块中抛出了异常,但是finally块中直接return了,异常就会被屏蔽掉
2 构造函数不建议抛出异常
原因: (1) 构造函数没有覆盖的概念,而且子类的构造函数可以抛出比父类更加宽泛的异常,这样就会违反里氏替换原则
假设 Parent类的构造函数抛出IOException,sub类的构造函数抛出Exception.
将new Parent() 替换成 new Sub()时,就会需要更改异常处理的代码,这就违反了里氏替换原则
(2) 导致子类代码膨胀
如果父类默认构造函数抛出了异常,子类构造函数正好调用了父类的无参构造函数,则子类也需要throws父类的异常或父类异常的父类
总之 不建议构造方法抛出异常
3 通过new Throwable().getStackTrace() 方法获取当前栈信息 : Throwable类的构造函数会记录下栈帧信息
例子如下
public class Hi{public static void main(String[] args){
Throwable ex = new Throwable();
StackTraceElement[] stackElements = ex.getStackTrace();
if(stackElements != null){for(int i = 0; i < stackElements.length; i++) {
System.out.println(stackElements[i].getClassName());
System.out.println(stackElements[i].getFileName());
System.out.println(stackElements[i].getLineNumber());
System.out.println(stackElements[i].getMethodName());
}}}}
第9章 多线程和并发
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
1 Thread类中有一个stopBeforeStart: 标志着是在线程启动前设置了停止标志.
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->
1 Thread类中有一个stopBeforeStart: 标志着是在线程启动前设置了停止标志.
(1) 如果在线程没有调用start前,调用了stop方法,则stopBeforeStart会设置成true
(2) start方法会先将线程启动,然后判断stopBeforeStart==true时,将线程停止,所以
线程在启动前,先调用stop方法后,在调用start方法会导致线程运行先运行后停止
2 不建议使用stop
原因:stop会直接将线程停止,所以会导致代码逻辑不完整,破坏原子逻辑,舍去所以的锁
3 线程异常处理器:UncaughtExceptionHandler
4 volatile可以保证变量的可见性,但是无法保证同步
5 线程的状态
线程运行时间可分成3部分:启动(新建),可运行(运行,阻塞,等待),终结(销毁)
启动==>可运行==>终结的顺序不可逆.
为了减少启动和销毁线程的时间,线程池出现了.
正常情况下一个线程执行完run方法的逻辑后,会被销毁,
销毁后的线程无法调用start再次进入运行状态.所以为了保证线程池中的线程在启动后,随时可运行,需要确保线程执行完任务后,保持阻塞状态,而不是被销毁.Java并发包中的线程池实现就是采用阻塞队列确保线程在没有任务时阻塞,而不是被销毁
6 并发包线程池实现
主要有3个主要部分:任务队列(采用阻塞队列),工作线程和任务接口
(1) 任务接口:runnable ,callable接口,
(2) 任务队列:存放等待处理的任务,一般是BlockingQueue的实现类
(3) 工作线程:线程池中的线程,一般是在可运行状态或者等待阻塞状态下的线程
7 lock可实现公平锁
(1) lock.tryLock :自旋锁,可以只定等待时间,等待时间内无法获得锁,自行终结任务.注意:在没有得到锁时,线程会自旋在那里,不会放弃cpu
8 阻塞队列blockingQueue:
(1) 阻塞队列的容量是固定的,
无法超出,否则抛异常.默认初始容量为integer的最大值
(2) 特殊的put方法:如果队列已经满了,put操作会一直循环等待,直到插入为止