反射概念
反射: 它是java中的一个很重要的概念,是框架设计的灵魂
框架呢?就是一个半成品软件,我们在这半成品上进行开发,比如我们经常提到spring springmvc springboot spingcloud 等等
也许有的小伙伴会说,框架别人都写好了,我只要在上进行CURD就行了,还要学习反射干什么?,话是这么说,但知道反射,对于后续框架的学习与应用会更加得心应手
反射是在运行状态中,将一个类的每一个组成部分(属性,构造,方法等等) 进行封装,任何一个对象都能进行调用这个类的 属性,构造,方法,这个过程就是反射机制
有点迷?看不懂,没关系
我们先来看一下,你写的java类在虚拟机中是怎么样运行的,它经历了什么
图解析 :
第一阶段:
一个 .java文件在编译期,它会被 javac命令 编译成 .class字节码文件
第二阶段:
编译好的.class文件会被类加载器导入到内存中,形成Class类对象,一个类对象会有成员变量,成员方法,构造方法 等等,这些都可能会有多个,于是就把它封装成用数组来归类存储
第三阶段:
就是运行时阶段了,就是我们常用的new一个对象,来使用
我们new一个对象,底层就是通过Class类阶段创建出来的
回过头来看反射机制那段话,现在是不是对反射机制有更好的理解了呢
反射有哪些好处呢?我们为什么要用反射
首先,反射可以在运行过程中,操作这些对象 ,不理解,没关系,举个例子
给大家看张图
搞错了
不知道大家是不是在使用idea进行编程的,idea这个编译器其实就用到了反射
看到这些方法了没,其实用的就是反射机制,这些方法封装到method数组中,我们idea就是一个程序,它是不是一直在运行,就把list的方法,展示到这个列表中 ,在运行中,我们就知道这个集合中有哪些方法
其次,就是反射可以提高程序可扩展性
反射API
获取Class方式:
先创建一个person类
package com.test1.demo;
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
1. Class.forName(“全类名”) :将字节码加载进内存,返回Class文件
//获取class
try {
Class<?> clazz = Class.forName("com.test1.demo.Person");
System.out.println(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
2. 类名.class : 通过类名属性获取
//获取class
Class<Person> clazz = Person.class;
System.out.println(clazz);
3. 对象.getClass
//获取class
Person person = new Person();
System.out.println(person.getClass());
注意:同一个字节码文件(.class) 文件,只会被加载一次,不论哪一种方式获取的class对象都是同一个对象
@Test
public void tes9() throws ClassNotFoundException {
//获取class
Class<?> clazz = Class.forName("com.test1.demo.Person");
System.out.println(clazz);
Class<Person> clazz1 = Person.class;
System.out.println(clazz1);
Person person = new Person();
Class<? extends Person> clazz2 = person.getClass();
System.out.println(clazz2);
//判断是不是同一个对象 ==
System.out.println(clazz == clazz1);
System.out.println(clazz == clazz2);
}
这三种方式,该选择哪一种呢?
分情况
Class.forName(“全类名”) 这种方式多用于配置文件,读取文件,加载类
类名.class 多用于参数传递
对象.getClass 一般情况下,有对象了,去获取字节码的方式
常用Class的方法:
1. 获取成员变量组合
getFields() 和 getDeclaredFields()
注意:这两个是有区别的, getFields() 是只能获取这个类中公共的字段,也就是只能获取修饰符是 public 的变量,并且包括父类中的字段
getDeclaredFields() 能获得某个类声明的所有变量,但不包括父类
很简单!!来个例子
现在有一个 Person 类,一个Animal 类,Person 类继承于 Animal
注意看 两个类中成员变量的 修饰符
public class Person extends Animal{
private String personName;
public Integer personAge;
}
public class Animal{
private Integer animalName;
public String animalAge;
}
测试1:
//获取class
Class<Person> clazz1 = Person.class;
Field[] fields = clazz1.getFields();
for (Field field : fields) {
System.out.println(field.getType()+"...." +field.getName());
}
输出结果:
可以看出,getFields() 只能获取public 修饰的变量,并且能获取父类中的public修饰变量
测试2:
//获取class
Class<Person> clazz1 = Person.class;
Field[] fields = clazz1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getType() + "...." + field.getName());
}
输出结果:
可以看出getDeclaredFields() 可以获取 所有声明成员变量,但无法获取父类
2. 获取构造方法
创建一个Perosn 类 ,有两个属性,分别赋予 全参构造,单参构造和 无参构造
public class Person {
private String name;
public Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person() {
}
private Person(String name) {
this.name = name;
}
}
- getDeclaredConstructors() 获取所有构造函数
//获取class
Class<Person> clazz1 = Person.class;
Constructor<?>[] declaredConstructors = clazz1.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
- getConstructors() 获取所有public 构造函数
//获取class
Class<Person> clazz1 = Person.class;
Constructor<?>[] declaredConstructors = clazz1.getConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
- getConstructor() 获取无参构造函数
//获取class
Class<Person> clazz1 = Person.class;
Constructor<Person> constructor = clazz1.getConstructor();
System.out.println(constructor);
这边把无参构造换成私有的,还能不能获取呢?
答案是:不能 ,有兴趣的小伙伴可以试试,会报错,贴出来给大家看看
- getDeclaredConstructor(Class<?>… parameterTypes) 根据参数类型获取构造
//获取class
Class<Person> clazz1 = Person.class;
Constructor<Person> declaredConstructor = clazz1.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
以上便是反射的基本使用了!!