Java入门系列-27-反射

咱们可能都用过 Spring AOP ,底层的实现原理是怎样的呢?

反射常用于编写工具,企业级开发要用到的 Mybatis、Spring 等框架,底层的实现都用到了反射。能用好反射,就能提高我们编码的核心能力。

反射机制

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性。

作用:

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

常用的类:

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

Class 类

Class 类的实例表示正在运行的 Java 应用程序中的类和接口,Class 没有公共构造方法,Class 对象是在加载类时由 Java 虚拟机及通过调用类加载器中的 defineClass 方法自动构造的。

  • 一个类在 JVM 中只会有一个 Class 实例
  • 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过 Class 可以完整地得到一个类中的完整结构

获取 Class 对象

获取 Class 对象有4种方式,前三种比较常用。

首先创建一个类用于测试

package com.jikedaquan.reflection;

public class User {
	
	private int id;
	private String username;
	private String password;

	public User() {
	}
	
	public User(int id, String username, String password) {
		super();
		this.id = id;
		this.username = username;
		this.password = password;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	public void show() {
		System.out.println("Hello");
	}
	
	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
	}
}

编写测试

package com.jikedaquan.reflection;

public class GetClass {

	public static void main(String[] args) {
		//方法1
		try {
			Class clz1=Class.forName("com.jikedaquan.reflection.User");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("找不到指定类");
		}
		//方法2
		Class clz2=User.class;
		//方法3
		User user=new User();
		Class clz3=user.getClass();
		
		//方法4 类的加载器
		try {
			Class clz4=GetClass.class.getClassLoader().loadClass("com.jikedaquan.reflection.User");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.out.println("找不到指定类");
		}
	}
}

方法1语法:Class Class对象 = Class.forName(包名+类名);

方法2语法:Class Class对象 = 类名.class;

方法3语法:Class Class对象 = 对象.getClass();
getClass() 方法是从 Object 类中继承过来的

获取类的结构

Class 类常用方法

方法名称说明
Annotation[] getAnnotations()返回此元素上存在的所有注解
Constructor getConstructor(Class<?>… parameterTypes)获取指定参数的构造函数
Constructor<?>[] getConstructors()返回包含的公有构造方法
Constructor<?>[] getDeclaredConstructors()返回所有构造方法
Field getDeclaredField(String name)返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段
Method getDeclaredMethod(String name, Class<?>… parameterTypes)根据方法名和参数获取方法对象

API 中可以看到有两种获取结构的方式:getDeclaredXxx()和getXxx();getDeclaredXxx()可以获取所有包括私有的

获取类的结构

package com.jikedaquan.reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class GetClassStruct {

	public static void main(String[] args) {
		try {
			Class clz=Class.forName("com.jikedaquan.reflection.User");
			System.out.println("===========构造===========");
			//获取构造方法
			Constructor[] cons=clz.getDeclaredConstructors();
			for (Constructor constructor : cons) {
				System.out.println(constructor);
			}
			//获取字段
			System.out.println("===========字段===========");
			Field[] fields=clz.getDeclaredFields();
			for (Field field : fields) {
				System.out.println(field);
			}
			//获取方法
			System.out.println("===========方法===========");
			Method[] methods=clz.getDeclaredMethods();
			for (Method method : methods) {
				System.out.println(method);
			}
			//获取父类
			System.out.println("===========父类===========");
			Class supperClass=clz.getSuperclass();
			System.out.println(supperClass.getName());
			//获取实现的接口
			System.out.println("===========接口===========");
			Class[] interfaces=clz.getInterfaces();
			for (Class interf : interfaces) {
				System.out.println(interf);
			}
			//获取注解
			System.out.println("===========注解===========");
			Annotation[] annotations=clz.getAnnotations();
			for (Annotation annotation : annotations) {
				System.out.println(annotation);
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

调用类的指定方法、属性

获取构造方法并实例化对象

注意:jdk1.9弃用此方式实例化对象
Object obj=clz.newInstance();

通过反射获取有参或无参构造后方可实例化化对象

package com.jikedaquan.reflection;

import java.lang.reflect.Constructor;

public class CallConstructor {

	public static void main(String[] args) {
		//获取User 的 Class
		Class<User> clz=User.class;
		
		//获取无参构造方法并实例化
		try {
			//getConstructor()方法不传参即无参
			Constructor<User> constructor=clz.getConstructor();
			User user=constructor.newInstance();
			System.out.println(user);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//获取有参构造方法并实例化
		try {
			Constructor<User> constructor=clz.getConstructor(int.class,String.class,String.class);
			User user=constructor.newInstance(18,"张三","abc123");
			System.out.println(user);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

获取指定构造方法时,第二个参数为动态参数,不填写即获取无参构造方法,填写指定个数和指定类型.class可获取对应方式的构造方法。

调用类中的方法

package com.jikedaquan.reflection;

import java.lang.reflect.Method;

public class CallMethod {

	public static void main(String[] args) {
		//获取User 的 Class
		Class<User> clz=User.class;
		//获取无参方法  show
		try {
			Method method=clz.getMethod("show");
			//执行clz中的方法
			method.invoke(clz.getConstructor().newInstance());
		} catch (Exception e) {
			e.printStackTrace();
		}
		//获取一个参数为String的方法
		try {
			Method method=clz.getMethod("setUsername", String.class);
			//反射实例化对象
			User user=clz.getConstructor().newInstance();
			//执行这个对象的方法
			method.invoke(user, "反射");
			//测试结果
			System.out.println(user);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

如果有多个参数,获取方法:getMethod("方法名称",参数1.class,参数2.class,参数3.class)

多个参数执行时:method.invoke(对象,参数1,参数2,参数3);

动态代理

动态代理是指客户通过代理类来调用其他对象的方法,并且是在程序运行时根据需要创建目标类的代理对象。

原理:

使用一个代理将对象包装起来,然后用该代理对象取代原对象,任何对原始对象的调用都要通过dialing,代理对象决定是否以及何时将方法调用转到原始对象上。

生活中海外代购其实就用到了代理,你可能不方便出国,但是代购可以,最终帮你完成购买行为。

以代购为例子完成静态代理

package com.jikedaquan.reflection;

//购买接口(约定)
interface Buy{
	void buyProduct();
}
//被代理的
class Customer implements Buy{

	@Override
	public void buyProduct() {
		System.out.println("购买商品");
	}
}
//代理
class ProxyBuy implements Buy{
	private Customer customer;
	
	public ProxyBuy(Customer customer) {
		this.customer=customer;
	}
	
	@Override
	public void buyProduct() {
		System.out.println("代理:出国");
		//被代理的对象的行为
		customer.buyProduct();
		System.out.println("代理:回国");
	}
}

public class TestStaticProxy {

	public static void main(String[] args) {
		Customer customer=new Customer();
		ProxyBuy proxyBuy=new ProxyBuy(customer);
		proxyBuy.buyProduct();
	}
}

那么动态代理意味着不能只代理 Customer 类的行为,还可以代理其他类的行为

package com.jikedaquan.reflection;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//工厂接口
interface Factory{
	void product();
}
//电脑工厂
class ComputerFactory implements Factory{

	@Override
	public void product() {
		System.out.println("生产电脑");
	}
}
//动态代理处理器
class MyInvocationHandler implements InvocationHandler{
	//要被代理的对象
	private Object proxyObj;
	//产生代理对象
	public Object bind(Object proxyObj) {
		this.proxyObj=proxyObj;
		return Proxy.newProxyInstance(
				proxyObj.getClass().getClassLoader(),
				proxyObj.getClass().getInterfaces(), 
				this
				);
	}
	//代理对象实际执行的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("代理:收收费");
		Object result=method.invoke(proxyObj, args);
		System.out.println("代理:代理完成");
		return result;
	}

}

public class TestDynamicProxy {

	public static void main(String[] args) {
		//创建代理对象生产器
		MyInvocationHandler invocationHandler=new MyInvocationHandler();

		//创建要被代理的对象
		ComputerFactory computerFactory=new ComputerFactory();
		//生产代理对象
		Object factoryProxy=invocationHandler.bind(computerFactory);
		Factory factory=(Factory) factoryProxy;
		factory.product();

		//创建另一个要被代理的对象(上个示例静态代理的对象和接口)
		Customer customer=new Customer();
		//生产代理对象
		Object buyProxy=invocationHandler.bind(customer);
		Buy buy=(Buy) buyProxy;
		buy.buyProduct();
	}
}

在 main 方法中,创建了一个 MyInvocationHandler 对象,通过 bind 方法可以传入任意要被代理的对象,实现了动态。

重点来了,拿好小本子笔记!!!!!

实现动态代理的步骤

1.创建要被代理的类的接口

2.创建要被代理的类实现类

3.创建代理对象处理器(MyInvocationHandler),实现 InvocationHandler 接口

4.编写生产代理对象的方法,方法内调用 Proxy.newInstance() 方法,返回代理对象

5.重写 InvocationHandler 的 invoke 方法

6.测试:创建代理对象生产器,生产代理对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【完整课程列表】 资源汇总下载地址:https://download.csdn.net/download/qq_27595745/55418618 Java SE编程入门教程 collection与collections(共7页).pptx Java SE编程入门教程 java GC(共6页).pptx Java SE编程入门教程 java instanceof(共3页).pptx Java SE编程入门教程 java IO(共28页).pptx Java SE编程入门教程 java Math(共11页).pptx Java SE编程入门教程 java object(共9页).pptx Java SE编程入门教程 java static final(共24页).pptx Java SE编程入门教程 java this和super(共10页).pptx Java SE编程入门教程 java 常用API(共22页).pptx Java SE编程入门教程 javaGUI编程快速入门(1)(共82页).pptx Java SE编程入门教程 java包装器(共9页).pptx Java SE编程入门教程 java反射机制(共16页).pptx Java SE编程入门教程 java泛型(共11页).pptx Java SE编程入门教程 java封装继承多态(共56页).pptx Java SE编程入门教程 java集合(共38页).pptx Java SE编程入门教程 java接口(共21页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共56页).pptx Java SE编程入门教程 java类和对象方法以及包的使用(共62页).pptx Java SE编程入门教程 java判断循环(共79页).ppt Java SE编程入门教程 java入门以及变量debug(共63页).pptx Java SE编程入门教程 java设计模式(共17页).pptx Java SE编程入门教程 java数组(共33页).pptx Java SE编程入门教程 java网络编程(共29页).pptx Java SE编程入门教程 java线程(共61页).pptx Java SE编程入门教程 java序列化(共14页).pptx Java SE编程入门教程 java异常(共57页).pptx Java SE编程入门教程 java正则(共8页).pptx Java SE编程入门教程 properties(共3页).pptx Java SE编程入门教程 String字符串(共27页).pptx Java 位运算.docx java正则表达式例子大全 共10页.doc JAVA正则表达式实例教程 共17页.docx JAVA正则表达式实例教程 共17页.pdf 递归算法.docx 堆和栈.wps

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值