大家好我是苏麟 , 今天聊聊反射 .
?专业的解释:
?反射允许对封装类的字段,方法和构造函数的信息进行编程访问
?是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
? 对于任意一个对象,都能够调用它的任意属性和方法
? 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
通俗的理解:
利用反射创建的对象可以无视修饰符调用类里面的内容
可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。
读取到什么类,就创建什么类的对象
读取到什么方法,就调用什么方法
此时当需求变更的时候不需要修改代码,只要修改配置文件即可。
反射涉及到四个核心类:
学什么?
反射都是从class字节码文件中获取的内容。
获取字节码文件对象的三种方式
//1.Class这个类里面的静态方法forName
//Class.forName("类的全类名"): 全类名 = 包名 + 类名
Class user = Class.forName("org.example.User");
//源代码阶段获取 --- 先把User加载到内存中,再获取字节码文件的对象
//clazz 就表示User这个类的字节码文件对象。
//就是当User.class这个文件加载到内存之后,产生的字节码文件对象
//2.通过class属性获取
//类名.class
Class userClass = User.class;
//因为class文件在硬盘中是唯一的,所以,当这个文件加载到内存之后产生的对象也是唯一的
System.out.println(user == userClass);//true
//3.通过Student对象获取字节码文件对象
User u = new User();
Class us = u.getClass();
System.out.println(user == userClass);//true
System.out.println(userClass == us);//true
哪些类型可以获取class
public static void main(String[] args) {
String s = new String("1");
//继承
Class aClass = s.getClass();
//.class
Class stringClass = String.class;
Class<Integer> integerClass = int.class;
Class<int[]> aClass1 = int[].class;
Class<Void> voidClass = void.class;
Class<Runnable> runnableClass = Runnable.class;
}
?字节码文件和字节码文件对象
java文件:就是我们自己编写的java代码。
字节码文件:就是通过java文件编译之后的class文件(是在硬盘上真实存在的,用眼睛能看到的)
字节码文件对象:当class文件加载到内存之后,虚拟机自动创建出来的对象。
? 这个对象里面至少包含了:构造方法,成员变量,成员方法。
而我们的反射获取的是什么?字节码文件对象,这个对象在内存中是唯一的。
创建对象
一般情况下我们通过反射创建类对象主要有两种方式:
// 通过 Class 对象的 newInstance() 方法。
Class user = Class.forName("org.example.User");
User newUser = (User) user.newInstance();
//通过 Constructor 对象的 newInstance() 方法
Class user = Class.forName("org.example.User");
User newUser = (User) user.getConstructor().newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法
API 可以看到 Class 有很多方法:
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
- getDeclaredConstructors():返回类中所有的构造方法。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
- getModifiers():获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】
- getSuperclass():返回调用类的父类。
- getInterfaces():返回调用类实现的接口集合。
入门案例
反编译一个类
package org.example;
/**
* @className: User
* @author: SL 苏麟
**/
public class User {
private String name;
public User() {
}
public User(String name) {
this.name = name;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "User{name = " + name + "}";
}
}
@Test
public void testUser() throws Exception {
StringBuffer stringBuffer = new StringBuffer();
Class<?> userClass = Class.forName("org.example.User");
stringBuffer.append(Modifier.toString(userClass.getModifiers()) + " class " + userClass.getSimpleName() + "{");
Constructor<?>[] constructors = userClass.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
stringBuffer.append("\n\t" + Modifier.toString(constructor.getModifiers()) + " " + "(");
Class<?>[] parameterTypes = constructor.getParameterTypes();
int flag = 0;
for (Class<?> parameterType : parameterTypes) {
StringBuffer buffer = flag == 0 ? stringBuffer.append(parameterType.getSimpleName()) : stringBuffer.append(" ," + parameterType.getSimpleName());
flag++;
}
stringBuffer.append(")" + "\n");
}
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
stringBuffer.append("\n\t" + Modifier.toString(field.getModifiers()) + " " + field.getType().getSimpleName() + " " + field.getName() + ";\n");
}
Method[] methods = userClass.getDeclaredMethods();
for (Method method : methods) {
stringBuffer.append("\n\t" + Modifier.toString(method.getModifiers()) + " " + method.getReturnType().getSimpleName() + " " + method.getName() + "{}\n");
}
stringBuffer.append("}");
System.out.println(stringBuffer);
}
打破封装
Field类的方法
public void setAccessible(boolean flag) 默认false,设置为true为打破封装
@Test
public void testUserPrivate() throws Exception {
Class user = Class.forName("org.example.User");
Field nameField = user.getDeclaredField("name");
//破坏封装
nameField.setAccessible(true);
User newUser = (User) user.getConstructor().newInstance();
nameField.set(newUser, "sl");
System.out.println(newUser);
}
这期就到这里 , 下期见!