Java基础——反射

本文深入探讨Java反射机制,介绍Class对象的获取方法及其实例的用途,详细解析通过反射调用属性、方法和构造器的过程。同时,文章阐述了动态代理的原理及其在程序运行时动态创建目标类代理对象的应用,辅以具体示例说明。

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

一、实例化Class的方法
 1、调用运行时类的.class属性

Class clazz1 = Person.class;
System.out.println(clazz1);

 2、调用对象的getClass()方法

Person p = new Person();
Class clazz3 = p.getClass();
System.out.println(clazz3);

 3、调用Class的forName(String className)静态方法:此方法可能会报ClassNotFoundException

String className = "com.bdm.java.Person";
Class clazz4 = Class.forName(className);
System.out.println(clazz4);

 4、通过类的加载器:

//获取加载器
ClassLoader classLoader = this.getClass().getClassLoader();
//此处的className和方式3的形式一样
Class clazz = classLoader.loadClass(className);

Tip:运行时类的Class对象只有一个,即使创建了多个Class对象,这些对象也都指向同一个Class实体
二、Class实例的用途
 1、创建对应的运行时类的对象:调用指定的构造器不是invoke(),而是newInstance()创建对象
  ①调用Class实例的newInstance(),相当于调用无参构造器

Class clazz = Class.forName("com.bdm.review.Animal");
Object obj = clazz.newInstance();
Animal a = (Animal)obj;
System.out.println(a);

  ②获取指定的构造器,再调用构造器的newInstance()

Class clazz = Animal.class;
Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);//获取指定构造器
cons.setAccessible(true);
Animal a = (Animal)cons.newInstance("Tom",10);//实例化对象
System.out.println(a);

 2、获取对应的运行时类的完整类结构:属性、方法、构造器、包、父类、接口、泛型、注解、异常、内部类…

//获取权限为public的方法(包含父类中声明的public方法)
Method[] m1 = clazz.getMethods();

//获取该类中声明的所有的方法(任何权限修饰符修饰的方法都能获取但不含父类中的)
Method[] m2 = clazz.getDeclaredMethods();

clazz.getInterfaces();//获取类实现的接口
clazz.getSuperClass();//获取类继承的父类
clazz.getPackage();//获取包
clazz.getAnnotations();//注解

  获取父类:

Class clazz = Student.class;
Class superClass = clazz.getSuperclass();

//获取实现的接口:只能获取直接实现的接口,接口的父接口获取不到
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> inf : interfaces) {
	System.out.println(inf);
}

  获取父类的泛型:

Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();//获取带泛型的父类
ParameterizedType paraType = (ParameterizedType)genericSuperclass;
Type[] types = paraType.getActualTypeArguments();//获取泛型参数
System.out.println(((Class)types[0]).getName());

 3、调用对应的运行时类中指定的结构:某个指定的属性、方法、构造器
  ①调用属性:getDeclaredField可获取所有的属性(public、private、static)

Class clazz = Class.forName("com.bdm.review.Animal");
Object obj = clazz.newInstance();
Animal a = (Animal)obj;

//调用private的属性
Field f1 = clazz.getDeclaredField("name");
f1.setAccessible(true);//对于声明为private的属性,需设置访问权限为可访问
f1.set(a, "Jerry");

//调用public的属性
Field f2 = clazz.getField("age");
//获取属性的权限修饰符:1代表public  2代表private  0代表default
int i = f2.getModifiers();
//借助Modifier的toString()方法转化
Modifier.toString(i);//转化为修饰符
f2.getType();//获取属性的类型
f2.set(a, 9);
System.out.println(f2.get(a));

//调用static的属性
Field f3 = clazz.getDeclaredField("desc");	
//静态属性是没有setter的,是在类实例初始化之前就有值,因此可直接获取属性值,注意传参null
System.out.println(f3.get(null));

  ②获取方法的结构:

Method[] methods = clazz.getMethods();//获取该类机器父类声明为public的方法
Method[] methods = clazz.getDeclaredMethods();//获取该类本身的所有方法

// 获取类中所有方法的完整结构
public static void getCompleteStructure(Class<?> clazz) {
	Method[] m2 = clazz.getDeclaredMethods();
	for (Method m : m2) {
		// 1、注解
		Annotation[] ann = m.getAnnotations();
		for (Annotation a : ann)
			System.out.println(a);

		// 2、权限修饰符
		String str = Modifier.toString(m.getModifiers());
		System.out.print(str + " ");

		// 3、返回值类型
		Class<?> returnType = m.getReturnType();
		System.out.print(returnType.getName() + " ");

		// 4、方法名
		System.out.print(m.getName() + " ");

		// 5、形参列表
		System.out.print("(");
		Class<?>[] params = m.getParameterTypes();
		for (int i = 0; i < params.length; i++)
			System.out.print(params[i].getName() + " args-" + i + " ");
		System.out.print(")");

		// 6、异常类型
		Class<?>[] exps = m.getExceptionTypes();
		if (exps.length != 0)
			System.out.print("throws ");
		for (int i = 0; i < exps.length; i++) {
			System.out.print(exps[i].getName() + " ");
		}
		System.out.println();
	}
}

  ③调用方法:

Class clazz = Class.forName("com.bdm.review.Animal");
Object obj = clazz.newInstance();
Animal a = (Animal)obj;
	
//调用非public的方法
Method m1 = clazz.getDeclaredMethod("getAge");
//私有方法需设置访问权限为true
m1.setAccessible(true);
int age = (Integer)m1.invoke(a);
System.out.println(age);

//调用public的方法:可通过传参确定想获取的是重载的哪一个方法
Method m2 = clazz.getMethod("show", String.class);
Object returnVal = m2.invoke(a,"金毛");
System.out.println(returnVal);

//调用static的方法
Method m3 = clazz.getDeclaredMethod("info");
m3.setAccessible(true);
//m3.invoke(Animal.class);
m3.invoke(null);

注意
  1️⃣Class的对象获取不到运行时类的代码块,因为代码块没有名字;
  2️⃣通过反射不能从子类的运行时类直接获取父类的私有属性,会报NoSuchFieldException异常;
  3️⃣若原方法中有参数是基本数据类型的,在利用反射的时候直接用基本数据类型.class即可,不用装箱成包装类.class,另外,若原方法有返回值,利用反射调用的时候也会返回值,只不过是Object类型的,可以强转为原类型后,按原来的方式接收和使用

Method run = clazz.getMethod("run",String.class,int.class);
run.invoke(p,"小二黑",24);

  4️⃣利用反射invoke静态方法的时候不用传对象,而是传类.class

Method m = clazz.getMethod("info");//info是静态方法
m.invoke(Person.class);

三、动态代理
 动态代理是指通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
 原理:使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
 所有对象的方法调用都可以通过调用MyInvocationHandler.invoke()来完成:

class MyInvocationHandler implements InvocationHandler{

	Object obj;//被代理的对象

	public Object blind(Object obj) {
		this.obj = obj;//初始化被代理对象
		//返回代理对象
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//...,这里可执行自定义逻辑,比如日志记录、事务开启等
		Object returnValue = method.invoke(obj, args);
		//...,这里也可以自定义逻辑,比如日志更改、事务关闭等
		return returnValue;
	}
}

 动态代理泛型方式实现:

// 泛型的方式实现动态代理:
class MyHandler<T> implements InvocationHandler {
	T t;

	public Object getProxy(T t) {
		this.t = t;
		return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("**********************");
		Object val = method.invoke(t, args);
		System.out.println("**********************");
		return val;
	}
}

 示例1:
  1️⃣通过代理对象调用方法

public class ProxyTest {
	public static void main(String[] args) {
		// 被代理类对象
		NikeShoes ns = new NikeShoes();
		// 代理
		MyProxy mp = new MyProxy();
		// 获取代理对象:该代理对象和被代理对象实现了相同的ShoesFactory接口
		Object blind = mp.blind(ns);
		// 强转为接口类型
		ShoesFactory sf = (ShoesFactory) blind;
		// 该方法实际上执行的是InvocationHandler的实现类的invoke()方法
		sf.makeShoes();
	}
}

interface ShoesFactory {
	void makeShoes();
}

class NikeShoes implements ShoesFactory {
	@Override
	public void makeShoes() {
		System.out.println("Just do it,make the right");
	}
}

class MyProxy implements InvocationHandler {
	Object obj;// 被代理的对象
	
	// 该方法的作用:①实例化被代理类的对象obj ②返回一个代理类对象
	public Object blind(Object obj) {
		// 实例化被代理类对象
		this.obj = obj;
		// 返回一个代理类对象
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
		// Proxy.newProxyInstance(loader:obj对象的类加载器,interfaces:obj对象所在的类实现的接口,h:实现了InvocationHandler接口的类的对象);
		// 要保证loader和被代理类的loader一样,interfaces和被代理类一样
	}

	@Override
	// 必须实现的InvocationHandler中的方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// 奇怪Object proxy参数并没有用上啊?
		// 该返回值实际上是被重写的接口中的方法的返回值
		System.out.println(">>>>>>>>");
		Object returnVal = method.invoke(obj, args);
		System.out.println(">>>>>>>>");
		return returnVal;
	}
}

  2️⃣直接调用invoke():这种方式不如1️⃣优雅,但更直观

public class ProxyTest {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, Throwable {
		// 被代理类对象
		NikeShoes ns = new NikeShoes();
		// 代理
		MyProxy mp = new MyProxy();
		//指明代理哪个对象的哪个方法
		mp.invoke(ns, ns.getClass().getDeclaredMethod("makeShoes"), null);
	}
}

interface ShoesFactory {
	void makeShoes();
}

class NikeShoes implements ShoesFactory {
	@Override
	public void makeShoes() {
		System.out.println("Just do it,make the right");
	}
}

class MyProxy implements InvocationHandler {

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println(">>>>>>>>");
		Object returnVal = method.invoke(proxy, args);
		System.out.println(">>>>>>>>");
		return returnVal;
	}
}

 示例2:

public class ProxyTest {
	public static void main(String[] args) {
		Fujian f = new Fujian();
		Object handler = Handler.getHandler(f);
		Nike n = (Nike) handler;
		n.makeClothes();
		System.out.println();
		n.makeShoes();
	}
}

interface Nike {
	void makeShoes();
	void makeClothes();
}

class Fujian implements Nike {
	@Override
	public void makeShoes() {
		System.out.println("鞋子是福建产的");
	}

	@Override
	public void makeClothes() {
		System.out.println("衣服是福建产的");
	}
}

class NikeFactory {
	public static void Maker() {
		System.out.println("外包工程");
	}

	public static void Made() {
		System.out.println("皮包公司");
	}
}

class MyHandler implements InvocationHandler {
	Object obj;

	public void setObject(Object obj) {
		this.obj = obj;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		NikeFactory.Maker();
		Object returnVal = method.invoke(obj, args);
		NikeFactory.Made();
		return returnVal;
	}
}

class Handler {
	public static Object getHandler(Object obj) {
		MyHandler mh = new MyHandler();
		mh.setObject(obj);
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), mh);
	}
}

 示例3:

public class ProxyTest {
	public static void main(String[] args) {
		MyHandler mh = new MyHandler();
		Lenovo lenovo = new Lenovo();
		Nike nike = new Nike();
		MakeProduction obj = (MakeProduction) mh.setObject(lenovo);
		obj.makeProduction();
		obj.provideService();
		Maker maker = (Maker) mh.setObject(nike);
		maker.makeShoes();
		maker.makeClothes();
	}
}

interface MakeProduction {
	void makeProduction();

	void provideService();
}

class Lenovo implements MakeProduction {
	@Override
	public void makeProduction() {
		System.out.println("联想产品,走向国际");
	}

	@Override
	public void provideService() {
		System.out.println("联想电脑,全国联保");
	}
}

interface Maker {
	void makeShoes();

	void makeClothes();
}

class Nike implements Maker {
	@Override
	public void makeShoes() {
		System.out.println("耐克,just do it");
	}

	@Override
	public void makeClothes() {
		System.out.println("耐克运动服装,sports right");
	}
}

class Prompt {
	public void pre() {
		System.out.println("*************************");
	}

	public void last() {
		System.out.println("#########################");
	}
}

class MyHandler implements InvocationHandler {
	Object obj;

	public Object setObject(Object obj) {
		this.obj = obj;
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Prompt p = new Prompt();
		p.pre();
		Object val = method.invoke(obj, args);
		p.last();
		return val;
	}
}

四、类加载器
 类加载器是用来把类装载进内存的。JVM规范定义了两种类型的类加载器:启动类加载器(bootstrap)和用户自定义类加载器(user-defined loader)。JVM在运行时会产生3个类加载器组成的初始化加载器层次结构,如下图所示:详情参看类加载器详解
在这里插入图片描述
 引导类加载器:用C++编写,是JVM自带的类加载器,用来加载Java核心类库,该加载器无法直接获取,引导类加载器是扩展类加载器的父加载器(并非继承关系,而是组合关系)
 扩展类加载器:负责jre/lib/ext目录下的jar包或-D java.ext.dirs指定目录下的jar包的加载,扩展类加载器是系统类加载器的父加载器(并非继承关系,而是组合关系)
 系统类加载器:负责java-classpath或-D java.class.path所指的目录下的类与jar包的加载,是开发中最常用的加载器

ClassLoader cl1 = ClassLoader.getSystemClassLoader();//系统类(应用类)加载器
System.out.println(cl1);// sun.misc.Launcher$AppClassLoader@2a139a55

ClassLoader cl2 = cl1.getParent();//扩展类加载器
System.out.println(cl2);// sun.misc.Launcher$ExtClassLoader@7852e922

ClassLoader cl3 = cl2.getParent();//引导类加载器(获取不到)
System.out.println(cl3);// null

 类的加载器可以通过Class类的对象通过getClassLoader()方法获取,返回值类型是ClassLoader,普通类的加载器都是系统类加载器,核心类(比如Object类等)的加载器是引导类加载器,不能直接获取:

ClassLoader classLoader2 = Student.class.getClassLoader();
System.out.println(classLoader2);//sun.misc.Launcher$AppClassLoader@2a139a55

ClassLoader classLoader = Object.class.getClassLoader();
System.out.println(classLoader);//null

 通过类的加载器可以从指定文件目录下将一个文件转化为一个输入流对象:注意加载的时候不是包名,而是目录

ClassLoader cl = this.getClass().getClassLoader();
InputStream is = cl.getResourceAsStream("com\\bdm\\java\\jdbc.properties");
Properties prop = new Properties();
prop.load(is);
String name = prop.getProperty("user");
String pwd = prop.getProperty("password");
System.out.println(name + "-" + pwd);

Tip:利用反射可以调用类中的任何结构包括封装的属性和方法甚至私有化的构造器,也就是说利用反射可打破单例模式,各种封装都形同虚设,可以借助反射创建任何类的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值