反射的概述
反射是一个非常重要的知识点,它是动态语言的关键。
动态语言就是不写死,而是根据程序的具体执行情况做出不同的变化。
反射概念很抽象
日常生活中的反射:照镜子可以反射物体的形态,镜子中可以看到实物的虚像,假说我们从镜子中看天空,我们则可以从镜中看到取天空的颜色,云彩等。
程序中的反射是通过某些信息(对象、类名 “java.lang.String”)可以映射到类,在程序运行期间获取类的相关信息。
反射的工作都是在程序运行期间进行的
反射具体操作简单来说就是在程序运行期间,动态获取类的信息,从而完成某些操作。常规情况下,是通过类创建对象,反射就是将其进行反转,通过对象来获取类的信息。
Class 类
Class 类是反射的源头,反射就是获取某些类的信息,抽象出一个对象来表示类的信息。
Class 就是用来创建这些对象的类(用来表示 反射获取的类的信息)。
Class 的实例化对象是专门用来描述其他类,Class 是专门用来描述其他类的类。
如何获取 Class 的实例化对象?
一共有 3 种方式,都跟目标类有关系。
public static void main(String[] args) {
try {
// 1、 通过类名获取
Class clazz = Class.forName("java.lang.String");
System.out.println(clazz);
// 拿到接口
Class<?> clazz1 = Class.forName("java.io.Serializable");
System.out.println(clazz1);
// 2、 类字面量获取
Class clazz2 = String.class;
System.out.println(clazz2);
// 3、通过实例化对象获取
String str = new String();
Class clazz3 = str.getClass();
System.out.println(clazz3);
/**
* 三种方式获取的对象都是同一个对象
*
*/
System.out.println(clazz == clazz2);
System.out.println(clazz2 == clazz3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
获取类的结构
这里我们创建一个User类
public class User {
private Integer id;
private String name;
private Integer age;
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public User(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
}
这里我添加了三个构造器
public static void main(String[] args) {
// 通过字面量获取目标类
Class clazz = User.class;
/**
* Constructor 是 Java 反射包提供的一个类,
* 它的作用是专门用来描述其他类的构造器的,
* 一个 Constructor 实例化对象映射的就是某个类的某个构造函数。
*/
Constructor[] constructors = clazz.getConstructors();
// 遍历输出拿到的构造
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
}
结果:
除了通过上面的获取类所有的构造器,也可以单个获取
try {
// 无参构造
Constructor constructor = clazz.getConstructor();
System.out.println(constructor);
// 有参构造
Constructor constructor1 = clazz.getConstructor(Integer.class);
System.out.println(constructor1);
Constructor constructor2 = clazz.getConstructor(Integer.class,String.class,Integer.class);
System.out.println(constructor2);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
获取接口
在User类上继承接口
public class User implements Serializable,MyInterface {
...
}
public static void main(String[] args) {
Class clazz = User.class;
// 将拿到的接口放入数组
Class[] interfaces = clazz.getInterfaces();
// 遍历输出
for (Class anInterface : interfaces) {
System.out.println(anInterface);
}
}
结果:
获取属性(成员变量)
getFields() : 获取的是类中的公有成员变量(包括从父类继承过来的公有成员变量),public 修饰的
getDeclaredFields() : 获取的是类中全部成员变量(跟修饰无关,但是不包含父类中的成员变量)
getField(String name): 根据名称获取公有成员变量(包含父类继承过来的)
getDeclaredField(String name): 根据名称获取成员变量(不包含父类继承过来的)
创建一个父类Person
public class Person {
public String gender;
}
子类继承父类
public class User extends Person implements Serializable,MyInterface {
private Integer id;
private String name;
public Integer age;
public String hobby;
....
}
public static void main(String[] args) {
System.out.println("getFields()方法");
Class clazz = User.class;
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("------------------------------------------------");
System.out.println("getDeclaredFields() 方法");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("------------------------------------------------");
System.out.println("getField(String name) 方法");
try {
Field age = clazz.getField("age");
System.out.println(age);
Field hobby = clazz.getField("hobby");
System.out.println(hobby);
// 父类的公有成员变量
Field gender = clazz.getField("gender");
System.out.println(gender);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
/**
* getDeclaredField(String name) 同上,取本类的所有成员变量,无关权限修饰符。
*/
}
结果:
获取属性后,我们可以通过反射给是属性赋值、取值。当然给属性赋值,取值的前提是有这个对象。
Class clazz = User.class;
try {
Field id= clazz.getDeclaredField("id");
//通过反射拿到无参构造
Constructor<User> constructor = clazz.getConstructro(null);
User user = constructor.newInstance(null);
//如果类里的成员变量访问权限为公有,可通过直接赋值,取值。如果是私有的成员变量则需 加上 id.setAccessible(true); 后再赋值 ,取值 (暴力反射)
id.set(user,1);
//Object o = id.get(user);
} catch (Exception e) {
e.printStackTrace();
}
获取方法
public class Test2 {
public static void main(String[] args) {
Class clazz = User.class;
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
getMethods 方法获取目标类中的所有公有方法,包含从父类继承过来的公有方法。
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
getDeclaredMethods 方法获取目标类中的所有方法,不包含从父类继承过来的方法。
通过反射调用方法
public static void main(String[] args) throws Exception {
Class clazz = User.class;
Method getId = clazz.getDeclaredMethod("getId", null);
//通过反射创建对象
User user = (User) clazz.getConstructor(null).newInstance(null);
//赋值 调用set方法
Method setId = clazz.getMethod("setId", Integer.class);
setId.invoke(user, 22);
//取值
System.out.println(getId.invoke(user, null));
}
反射机制在实际开发中最主要的三个应用构造器、方法、成员变量。
Spring 框架,IoC 容器,自动创建对象,不需要开发者手动创建对象,程序根据配置文件自动生成程序需要的各种对象。
1、通过构造器创建对象
2、给对象的属性赋值