异常体系
Exception 和 Error的区别
Exception 和 Error 都继承于 Throwable,在 Java 中,只有 Throwable 类型的对象才能被 throw 或者 catch,它是异常处理机制的基本组成类型。
Exception 和 Error 体现了 Java 对不同异常情况的分类。
- Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应的处理。
- Error 是指在正常情况下,不大可能出现的情况,绝大部分 Error 都会使程序处于非正常、不可恢复的状态。既然是非正常,所以不便于也不需要捕获,常见的 OutOfMemoryError 就是 Error 的子类。
Exception 又分为 checked Exception 和 unchecked Exception。
checked Exception 在代码里必须显式的进行捕获,这是编译器检查的一部分。
unchecked Exception 也就是运行时异常,类似空指针异常、数组越界等,通常是可以避免的逻辑错误,具体根据需求来判断是否需要捕获,并不会在编译器强制要求
return问题答案
catch块和finally块中的代码都执行了,但是finally块中的返回值覆盖了catch中的return值
try,catch,finally中可以同时都出现return,一旦return写在最后,要么在try,catch,finally都不写return,要么在try中写return,才能通过编译。需要特别注意的是,基本数据类型(就不再强行用内存解释了),简单点记忆就是基本数据类型返回值是“覆盖”,引用类型是“叠加”
https://juejin.im/post/5a9cec866fb9a028d042ef47//陷阱
一般情况下finally块中的代码是一定会被执行,除非JVM停止工作
缺点
影响性能
程序进入catch里,非常耗资源
Try/with/resources
return,break,continue
return的功能是结束一个方法
continue的功能和break有点类似,区别是continue只是中止本次循环,接着开始下一次循环
break则是完全中止整个循环
答案是否定的,也就是不会打印输出这句话。
不论try块是正常结束,还是中途非正常退出,也就是发生各种异常,finally块确实都会执行。然而在这个程序中,程序中try块中增加了System.exit(0);来退出程序,try语句根本就没有结束其执行过程,System.exit(0);将停止当前线程和所有其他当场死亡的线程。finally块并不能让已经停止的线程继续执行
代理
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题
其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理
解耦合
静态代理
优点
解耦合、隐藏实现
缺点
- 代理类和委托类实现了相同的接口,代理类通过委托类实现了相同的方法。这样就出现了大量的代码重复
- 代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了
动态代理
动态代理是在运行时,通过反射机制实现动态代理,并且能够代理各种类型的对象
通过InvocationHandler代理不同类型的对象,如果我们把对外的接口都通过动态代理来实现,那么所有的函数调用最终都会经过invoke函数的转发,因此我们就可以在这里做一些自己想做的操作,比如日志系统、事务、拦截器、权限控制等。这也就是AOP(面向切面编程)的基本原理
InvocationHandler handler = new ProxyHandler(hello);
HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);
proxyHello.sayHello();
public class ProxyHandler implements InvocationHandler{
private Object object;
public ProxyHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before invoke " + method.getName());
method.invoke(object, args);
System.out.println("After invoke " + method.getName());
return null;
}
}
动态代理的实现方式,AOP的实现方式
- JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- CGlib动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- 区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类
泛型
简单的说,意义和作用有:
类型的参数化,就是可以把类型像方法的参数那样传递。这一点意义非凡。
泛型使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常。
Java 语言中引入泛型是一个较大的功能增强
消除强制类型转换
类型安全
泛型中的类型擦除是什么
https://www.cnblogs.com/the-wang/p/10234727.html
https://www.cnblogs.com/coprince/p/8603492.html
https://www.jianshu.com/p/986f732ed2f1
JVM 是如何实现泛型的?
Java 中的泛型不过是一个语法糖,在编译时还会将实际类型给擦除掉,不过会新增一个 checkcast 指令来做编译时检查,如果类型不匹配就抛出 ClassCastException。
不过呢,字节码中仍然存在泛型参数的信息,如方法声明里的 T foo(T),以及方法签名 Signature 中的 “(TT;)TT”,这些信息可以通过反射 Api getGenericXxx 拿到。
除此之外,需要注意的是,泛型结合数组会有一些容易忽视的问题。数组是协变且具体化的,数组会在运行时才知道并检查它们的元素类型约束,可能出现编译时正常但运行时抛出 ArrayStoreException,所以尽可能的使用列表,这就是 Effective Java 中推荐的列表优先于数组的建议。这在我们看集合源码时也能发现的到,比如 ArrayList,它里面存数据是一个 Object[],而不是 E[],只不过在取的时候进行了强转。还有就是利用通配符来提升 API 的灵活性,简而言之即 PECS 原则,上取下存。典型的案例即 Collections.copy 方法了:
Collections.copy(List<? super T> dest, List<? extends T> src);
泛型
https://blog.csdn.net/weixin_44339238/article/details/104970464