反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
下面介绍下反射在Java项目中的使用
Class类的使用
概念理解
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
获取Class实例的方式
不能直接创建Class的实例对象,因为Class类的构造方法是私有的,只有jvm可以去创建。(我们没有创建Class对象的权限,只能有JVM在运行时创建出Class对象)
利用对象调用getClass()方法获取该对象的Class实例;
使用Class类的静态方法forName(),用类的名字获取一个Class实例,源码如下;
@CallerSensitive
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
{
if (loader == null) {
loader = BootClassLoader.getInstance();
}
Class<?> result;
try {
// 这个过程是通过JVM完成的,此处为Native方法
result = classForName(name, initialize, loader);
} catch (ClassNotFoundException e) {
Throwable cause = e.getCause();
if (cause instanceof LinkageError) {
throw (LinkageError) cause;
}
throw e;
}
return result;
}
- 运用.class的方式获取Class实例,对于基本数据类型的封装类,还可以采用TYPE来获取对应的基本数据类型的Class实例
综上所述,其实我们代码中创建的每一个类都是一个对象,只不过它是Class类的实例对象,这个对象我们称为该类的类类型。并且一个类只可能是Class类的一个实例对象,即获取的类类型是相同的
JVM创建Class对象的过程
源文件经过编译(javac.exe)以后,得到一个或多个.class文件。
.class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),加载到内存中的缓存。
每一个放入缓存中的.class文件就是一个Class的实例.
下面是获取Class对象的四种方法
public class ClazzDemo {
public static void main(String[] args) {
// 通过类名直接获取
Class<ClazzDemo> clazz1 = ClazzDemo.class;
// 通过对象的getClass()获取
ClazzDemo demo = new ClazzDemo();
Class<ClazzDemo> clazz2 = (Class<ClazzDemo>) demo.getClass();
// 通过Class.forName()动态加载获取
Class<ClazzDemo> clazz3 = null;
try {
clazz3 = (Class<ClazzDemo>) Class.forName("reflect.ClazzDemo");
} catch (ClassNotFoundException ignored) {
}
// 通过类加载器进行获取
ClassLoader classLoader = demo.getClass().getClassLoader();
Class<ClazzDemo> clazz4 = null;
try {
clazz4 = (Class<ClazzDemo>) classLoader.loadClass("reflect.ClazzDemo");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("clazz1 " + clazz1.hashCode());
System.out.println("clazz2 " + clazz2.hashCode());
System.out.println("clazz3 " + clazz3.hashCode());
System.out.println("clazz4 " + clazz4.hashCode());
/*
结果: hashcode 一样,为同一个对象
clazz1 1956725890
clazz2 1956725890
clazz3 1956725890
clazz4 1956725890
*/
}
}
根据Class类型动态创建类
//~ClazzDemo中
private static void newInstanceTest() {
// 通过类的类型创建实例对象
ClazzDemo demo = null;
Class<ClazzDemo> clazz = ClazzDemo.class;
try {
// 使用newInstance()动态创建, 调用无参的构造方法
demo = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
assert demo != null;
demo.showMsg();
/*
结果
clazzDemo is private , but we can contract this by reflect
ClazzDemo #showMsg()
*/
}
// 反射也可以在类外部调用私有构造器
private ClazzDemo(){
System.out.println("clazzDemo is private , but we can contract this by reflect");
}
private void showMsg() {
System.out.println("ClazzDemo #showMsg()");
}
动态获取类的信息
/**
* 动态获取Class类的信息
*/
private static void getClassInfo() {
Class<ClazzDemo> clazz = ClazzDemo.class;
// Get class name.
System.out.println("Class name " + clazz.getName());
// Get all declared methods.
// getMethod()只能获取所有公共方法
// getDeclaredMethods()获取所有声明的方法,包括私有
Method[] methods = clazz.getDeclaredMethods();
for (Method aMethod : methods) {
System.out.println("Method: " + aMethod.getName());
// 1. 获取注解
Annotation[] annotations = aMethod.getDeclaredAnnotations();
for (Annotation aAnnotation : annotations) {
System.out.println(" Annotation:" + aAnnotation);
}
// 2.获取返回类型
Class returnType = aMethod.getReturnType();
System.out.println("return:" + returnType.getSimpleName());
// 3. 获取参数类型
Class[] params = aMethod.getParameterTypes();
for (Class aParams : params) {
System.out.println(" params: " + aParams.getSimpleName());
}
// 4. 获取异常类型
Class[] expts = aMethod.getExceptionTypes();
for (Class aExpts : expts) {
System.out.println(" expt:" + aExpts.getSimpleName());
}
// 5. 获取声明关键字,public,protected,static等
int modifier = aMethod.getModifiers();
System.out.println("isPrivate: " + Modifier.isPrivate(modifier));
System.out.println("isStatic: " + Modifier.isStatic(modifier));
System.out.println("------------------------------------");
}
}
运行结果
Class name reflect.ClazzDemo
Method: getClassInfo
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: newInstanceTest
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: showMsg
return:void
expt:RuntimeException
isPrivate: true
isStatic: false
------------------------------------
Method: testMethod
return:void
isPrivate: false
isStatic: false
------------------------------------
Method: getClazzTest
return:void
isPrivate: true
isStatic: true
------------------------------------
Method: main
return:void
params: String[]
isPrivate: false
isStatic: true
------------------------------------
获取声明的成员变量
/**
* 获取所有声明的类变量
*/
private static void getClassField() {
Class<ClazzDemo> clazz = ClazzDemo.class;
Field[] fields = clazz.getDeclaredFields();
for (Field aField : fields) {
// 获取名称
System.out.println(aField.getName());
// 获取类型
System.out.println(aField.getType().getSimpleName());
// 获取声明关键字
int modifder = aField.getModifiers();
System.out.println("isFinal " + Modifier.isFinal(modifder));
System.out.println("-------------------------------");
}
}
运行结果
mPubField
String
isFinal false
-------------------------------
mProField
String
isFinal false
-------------------------------
mPriField
String
isFinal false
-------------------------------
$assertionsDisabled
boolean
isFinal true
-------------------------------
获取类的构造器
/**
* 获取类的构造器
*/
private static void getClassConstrator() {
Class<ClazzDemo> clazz = ClazzDemo.class;
Constructor<ClazzDemo>[] constructors = (Constructor<ClazzDemo>[]) clazz.getDeclaredConstructors();
for (Constructor<ClazzDemo> aConstructor : constructors) {
System.out.println(aConstructor.getName());
// 获取参数类型
Class[] parameter = aConstructor.getParameterTypes();
for (Class aParameter : parameter) {
System.out.println(" " + aParameter.getName());
}
System.out.println("---------------------------------");
}
}
运行结果
reflect.ClazzDemo
java.lang.String
java.lang.String
java.lang.String
---------------------------------
reflect.ClazzDemo
---------------------------------
反射调用方法
使用setAccessible(true)取消安全检查,提高反射效率
AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问
由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
/**
* 反射调用某个方法
*/
private static void invokeClazzMethod() {
Class<ClazzDemo> clazz = ClazzDemo.class;
ClazzDemo demo = new ClazzDemo();
try {
// 使用反射找到某个方法,此处为无参方法
// getMethod()只能调用public方法,使用getDeclaredMethod()可以调用任何方法
Method method = clazz.getDeclaredMethod("showMsg");
// 反射调用
method.invoke(demo);
// 调用有参的方法
Method method1 = clazz.getDeclaredMethod("showMsg", String.class);
method1.invoke(demo, "Hello World");
} catch (Exception e) {
e.printStackTrace();
}
}
下面是重载的两个方法
private void showMsg() throws RuntimeException {
System.out.println("ClazzDemo #showMsg()");
}
private void showMsg(String msg) {
System.out.println(msg);
}
反射调用结果
ClazzDemo #showMsg()
Hello World
反射修改成员变量的值
public String mPubField;
protected String mProField;
private String mPriField;
/**
* 修改成员变量的值
*/
private static void modifyFieldValue() {
Class<ClazzDemo> clazz = ClazzDemo.class;
ClazzDemo demo = new ClazzDemo();
try {
// 获取属性
Field pub = clazz.getDeclaredField("mPubField");
Field pro = clazz.getDeclaredField("mProField");
Field pri = clazz.getDeclaredField("mPriField");
// 取消Java运行时的安全检查,提高执行效率
pub.setAccessible(true);
pro.setAccessible(true);
pri.setAccessible(true);
// 动态修改这个对象的属性值
pub.set(demo, "public_field");
pro.set(demo, "protected_field");
pri.set(demo, "private_field");
System.out.println(demo.mPubField);
System.out.println(demo.mProField);
System.out.println(demo.mPriField);
} catch (Exception e) {
e.printStackTrace();
}
}
/*运行结果
public_field
protected_field
private_field
*/
通过反射修改final修饰的成员变量
通过反射修改final修饰的成员变量需要分两种情况进行考虑.
当final修饰的成员变量在定义的时候就初始化了值,那么java反射机制就已经不能动态修改它的值了。
当final修饰的成员变量在定义的时候并没有初始化值的话,那么就还能通过java反射机制来动态修改它的值。
原因是编译期间final类型的数据自动被优化了,即:所有用到该变量的地方都被替换成了常量。所以我们就无法通过反射修改属性值了
private static void modifyFinalFieldValue() {
Class<Target> clazz = Target.class;
Target target = Target.newInstance();
// 修改两个属性值
try {
final Field field1 = clazz.getDeclaredField("canModify");
final Field field2 = clazz.getDeclaredField("cannotModify");
final Field field3 = clazz.getDeclaredField("CONSTANT");
/**
* 修改static final修饰常量,需要去掉final的影响
* 修改这些变量由于JVM的优化特性可能不成功, 如int,String,在JVM运行时会将这些值优化成常量
* 这里使用Object作为演示,JVM不会优化这个变量,所以能够成功
*/
final Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
modifiers.set(field3, field3.getModifiers() & ~Modifier.FINAL);
// 通过setAccessible(true)提高性能,否则修改私有的属性会出现IllegalAccessException
field1.setAccessible(true);
field2.setAccessible(true);
field3.setAccessible(true);
// 使用反射修改两个final成员变量
field1.set(target, "changed_value");
field2.set(target, "changed_value");
field3.set(target, "changed_value");
System.out.println(target.getCanModify());
System.out.println(target.getCannotModify());
System.out.println(Target.getCONSTANT());
/*结果, 第一个修改了,第二个没有修改
changed_value
original_string
changed_value
*/
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
反射使用的Target对象
public class Target {
/***
* 修改static final字段
*/
private static final Object CONSTANT = "original_constant";
// 反射修改用`final`修饰的成员变量
// 如果在类定义时声明,则不能通过反射修改
private final String cannotModify = "original_string";
// 在构造器中进行定义的成员变量,能够通过反射修改
private final String canModify;
public static Target newInstance() {
return new Target("original_string");
}
private Target(String canModify) {
// 此处的变量在构造器中初始化, 可以通过反射进行修改
this.canModify = canModify;
System.out.println("私有构造器执行了");
}
public String getCannotModify() {
return cannotModify;
}
public String getCanModify() {
return canModify;
}
public static Object getCONSTANT() {
return CONSTANT;
}
}
反射调用构造器
/**
* 通过反射调用构造器
*/
private static void invokeConstructor() {
Constructor constructor = null;
try {
constructor = Target.class.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Target target = (Target) constructor.newInstance("123");
System.out.println(target.getCanModify());
//结果
// 私有构造器执行了
// 123
} catch (Exception e) {
e.printStackTrace();
}
}
通过反射破坏泛型
public static void main(String[] args) {
ArrayList list = new ArrayList();
ArrayList<String> list1 = new ArrayList<String>();
list1.add("hello");
//list1.add(20);编译错误
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.println(c1 == c2);
//反射的操作都是编译之后的操作
try {
// 编译后泛型会被擦除成边界,这是通过add方法可以添加任何类型的对象了
Method m = c2.getMethod("add",Object.class);
m.invoke(list1,20);//绕过编译操作就绕过了泛型
System.out.println(list1.size());
System.out.println(list1);
} catch (Exception e) {
e.printStackTrace();
}
}