概述
定义
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