java基础-反射理解(整理)

本文介绍Java在运行时识别对象和类信息的两种方式:RTTI和反射机制。阐述了Class对象的作用、创建方式及初始化步骤,还说明了运行时获取类相关信息的方法,如getName、getFields等。最后提及反射在动态代理、Spring AOP等方面的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原理

Java在运行时能识别对象和类的信息,主要有2种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。即对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。

Class对象

Class对象就是用来创建所有“常规”对象的,Java使用Class对象来执行RTTI

每个类都会产生一个对应的Class对象,也就是保存在.class文件。所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类。Class对象仅在需要的时候才会加载,static初始化是在类加载时进行的。(静态属性优先于对象而存在就是这个道理

  • Class对象创建方式的几种方式
  • 注意:使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象
    初始化步骤如下
    加载:由类加载器完成,找到对应的字节码,创建一个Class对象
    链接:验证类中的字节码,为静态域分配空间
    初始化:如果该类有超类,则对其初始化,执行静态初始化器和静态初始化块
  1. 通过对象调用 getClass() 方法来获取
	// 用于不知道传入的对象是什么
	ParentClass p1 = new ParentClass();
	Class c1 = p1.getClass();
  1. 直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
	// 程序性能更高
	Class c2 = ParentClass.class;
  1. 通过 Class 对象的 forName() 静态方法来获取
	Class c3 = Class.forName("snipe.proxy.ParentClass");
  1. 通过已有对象clone出来

在这里插入图片描述
无论如何创建:一个类在 JVM 中只会有一个 Class 实例

运行时获取类相关信息

  1. getName():获得类的完整名字。
  2. getFields():获得类的public类型的属性。
  3. getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  4. getMethods():获得类的public类型的方法。
  5. getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  6. getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  7. getConstructors():获得类的public类型的构造方法。
  8. getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  9. newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
  • 反射获取父类属性
		public void testGetParentField() throws Exception{
	        Class c1 = Class.forName("snipe.proxy.SonClass");
	        //获取父类私有属性值
	        System.out.println(getFieldValue(c1.newInstance(),"privateField"));
	    }
	    
	    /**
	     * 
	     * @param obj 对象
	     * @param fieldName 属性名称
	     * @return
	     */
	    public static Field getDeclaredField(Object obj,String fieldName) {
	        Field field = null;
	        Class c = obj.getClass();
	        // 循环获取,获取父类Class  
	        // 如果子类有属性的则取到子类的属性 通过getSuperclass() 方法获取子类的父类
	        for(; c != Object.class ; c = c.getSuperclass()){
	            try {
	                field = c.getDeclaredField(fieldName);
	                field.setAccessible(true);
	                return field;
	            }catch (Exception e){
	                //这里甚么都不要做!并且这里的异常必须这样写,不能抛出去。
	                //如果这里的异常打印或者往外抛,则就不会执行c = c.getSuperclass(),最后就不会进入到父类中了
	            }
	        }
	        return null;
	    }
	    
	    /**
	     * 通过对象和属性名称获取属性值
	     * @param object 对象 
	     * @param fieldName 属性名称
	     * @return
	     * @throws Exception
	     */
	    public static Object getFieldValue(Object object,String fieldName) throws Exception{
	        Field field = getDeclaredField(object,fieldName);
	        return field.get(object);
	    }

RTTI和反射之间的真正区别只在于

  • RTTI,编译器在编译时打开和检查.class文件
  • 反射,运行时打开和检查.class文件

应用

动态代理:反射多用于代理中解耦业务代码。以及spring aop中面向切面代理执行 例如事物、缓存等。
简单例子

/**
 * 目标类接口
 * @author Administrator
 *
 */
public interface TargetInterface {
	void sayHi(String str);
}
/**
 * 目标类
 * @author Administrator
 *
 */
public class TargetClass implements TargetInterface{

	@Override
	public void sayHi(String str) {
		System.out.println("hello world !");
	}

}
/***
 * 	日志接口
 * @author Administrator
 *
 */
public interface TestLoggerInterface {
	
	public void start(Method method,String str) ; 
	public void end(Method method) ; 
	public void test() ; 
}

/**
 * JDK代理类
 * @author Administrator
 *
 */
public class ProxyClassDyn implements InvocationHandler{
	private Object proxy; // 调用对象--> 切面类
	private Object target; // 目标对象 --> 被被代理类
	
	/**
	 *  用户创建代理类
	 * @param target 目标对象
	 * @param proxy 调用对象
	 * @return
	 */
	public Object bind(Object target,Object proxy) {
		this.proxy = proxy;
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);	 
	}
	
	/**
	 *  代理具体实现
	 * @param proxy 代理对象,即调用对象
	 * @param method 目标对象的方法
	 * @param args 方法参数
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 通过代理对象调用对象方法执行代理方法
		Method startMethod = this.proxy.getClass().getDeclaredMethod("start", new Class[] {Method.class,String.class});
		startMethod.invoke(this.proxy, new Object[] {startMethod,"method"}); // 执行开始的方法
		// 执行目标对象
		Object result = method.invoke(target, args);
		// 通过代理对象调用对象方法执行代理方法
		Method endMethod = this.proxy.getClass().getDeclaredMethod("end",new Class[] {Method.class});
		endMethod.invoke(this.proxy,endMethod); //执行结束的方法
		// 通过代理对象调用对象方法执行代理方法
		Method testMethod = this.proxy.getClass().getDeclaredMethod("test"); 
		testMethod.invoke(this.proxy);  // 不带参数的方法
		return result;
	}
	
	public static void main(String[] args) {
		// 创建代理类
		TargetInterface targetInterface = (TargetInterface)new ProxyClassDyn().bind(new TargetClass(), new TestLoggerImplements());
		targetInterface.sayHi("world");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值