第1章 反射
1.1 类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
就是我们以前讲过的初始化步骤
1.2 类的初始化
Java类只要满足以下任意一条,都讲完成类的初始化。
- 创建类的实例
- 类的静态变量,或者为静态变量赋值
- 类的静态方法
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
到目前为止我们已经知道把class文件加载到内存做了哪些事情了,那么, 如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?那就是我们反射将要学习的内容了。
1.3 反射概述
Java反射机制是在运行状态中,对指定的类,任意的方法或任意的字段进行操作,这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。
简而言之,反射就是:在运行时通过代码操作类。
想要操作类,就必须先获得该类的字节码对象Class。通过Class提供的方法对类进行进一步的解剖,从而实现各种操作。
在进行具体操作之前,大家需要先掌握类各个组成的专业描述
Class类
Constructor 构造
Method 方法
Field 字段
instance 实例
invoke 执行
1.4 准备数据
添加字段
public class Bean {
private String id;
private String className;
Bean类提供getter和setter方法
public String getId() {
System.out.println("getId方法执行");
return id;
}
public void setId(String id) {
System.out.println("setId方法执行:" + id);
this.id = id;
}
1.5 Class获得方式
//1 通过类型获得
// 语法:类名.class
// 应用场景:确定类型 等
Class clazz1 = Bean.class;
//2 通过实例对象获得
// 语法:变量.getClass()
// 应用场景:在方法内部通过参数获得类型 等
Bean bean = new Bean();
Class clazz2 = bean.getClass();
//3 通过字符串获得
// 语法:Class.forName(“全限定类名”)
// 应用场景:通过配置获得字符串 等
Class clazz3 = Class.forName(“com.itheima_00_Bean.Bean”);
1.6 构造方法与实例
1.6.1 添加构造
修改Bean ,添加构造方法
public Bean() {
System.out.println("无参构造");
}
public Bean(String id, String className) {
this.id = id;
this.className = className;
System.out.print("有参构造:" + id);
System.out.println(" ," + className);
}
1.6.2 无参构造
无参构造,并获得实例
@Test
public void testDefaultCons() throws Exception{
//无参构造 , 并实例化
//1获得Class
Class beanClass = Bean.class;
//2获得构造 -- 没有形参
Constructor constructor = beanClass.getConstructor();
//3 实例对象,没有实参
Object bean = constructor.newInstance();
System.out.println(bean);
/* 结果:
* 无参构造
* com.itheima_00_Bean.Bean@8c5488
*/
}
1.6.3 有参构造
有参构造,并获得实例
@Test
public void testParamCons() throws Exception{
//有参构造 , 并实例化
//1获得Class
Class beanClass = Bean.class;
//2获得构造 -- 两个字符串形参 -- Bean(String id, String className)
Constructor constructor = beanClass.getConstructor(String.class,String.class);
//3 实例对象,两个字符串实参
Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList");
System.out.println(bean);
/* 结果:
* 有参构造:ArrayListId ,java.util.ArrayList
* com.itheima_00_Bean.Bean@101acff
*/
}
1.6.4 无参构造,简化版获得实例
无参构造,简化版获得实例
@Test
public void testDefaultSimple() throws Exception{
//无参构造 , 简化版
//1获得Class
Class beanClass = Bean.class;
//2 直接获得实例对象,两个字符串实参
Object bean = beanClass.newInstance();
System.out.println(bean);
/* 结果:
* 无参构造
* com.itheima_00_Bean.Bean@101acff
*/
}
1.6.5 扩展:私有构造(暴力反射)
修改Bean添加私有构造
private Bean(String id) {
this.id = id;
System.out.println("有参构造:" + id);
}
getConstructor() 使用该方法将无法获得私有方法,程序运行抛异常
没有使用setAccessible(true),将抛异常
@Test
public void testPrivateCons() throws Exception{
//私有构造
//1获得Class
Class beanClass = Bean.class;
//2获得构造 -- 两个字符串形参 -- Bean(String id, String className)
// * getConstructor() 将抛异常 java.lang.NoSuchMethodException
// * getDeclaredConstructor 可以获得私有构造
Constructor constructor = beanClass.getDeclaredConstructor(String.class);
//暴力访问
constructor.setAccessible(true);
//3 实例对象,两个字符串实参
Object bean = constructor.newInstance("userId");
System.out.println(bean);
/* 结果:
* 有参构造:userId
* com.itheima_00_Bean.Bean@8c5488
*/
}
1.7 方法与执行
1.7.1 public方法
获得方法并设置
@Test
public void testMethod() throws Exception{
//1 获得Class
Class clazz = Class.forName("com.itheima_00_Bean.Bean");
//2 获得实例 ,相当于 Object obj = new Bean();
Object obj = clazz.newInstance();
//3 操作setId方法
//3.1 获得的方法,一个形参
// * 格式:getMethod(方法名,形成列表)
Method setMethod = clazz.getMethod("setId", String.class);
//3.2 执行方法,一个实参
Object setReturnObj = setMethod.invoke(obj, "我是参数");
System.out.println("set方法返回值:" + setReturnObj);
/* 运行结果:
* 无参构造
* setId方法执行:我是参数
* set方法返回值:null
*/
System.out.println("---------------");
//4 操作getId方法 (巩固)
// 3.1 获得方法,没有形参
Method getMethod = clazz.getMethod("getId");
// 3.2 执行方法,没有实参
Object getReturnObj = getMethod.invoke(obj);
System.out.println("get方法返回值:" + getReturnObj);
/* 运行结果:
* getId方法执行
* get方法返回值:我是参数
*/
}
1.7.2 扩展:私有方法(暴力反射)
修改bean,添加方法:
//私有方法
private String show(){
System.out.println("私有方法执行");
return "Bean["+id+", "+ className +"]";
}
getMethod() 使用该方法将无法获得私有方法,程序运行抛异常
没有使用setAccessible(true),将抛异常
@Test
public void testPrivateMethod() throws Exception{
//1 获得Class
Class clazz = Class.forName("com.itheima_00_Bean.Bean");
//2获得构造 -- 两个字符串形参 -- Bean(String id, String className)
Constructor constructor = clazz.getConstructor(String.class,String.class);
//3 实例对象,两个字符串实参
Object bean = constructor.newInstance("ArrayListId","java.util.ArrayList");
//3获得方法 -- 私有方法 -- private String show()
// * getMethod(方法名,形成列表) 将抛异常 java.lang.NoSuchMethodException
// * getDeclaredMethod(方法名,形成列表) 可以获得私有构造
Method showMethod = clazz.getDeclaredMethod("show");
//暴力访问
showMethod.setAccessible(true);
// 4 执行方法,没有实参
Object getReturnObj = showMethod.invoke(bean);
System.out.println("show方法返回值:" + getReturnObj);
/* 运行结果:
* 有参构造:ArrayListId ,java.util.ArrayList
* 私有方法执行
* show方法返回值:Bean[ArrayListId, java.util.ArrayList]
*/
}
1.7.3 扩展:main方法与执行
修改Bean,添加main方法
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.print(args[i] +", ");
}
}
通过反射,调用执行main方法
@Test
public void testMainMethod() throws Exception{
//调用main方法
//1 获得Class
Class clazz = Class.forName("com.itheima_00_Bean.Bean");
//2获得方法 -- main静态方法 -- public static void main(String[] args)
Method mainMethod = clazz.getMethod("main", String[].class);
Object getReturnObj = mainMethod.invoke(null, (Object)new String[]{"aaa","bbb"});
System.out.println();
System.out.println("main方法返回值:" + getReturnObj);
/*
* 运行结果:
* aaa, bbb,
* main方法返回值:null
*/
}
1.8 字段(成员变量)与数据操作(了解)
1.8.1 添加public字段
Bean类提供成员变量 address
public class Bean {
private String id;
private String className;
public String description;
1.8.2 public字段的操作
@Test
public void testField() throws Exception{
/* 获得实例,为public字段赋值、获取值
* public String description;
*/
//1 获得Class
Class clazz = Class.forName("com.itheima_00_Bean.Bean");
//2 获得实例 ,相当于 Object obj = new Bean();
Object bean = clazz.newInstance();
//3 操作字段,进行赋值,public String description;
//3.1 获得的字段,一个形参
// * 格式:getField(字段名)
Field descriptionField = clazz.getField("description");
//3.2 为对象的字段赋值
descriptionField.set(bean, "Bean的描述");
//3.3 获取对象的字段值
Object fieldReturnObj = descriptionField.get(bean);
System.out.println("description字段返回值:"+fieldReturnObj);
}
1.8.3 扩展:私有字段(暴力反射)
getField() 使用该方法将无法获得私有字段,程序运行抛异常
没有使用setAccessible(true),将抛异常
@Test
public void testPrivateField() throws Exception{
//私有字段 className
//1获得Class
Class clazz = Bean.class;
//2 获得实例 ,相当于 Object obj = new Bean();
Object bean = clazz.newInstance();
//3获得字段 -- 私有字段 -- className
// * getField() 将抛异常 java.lang.NoSuchMethodException
// * getDeclaredField 可以获得私有字段
Field classNameField = clazz.getDeclaredField("className");
//暴力访问
classNameField.setAccessible(true);
//3.2 为对象的字段赋值
classNameField.set(bean, "Bean的类名称");
//3.3 获取对象的字段值
Object fieldReturnObj = classNameField.get(bean);
System.out.println("description字段返回值:"+fieldReturnObj);
/* 结果:
* 无参构造
* description字段返回值:Bean的类名称
*/
}