Java特性之反射

概述

定义

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

用途

在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的。

反射机制的相关类

与Java反射相关的类如下(记住这些类,因为Class的很多方法都是以下面几个类的名字命名的):

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性)
Method类代表类的方法
Constructor类代表类的构造方法

Class类

Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们这里不介绍如果想看Class的方法可以去JDK文档里面找。在下文如果出现Class的相关方法,我会把JDK上的介绍截图出来。

Field类

Field代表类的成员变量(成员变量也称为类的属性)。

Method类

Method代表类的方法。

Constructor类

Constructor代表类的构造方法。

 

示例

为了演示反射的使用,首先构造一个与书籍相关的model——Book.java,然后通过反射方法示例创建对象、反射私有构造方法、反射私有属性、反射私有方法。

被反射类Book.java
 

public class Book{
    //定义三个属性
    private final static String TAG = "BookTag";
    private String name;
    private String author;



    //定义2个构造方法。
    public Book() {
    }

    private Book(String name, String author) {
        this.name = name;
        this.author = author;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }

    //定义一个私有方法。
    private String declaredMethod(int index) {
        String string = null;
        switch (index) {
            case 0:
                string = "I am declaredMethod 1 !";
                break;
            case 1:
                string = "I am declaredMethod 2 !";
                break;
            default:
                string = "I am declaredMethod 1 !";
        }
        return string;
    }


    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                '}';
    }
}

注:上面主要三个属性,2个构造方法,1个私有方法。

 

反射Test

public class BookTest {

    //通过类路径,创建对象
    public static void main(String[] args) {
        try {
            Class<?> classBook = Class.forName("com.zhongan.fanse.Book");
            Object objectBook = classBook.newInstance();
            Book book = (Book) objectBook;
            book.setName("Android进阶之光");
            book.setAuthor("刘望舒");
            System.out.println(book.toString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        //打印    Book{name='Android进阶之光', author='刘望舒'}
    }
}
Class.forName这行代码是做什么的?看下JDK官方怎么解释的,先找到JDK官方文档https://docs.oracle.com/javase/8/docs/api/然后在页面上CTRL+F所有关键字Class

点击后,在右边一栏找到forName

 

通过参数字符串className(这个className是类的全路径,或者是接口的全路径)得到类对象。Class也是一个类Book也是一个类,是类它就会有对象,所以代码Class<?> classBook中,classBook就是Class类的实例对象,不是Book类的实例对象。只不过这个对象比较特殊,他是用来描述Class这个类的对象。那JDK为什么要定义这么一个类?为了描述类的信息,比如Book这个类,所以Class类里面有很多属性和方法,都是为了表达一个Book类的信息,就像Spring的Bean一样,Bean的信息用什么描述,Bean的信息用BeanDefinition这个类来描述,描述他是否懒加载,描述他存的Bean类型,描述他是否是单利,作用都是一样的。只是维度不一样。

看下classBook.newInstance()是做什么的?

创建这个类的新实例,返回这个类的对象。也就是Book这个类,感兴趣的可以一步步更进去,看他怎么创建的,这里不分析。最终得到Book这个类的实例对象。

 

// 反射私有的构造方法

import java.lang.reflect.Constructor;

public class BookTest {
    public static void main(String[] args) {
        try {
            Class<?> classBook = Class.forName("com.zhongan.fanse.Book");
            Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class,String.class);
            declaredConstructorBook.setAccessible(true);
            Object objectBook = declaredConstructorBook.newInstance("Android开发艺术探索","任玉刚");
            Book book = (Book) objectBook;
            System.out.println(book.toString());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        //打印  Book{name='Android开发艺术探索', author='任玉刚'}
    }
}

classBook.getDeclaredConstructor代码的作用,我们先看下官方对getDeclaredConstructor的介绍,

功能:返回一个对象的构造器,如果有多个构造方法,参数的类型和个数对应构造器的参数类型和个数。

declaredConstructorBook.setAccessible(true)

是干嘛的,我们再看下官网怎么介绍的,

原来是他的顶层类的方法里面

大致意识就是如果设置为true就表示不进行访问检查,如果是false就要进行访问检查。针对方法或者属性,都要通过设置Accessible为true来完成后续的操作。

继续看下一行代码。

declaredConstructorBook.newInstance("Android开发艺术探索","任玉刚");

大致的意识是说,通过declaredConstructorBook构造函数对象,调用创建和初始化一个实例,生成一个对象,初始话的参数就是

"Android开发艺术探索","任玉刚"

最终生成Book的实例对象。不像我们通过new Book()这种隐式的创建对象了。

 

反射私有属性

Book类中有个TAG属性,值是=BookTag,那么我们怎么通过反射来拿到这个值呢。看下面代码。

public class BookTest {
    public static void main(String[] args) {
        try {
            Class<?> classBook = Class.forName("com.zhongan.fanse.Book");
            Object objectBook = classBook.newInstance();
            Field fieldTag = classBook.getDeclaredField("TAG");
            fieldTag.setAccessible(true);
            String tag = (String) fieldTag.get(objectBook);
            System.out.println(tag);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        //打印  BookTag
    }
}
classBook.newInstance();

这个我就不介绍,通过无参构造器创建对象实例。

classBook.getDeclaredField("TAG");

上面那行代码,我们看下文档怎么解释的,

通过参数得到这个类字段对象,注意这个字段是一个对象,类型是Field。在通过下面一行代码得到这个字段的值。

(String) fieldTag.get(objectBook);

下面是Field类api中的get(Object)方法。

返回指定参数对象上该字段的。最终结果输出BookTag。更多的字段api可以去看下官方的介绍哈。

 

反射私有方法

在Book类中有个私有方法declaredMethod,我们怎么得到这个方法对象,然后再去执行他。拿到输出结果。

//定义一个私有方法。
private String declaredMethod(int index) {
    String string = null;
    switch (index) {
        case 0:
            string = "I am declaredMethod 1 !";
            break;
        case 1:
            string = "I am declaredMethod 2 !";
            break;
        default:
            string = "I am declaredMethod 1 !";
    }
    return string;
}

看下主方法

import java.lang.reflect.Method;

public class BookTest {
    public static void main(String[] args) {
        try {
            Class<?> classBook = Class.forName("com.zhongan.fanse.Book");
            Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);
            methodBook.setAccessible(true);
            Object objectBook = classBook.newInstance();
            String string = (String) methodBook.invoke(objectBook,0);
            System.out.println(string);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        //打印  I am declaredMethod 1 !
    }
}

分析上面代码,

Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);

是什么操作,我们看下api对getDeclaredMethod的介绍,

返回一个方法对象(Method),注意Method也是一个类,只不过是描述类中方法的类。该方法对象(Method) 表示该类对象(classBook)指定的方法。name参数是一个指定所需方法名称的字符串,而parameterTypes参数是一个类对象数组,它以声明的顺序标识方法的形式参数类型。

String string = (String) methodBook.invoke(objectBook,0);

 

 

第一个参数,要调用的类对象(objectBook),参数第二个到最后是调用方法类对象的指定方法(declaredMethod)所需要的参数。最后得到declaredMethod方法的返回值。

 

总结

在阅读反射相关源码时,一定要记着这几个类(Class,Field,Method,Constructor),他们分别用来表示什么,有主意更好地阅读反射源码,当时更好的理解反射后,在去看动态代理或者spring源码之类的有很大的帮助。

 

 

 

本文摘自作者:peter_RD_nj
链接:https://www.jianshu.com/p/9be58ee20dee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值