异常处理
-
捕获异常
-
处理多个异常的代码相同,用
|合并到一起public static void main(String[] args) { try { process1(); process2(); process3(); } catch (IOException | NumberFormatException e) { System.out.println("Bad input"); } catch (Exception e) { System.out.println("Unknown error"); } }
-
-
抛出异常
-
为能追踪到完整的异常栈,在构造异常时,把原始
Exception实例传进去,新的Exception就可以持有原始Exception信息public class Main { public static void main(String[] args) { try { process1(); } catch (Exception e) { e.printStackTrace(); } } static void process1() { try { process2(); } catch (NullPointerException e) { // 将e传进去 throw new IllegalArgumentException(e); } } static void process2() { throw new NullPointerException(); } }java.lang.IllegalArgumentException: java.lang.NullPointerException at Main.process1(Main.java:15) at Main.main(Main.java:5) Caused by: java.lang.NullPointerException at Main.process2(Main.java:20) at Main.process1(Main.java:13)Caused by:指向根源问题可以使用
Throwable.getCause()获取原始异常。如果返回null说明已是根异常。 -
异常屏蔽
用
origin保存原始异常,调用Throwable.addSuppressed()把原始异常添加进来,最后在finally抛出:public class Main { public static void main(String[] args) throws Exception { Exception origin = null; try { System.out.println(Integer.parseInt("abc")); } catch (Exception e) { origin = e; throw e; } finally { Exception e = new IllegalArgumentException(); if (origin != null) { e.addSuppressed(origin); } throw e; } } }
-
-
断言assert
断言条件
x >= 0预期为true。如果计算结果为false,则断言失败,抛出AssertionErrorassert x >= 0: "x must >= 0";
反射(Reflection)
程序在运行期可以拿到一个对象的所有信息。
-
Class类
一个
Class实例包含了该class的所有完整信息。-
获取一个
class的Class实例(3个方法)-
通过一个
class的静态变量classClass cls = String.class; -
通过一个实例变量的
getClass()获取String s = "Hello"; Class cls = s.getClass(); -
已知一个
class的完整类名,通过静态方法Class.forName()获取Class cls = Class.forName("java.lang.String");
因为
Class实例在JVM中是唯一的,所以上述方法获取的是同一个实例,可以用==比较两个Class实例。 -
-
获取父类
public class Main { public static void main(String[] args) throws Exception { Class i = Integer.class; Class n = i.getSuperclass(); } } -
获取interface
public class Main { public static void main(String[] args) throws Exception { Class s = Integer.class; Class[] is = s.getInterfaces(); for (Class i : is) { System.out.println(i); } } }getInterfaces只返回当前类直接实现的接口类型,不包括父类实现的接口类型。
-
-
访问字段
-
Class提供以下方法获取字段Field getField(name):根据字段名获取某个public的field(包括父类)Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)Field[] getFields():获取所有public的field(包括父类)Field[] getDeclaredFields():获取当前类的所有field(不包括父类)
-
Field包含一个字段所有信息:getName():返回字段名称,例如,"name";getType():返回字段类型,也是一个Class实例,例如,String.class;getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
Field f = String.class.getDeclaredField("value"); f.getName(); // "value" f.getType(); // class [B 表示byte[]类型 int m = f.getModifiers(); Modifier.isFinal(m); // true Modifier.isPublic(m); // false Modifier.isProtected(m); // false Modifier.isPrivate(m); // true Modifier.isStatic(m); // false -
获取/设置字段值
// 不管这个字段是不是public,一律允许访问 f.setAccessible(true); Object value = f.get(p); f.set(p, "Xiao Peng");
-
-
调用方法(类比访问字段)
-
调用方法(
invoke())遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。
public class Main { public static void main(String[] args) throws Exception { String s = "Hello world"; Method m = String.class.getMethod("substring", int.class); String r = (String) m.invoke(s, 6); Method m = Integer.class.getMethod("parseInt", String.class); // 调用该静态方法 Integer n = (Integer) m.invoke(null, "12345"); } } -
构造方法
-
public无参数构造方法
Person p = Person.class.newInstance(); -
任意构造方法
Class.newInstance()只能调用public无参数构造方法,其他不可。为了调用任意构造方法,Java反射API提供了Constructor对象public class Main { public static void main(String[] args) throws Exception { // 获取构造方法Integer(int) Constructor cons1 = Integer.class.getConstructor(int.class); Integer n1 = (Integer) cons1.newInstance(123); // 获取构造方法Integer(String) Constructor cons2 = Integer.class.getConstructor(String.class); Integer n2 = (Integer) cons2.newInstance("456"); } }
-
-
-
动态代理(Dynamic Proxy)
JDK提供的动态创建接口对象的方式:
Proxy.newProxyInstance()- 定义一个
InvocationHandler实例,它负责实现接口的方法调用 - 通过
Proxy.newProxyInstance()创建interface实例,它需要3个参数:- 使用的
ClassLoader,通常就是接口的ClassLoader - 需要实现的接口数组,至少需要传入一个接口进去
- 用来处理接口方法调用的
InvocationHandler实例
- 使用的
- 将返回的
Object强制转型为接口
public class Main { public static void main(String[] args) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method); if (method.getName().equals("morning")) { System.out.println("Good morning, " + args[0]); } return null; } }; Hello hello = (Hello) Proxy.newProxyInstance( Hello.class.getClassLoader(), // 传入ClassLoader new Class[] { Hello.class }, // 传入要实现的接口 handler); // 传入处理调用方法的InvocationHandler hello.morning("Bob"); } } interface Hello { void morning(String name); } - 定义一个