反射技术

    技术2026-03-03  8

    JavaSE

    一.类加载

    步骤

    1.加载

    通过一个类的全限定名来获取定义此类的二进制字节流。把数据传输到内存中(字节码文件 —javac—> 内存 从文件到内存就需要一个字节流)在内存中生成一个代表这个类的java.lang.Class对象。作为方法区这个类的各种数据访问入口。

    2.连接(绝大部分时间)

    验证:确保被加载类的正确性(why编译器已经通过了,还要再检查一次呢? 因为有些熟悉字节码文件的人会自己编写字节码文件,跳过编译器的检查,为了避免这一部分人的误操作;还有一个文件直接改为 .class,这种文件没有Magic Number(即ca fe ba be,每个真正的字节码文件才有的),不会因此混入。)准备:负责为类的静态成员分配内存并设置默认初始化值解析:将类中的符号引用(编译相关,跟语言无关)替换为直接引用(把地址填进去)。

    3.初始化

    给静态成员变量赋初值(真正赋值,之前只是赋默认值),执行静态代码块。

    类加载时机

    创建该类的对象时调用该类的静态变量时调用该类的静态方法时使用反射方式创建每个类或接口的Class对象时初始化某个类的子类,会先触发父类的加载java.exe命令来运行某个主类(java 类名)

    类加载器(jdk8)

    只完成加载中的第一步。

    Bootstrap ClassLoader 根类加载器 负责加载Java核心类的加载,在JDK中的JRE的lib中的rt.jar。(jar包是打包字节码文件的方式)JVM启动后就自动加载。Extension ClassLoader 扩展类加载器 加载JRE的扩展目录中jar包的加载(JDK中的JRE的lib目录下的ext目录中的jar包)System ClassLoader 系统类加载器 负责加载自己定义的Java类

    二.反射

    Java反射技术可以帮助我们在运行时发现和使用运行时的类型信息。即研究Class对象和它的使用

    获取Class对象

    通过该类对象对象获取

    对象.getClass()

    MyClass myClass = new MyClass(); Class classObj1 = myClass.getClass(); //通过该类对象 获取 所属类的Class对象 class MyClass { }

    通过字面值常量

    Class classObj = 类名.class

    Class classObj = MyClass.class; //类的字面值常量。类一加载完,类的对象就确定了。所以是一个常量。

    通过Class类的静态方法

    static Class forName(String className) 返回给定字符串名 的类或接口的Class对象 Class.forName(“全类名”)。

    Class classObj = Class.forName("com.google.www.reflection.getclass.MyClass") //全类名

    注意事项:

    第二种获取Class对象的方式比较简单,但是通过它触发类加载的过程不是一个完整的加载过程(类中的静态代码块没有执行,即没有完成类加载中的初始化工作)。相比较通过方式三获取Class对象,它所触发的类加载是一个完整的类加载过程。(学习数据库时需要使用)开发中一般用第3种办法,因为更加灵活

    第三种方式的常用操作:

    创建一个配置文件 resource bundle。后缀:.properties添加配置: 例:className(属性名)=com.google.www.reflection.getclass.MyClass(属性值) 注意,配置文件不需要双引号。读取配置文件中的配置信息 Properties properties = new Properties(); //直接创建对象 FileInputStream fis = new FileInputStream("配置文件"); //文件字节输入流 load时要用。写入相对路径时,要注意当前工作目录设置是否正确 /* 如果配置文件中的路径有中文。需要使用字符输入流 InputStreamReader isr = new InputStreamReader(fis,"GBK"); properties.load(isr); //重载的一个字符输入流读取方法。 */ properties.load(fis); //load方法利用指向配置文件的文件输入流,自动读取配置文件中的一条条配置,存储到Properties对象中,每条会自动分成属性名和属性值方式存储。(默认解码方式时ISO-8859-1 并没有中文) String className = properties.getProperty("className"); //获取属性值。都是String类型。 Class aClass = Class.forName(className);

    获取构造方法信息

    Java中的所有构造方法都由一个类来描述Constructor,所以每个构造方法都是一个Constructor对象。

    API

    一次获取多个构造方法: Contructor[] getContructors() //只返回public访问权限的构造方法。 Contructor[] getDeclaredConstructors() //返回所有的构造方法。 一次获取一个构造方法: Constructor<T> getConstructor(Class... parameterTypes) //public访问权限的单个指定的构造方法 Constructor<T> getDeclaredConstructor(Class... parameterTypes) //具有任意访问权限的单个指定的构造方法 注意事项: 1. 通过参数列表来区分不同的构造方法。获取单个构造方法是必须指明其参数列表。这些用法都是在已知构造方法的参数时使用的。 2. 参数列表中的参数类型要用对应的Class对象来表示。 例: Class integerClass = int.getClass(); 3. 数据类型... 指可变参数,即参数是可变的。 public static void callVararg() { varargMethod(1,2,3,4,5); // 可以传任意个参数 varargMethod(100); varargMethod(); } public static void varargMethod(int... a) { //可变参数只能有一个,且在参数列表的最后。 for(int i = 0; i < a.length; ++i) //访问可变参数的各个值 { System.out.println(a[i]) } } 获取多个构造方法: Class classObj = Class.forName("全类名"); //反射技术的起点是获取Class对象。 Contructor[] constructors = classObj.getContructors(); //获取public权限的多个构造方法 System.out.println(Arrays.toString(costructors)); Contructor[] declaredConstructors = classObj.getDeclaredConstructors(); //获取所有构造方法。 System.out.println(Arrays.toString(declaredConstructors)); //------------------------------------------------------------------------------------ 获取单个构造方法: Properities properities = new Properities(); FileInputStream fis = new FileInputStream("配置文件"); properities.load(fis); String s = properities.getProperity("属性名"); Class classObj = Class.forName(s); Constructor publicConstructor = classObj.getConstructor(int.class); //获取一个public权限的构造方法 System.out.println(publicConstructor); Constructor privateConstructor = classObj.getDeclaredConstructor(int.class, double.class, String.class); //获取任意构造方法 System.out.println(privateConstructor); //-------------------------------------------------------------------------------------------------------------- Constructor对象的使用: Constructor对象.newInstance(Object... initargs) //创建该类的新实例,并初始化该实例(用常规对象就好int,double...)。 //--------------------------------------------------------------------------------------------------- //完整演示: public class Demo1 { Class classObj = Class.forName("全类名"); Constructor privateConstructor = classObj.getDeclaredConstructor(int.class, double.class, String.class); int intValue = 1; double doubleValue = 2.5; String str = "zhangsan"; privateConstructor.setAccessible(true); //是否绕过权限检查。默认是false。调用后才可以使用下面的方法创建对象。因为获取的构造函数是private权限的,按道理不能在这个类中调用。 ConstructorClass o = (ConstructorClass) privateConstructor.newInstance(intValue, doubleValue, str); System.out.println(o); } //目标类 class ContructorClass { int intValue; double doubleValue; String strValue; // public 权限的构造方法 public ConstructorClass(int intValue) { this.intValue = intValue; } // protected 访问权限 protected ConstructorClass(double doubleValue) { this.doubleValue = doubleValue; } // 默认权限 ConstructorClass(int intValue, double doubleValue) { this.intValue = intValue; this.doubleValue = doubleValue; } private ConstructorClass(int intValue, double doubleValue, String strValue) { this.intValue = intValue; this.doubleValue = doubleValue; this.strValue = strValue; } public String toString() { return "ConstructorClass{" + "intValue=" + intValue + ", doubleValue=" + doubleValue + ", strValue='" + strValue + '\'' + '}'; } }

    获取成员变量信息

    Java中的成员变量都由Field类来描述。一个Field对象对应一个成员变量。

    API:
    获取多个成员变量: Filed[] getFields() //获取该Class对象和其父类的public成员变量。 Filed[] getDelaredFields() //获取该Class对象的所有成员变量,不包括父类定义的。 获取单个成员变量: Field getField(String name); // 通过指定成员变量名,从Class对象对应类中获取public成员变量(还可以获得父类的public成员变量)。步骤是先查子类中的成员变量,再查父类。如果没有则抛出异常。 Field getDeclaredField(String name); //获取任意权限的成员变量(不包括父类中的成员变量) 用法: Object get(Object obj); //用Field对象调用该方法,获取括号中参数对象对应的成员变量值。返回值可以根据Field对象的真实数据类型进行强转。 void set(Object obj, Object new Value); //设置成新值。 //类 class FieldFather { public int i = 100; // 定义和子类同名的成员变量 public boolean sonI; private double j = 6.6; } class FieldSon extends FieldFather { public int sonI; protected double sonJ; String name; private Object obj; private int privateValue = 1000; public FieldSon(int sonI, double sonJ, String name, Object obj) { this.sonI = sonI; this.sonJ = sonJ; this.name = name; this.obj = obj; } @Override public String toString() { return "FieldSon{" + "sonI=" + sonI + ", sonJ=" + sonJ + ", name='" + name + '\'' + ", obj=" + obj + ", privateValue=" + privateValue + "} "; } } //获取多个成员变量 Class sonClass = FieldSon.class; Field[] fields = sonClass.getFields(); //获取该Class对象和其父类的public成员变量。 System.out.println(fields); Field[] declaredFields = sonClass.getDeclaredFields();//获取该Class对象的所有成员变量,不包括父类定义的。 System.out.println(declaredFields); //---------------------------------------------------------------------- //获取单个成员变量 Field sonI = sonClass.getField("sonI"); Field protectedField = sonClass.getField("sonJ"); Field privateField = sonClass.getDeclaredField("obj"); System.out.println(privateField); //------------------------------------------------------------------------ Class fieldSon = FieldSon.class; //获取该类的Class对象 FieldSon fieldSon = new FieldSon(1,6.6,"张三",null); //创建该类的一个对象 Field privateField = fieldSon.getDeclaredField("privateValue"); //获取该类的privateValue成员变量。 int privateValue = (int) privateField.get(fieldSon); //获取fieldSon中的privateValue的值。返回值为Object所以要强转。 privateField.setAccessable(true); //绕过权限。 privateField.set(fieldSon,2000); //参数是(对应对象 + 新值)

    获取成员方法信息

    API

    Java中所有的成员方法都是由一个Method类描述的,一个Method对象对应一个方法。

    //获取多个方法 Method[] getMethods() //获取该Class对象的所有public方法和父类的public方法。 Method[] getDeclaredMethods() //获取该Class对象的所有方法 //获取单个方法 通过方法签名指定:方法名+参数列表 Method getMethod(String name, Class... parameterTypes); //获取当前类和父类定义的单个public方法。先子类查,再父类查,直到搜索到最顶层的父类。 Method getDeclaredMethod(String name, Class... parameterTypes); //获取当前类任意方法 //使用 Object invoke(Object obj, Object... args) //返回值为Object类型(有些方法会有返回值),可进行强转。 Method对象.invoke(调用该方法的对象,方法运行的实际参数); class MethodClass { public int publicMethod(int a, String b){ return 0; } protected String protectedMethod() {return "zhangsan";} void defaultMethod() {} private double privateMethod(boolean flag, double a, char c) { System.out.println("privateMethod"); return 0.5; } } Class methodClass = MethodClass.class; //获取多个方法 Method[] methods = methodClass.getMethods(); System.out.println(Arrays.toString(methods)); Method[] declaredMethods = methodClass.getDeclaredMethods(); System.out.println(Arrays.toString(declaredMethods)); //------------------------------------------------------------------------- //获取指定方法 Method publicMethod = methodClass.getMethod("publicMethod", int.class, String.class); System.out.println(publicMethod); Method privateMethod = methodClass.getDeclaredMethod("privateMethod",boolean.class,double.class,char.class); System.out.println(privateMethod); //----------------------------------------------------------------------------------- //Method对象调用方法 Method privateMethod = methodClass.getDeclaredMethod("privateMethod",boolean.class,double.class,char.class); MethodClass obj = new MethodClass(); privateMethod.setAccessible(true); //绕过检查机制 // obj.praveteMethod(true,8.8,'a'); 无法调用。 double a = (double) privateMethod.invoke(obj,true,8.8,'a'); //通过反射才能调用。

    获取静态变量和静态方法

    静态变量和方法的获取与普通变量和方法相同,区别在于使用上无需指定类的对象。

    class StaticClass { // 静态变量 static int a = 30; private static boolean staticMethod() { return false; } } Class staticClass = StaticClass.class; Field a = staticClass.getDeclaredField("a"); int o = (int)a.get(null); System.out.println(o); //输出30 a.set(null,300); System.out.println(staticClass.a); //输出300;
    Processed: 0.016, SQL: 9