java反射机制
1、反射的概念
概述
- Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
- 当我们拿到了一个类的字节码文件时,就可以通过反射来拿到该类中,所有的属性、方法、及其构造方法等等。
- 框架的底层一般都涉及到反射机制,反射是框架开发的灵魂。
- 解耦,增强程序的可扩展性。
2、获取字节码
环境 类
package com.qiumin.library_management_system.test;
/**
* @Author: qiumin
* @Description: love code
* @Date: Created in 22:13 2022/4/19
*/
public class Fanshe {
public String name;
private Integer code;
public Fanshe() {
}
public Fanshe(String name, Integer code) {
this.name = name;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String toString() {
return "Fanshe{" +
"name='" + name + '\'' +
", code=" + code +
'}';
}
public void eat(){
System.out.println("eat--------方法");
}
public void eat(Integer d){
System.out.println("eat--------方法"+d);
}
}
获取Class字节码的三种方式
- Class aClass = Class.forName(“com.qiumin.library_management_system.test.Fanshe”);·//括号里填类的全类名即包加类的全限定名
@Test
void testFanshe(){
try {
//括号里填类的全类名即包加类的全限定名
Class aClass = Class.forName("com.qiumin.library_management_system.test.Fanshe");
System.out.println(aClass);
} catch (ClassNotFoundException | NoSuchFieldException e) {
e.printStackTrace();
}
}
- 直接类名.class Fanshe.class
@Test
void testFanshe(){
try {
Class<Fanshe> aClass = Fanshe.class; //类名.class
System.out.println(aClass);
} catch (Exception e) {
e.printStackTrace();
}
}
- 当有该类的对象时可以用 **对象名.getClass()**得到
@Test
void testFanshe(){
try {
Fanshe fanshe = new Fanshe(); //new出了对象
Class aClass = fanshe.getClass(); //用对象得到class
System.out.println(aClass);
} catch (Exception e) {
e.printStackTrace();
}
}
无论用那种方式得到的class文件都是同一份,即相等
3、class字节码文件的功能
获取类中的属性
-
Field name1 = aClass.getField(“name”); 【获取名为name的属性 public】
-
Field[] fields1 = aClass.getFields(); 【获取所有的属性 public】
-
Field name = aClass.getDeclaredField(“name”); 【获取名为name属性 包括非public的属性】
-
Field[] fields = aClass.getDeclaredFields(); 【获取所有的属性 包括非public的属性】
-
Object o = name1.get(fanshe); 【查看属性的值 里面需要传入该类的对象fanshe】
-
name1.set(fanshe,“qiumin”); 【设置属性的值,第一个参数为该类的对象fanshe,第二个是设置的值】
@Test
void testFanshe(){
try {
//得到class
Fanshe fanshe = new Fanshe();
Class aClass = fanshe.getClass();
System.out.println(aClass);
//获取属性
Field name1 = aClass.getField("name");
//查看属性的值
Object o = name1.get(fanshe); //里面需要传入该类的对象
System.out.println(o);
name1.set(fanshe,"qiumin"); //设置属性的值,第一个参数为该类的对象,第二个是设置的值
System.out.println(fanshe);
//查看上述输出结果 下图
Field[] fields1 = aClass.getFields();
Field[] fields = aClass.getDeclaredField("name");
Field[] fields = aClass.getDeclaredFields();
System.out.println(fields);
} catch (Exception e) {
e.printStackTrace();
}
}
注意:
- 没有带有**Declared**的方法得到的属性只能是public修饰的 如:Field name1 = aClass.getField(“name”);只能获取public修饰的。
- 当通过带有Declared 的方法得到的private修饰属性要设置值时我们要设置暴力反射,否则就不能设置值
- 设置暴力反射:属性.setAccessible(true);
获取类中的方法
- Method eat = aClass.getMethod(参数1方法名,参数2方法名中的参数列表); 【获取指定非构造方法的类方法 public】
- Method[] methods = aClass.getMethods(); 【获取所有非构造方法的类方法 public】
- Method methods2 = aClass.getDeclaredMethod(参数1方法名,参数2方法名中的参数列表); 【获取所有非构造方法的指定类方法 】
- Method[] methods3 = aClass.getDeclaredMethods(); 【获取所有非构造方法的类方法 】
- 执行方法 method.invoke(参数1该类对象名,参数2参数列表值 ) 相当于调用方法
@Test
void testFanshe(){
try {
//得到class
Fanshe fanshe = new Fanshe();
Class aClass = fanshe.getClass();
System.out.println(aClass);
Method eat = aClass.getMethod("eat"); //得到类中的无参的eat方法
Method eat2 = aClass.getMethod("eat",Integer.class); //得到类中的有参的eat方法
System.out.println(eat);
System.out.println(eat2);
eat.invoke(fanshe); //执行方法 因为调用无参的eat所以只传入该类对象即可
eat2.invoke(fanshe,6); //执行方法 调用有参数的eat
Method[] methods = aClass.getMethods();
Method methods2 = aClass.getDeclaredMethod("eat");
Method[] methods3 = aClass.getDeclaredMethods();
} catch (Exception e) {
e.printStackTrace();
}
}
注意:
- 没有带有**Declared**的方法得到的构造方法只能是public修饰的
- 设置暴力反射:方法名.setAccessible(true);
- 执行方法 方法名.invoke(对象名,参数)
获取类的构造方法
- Constructor constructor = aClass.getConstructor(String.class, Integer.class); 【获取参数类型为括号中类型的构造器,不填为无参构造 public】
- Constructor[] constructors = aClass.getConstructors(); 【获取无参的构造器 public】
- Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class); 【获取所有参数类型为括号中类型的构造器,不填为无参构造】
- Constructor[] declaredConstructors = aClass.getDeclaredConstructors(); 【获取无参的构造器】
- 利用构造方法创建对象 Object o = constructor.newInstance(“qiumin”,200); //利用构造器创建对象
@Test
void testFanshe(){
try {
//得到class
Fanshe fanshe = new Fanshe();
Class aClass = fanshe.getClass();
System.out.println(aClass);
//得到类中的有参构造方法 括号传入构造方法参数的类型,不写为空参构造
Constructor constructor = aClass.getConstructor(String.class, Integer.class);
Object o = constructor.newInstance("qiumin",200); //利用构造器创建对象
System.out.println(o);
Constructor[] constructors = aClass.getConstructors();
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, Integer.class);
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
} catch (Exception e) {
e.printStackTrace();
}
}
注意:
- 没有带有**Declared**的方法得到的构造方法只能是public修饰的
- 设置暴力反射:构造方法.setAccessible(true);
- 当是无参构造创建对象时可以简化 直接 字节码对象.newInstance()
获取类名
- String name = aClass.getName(); 【获取全限定类名 com.qiumin.library_management_system.test.Fanshe】
- String simpleName = aClass.getSimpleName(); 【获取简短类名 Fanshe】
@Test
void testFanshe(){
try {
//得到class
Fanshe fanshe = new Fanshe();
Class aClass = fanshe.getClass();
System.out.println(aClass);
String name = aClass.getName(); //获取全限定类名
System.out.println(name);
String simpleName = aClass.getSimpleName();
System.out.println(simpleName);
} catch (Exception e) {
e.printStackTrace();
}
}
4、反射案例
业务需求:写一个框架,我们可以获取任意类即任意方法
实现:
- 配置文件【properties】
- 反射机制
步骤:
- 将需要创建的对象的全类名和需要执行的的方法写入配置文件
- 加载配置文件
- 反射机制将该类进内存
- 创建对象
- 执行方法
1、类
package com.qiumin.library_management_system.test;
/**
* @Author: qiumin
* @Description: love code
* @Date: Created in 22:13 2022/4/19
*/
public class Fanshe {
public void sleep(){
System.out.println("sleep.......");
}
}
package com.qiumin.library_management_system.test;
/**
* @Author: qiumin
* @Description: love code
* @Date: Created in 11:44 2022/4/20
*/
public class Fanshe2 {
public void eat(){
System.out.println("eat.......");
}
}
2、properties
className=com.qiumin.library_management_system.test.Fanshe
method=sleep
- 注意:spring boot中改文件需放在resource下面
- 将需要的类和方法配置在properties文件下
- 我们只要改配置文件,不需要改程序代码
测试
package com.qiumin.library_management_system.test;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @Author: qiumin
* @Description: love code
* @Date: Created in 12:01 2022/4/20
*/
public class Test {
public static void main(String[] args) throws Exception {
//new 出properties对象
Properties pro = new Properties();
//得到该类的类加载器
ClassLoader classLoader = Test.class.getClassLoader();
//通过类加载器加载properties配置文件流文件
InputStream stream = classLoader.getResourceAsStream("Fanshe.properties");
//加载
pro.load(stream);
//获取配置文件的指定值
String className = pro.getProperty("className");
String sleep = pro.getProperty("method");
//通过方式得到自定类的字节码文件
Class aClass = Class.forName(className);
//创建对象
Object o = aClass.newInstance();
//获取方法
Method sleep1 = aClass.getMethod(sleep);
//执行方法
sleep1.invoke(o);
}
}
需要扩展改配置文件即可,不需动程序代码,提高可扩展性,这就是反射的好处之一
5、总结
- 反射机制是编写框架的基础,反射是框架的灵魂。
- 通过反射可以了解一个类的所有的东西。
- 反射是java基础的的基础,一般来说反射跟注解一起使用会简化开发。
- 不需动程序代码,提高可扩展性,这就是反射的好处之一。
- 一般的使用场景为:
反射机制
+properties配置文件
。
qiumin