Java的反射与动态代理

1.类的加载的概述和加载时机

1.1 类的加载的概述
  • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类的初始化
  • 加载

就是将class文件读入内存,并为之创建一个class对象,任何类被使用时,系统都会建立一个class对象

  • 连接

    • 验证是否有正确的内部结构,并与其他类协调一致
    • 准备,负责为类的静态成员分配内存,并设置默认初始化值(类的静态成员随着类的加载而加载)
    • 解析,将类的二进制数据中的符号引用替换为直接引用。
  • 加载时机

    • 创建类的实例
    • 访问类的静态变量,或为静态变量赋值
    • 调用类的静态方法
    • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
    • 初始化某个类的子类
    • 直接使用java.exe命令来运行某个主类
1.2 类加载器的概述
  • 类加载器的概述

    负责将 .class文件加载到内存中,并生成与之对应的Class对象,虽然我们不需要关心类加载机制,但了解这个机制能够更好的理解程序是如何运行的。

  • 类加载器的分类

    • Bootstrap ClassLoader 根类加载器
    • Extension ClassLoader。扩展类加载器
    • System ClassLoader 系统类加载器
  • 类加载器的作用

    • Bootstrap ClassLoader 根类加载器

    也被称为应到类加载器,负责Java核心类的加载
    比如System,String等,在JDK中JRE的lib目录下 rt.jar文件中。

    • Extension ClassLoader。扩展类加载器

    负责JRE的拓展目录中jar包的加载
    在JDK中JRE的lib目录下 ext目录

    • System ClassLoader 系统类加载器

    负责JVM启动时加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径

2 反射

2.1 反射的概述
  • Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
  • Java反射机制是指在运行状态中,对于任意一个类,都能获取这个类的所有属性和方法
  • 在运行时对于任意一个对象,都能够调用它的方法和属性
  • 这种动态获取信息以及动态调用对象的方法和属性的功能称为Java语言的反射机制
  • 要想解刨一个类,必须要获取到该类的字节码文件对象
  • 而解刨使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
2.2 三种方式获取Class对象
  • 1.通过类名获取–>类名.class

    扫描二维码关注公众号,回复: 6518117 查看本文章
  • 通过对象获取–>对象名.getClass()

  • 通过全类名获取(会抛出异常)–>Class.forName(“全类名”)
    在这里插入图片描述

  • 案例:代码

public static void main(String[] args) throws ClassNotFoundException {
	Class clazz1 = Class.forName("reflect.Person"); //通过全类名获取字节码对象
	Class clazz2 = Person.class; //通过类名
	
	Person p = new Person();
	Class clazz3 = p.getClass(); //通过对象名
//上面这个例子的意义不大,因为已经知道person类型是Person类,再这样写就没有必要了
	
	System.out.println(clazz1 == clazz2);
	System.out.println(clazz2 == clazz3);
}
2.3通过反射获取带参数的构造方法
    1. Constructor构造器方法

1. Class类的newInstance()方法是使用该类无参的构造函数创建对象,如果一个类没有无参的构造函数,就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数,然后再调用Constructor类的newInstance("马云",18)方法创建对象

public static void main(String[] args) throws Exception {
	Class clazz = Class.forName("com.Person");//通过全类名获取字节码对象
	//Person p = (Person) clazz.newInstance();//获取无参构造

	//获取有参构造
	Constructor c = clazz.getConstructor(String.class,int.class);
	
	//利用Class对象的newInstance方法创建一个类的实例
	Person p = (Person) c.newInstance("马云",23);
	System.out.println(p);
}
2.4 通过反射获取成员变量
  • Field

1.Class.getField(String)方法可以获取类中的指定字段(可见的) 2.如果是私有的可以用getDeclaedField("name")方法获取,通过set(obj,"成龙")方法可以设置指定对象上的字段的值 3.如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定字段get(obj)可以获取指定对象中该字段的值。

public static void main(String[] args) throws Exception {
	Class clazz = Class.forName("reflect.Person"); //获取字节码对象
	Constructor c = clazz.getConstructor(String.class,int.class);
	
	Person p = (Person) c.newInstance("张三",23);//通过有参构造创建对象		
	//Field f = clazz.getField("name");    //获取姓名字段
	//f.set(p, "李四");	                   //修改姓名的值
	
	Field f = clazz.getDeclaredField("name");//获取私有时暴力反射获取字段
	f.setAccessible(true);	              //取效私有权限
	f.set(p, "李四");	
	System.out.println(p);
}
2.5 通过反射获取方(method)
  • Method

Class.getMethod(String,Class...)和Class.getDeclaredMethod(String,Class...)方法可以获取类中指定的方法,调用invoke(Object,Object)可以调用该方法,Class.getMethod("eat") invoke(obj) Class.getMethod("eat",int.class) invoke(obj,10)

public static void main(String[] args) throws Exception {
	Class clazz = Class.forName("reflect.Person"); //通过全类名获取
	//获取带参构造
	Constructor c = clazz.getConstructor(String.class,int.class);

	Person p = (Person) c.newInstance("马云的爸爸",23);//通过有参构造创建对象
	
	Method m = clazz.getMethod("eat");//获取方法
	m.invoke(p);
	
	Method m2 = clazz.getMethod("eat", int.class);
	m2.invoke(p, 10);
}
2.6 通过反射越过泛型检查
  • ArrayList<Interger>的一个对象,在这个集合中添加一个字符串数据,该如何实现呢?
  • 泛型只在编译器有效,在运行期会被擦出掉。
public static void main(String[] args) throws Exception {
	ArrayList<Integer> list = new ArrayList<>();
	list.add(111);
	list.add(222);
	
	//通过全类名获取字节码文件
	Class clazz = Class.forName("java.util.ArrayList");
	Method m = clazz.getMethod("add", Object.class);//获取add方法
	m.invoke(list, "abc");  //添加abc字符串****
	
	System.out.println(list);
	
}

2.6反射练习
  • 写一个properties格式的配置文件,配置类的完整名称
  • 写一个程序,读取这个配置文件,获取类的完整名称并加载这个类,通过反射的方法运行这个类的方法。
已知类:
pakeage reflect.sus;
public class demo1{
   public void run(){
   System.out.println("welcome to sus!");
   }
}

配置文件内容

reflect.sus.demo1

测试类

	public static void main(String[] args) throws Exception {
		//创建流读取配置文件xxx.properties
BufferedReader br = new BufferedReader(new FileReader("xxx.properties"));
	  //通过全类名路径获取字节码对象
		Class clazz = Class.forName(br.readLine());
		//通过字节码创建对象
		DemoClass dc = (DemoClass) clazz.newInstance();
		dc.run();
	}

3.动态代理的概述和实现

3.1动态代理的概述
  • 代理:本来应该自己做的事,请别人来做,而被请的人就是代理对象
  • 举例:春节回家让人帮忙代买票
  • 动态代理:指的是在程序运行过程中创建的代理的方式去执行某些东东称为动态代理。
  • 动态代理的简单实现

1.在java的java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过这个类和这个接口可以生成JDK动态代理类和动态代理对象。 2.JDK提供的代理只能针对接口做代理。

  • 创建一个Student接口
public interface Student {
	public void login();
	
	public void submit();
}
  • 创建一个StudentImpl类实现Student类
public class StudentImp implements Student {
	@Override
	public void login() {
		System.out.println("学生登录");
	}

	@Override
	public void submit() {
		System.out.println("学生提交");
	}
}
  • 编写实现代理类
public class MyInvocationHandler implements InvocationHandler {

private Object target;   //声明一个对象
    //创建一个带参的构造函数
public MyInvocationHandler(Object target) {
	this.target = target;
}
 @Override
public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
	System.out.println("权限校验");
	method.invoke(target, args);   //执行被代理的target对象的方法
	System.out.println("日志记录");
	return null;
}
}
  • 编写测试类
public class Test{
  public static void main(String[] args) {
  StudentImp si = new StudentImp();
		si.login();
		si.submit();
	System.out.println("-------------------------------");
	//new一个代理类
	MyInvocationHandler m = new MyInvocationHandler(si);
Student s = (Student)Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), m);
	
		s.login();
		s.submit();
	}

}

猜你喜欢

转载自blog.csdn.net/xiayubao7788/article/details/88806927