什么是反射
反射机制是指程序在运行过程中,对任意一个类都能获取其所有的属性和方法,并且对任意一个对象都能调用其所有的属性和方法。这种动态获取类和对象的信息,以及动态调用对象的方法的功能被称为java语言的反射机制。
反射的实现原理
JVM启动时,java类被编译成二进制的.class文件,然后类加载器根据类的全限定名把.class的二进制字节码代表的静态存储结构转化为方法区的运行时数据结构,然后根据方法区的类型区生成class对象存于堆中,JVM保证一个类有且只有一个Class对象。能够拿到class对象,就能获取整个类的结构并生成对应的java对象,供调用。
java反射常用API
Class类:用于获取类的属性、方法、构造器等信息。
- 获得类相关的方法
方法 | 说明 |
---|---|
forName(String className) | 根据类名返回类的class对象 |
getName() | 获取类的完成路径名 |
getPackage() | 获取类的包名 |
getSimpleName() | 获取类的名字 |
newInstance() | 创建类的实例,需要类中有无参构造函数 |
getSuperclass() | 获取当前类父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
- 获取类中属性相关的方法
方法 | 说明 |
---|---|
getField(String name) | 根据名字获取某个公有属性对象 |
getDeclaredField(String name) | 根据名字获取某个属性对象(私有属性也可以获取) |
getFields() | 获得所有公有的属性对象 |
getDeclaredFields() | 获得所有属性对象 |
- 获取类中构造器相关的方法
方法 | 属性 |
---|---|
getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法(包括私有) |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructors() | 获取该类所有的构造方法,包括私有 |
- 获取类中方法相关的方法
方法 | 说明 |
---|---|
getMethod(String name, Class…<?> parameterTypes) | 获得该类中某个公有参数类型匹配的方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类中某个参数类型匹配的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethods() | 获得该类所有方法,包括私有 |
- 获取类中注解相关的方法
方法 | 说明 |
---|---|
getAnnotation(Class<?> annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getDeclaredAnnotation(Class<?> annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象,包括私有 |
可以发现带有Declared修饰的方法可以反射到私有的方法,没有Declared修饰的只能用来反射公有的方法
Field类:标识类的成员变量,用于获取和设置类中的属性值。
方法 | 说明 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
method类:表示类的方法,用于获取方法的描述信息或者执行某个方法。
方法 | 说明 |
---|---|
invoke(Object obj, Object… args) | 传递object对象及参数调用该对象对应的方法 |
Constructor类:表示类的构造方法。
方法 | 说明 |
---|---|
newInstance(Object… initargs) | 根据传递的参数创建类的对象 |
代码实例
通过实例代码,展示上述方法中一些常用方法的使用
一、创建一个类
public class Student {
private String username;
private int age;
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
public Student(String username, int age) {
this.username = username;
this.age = age;
}
private void soutStr(String username, int age) {
System.out.println("1111111");
}
public Student() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2.使用反射API获取类的属性、方法等信息
@RunWith(SpringRunner.class)
@SpringBootTest
public class reglexTest {
//三种获取Class对象的方法
@Test
public void getClassTest() throws ClassNotFoundException {
//1.调用某个对象的getClass方法来获取该类的Class对象
Student s = new Student();
Class clazz = s.getClass();
//2.调用某个类的class属性来获取该类的Class对象
Class clazz1 = Student.class;
//3.调用Class类中的forName静态方法来获取对应的Class对象,这是最安全、性能最好的方法
Class clazz3 = Class.forName("com.example.reflex.Student");
System.out.println("clazz:"+clazz);
System.out.println("clazz1:"+clazz1);
System.out.println("clazz3:"+clazz3);
}
//通过Class对象获取类方法,属性信息,构造方法等信息
@Test
public void test1() throws ClassNotFoundException {
//1.获取Student类的Class对象
Class clazz3= Class.forName("com.example.reflex.Student");
//2.获取所有方法
Method[] methods = clazz3.getDeclaredMethods();
for (Method m : methods){
System.out.println(m.toString());
System.out.println(m.toGenericString());
}
//3.获取类的所有成员的属性信息
Field[] fields = clazz3.getDeclaredFields();
for (Field f : fields){
System.out.println(f.toString());
System.out.println(f.toGenericString());
}
//4.获取类所有构造方法信息
Constructor[] constructors = clazz3.getDeclaredConstructors();
for (Constructor c : constructors){
System.out.println(c.toString());
}
}
//使用反射创建对象 第一种方法:使用Class的newInstance()方法
//第二种:获取构造方法,通过构造方法创建对象
@Test
public void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.1获取Student 类的class对象
Class clazz= Class.forName("com.example.reflex.Student");
//1.2使用newInstance方法创建对象
Student s = (Student) clazz.newInstance();
System.out.println(s.toString());
//2.1获取构造方法并创建对象
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
Student s2 = (Student) c.newInstance("张三",20);
System.out.println(s2.toString());
}
//使用反射调用目标类中的方法
@Test
public void test3() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//1获取person类的class对象
Class clazz= Class.forName("com.example.reflex.Student");
//2.获取class对象的setName方法
Method m = clazz.getDeclaredMethod("setUsername",String.class);
//3.通过获取Constructor来创建student对象
Constructor constructor = clazz.getDeclaredConstructor();
Object obj = constructor.newInstance();
//4.调用method的invoke方法
m.invoke(obj,"李四");
System.out.println(obj.toString());
}
}
为什么要用到反射
1.有些类,没有办法通过new获取实例化对象
①调用的是另外一个应用传来的.class文件,没有java代码。
②注解: 注解本身仅仅是起到标记作用,它需要利用反射机制,根据注解标记去调用注解解释器,执行行为.
2.动态加载,提高程序的灵活性和扩展型。
①当需要修改代码中初始化对象使用的类的类型时,如果用反射,可以将类的全限定名配置到配置文件中,而不需要修改代码,比如:实例化一个Student对象,如果想要变成实例化其他类,如果使用new就需要修改源码,如果使用class.forName(“className”).newInstance();就只需要将className做配置化就行了。
开发通用框架 - 反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
参考链接:https://blog.csdn.net/tongdanping/article/details/103252352
②动态代理:在切面编程(AOP)中,需要拦截特定的方法,通常,会选择动态代理方式。这时,就需要反射技术来实现了
3.低耦合
反射缺点
1.开销大
2.安全性:使用反射可以使被反射的类信息全部暴露,包括私有属性。
问题:java两种反射的区别 - Class.forName()和ClassLoader.loadClass()