反射基础知识

类的加载步骤

1.加载

  • 通过全路径名获取二进制字节流文件
二进制流的来源  
	1.从压缩包(jar war ear)中获取
	2.从网络中获取(Apple技术)
	3.**从运行过程中动态生成(动态代理) -- Proxy-->$Proxy  (AVRO,Tlwift等格式可以在运行时将某个schema文件生成对应的若干class再加载)
	4.从其他文件生成(JSP文件生成class)
	5.从数据库中读取
  • 将字节流文件静态数据结构转化为方法区中运行的数据结构
  • 在内存中生成代表这个类的Class对象,作为方法区中该类数据结构的访问入口
  • 说明
1.任何类被使用时,系统都会创建一个Class对象
2.**加载通过程序能控制

2.连接

  • 将已读入到内存的类的二进制数据合并到虚拟机运行时的环境中去
  • 验证
确保类的正确性+保证字节流中包含的文件符号当前虚拟机的要求,并且不会危害虚拟机的安全

1.为啥要验证字节码? 
	Class文件的产生不强制要求必须是Java代码编译而来,甚至可以自己用十六进制编辑器直接编写
2.验证什么? 
	类文件结构+语义检查+字节码验证+二进制兼容性验证
  • 准备
为静态成员在方法区分配内存,并设置默认值
  • 解析
将类的二进制数据中的符号引用替换为直接引用
	对象名.方法名()替换成指针,该指针指向类的方法在方法区中内存的位置

3.初始化

  • 真正执行Java类中代码
  • 根据程序员写的代码去初始化变量和资源
  • 类的主动使用与被动使用
1.JVM启动后,对类的初始化是一个延迟机制,即JVM虚拟机实现必须在每个类或接口被Java程序首次主动使用时初始化他们(使用此类时才会才会加载)
2.六种主动使用的场景
	创建类的实例+访问或对某个类或接口的静态变量赋值+调用类的静态方法+反射+初始化类的子类(直接通过子类访问父类的静态变量会导致父类初始化,子类不会)+启动类sun.misc.Launcher
3.除以上六种情况都叫被动使用,不会导致类的加载和初始化(构造某个类的数组不会引起该类初始化+引用类的静态常量不会导致类的初始化)

4.使用

  • new

5.unloading从jvm中移除次实例

类加载器

1.作用

  • 将Class文件读入内存,并位置创建一个Class对象

2.种类

  • 根类(启动类)加载器 Bootstrap ClassLoader
1.负责Java核心类库的加载(jre/lib),只将能被虚拟机识别的类库加载到内存中
2.可通过-Xbootclasspath指定,也可通过系统属性sun.boot.class.path获取当前根类加载器加载到资源
3.C++编写
  • 扩展类加载器 Extension ClassLoader
1.负责jre扩展目录jre/lib/ext中jar包的加载
2.通过系统属性java.ext.dirs获取加载情况
3.Java编写,类名:sun.misc.Launcher$ExtClassLoader
  • 应用程序类加载器 Application ClassLoader
1.也称系统类加载器
2.负责用户路径classpath下指定类库的加载
3.可通过-classpath指定,也可通过系统属性java.class.path获取

3.双亲委派模型

  • 双亲委派模型:三种类加载器的加载顺序
Application在缓存中找该字节码,找到就直接返回,找不在就去Extention中找
Extention在缓存中找该字节码,找到就直接返回,找不在就去Bootstrap中找
Bootstrap在缓存中找该字节码,找到就直接返回

找不在就去JAVA_HOME/jre/lib路径中找
找不到就去jre/lib/ext找
找不到就去Application类路径下找
还找不到就抛出异常ClassNotFound异常

请添加图片描述

  • 统一整个系统的结构

反射

1.概述

  • Java语言的反射机制
在运行状态中
	对任意一个类,都可知道这个类所有的属性和方法
	对任意一个对象,都能调用它的属性和方法
===>动态获取信息
  • Class类实例表示正在运行的Java应用程序中类和接口

2.获取Class类实例的方法

  • Class类静态方法: Class.forName(“全路径”)
开发中常用Class类静态方法获取class实例
	因为全路径名是一个字符串,而不是一个具体的类,可以将该字符串配置到配置文件中
  • Object方法: 对象名.getClass()
  • 数据类型的静态属性: 类名.class
  • 注意
不管new几个对象,同一个类的Class类实例地址值是相同的

3.通过反射获取构造方法

  • 获取单个(public+private)构造方法(无参+有参)
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取单个公共无参构造方法
	//public Constructor<T> getConstructor(Class<?>... parameterTypes)
	//参数:要获取的构造方法的参数数据类型的class字节码文件对象
	Constructor con = c.getConstructor();// 返回的是构造方法对象

	//public T newInstance(Object... initargs)
	//使用此Constructor对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
	Object obj = con.newInstance();
	System.out.println(obj);//全路径对象
3.获取单个公共有参构造方法
	//public Constructor<T> getConstructor(Class<?>... parameterTypes)
	Constructor con = c.getConstructor(String.class, int.class,String.class);

	//public T newInstance(Object... initargs)
	Object obj = con.newInstance("name1", 20, "北京");
	System.out.println(obj);
4.获取单个私有构造方法
	//IllegalAccessException:非法的访问异常
	Constructor con = c.getDeclaredConstructor(String.class);

	//暴力访问
	con.setAccessible(true);//值为true则指示反射的对象在使用时应该取消Java语言访问检查
	Object obj = con.newInstance("name1");
	System.out.println(obj);
  • 获取所有public构造方法
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public构造方法
	//public Constructor[] getConstructors():所有公共构造方法
	Constructor[] cons = c.getConstructors();
	for (Constructor con : cons) {
		System.out.println(con);
	}
  • 获取所有(public+private)构造方法
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public+private构造方法
	//public Constructor[] getDeclaredConstructors():所有构造方法
	Constructor[] cons = c.getDeclaredConstructors();
	for (Constructor con : cons) {
		System.out.println(con);
	}

4.通过反射获取成员变量

  • 获取单个(public+private)成员变量并赋值
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.通过无参构造方法创建对象
	Constructor con = c.getConstructor();
	Object obj = con.newInstance();
2.获取单个公共成员变量并赋值
	Field field = c.getField("变量名");
	//public void set(Object obj,Object value)
	field.set(obj, "北京");//给obj对象的field字段设置值为"北京"
	System.out.println(obj);
3.获取单个私有成员变量并赋值
	Field field = c.getDeclaredField("变量名");
	field.setAccessible(true);
	field.set(obj, "北京");
	System.out.println(obj);
  • 获取所有public成员变量
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public成员变量
	Field[] fields = c.getFields();
	for (Field field : fields) {
		System.out.println(field);
	}
  • 获取所有(public+private)成员变量
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public+private成员变量
	Field[] fields = c.getDeclaredFields();
	for (Field field : fields) {
		System.out.println(field);
	}

5.通过反射获取成员方法

  • 获取单个(public+private)成员方法并使用
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.通过无参构造方法创建对象
	Constructor con = c.getConstructor();
	Object obj = con.newInstance();
3.获取单个公共无参成员方法并使用
	//public Method getMethod(String name,Class<?>... parameterTypes)
	//第一个参数表示的方法名,第二个参数表示的是方法参数的class类型
	Method m1 = c.getMethod("方法名");
	
	//public Object invoke(Object obj,Object... args)
	//返回值是Object接收,第一个参数表示激活方法的对象,第二参数表示给方法传入的实参
	m1.invoke(obj); //调用obj对象的m1方法,不传入参数
4.获取单个公共有参成员方法并使用
	//public Method getMethod(String name,Class<?>... parameterTypes)
	Method m2 = c.getMethod("method", String.class);
	m2.invoke(obj, "hello");
5.获取单个私有成员方法并使用
	Method m4 = c.getDeclaredMethod("方法名");
	//暴力访问
	m4.setAccessible(true);
	m4.invoke(obj);
  • 获取所有public成员方法,包括父类的
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public成员方法,包括父类的
	Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法
	for (Method method : methods) {
		System.out.println(method);
	}
  • 获取所有(public+private)成员方法
1.获取字节码文件对象
	Class c = Class.forName("全路径");
2.获取public+private成员方法
	Method[] methods = c.getDeclaredMethods();
	for (Method method : methods) {
		System.out.println(method);
	}

6.通过反射运行配置文件内容

  • class.txt(配置文件)
className=类的全路径名
methodName=方法名
  • 使用反射加载
1.加载数据
	Properties p = new Properties();
	FileRead fr = new FileRead("class.text");
	p.load(fr);
	fr.close();
2.获取数据
	String className = p.getProperty("className");
	String methodName = p.getProperty("methodName");
3.反射创建类实例
	Class c = Class.forName(className);
	Constructor con = c.getConstructor();
	Object obj = con.newInstance();
4.调用方法
	Method method = c.getMethod(methodName);
	method.invoke(obj);

7.通过反射越过泛型检查

  • 向ArrayList中添加一个字符串数据
ArrayList<Integer> array = new ArrayList<Integer>();

Class c = array.getClass();
Method method = c.getMethod("add",Object.class);

m.invoke(array,"java");

8.通过反射知道某对象的某属性为指定的值

  • 将Object value的值设置给obj对象的属性String name
Class c = obj.getClass();
Field field = c.getDeclaredField("name");
field.setAccessible(true);
field.set(obj,value);

动态代理

1.概述

  • 代理
本来自己要做的事情,却请了别人来做,被请的人就是代理对象
  • 动态代理
在程序运行过程中产生这个对象,而反射正是在程序运行过程中产生这个对象
动态代理其实就是利用反射生成代理

2.生成动态代理对象

  • 概述
ProxyInvocationHandler接口

注意:JDK提供的代理只能争对接口做代理
  • 实现
public class MyInvocationHandler implements InvocationHandler {
	private Object target; // 目标对象obj

	public MyInvocationHandler(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
		//proxy代理实例
		//method对应代理实例接口上的方法
		//args接口上方法的参数
		Object result = method.invoke(target, args);
		return result; // 返回的是代理对象
	}
}
===============================================
public class Test {
	public static void main(String[] args) {
		UserDao ud = new UserDaoImpl();
		
		// 准备对ud对象做一个代理对象
		MyInvocationHandler handler = new MyInvocationHandler(ud);
		
		// Proxy类中有一个方法可以创建动态代理对象
		// public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
		UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(), ud.getClass().getInterfaces(), handler);
		
		proxy.add();
		proxy.delete();
		proxy.update();
		proxy.find();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值