什么是反射
- 反射是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API 取的任何类的内部信息,并能够操作任意对象的内部属性和方法。
- 加载完类之后再堆内存的方法区会产生一个class类型的对象,这个对象包含了完整的类结构信息。这个对象就像一面镜子,透过这个对象可以看到类的结构,所以形象的将它称之为反射。
反射的过程与正常的执行过程相反。
- 正常方式 :引入包名和类名=>通过new实例化=>取得实例化对象
- 反射方式:实例化对象=>获得getClass()方法=>得到包名和类名
反射相关的API
java.lang.Class : 代表一个类
java.lang.reflect.Method :代表类的方法
java.lang.reflect.Field :代表类的成员变量
java.lang.reflect.Constructor :代表类的构造器
……
在Object类中定义了如下方法,此方法被所有类继承
public final Class getClass()
获得Class对象的方法
//已知全类名
Class c = Class.forName("com.example.reflect.User");
//已知某个类的实例
Class c = user.getClass();
//已知具体的类
Class c = User.class;
//获得父类Class
Class c0 = c.getSuperclass();
哪些类型拥有class对象
Class c1 = Object.class; //类
Class c2 = Comparable.class; //接口
Class c3 = String[].class; //一维数组
Class c4 = int[][].class; //二维数组
Class c5 = Override.class; //注解
Class c6 = ElementType.class; //枚举
Class c7 = Integer.class; //基本数据类型
Class c8 = void.class; //void
Class c9 = Class.class; //class
只要元素类型一样,就是同一个class
类的加载过程
- 类的加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成(生成Class对象)
- 类的链接:将类的数据合并到jre中(为类变量static静态代码块分配内存)
- 类的初始化:JVM对类进行初始化(执行类构造器()方法为对象赋值)
类的运行时结构
测试类:User
package com.example.reflect;
class User{
private String name;
private int id;
private int age;
public User() {
}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
}
测试:通过反射获取class对象里的内容
public class GetClassInfo {
public static void main(String[] args) throws Exception{
//获取类的三种方法
// Class c = Class.forName("com.example.reflect.User");
Class c = User.class;
// User user = new User();
// Class c = user.getClass();
//获取包名加类名
System.out.println(c.getName());
//获得类名
System.out.println(c.getSimpleName());
//获得类的属性
System.out.println("--------------------------------分割线(类的公共属性)---------------------------------");
Field[] fields = c.getFields(); //只能获取public属性
for (Field field : fields) {
System.out.println("类的公共属性:"+field);
}
System.out.println("--------------------------------分割线(类的全部属性)---------------------------------");
fields = c.getDeclaredFields(); //可以获取全部属性
for (Field field : fields) {
System.out.println("类的全部属性:"+field);
}
System.out.println("--------------------------------分割线(类的特定属性)---------------------------------");
System.out.println("类的特定属性:"+c.getDeclaredField("name"));
System.out.println("--------------------------------分割线(本类以及父类的公共方法)---------------------------------");
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println("本类以及父类的公共方法:"+method);
}
System.out.println("--------------------------------分割线(本类的全部方法)---------------------------------");
methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println("本类的全部方法:"+method);
}
System.out.println("--------------------------------分割线(本类的特定方法)---------------------------------");
System.out.println("本类的特定方法:"+c.getDeclaredMethod("getName", null));
System.out.println("本类的特定方法:"+c.getDeclaredMethod("setName", String.class));
System.out.println("--------------------------------分割线(获得公共构造器)---------------------------------");
Constructor[] constructors = c.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("公共构造器:"+constructor);
}
System.out.println("--------------------------------分割线(获得全部构造器)---------------------------------");
constructors = c.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("全部构造器:"+constructor);
}
System.out.println("--------------------------------分割线(获得指定构造器)---------------------------------");
System.out.println("有参构造"+c.getConstructor(String.class, int.class, int.class));
System.out.println("无参构造"+c.getConstructor());
}
}
输出结果:
类的包名加类名:com.example.reflect.User
类名:User
--------------------------------分割线(类的公共属性)---------------------------------
--------------------------------分割线(类的全部属性)---------------------------------
类的全部属性:private java.lang.String com.example.reflect.User.name
类的全部属性:private int com.example.reflect.User.id
类的全部属性:private int com.example.reflect.User.age
--------------------------------分割线(类的特定属性)---------------------------------
类的特定属性:private java.lang.String com.example.reflect.User.name
--------------------------------分割线(本类以及父类的公共方法)---------------------------------
本类以及父类的公共方法:public java.lang.String com.example.reflect.User.toString()
本类以及父类的公共方法:public java.lang.String com.example.reflect.User.getName()
本类以及父类的公共方法:public int com.example.reflect.User.getId()
本类以及父类的公共方法:public void com.example.reflect.User.setName(java.lang.String)
本类以及父类的公共方法:public void com.example.reflect.User.setAge(int)
本类以及父类的公共方法:public void com.example.reflect.User.setId(int)
本类以及父类的公共方法:public int com.example.reflect.User.getAge()
本类以及父类的公共方法: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()
--------------------------------分割线(本类的全部方法)---------------------------------
本类的全部方法:public java.lang.String com.example.reflect.User.toString()
本类的全部方法:public java.lang.String com.example.reflect.User.getName()
本类的全部方法:public int com.example.reflect.User.getId()
本类的全部方法:public void com.example.reflect.User.setName(java.lang.String)
本类的全部方法:public void com.example.reflect.User.setAge(int)
本类的全部方法:public void com.example.reflect.User.setId(int)
本类的全部方法:public int com.example.reflect.User.getAge()
--------------------------------分割线(本类的特定方法)---------------------------------
本类的特定方法:public java.lang.String com.example.reflect.User.getName()
本类的特定方法:public void com.example.reflect.User.setName(java.lang.String)
--------------------------------分割线(获得公共构造器)---------------------------------
公共构造器:public com.example.reflect.User()
公共构造器:public com.example.reflect.User(java.lang.String,int,int)
--------------------------------分割线(获得全部构造器)---------------------------------
全部构造器:public com.example.reflect.User()
全部构造器:public com.example.reflect.User(java.lang.String,int,int)
--------------------------------分割线(获得指定构造器)---------------------------------
有参构造public com.example.reflect.User(java.lang.String,int,int)
无参构造public com.example.reflect.User()
class的使用
获得class对象以后如何才能使用它呢?
- 首先获得类的class对象
//获得class对象
Class c = Class.forName("com.example.reflect.User");
- 通过无参构造得到对象
//通过无参构造器创建对象
User user = (User) c.newInstance(); //本质是调用了类的无参构造器,没有无法构造无法创建
System.out.println(user);
- 通过有参构造得到对象
//通过有参构造器创建对象
Constructor constructor = c.getDeclaredConstructor(String.class, int.class, int.class);
User user = (User) constructor.newInstance("香草拿铁", 8, 18);
- 通过反射操作方法
//通过无参构造器创建对象
User user = (User) c.newInstance();
//获取setName方法
Method setName = c.getDeclaredMethod("setName", String.class);
//调用invoke方法给方法传递参数(对象,方法参数)
setName.invoke(user,"香草拿铁");
System.out.println(user.getName());
- 通过反射操作属性
//通过无参构造器创建对象
User user = (User) c.newInstance();
//获取name属性
Field name = c.getDeclaredField("name");
//由于属性是private属性,因此需要添加权限才能修改
name.setAccessible(true);
//给name属性赋值
name.set(user,"香草拿铁");
System.out.println(user.getName());
普通方式和反射方式性能对比
反射机制虽然功能强大,但在性能方面消耗资源的也比较多
测试同时调用一个方法使用的时间对比
public class TimeTest {
//普通方式调用
public static void test1(){
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
long time = endTime-startTime;
System.out.println("普通方式执行时间:"+time+"ms");
}
//反射方式调用
public static void test2() throws Exception {
User user = new User();
Class c = user.getClass();
Method getName = c.getMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
long time = endTime-startTime;
System.out.println("反射方式执行时间:"+time+"ms");
}
//反射方式调用(关闭检测)
public static void test3() throws Exception {
User user = new User();
Class c = user.getClass();
Method getName = c.getMethod("getName", null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
long time = endTime-startTime;
System.out.println("反射方式(关闭检测)执行时间:"+time+"ms");
}
public static void main(String[] args) throws Exception {
test1();
test2();
test3();
}
}
输出结果分析:
普通方式执行时间:4ms
反射方式执行时间:2679ms
反射方式(关闭检测)执行时间:1211ms
很显然,使用反射机制调用时间更长,使用 反射获取的对象.setAccessible(true); 关闭检测可以适当提高性能,但却仍比普通方式要慢很多,因此一般情况下不建议大量使用反射机制。
通过反射操作泛型
通过getGenericParameterTypes()和getGenericReturnType()方法可以获取参数和结果中的泛型信息
public class Test {
public void test01(Map<String,User> map, List<String> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws Exception {
Class c = MyTest.class;
Method method01 = c.getMethod("test01", Map.class, List.class);
Type[] genericParameterTypes = method01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("参数值泛型信息:"+genericParameterType);
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("参数值泛型内部参数信息:"+actualTypeArgument);
}
}
}
System.out.println("-----------------------------------------我是分割线---------------------------------------------");
Method method02 = c.getMethod("test02", null);
Type genericReturnType = method02.getGenericReturnType();
System.out.println("返回值泛型信息:"+genericReturnType);
if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("返回值泛型内部参数信息:"+actualTypeArgument);
}
}
}
}
输出结果:打印出Map和List的信息,以及Map和List内部参数信息
参数值泛型信息:java.util.Map<java.lang.String, com.example.reflect.User>
参数值泛型内部参数信息:class java.lang.String
参数值泛型内部参数信息:class com.example.reflect.User
参数值泛型信息:java.util.List<java.lang.String>
参数值泛型内部参数信息:class java.lang.String
-----------------------------------------我是分割线---------------------------------------------
返回值泛型信息:java.util.Map<java.lang.String, com.example.reflect.User>
返回值泛型内部参数信息:class java.lang.String
返回值泛型内部参数信息:class com.example.reflect.User
通过反射操作注解
通过反射我们还可以获得自定义的一些注解的值。
- 实现两个注解,分别为类注解与属性注解
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filed{
String columnName();
String type();
int length();
}
- 测试类
@Table("db.user")
class User{
@Filed(columnName = "db_id",type = "int",length = 10)
private int id;
@Filed(columnName = "db_age",type = "int",length = 10)
private int age;
@Filed(columnName = "db_name",type = "varchar",length = 3)
private String name;
public User() {
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
- 获取注解中的值进行操作
public class TestAnnotation {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.example.reflect.User");
Annotation[] annotations = c.getAnnotations();
//通过反射获得注解
for (Annotation annotation : annotations) {
System.out.println("类注解对象:"+annotation);
}
//获得注解的value值
Table table = (Table) c.getAnnotation(Table.class);
System.out.println("类注解的值:"+table.value());
//获取特定属性的注解值
Field[] declaredFields = c.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("--------------------------属性名:"+declaredField.getName()+"----------------------------------");
Filed annotation = declaredField.getAnnotation(Filed.class);
System.out.println("注解columnName:"+annotation.columnName());
System.out.println("注解type:"+annotation.type());
System.out.println("注解length:"+annotation.length());
}
}
}
- 输出结果
类注解对象:@com.example.reflect.Table(value=db.user)
类注解的值:db.user
--------------------------属性名:id----------------------------------
注解columnName:db_id
注解type:int
注解length:10
--------------------------属性名:age----------------------------------
注解columnName:db_age
注解type:int
注解length:10
--------------------------属性名:name----------------------------------
注解columnName:db_name
注解type:varchar
注解length:3
使用这种方式就可以通过反射获取一些注解的信息,从而进行数据的操作,这种方式被大量运用在框架当中。