反射
认识反射
可以允许程序在运行过程中动态地获取类的信息(成员变量、方法、构造器等),还可以创建对象。

反射的步骤:
- 反射第一步:加载类,获取类的字节码:Class对象
- 获取类的构造器:Constructor对象
- 获取类的成员变量:Field对象
- 获取类的成员方法:Method对象
获取类
获取类的三种方式:
- 调用Class提供方法:public static Class forName(String package);
- Object提供的方法: public Class getClass(); Class c3 = 对象.getClass();
Java Cat public class Cat extends CatParent{ private String name; private Integer age; public String hobby; public Cat(){ System.out.println("++++公有构造+++++"); } public Cat(Integer age){ System.out.println("++++公有构造+++++"+age); } public Cat(String name,Integer age){ System.out.println("++++公有构造+++++"+name+"+"+age); } private Cat(String name){ System.out.println("++++私有构造+++++"+name); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby = hobby; } public String dance1(String a,Integer b){ return "跳舞1"; } public String dance2(String a,int b){ return "跳舞2"; } private String sing(){ return "唱歌"; } } |
Java CatParent public class CatParent { private String a; protected String b; public String c; public void a1(){ System.out.println("+++++++a1+++++++"); } private void a2(){ System.out.println("+++++++a2+++++++"); } protected void a3(){ System.out.println("+++++++a3+++++++"); } } |
Java System.out.println("----------------获取class对象---------------------"); System.out.println("----------------通过类名获取---------------------"); Class catClass1 = Cat.class; System.out.println(catClass1); System.out.println("----------------通过class.forname方法获取---------------------"); //不存在的类会抛出异常 //Class noExistClass2 = Class.forName("com.example._01reflect.Cat2"); Class catClass2 = Class.forName("com.example._01reflect.Cat"); System.out.println(catClass2); System.out.println("----------------通过对象的getClass方法获取获取---------------------"); Class CatClass3 = new Cat().getClass(); System.out.println(CatClass3); //同一个类的class对象全局只有一个,返回true System.out.println(catClass1 == catClass2); |
面试题
为什么返回的是true?
Java //同一个类的class对象全局只有一个,返回true System.out.println(catClass1 == catClass2); |
答:同一个类加载器下,每个类的Class对象在JVM中是唯一的单例。无论通过类名.class、Class.forName()还是对象getClass()获取的Class对象,只要属于同一个类,它们都指向JVM中同一块内存地址的Class实例,因此catClass1 == catClass2比较的是同一引用,结果为true。这体现了Java类加载机制的一致性。
获取类中的成分并对其进行操作
获取构造器的方法。

获取类构造器的作用:依然是初始化对象返回(就是创建对象)

Java System.out.println("----------------获取构造器---------------------"); System.out.println("----------------获取所有public的构造器---------------------"); Constructor[] constructors1 = catClass1.getConstructors(); for (Constructor constructor : constructors1) { System.out.println(constructor); } System.out.println("----------------获取所有构造器---------------------"); Constructor[] constructors2 = catClass1.getDeclaredConstructors(); for (Constructor constructor : constructors2) { System.out.println(constructor); } System.out.println("----------------获取特定参数的构造器(只能获取public)---------------------"); Constructor constructor1 = catClass1.getConstructor(Integer.class); System.out.println(constructor1); Constructor constructor2 = catClass1.getConstructor(String.class, Integer.class); System.out.println(constructor2); //不存在会抛异常 //Constructor notExistConstructor = catClass1.getConstructor(Long.class); //获取私有也会抛异常 //Constructor privateConstructors = catClass1.getConstructor(String.class); System.out.println("----------------获取特定参数的构造器---------------------"); Constructor privateConstructor = catClass1.getDeclaredConstructor(String.class); System.out.println(privateConstructor); System.out.println("----------------创建对象---------------------"); //要严格根据构造方法的参数类型和顺序进行传参 // Cat cat = (Cat) constructor1.newInstance("小花"); Cat cat1 = (Cat) constructor1.newInstance(18); // Cat cat = (Cat) constructor2.newInstance(18,"小花"); Cat cat2 = (Cat) constructor2.newInstance("小花", 18); System.out.println(cat1); System.out.println(cat2); System.out.println("----------------使用私有构造器创建对象---------------------"); //必须先设置权限 privateConstructor.setAccessible(true); Cat cat3 = (Cat) privateConstructor.newInstance(""); System.out.println(cat3); |
获取类的成员变量
Class提供了从类中获取成员变量的方法。

获取到成员变量的作用:依然是赋值、取值。

Java System.out.println("----------------获取成员变量---------------------"); System.out.println("----------------获取所有public的成员变量---------------------"); Field[] fields1 = catClass1.getFields(); for (Field field : fields1) { System.out.println(field); } System.out.println("----------------获取所有成员变量(不包含父类)---------------------"); Field[] fields2 = catClass1.getDeclaredFields(); for (Field field : fields2) { System.out.println(field); } System.out.println("----------------获取特定名称的成员变量(只能获取public)---------------------"); Field hobbyField = catClass1.getField("hobby"); System.out.println(hobbyField); //不存在会抛异常 // Field notExistField = catClass1.getField("hobby2"); //获取私有会抛异常 // Field privateField = catClass1.getField("name"); System.out.println("----------------获取特定名称的成员变量(不包含父类)---------------------"); Field privateNameField = catClass1.getDeclaredField("name"); System.out.println(privateNameField); //赋值和取值 System.out.println("兴趣爱好是"+cat1.getHobby()); hobbyField.set(cat1,"游泳"); System.out.println("兴趣爱好是"+cat1.getHobby()); //还可以通过反射获取字段值 System.out.println("兴趣爱好是"+hobbyField.get(cat1)); //私有字段的赋值和取值 privateNameField.setAccessible(true); System.out.println("名字是"+privateNameField.get(cat2)); privateNameField.set(cat2,"小花"); System.out.println("名字是"+privateNameField.get(cat2)); |
获取类的成员方法

成员方法的作用:依然是执行

Java System.out.println("----------------获取成员方法---------------------"); System.out.println("----------------获取所有public的成员方法---------------------"); Method[] methods1 = catClass1.getMethods(); for (Method method : methods1) { System.out.println(method); } System.out.println("----------------获取所有成员方法(不包含父类)---------------------"); Method[] method2 = catClass1.getDeclaredMethods(); for (Method method : method2) { System.out.println(method); } System.out.println("----------------获取特定名称的成员方法(只能获取public)---------------------"); //无参 Method getNameMethod = catClass1.getMethod("getName"); System.out.println(getNameMethod); //有参 Method setNameMethod = catClass1.getMethod("setName", String.class); System.out.println(setNameMethod); //多个参 Method dance1Method = catClass1.getMethod("dance1", String.class, Integer.class); System.out.println(dance1Method); //要注意基本数据类型和包装类的情况,基本数据类型也有class对象,不然会抛出异常 // Method dance2Method = catClass1.getMethod("dance2", String.class, Integer.class); Method dance2Method = catClass1.getMethod("dance2", String.class, int.class); System.out.println(dance2Method); //不存在会抛异常 //Method notExistDance3Method = catClass1.getMethod("dance3",String.class); //参数列表不对应也会抛异常 //Method wrongArgDance1Method = catClass1.getMethod("dance1",Long.class); //参数顺序不对应也会抛异常 //Method wrongArgDance1Method = catClass1.getMethod("dance1",Integer.class,String.class); //获取私有会抛异常 //Method privateSingMethod = catClass1.getMethod("sing"); System.out.println("----------------获取特定名称的成员方法(不包含父类)---------------------"); Method privatesingMethod = catClass1.getDeclaredMethod("sing"); System.out.println(privatesingMethod); System.out.println("----------------成员方法执行---------------------"); String result = (String) getNameMethod.invoke(cat1); System.out.println(result); //有参数 setNameMethod.invoke(cat2,"小花"); System.out.println(cat2.getName()); //多个参数 String dance1Result = (String) dance1Method.invoke(cat2, "小花", 18); System.out.println(dance1Result); //参数列表有误,会抛异常 // String dance2Result = (String) dance2Method.invoke(cat2, "小花", "b"); //调用私有方法会抛异常 privatesingMethod.setAccessible(true); String singResult = (String) privatesingMethod.invoke(cat2); System.out.println(singResult); |
作用、应用场景
反射的作用
- 基本作用:可以得到一个类的全部成分然后操作。(除了静态代码块和实例代码块)
- 最重要的用途是:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能。
Java 这里的代码要用jdk11运行 System.out.println("----------------反射可以绕过泛型检查---------------------"); List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); //不允许 //list.add(1); //使用反射 Field elementDataFiled = list.getClass().getDeclaredField("elementData"); elementDataFiled.setAccessible(true); Object[] elementData = (Object[]) elementDataFiled.get(list); elementData[0] = 123; System.out.println(list); |