定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
用途
在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法。当然,也不是所有的都适合反射,之前就遇到一个案例,通过反射得到的结果与预期不符。阅读源码发现,经过层层调用后在最终返回结果的地方对应用的权限进行了校验,对于没有权限的应用返回值是没有意义的缺省值,否则返回实际值起到保护用户的隐私目的
反射机制的相关类
与Java反射相关的类如下:
类名 | 用途 |
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class类
Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
- 获得类相关的方法
方法 | 用途 |
---|---|
asSubclass(Class<U> clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
- 获得类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
- 获得类中注解相关的方法
方法 | 用途 |
---|---|
getAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
- 获得类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
- 获得类中方法相关的方法
方法 | 用途 |
---|---|
getMethod(String name, Class...<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class...<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
- 类中其他重要的方法
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
Field类
Field代表类的成员变量(成员变量也称为类的属性)。
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
Method类
Method代表类的方法。
方法 | 用途 |
---|---|
invoke(Object obj, Object... args) | 传递object对象及参数调用该对象对应的方法 |
Constructor类
Constructor代表类的构造方法。
方法 | 用途 |
---|---|
newInstance(Object... initargs) | 根据传递的参数创建类的对象 |
反射的使用
创建一个LOL类
package lpl;
public class LOL {
public String playerName;//玩家姓名
public int heroNum;//英雄数量
public int skinNum;//皮肤数量
}
1:获取类对象
获取类对象有3种方式
Class.forName()(常用)
LOL.class
new Hero().getClass()
在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样。(此处准确是在ClassLoader下,只有一个类对象)
示例:
package lpl;
public class LPLTest {
public static void main(String[] args) {
String className = "lpl.LOL";
try {
//获取类对象的第一种方式
Class pClass1 = Class.forName(className);
//获取类对象的第二种方式
Class pClass2 = LOL.class;
//获取类对象的第三种方式
Class pClass3 = new LOL().getClass();
System.out.println(pClass1==pClass2);//输出true
System.out.println(pClass1==pClass3);//输出true
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
三种方式中,常用第一种,第二种需要导入类的包,依赖太强,不导包就抛编译错误。第三种对象都有了还要反射干什么。一般都第一种,一个字符串可以传入也可写在配置文件中等多种方法。
2 利用反射机制创建对象
基本步骤
与传统的通过new 来获取对象的方式不同反射机制,反射会先拿到LOL的“类对象”,然后通过类对象获取“构造器对象”再通过构造器对象创建一个对象,具体步骤
1.获取类对象 Class class = Class.forName("lpl.LOL");
2.获取构造器对象 Constructor con = clazz.getConstructor(形参.class);
3 获取对象 LOL lol =con.newInstance(实参);
上面是最简单的获取方法,当LOL的构造方法不是无参构造方法时,获取构造器对象略有不同,见下面测试:
构造方法不同时,获取构造器对象的方法
示例:
- LOL类添加6种构造方法
//---------------构造方法-------------------
//(默认的构造方法)
LOL(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//无参构造方法
public LOL(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public LOL(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public LOL(String name ,float hp){
System.out.println("姓名:"+name+"血量:"+ hp);
}
//受保护的构造方法
protected LOL(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private LOL(float hp){
System.out.println("私有的构造方法 血量:"+ hp);
}
通过反射机制获取对象
package lpl;
import java.lang.reflect.Constructor;
public class ConstructorTest {
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 2.创建对象
* Constructor对象调用newInstance(Object... initargs)
*/
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("lpl.LOL");
//2.获取所有公有构造方法
System.out.println("**********************所有公有构造方法*********************************");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray){
System.out.println(c);
}
System.out.println("*****************获取公有、无参的构造方法*******************************");
Constructor con = clazz.getConstructor(null);
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(float.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance(100);
}
}
**********************所有公有构造方法*********************************
public lpl.LOL(java.lang.String,float)
public lpl.LOL(char)
public lpl.LOL()
************所有的构造方法(包括:私有、受保护、默认、公有)***************
private lpl.LOL(float)
protected lpl.LOL(boolean)
public lpl.LOL(java.lang.String,float)
public lpl.LOL(char)
public lpl.LOL()
lpl.LOL(java.lang.String)
*****************获取公有、无参的构造方法*******************************
con = public lpl.LOL()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
private lpl.LOL(float)
私有的构造方法 血量:100.0
总结:
1.获取构造器对象方法:
1).批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法:
public Constructor getConstructor(Class… parameterTypes): 获取单个的"公有的"构造方法
public Constructor getDeclaredConstructor(Class…parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
获取成员方法并使用
获取成员方法:
public Method getMethod(String name ,Class<?>… parameterTypes):获取"公有方法";(包含了父类的方法也包含Object类)
public Method getDeclaredMethods(String name ,Class<?>… parameterTypes) :获取成员方法,包括私有的(不包括继承的)
参数解释:
name : 方法名;
Class … : 形参的Class类型对象
调用方法
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参
创建方法:
package lpl;
public class LOL {
public String playerName;//玩家姓名
public int heroNum;//英雄数量
public int skinNum;//皮肤数量
public void legendary(String playerName,int heroNum){
System.out.println("玩家"+playerName+"超神了!拥有"+heroNum+"个英雄");
}
}
运行:
package lpl;
import java.lang.reflect.Method;
public class LPLTest {
public static void main(String[] args) {
try {
String className = "lpl.LOL";
Class pClass1 = Class.forName(className);
Object objectBook = pClass1.newInstance();
final Method method = pClass1.getMethod("legendary",String.class,int.class);
method.invoke(objectBook,"faker",108);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
输出结果如下:
玩家faker超神了!拥有108个英雄
参考链接: