浅析JAVA反射

废话不多说,直切进入正题。先来一张反射机制的图-->

image

认识反射

  • 首先反射我们通过“反”来理解,既然有“反”就一定有“正”,在正常情况下,我们认为一定是先有类再有对象。如下:
    import java.util.Date;//先有类

    public class ReflectTest1 {
    public static void main(String[] args) {
        Date date = new Date();//后有对象
        System.out.println(date);
    }
    }
  • ** 所谓的“反”就是指利用对象找到此对象的出处。在Object类里提供有一个方法:**

    取得Class对象:public final Class<?> getClass()。
    范例:观察“反”

    import java.util.Date;

    public class ReflectTest2 {
    public static void main(String[] args) {
        Date date = new Date();
        System.out.println(date.getClass());
    }
    }
    "class java.util.Date"
     发现调用了getClas()方法后的输出,就输出了类的完整名称,等于找到了对象的出处。
  • Class类对象实例化

    java.lang.Class是一个类,这个类是反射操作的源头,即:所有的反射都要从类开始进行,而最关键的是这个类有三种实例化方法:

1.****调用Object类中的getClass()方法:

    import java.util.Date;

    public class ReflectTest3 {
    public static void main(String[] args) {
        Date date = new Date();
        Class<?> cls =  date.getClass();
        System.out.println(cls);
    }
    }
    "class java.util.Date"

2. 使用“类.class”取得:

    import java.util.Date;

    public class ReflectTest4 {
    public static void main(String[] args) {
        Class<?> cls =  Date.class;
        System.out.println(cls);
    }
    }
    "class java.util.Date"
     之前是在实例化了Date对象之后取得的Class类对象,但是现在并没有实例化对象的产生。这种方法用在Hibernate、Mybatis、Spring等。

**3. 调用Class类提供的一个方法--实例化Class对象(在java.lang.Object下****public ****static **

** Class<?> forName(String className) throws ClassNotFoundException):**

    public class ReflectTest5 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> cls =  Class.forName("java.util.Date");
        System.out.println(cls);
    }
    }
    "class java.util.Date"
      此时可以不使用import语句导入一个明确的类,而类的名称是采用字符串的形式进行描述的。

反射实例化对象

     当拿到一个类的时候,肯定要直接使用关键字new 进行对象实例化操作这属于我们习惯性的做法。如果拿到Class类对象,那么就可以做到利用反射来实例化对象操作:

public T newInstance() throws InstantiationException,IllegalAccessException

范例: 利用反射来实例化对象

    /*正常实例化*/
    class Book{
    public Book(){
        System.out.println("*** Book的构造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 这是Book方法 ---";
    }
    }
    public class ReflectTest6 {
    public static void main(String[] args) throws ClassNotFoundException {
        Book b = new Book();
        System.out.println(b);
    }
    }
    "*** Book的构造方法 ***
     --- 这是Book方法 ---"复制代码
    package com.jkx.lzh.test;    
    /*反射实例化*/
    class Book{
    public Book(){
        System.out.println("*** Book的构造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 这是Book方法 ---";
    }
    }
    public class ReflectTest7 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
        Object obj = cls.newInstance();
    }
    }
    "*** Book的构造方法 ***"复制代码
     class Book{
    public Book(){
        System.out.println("*** Book的构造方法 ***");
    }
    @Override
    public String toString() {
        return "--- 这是Book方法 ---";
    }
    }
    public class ReflectTest8 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
        Object obj = cls.newInstance();
        Book b = (Book) obj;
        System.out.println(b);
    }
    }
"*** Book的构造方法 ***
     --- 这是Book方法 ---"复制代码
    有了反射之后,以后进行对象化实例化的操作不在只是单独的依靠关键字new完成了,反射也同样可以,但是这并不表示new就被完全取代了。

    这样有人就会说:“下面两行代码相当于:Book b = new  Book();这样不是更复杂了,么“。
    Class<?> cls = Class.forName("com.jkx.lzh.test.Book");     
    Object obj = cls.newInstance();
     PS: 在任何的开发之中,new是造成耦合的最大元凶。一切的耦合都起源于new。

    ** 范例:**观察工厂模式-->
    interface Fruit{
    void eat();
    }
    class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃苹果 *");
    }
    }
    class Factory{
    public static Fruit getInstance(String className){
        if("apple" == className){
        return new Apple();
        }
        return null;
    }
    }
    public class TestFactory {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("apple");
        f.eat();
    }
    }
    "* 吃苹果 *"
     代码如上,但是此时,如果我们要增加一个Fruit接口子类“orange”,就意味着我们就要修改工厂模式的方法。
    class Orange implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃橘子 *");
    }
    }
    class Factory{
    public static Fruit getInstance(String className){
        if("apple" == className){
        return new Apple();
        }else if("orange" == className){
        return new Orange();    
            }
        return null;
    }
    }
      由此可见,每增加一个Fruit接口子类,就要修改工厂类,那么如果随时需要增加子类呢?

      因为现在工厂类都是new关键字直接实例化的,所以new就造成了所有问题的关键点。要想解决这一问题,就只能依靠反射完成。

修改工厂模式的方法如下:

    package com.jkx.lzh.test;

    interface Fruit{
    void eat();
    }
    class Apple implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃苹果 *");
    }
    }
    class Orange implements Fruit{
    @Override
    public void eat() {
        System.out.println("* 吃橘子 *");
    }
    }
    class Factory{
    public static Fruit getInstance(String className){
        Fruit f = null;
        try {
            f = (Fruit) Class.forName(className).newInstance();
        } catch (Exception e) {
        e.printStackTrace();
        }
        return f;
    }
    }
    public class TestFactory {
    public static void main(String[] args) {
        Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple");
        f.eat();
    }
    }
    此时的程序就真正的完成了解耦合的目的,而且可扩展性非常强。

使用反射调用构造

    在之前所编写的代码中,我们都默认调用了类中的无参构造方法。可是类中也有可能不提供无参构造方法。

范例:观察当前程序的问题

Book.java

    package com.jkx.lzh.po;

    public class Book {
    private String title;
    private double price;
    public Book(String title,double price){
        this.title = title;
        this.price = price;
    }
    public String toString(String title,double price) {
        return "书名:"+ this.title + "价格: " + this.price ;
    }
    }

ReflectTest9.java

    package com.jkx.lzh.test;

    public class ReflectTest9 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException{
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();
        System.out.println(obj);
    }
    }

以上代码由于Book没有无参构造方法(<init>()),所以抛出以下异常:

Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book
    at java.lang.Class.newInstance(Class.java:427)
    at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7)
Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.newInstance(Class.java:412)
    ... 1 more

那么怎么解决这个问题呢?

在Class类中提供了这么一个构造方法可以取得构造:

  1. 取得全部构造方法:public Constructor<?>[] getConstructors() throws SecurityException;
  2. 取得一个指定参数顺序的构造:public Constructor<T>(Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;

以上两个方法返回都是“java.lang.reflect.Constructor”类的对象。在这个类中我们提供一个明确传递有参构造内容实例化对象方法:

public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;

范例:明确调用类中的构造方法

ReflectTest9.java修改如下:

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

    public class ReflectTest9 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException, NoSuchMethodException, 
            SecurityException, IllegalArgumentException, InvocationTargetException{

                Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
            //public Book(String title,double price);Constructor传的是有参构造的参数类型
            Constructor<?> con = cls.getConstructor(String.class,double.class);
            Object obj = con.newInstance("JAVA 反射,",82.22);
            System.out.println(obj);
    }
    }
    "书名:JAVA 反射价格: 82.22"
     以上给我一个警示:在我们的简单java类的开发中不管提供多少构造方法,我们至少要保留无参构造方法。

反射调用方法

** 类中普通方法只有在这个类实例化对象之后才能被调用。并且实例化方法有三种(new,conle,反射)**

范例:定义一个类

    public class Book {
    private String title;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    }

这个类有无参构造方法,所以实例化对象的时候可以直接利用Class提供的newInstance方法

在Class类里面提供有以下取得类中Method()的操作:

1. 取得一个类中的全部方法:
public Method[] getMethods() throws SecurityException;

2. 取得类中指定方法:
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;

以上两个操作返回的是“java.lang.reflect.Method”类对象,在这里类里面我们重点关注一个方法:调用方法-->

public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;

范例:反射调用方法

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class ReflectTest10 {
    public static void main(String[] args) throws InstantiationException, 
            IllegalAccessException, ClassNotFoundException, NoSuchMethodException, 
            SecurityException, IllegalArgumentException, InvocationTargetException{
        String fileName = "title";//要操作的成员
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();//必须实例化对象
        Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class);
        Method getMet = cls.getMethod("get"+ inticap(fileName));
        setMet.invoke(obj, "JAVA 反射");
        System.out.println(getMet.invoke(obj));
    }
    public static String inticap(String str){
        return str.substring(0,1).toUpperCase() + str.substring(1);
    }
    }
"JAVA 反射"复制代码

此时我们完全看不到操作类型,也就是说利用反射可以实现任意类的指定方法调用。

反射调用成员

  • 类中属性一定要在本类实例化对象产生之后才可以分配内存空间。在Class类中提供了取得成员的方法:

    1. 取得全部成员:public Filed[] getDeclaredFileds() throws SecurityException;

    1. 取得指定成员:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
  • 返回的类型是“java.lang.reflect.Filed”类,在这个类里面有两个重要的方法:

    1. 取得属性的内容:public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException

    2. 设置属性的内容:public void Object getObject(Object obj,Object values) throws IllegalAccessException,IllegalArgumentException

  • 在“java.lang.reflect.AccessibleObject”类下面(在JDK1.8之后修改):

    1. Executable下面继承了Constructor,Method;

    2. Filed: 在这个类中提供有一个方法:public void setAccessible(boolean flag) throws SecurityException;设置是否取消封装

范例:现在提供如下类

    package com.jkx.lzh.po;

    public class Book {
        private String title;
    }

这个类中定义了一个私有属性,按照我们原始的做法,此时它一定无法被外部所使用。

范例: 但是使用setAccessible取消封装就可以使用了

import java.lang.reflect.Field;

public class ReflectTest11 {
    public static void main(String[] args) throws NoSuchFieldException, 
            SecurityException, ClassNotFoundException, 
            InstantiationException, IllegalAccessException {
        Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
        Object obj = cls.newInstance();//必须实例化对象
        Field titleField = cls.getDeclaredField("title");
        titleField.setAccessible(true);
        titleField.set(obj, "JAVA 反射");//相当于:Book对象.title = "JAVA 反射";
        System.out.println(titleField.get(obj));//相当于:Book对象.title;
    }
} 
"JAVA 反射"

构造方法和普通方法同样可以取消,只不过我们很少这样去做,而且对于属性的访问还是建议使用settr、gettr

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

推荐阅读更多精彩内容

  • 导语 反射机制是Java程序开发语言的重要特征之一。 主要内容 认识反射 理解反射作用 利用反射来调用类的结构 具...
    一个有故事的程序员阅读 649评论 2 5
  • 反射机制的功能 Java反射机制主要提供了以下功能:在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象...
    Anderson大码渣阅读 2,823评论 1 43
  • 学习Android的同学注意了!!!学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Android学习交流群...
    kingZXY2009阅读 332评论 0 0
  • 对于之前的程序实际上采用的模式是“程序类+配置文件”,但这种也是需要手工维护配置文件,所以现在考虑另一种方式。 C...
    tingtingtina阅读 223评论 0 0
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 124,708评论 2 7