Java反射机制学习笔记

Java反射机制学习笔记 1. 反射机制概述作用通过反射机制可以操作字节码文件。相关类位于java.lang.reflect.*包中重要类包括java.lang.Class—— 代表整个字节码代表整个类java.lang.reflect.Method—— 代表字节码中的方法代表类中的方法java.lang.reflect.Constructor—— 代表字节码中的构造方法代表类中的构造方法java.lang.reflect.Field—— 代表字节码中的属性代表类中的成员变量要操作一个类的字节码就要先获取这个类的字节码Class 对象。2. 获取 Class 对象的三种方式2.1 第一种Class.forName()静态方法参数是一个完整的类名必须带包名java.lang包也不能省略示例Classc1Class.forName(java.lang.String);该方法会导致类加载。如果只希望执行一个类的静态代码块而不执行其他代码可以使用这个方法静态代码块在类加载时执行且只执行一次。2.2 第二种getClass()Java 中任何对象都有这个方法通过对象可以获取对应的类的字节码。Strings111;Classc2s.getClass();// 得到 String.classbooleanb(c1c2);// true2.3 第三种.class属性Java 中任一类型都有.class属性包括基本数据类型。Classc3String.class;booleanb2(c1c3);// true3. 通过反射实例化对象Class类提供了newInstance()方法会调用该类的无参构造方法。Objectoc3.newInstance();// 调用 String 类的无参构造通过反射机制创建对象的优点在于灵活性可以在不改变源码的情况下实现不同对象的实例化。4. 文件路径与资源加载直接使用绝对路径会导致移植性差。可以使用一种与代码位置无关的通用路径前提是文件必须放在类路径下src目录下就是类路径src是类的根路径。4.1 获取文件的绝对路径StringpathThread.currentThread().getContextClassLoader().getResource(从src之后的文件路径).getPath();getContextClassLoader()线程对象的方法获取当前线程的类加载器对象。getResource()类加载器对象的方法当前线程的类加载器默认从类的根路径下加载资源。4.2 以流的形式返回InputStreamreaderThread.currentThread().getContextClassLoader().getResourceAsStream(从src之后的文件路径);4.3 资源绑定器ResourceBundlejava.util包提供了资源绑定器便于获取属性配置文件中的内容。使用该方式时属性配置文件必须放在类路径下。资源绑定器只能绑定.properties文件且写路径时不写扩展名。ResourceBundlebundleResourceBundle.getBundle(文件路径);Stringsbundle.getString(key);// 获取 value5. 类加载器5.1 什么是类加载器专门负责加载类的命令/工具即ClassLoader。5.2 JDK 自带的三个类加载器启动类加载器Bootstrap ClassLoader扩展类加载器Extension ClassLoader应用类加载器Application ClassLoader5.3 加载过程与双亲委派机制假设有以下代码Stringa111;代码开始执行前会将所需类全部加载到 JVM 中。见到以上代码类加载器会找到String.class文件加载过程如下首先通过“启动类加载器”加载。启动类加载器专门加载jre/lib/rt.jarrt.jar是 JDK 最核心的类库。如果启动类加载器加载不到则通过“扩展类加载器”加载。扩展类加载器专门加载jre/lib/ext/*.jar。如果扩展类加载器也加载不到最后通过“应用类加载器”加载。应用类加载器专门加载classpath中的类。这种“启动类加载器 → 扩展类加载器 → 应用类加载器”的机制称为双亲委派机制是一种安全机制。6. 反射操作 Field6.1 获取 FieldClasscClass.forName(完整类名);// 获取类中所有 public 修饰的 FieldField[]fieldsc.getFields();// 获取所有的 FieldField[]fsc.getDeclaredFields();for(Fieldfield:fs){intifield.getModifiers();// 修饰符代号StringmModifier.toString(i);// 转为字符串Stringftfield.getType().getName();// 属性类型Stringfnfield.getName();// 属性名称}6.2 反编译 Field通过反射将类的属性源码还原出来ClasscClass.forName(完整类名);Field[]fieldsc.getFields();StringBuildersnewStringBuilder();s.append(Modifier.toString(c.getModifiers()) class c.getSimpleName() {\n);Field[]fsc.getDeclaredFields();for(Fieldfield:fs){s.append(\t);intifield.getModifiers();StringmModifier.toString(i);s.append(m);s.append( );Stringftfield.getType().getSimpleName();s.append(ft);s.append( );Stringfnfield.getName();s.append(fn);s.append(;\n);}s.append(});System.out.println(s);6.3 通过反射访问对象的属性重点给属性赋值 / 获取属性的值ClasscClass.forName(完整类名);Objectoc.newInstance();// 获取属性Fieldfc.getDeclaredField(属性名称);// 若要访问私有属性必须先打破封装f.setAccessible(true);// 赋值f.set(o,要赋的值);// 获取属性的值f.get(o);7. 可变长度参数语法类型...例如int... a参数个数可以是 0 到 N 个类型不能改变可变长度参数在形参列表中只能放在最后面且只能有一个可变长度参数可以当作数组使用也可以直接传入数组8. 反射操作 Method8.1 获取 MethodClasscClass.forName(完整类名);Method[]msc.getDeclaredMethods();for(Methodm:ms){Stringnm.getName();// 方法名Stringtm.getReturnType().getSimpleName();// 返回值类型StringmdModifier.toString(m.getModifiers());// 修饰符列表Class[]ptsm.getParameterTypes();// 形参类型for(Classpt:pts){Stringpnpt.getSimpleName();}}8.2 通过反射机制调用方法重点ClasscClass.forName(完整类名);Objectoc.newInstance();Methodmc.getDeclaredMethod(方法名,形参类型.class,...);ObjectretValuem.invoke(o,实参,...);// 获取返回值9. 反射操作 Constructor9.1 获取 ConstructorClasscClass.forName(完整类名);Constructor[]csc.getDeclaredConstructors();// 反编译方式同之前 Field 的反编译9.2 使用反射机制创建对象ClasscClass.forName(完整类名);// 调用无参构造Objectoc.newInstance();// 获取有参构造方法Constructorconc.getDeclaredConstructor(形参类型.class,...);Objecto2con.newInstance(实参,...);10. 获取父类和父接口重点ClasscClass.forName(完整类名);Classscc.getSuperclass();Class[]infc.getInterfaces();// 一个类可以实现多个接口所以用数组