一、概述
反射:指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。也就是可以获取正在运行的Java对象。
反射作为Java中一个强大的工具,不仅可以很方便创建灵活的代码,而且对一些其他第三方代码可以进行增强。
其主要功能主要有:
1、可以判断运行时对象所属的类
2、可以判断运行时对象所拥有的成员属性和方法
3、生成动态代理
光看概念有些晦涩,我们先说一个简单的应用反射的例子:
public class Item {
private String id;
private String name;
private double price;
public Item(String id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public String getId() { return id; }
public String getName() { return name; }
public double getPrice() { return price; }
}
上面这部分代码不管出于什么目的,其成员属性只在new对象时接收,其他情况一律修改不了。我们可以通过反射来修改类的私有属性。
public static void main(String[] args) throws Exception {
Item item = new Item("1", "a", 1.0);
Field field = item.getClass().getDeclaredField("id");
field.setAccessible(true);
field.set(item, "2");
System.out.println(item.getId()); // 2
}
运行可以发现,我们已经把其对象的id值更新到了2。
二、具体功能实现
1、获取类并创建其对象:反射中获取类通常有三种方法
Class clz1 = Class.forName("java.lang.String"); // 1.通过包和类名
Class clz2 = String.class; // 2.直接通过类名
String str = new String();
Class clz3 = str.getClass(); // 3.根据对象(运行时类)
clz1.newInstance();
clz2.newInstance();
其他方法名 | 含义 |
---|---|
getEnclosingClass() | 返回底层类的立即封闭类 |
getDeclaredClasses() | 返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口 |
getDeclaringClass() | 如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类 |
2、获取类的属性
方法名 | 含义 |
---|---|
getFields() | 获取类的所有public属性,包括其父类 |
getField() | 获取某一个属性 |
getDeclaredFields() | 获取类的所有声明的字段,不包括父类 |
getDeclaredField() | 获取某一个属性 |
3、获取类的方法
方法名 | 含义 |
---|---|
getMethods() | 获取类的所有public方法,包括其父类 |
getMethod() | 获取某一个方法 |
getDeclaredMethods() | 获取类的所有声明的方法,不包括父类 |
getDeclaredMethod() | 获取某一个方法 |
getConstructors() | 获取访问权限是public的所有构造方法 |
getConstructor() | 获取某一个构造方法 |
getDeclaredConstructors() | 获取类的所有构造方法 |
getConstructors() | 获取某一个构造方法 |
4、总结
- 反射里的Class, Field, Method, Constructor必须结合对象使用,除非是静态的。
- 获取非public对象须用类似getDeclaredMethod()而不是getMethod()。
- Field和Method都能设置accessible(true),之后能访问到私有权限。
三、一个简单的例子
下面是一个Person类,其中有两个私有属性id和name;拥有一个构造方法和一个私有方法print;
- 还有一个public内部类BaseInfo:含有两个私有属性nation和bloodType;拥有一个构造方法和一个私有方法print;
- 还有一个private内部类FamilyInfo:含有两个私有属性nativePlace和address,拥有一个构造方法和一个私有方法print。
由于FamilyInfo为私有的,因此为了可以初始化其属性,在Person中添加了一个私有属性,并在构造方法中对其进行了初始化。
public class Person {
private String id;
private String name;
private FamilyInfo familyInfo;
public Person(String id, String name) {
this.id = id;
this.name = name;
this.familyInfo = this.new FamilyInfo("beijing", "beijing");
}
private void print() {
System.out.println("Person{id=" + id + ", name=" + name + "}");
}
public class BaseInfo {
private String nation;
private String bloodType;
public BaseInfo(String nation, String bloodType) {
this.nation = nation;
this.bloodType = bloodType;
}
private void print() {
System.out.println("BaseInfo{nation=" + nation + ", bloodType=" + bloodType + "}");
}
}
private class FamilyInfo {
private String nativePlace; // 籍贯
private String address; // 住址
private FamilyInfo(String nativePlace, String address) {
this.nativePlace = nativePlace;
this.address = address;
}
private void print() {
System.out.println("FamilyInfo{nativePlace=" + nativePlace + ", address=" + address + "}");
}
}
}
下面的Main方法中,初始化了Person类,通过一个getFiledFromPerson()
方法打印出其私有属性、私有方法、内部类私有属性及方法。
public class Main {
private static void getFiledFromPerson(Person person, Person.BaseInfo baseInfo) {
try {
// 获取外部类的私有属性
Field idField = person.getClass().getDeclaredField("id");
idField.setAccessible(true);
String id = (String) idField.get(person);
System.out.println("id:" + id);
Field familyInfoField = person.getClass().getDeclaredField("familyInfo");
familyInfoField.setAccessible(true);
Object familyInfo = familyInfoField.get(person);
// 获取外部类的私有方法
Method printPersonMethod = person.getClass().getDeclaredMethod("print");
printPersonMethod.setAccessible(true);
printPersonMethod.invoke(person);
// 获取内部类的私有属性
Class baseInfoClz = Class.forName("demo.Person$BaseInfo");
Field nationField = baseInfoClz.getDeclaredField("nation");
nationField.setAccessible(true);
String nation = (String) nationField.get(baseInfo);
System.out.println("nation:" + nation);
Class familyInfoClz = Class.forName("demo.Person$FamilyInfo");
Field field = familyInfoClz.getDeclaredField("address");
field.setAccessible(true);
String address = (String) field.get(familyInfo);
System.out.println("address:" + address);
// 获取内部类的私有方法
Method printBaseInfoMethod = baseInfoClz.getDeclaredMethod("print");
printBaseInfoMethod.setAccessible(true);
printBaseInfoMethod.invoke(baseInfo);
Method printFamilyInfoMethod = familyInfoClz.getDeclaredMethod("print");
printFamilyInfoMethod.setAccessible(true);
printFamilyInfoMethod.invoke(familyInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Person person = new Person("1001", "John"); // 初始化Person以及默认FamilyInfo
Person.BaseInfo baseInfo = person.new BaseInfo("han", "A"); // 初始化BaseInfo
getFiledFromPerson(person, baseInfo);
}
}