java反射机制详解

目录

类加载器
01类的加载
02类的加载时机
03三种类的加载器

反射
01反射的概念以及作用
02class文件的产生过程
03获取class文件对象三种方式
04反射获取空参数构造方法并运行
05反射获取有参数的构造方法并运行
06反射获取构造方法并运行的快速的方式
07反射获取私有构造方法并运行
08反射获取成员变量并改值
09反射获取空参数成员方法并运行
10反射获取有参数的成员方法并运行
11反射泛型擦除
12反射通过配置文件运行的步骤
13反射通过配置文件运行功能实现

一、类加载器
* A.类的加载
	当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
	* a 加载 
		* 就是指将class文件读入内存,并为之创建一个Class对象。
		* 任何类被使用时系统都会建立一个Class对象
	* b 连接
		* 验证 是否有正确的内部结构,并和其他类协调一致
		* 准备 负责为类的静态成员分配内存,并设置默认初始化值
		* 解析 将类的二进制数据中的符号引用替换为直接引用
·	* c 初始化 
		* 就是我们以前讲过的初始化步骤(new 对象)
	* 注:简单的说就是:把.class文件加载到内存里,并把这个.class文件封装成一个Class类型的对象。
* B.类的加载时机
	以下的情况,会加载这个类。
	* a. 创建类的实例
	* b. 类的静态变量,或者为静态变量赋值
	* c. 类的静态方法
	* d. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
	* e. 初始化某个类的子类
	* f. 直接使用java.exe命令来运行某个主类
		
* C: 类加载器(了解)
	负责将.class文件加载到内在中,并为之生成对应的Class对象。
	* a. Bootstrap ClassLoader 根类加载器
		* 也被称为引导类加载器,负责Java核心类的加载
		* 比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
	* b. Extension ClassLoader 扩展类加载器
		* 负责JRE的扩展目录中jar包的加载。
		* 在JDK中JRE的lib目录下ext目录
	* c. System ClassLoader 系统类加载器
		* 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
		* 我们用的是System ClassLoader 系统类加载器
三、反射机制原理及示例
A. 反射定义

a. JAVA反射机制是在运行状态中
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

b.反射技术
条件:运行状态
已知:一个类或一个对象(根本是已知.class文件)
结果:得到这个类或对象的所有方法和属性
注: 要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。

B. Class类

a. Class类及Class对象的了解
要想解剖一个类,必须先了解Class对象。
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

b. 得到Class对象的三个方法

方式1: 通过Object类中的getClass()方法
Person person = new Person();
Class clazz = person.getClass();

方式2: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
Class clazz = Person.class;

方式3: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
Class c3 = Class.forName(“Person”);
注:第三种和前两种的区别是:
前两种你必须明确Person类型。后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了

得到Class对象的三个方法代码演示:测试类ReflectDemo .java

/*
	* 获取.class字节码文件对象的方式
    * 1:通过Object类中的getObject()方法
	 * 2: 通过 类名.class 获取到字节码文件对象
	 * 3: 反射中的方法,
	 * public static Class<?> forName(String className) throws ClassNotFoundException
	 * 返回与带有给定字符串名的类或接口相关联的 Class 对象 
 */
public class ReflectDemo {
	public static void main(String[] args) throws ClassNotFoundException {
		// 1: 通过Object类中的getObject()方法
		// Person p1 = new Person();
		// Class c1 = p1.getClass();
		// System.out.println("c1 = "+ c1);
		
		// 2: 通过 类名.class 获取到字节码文件对象
		// Class c2 = Person.class;
		// System.out.println("c2 = "+ c2);

		// 3: 反射中的方法
		Class c3 = Class.forName("net.xikee_reflect.Person");// 包名.类名,获得字节码文件对象
		System.out.println("c3 = " + c3);
		}
}

: Class类型的唯一性
因为一个.class文件在内存里只生成一个Class对象,所以无论那一种方法得到Class对象,得到的都是同一个对象。

C.通过反射获取无参构造方法并使用

通过调用newInstance()方法运行构造方法,得到该类的对象。

//通过Person类的全限定名,获取该类字节码文件对象
Class c = Class.forName("cn.itcast.demo1.Person");
//使用class文件对象,获取指定的构造方法,空参数的构造方法
Constructor con =  c.getConstructor();

//运行空参数构造方法,Constructor类方法 newInstance()运行获取到的构造方法
//运行构造方法相当于创建Person类的对象
Object obj = con.newInstance();
D. 通过反射获取有参构造方法并使用
/*
	 *  通过反射,获取有参数的构造方法并运行
	 *  方法getConstructor,传递可以构造方法相对应的参数列表即可
 */
Class c = Class.forName("cn.itcast.demo1.Person");

//获取带有,String和int参数的构造方法
//String.class:获得String类型的字节码文件对象
Constructor con = c.getConstructor(String.class,int.class);

//运行构造方法, 运行构造方法后,传递的实际参数
Object obj = con.newInstance("张三",20);
System.out.println(obj.toString());
E. 通过反射获取无参构造方法并使用快捷方式

a. 使用的前提
类有空参的公共构造方法。(如果是同包,默认权限也可以)

b. 使用的基础
Class类的 public T newInstance() 方法
创建此 Class 对象所表示的类的一个新实例。

Class c = Class.forName("cn.itcast.demo1.Person");

// Class类中定义方法, T newInstance() 直接创建被反射类的对象实例
Object obj = c.newInstance();
F. 通过反射获取私有构造方法并使用
Class c = Class.forName("cn.itcast.demo1.Person");
Constructor con = c.getDeclaredConstructor(int.class,String.class);
					
//Constructor类,父类AccessibleObject,定义方法setAccessible(boolean b)
con.setAccessible(true);
Object obj = con.newInstance(18,"lisi");

注:不推荐,破坏了程序的封装性,安全性。

G. 反射获取成员变量并改值
//获取指定的成员变量 String name
//Class类的方法  Field getField(传递字符串类型的变量名) 获取指定的成员变量
Field field = c.getField("name");

//Field类的方法 void set(Object obj, Object value) ,修改成员变量的值
//Object obj 必须有对象的支持,  Object value 修改后的值
field.set(obj,"xikee");
H. 反射获取空参数成员方法并运行
//获取指定的方法eat运行
// Method getMethod(String methodName,Class...c)
// methodName获取的方法名  c 方法的参数列表
Method method = c.getMethod("eat");
					
//使用Method类中的方法,运行获取到的方法eat
//Object invoke(Object obj, Object...o),参数:对象和相关参数(无参可忽略)
method.invoke(obj);
I. 反射获取有参数成员方法并运行
//调用Class类的方法getMethod获取指定的方法sleep
Method method = c.getMethod("sleep", String.class,int.class,double.class);

//调用Method类的方法invoke运行sleep方法
method.invoke(obj, "休眠", 10, 5d);
J. 反射泛型擦除----仅供理解反射用

a. 使用情况
例如:在泛型为String的集合里,添加Integer的数据
ArrayList list = new ArrayList();
list.add(100);

b. 能用泛型擦除的理论
伪泛型:在编译后的.class文件里面是没有泛型的。类型为Object。
可用反射的方法绕过编译,得到Class文件对象,直接调用add方法。

ArrayList<String> array  = new ArrayList<String>();
array.add("a");
//反射方式,获取出集合ArrayList类的class文件对象
Class c = array.getClass();
//获取ArrayList.class文件中的方法add
Method method = c.getMethod("add",Object.class);
//使用invoke运行ArrayList方法add
method.invoke(array, 150);
method.invoke(array, 1500);
method.invoke(array, 15000);
System.out.println(array);

注:通过反射擦去泛型,将int类型的值插入String类型的集合后,由于类型无法确定,只能通过array.toString()查看里面的值,无法遍历。

K. 反射通过配置文件来决定运行的步骤

实现步骤:

  1. 准备配置文件,键值对
  2. IO流读取配置文件 Reader
  3. 文件中的键值对存储到集合中 Properties 集合保存的键值对,就是类名和方法名
  4. 反射获取指定类的class文件对象
  5. class文件对象,获取指定的方法
  6. 运行方法
/*
*  调用Person方法,调用Student方法,调用Worker方法
*  类不清楚,方法也不清楚
*  通过配置文件实现此功能
*    运行的类名和方法名字,以键值对的形式,写在文本中
*    运行哪个类,读取配置文件即可
 */
public class Test {
	public static void main(String[] args) throws Exception{
	//IO流读取配置文件
	FileReader r = new FileReader("config.properties");
	//创建集合对象
	Properties pro = new Properties();
	//调用集合方法load,传递流对象
	pro.load(r);
	r.close();
	//通过键获取值
	String className = pro.getProperty("className");
	String methodName = pro.getProperty("methodName");
	//反射获取指定类的class文件对象
	Class c = Class.forName(className);
	Object obj = c.newInstance();
	//获取指定的方法名
	Method method = c.getMethod(methodName);
	method.invoke(obj);
	}
}

config.properties配置文件:

#className=cn.itcast.demo3.Student	//“#”符号为注释
#methodName=study
			
className=cn.itcast.demo3.Person
methodName=eat

#className=cn.itcast.demo3.Worker
#methodName=job
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值