Java的反射机制 及 操作Class类

反射

一、静态语言和动态语言

1.1 静态语言

静态语言是在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型,运行时结构不可变。

例如:C、C++、Java、Delphi、C#等。

1.2 动态语言

动态语言是在运行时可以改变结构的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型。

例如:PHP、Ruby、Python、SQL、JavaScript等。

Java不是动态语言,但因为反射,Java可以称之为“准动态语言”。

二、反射机制

2.1 定义

  • Java反射机制是在运行状态中
  • 对任意一个类,都能够知道这个类的所有属性和方法
  • 对任意一个对象,都能够调用这个对象的任意方法和属性
  • 这种动态获取信息以及动态调用对象方法的功能,称为 Java 语言的反射机制,是 Java 被视为动态语言的关键

反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

2.2 用途

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时获取所需的私有成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

2.3 优缺点

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响。

2.4 反射的使用

通过反射获取类的Class对象:

  • Class.forName(包名) 获得Class对象
  • 一个类在内存中只有一个Class对象
  • 一个类被加载后,类的整个结构都会被封装在Class对象中
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("cn.edu.scau.reflection.User");
        Class c2 = Class.forName("cn.edu.scau.reflection.User");
        System.out.println(c1);
        System.out.println(c1 == c2);
        // 一个类在内存中只有一个Class对象,因此输出true
    }
}

class User {
    private String name;
    private int age;

    // 省略构造方法
    // 省略getter、setter方法
    // 省略toString方法
}

三、Class类

3.1 定义

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在 JVM 中只会有一个Class实例
  • 一个Class对象对应的是一个加载到 JVM 中的一个.class文件
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整地得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,针对任何想动态加载、运行的类,唯有先获得相应的Class对象

3.2 获取Class类的常用方法

  • 已知具体的类,通过类的class属性获取,该方法安全性和可靠性最高

    Class clazz = Person.class;
    
  • 已知某个类的实例,调用该类的getClass()方法获取Class对象

    Person person = new Person();
    Class clazz = person.getClass();
    
  • 已知一个类的全名和路径,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常

    public class Test {
        public static void main(String[] args) throws ClassNotFoundException {
            Class clazz = Class.forName("cn.edu.scau.reflection.Person");
        }
    }
    
  • 基本内置类型的包装类都有一个Type属性

    Class type = Integer.TYPE;
    
  • 获得父类类型的Class对象

    public class Test {
        public static void main(String[] args) throws ClassNotFoundException {
            Student student = new Student();
            Class clazz = student.getClass();
            Class superclass = clazz.getSuperclass();
            System.out.println(superclass);
            // 输出:class cn.edu.scau.reflection.Person
        }
    }
    
    class Person {}
    
    class Student extends Person{}
    

3.3 哪些类型可以有Class对象

  • class:外部类,成员(成员内部类、静态内部类),局部内部类,匿名内部类
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void
Class c1 = Object.class;        //类
Class c2 = Runnable.class;      //接口
Class c3 = String[].class;      //数组
Class c4 = int[][].class;       //二维数组
Class c5 = ElementType.class;   //枚举
Class c6 = Override.class;      //注解
Class c7 = Integer.class;       //基本数据类型
Class c8 = void.class;          //void
Class c9 = Class.class;         //Class

四、获取Class对象信息

  • 获取Class对象

    Class clazz = Class.forName("cn.edu.scau.reflection.User");
    
  • 获取类的名字

    System.out.println(clazz.getName()); // 包名 + 类名
    System.out.println(clazz.getSimpleName()); // 类名
    
  • 获取类的属性

    // 本类 + 父类 + public
    Field[] fields = clazz.getFields();
    for (Field field : fields) {
    	System.out.println(field);
    }
    
    // 本类 + 所有(public + private + proteced)
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field declaredField : declaredFields) {
    	System.out.println(declaredField);
    }
    
  • 获取指定属性

    // 本类 + 父类 + public
    Field name = clazz.getField("name");
    
    // 本类 + 所有(public + private + proteced)
    Field age = clazz.getDeclaredField("age");
    
    System.out.println("name: " + name + "\nage: " + age);
    
  • 获取类的方法

    // 本类 + 父类 + public
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
    	System.out.println(method);
    }
    
    // 本类 + 所有(public + private + proteced)
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
    	System.out.println(declaredMethod);
    }
    
  • 获取指定方法

    // 本类 + 父类 + public
    Method getName = clazz.getMethod("getName");
    // 需要传入数据类型的class文件
    Method setAge = clazz.getMethod("setAge", int.class);
    System.out.println(getName);
    System.out.println(setAge);
    
    // 本类 + 所有(public + private + proteced)
    // clazz.getDeclaredMethod()
    
  • 获取类的构造器

    // 本类 + 父类 + public
    Constructor[] constructors = clazz.getConstructors();
    for (Constructor constructor : constructors) {
    	System.out.println(constructor);
    }
    
    // 本类 + 所有(public + private + proteced)
    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    for (Constructor declaredConstructor : declaredConstructors) {
    	System.out.println(declaredConstructor);
    }
    
  • 获取指定构造器

    Constructor constructor = clazz.getConstructor(String.class, int.class);
    System.out.println(constructor);
    

五、调用运行时类的指定结构

5.1 创建对象

  • 本质是调用无参构造器,并且要有访问权限,不能是私有构造器
  • 如果没有无参构造则报错
Class clazz = Class.forName("cn.edu.scau.reflection.User");
User user = (User) clazz.newInstance();
  • 通过有参构造器也可以创建对象
  • 通过Class的getConstructor() 方法获取构造器
  • 向构造器中传入有参构造器的参数,实例化构造器
Constructor constructor = clazz.getConstructor(String.class, int.class);
User user = (User) constructor.newInstance("冬", 18);

5.2 调用方法

  • 通过反射调用方法
  • 获取方法后调用invoke() 方法激活,传递(对象,方法值)
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user,"冬"); // 激活,传递(对象,方法值)
System.out.println(user.getName());

5.3 操作属性

  • 设置setAccessible() 方法
  • 方法、属性、构造器 都有setAccessible() 方法
  • setAccessible() 是启动和禁用访问安全的开关,如果设置为true,则可以访问private修饰的方法
  • 如果反射使用得多的话,可以开启
Field age = clazz.getDeclaredField("age");
age.setAccessible(true); // 关闭程序的安全权限、获取私有属性或者方法
age.set(user,18); // 设置(对象,属性值)
System.out.println(user.getAge());

六、使用反射操作注解

  • 编写注解,一个作用在类上,一个作用在属性上

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @interface ClassAnno {
        String value();
    }
    
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface FieldAnno {
        String value();
    }
    
  • 编写实体类,并为实体类得属性和方法添加注解

  • 作用在类上得注解表示User的身份

  • 作用在属性上的注解分别是对应的值

    @ClassAnno("学生")
    public class UserDemo {
        @FieldAnno("冬")
        String name;
        @FieldAnno("18")
        int age;
    
        public UserDemo() {
        }
    
        public UserDemo(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  • 编写测试类

  • 使用getAnnotation() 方法可以获取注解

  • 再使用注解.value() 方法可以获取该注解的值

    public class TestDemo {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
            Class clazz = Class.forName("cn.edu.scau.reflection.UserDemo");
    
            ClassAnno classAnno = (ClassAnno) clazz.getAnnotation(ClassAnno.class);
            String classValue = classAnno.value();
            System.out.println("你的身份是:" + classValue);
    
            Field name = clazz.getDeclaredField("name");
            FieldAnno nameAnno = name.getAnnotation(FieldAnno.class);
            String nameValue = nameAnno.value();
            System.out.println("你的名字:" + nameValue);
    
            Field age = clazz.getDeclaredField("age");
            FieldAnno ageAnno = age.getAnnotation(FieldAnno.class);
            String ageValue = ageAnno.value();
            System.out.println("你的年龄:" + ageValue);
        }
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值