什么是反射
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁
一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
Apple apple = new Apple(); //直接初始化,「正射」
apple.setPrice(4);
上面这样子进行类对象的初始化,我们可以理解为「正」。
而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。
这时候,我们使用 JDK 提供的反射 API 进行反射调用:
Class clz = Class.forName("com.chenshuyi.reflect.Apple");
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。
反射的基本使用
1.获取类
(1)任意对象的getClass()方法
通过任意对象的getClass方法,就能得到他的类是什么,得到Class对象
//第一种方式获取Class对象
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
System.out.println(stuClass.getName());
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
//第二种方式获取Class对象
Class stuClass2 = Student.class;
System.out.println(stuClass2);
(3)通过Class类的静态方法:forName(String className)(最常用)
//第三种方式获取Class对象
try {
Class stuClass3 = Class.forName("com.test1.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
三种方式中,常用第三种,第一种对象都有了还要反射干什么,第二种需要导入类包,依赖太强,不导包就抛编译错误。一般都使用第三种,一个字符串可以传入也可以写在配置文件中等多种方法。
2.判断是否为某个类的实例
try {
Student s = new Student();
Class stuClass3 = Class.forName("com.test1.Student");
//判断对象s是否是stuClass3类的实例
boolean b = stuClass3.isInstance(s);
System.out.println(b);//true
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
3.创建实例
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例
Class stuClass3 = Class.forName("com.test1.Student");
//通过类反射出对象,这个方法从jdk9之后就被抛弃了
Object str = stuClass3.newInstance();
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象
这种方法可以用指定的构造器构造类的实例
Class stuClass3 = Class.forName("com.test1.Student");
//通过构造器反射对象,getConstructor的参数为构造器要传入的数据类型的类
Constructor constructor = stuClass3.getConstructor(String.class,int.class);
Object obj = constructor.newInstance("李明",32);//传入构造器不同的参数
4.获取构造器
//获取全部公共的构造器
Constructor[] constructors = stuClass3.getConstructors();
//获取所有构造器,不管访问权限是什么
Constructor[] constructors2 = stuClass3.getDeclaredConstructors();
//获取单个公共的构造器
Constructor constructor = stuClass3.getConstructor(String.class,int.class);
//获取单个构造器,不管访问权限
Constructor constructor2 = stuClass3.getDeclaredConstructor();
//调用构造器的方法反射对象
Object obj = constructor.newInstance("李明",32);
5.设置访问权限
不管是属性,方法还是构造器对象。哪怕是私有的,通过反射,可以忽略访问修饰符,直接访问
constructor.setAccessible(true);//暴力访问(忽略掉访问修饰符)
6.获取属性并设置属性
Class stuClass3 = Class.forName("com.test1.Student");
//获取所有公共字段
Field[] fieldArray = stuClass3.getFields();
//获取所有字段,不限于访问修饰符
Field[] fieldArray2 = stuClass3.getDeclaredFields();
//获取age字段,公共的
Field f = stuClass3.getField("age");
//获取score字段,不限于访问符
Field f2 = stuClass3.getDeclaredField("score");
f2.setAccessible(true);//score字段是私有的,设置访问
//设置属性
//通过类的构造器反射一个对象
Object obj = stuClass3.getConstructor().newInstance();
//设置属性,传入这个属性的对象,和值。反射思想反着来
f.set(obj,"23");
7.获取方法并使用
Class stuClass3 = Class.forName("com.test1.Student");
//全部,公共
Method[] m =stuClass3.getMethods();
//全部,所有访问修饰,不包括继承的
Method[] m2 =stuClass3.getDeclaredMethods();
//单个,公共,传入参数为方法名,可变形参为方法参数的类型类
Method m3 =stuClass3.getMethod("run");
//单个,所有修饰符,show方法是私有的
Method m4 = stuClass3.getDeclaredMethod("show",String.class,int.class);
m4.setAccessible(true);
//使用方法
//通过类的构造器反射一个对象
Object obj = stuClass3.getConstructor().newInstance();
//调用方法,传入这个方法的对象,和参数
Object i = m4.invoke(obj,"传入文字参数",123);//方法返回值为i
//如果方法是静态的,第一个对象参数传入null
m3.invoke(null);
8.反射创建数组
//反射一个String[]数组,长度为5
Object objarr = Array.newInstance(String.class,5);
Array.set(objarr,0,"数组的第一个值");
Array.set(objarr,1,"数组的第二个值");
Array.set(objarr,2,"数组的第三个值");
//获取某一项的内容
System.out.println(Array.get(objarr,1));
9.通过反射越过泛型检查
泛型用在编译期,编译过后泛型擦除(消失掉),所以是可以通过反射越过泛型检查的
//创建一个数组列表,这时候它限定的是String类型
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
Class arrlist = strList.getClass();
Method add = arrlist.getMethod("add",Object.class);//获取add方法,添加的类型为object
//向这个strList列表对象中,添加各种类型的数据
add.invoke(strList,100);
add.invoke(strList,true);
for(Object obj:strList){
System.out.println(obj);
}