学习笔记——反射
在运行期动态获取类中的属性方法等成员。
-
降低耦合。
-
弊端:消耗内存
-
要适当使用,不能过度使用。
1.1 字节码获取方式
使用反射需要先获取字节码对象,获取字节码对象三种方式:
// Class 类
public class User {
// Field 属性
private Integer age;
private String name;
//Constructor 构造器
public User() {
}
public User(Integer age, String name) {
this.age = age;
this.name = name;
}
//method 方法
public void login(){
System.out.println("登陆");
}
public void login(String name){
System.out.println("登陆"+name);
}
public void login(int age){
System.out.println("登陆"+age);
}
private void sleep(){
System.out.println("睡觉!!!!!!");
}
//省略get、set方法
// 1.
Class u1= User.class;
// 2. 最终要提供user类, 否则还是进异常
Class<?> u2 = Class.forName("com._1reflect.User");
// 3.
User user = new User();
Class<? extends User> u3= user.getClass();
1.2 获取对象
-
getXXX 返回单个对象
-
getXXXs 返回多个数组对象
-
getMethods 返回本类和父类公有的方法
-
getDeclaredxxxxs() 返回本类所有方法
1.2.1获取Method对象
可以通过getName获取方法名,寻找所需要的方法。
public class UserTest02 {
public static void main(String[] args) throws Exception{
Class<User> u = User.class;
Method[] ms = u.getMethods();
for (Method m : ms) {
System.out.println(m.getName());
if ("login".equals(m.getName())){
System.out.println("找到login");
}
}
System.out.println("--------------------");
Method[] dms = u.getDeclaredMethods();
for (Method dm : dms) {
System.out.println(dm);
}
Method login = u.getMethod("login",null);
System.out.println(login);
System.out.println("------------");
Method login2 = u.getMethod("login",String.class);
System.out.println(login2);
}
}
其中getMethods()是查找所有的公共方法,包括继承的方法
而getDeclaredMethods()是返回该类或接口中,指定声明的所有方法
对于属性、构造器等同获取Method对象的流程类似。
1.3 私有性封装破坏
1.3.1 对方法的封装破坏
public static void privateMethod() throws Exception {
// 获取字节码对象
Class<User> c = User.class;
// 获取指定的private void sleep()
Method m = c.getDeclaredMethod("sleep");
// 调用原始方法
m.setAccessible(true); // 强制调用 破环封装
m.invoke(c.newInstance());
}
我们可以调用setAccessible(true)将我们的方法通过反射形式破坏原有的封装性,将方法设置为可访问,从而通过反射创建对象,调用我们的私有方法。
属性、构造器同理
1.3.2 对构造器的封装破坏
首先创建一个构造器为私有的类,例如:
public class Stu {
private String name;
private Integer age;
private Stu(){}
private Stu(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("私有de构造器");
}
private String method(int age){
System.out.println("私有de方法");
return "返回值";
}
再通过反射,逐步对其构造器和方法进行封装破坏。
public class StuTest01 {
public static void main(String[] args) throws Exception{
// Class<Stu> stuClass = Stu.class;
// 1. 获取字节码对象
Class<?> c = Class.forName("com._1reflect.privat.Stu");
// 2. 获取指定方法
Method m = c.getDeclaredMethod("method", int.class);
// 3. 调用原始方法 obj: 原始方法所属对象。 对象创建不了。。反射创建对象
// 反射创建对象步骤
// 3.1 找到私有的构造方法
// Constructor<?> d = c.getDeclaredConstructor();
Constructor<?> d = c.getDeclaredConstructor(String.class, Integer.class);
// 3.2 设置可访问
d.setAccessible(true);
// 3.3 创建对象
Object o = d.newInstance("bk", 123);
System.out.println(o);
// 3.4 设置方法可以访问
m.setAccessible(true);
m.invoke(o,100);
}
}
2. 小结
2.1 注意点
-
找成员方法的方式
-
方法名
-
方法的参数类型。
-
-
找构造方法
-
参数类型。
-
-
找成员变量
-
变量名即可。
-
2.2 获取方法分类
-
getXXXXs:获取所有本类父类公有方法
-
getXXXX:获取指定方法名,参数类型的本类父类公有方法
-
getDexxx:获取本类私有和公有的方法
2.2.1 调用方法步骤
-
获取字节码对象
-
根据字节码对象获取指定方法对象
-
通过方法对象,invoke(原始方法所属对象,原始方法实参)调用原始方法,
-
如果方法是私有的,需要设置可以访问,setA…..(true)
-
如果调用的不是方法,而是构造方法,不是用invoke是用newInstance(实参)
-
掌握怎么调用公有方法的套路就行。
-
如果调用的不是方法,而是构造方法,不是用invoke是用newInstance(实参)

浙公网安备 33010602011771号