java反射机制

  那么首先,就从自己栽的跟斗上开始吧,自己在刚学java的时候,没怎么用过java的框架,所以看到书上对java反射的描述,也只是把这个跟其他的知识点一样作为一个新的知识点,在脑子里有了一个印象。那个时候并不知道java反射机制有什么作用,只知道java反射机制可以破坏单例模式。到了后来接触的多了,才发现java反射机制真的是一种特别有用的东西,我们所知的spingMVC、struts2等框架的底层实现就运用了反射机制。

      先来看一下java反射机制的概念,百度百科里面给出的是:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。从概念上我们就可以知道,为什么java反射机制是众多java框架的基石之一了。最常见的我们在配置servlet的时候,要给定servlet实现类的全名,然后再配置这个servlet处理哪些url请求。这里当我们的web应用接受到的请求与url-pattern相匹配时,就知道要将这个请求交与我们配置的servlet来进行处理,但是我们在web.xml中的配置只是配置了一个处理该请求的类的名字,web应用却能通过我们的配置来实现用该类处理相应的请求。正是通过java反射机制,我们的应用才能够根据我们的配置对于各种不同的请求生成相应的servlet对象并调用正确的处理方法。各种监听器、过滤器的配置也是如此。还有我们经常使用的jdbc,因为java给各数据库厂商提供了统一接口,并没有具体实现,我们在使用jdbc时,java程序并不知道我们所用的数据库是哪种类型,所以我们在使用jdbc时首先就要加载数据库的驱动(虽然JDBC4之后可以通过classpath下的jar包自动加载驱动了),Class.forName("DriverName"),这句话就是告诉java程序我们所使用的驱动是哪一种,是oracle还是mysql。那么为什么执行了这句代码后,虚拟机就知道我们使用的是哪一种驱动程序呢。Class.forName("DriverName")执行后jvm会查找并加载名为DriverName的类并返回该类的一个类对象,也就是说jvm会执行该类的静态代码段,而不管oracle还是mysql所有Driver类的静态代码块中都有java.sql.DriverManager.registerDriver(new Driver()); 这样一句代码,就是在类加载时将自己的一个驱动类对象注册到驱动管理器中。所以Class.forName("DriverName")这句代码可以实现驱动类的加载。

  接下来看一看java反射机制是如何实现的呢。要想实现java反射,我们需要掌握Class、Constructor、Field、Method这四个类的用法。顾名思义,class代表的是类对象,Constructor代表的是构造器对象,Field是类的属性对象,Method是类的方法对象。看到这四个类我们就能知道为什么说java反射可以在运行时知道任意一个类(对象)的所有属性和方法。

  •   Class

   Clas类是反射中最核心的类,也是反射实现的起点。Class本身表示一个类的本身,通过Class可以完整地得到一个类中的完整结构,包括此类中的方法定义、属性定义等。获取Class对象的方式有三种:

    •   通过Object类的getClass()方法获取。
    •        通过Class类的静态方法forName(String className)获取。
    •        通过“类.class”来获取。

 

package reflect;

public class ClassTest {
    public static void getClassMethod1(){
        ClassTest t = new ClassTest();
        Class clazz = t.getClass();
        System.out.println("方法1获取的类对象对应的类名是"+clazz.getName());
    }
    public static void getClassMethod2(){
        Class clazz = ClassTest.class;
        System.out.println("方法2获取的类对象对应的类名是"+clazz.getName());
    }
    public static void getClassMethod3() throws ClassNotFoundException{
        Class clazz = Class.forName("reflect.ClassTest");
        System.out.println("方法3获取的类对象对应的类名是"+clazz.getName());
    }
    public static void main(String[] args) throws ClassNotFoundException {
        getClassMethod1();
        getClassMethod2();
        getClassMethod3();
    }
}

输出结果为:

通过Class对象能获取Constructor、Field、Method这三个其他类的对象。

  •  获取Constructor
    •  Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数
    •  Constructor[] getConstructors() -- 获得类的所有公共构造函数
    •  Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关)
    •  Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关)
package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

@SuppressWarnings({"all"})
public class ClassGetConstructor {
    public int i;
    private static final Class clazz = ClassGetConstructor.class;
    public ClassGetConstructor(int i){
        this.i = i;
    }
    private ClassGetConstructor(){}
    public static void getConstructorTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("getConstructorTest测试结果:");
        //获取使用指定参数类型的公共构造参数,因为是获取公共方法这里会抛出权限异常SecurityException
        Constructor c1 = clazz.getConstructor(Integer.TYPE);
        //这里使用Integer.class会报异常,Integer.TYPE表示int类的Class对象,Integer.class表示Integer类的Class对象
        ClassGetConstructor t1 = (ClassGetConstructor) c1.newInstance(1);
        System.out.println(t1.i);
        Constructor c2 = clazz.getConstructor(null);
        //这里会报异常,因为无参的构造方法是private的,但报出的异常是 java.lang.NoSuchMethodException,却不是SecurityException
        ClassGetConstructor t2 = (ClassGetConstructor) c2.newInstance();
        System.out.println(t2.i);
    }
    
    public static void getConstructorsTest(){
        System.out.println("getConstructorsTest测试结果:");
        Constructor[] cs = clazz.getConstructors();
        //这里只能获取到有参的构造方法
        for(Constructor c : cs)
            System.out.println(c);
    }
    public static void getDeclaredConstructorTest() throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        System.out.println("getDeclaredConstructorTest测试结果:");
        Constructor c1 = clazz.getDeclaredConstructor(Integer.TYPE);
        Constructor c2 = clazz.getDeclaredConstructor();
        System.out.println(c1);
        ClassGetConstructor t1 = (ClassGetConstructor) c1.newInstance(1);
        System.out.println(t1.i);
        System.out.println(c2);
        ClassGetConstructor t2 = (ClassGetConstructor) c2.newInstance();
        System.out.println(t2.i);
    }
    public static void getDeclaredConstructorsTest(){
        System.out.println("getDeclaredConstructorsTest测试结果:");
        Constructor[] cs = clazz.getDeclaredConstructors();
        for(Constructor c : cs)
            System.out.println(c);
    }
    public static void main(String[] args) throws Exception{
        try {
            getConstructorTest();
        } catch (Exception e) {
            e.printStackTrace();
        }
        getConstructorsTest();
        getDeclaredConstructorTest();
        getDeclaredConstructorsTest();
    }
}

输出结果:

  •  获取Field
    • Field getField(String name) -- 获得指定命名的公共字段
    • Field[] getFields() -- 获得类的所有公共字段
    • Field getDeclaredField(String name) -- 获得类声明的指定命名的字段
    • Field[] getDeclaredFields() -- 获得类声明的所有字段
package reflect;

import java.lang.reflect.Field;

public class ClassGetField {
    private String s;
    private static int j;
    public static int i;
    public ClassGetField classGetField;
    public static Class clazz = ClassGetField.class;
    public static void getFieldTest() throws NoSuchFieldException, SecurityException{
        System.out.println("getFieldTest测试结果:");
        try {
            System.out.println(clazz.getField("s"));//报异常
        } catch (Exception e) {
            System.out.println("获取属性s失败!");
//            e.printStackTrace();
        }
        System.out.println(clazz.getField("i"));
        try {
            System.out.println(clazz.getField("j"));//报异常
        } catch (Exception e) {
            System.out.println("获取属性j失败!");
//            e.printStackTrace();
        }
        System.out.println(clazz.getField("classGetField"));
        System.out.println(clazz.getField("clazz"));
    }
    public static void getFieldsTest(){
        System.out.println("getFieldsTest测试结果:");
        Field[] fs = clazz.getFields();
        for (Field f : fs){
            System.out.println(f);
        }
    }
    public static void getDeclaredFieldTest() throws NoSuchFieldException, SecurityException{
        System.out.println("getDeclaredFieldTest测试结果:");
        System.out.println(clazz.getDeclaredField("s"));
        System.out.println(clazz.getDeclaredField("i"));
        System.out.println(clazz.getDeclaredField("j"));
        System.out.println(clazz.getDeclaredField("classGetField"));
        System.out.println(clazz.getDeclaredField("clazz"));
    }
    public static void getDeclaredFieldsTest(){
        System.out.println("getDeclaredFieldsTest测试结果:");
        Field[] fs = clazz.getDeclaredFields();
        for (Field f : fs){
            System.out.println(f);
        }
    }
    public static void main(String[] args) throws Exception{
        getFieldTest();
        getFieldsTest();
        getDeclaredFieldTest();
        getDeclaredFieldsTest();
    }
}

输出结果:

 

  • 获取Method对象
    • Method getMethod(String name, Class[] params) -- 使用特定的参数类型,获得命名的公共方法
    • Method[] getMethods() -- 获得类的所有公共方法
    • Method getDeclaredMethod(String name, Class[] params) -- 使用特写的参数类型,获得类声明的命名的方法
    • Method[] getDeclaredMethods() -- 获得类声明的所有方法
package reflect;

public class ClassGetMethodParent {
    public void Method(int i , int j){}
    void Method(int i , int j,int k){}
}

package reflect;
import java.lang.reflect.Method;
public class ClassGetMethod extends ClassGetMethodParent{
    public void Method(String s){}
    void Method(int i){}
    public void Method(){}
    public void Method(String s , int i){}
    public static Class clazz = ClassGetMethod.class;
    
    public static void getMethodTest() throws NoSuchMethodException, SecurityException{
        System.out.println("getMethodTest测试结果:");
        Method m1 = clazz.getMethod("Method", new Class[]{String.class});
        System.out.println(m1);
        try {
            Method m2 = clazz.getMethod("Method", new Class[]{Integer.TYPE});
            System.out.println(m2);
        } catch (Exception e) {
            System.out.println("获取方法Method(int i)失败");
        }
        Method m3 = clazz.getMethod("Method", new Class[]{});//参数类型也可写作null
        System.out.println(m3);
        Method m4 = clazz.getMethod("Method", new Class[]{String.class,Integer.TYPE});
        System.out.println(m4);
        Method m5 = clazz.getMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE});//父类方法
        System.out.println(m5);
        try {
            Method m6 = clazz.getMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE,Integer.TYPE});//父类方法
            System.out.println(m6);
        } catch (Exception e) {
            System.out.println("获取父类方法方法Method(int i,int j, int k)失败");
        }
    }
    public static void getMethodsTest(){
        //这里会得到父类继承的公共方法
        System.out.println("getMethodsTest测试结果:");
        Method[] methods = clazz.getMethods();
        for (Method m : methods){
            System.out.println(m);
        }
    }
    public static void getDeclaredMethodTest() throws NoSuchMethodException, SecurityException{
        System.out.println("getDeclaredMethodTest测试结果:");
        Method m1 = clazz.getDeclaredMethod("Method", new Class[]{String.class});
        System.out.println(m1);
        try {
            Method m2 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE});
            System.out.println(m2);
        } catch (Exception e) {
            System.out.println("获取方法Method(int i)失败");
        }
        Method m3 = clazz.getDeclaredMethod("Method", new Class[]{});//参数类型也可写作null
        System.out.println(m3);
        Method m4 = clazz.getDeclaredMethod("Method", new Class[]{String.class,Integer.TYPE});
        System.out.println(m4);
        try {
            Method m5 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE});//父类方法
            System.out.println(m5);
        } catch (Exception e1) {
            System.out.println("获取父类方法方法Method(int i,int j)失败");
        }
        try {
            Method m6 = clazz.getDeclaredMethod("Method", new Class[]{Integer.TYPE,Integer.TYPE,Integer.TYPE});//父类方法
            System.out.println(m6);
        } catch (Exception e) {
            System.out.println("获取父类方法方法Method(int i,int j, int k)失败");
        }
    }
    public static void getDeclaredMethodsTest(){
        System.out.println("getDeclaredMethodsTest测试结果:");
        Method[] methods = clazz.getDeclaredMethods();
        for (Method m : methods){
            System.out.println(m);
        }
    }
    public static void main(String[] args) throws Exception{
        getMethodTest();
        getMethodsTest();
        getDeclaredMethodTest();
        getDeclaredMethodsTest();
    }
}

输出结果:

 

从结果中可以看出使用获取类的公共方法的方法时(getMethod与getMethods)可以获取该类从父类继承的所有共有方法,而使用getDeclaredMethod与getDeclaredMethods方法时只能获取该类自己声明的所有方法。这一点前面获取Constructor、Field也是如此。class对象常用的方法还有newInstance(),该方法返回一个类的实例对象,调用的是类的无参构造函数。

  • Constructor

  当我们使用class对象的newInstance方法来生成一个类的实例对象时,如果该类没有默认的无参构造函数,我们就不能使用class对象的newInstance方法了。或者我们需要使用指定的构造函数来生成类的实例对象,这个时候我们可以使用Constructor类来实现。

  通过Constructor类的newInstance方法我们可以使用指定的构造方法来生成实例对象,Constructor类的newInstance与class类的newInstance不同之处在于:Constructor类的newInstance的参数为Class对象数组,表示指定构造器的参数类型,而class类的newInstance方法没有方法参数,默认调用类的无参构造器。

package reflect;

import java.lang.reflect.Constructor;

public class ConstructorTest {
    public int i;
    public static final Class clazz = ConstructorTest.class;
    public ConstructorTest(int i ){
        this.i = i;
    }
//    public ConstructorTest(){}
    public static void main(String[] args) throws Exception {
        Constructor c1 = clazz.getConstructor(new Class[]{int.class});
        ConstructorTest test1 = (ConstructorTest) c1.newInstance(1);
        System.out.println(test1.i);
        ConstructorTest test2 = (ConstructorTest) clazz.newInstance();
    }
}

测试结果:

 Constructor类比较简单,可以看成是对class类的newInstance方法的一个补充吧,常用的方法就是newInstance。

  • Field

  Field类代表某个类中一个成员变量,我们通过class类以及Constructor类获得了某个类的实例后,如果想要进一步的对该实例对象进行控制,例如给实例对象的一些成员变量进行赋值等等,这个时候可以用到Field类。这个时候我们不禁要问,既然我们已经获取到了该实例对象,那么我们可以直接操作该对象,为什么还要用反射来进行成员变量的设置呢?我们自己在写代码的时候获取到了实例对象,在IDE环境中点一下就会出现该对象的方法和所有属性,但是在jvm中获取到了实例对象后如果要堆该对象的成员变量进行操作就只能依赖于Field类。

  获取到了Field对象后可以调用Field类的set方法对成员变量进行赋值,set方法的参数有两个,第一个为要操作的实例对象,第二个为要赋的值。对于private的成员变量操作如果不是在该实例对象的类的内部进行的话是会抛出异常的,这种情况下需要调用Field类的setAccessible方法,传入参数为boolean类型,这里使用true的话表示暴力访问。

package reflect;

import java.lang.reflect.Field;

public class FieldTest {
    private  String s = "111";
    private int i = 0;
    public String toString(){
        return "s: " + s + "  i: " + i ;
    }
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
        FieldTest test1 = new FieldTest();
        Field str=FieldTest.class.getDeclaredField("s");
        str.setAccessible(true);  //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问
        str.set(test1, "222");
        System.out.println(test1.s);
        Field intf=FieldTest.class.getDeclaredField("i");
        intf.set(test1, 333);
        System.out.println(test1.i);
    }
}

可以看到 在类的内部我们使用Field来设置成员变量的值,无论成员变量的访问修饰符是什么,我们都可以直接调用set方法来进行设置。但是在类的外部,对private的成员变量的操作前必须调用setAccessible(true)方法。

package reflect;

import java.lang.reflect.Field;

public class Test {
    public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
        FieldTest test1 = new FieldTest();
        Field str=FieldTest.class.getDeclaredField("s");
        str.setAccessible(true);  //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问
        str.set(test1, "222");
        System.out.println(test1);
        Field intf=FieldTest.class.getDeclaredField("i");
        intf.set(test1, 333);
        System.out.println(test1);
    }
}

除了最常用的set方法以为,还有get方法,即获得当前的属性值,getType方法获取属性的类型,以及针对基本数据类型的setInt、setFloat、setBoolean方法等。

  • Method

  Method类表示类中的成员方法,每个方法都由 修饰符、返回值、参数、注解和抛出的异常组成。这个在我们前面的class对象获取Method对象的测试中可以看到,当我们打印Method对象时,会将这几个部分都打印出来。

  Method类最常用的方法是invoke方法,该方法接受两个参数,一个是执行该方法的对象,一个是传入的参数数组。同样的对于private的方法,我们进行反射调用时需要调用setAccessible方法。

package reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@SuppressWarnings({"all"})
public class MethodTest {
    private void Method(){
        System.out.println("private void Method");
    }
    public void Method(int i,int j){
        System.out.println("public void Method("+ i + j +")");
    }
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method m1 = MethodTest.class.getDeclaredMethod("Method", null);
        m1.invoke(new MethodTest(), null);
        
        Method m2 = MethodTest.class.getDeclaredMethod("Method", new Class[]{int.class,int.class});
        m2.invoke(new MethodTest(), new Object[]{1,1});
    }
}

在类中,无论是private还是public都可以直接通过反射调用。

package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) throws Exception{
//        FieldTest test1 = new FieldTest();
//        Field str=FieldTest.class.getDeclaredField("s");
//        str.setAccessible(true);  //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问
//        str.set(test1, "222");
//        System.out.println(test1);
//        Field intf=FieldTest.class.getDeclaredField("i");
//        intf.set(test1, 333);
//        System.out.println(test1);
        testMethod();
    }
    public static  void testMethod() throws Exception{
        Method m1 = MethodTest.class.getDeclaredMethod("Method", null);
        m1.setAccessible(true);
        m1.invoke(new MethodTest(), null);
        
        Method m2 = MethodTest.class.getDeclaredMethod("Method", new Class[]{int.class,int.class});
        m2.invoke(new MethodTest(), new Object[]{1,1});
    }
}

在其他类中,如果通过反射调用private方法必须调用setAccessible(true)来避开权限检查。

posted @ 2017-11-05 19:48  封尘寒剑  阅读(225)  评论(0编辑  收藏  举报