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<T> getConstructor(Class<?>... parameterTypes) //获取该类指定参数的public访问权限的构造器
public Constructor<T> 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反射机制深入理解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容