Java Reflection
一、Java 反射机制
Java的反射(reflection)机制是指在程序的运行状态中,可以通过反射 API 获取任何类的内部信息(成员变量、构造器、方法等),并操作对象的属性和方法。
二、Class类对象
-
Class是一个类,父类是Object
-
Class类对象不是new出来的,而是系统创建的
//Class类构造器私有化,通过类加载器ClassLoader创建 private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; }
-
对于一个类的Class类对象,在内存中只存在一份(类只会被加载一次)
//两个Class对象hashcode相同 Class<?> c1 = Class.forName("com.qingsongxyz.User"); User user = new User(); Class c2 = user.getClass(); System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); /* 1956725890 1956725890 */
三、获取Class类对象方式
/**
* 获取Class类对象的方式
*/
public class GetClass {
/**
* 通过Class.forName(类的全路径)获取Class类对象
* @throws ClassNotFoundException
*/
@Test
public void test1() throws ClassNotFoundException {
Class<?> c1 = Class.forName("com.qingsongxyz.User");
System.out.println(c1);
//class com.qingsongxyz.User
}
/**
* 通过对象.getClass()获取Class类对象
*/
@Test
public void test2(){
User user = new User();
Class c1 = user.getClass();
System.out.println(c1);
//class com.qingsongxyz.User
}
/**
* 通过类加载器classLoader.loadClass(类的全路径)获取Class类对象
* @throws ClassNotFoundException
*/
@Test
public void test3() throws ClassNotFoundException {
String classAllPath = "com.qingsongxyz.User";
Class c1 = Class.forName(classAllPath);
ClassLoader classLoader = c1.getClassLoader();
Class c2 = classLoader.loadClass(classAllPath);
System.out.println(c2);
//class com.qingsongxyz.User
}
/**
* 对于八种基本数据类型通过 类型.class 获取Class类对象
*/
@Test
public void test4() {
Class c1 = byte.class;
Class c2 = short.class;
Class c3 = int.class;
Class c4 = long.class;
Class c5 = float.class;
Class c6 = double.class;
Class c7 = char.class;
Class c8 = boolean.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
/*
byte
short
int
long
float
double
char
boolean
*/
}
/**
* 对于包装类通过 类型.TYPE 获取Class类对象
*/
@Test
public void test5() {
Class c1 = Byte.TYPE;
Class c2 = Short.TYPE;
Class c3 = Integer.TYPE;
Class c4 = Long.TYPE;
Class c5 = Float.TYPE;
Class c6 = Double.TYPE;
Class c7 = Character.TYPE;
Class c8 = Boolean.TYPE;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
/*
byte
short
int
long
float
double
char
boolean
*/
}
}
(基本数据类型和他们的包装类的Class类对象相同)
四、不同类型的Class类对象
- 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- 接口
- 数组
- 枚举
- 注解
- 基本数据类型
- void
Class c1 = String.class; //外部类
Class c2 = Collection.class; //接口
Class c3 = int[].class; //一维数组
Class c4 = float[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = void.class; //void
Class c8 = int.class; //基本数据类型
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
/*
class java.lang.String
interface java.util.Collection
class [I
class [[F
interface java.lang.Override
class java.lang.annotation.ElementType
void
int
*/
五、Class类对象 API
public class User {
private String name;
private int age;
private String gender;
public User() {
}
public User(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", gender='" + gender + '\'' +
'}';
}
}
/**
* 使用Reflection API 操作对象
*/
@SuppressWarnings({"all"})
public class ReflectionAPI {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
Class c1 = Class.forName("com.qingsongxyz.User");
System.out.println("获取类所在包名:" + c1.getPackage());
System.out.println("获取类的全路径:" + c1.getName());
System.out.println("获取类名:" + c1.getSimpleName());
System.out.println("-----------------------------------");
/*
获取类所在包名:package com.qingsongxyz
获取类的全路径:com.qingsongxyz.User
获取类名:User
*/
System.out.println("获取类中public修饰的字段:");
Field[] fields = c1.getFields();
System.out.println(Arrays.toString(fields));
System.out.println("-----------------------------------");
/*
获取类中public修饰的字段:
[]
*/
System.out.println("获取类中声明的所有字段(包括private修饰的):");
fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------------------------------");
/*
获取类中声明的所有字段(包括private修饰的):
private java.lang.String com.qingsongxyz.User.name
private int com.qingsongxyz.User.age
private java.lang.String com.qingsongxyz.User.gender
*/
System.out.println("获取该类 以及 它的父类中public修饰的方法:");
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-----------------------------------");
/*
获取该类 以及 它的父类中public修饰的方法:
public java.lang.String com.qingsongxyz.User.toString()
public java.lang.String com.qingsongxyz.User.getName()
public void com.qingsongxyz.User.setName(java.lang.String)
public void com.qingsongxyz.User.setGender(java.lang.String)
public int com.qingsongxyz.User.getAge()
public java.lang.String com.qingsongxyz.User.getGender()
public void com.qingsongxyz.User.setAge(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
*/
System.out.println("获取该类中声明的所有方法(包括private修饰的):");
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("-----------------------------------");
/*
获取该类中声明的所有方法(包括private修饰的):
public java.lang.String com.qingsongxyz.User.toString()
public java.lang.String com.qingsongxyz.User.getName()
public void com.qingsongxyz.User.setName(java.lang.String)
public void com.qingsongxyz.User.setGender(java.lang.String)
public int com.qingsongxyz.User.getAge()
public java.lang.String com.qingsongxyz.User.getGender()
public void com.qingsongxyz.User.setAge(int)
*/
System.out.println("获取该类中public修饰的构造方法:");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("-----------------------------------");
/*
获取该类中public修饰的构造方法:
public com.qingsongxyz.User()
public com.qingsongxyz.User(java.lang.String,int,java.lang.String)
*/
System.out.println("获取该类中声明的所有构造方法(包括private修饰的):");
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("-----------------------------------");
/*
获取该类中声明的所有构造方法(包括private修饰的):
public com.qingsongxyz.User()
public com.qingsongxyz.User(java.lang.String,int,java.lang.String)
*/
ClassLoader classLoader = c1.getClassLoader();
System.out.println("获取该类的类加载器:" + classLoader);
System.out.println("-----------------------------------");
//获取该类的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println("newInstance获取该类的对象(该类有无参构造并且访问修饰符合适):");
User user = (User)c1.newInstance();
System.out.println("user:" + user);
System.out.println("-----------------------------------");
/*
newInstance获取该类的对象(该类有无参构造并且访问修饰符合适):
user:User{name='null', age='0', gender='null'}
*/
System.out.println("操作类的成员变量:");
Field name = c1.getDeclaredField("name");
System.out.println(name);
//关闭权限检查
name.setAccessible(true);
System.out.println("原始name:" + name.get(user));
name.set(user, "张三");
System.out.println("修改后name:" + name.get(user));
System.out.println("-----------------------------------");
/*
操作类的成员变量:
private java.lang.String com.qingsongxyz.User.name
原始name:null
修改后name:张三
*/
System.out.println("获取该类构造函数创建对象:");
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, String.class);
System.out.println(constructor);
User user1 = (User)constructor.newInstance("王五", 18, "男");
System.out.println("user1:" + user1);
System.out.println("-----------------------------------");
/*
获取该类构造函数创建对象:
public com.qingsongxyz.User(java.lang.String,int,java.lang.String)
user1:User{name='王五', age='18', gender='男'}
*/
System.out.println("获取该类方法并invoke执行:");
Method setAge = c1.getDeclaredMethod("setAge", int.class);
System.out.println(setAge);
setAge.invoke(user1, 20);
System.out.println("user1:" + user1);
System.out.println("-----------------------------------");
/*
获取该类方法并invoke执行:
public void com.qingsongxyz.User.setAge(int)
user1:User{name='王五', age='20', gender='男'}
*/
}
}
六、性能比较
/**
* 将反射获取对象和通过new创建对象操作进行耗时比较并优化
*/
public class Compare {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
test1();
test2();
test3();
}
public static void test1(){
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long end = System.currentTimeMillis();
System.out.println("new创建对象调用getName()十亿次用时:" + (end - start) + "ms");
}
public static void test2() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> c1 = Class.forName("com.qingsongxyz.User");
User user = (User)c1.newInstance();
Method getName = c1.getDeclaredMethod("getName");
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user);
}
long end = System.currentTimeMillis();
System.out.println("关闭权限检查前,反射创建对象调用getName()十亿次用时:" + (end - start) + "ms");
}
public static void test3() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> c1 = Class.forName("com.qingsongxyz.User");
User user = (User)c1.newInstance();
Method getName = c1.getDeclaredMethod("getName");
//关闭访问安全检查
getName.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user);
}
long end = System.currentTimeMillis();
System.out.println("关闭权限检查后,反射创建对象调用getName()十亿次用时:" + (end - start) + "ms");
}
}
/*
new创建对象调用getName()十亿次用时:0ms
关闭权限检查前,反射创建对象调用getName()十亿次用时:1969ms
关闭权限检查后,反射创建对象调用getName()十亿次用时:1479ms
*/
关闭类中属性、方法和构造函数的权限检查可以提高效率(xxx.setAccessible(true))
七、模拟Java框架底层
//自定义注解(模拟Spring注解@Value)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyValue {
String value() default "";
}
//pojo
public class Person {
@MyValue("张三")
private String name;
@MyValue("18")
private int age;
@MyValue("男")
private String gender;
public Person() {
}
public Person(String name, int age, String gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
/**
* 使用注解和反射,模拟框架底层实现
*/
public class Framework {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException {
//获取Person类Class对象
Class personClass = Person.class;
Field[] fields = personClass.getDeclaredFields();
//获得一个Person类的实例(通过无参构造)
Person person = (Person) personClass.newInstance();
//注入值
for (Field field : fields) {
//关闭访问安全检查
field.setAccessible(true);
//字段名
String fieldName = field.getName();
MyValue annotation = field.getAnnotation(MyValue.class);
//字段上注解注入的值
String value = annotation.value();
if(field.getType().equals(String.class)) {
field.set(person, value);
}
else if(field.getType().equals(int.class))
{
field.set(person, Integer.parseInt(value));
}
...
}
System.out.println(person);
}
}
//Person{name='张三', age=18, gender='男'}