Java-Day-36
通过反射获取类的结构信息
第一组:java.lang.Class 类
- 以下说的包含本类和父类 —— 也包括超类等
- 方法属性之类的若是输出时不加 .getName,则都是输出:com.zyz.Zyz()
public class test {
public static void main(String[] args) {
}
@Test
public void api() throws ClassNotFoundException {
// 获取Class对象
Class<?> zyzClass = Class.forName("com.zyz.Zyz");
// getName:获取全类名
System.out.println(zyzClass.getName()); // com.zyz.Zyz
// getSimpleName:获取简单类名
System.out.println(zyzClass.getSimpleName()); // Zyz
// getFields:获取所有public修饰的属性,包括本类以及父类
Field[] fields = zyzClass.getFields();
for (Field field : fields) {
System.out.println("本类即父类的public属性: " + field.getName());
}
// getDeclaredFields:获取本类中所有的属性,无视修饰符
Field[] declaredFields = zyzClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("获取本类中所有的属性(无视修饰符): " + declaredField.getName());
}
// getMethods:获取所有public修饰的方法,包含本类以及父类的
Method[] methods = zyzClass.getMethods();
for (Method method : methods) {
System.out.println("所有的public修饰的方法: " + method.getName());
}
// getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = zyzClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法: " + declaredMethod.getName());
}
// getConstructors:获取所有public修饰的构造器,只限本类
Constructor<?>[] constructors = zyzClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("本类所有public修饰的构造器: " + constructor.getName()); // com.zyz.Zyz
// 这里只是输出名字,因为是构造器,所以就是类名路径
}
// getDeclaredConstructors:获取本类的任何修饰符的构造器
Constructor<?>[] declaredConstructors = zyzClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("本类所有构造器: " + declaredConstructor.getName()); // com.zyz.Zyz
// 这里只是输出名字,因为是构造器,所以就是类名路径
}
// getPackage:以Package形式返回包全路径
System.out.println(zyzClass.getPackage()); // package com.zyz
// getSuperclass:以Class形式返回父类的全路径
System.out.println(zyzClass.getSuperclass()); // class com.zyz.Person
// getInterfaces:以Class[]形式返回接口信息
Class<?>[] interfaces = zyzClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("接口信息: " + anInterface); // interface 接口路径
}
// getAnnotations:以Annotation[]形式返回注解信息
Annotation[] annotations = zyzClass.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息: " + annotation);
}
}
}
class Person {
public String country;
public void hi() {}
}
interface ABC {}
interface CBA {}
class Zyz extends Person implements ABC, CBA{
// 四种不同访问权限的测试
public String name;
protected int age;
String job;
private double sal;
// 方法
public void m1(){}
protected void m2(){}
void m3(){}
private void m4(){}
public Zyz() {
}
}
第二组:java.lang.reflect.Field 类
- getModifiers:以 int 形式返回修饰符
- 默认修饰符是 0,public 是 1, private 是 2, protected 是 4,static 是 8,final 是 16
- 类似 public static 这样多个的话就是相加,也就是 9
- getType:以 Class 形式返回类型
- getName:返回属性名
// getDeclaredFields:获取本类中所有的属性,无视修饰符
Field[] declaredFields = zyzClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("获取本类中所有的属性(无视修饰符): " + declaredField.getName()
+ "该属性修饰符值为:" + declaredField.getModifiers()
+ "该属性的类型为:" + declaredField.getType());
}
第三组:java.lang.reflect.Method 类
- getModifiers:以 int 形式返回修饰符
- 默认修饰符是 0,public 是 1, private 是 2, protected 是 4,static 是 8,final 是 16
- 类似 public static 这样多个的话就是相加,也就是 9
- getReturnType:以 Class 形式获取返回类型
- getName:返回方法名
- getParameterTypes:以 Class[] 返回参数类型数组
// getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = zyzClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类中所有方法: " + declaredMethod.getName()
+ "该方法的访问修饰符为:" + declaredMethod.getModifiers()
+ "该方法返回的类型为:" + declaredMethod.getReturnType());
// 输出当前这个方法的形参数组情况
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("该方法的形参类型为:" + parameterType);
}
}
第四组:java.lang.reflect.Constructor 类
-
getModifiers:以 int 形式返回修饰符
-
getName:返回构造器名 ( 全类名 )
-
getParameterTypes:以 Class[] 返回参数类型数组
-
此处三个方法在上面都有类似例子
通过反射创建对象
-
方式一:调用类中的 public 修饰的无参构造器
-
方式二:调用类中的指定构造器
-
Class 类相关方法
- newInstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class ... clazz): 根据参数列表,获取对应的 public 构造器对象
- getDecalaredConstructor(Class ... clazz):根据参数列表,获取所有的对应的构造器对象
-
Constructor 类相关方法
- setAccessible:暴破
- newInstance(Object ... obj):调用构造器
案例
class User {
private String name = "zyz";
private int age = 100;
public User() { // public的无参构造器
}
public User(String name) { // public的有参构造器
this.name = name;
}
private User(String name, int age) { // private的有参构造器
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
-
通过反射创建某类的对象,要求该类中必须有 public 的无参构造器
// 1. 先获取到User类的Class对象 Class<?> userClass = Class.forName("com.zyz.User"); // 2. 通过public的无参构造器创建实例 Object o = userClass.newInstance(); System.out.println("无参的赋最初值的构造器 ——> " + o); // 无参的赋最初值的构造器 ——> User{name='zyz', age=100}
-
通过调用某个特定构造器的方式,实现创建某类的对象
- public 的有参构造器
- private 的有参构造器
// 3. 通过public的有参构造器创建实例 /* 此时的constructor就是: public User(String name) { // public的参数列表为一个String的有参构造器 this.name = name; } */ // 3.1. 先获取对应的构造器 Constructor<?> constructor = userClass.getConstructor(String.class); // 所有String类型的class对象 // 3.2. 再创建实例并传入形参 Object babaName = constructor.newInstance("霸霸"); System.out.println("构造器传入新name ——> " + babaName); // 构造器传入新name ——> User{name='霸霸', age=100} // 4. 通过非public的有参构造器创建实例 ——> 传统方法不行,但反射可以 // 4.1. 得到private的构造器对象 Constructor<?> constructor1 = userClass.getDeclaredConstructor(String.class, int.class); // 4.2. 如果是私有的private,就需要暴破 —— 暴力破解,使用反射可以访问private构造器/方法/属性 constructor1.setAccessible(true); // 4.3. 创建实例 Object zyz = constructor1.newInstance("老朱", 125); System.out.println("private的构造器 ——> " + zyz); // private的构造器 ——> User{name='老朱', age=125}
通过反射访问类中的成员
- 静态可不指明对象,Declared 拿所有成员,私有还是要暴破
属性
- 根据属性名获取 Field 对象
- 暴破,私有属性也可以访问到
- 访问
- f.set(o, 值); // o表示对象
- f.get(o); // o表示对象
- 注意:如果是静态属性,则 set 和 get 中的参数 o 可以写成 null
public class test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 1. 得到User类对应的Class对象
Class<?> userClass = Class.forName("com.zyz.User");
// 2. 创建对象
Object o = userClass.newInstance(); // o 的运行类型就是 User
// 3. 使用反射得到属性对象 ———— public
Field pubName = userClass.getField("name");
System.out.println(pubName);
pubName.set(o, "zyz");
System.out.println("加上共有属性的名称" + o);
System.out.println(pubName.get(o));
// 4. 使用反射操作age属性 ———— private static
Field priAge = userClass.getDeclaredField("age");
// 4.1 对私有的只能用Declared才能包含在内,但想使用或者更改,还是需要暴破,否则无法进行操作
priAge.setAccessible(true);
// priAge.set(o, 100);
priAge.set(null, 250); // 对象位置写null也可以,因为age是静态属性
// ———— 静态是属于所有对象的,在类加载的时候就已经有了,所以null也无妨,写上 o 的写法是因为对象也是可以直接操作静态的
System.out.println("再加上私有属性的年龄" + o);
System.out.println("通过 o 获取:" + priAge.get(o));
System.out.println("因为静态,所以对象部分写null也可获取:" + priAge.get(null));
}
}
class User {
public String name;
private static int age;
public User() {
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
方法
- 根据方法名和参数列表获取 Method 方法对象:Method m = clazz.getDeclaredMethod(方法名, XX.class);
- 加上 Declared 可以访问所有的,去掉就只能是 public 共有的了
- 获取对象:Object o = clazz.newInstance();
- 暴破:m.setAccessible(true);
- 访问:Object returnValue = m.invoke(o, 实参列表);
- 就算方法有指定返回值也无用,都是统一用 Object 接收的
- 注意:如果是静态方法,则 invoke 的参数 o 可以写成 null
public class test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 1. 得到User类对应的Class对象
Class<?> userClass = Class.forName("com.zyz.User");
// 2. 创建对象
Object o = userClass.newInstance(); // o 的运行类型就是 User
// 3. 调用public的hello方法
// Method hello = userClass.getMethod("zyz, String.class");
// 3.1 得到hello方法对象
Method hello = userClass.getDeclaredMethod("hello", String.class); // 有参的话就要加参数
// 3.2 调用
hello.invoke(o, "zhuYaZhu");
// 4. 调用私有的静态方法
// 4.1 得到say方法对象
Method say = userClass.getDeclaredMethod("say", String.class, int.class, char.class);
// 4.2 私有 —— 暴破
say.setAccessible(true);
// 4.3 调用
System.out.println(say.invoke(o, "zyz", 250, '男'));
// 因为静态所以可以用null
Object reValue = say.invoke(null, "zhuYaZhu", 100, '男');
// 运行类型还是String(与方法定义的一致),但返回时还是编译类型Object接收
System.out.println("运行类型:" + reValue.getClass()); //
}
}
class User {
public String name;
private static int age;
public User() {
}
private static String say(String n, int a, char c) { // 私有的静态方法
return n + "的年龄:" + a + "————" + c;
}
public void hello(String name) { // 普通方法
System.out.println("hello" + name);
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
章节练习
-
通过反射修改私有成员变量,前提:
class PrivateTest { private String name = "zyz"; public String getName(){ return name; } }
- 解:
public class test { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("com.zyz.PrivateTest"); Object o = cls.newInstance(); Method getName = cls.getDeclaredMethod("getName"); System.out.println(getName.invoke(o)); Field name = cls.getDeclaredField("name"); name.setAccessible(true); name.set(o, "gaiZyz"); System.out.println(getName.invoke(o)); } }
-
利用反射和 File 完成以下功能
- 利用 Class 类的 forName 方法得到 File 类的 class 对象
- 在控制台打印 File 类的所有构造器
- 通过 newInstance 的方法创建 File 对象,并创建 E:\mynew.txt 文件
- 解
public class test { public static void main(String[] args) throws Exception { // 1. 得class对象 Class<?> cls = Class.forName("java.io.File"); Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println("File 的构造器:" + declaredConstructor); // getName 后就都是 java.io.File 了 } // 单独的得到自己要用的构造器:public java.io.File(java.lang.String) // 2. 拿要用的构造器 Constructor<?> declaredConstructor = cls.getDeclaredConstructor(String.class); String filePath = "E:\\mynew.txt"; // 内存 // 3. 构造器拿到File对象 Object file = declaredConstructor.newInstance(filePath); // 通过有参构造器进行实例化 —— 创建File对象 // 4. 拿对象中要用的方法 Method createNewFile = cls.getDeclaredMethod("createNewFile"); // 5. 调用方法 createNewFile.invoke(file); // 注意创建文件的正常写法: // File file = new File("E:\\mynew.txt"); // 内存 // file.createNewFile(); // 真正创建了文件的方法 } }