1. 反射
定义:程序在运行状态中,对于任意一个类,都可以知道这个类的所有属性和方法;对于任意一个对象,都能够调用他的任意方法和属性
加载流程:假设我们写了一个User类,这个User类加载进内存前
jvm会为这种类创建唯一的对应的Class类实例,注意这里的Class
是一个类型,所有User类都共享同一个对应的Class类,这个Class类包含包名,类名,属性,方法等种种信息
2.Class类
获取Class有三种方法
Class cls = User.class; //通过类名.class
Class cls = user.getClass(); //通过实例.getClass()
Class cls = Class.forName("User"); //通过Class.forName("全限定类名")
- Class实例是唯一的存在,所以上面获取的Class都是同一个。但这注意 instance of 匹配的是本类及其子类,而Class只匹配本类,不包括子类
Class常用方法
方法 | 解释 |
---|---|
newInstance() | 创建由此类对象表示的类的新实例 |
forName(String className) | 返回与给定字符串名称的类或接口相关联的类对象 |
getClassLoader() | 返回类的类加载器 |
getPackage() | 获取此类的包 |
getResourceAsStream(String name) | 查找具有给定名称的资源(返回类型为InputStream) |
getDeclaredField(Srting name) | 反映此表示的类或接口的指定已声明字段类对象 |
getDeclaredMethod(String name, Class<?>... p) | 返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 类对象 |
getDeclaredConstructor(Class<?>... p) | 返回Constructor对象 |
- Class.newInstance()只能调用public的无参数构造方法
3. Field类
获取字段的方法
方法名 | 解释 |
---|---|
getField(String name) | 根据字段名获取某个public的field(包括父类) |
getDeclaredField(String name) | 根据字段名获取当前类的某个field(不包括父类) |
getFields() | 获取所有public的field,返回数组(包括父类) |
getDeclaredFields() | 获取当前类的所有field,返回数组(不包括父类) |
- 其中,没有Declared的能获取父类,本类public的field,而有Declared的只能访问本类的所有field
字段常用方法
方法 | 解释 |
---|---|
get(Object obj) | 返回该所表示的字段的值Field ,指定的对象上 |
set(Object obj, Object value) | 将指定对象参数上的此 Field对象表示的字段设置为指定的新值 |
getName() | 返回由此Field对象表示的字段的名称 |
getType() | 返回一个 类对象标识了此表示的字段的声明类型 Field对象 |
getModifiers() | 返回由该 Field对象表示的字段的Java语言修饰符,作为整数 |
操作(这里准备了UserExtends类)
public class UserExtends {
private int id = 1;
public String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
具体操作
public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Class cls = UserExtends.class;
//可以获取字段
System.out.println(cls.getDeclaredField("id"));
System.out.println("----------------------------------");
//可以获取字段的名字,类型,修饰符
Field f = cls.getDeclaredField("id");
System.out.println(f.getName());
System.out.println(f.getType());
int m = f.getModifiers();
System.out.println(Modifier.isPrivate(m)); //Modifiers是一个类,不同数值代表不同修饰符
System.out.println("-------------------------------------");
//获取私有字段的值,需要改变一些方法 1. 字段改为public 2.用f.setAccessible(true);
//SecurityManager,可以防止private属性访问,所以不用说设置private包装那有什么用了
f.setAccessible(true);
UserExtends ue = new UserExtends();
ue.setId(2);
//获取参数对象的字段值
Object value = f.get(ue);
System.out.println(value);
//修改参数对象的字段值
f.set(ue, 3);
System.out.println(ue.getId());
}
输出
private int reflectionfield.UserExtends.id
----------------------------------
id
int
true
-------------------------------------
2
3
4. Method
获取方法的方法
方法 | 解释 |
---|---|
getMethod(name, Class...) | 获取某个public的Method(包括父类) |
getDeclaredMethod(name, Class...) | 获取当前类的某个Method(不包括父类) |
getMethods() | 获取所有public的Method(包括父类) |
getDeclaredMethods() | 获取当前类的所有Method(不包括父类) |
常用方法
方法 | 解释 |
---|---|
getName() | 返回方法名称 |
getReturnType() | 返回方法返回值类型 |
getParameterTypes() | 返回方法的参数类型 |
getModifiers() | 返回方法的修饰符,返回一个整形 |
invoke(Object obj, Object... args) | 在具有指定参数的 方法对象上调用此 方法对象表示的底层方法 |
操作(准备了PersonExtends类,里面有两个方法)
public class PersonExtends extends Person{
public int getScore(String type) {
return 99;
}
private int getGrade(int year) {
return 1;
}
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取类类型
Class cls = PersonExtends.class;
//获取方法,第一个参数为方法名,第二个为参数类型的类类型
System.out.println(cls.getDeclaredMethod("getGrade", int.class));
//方法的基本信息
Method md = cls.getDeclaredMethod("getGrade", int.class);
md.getName();
md.getReturnType();
md.getParameters(); //返回的是Class数组
md.getModifiers();
//invoke第一个参数是实例对象,对二个填写参数,就是正常调用时方法的参数
PersonExtends ps = new PersonExtends();
System.out.println( md.invoke(ps,123) );
// 静态方法,第一个参数对象就写null
Method med = Integer.class.getMethod("parseInt", String.class);
System.out.println( med.invoke(null, "123"));
//本类的私有方法
//m.setAccessible(true);
//任然遵循多态,反射父类方法,方法传入子类对象,调用的是子类方法
}
private int reflectionmethod.PersonExtends.getGrade(int)
1
123
- 方法支持多态,反射父类方法,方法传入子类对象,调用的是子类方法
5. Constructor
获取构造方法也是那四种,就不写了
操作(准备了User类,里面含有无参及有参构造函数)
public class User {
public int id = 1;
public String name = "Howl";
public User(){
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//Class的newInstance只支持public的无参构造函数
User user = User.class.newInstance();
System.out.println(user.getId());
System.out.println(user.getName());
//与Method不同,这里的Constructor直接返回一个实例对象
//参数直接填写参数的类类型,实现重载
Constructor cst = User.class.getConstructor(int.class,String.class);
User user1 = (User) cst.newInstance(2,"Howlet");
System.out.println(user1.getId());
System.out.println(user1.getName());
}
1
Howl
2
Howlet
- 构造函数不支持多态,只能调用本类的构造函数
6. 反射实现动态代理
在编译时期不写接口的实现类,反而在运行时期创建接口的实例,这就叫做动态代理
interface类型的变量总是通过向上转型并指向某个实例,在反射中有体现,比如有个接口叫User,某个实现了接口的类叫做UserImp,那么他们转型可以这样写
User user = new UserImp();
具体还可以了解代理模式,请戳这里 代理模式