反射
反射:框架设计的灵魂
框架:半成品软件。可以在框架的基础上进行软件的开发,来简化编码
反射的概念:将 类的各个组成部分 封装为其他对象,这就是反射机制
Java代码 在计算机中经历的三个阶段:
Source源代码阶段、Class类对象阶段、Runtime运行时阶段
Class类对象阶段:
把成员变量封装为Field对象 Field[ ]fields ,
把构造方法封装为Constructor对象 Constructor[ ]cons ,
把成员方法封装为Method对象 Method[ ]methods 。 这就是反射机制
反射的好处:
1.可以在程序运行过程中 操作这些对象
2.可以解耦,降低程序的一些耦合性和紧密的联系程度,来提高程序的可扩展性
获取Class对象的方式:(共三个方式,和三个阶段相对应)
1. Class.forName("全类名") :将字节码文件加载进内存,返回Class对象
2. 类名.class : 通过类名的属性class获取
3. 对象.getClass() : getClass()方法在object类中定义着
注:object类是所有类的父类
示例(获取Class对象)如下:
先定义两个成员变量:
可得到这两个参数的构造:
空参的构造:
然后把两个的Getter和Setter也弄出来
(setter是一种更新变量值的方法。Getter是一种读取变量值的方法)
最后再定义一个toString方法:
(使用Ctrl键可以同时选中两个)
那么这个Person.java类 就写完了,即字节码文件就有了
反射reflect范例:
Class.forName:返回与给定的字符串名称相关联 类 或接口 的Class对象。
Class.forName是一个静态方法
全类名 就是 包名.类名
出现编译异常,把它直接抛出去即可:
返回一个class对象
注意 这里起名的时候就不要起class了,因为它是一个定义类的关键字
运行主方法,可以发现运行成功:
运行这三种方式,结果显示 三个字符串的表示形式都是一样的(但不能直接说明这三个对象是一样的,要进行判断)
这三个对象对应的都是Person这个字节码文件在内存里面生成的Class类对象
可以用==来比较三个对象,来判断这三个对象是否是一样的(==比较的是对象的内存地址)
(如果地址值相同,则为同一个对象;反之,这三个对象是不一样的)
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象 都是同一个!
每一个字节码文件 对应的Class类对象 都不相同
获取Class对象的方式:
1. Class.forName("全类名") :将字节码文件加载进内存,返回Class对象
//多用于配置文件,将类名 定义在配置文件中。 读取文件,加载类
2. 类名.class : 通过类名的属性class获取
//多用于参数的传递
3. 对象.getClass() : getClass()方法在object类中定义着
//多用于对象的获取字节码的方式
Class对象的功能:
获取功能:
1.获取成员变量们
Field[ ] getFields( ) :获取所有public修饰的成员变量
Field getField(String name) :获取指定名称的public修饰的成员变量
Field[ ] getDeclaredFields( ) :获取所有的成员变量,不考虑访问权限修饰符
(在反射面前,甚至是私有的private也可以被访问)
Field getDeclaredField(String name)
2.获取构造方法们
Constructor<?>[ ] getConstructors( )
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<?>[ ] getDeclaredConstructors( )
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
3.获取成员方法们
Method[ ] getMethods( )
Method getMethod(String name, 类<?>... parameterTypes)
Method[ ] getDeclaredMethods( )
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4.获取类名
String getName( )
补充:1.(快捷键) psvm,可快捷生成main方法
Enter键 回车
2.(快捷键)iter ,生成高效for循环
3.(快捷键)sout ,自动补全System.out.println();
4.ieda-自动引入局部变量(introduce local variable)快捷键
ctrl+alt+V 自动创建一个对象类型和对象名
Field:成员变量
操作:
1.设置值
void set(Object obj, Object value)
(没有显示出来,可能是因为没有重新去修改toString方法,需要去重新生成一下)
2.获取值
get(Object obj)
注意:传的是对象,所以括号里的是一个对象
3.忽略访问权限修饰符的安全检查
setAccessible(true)
暴力反射(不管是Field对象还是Constructor、Method 对象 里面都有这一方法)
上图出现了ILLegal....非法的访问异常, 所以在使用一些不是由访问权限修饰符public修饰的成员的时候,在访问之前,需要以下这行代码来 忽略访问权限修饰符的安全检查(即暴力反射)
.setAccessible(true);
Consructor :构造方法
创建对象:
T newInstance(Object... initargs)
参数initargs:表示通过构造方法创建对象时 传递的实际参数
(使用此 Constructor对象 表示的构造函数,使用指定的初始化参数 来创建和初始化 构造函数的声明类的新实例.)
如果使用 空参数 构造方法 创建对象,操作可以简化 :使用Class对象的newInstance方法
补充: 当使用Declared时 仍然要使用到 暴力反射: constructor1.setAccessible(true);
Method :方法对象
执行方法:
Object invoke(Object obj, Object...args)
获取方法名称:
String getName
获取功能的示例如下:
反射的案例:
需求:写一个“框架”,不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现这个需求需要借助两个技术:
1.配置文件
2.反射
步骤:
1.将需要创建的对象的全类名 和 需要执行的方法 定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射技术来加载类文件进内存
4.创建对象
5.执行方法
已有两个方法,一个是Person.java中的eat方法
另一个是Student.java中的sleep方法
在src里创建一个New的(file)文件
或者
第一个执行结果如下:
第二个结果如下:
注:
1.类加载器classLoader
2.如果写一个类需要动态的获取某个文件的位置,从而能够获取此文件的资源,可以使用Class.getResourceAsStream()方法,可以看到它最终返回了一个输入流,也就是返回一个InputStream对象方便对此文件资源通过IO流进行获取
3.Class.forName:返回与给定的字符串名称相关联 类 或接口 的Class对象。
Class.forName是一个静态方法