反射

重点掌握

  • 理解 Class 类
  • 理解 Java 的类加载机制
  • 学会使用 ClassLoader 进行类加载
  • 理解反射的机制
  • 掌握 Constructor、Method、Field 类的用法

类的加载

  • 类的加载
    • 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
    • 加载
      • 就是指将class文件读入内存,并为之创建一个Class对象。
      • 任何类被使用时系统都会建立一个Class对象。
    • 连接
      • 验证 是否有正确的内部结构,并和其他类协调一致
      • 准备 负责为类的静态成员分配内存,并设置默认初始化值
      • 解析 将类的二进制数据中的符号引用替换为直接引用
    • 初始化

类的初始化时机

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

类加载器

  • 类加载器
    负责将.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包和类路径

ClassLoader(类装载器):

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
在这里插入图片描述

演示类加载机制的层次关系

public class ClassLoaderDemo {
	public static void main(String[] args) {
		ClassLoader classloader;
		//获取系统缺省的ClassLoader
		classloader = ClassLoader.getSystemClassLoader();
		System.out.println(classloader);
		while (classloader != null) {
			//取得父的ClassLoader
			classloader = classloader.getParent();
			System.out.println(classloader);
		}
		try {
			Class cl = Class.forName("java.lang.Object");
			classloader = cl.getClassLoader();
			System.out.println("java.lang.Object's loader is  " + classloader);
			cl = Class.forName(“tcpc1.Student");
			classloader = cl.getClassLoader();
			System.out.println("ClassLoaderDemo's loader is  " + classloader);
		} catch (Exception e) {
			System.out.println("Check name of the class");
		}
	}

执行结果如下:
//表示系统类装载器实例化自类sun.misc.Launcher A p p C l a s s L o a d e r s u n . m i s c . L a u n c h e r AppClassLoader sun.misc.Launcher AppClassLoadersun.misc.LauncherAppClassLoader@19821f
//表示系统类装载器的parent实例化自类sun.misc.Launcher E x t C l a s s L o a d e r s u n . m i s c . L a u n c h e r ExtClassLoader sun.misc.Launcher ExtClassLoadersun.misc.LauncherExtClassLoader@addbf1
//表示系统类装载器parent的parent为bootstrap,无法直接获取
null
//表示类Object是由bootstrap装载的
java.lang.Object’s loader is null
//表示用户类是由系统类装载器装载的
ClassLoaderDemo’s loader is sun.misc.Launcher$AppClassLoader@19821f

反射

反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.

Java反射的应用

  • Spring框架:IOC(控制反转)
  • Hibernate框架:关联映射等
  • 白盒测试

Java反射相关的API

  • java.lang包下
    Class:表示一个正在运行的 Java 应用程序中的类和接口,是Reflection的起源
  • java.lang.reflect包下
    Field 类:代表类的成员变量(也称类的属性)
    Method类:代表类的方法
    Constructor 类:代表类的构造方法
    Array类:提供了动态创建数组,以及访问数组的元素的静态方法

反射——获取字节码对象

三种获取字节码文件对象的方式

方式1:
Person p = new Person();
Class c = p.getClass();
通过对象获取字节码文件对象

方式2:
Class c2 = Person.class;
任意数据类型都具备一个class静态属性,看上去要比第一种方式简单.

方式3:
Class c3 = Class.forName(“Person”);
将类名作为字符串传递给Class类中的静态方法forName即可。

反射——获取字节码对象成员(field)

  • 获取成员
    • 获取所有成员
      getFields,getDeclaredFields
    • 获取单个成员
      getField,getDeclaredField
    • 修改成员的值
      set(Object obj,Object value) 将指定对象变量上的此 Field 对象表示的字段设置为指定的新值

Field对象:

通过Class对象的getFields()或者getField()方法可以获得该类所包括的全部Field属性或指定Field属性。Field类提供了以下方法来访问属性

getXxx(Object obj):获取obj对象该Field的属性值。此处的Xxx对应8个基本数据类型,如果该属性类型是引用类型则直接使用get(Object obj)

setXxx(Object obj,Xxx val):将obj对象的该Field赋值val。此处的Xxx对应8个基本数据类型,如果该属性类型是引用类型则直接使用set(Object obj, Object val)

setAccessible(Boolean flag):若flag为true,则取消属性的访问权限控制,即使private属性也可以进行访问

public class FieldTest {
	public static void main(String[] args) throws Exception {
		Class clazz = Class.forName("cn.jbit.reflection.Student");
		Object obj = clazz.newInstance();

		// 调用getDeclaredField("name") 取得name属性对应的Field对象
		Field f = clazz.getDeclaredField("name");

		// 取消属性的访问权限控制,即使private属性也可以进行访问。
		f.setAccessible(true);

		// 调用get()方法取得对应属性值。
		System.out.println(f.get(obj));  //相当于obj.getName();

		// 调用set()方法给对应属性赋值。
		f.set(obj, "lkl");  //相当于obj.setName("lkl"); 

		// 调用get()方法取得对应属性修改后的值。
		System.out.println(f.get(obj));
	}
}

反射——获取字节码对象成员方法

  • 获取普通方法
    • 获取所有方法
      getMethods
      getDeclaredMethods
    • 获取单个方法
      getMethod
      getDeclaredMethod
    • 暴力访问
      method.setAccessible(true);

Method对象

通过Class对象的getMethods() 方法可以获得该类所包括的全部方法, 返回值是Method[]
通过Class对象的getMethod()方法可以获得该类所包括的指定方法, 返回值是Method
每个Method对象对应一个方法,获得Method对象后,可以调用其invoke() 来调用对应方法

Object invoke(Object obj,Object [] args):obj代表当前方法所属的对象的名字,args代表当前方法的参数列表,返回值Object是当前方法的返回值,即执行当前方法的结果。

public class TestMethod {
	public int add(int x, int y) {	return x + y;	}
	public void shout(String name) {System.out.println("my name is"+name);}

	public static void main(String[] args) throws Exception {
		// 创建该类的一个对象
		Class clazz = TestMethod.class;
		Object obj = clazz.newInstance();
		// 调用该对象的add方法
		Method amethod=clazz.getMethod("add",new Class[]{int.class, int.class});
		Object result = amethod.invoke(obj, new Object[] { 5, 7 });
		System.out.println(result);
		// 调用该对象的shout方法
		Method smethod=clazz.getMethod("shout",new Class[]{String.class});
		smethod.invoke(obj, new Object[] { "lkl" });		
	}
}

反射——动态代理

  • 动态代理
    • 代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
      举例:春季回家买票让人代买
    • 动态代理:在程序运行过程中产生的这个对象
      而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
    • 在代理过程中,可以在本类基础上添加新的功能,使其功能更强大
    • 在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib。
    • Proxy类中的方法创建动态代理类对象
      public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
      最终会调用InvocationHandler的方法
    • InvocationHandler
      Object invoke(Object proxy,Method method,Object[] args

反射总结

反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的Java反射技术应用领域很广,如软件测试、JavaBean等
许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术

反射的缺点

  • 性能问题
    使用反射基本上是一种解释操作,用于字段和方法接入时要远慢 于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很 高的系统框架上,普通程序不建议使用。
  • 使用反射会模糊程序内部逻辑
    程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值