一、反射的简单介绍
1、什么是反射?
反射就是将类的各个组成部分封装为其他的对象,这个就是反射的机制
画图解释说明
字节码文件当中存放的是对应类有关成员变量、构造方法、成员方法和类的名称等信息,在硬盘上存储着
如果我们想进入第三个阶段创建对象的话,必须将硬盘中存储者的class文件加载到内存当中,我们才能调用对应类的构造方法。这时候就需要通过类加载器将class字节码文件加载到内存当中,在内存当中通过Class类来描述字节码文件,而这个类对象中包含三个比较重要的东西分别是成员变量、构造方法和成员方法。由于这三部分是不同的东西并且自己有自己的特征,所以我们把着三部分封装为其他的一些对象,成员变量封装为Field对象,构造方法封装为Constructor对象,成员方法封装为Method对象。由于一个字节码文件当中可能存在不止一个的成员变量和构造方法还有成员方法,所以这里使用了数组将成员变量封装为的对象存入了Field[ ]数组当中,构造方法封装为了的对象存入了Constructor[ ]数组当中、成员方法封装成了的对象存入到了Method[ ]数组当中
2、反射的优点
① 在程序的运行过程中,来操作这个对象
当我们在编译的时候打点会提示出来很多方法,而这些方法其实就是第二阶段中加载到内存当中的方法数组当中的所有方法
② 可以解耦,提高程序的可扩展性
二、获取Class对象的不同方式
1、Class.forName(“全类名”)
将字节码文件加载进内存,返回Class对象
*多用于配置文件,将类名定义在配置文件中。读取文件,加载类
举例示范
public static void main(String[] args) throws ClassNotFoundException {
Class cls = Class.forName("Project2020.Mouth11.Day09.Person");
System.out.println(cls);
}
得到的结果为:
注意:
1、全类名的意思是类包名+类名
2、如果出现了ClassNotFoundException
异常那么一定是全类名输入错误
3、Class.forName方法是个异常方法需要对它进行处理
2、类名.class
通过类名的属性class获取
*多用于参数的传递
3、对象.getClass()
通过对象打点调用getClass方法获取,该方法时Object中的方法,所以任和对象都能使用
*多用于对象的获取字节码的方式
注意
同一个字节码文件(*.class)在一次程序运行过程当中,只会被加载一次,不论通过哪一种加载的方式获取的Class对象
三、Class对象的功能
获取功能
1、获取成员变量们
① Filed[ ] getFields( )
获取所有被public修饰的成员变量
举例说明
Person类
public class Person {
public String a;
public String e;
protected String b;
String c;
private String d;
}
主方法类
public static void main(String[] args) {
Class personClass = Person.class;
Field[] fields = personClass.getFields();
for(Field i : fields){
System.out.println(i);
}
}
得到的结果为:
② Field getField(String name)
获取指定名称的public修饰的成员变量
③ Field[ ] getDeclaredFields()
获取所有的成员变量,不考虑修饰符
④ Field getDeclaredField(String name)
获取所有成员变量中指定名字的成员变量
注意
如果我们想给获取到的被private修饰的成员变量进行设置值或者查询值的操作之前,我们需要忽略访问权限修饰符的安全检查,也称之为暴力反射
举例示范
Person类
public class Person {
public String a;
public String e;
protected String b;
String c;
private String d;
}
主方法:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class personClass = Person.class;
Field d = personClass.getDeclaredField("d");
d.setAccessible(true);
Person p1 = new Person();
d.set(p1, "这里给被private修饰的变量进行的赋值");
System.out.println(d.get(p1));
}
得到的结果如下:
如果不使用暴力反射的话会出现如下结果:(会显示非法访问异常
2、获取构造方法(也就是构造器)们
① Constructor<?>[ ] getConstructors( )
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法(被public修饰的)。
② Constructor<T>[ ] getConstructors(类<?>... parameterTypes)
返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法(被public修饰的)。
③ Constructor<T>[ ] getDeclaredConstructors(类<?>... parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
其中当构造方法被private修饰了的话也可以通过暴力反射的方式来创建对象
④ Constructor<?>[ ] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。
3、获取成员方法们
① Method[ ] getMethods( )
② Method getMethod(String name, 类<?>...parameterTypes)
③ Method[ ] getDeclaredMethods( )
④ Method getDeclaredMethod(String name, 类<?>...parameterTypes)
4、获取类名
String getName( )
Field:成员变量
1、设置值的操作
void set(Object obj, Object value)
2、获取值的操作
get(Object obj)
举例示范
Person类
public class Person {
public String a;
public String e;
protected String b;
String c;
private String d;
}
主方法:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Class personClass = Person.class;
Field a = personClass.getField("a");
Person p1 = new Person();
// 传递一个对象,并且赋a的值
a.set(p1, "这里设置了a的值");
// 打印输出对象p1的a的值
System.out.println(a.get(p1));
}
}
运行结果为:
Constructor构造方法
1、T newInstance(Object... initargs)
Constructor的方法 newInstance(Object…initargs)功能//可变参数
newInstance方法是根据构造函数来生成对象
其中initargs就是你这个对象的构造函数的参数,如果构造函数是无参的,就一个也不要传,这个列表和构造函数的参数列表时一样的,只不过,当是基本数据类型传入的时候,需要一个包装类来包装它,但是jdk1.5也可以直接传入基本数据类型了,因为编译器会帮你自动装箱,拆箱