• [Java5新特性]反射


    什么是反射机制

    反射机制是Java 5版本提供的高级新特性,这种机制允许Java程序在运行状态中,对任意一个类都能知道该类的所有属性和方法;对任意一个对象,都能调用该对象的属性和方法。这种动态获取信息及动态调用对象的属性和方法的功能称之为Java的反射机制。

    在我们编写Java类文件时,真正运行的是编译之后的“.class”文件。而“.class”文件在运行时,被加载到内存后,都是一个Class类的对象。我们可以通过Java 5提供的反射机制获取到该类的构造器、方法及成员变量等。

    简单来说,Java的反射机制中提供了Class类、Constructor、Method及Field等,Class类就是类的元神,Constructor就是构造器的元神,Method就是方法的元神,Field就是成员变量的元神。

    反射中的Class类

    Class类是Java 5提供一个新类型,就叫做类类型。如何可以获取一个Class类型的对象呢?传统方式就是利用new Class()方式,但Java 5提供不同的方式。参看以下代码:

    public class Demo {
        @Test
        public void demo() throws ClassNotFoundException {
    
            // 第一种方式:类名.class
            Class c1 = int.class;
            Class c2 = int[].class;
    
            // 第二种方式:对象.getClass()
            Class c3 = "hello".getClass();
    
            // 第三种方式:Class.forName("类全名")
            Class c4 = Class.forName("app.java.reflect.Demo");
        }
    }
    

    通过以上代码我们可以了解,获取Class实例的方式有三种:

    • 类名.class,例如:Class c1 = Object.class;
    • 对象.getClass(),例如:Class c2 = “Hello”.getClass();
    • Class.forName(“类全名”),例如:Class c1 = Class.forName(“app.java.reflect.Demo”);

    获取到Class类实例之后,有什么具体用途呢?第一,我们可以通过Class类实例的getName()和getSimpleName()方法,获取到对应类的全类名或类名。

    public class Demo {
        @Test
        public void demo() throws ClassNotFoundException {
            Class c1 = "hello".getClass();
            // java.lang.String
            System.out.println(c1.getName());
    
            Class c2 = int[].class;
            // [I:"["表示数组,"I"表示int类型
            System.out.println(c2.getName());
            // int[]
            System.out.println(c2.getSimpleName());
        }
    }
    

    第二,我们可以通过Class类实例的getSuperclass()方法,获取到对应类的完整继承链关系。

    public class Demo {
        @Test
        public void demo() throws ClassNotFoundException {
            Class c = java.awt.Frame.class;
            while (c.getSuperclass() != null) {
                System.out.println(c.getSuperclass().getName());
                c = c.getSuperclass();
            }
        }
    }
    

    第三,我们可以通过反射机制来创建对象。

    public class Demo {
        @Test
        public void demo() throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            String className = "app.java.reflect.User";
            Class c = Class.forName(className);
            User user = (User) c.newInstance();
            user.setUsername("king");
            System.out.println(user.getUsername());
        }
    }
    
    class User{
        private String username;
        public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        private String password;
    }
    

    反射中的Constructor

    传统Java方式获取类的实例,基本都是通过构造器来实现,反射机制中的Class类同样具有构造器。那反射机制中Class类的构造器如何获得呢?我们来看以下代码:

    public class Demo {
    
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            // 获取c类型中所有public构造器
            Constructor[] constructors1 = c.getConstructors();
            // 获取c类型中所有构造器
            Constructor[] constructors2 = c.getDeclaredConstructors();
    
            System.out.println(constructors1.length);
            System.out.println(constructors2.length);
    
            // 获取没有参数的public构造器
            Constructor con1 = c.getConstructor();
            // 获取参数类型依次为String.class,String.class的public构造器
            Constructor con2 = c.getConstructor(String.class,String.class);
            // 获取参数类型为String.class的构造器
            Constructor con3 = c.getDeclaredConstructor(String.class);
        }
    }
    

    通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的构造器。

    • getConstructors()方法:获取Class类所有的public构造器,其中包含没有参数和有参数的。
    • getDeclaredConstructors()方法:获取Class类所有的构造器,其中包含由public、private和protected修饰符的。
    • getConstructor()方法:获取具体的public修饰构造器,参数表示是否具有参数的构造器。
    • getDeclaredConstructor()方法:获取具体的构造器,其中包含由public、private和protected修饰符的。

    我们得到Class类型的构造器之后,又会有什么样的用途呢?第一,可以通过构造器来创建实例对象。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
            Constructor<User> constructor = c.getConstructor(String.class,String.class);
            User user = constructor.newInstance("king","123");
            System.out.println(user);
        }
    }
    

    第二,我们可以构造器来打印指定类型的所有构造器,以及参数类型。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<String> c = String.class;
            Constructor[] constructors = c.getDeclaredConstructors();
            for (Constructor constructor : constructors) {
                System.out.print(constructor.getDeclaringClass().getSimpleName() + "(");
                Class[] classes = constructor.getParameterTypes();
                for (int i = 0; i < classes.length; i++) {
                    System.out.print(classes[i].getSimpleName());
                    if (i < classes.length - 1) {
                        System.out.print(", ");
                    }
                }
                System.out.println(")");
            }
        }
    }
    

    反射中的Method

    与Class类的构造器类似的就是方法,我们首先讨论如何获取方法。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            // 获取本类和父类中的所有public方法
            Method[] ms1 = c.getMethods();
            System.out.println(ms1.length);
    
            // 获取本类中所有方法
            Method[] ms2 = c.getDeclaredMethods();
            System.out.println(ms2.length);
    
            // 获取本类或父类中名称为setUsername,参数类型为String的public方法
            Method m1 = c.getMethod("setUsername", String.class);
    
            // 获取本类中声明的名称为toString的,没有参数的方法,它可以是任何访问级别,但它不能是父类中的方法
            Method m2 = c.getDeclaredMethod("toString");
        }
    }
    

    通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的方法。

    • getMethods()方法:获取Class类及父类的所有public方法。
    • getDeclaredMethods()方法:获取Class类的所有方法,其中包含由public、private修饰符的。
    • getMethod()方法:获取Class类及父类中具体的public方法。
    • getDeclaredMethod()方法:获取Class类中具体的方法,其中包含由public、private修饰符的。但不能是父类中的方法。

    下面我们来看一个利用Class类实例方法的练习。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            Method method = c.getMethod("setUsername", String.class);
    
            Object object = c.newInstance();
    
            method.invoke(object, "king");
    
            System.out.println(object);
        }
    }
    

    反射中的Field

    与Class类的构造器和方法类似的就是成员变量,我们首先讨论如何获取成员变量。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            // 获取本类和父类中的所有public成员变量
            Field[] fields1 = c.getFields();
            System.out.println(fields1.length);
    
            // 获取本类中所有成员变量
            Field[] fields2 = c.getDeclaredFields();
            System.out.println(fields2.length);
    
            // 获取本类或父类中名称为password,参数类型为String的public成员变量
            Field field1 = c.getField("password");
    
            // 获取本类中名称为password的成员变量,但它不能是父类中的成员变量
            Field field2 = c.getDeclaredField("password");
        }
    }
    

    通过上面的代码,我们可以知道:在获取到Class类实例后,有四种方式可以获取到对应的成员变量。

    • getFields()方法:获取Class类及父类的所有public成员变量。
    • getDeclaredFields()方法:获取Class类的所有成员变量,其中包含由public、private修饰符的。
    • getField()方法:获取Class类及父类的具体成员变量。
    • getDeclaredField()方法:获取Class类的具体成员变量,其中包含由public、private修饰符的。但不能是父类中的方法。

    获取到Class类的成员变量之后,我们可以进行设置和获取操作。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            Field field = c.getField("password");
    
            Object object = c.newInstance();
    
            field.set(object, "123");
            System.out.println(field.get(object));
        }
    }
    
    

    需要注意的是这里只能设置和获取修饰符为public的成员变量,不能操作修饰符为private的成员变量。

    AccessibleObject

    AccessibleObject类是Constructor、Method和Field三个类的父类。AccessibleObject类的常用方法有以下几种:

    • isAccessible()方法:判断当前成员是否可访问。
    • setAccessible()方法:设置当前成员是否可访问。

    当Class类中的构造器、方法和成员变量是私有的时候,如果我们想反射操作的话,就必须先调用setAccessible(true)方法。

    public class Demo {
        @Test
        public void demo() throws Exception{
            Class<User> c = User.class;
    
            Field field = c.getDeclaredField("username");
    
            Object object = c.newInstance();
    
            field.setAccessible(true);
    
            field.set(object, "king");
    
            System.out.println(field.get(object));
        }
    }
    

    转载说明:请注明作者及原文链接,谢谢!

  • 相关阅读:
    解构赋值好处
    react中给state赋予初始值(使用dva)
    popover/ToolTip设置在root标签外面,如何设置样式-antd
    判断对象为空的情况
    HTML、XHTML、XML和HTML5之间的联系与区别
    innerHTML、innerText、outerHTML和value的区别
    JS面向对象经典题目(一)
    js中apply、call和bind的区别
    react的TodoList增删改
    JS原生addClass、removeClass实现
  • 原文地址:https://www.cnblogs.com/longestory/p/4566983.html
Copyright © 2020-2023  润新知