大家好我是苏麟 , 今天聊聊反射 .
反射
专业的解释:
反射允许对封装类的字段,方法和构造函数的信息进行编程访问
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
对于任意一个对象,都能够调用它的任意属性和方法
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
通俗的理解:
-
利用反射创建的对象可以无视修饰符调用类里面的内容
-
可以跟配置文件结合起来使用,把要创建的对象信息和方法写在配置文件中。
读取到什么类,就创建什么类的对象
读取到什么方法,就调用什么方法
此时当需求变更的时候不需要修改代码,只要修改配置文件即可。
反射涉及到四个核心类:
- java.lang.Class.java:类对象;
- java.lang.reflect.Constructor.java:类的构造器对象;
- java.lang.reflect.Method.java:类的方法对象;
- java.lang.reflect.Field.java:类的属性对象;
学什么?
反射都是从class字节码文件中获取的内容。
- 如何获取class字节码文件的对象
- 利用反射如何获取构造方法(创建对象)
- 利用反射如何获取成员变量(赋值,获取值)
- 利用反射如何获取成员方法(运行)
获取字节码文件对象的三种方式
//1.Class这个类里面的静态方法forName//Class.forName("类的全类名"): 全类名 = 包名 + 类名Class user = Class.forName("org.example.User");//源代码阶段获取 --- 先把User加载到内存中,再获取字节码文件的对象//clazz 就表示User这个类的字节码文件对象。//就是当User.class这个文件加载到内存之后,产生的字节码文件对象
//2.通过class属性获取//类名.classClass 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);//trueSystem.out.println(userClass == us);//true
哪些类型可以获取class
public static void main(String[] args) {String s = new String("1");//继承Class aClass = s.getClass();//.classClass 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() 方法
- 通过 Constructor 对象的 newInstance() 方法
// 通过 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 + "}";}
}
@Testpublic 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为打破封装
@Testpublic 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);}
这期就到这里 , 下期见!