• Java(十二)——反射


    反射

    一、反射

    通过Class实例获取class信息的方法称为反射(Reflection)

    • JVM为每个加载的classinterface创建了对应的Class实例来保存classinterface的所有信息

    • 获取一个class对应的Class实例后,就可以获取该class的所有信息;

    二、Class类及其实例

    1、Class类

    Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识,即所谓的RTTI。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
    简单解释上面一段话:
    1、Class类也是类的一种,只是名字和class关键字高度相似。Java是大小写敏感的语言。
    2、Class类的对象内容是你创建的类的类型信息,比如你创建一个shapes类,那么,Java会生成一个内容是shapes的Class类的对象。
    3、Class类的对象不能像普通类一样,以 new shapes() 的方式创建,它的对象只能由JVM创建,因为这个类没有public构造函数。
    4、Class类的作用是运行时提供或获得某个对象的类型信息。

    2、获取Class实例

    1、Class类的静态方法forName

    // 如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
    Class cls = Class.forName( String className);
    
    Class cls = Class.forName("java.lang.String");
    

    2、对象的getClass()方法

    // 如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取 
    Student student = new Student();
     Class<? extends Student> aClass1 = student.getClass();
    

    3、使用类名加.class

    // 直接通过一个class的静态变量class获取
    Class classes = Student.class;
    

    3、获取基本信息

    public class Main {
        public static void main(String[] args) {
            printClassInfo("".getClass());
            printClassInfo(Runnable.class);
            printClassInfo(java.time.Month.class);
            printClassInfo(String[].class);
            printClassInfo(int.class);
        }
    
        static void printClassInfo(Class cls) {
            System.out.println("Class name: " + cls.getName());
            System.out.println("Simple name: " + cls.getSimpleName());
            if (cls.getPackage() != null) {
                System.out.println("Package name: " + cls.getPackage().getName());
            }
            System.out.println("is interface: " + cls.isInterface());
            System.out.println("is enum: " + cls.isEnum());
            System.out.println("is array: " + cls.isArray());
            System.out.println("is primitive: " + cls.isPrimitive());
            System.out.println("---------------------------------------------");
        }
    }
    
    

    三、Class实例操作class的成员变量

    public class Student{
        private String name;
        private int age;
        
        public Student(){}
        
        public Student(String name) {
            this.name = name;
        }
    }
    
    
    public class Main{
        public static void main(String[] args){
            ...
        }
    }
    

    1、获取类的变量

    注意:

    getField(name)getDeclaredField(name)中 name 可能不存在,源码中用了throws来抛出可能出现的异常。所以在使用时,必须用try...catch...来捕获异常

    Field getField(name)	   // 根据字段名获取包括父类字段在内的某个public的field()
    Field getDeclaredField(name)		// 根据字段名获取当前类的某个field(不包括父类)
    Field[] getFields()			// 获取包括父类在内的所有public的field,返回值为数组
    Field[] getDeclaredFields()			// 获取当前类的所有field(不包括父类),返回值为数组
    
    Class cls = Student.class;
    try{
        Field f = cls.getDeclaredField("name");
    }catch(Exception e){
        if (e.equals('NoSuch...')){
            ......
        }
    }
    
    
    

    2、获取变量的属性

    getName()       //返回字段名称
    getType()			//返回字段类型,也是一个Class实例
    getModifiers()		// 返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
    
    Class cls = Student.class;
    try{
        Field f = cls.getDeclaredField("name");
        f.getName();    // name
        f.getType();		// String
        f.getModifiers();
    }catch(Exception e){
        if (e.equals('NoSuch...')){
            ......
        }
    }
    

    3、获取、修改变量的值

    get(obj);   // 获取obj对象xx变量值
    set(obj, value);   // 设置obj对象的xx变量的值为value
    
    // 获取s对象name的值
    Object s = new Student("Xiao Ming");
    Class c = s.getClass();
    try{
        Field f = c.getDeclaredField("name");
        f.setAccessible(true);     // 由于name为私有变量,外部无法访问,通过setAccessible(true)可以允许访问任何权限变量
        Object value = f.get(s);    // 获取值
        System.out.println(value); // "Xiao Ming"
    }catch(Exception e){
        if (e.equals('NoSuch...')){
            ......
        }
    }
    
     
    
    // 设置s对象的age变量的值
    Object s = new Student("Xiao Ming");
    Class c = s.getClass();
    Field f = c.getDeclaredField("age");
    f.setAccessible(true);
    f.set(s, 18)    // 设置值
    

    四、Class实例操作class的方法

    public class Student{
        private String name;
        private int age;
        
        public Student(){}
        
        public Student(String name) {
            this.name = name;
        }
        
        public void setName(String name){
            this.name = name
        }
        
        public static void add(int a){
            a += 1;
        }
        
        private void check(){
            ...
        }
        
    }
    
    
    public class Main{
        public static void main(String[] args){
            ...
        }
    }
    

    1、获取类的方法

    注意:

    getMethod(name)getDeclaredMethod(name)中 name 可能不存在,源码中用了throws来抛出可能出现的异常,所以在使用时,必须用try...catch...来捕获异常

    Method getMethod(name, Class...)		// 获取包括父类方法在内的某个public的Method
    Method getDeclaredMethod(name, Class...)		// 获取当前类的某个Method(不包括父类)
    Method[] getMethods()		// 获取包括父类方法在内的所有public的Method,返回一个数组
    Method[] getDeclaredMethods()		// 获取当前类的所有Method(不包括父类),返回一个数组
    
    Class cls = Student.class;
    try {
        
        Method method = cls1.getDeclaredMethod("setName", String.class);
    }catch (Exception e){
        ...
    }
    

    2、获取方法的属性

    getName()		// 返回方法名称
    getReturnType()     // 返回方法返回值类型,也是一个Class实例
    getParameterTypes()     // 返回方法的参数类型,是一个Class数组
    getModifiers()		// 返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
    
    Class cls = Student.class;
    try {
        // 获取setName方法,参数为String
        Method method = cls1.getDeclaredMethod("setName", String.class);
        System.out.println(method.getName());    //  setName
    }catch (Exception e){
        ...
    }
    

    3、调用方法

    invoke()     // 调用执行方法
    

    (1)调用public非静态方法

    invoke(obj, 参数)   // 对Method实例调用invoke就相当于调用该方法, 
        // invoke的第一个参数是对象实例,即在哪个实例上调用该方法,后面的可变参数要与方法参数一致,否则将报错。
    
    Student s = new Student("tom", 18)
    Class cls = s.getClass();
    try {
        // 获取setName方法,参数为String
        Method method = cls1.getDeclaredMethod("setName", String.class);
        method.invoke(s, "john")
    }catch (Exception e){
        ...
    }
    

    (2)调用public静态方法

    调用静态方法时,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null

    Student s = new Student("tom", 18)
    Class cls = s.getClass();
    try {
        // 获取setName方法,参数为String
        Method method = cls1.getDeclaredMethod("add", int.class);
        method.invoke(null, 5)
    }catch (Exception e){
        ...
    }
    

    (3)调用 非public 方法

    对于非public方法,我们虽然可以通过Class.getDeclaredMethod()获取该方法实例,但直接对其调用将得到一个IllegalAccessException。为了调用非public方法,我们通过Method.setAccessible(true)允许其调用。

    注意:

    • setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对javajavax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全
    Student s = new Student("tom", 18)
    Class cls = s.getClass();
    try {
        // 获取setName方法,参数为String
        Method method = cls1.getDeclaredMethod("check");
        Method.setAccessible(true)
        method.invoke(s)
    }catch (Exception e){
        ...
    }
    

    4、多态

    使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在)。

    即先从子类中找有无方法,没有再去父类

    五、调用构造方法

    注意:

    getConstructor(name)getDeclaredConstructor(name)中 name 可能不存在,源码中用了throws来抛出可能出现的异常,所以在使用时,必须用try...catch...来捕获异常

    getConstructor(Class...)		// 获取某个public的Constructor
    getDeclaredConstructor(Class...)		// 获取某个Constructor
    getConstructors()			// 获取所有public的Constructor
    getDeclaredConstructors()			// 获取所有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);
            System.out.println(n1);
    
            // 获取构造方法Integer(String)
            Constructor cons2 = Integer.class.getConstructor(String.class);
            Integer n2 = (Integer) cons2.newInstance("456");
            System.out.println(n2);
        }
    }
    
    

    六、获取继承关系

    Class i = Integer.class;
    // 获取class的父类
    Class n = i.getSuperclass();
    
    // 获取class的所有接口
    Class[] is = s.getInterfaces();
    
    // 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现。
    // Integer i = ?
    Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
    // Number n = ?
    Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
    // Object o = ?
    Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
    // Integer i = ?
    Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer
    
  • 相关阅读:
    企业微信应用授权
    exec存储过程示例
    jquery判断对象是否存在
    IScroll5要防止重复加载
    transitionEnd不起作用解决方法
    微信接口 output {"errMsg":"translateVoice:fail, the permission value is offline verifying"}
    javascript保留两位小数
    html取消回车刷新提交
    企业微信后台登录
    企业微信开启开发者工具
  • 原文地址:https://www.cnblogs.com/linagcheng/p/12186371.html
Copyright © 2020-2023  润新知