Java反射剖析

1.Java反射概念

1.1定义

      Java反射机制是指在运行状态下,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。简单来说,就是只要知道类的名称,就能获取到它的属性和方法。

1.2反射的优缺点

    为什么要用反射机制?直接创建对象不就可以了么,这就涉及到静态和动态的概念。

  • 静态编译:在编译时确定类型,绑定对象
  • 动态编译:运行时确定类型,绑定对象。动态编译最大限度的发挥了java的灵活性,提现了多态的应用,降低了类之间的耦合性。
  • 优点
    可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
  • 缺点
    对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

2.Class类

2.1概念

我们常说:

在面向对象的世界里,万事万物皆为对象。

    Java里对象是什么,它是一个类的实例,那么我们创建的类是一个对象么?答案:是。类是java.lang.Class类的对象。(there is a class named Class)
    Java程序在运行时,系统一直对所有的对象进行所谓的运行时类型标识。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。Class没有公共的构造方法,Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass()方法自动构造的。
    在运行程序的时候,JVM会首先检查所要加载的类对应的Class对象是否已经加载,如果没有加载,则会根据类名查找.class文件,并将其Class对象载入。

2.2类的对象如何表示

搞明白了Class类,下面我们看看Class类的实例如何表示。如下:

public class ReflectionTest1 {
    public static void main (String[] args){
        //通常A的实例对象的表示
        A a = new A();

        //表示方式一:直接访问class变量   --->实际在告诉我们每一个类都一个隐含的静态成员变量class
        Class class1 = A.class;

        //表示方式二:已知该类的对象 调用getClass方法
        Class class2 = a.getClass();

        //class1 class2称为A类的类类型(class type),同时也是Class类的对象,这个对象我们称之为该类的类类型

        //不管class1 or class2都代表了A的类类型,一个类只可能是Class类的一个实例对象
        System.out.println(class1 == class2);  //true
        //表示方式三:
        try {
            Class class3 = Class.forName("com.learn.java.A");
            System.out.println(class3 == class2);  //true
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class A{
    public void run(){
        System.out.println("A's run method!");
    }
}

我们可以看到得到类对象的实例有3中方式,可以发现:

  • 得到的Class类的实例我们称为对应类的类类型(class type)
  • 不管我们以三种方式中的哪一种方法得到Class类的实例,其他他们都是一个实例。从原理上讲,当JVM发现这个类的Class对象没有被加载的时候,才会去加载生成Class对象,所以只会有一个。
  • 第三种方式,Class.forName()方法中的字符串须为类的全路径名。

从上面的代码中我们得到了class1 class2 class3,这三个都是Class类的对象,也就是对应的A类,那如何得到A类的对象了?又是怎么可以调用方法了?代码修改如下:

public class ReflectionTest1 {
    public static void main (String[] args){
        A a = new A();

        Class class1 = A.class;
        try {
            A a1 = (A)class1.newInstance();  //这里得到的a1就是A的一个实例
            a1.run();   
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

注意这里,如果调用newInstance()方法,需要保证A类中有无参构造。

3.反射的使用方法

3.1获取构造函数

    上面代码中我们可以通过class1.newInstance()的方式,获取得到A的一个实例对象,但是这种方式有一个前提,就是必须有一个无参构造方法,那如果是没有或者想调用有参数的构造器,怎么办了?下面我们就来看看。
    类的成构造函数也是一个对象,它是java.lang.reflect.Constructor的一个对象,所以我们通过java.lang.reflect.Constructor里面封装的方法来获取这些信息。
1.获取单个的构造方法。

public Constructor getConstructor(Class... parameterTypes) //获取该类指定参数的public访问权限的构造器
public Constructor getDeclaredConstructor(Class... parameterTypes) //获取该类指定参数的所有构造器

例如:

public class ReflectionTest1 {
    public static void main (String[] args){
        try {
            Class classA = Class.forName("com.learn.java.A");
            Constructor constructor = classA.getConstructor(int.class);
            System.out.println(constructor.getName());
            A a = (A)constructor.newInstance(6);
            a.run();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

class A{
    private int age;
    private String name;
    public A(int age) {
        this.age = age;
    }
    public A(String name) {
        this.name = name;
    }
    public A(int age, String name) {
        this.age = age;
        this.name = name;
    }
    public void run(){
        System.out.println("A's run method!");
    }
}
//运行结果:
//com.learn.java.A
//A's run method!

2.获取所有的构造函数

public Constructor[] getConstructors() throws SecurityException
public Constructor[] getDeclaredConstructors() throws SecurityException 

如下:

public static void main (String[] args){
        try {
            Class classA = Class.forName("com.learn.java.A");
            Constructor[] constructors = classA.getConstructors();
            for (Constructor constructor : constructors) {
                System.out.println(constructor);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
//返回结果:
//public com.learn.java.A(int,java.lang.String)
//public com.learn.java.A(java.lang.String)
//public com.learn.java.A(int)

A类中的方法同上。

3.2获取成员变量

    同样,在java.lang.reflect的包中,有一个Field的类,类的成员变量也是一个对象,它是java.lang.reflect.Field的一个对象,所以我们通过java.lang.reflect.Field里面封装的方法来获取这些信息。
1.单个的获取成员变量

public Field getField(String name) //获取指定参数的public变量
public Field getDeclaredField(String name) //获取指定参数的所有变量

示例如下:

public static void main (String[] args){
        try {
            Class classA = Class.forName("com.learn.java.A");
            //方式一:getField
            //Field field = classA.getField("age");
            //方式二:getDeclaredField
            Field field = classA.getDeclaredField("age");
            System.out.println(field);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

//输出:
//方式一:
//java.lang.NoSuchFieldException: age
//方式二:
//private int com.learn.java.A.age

2.获取所有的成员变量

public Field[] getFields() throws SecurityException
public Field[] getDeclaredFields() throws SecurityException

3.3获取方法

    类的方法也是一个对象,它是java.lang.reflect.Method的一个对象,所以我们通过java.lang.reflect.Method里面封装的方法来获取这些信息。
1.获取单个的方法

public Method getDeclaredMethod(String name, Class... parameterTypes) // 得到该类所有的方法,不包括父类的
public Method getMethod(String name, Class... parameterTypes) // 得到该类所有的public方法,包括父类的

示例

public class ReflectionTest1 {
    public static void main (String[] args){
        try {
            Class classA = Class.forName("com.learn.java.A");
            Object a = classA.newInstance();
            //方式二:getDeclaredMethod()
            System.out.println("second type:");
            Method method2 = classA.getDeclaredMethod("introduce", String.class, int.class);
            System.out.println(method2);
            method2.setAccessible(true);  //因为introduce方法是私有的,所以需要设置
            method2.invoke(a, "meimei", 18);
            //方式一:getMethod()
            System.out.println("first type:");
            Method method1 = classA.getMethod("introduce", String.class, int.class);
            System.out.println(method1);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

class A{
    private int age;
    private String name;

    private void introduce(String name, int age) {
        System.out.println("hello ! my name is " + name + ", I'm " + age +" years old!");
    }
    public void run(){
        System.out.println("A's run method!");
    }
}
//运行结果:
//second type:
//private void com.learn.java.A.introduce(java.lang.String,int)
//hello ! my name is meimei, I'm 18 years old!
//first type:
//java.lang.NoSuchMethodException: com.learn.java.A.introduce(java.lang.String, int)
    at java.lang.Class.getMethod(Class.java:1773)
    at com.learn.java.ReflectionTest1.main(ReflectionTest1.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

2.获取所有的方法

public Method[] getDeclaredMethods() throws SecurityException // 得到该类所有的方法,不包括父类的
或者:
public Method[] getMethods() throws SecurityException// 得到该类所有的public方法,包括父类的

4.反射的应用

4.1动态加载

详见文章列表

4.2集合类反射原理剖析

详见文章列表

4.3框架相关反射应用

spring 的 ioc/di 也是反射....
javaBean和jsp之间调用也是反射....
struts的 FormBean 和页面之间...也是通过反射调用....
JDBC 的 classForName()也是反射.....
hibernate的 find(Class clazz) 也是反射...

参考

Java反射机制
反射机制的理解与用途
Java反射入门
Java反射机制
Java反射机制深入理解

你可能感兴趣的:(Java反射剖析)