1

Java反射机制

前言

什么是反射机制

反射是java语言的一个特性,它允程序在运行时(注意不是编译的时候)来进行自我检查并且对内部的成员进行操作。例如它允许一个java的类获取他所有的成员变量和方法并且显示出来。这个能特定我们不常看到,但是在其他的比如C或者C++语言中很不就存在这个特性。一个常见的例子是在JavaBean中,一些组件可以通过一个构造器来操作。这个构造器就是用的反射在动态加载的时候来获取的java中类的属性的。

主要的类

  • Class 类的实例表示正在运行的 Java 应用程序中的类和接口。Class没有公共的构造方法,Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

  • Constructor 提供关于类的单个构造方法的信息以及对它的访问权限(主要提供的是对构造方法使用)

  • Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)

  • Field 主要提供对类中的成员变量的访问和使用

Class<T>

Class类也使用了泛型,即是Class<T>这种形式的,可以直接使用一个具体的类传入,这样的话就不需要强制转换了,比如Class.newInstance()这样使用默认的构造方法构造一个对象就需要不再需要强制转换了即使用(ClassName)Class.newInstance()

常用的方法

  • getConstructor(Class[] params) 获取公共的(public)的构造方法,并且限定其中的参数个数和类型可以获得不同的公共构造方法

  • Constructor[] getConstructors() 返回所有的公共(public)的构造方法

  • getDeclaredConstructor(Class[] params) 获取所有指定的构造方法,但是需要注意的是当获取私有的构造方法的时候需要使用setAccessible设置访问权限为true才能进行构造,否则出现异常

  • Constructor[] getDeclaredConstructors() 返所有的构造方法包括public和private,protected修饰的

  • T newInstance() 返回的是一个调用默认的构造方法(public class_name())实例化的一个Object对象,如果使用泛型那么就返回T类型的,反之返回的是Object需要强制转换才能使用这个对象调用成员函数和成员变量

  • Class forName(String class_name) 返回class对象,每一个对都有一个方象法返回Class对象(test.class)

  • Package getPackage() 返回此类所在的包名(package demo) 当然也可以使用Package.getName()获得包的名字(demo)比如constructor.getPackage().getName()

  • int getModifiers() 返回的是类的修饰符的整数 类型(修饰符的类型有public private protected)其中得到整数可以使用Modifier中toString(int num)得到public,private,protected的类型,比如Modifier.toString(class1.getModifiers())
    * Method getMethod(String name, Class<?>... parameterTypes) 返回指定参数的方法Method对象,注意这里仅仅是返回的时公共的方法(public) 比如:Method method=class1.getMethod("display",new Class[]{int.class})这里的display是方法的名字,有一个参数,类型为int

  • Method[] getMethods() 获取所有的公共的方法(public)返回的是一个数组(Method)

  • Method getDeclaredMethod(String name,Class<?>... parameterTypes)返回所有的指定的参数的方法(public,private,protected,但是不包括继承的),其中参数可以为null(无参数)

  • Method[] getDeclaredMethods() 获取所有的方法

  • Field getField(String name) 指定名字的公共成员变量(public)

  • Field[] getFields() 获取所有的公共的成员变量

  • Field getDeclaredField(String name) 获取所有的指定名称的成员变量(public,protected,private),同样在调用私有成员变量的时候需要先设置访问的权限,field.setAccessible(true)

  • Field[] getDeclaredFields() 获取所有的成员变量(public,protected,private)

  • getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

  • URL getResource(String name) 查找指定名称的资源(图片,文件...)注意这个资源一定要和指定类在一个包中,否则返回null,比如查找Test类下的airplane.png图片:Test.class.getResource("airplane.png")这里返回的将是绝对路径

获取Class的对象并且实例化

  • 使用Class.forName(String className) 其中className一定是包含包的名字,下面的demo就是包的名字,Test是类的名字。这是最常用的方法,学过JDBC的都知道加载驱动的时候就是使用的Class.forName()

    /*
     * 第一种使用forName(String className),其中className一定是包含包的名字,下面的demo就是包的名字,Test是类的名字
     */
    Class cls=Class.forName("demo.Test");
    Test test=(Test)cls.newInstance();    //这里只是使用了默认的构造方法实例化对象
    
  • 使用类名.class

    Class cls=Test.class;
  • 使用对象.getClass()

    Test test=new Test();
    Class cls=test.getClass();   

Constructor<T>

  • 主要是用来对类的构造方法进行操作的,可以看出这个也使用了泛型,和上面的Class是一样的,注意这里如果没有使用泛型,那么原本放回T类型的现在都是返回Object

常用的方法

  • T newInstance(Object parms) 使用带有参数的构造方法实例化对象,如果使用了泛型,那么返回的就是T类型的,反之返回的是Object类型的,需要强制转换

  • getName() 以字符串的形式返回构造方法的名称,具体的路径包含包名(demo.Test)

  • int getModifiers()Class类中的方法一样

Method

主要提供的是对类中的方法的操作

常用的方法

  • Object invoke(Object obj,object args) 使用得到的Method对象调用方法,obj是类的已经构造好的对象,如果是静态方法直接写null,因为静态方法的调用不需要对象,返回值是Object类型的,如果接受返回值,需要使用强制转换成相应的类型,args是传入的参数,如果有多个参数,那么可以直接在后面用逗号添加或者直接创建数组new Object[]{22,"chenjiabing"}比如:method.invoke(test,22,"chenjiabing") method.invoke(test,new Object[]{22,"chenjiabing"})注意:如果调用的private类型的方法,那么需要在前面设置访问的权限,method.setAccessible(true)

  • String getName() 返回此方法的名字(display)

  • Modifier getModifiers() 返回此方法的修饰符的类型表示的整数(public,private...),可以使用Modifier.toString()转换成字符串形式

  • Class getReturnType() 返回这个方法的返回类型

  • String toString() 返回这个方法表示的字符串的形式

Field

主要提供对类的成员变量的操作

常用方法

  • String getName() 返回变量名字

  • Object get(Object obj) 返回此变量在指定对象中的值,因为在构造对象的时候每一个传入的变量的值都不一样,因此需要使用对象obj。obj表示传入的对象,返回的Object类型,因此需要强制转换

  • void set(Object obj,Object value) 改变obj对象上的变量的值为value

  • Modifier getModifiers() 返回整数表示修饰的类型

  • String getType() 获取变量的类型(int,String,double float.....)

Modifier

Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。修饰符集被表示为整数,用不同的位位置 (bit position) 表示不同的修饰符。

常用的方法

  • static String toString(int mode) 将代表修饰符的整数形式转换为字符串形式的修饰符,比如将1转换成public

  • static isInterface(int mode) 如果整数参数包括 interface 修饰符,则返回 true,否则返回 false

  • static isStatic(int mode)

  • static isPrivate(int mode)

  • static isPublic(int mode)

  • static isAbstract(int mode)

实例

     Modifier.toString(Test.class.getModifiers())    //得到Test类的修饰符

使用

有了上面的铺垫,我们就可以使用上面的这些类进行操作了,在进行操作之前,我们需要先定义一个类Test,放在demo包下,内容如下

package demo;

import java.util.jar.Attributes.Name;

import javax.print.attribute.standard.MediaSize.NA;

public class Test {
    public String name;
    private int age;

    public Test() {
        this.name = "陈加兵";
        this.age = 23;
    }

    public Test(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void display() {
        System.out.println("name=" + this.name + "----age=" + this.age);
    }

    public void set(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private int getAge() {
        return this.age;
    }

}

实例化对象

  • 使用Class默认的构造newInstance()

        Class class1=Class.forName("demo.Test");  //静态加载Class
        Test test=(Test)class1.newInstance();  //调用默认的构造方法(public Test())实例化对象,由于没有使用泛型,因此需要强转
        test.display();    //调用display方法
  • 使用Class中的getConstructor()方法构造对象,需要注意的使用private类型构造方法时一定要先设置访问权限为true-constructor.setAccessible(true);

        /*
         *调用public Test(String name,int age)得到Constructor的两种形式
         *   1.Constructor constructor=class1.getConstructor(new Class[]{String.class,int.class});
         *   2.Constructor constructor=class1.getConstructor(String.class,int.class);这个和上面的是一样的,就是使用的参数形式不一样
         * 
         * 
         * 
         * 
         *使用newInstance()构造对象的两种方式
         *   1.Test test=(Test)constructor.newInstance(new Object[]{"chenjiabing",22});
         *     2.Test test=(Test)constructor.newInstance("chenjiabing",22); 只是形式不同而已,不过我还是喜欢上面的形式
         *
         */
        
        
        
        
        /*
         * 调用public Test(String name,int age)
         *         Class.getConstructor()得到的是公共的构造方法,如果有私有的构造方法,那么就会报错,这时就要使用getDeclaredConstructor(Class<?>... parameterTypes)
         *         Test test=(Test)constructor.newInstance("陈加兵",22);
         * 
         * 
         * 调用public Test() 
         *      Constructor constructor=class1.getConstructor(null);
         *      Test test=(Test)constructor.newInstance(null);
         * 
         * 
         * 调用private Test(int age)
         *         Constructor constructor=class1.getDeclaredConstructor(new Class[]{int.class});
                constructor.setAccessible(true);   //因为private类型是不可以直接访问的,因此需要设置访问权限为true
                Test test=(Test)constructor.newInstance(new Object[]{1000});
        */
        
        
        
        Class class1=Class.forName("demo.Test");
        //访问public Test(String name,int age)
//        Constructor constructor=class1.getConstructor(new Class[]{String.class,int.class});
//        Test test=(Test)constructor.newInstance("陈加兵",22);
        
        //访问默认的构造方法
//        Constructor constructor=class1.getConstructor(null);
//        Test test=(Test)constructor.newInstance(null);

        //访问private类型的构造方法
        Constructor constructor=class1.getDeclaredConstructor(new Class[]{int.class});
        constructor.setAccessible(true);
        Test test=(Test)constructor.newInstance(new Object[]{1000});
        test.display();
        

成员方法的操作

使用Class.getMethod()Class.getDeclaredMethod()方法获取方法,这两个方法的区别前面已经说过了,注意的是调用私有成员方法的之前一定要设置访问权限(method.setAccessible(true))

Method类中的其他方法前面也已经说过了,详细使用请自己尝试

        /*
         * 获取Method对象的两种方式:
         *         1.Method method_set=class1.getMethod("set", new Class[]{String.class,int.class});
         *         2.Method method_set=class1.getMethod("set", String.class,int.class);
         * 
         * 
         * 使用Method.invoke()调用方法的两种方式
         *         1.Object o=method_set.invoke(test, new Object[]{"陈加兵",200});
         *         2.Object object=method_set.invoke(test, "陈加兵",2000);
         */
        
        
        
        
        /*
         * 获取公共方法(public):
         *         1.Method method=class1.getMethod("display",null);  //public void display()
         *         2.Method method_set=class1.getMethod("set", new Class[]{String.class,int.class});  //获取public void set(String name,int age)
         * 
         * 
         * 获取私有方法(private,protected)
         *         1.Method method_getAge=class1.getDeclaredMethod("getAge", null);
         */
        
        
        //使用构造方法构造一个Test对象
        Class class1 =Class.forName("demo.Test");
        Constructor<Test> constructor=class1.getDeclaredConstructor(new Class[]{String.class,int.class});
        Test test=constructor.newInstance(new Object[]{"陈加兵",22});
        
        
        Method method=class1.getMethod("display",null);   //获取public void display()方法的Method对象
        Object obj=method.invoke(test, null);    //调用方法display
        
        //获取public void set(String name,int age)
//        Method method_set=class1.getMethod("set", new Class[]{String.class,int.class});
        Method method_set=class1.getMethod("set", String.class,int.class);
        
//        Object o=method_set.invoke(test, new Object[]{"陈加兵",200});
        Object object=method_set.invoke(test, "陈加兵",2000);
        test.display();
        
        
        //获取私有方法private int getAge()
        Method method_getAge=class1.getDeclaredMethod("getAge", null);
        method_getAge.setAccessible(true);   //必须设置访问权限为true
        //判断返回值类型是否为int类型的
        if("int".equals(method_getAge.getReturnType().toString()))
        {
            int ReturnData=(int) method_getAge.invoke(test, null);   //调用并且获取返回值
            System.out.println(ReturnData);
        }
        

成员变量的操作

主要使用的Field类,前面已经详细的说过了

/*
         * 获取public修饰的成员变量:
         *         1.Field field=class1.getField("name"); //获取public的成员变量name的Filed对象
         * 
         * 获取private,protected修饰的成员变量:
         *         1.    Field field2=class1.getDeclaredField("age");
         */
         
         
        Class class1=Class.forName("demo.Test");
        Test test=new Test("陈加兵",1000);
        Field field=class1.getField("name"); //获取public的成员变量name的Filed对象
        System.out.println(field.get(test));   //获得test对象中的name属性的值
        
        //获取private int age的Field对象
        Field field2=class1.getDeclaredField("age");
        field2.setAccessible(true);  //设置访问权限
        System.out.println(field2.get(test));

参考文章


码猿技术专栏
486 声望108 粉丝