对程序可能存在的一种错误的排查或处理机制,称为异常机制
*java中提供了两种错误处理机制,一种是系统层面的Error和另一种程序层面的Exception,两种都继承自一个共同的父类Throwable
*Error类型的错误通常是程序中不可控的,例如常见的OutOfMemoryError堆内存溢出错误,需要来调整堆内存的大小或是查找出程序中导致了大量占用堆内存的地方进而优化。
*Exception是可控的,因此java异常机制主要是针对Exception
*非检查型异常指的是RuntimeException(运行时异常)及其子类,除此之外的都是检查型异常
*当方法体内抛出检查型异常,需要对其进行捕获或者声明在方法上,那么调用该方法的时候就得捕获,或者继续在方法上声明抛出
throw是在方法体中声明可能会抛出一个异常,throws是在方法上声明该方法一定会抛出一个或多个异常
final声明一个常量
finalize()是object提供的一个垃圾回收前执行的方法
finally是异常机制的代码块,在finally代码块中的代码,无论是否捕获到异常,都会执行
检查型异常:
ClassNotFoundException:类找不到
SQLException:数据库访问相关异常
NoSuchFieldException:变量不存在异常
NoSuchMethodException:NoSuchMethodException 方法不存在 当访问的某个方法不存在的时候抛出此异常
illegalAccessException: 非法访问 当访问某个类被拒绝的时候抛出此异常
InterruptedException:线程中断异常
运行时异常:
RuntimeException: 所有运行时异常的父类
NullPointerException:空指针异常
illegalArgumentException: 参数传递异常
IOException:IO异常
ArithmeticException:运算异常
IndexOutOfBoundsException:索引越界异常
ClassCastException:类型转换异常
在某一个Catch块中捕获到异常,并抛出一个新的异常称为异常链
代码示例:
//抛出检查异常的方法
public static void throwCheckException() throws NullPointerException {
throw new NullPointerException("检查异常");
}
//抛出运行时异常的方法
public static void throwUncheckException() {
try {
throwCheckException();
} catch (NullPointerException e) {
throw new RuntimeException("运行时异常",e);
}
}
//测试异常链
public static void main(String[] args) {
throwUncheckException();
}
控制台会自动打印出运行时异常信息:
Exception in thread "main" java.lang.RuntimeException: 运行时异常
at cn.qu.pool.TestThreadPool.throwUncheckException(TestThreadPool.java:20)
at cn.qu.pool.TestThreadPool.main(TestThreadPool.java:27)
Caused by: java.lang.NullPointerException: 检查异常
at cn.qu.pool.TestThreadPool.throwCheckException(TestThreadPool.java:13)
at cn.qu.pool.TestThreadPool.throwUncheckException(TestThreadPool.java:18)
... 1 more
且看下面两段代码执行结果:
public static void testFinallyOne() {
try{
System.out.println("try代码块打印信息...");
return;
}catch (Exception e) {
e.printStackTrace();
}finally{
System.out.println("finally代码块打印信息...");
}
}
控制台打印:
try代码块打印信息...
finally代码块打印信息...
public static void testFinallyTwo() {
try{
throw new Exception("try代码块中抛出了检查型异常...");
}catch (Exception e) {
System.out.println("catch代码块打印出异常信息:" + e.getMessage());
return;
}finally{
System.out.println("finally代码块打印信息...");
}
}
控制台打印:
catch代码块打印出异常信息:try代码块中抛出了检查型异常...
finally代码块打印信息...
结论:无论是try还是catch中有return,finally中的代码都会执行。其实再挖深一点的话,就是return不是没有执行,return形成了返回路径,然后才去执行的finally,最后返回
在运行期可以获取一个类的所有构造、属性和方法,并构建一个对象,并且可以操作对象的属性和方法,这种动态获取一个类的所有信息,并操作其对象的机制称为反射。
*当我们在编译时期无法得知一个对象的类型的时候,即该对象的类型是动态可变的,那么我们就需要用反射机制来获取该对象的类型。
*当我们再编译时期无法得知我们需要的是什么类的时候,即该类是动态可变的,那么我们可以通过反射机制使用一个类的完全限定名字符串来获取类以及构建并使用它
*当我们需要在程序中获取一个类的所有构造、属性和方法的时候可以使用反射机制
*通过类的完全限定名获取:Class.forName(String className)
*通过对象获取:object.getClass()
*通过类名获取:Object.class()
优点:反射的功能既是它的优点
缺点:性能差,打破了封装性
*spring的IOC机制底层用的就是反射技术来构建用户提供的bean的实例
*jdbc为各个厂商提供了驱动规范,各个厂商分别有自己对应的实现,jdbc就是通过用户使用不同类型的驱动类去构建驱动对象的,初始化的时候,通过反射去调用驱动对象的各个方法
用法示例:
1.打印某个类的所有构造,属性,方法
public static void reflectOne(String className) {
try {
Class> clazz = Class.forName(className);
Constructor[] constructors = clazz.getDeclaredConstructors();
System.out.println("-------------打印" + className + "的所有构造名称" + "-------------");
for (Constructor constructor : constructors) {
System.out.println(constructor.getName());
Class[] parameterTypes = constructor.getParameterTypes();
for (Class parameterType : parameterTypes) {
System.out.println(constructor.getName() + "构造的参数类型" + parameterType.getName());
}
System.out.println();
}
System.out.println("-------------打印" + className + "的所有属性名称" + "-------------");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("-------------打印" + className + "的所有方法名称" + "-------------");
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
Class>[] parameterTypes = method.getParameterTypes();
for (Class> parameterType : parameterTypes) {
System.out.println(method.getName() + "方法的参数类型" + parameterType.getName());
}
System.out.println();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
2.构建某个类的对象,并调用其方法
public class ReflectTest {
//测试
public static void main(String[] args) {
String className = "cn.qu.reflect.Person";
reflectTwo(className);
}
public static void reflectTwo(String className) {
try {
//加载类,并获取私有构造
Class> clazz = Class.forName(className);
Constructor> declaredConstructor = clazz.getDeclaredConstructor();
//如果该类的无参构造私有,则允许访问私有构造
declaredConstructor.setAccessible(true);
//构建实例
Object obj = declaredConstructor.newInstance();
//获取方法并执行
Method setId = clazz.getDeclaredMethod("setId",Long.class);
setId.invoke(obj, 20L);
Method getId = clazz.getDeclaredMethod("getId");
String id = getId.invoke(obj).toString();
//获取属性并设置值
Field field = clazz.getDeclaredField("name");
//因为属性都是私有的,所以需要设置允许访问
field.setAccessible(true);
field.set(obj, "Lucy");
System.out.println("id=" + id + " name=" + field.get(obj));
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Person {
private Long id;
private String name;
private Person() {
}
public Person(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
3.关于示例2的一些说明
示例2的流程是这样的:
A开发了reflectTwo接口,B开发了Person类,C就是我们啦。
A开发的时候并没有Person类,所以就用上了反射,也许有人疑问,A怎么知道B有什么方法呢,通常B类都是实现了A的一个规范,B里面的方法,基本都是固定的,A也只会操作它知道的方法,当然了A也可以操作B的未知方法,但这是没有什么意义的。