这周在干什么
主要是在复习、看看老师写的代码、看看worktile、对知识进行扩充,还有不到两周就结课了,但是没有收到考试时间以及放假时间的通知......😅😅😅
什么是反射?
反射(Reflection)是 Java 的一种特性,它可以让程序在运行时获取自身的信息,并且动态地操作类或对象的属性、方法和构造器等。通过反射功能,可以让我们在不知道具体类名的情况下,依然能够实例化对象,调用方法以及设置属性。
反射机制关键在于Class对象
,即类对象
。是由Java虚拟机在JVM在加载类时自动创建的。
虚拟机创建类的过程:
当我们编写一个类并完成编译后,编译器会将转换为字节码存储在.class后缀文件中,在类 的加载过程中虚拟机利用Class Loader读取.class文件,将其中的字节码加载到内存中,并基于这些信息创建相应的Class对象,由于每个类值JVM中只加载一次所以每个类都对应着一个唯一的Class对象
demo
public class User extends People {
public String name;
private int age;
private static int staticFiled = 10;
private final String sex;
protected String protectedFiled;
static {
System.out.println("静态方法被执行");
}
public User(String name,String sex) {
this.name = name;
this.sex = sex;
}
private void privateMethod() {
System.out.println("我是私有方法");
}
public void publicMethod() {
System.out.println("我是公有方法");
}
}
public class People {
public String publicFiled;
private String privateFiled;
}
获取Class对象的三种方式
第一种方式
通过类名 .class 进行获取,是在编译型进行获取的,所以可以明确指定类型是User不报错。使用这种方式对象经行获取时,不会出发类的初始化,只有在访问类的静态成员,或者实例的时候才会进行触发。
Class<User> userClass = User.class;
实例化对象
User userInstance = userClass.getDeclaredConstructor(String.class, String.class).newInstance("张三", "男");
第二种方式
通过对象的 getClass() 方法进行获取到。这种方法适用于某个类的已被实例化进行获取。
可以看到类型不是User,而是通配符 “?”,原因:这个Class对象是从User实力获取的,而实例的具体类型只能在运行是进行创建和确定,而此时当前是编译阶段,因此无法获取到Class的类型
User user = new User("张三", "男");
Class<?> userClass = user.getClass();
实例化对象:
Constructor<?> constructor = userClass.getConstructor(String.class, String.class);
第三种方式
使用Class的forName()
静态方法,通过完整路径获取,由于它也是运行才能知道类型的,所以类型是通配符,通过这种方式获取到Class对象会立即出发类的初始化。
Class<?> userClass = Class.forName("org.example.reflect.entity.User");
创建实例对象
Constructor<?> constructor = userClass.getDeclaredConstructor(String.class, String.class);
constructor.newInstance("张三", "男");
获取到对象的属性
getFields()
获取所有公共字段,包括从父类继承的公共字段。
Field[] fields = user.getFields();
for (Field field : fields) {
System.out.println(field);
}
输出:
public java.lang.String org.example.relect.entity.User.name
public java.lang.String org.example.relect.entity.People.publicFiled
getDeclaredFields()
获取类中声明的所有字段,不管是什么级别,都可以获取到,但不包括从超类继承的字段,
Field[] fields = user.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
//输出:
public java.lang.String org.example.relect.entity.User.name
private int org.example.relect.entity.User.age
private final java.lang.String org.example.relect.entity.User.sex
protected java.lang.String org.example.relect.entity.User.protectedFiled
如果想获取到父类的字段呢?getSuperclass()
可以解决
Field[] fields = user.getSuperclass().getDeclaredFields();
//和之间的for循环一样,输出:
public java.lang.String org.example.relect.entity.People.publicFiled
private java.lang.String org.example.relect.entity.People.privateFiled
getField(String name)
获取指定名称的公共字段
getDeclaredField(String name)
:获取指定名称的字段,无论其访问级别如何
尝试获取一个不存在的属性会出现什么问题呢?
可以从图下看出:在编译中,并没有报错,但是在执行的时候确报错了。
原因:通过反射获取字段的过程是在运行时动态执行的,所以在编译的阶段,无法进行错误的检测和捕获。
给属性设置值
我们在User实体中加入一个私有静态属性 private static final int staticFiled = 10;
尝试获取到这个值,此时出现了IllegalAccessException
异常。
此时需要field.setAccessible(true)
,设置访问权限即可,
如果属性还使用了final关键字进行修饰。那么是否可以修改呢?
从下面的试验可以看出,即使是使用final关键字进行修改过的属性,也是可以修改的。
Class<?> userClass = Class.forName("org.example.reflect.entity.User");
Constructor<?> constructor = userClass.getDeclaredConstructor(String.class, String.class);
Object obj = constructor.newInstance("张三", "男");
Field field = userClass.getDeclaredField("sex");
field.setAccessible(true);
field.set(obj, "女生");
System.out.println(field.get(obj));
输出:
女生
获取方法
获取方法其实和获取属性是一样的,就不一一展示出来了。
getMethods()
获取类及其父类中所有公共方法,包括继承的公共方法getDeclaredMethods()
获取类中声明的所有方法,包括公共、保护、和私有方法,但不包括继承的方法getMethod(String name, Class<?>... parameterTypes)
获取类中的特定公共方法,需指定方法名称和参数类型getDeclaredMethod(String name, Class<?>... parameterTypes)
获取类中声明的特定方法,无论其访问级别如何,需指定方法名称和参数类型。
总结
- 从上面可以看出带Declare 的就是表示获取所有的字段、属性。不带Declare,就是获取所有公共(public)的属性、方法。
例:getDeclaredField
表示获取所有的字段,getField()
,所有的公共字段。 - 反射可以绕过访问控制检查,不管是用private,还是final关键字进行修饰,都可以获取到,以及进行修改,这也破坏了封装性,所以在使用的时候需要格外谨慎使用。
参考资料
https://segmentfault.com/u/zhaokaiqiang
https://space.bilibili.com/19260126?spm_id_from=333.788.0.0
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。