Java反射

Java反射

G i t H u b 链 接 \color{red}{GitHub链接} GitHub

1.Class 类

  • 类是对象,类是java.lang.Class类的实例对象

    public class MyClass {
        public static void main(String[] args) {
            // People的实例对象
            People people = new People();
            // 任何一个类,都是java.lang.Class的实例对象
        }
    }
    // People本身就是一个对象,是java.lang.Class的对象
    class People { }
    
  • Class类的三种表达方式

    Class c1 = People.class;
    // 方法2:通过已知类对象的getClass方法
    Class c2 = people.getClass();
    // 方法3:通过Class类静态方法forName()
    Class c3 = Class.forName("com.nj.reflect.People");
    
    /*
     * c1、c2表示了People类的类类型(class type)
     * 类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型
     */
    // 不管是c1、c2还是c3都代表People的类类型,一个类只可能是Class类的一个实例对象。
    System.out.print(c1 == c2); // true
    System.out.print(c1 == c3); // true
    
  • 获取对象实例

    // 通过People类的类类型c1、c2或c3创建该类的实例对象。
    People people1 = (People) c1.newInstance(); // 需要有无参构造方法
    

2.动态加载类

  • Class.forName(“类的全称”)

    • 不仅表示了类的类类型,还代表了动态加载类
    • 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。
  • 静态加载类

    public class Animal {
        public static void main(String[] args) {
            if (args[0].equals("Dog")) {
                Dog dog = new Dog();
                dog.eat();
            }
            if (args[0].equals("Cat")) {
                Cat cat = new Cat();
                cat.eat();
            }
        }
    }
    class Dog {
        public void eat() { }
    }
    class Cat {
        public void eat() { }
    }
    
    • 静态加载类的缺点

      1.如果Dog或者Cat不存在,会报错。但是Dog和Cat不一定会使用。

      2.通过new创建对象是静态加载类,在编译时刻,就需要加载所有可能使用到的类。

  • 动态加载类

    通过Class.forName()动态加载类,就不会在编译时报错了。

    public class AnimalManager {
        public static void main(String[] args){
            try {
                // 动态加载类,在运行时刻加载。编译不会再报错。
                Class c = Class.forName(args[0]);
                // 创建对象,通过类类型创建该类的对象
                Object o = c.newInstance();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    

    但是我们通过c.newInstance()创建类对象的时候,问题来了,是该转换成Dog,还是该转换成Cat。所以,需要统一标准,创建Animals接口,使Dog和Cat都实现Animals接口。

    public class AnimalManager {
        public static void main(String[] args){
            try {
                // 动态加载类,在运行时刻加载。编译不会再报错。
                Class c = Class.forName(args[0]);
                // 创建对象,通过类类型创建该类的对象
                Animals animals = (Animals) c.newInstance();
                animals.eat();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    interface Animals{
        void eat();
    }
    class Dog implements Animals{
        @Override
        public void eat() {}
    }
    class Cat implements Animals{
        @Override
        public void eat() {}
    }
    
    • 优点

      1.动态加载,更加灵活

      2.方便拓展,如果想要加个Pig类,直接创建Pig类,实现Animals接口和方法即可,无需再修改AnimalManager类。

3.反射的使用

  • 获取class对象的相关信息

    1.获取class对象的成员变量

    // 获取class1对象的所有成员变量,不包括父类的成员变量
    Field[] allFields = class1.getDeclaredFields();
    // 获取class1对象的public成员变量,包括父类里面的成员变量
    Field[] publicFields = class1.getFields();
    // 获取class1对象指定的成员变量,父类的无法获取
    Field name1 = class1.getDeclaredField("name");
    // 获取class1对象指定的public成员变量,包括查找父类
    Field name2 = class1.getField("name");
    
    // 获取变量的名称
    String name = field.getName();
    // 获取变量的类型
    Class type = field.getType();
    

    2.获取class对象的方法

    // 获取class1对象的所有方法,不包括父类的方法
    Method[] allMethods = class1.getDeclaredMethods();
    // 获取class1对象的public方法,包括父类的public方法
    Method[] publicMethods = class1.getMethods();
    // 获取class1对象指定的方法,父类的无法获取
    Method method1 = class1.getDeclaredMethod("getName");
    // 获取class1对象指定的方法,并且有一个int类型参数的方法
    Method method2 = class1.getDeclaredMethod("getName", int.class);
    // 获取class1对象有两个参数的指定方法
    Method method3 = class1.getDeclaredMethod("getName", int.class, String.class);
    // 获取class1对象指定的public方法,包括父类的public方法。
    Method method4 = class1.getMethod("getName");
    

    3.获取class对象的构造函数

    // 获取class1对象所有的构造函数
    Constructor[] allConstructors = class1.getDeclaredConstructors();
    // 获取class1对象public的构造函数
    Constructor[] publicConstructors = class1.getConstructors();
    // 获取class1对象指定参数的构造函数
    Constructor constructor1 = class1.getDeclaredConstructor(int.class);
    // 获取class1对象指定参数并且为public的构造函数
    Constructor constructor2 = class1.getConstructor(int.class, String.class);
    

    4.class对象的其他方法

    Annotation[] annotations = class1.getAnnotations();// 获取class对象的所有注解
    Annotation annotation = class1.getAnnotation(Override.class);// 获取class对象指定注解
    Class superclass = class1.getSuperclass(); // 获取class对象直接父类的类对象
    Type genericSuperclass = class1.getGenericSuperclass(); // 获取class对象的直接父类的Type
    Type[] genericInterfaces = class1.getGenericInterfaces(); // 获取class对象所有接口的Type集合
    String name = class1.getName(); // 获取class对象名称,包括报名路径
    String simpleName = class1.getSimpleName(); // 获取class类名
    Package aPackage = class1.getPackage(); // 获取class包信息
    boolean annotation = class1.isAnnotation();// 判断是否为注解类
    boolean anEnum = class1.isEnum();// 判断是否为枚举
    boolean anInterface = class1.isInterface();// 判断是否为接口类
    boolean array = class1.isArray();//判断是否为集合类型
    boolean primitive = class1.isPrimitive(); // 判断是否为原始类型(8个基础类型)
    boolean anonymousClass = class1.isAnonymousClass(); // 判断是否是匿名内部类
    boolean annotation1 = class1.isAnnotationPresent(Override.class);//判断是否被某个注解类修饰
    

    5.Method的相关方法

    Method method = class1.getMethod("getName", String.class);
    String name = method.getName(); // 获取方法名称
    // 获取方法的返回值类型的类类型
    Class returnType = method.getReturnType();
    // 获取方法的参数列表类型的类类型的数组
    Class[] parameterTypes = method.getParameterTypes();
    
  • Java反射的使用

    1.生成来的实例对象

    // 方法一:调用newInstance()方法,但是这个方法要求class1的类必须有无参构造函数。
    Dog dog = (Dog) class1.newInstance();
    // 方法二:通过getConstructor获取有参的Constructor构造函数,再调用newInstance()创建实例
    Constructor constructor = class1.getConstructor(String.class);
    Dog dog1 = (Dog) constructor.newInstance("dog");
    

    2.调用类的方法

    // 第一步:生成类对象
    Dog dog = (Dog) class1.newInstance();
    // 第二步:通过class对象的getMethod()等方法,获得具体需要执行的Method对象。
    Method method = class1.getDeclaredMethod("getName", String.class);
    // 如果方法是private修饰,直接调用invoke,会报错,Java要求程序必须有调用该方法的权限。
    // 调用setAccessible,并将参数设置为true,就会取消Java语言的访问权限检查。
    method.setAccessible(true);
    // 调用Method对象的invoke方法,第一个参数是调用该方法的实例对象,第二个参数是对应需要传入的参数。
    // invoke的返回值:如果方法的返回值为void,则invoke返回null,如果不是void,则返回具体的返回值
    // 相当于 dog.getName("dog");
    Object o = method.invoke(dog, "dog");
    

    3.访问成员变量的值

    // 第一步:生成类对象
    Dog dog = (Dog) class1.newInstance();
    // 第二步:通过getDeclaredField等方法,获取相应的Field对象
    Field field = class1.getDeclaredField("age");
    // 取消Java权限检查
    field.setAccessible(true);
    // 获取dog对象中age成员变量的值
    int age = field.getInt(dog);
    // 修改dog对象中age成员变量的值
    field.setInt(dog, 25);
    // 获取dog对象中String类型的成员变量
    Field field1 = class1.getDeclaredField("name");
    // 取消Java权限检查
    field1.setAccessible(true);
    // 获取name变量的值
    Object o = field1.get(dog);
    // 修改name变量的值
    field1.set(dog, "sam");
    

参考资料

1.Java反射

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值