Java 反射机制

Java 反射机制

  • 在程序运行时,获取类的完整结构信息,调用类对象的方法

  • 正射使用某个类,都会知道这个类,以及要用它来做什么,可以直接通过 new 实例化创建对象,然后使用这个对象对类进行操作

  • 反射一开始并不知道要初始化的是什么类,无法使用 new 来实例化创建对象,在运行时才知道要操作的是什么类,然后通过 JDK 提供的反射 API 来初始化对象,同样可以获取到类的完整构造以及调用对应的方法

反射的原理

  • Java 反射机制(Java Reflection) 是:Java 语言中一种动态(运行时)访问、检测、修改它本身的能力

  • 主要作用是:动态(运行时)获取类的完整结构信息、调用对象的方法

  • 另一种说法:Java 程序在运行时(动态)通过先创建一个类的反射对象,再对类进行相关操作

  • 获取一个类的反射对象的主要过程

    • 获取类的 Class 实例对象。

    • 根据 Class 实例对象获取 Constructor 对象。

    • 再根据 Constructor 对象的 newInstance() 方法获取到类的反射对象

    • 同样,根据 Class 实例对象可以获取类的 Method 对象。

    • 再根据 Method 对象的 invoke() 方法调用具体的类方法。

Java 反射 API

  • Class 类每个类都有的唯一对象反射的核心类,可以获取类的属性,方法等信息。

  • Field 类:表示类的成员变量,在 Java.lang.reflec 包中,可以用来获取和设置类之中的属性值

  • Method 类:表示类的方法,在 Java.lang.reflec 包中,可以用来获取类中的方法信息或者执行方法

  • Constructor 类:表示类的构造方法,在 Java.lang.reflec 包中。

例子

正射的例子

  • 正射调用时,目标类的静态代码块代码块 都调用了。

  • 而,构造方法则是调用时再启动。

public class JavaReflectionTest {
	
	public static void mainO1(String[] args) {
		// 正射调用
		System.out.println("=== 正射调用过程 1 ===");
		ReflectionTest reflectionTest = new ReflectionTest();
		reflectionTest.setId(1);
		System.out.println(reflectionTest.toString());
	}
	
	public static void mainO2(String[] args) {
		// 正射调用
		System.out.println("=== 正射调用过程 2 ===");
		ReflectionTest reflectionTest1 = new ReflectionTest("反射类", 2);
		System.out.println(reflectionTest1.toString());
	}

}

class ReflectionTest {
	
	private static final String NAME = "ReflectionTest";
	
	private static int number = 20221204;
	
	static {
		System.out.println("ReflectionTest 静态代码块");
	}
	
	private String describe;
	
	private int id;
	
	{
		System.out.println("ReflectionTest 代码块");
	}
	
	public ReflectionTest() {
		System.out.println("ReflectionTest 无参构造方法");
	}
	
	public ReflectionTest(String describe, int id) {
		System.out.println("ReflectionTest 全参构造方法");
		this.describe = describe;
		this.id = id;
	}
	
	public String getDescribe() {
		return describe;
	}
	
	public void setDescribe(String describe) {
		this.describe = describe;
	}
	
	public int getId() {
		return id;
	}
	
	public void setId(int id) {
		this.id = id;
	}
	
	@Override
	public String toString() {
		return "{" +
				"NAME = '" + NAME + '\'' +
				", number = " + number +
				", describe = '" + describe + '\'' +
				", id = " + id +
				'}';
	}
}

反射的例子

  • 其中,目标类的静态代码块代码块无参构造函数 都调用了。

  • 只有 全参构造函数 方法按需调用。

  • 注意:

    • ReflectionTest.class 方式中:静态代码块、代码块、无参构造函数 全都在 newInstance() 方法时调用

    • Class.forName(“”) 方式中:静态代码块 在获取 Class 时调用代码块、无参构造函数 都在 newInstance() 方法时调用

    • new ReflectionTest().getClass() 方式中:静态代码块、代码块、构造函数 全都在获取 Class 时调用而 newInstance() 方法再次调用了代码块、构造函数

public class JavaReflectionTest {
	
	public static void main1(String[] args) {
		// 反射调用 1
		System.out.println("=== 反射调用过程 1 ===");
		// 获取 Class 对象
		Class<ReflectionTest> reflectionTestClass = ReflectionTest.class;
		try {
			// 获取 Constructor 对象
			Constructor<ReflectionTest> classConstructor = reflectionTestClass.getConstructor();
			// 获取类对象
			ReflectionTest reflectionTest = classConstructor.newInstance();
			// 类对象直接调用 方法(没有问题)
			System.out.println(reflectionTest.toString());
			
			// 获取 Method 对象(setId() 方法)
			Method setId = reflectionTestClass.getMethod("setId", int.class);
			// 调用获得的方法(setId() 方法)
			setId.invoke(reflectionTest, 3);

			// 获取 Field 对象(sex 属性)
			Field sex = reflectionTestClass.getDeclaredField("sex");
			// 设置 sex 属性的值
			// 设置失败,还是用 方法吧
			sex.set("男", String.class);
			
			// 获取 Method 对象(toString() 方法)
			Method toString = reflectionTestClass.getMethod("toString");
			// 调用获得的方法(toString() 方法)
			System.out.println(toString.invoke(reflectionTest));
		} catch(NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
	public static void main2(String[] args) {
		// 反射调用 2
		System.out.println("=== 反射调用过程 2 ===");
		try {
			// 获取 Class 对象
			Class<?> reflectionTestClass2 = Class.forName("com.example.reflex.ReflectionTest");
			// 获取 Constructor 对象
			Constructor<?> classConstructor = reflectionTestClass2.getConstructor();
			// 获取类对象
			Object reflectionTest = classConstructor.newInstance();
			
			// 获取 Method 对象(setId() 方法)
			Method setId = reflectionTestClass2.getMethod("setId", int.class);
			// 调用获得的方法(setId() 方法)
			setId.invoke(reflectionTest, 4);
			
			// 获取 Method 对象(toString() 方法)
			Method toString = reflectionTestClass2.getMethod("toString");
			// 调用获得的方法(toString() 方法)
			System.out.println(toString.invoke(reflectionTest));
		} catch(ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
	public static void main3(String[] args) {
		// 反射调用 3
		System.out.println("=== 反射调用过程 3 ===");
		// 获取 Class 对象
		Class<? extends ReflectionTest> reflectionTestClass3 = new ReflectionTest().getClass();
		try {
			// 获取 Constructor 对象
			Constructor<? extends ReflectionTest> classConstructor = reflectionTestClass3.getConstructor();
			// 获取类对象
			ReflectionTest reflectionTest = classConstructor.newInstance();
			
			// 获取 Method 对象(setId() 方法)
			Method setId = reflectionTestClass3.getMethod("setId", int.class);
			// 调用获得的方法(setId() 方法)
			setId.invoke(reflectionTest, 5);
			
			// 获取 Method 对象(toString() 方法)
			Method toString = reflectionTestClass3.getMethod("toString");
			// 调用获得的方法(toString() 方法)
			System.out.println(toString.invoke(reflectionTest));
		} catch(NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}
	
	public static void main4(String[] args) {
		// 反射调用 4
		System.out.println("=== 反射调用过程 4 ===");
		// 获取 Class 对象
		Class<? extends ReflectionTest> reflectionTestClass4 = new ReflectionTest("反射类", 6).getClass();
		try {
			// 获取 Constructor 对象
			Constructor<? extends ReflectionTest> classConstructor = reflectionTestClass4.getConstructor();
			// 获取类对象
			ReflectionTest reflectionTest = classConstructor.newInstance();
			
			// 获取 Method 对象(setId() 方法)
			Method setId = reflectionTestClass4.getMethod("setId", int.class);
			// 调用获得的方法(setId() 方法)
			setId.invoke(reflectionTest, 7);
			
			// 获取 Method 对象(toString() 方法)
			Method toString = reflectionTestClass4.getMethod("toString");
			// 调用获得的方法(toString() 方法)
			System.out.println(toString.invoke(reflectionTest));
		} catch(NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
			e.printStackTrace();
		}
	}

}

class ReflectionTest {
	
	private static final String NAME = "ReflectionTest";
	
	private static int number = 20221204;
	
	static {
		System.out.println("ReflectionTest 静态代码块");
	}
	
	private String describe;

	public String sex;
	
	private int id;
	
	{
		System.out.println("ReflectionTest 代码块");
	}
	
	public ReflectionTest() {
		System.out.println("ReflectionTest 无参构造方法");
	}
	
	public ReflectionTest(String describe, int id) {
		System.out.println("ReflectionTest 全参构造方法");
		this.describe = describe;
		this.id = id;
	}
	
	public String getDescribe() {
		return describe;
	}
	
	public void setDescribe(String describe) {
		this.describe = describe;
	}
	
	public int getId() {
		return id;
	}
	
	public void setId(int id) {
		this.id = id;
	}
	
	public static String getNAME() {
		return NAME;
	}
	
	public static int getNumber() {
		return number;
	}
	
	public static void setNumber(int number) {
		ReflectionTest.number = number;
	}
	
	public String getSex() {
		return sex;
	}
	
	public void setSex(String sex) {
		this.sex = sex;
	}
	
	@Override
	public String toString() {
		return "{" +
				"NAME = '" + NAME + '\'' +
				", number = " + number +
				", describe = '" + describe + '\'' +
				", sex = '" + sex + '\'' +
				", id = " + id +
				'}';
	}
}
反射机制中获取 Class 的三种方式
  • 以 MyClass 类为例,现在要获取 MyClass 类的 Class 对象

  • 注意下面三种方式获取到的 Class 对象是同一个,内存地址是相同的

    • 都是 JVM 类加载的,加载过程中都是使用同一个 ClassLoader 类加载器来加载的。

    • 使用同一个 ClassLoader 类加载器的 Class 对象是属于同一个类的

    • 如果自定义类加载器破坏了 JVM 的双亲委派机制,便可以使得同一个类被不同类加载器加载。获取到不同的 Class 对象

public class JavaReflectionTest {
	
	public static void main(String[] args) throws ClassNotFoundException {
		System.out.println("=== ReflectionTest.class 反射调用过程 ===");
		// 获取 Class 对象
		Class<ReflectionTest> reflectionTestClass = ReflectionTest.class;
		
		System.out.println("=== Class.forName() 反射调用过程 ===");
		// 获取 Class 对象
		Class<?> reflectionTestClass1 = Class.forName("com.example.reflex.ReflectionTest");
		
		System.out.println("=== new ReflectionTest().getClass() 反射调用过程 ===");
		// 获取 Class 对象
		Class<? extends ReflectionTest> reflectionTestClass3 = new ReflectionTest().getClass();
	}

}
MyClass.class
  • 通过 MyClass.class 获取,JVM 会使用 ClassLoader 类加载器将类加载到内存中。

  • 但并不会做任何类的初始化工作

  • 返回 java.lang.Class 对象。

Class.forName(“MyClass”)
  • 通过 Class.forName("com.example.reflex.MyClass") 获取,同样,JVM 会使用 ClassLoader 类加载器将类加载到内存中。

  • 语法:Class.forName("类的全局限定名称")

  • 并且会进行类的静态初始化工作

  • 返回 java.lang.Class 对象。

new MyClass().getClass()
  • 通过 new MyClass().getClass() 获取,这种方式使用了 new 进行实例化操作。

  • 因为使用了 new 实例化,相应的也可以指定调用不同的构造方法。如:new MyClass().getClass();new MyClass("aaa").getClass();

  • 推荐将 new MyClass().getClass() 方式转换成 MyClass.class 方式

  • 因此,类的静态初始化和非静态初始化工作都会进行

  • getClass() 方法属于顶级 Object 类中的方法,任何子类对象都可以调用,哪个子类调用,就返回那个子类的 java.lang.Class 对象。

  • 返回 java.lang.Class 对象。

应用扩展

  • 优化简单工厂模式

    • 程序运行时通过 ClassLoader 类加载器动态获取到配置文件中定义的子类的全局定名。
  • 实现代理模式中的动态代理

  • Java JDBC数据库操作。

    • 利用反射加载 JDBC 驱动。
Class.forName("com.mysql.cj.jdbc.Driver"); //加载MySQL驱动
Class.forName("oracle.jdbc.driver.OracleDriver"); //加载Oracle驱动
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十⑧

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值