java反射机制

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

      先来看一下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")这句代码能够实现驱动类的加载。
mysql

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

  •   Class

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

    •   经过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();
    }
}

输出结果:oracle

  •  获取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();
    }
}

输出结果:jvm

 

从结果中能够看出使用获取类的公共方法的方法时(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)来避开权限检查。

相关文章
相关标签/搜索