学习笔记——反射

1. 反射

在运行期动态获取类中的属性方法等成员。

  1. 降低耦合。

  2. 弊端:消耗内存

  3. 要适当使用,不能过度使用。

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方法
   
   @Override
   public String toString() {
       return "User{" +
               "age=" + age +
               ", name='" + name + '\'' +
               '}';
  }
}
// 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 "返回值";
  }

   @Override
   public String toString() {
       return "Stu{" +
               "name='" + name + '\'' +
               ", age=" + age +
               '}';
  }
}

再通过反射,逐步对其构造器和方法进行封装破坏。

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 注意点

  1. 找成员方法的方式

    1. 方法名

    2. 方法的参数类型。

  2. 找构造方法

    1. 参数类型。

  3. 找成员变量

    1. 变量名即可。

2.2 获取方法分类

  1. getXXXXs:获取所有本类父类公有方法

  2. getXXXX:获取指定方法名,参数类型的本类父类公有方法

  3. getDexxx:获取本类私有和公有的方法

2.2.1 调用方法步骤

  1. 获取字节码对象

  2. 根据字节码对象获取指定方法对象

  3. 通过方法对象,invoke(原始方法所属对象,原始方法实参)调用原始方法,

    1. 如果方法是私有的,需要设置可以访问,setA…..(true)

    2. 如果调用的不是方法,而是构造方法,不是用invoke是用newInstance(实参)

    3. 掌握怎么调用公有方法的套路就行。

如果调用的不是方法,而是构造方法,不是用invoke是用newInstance(实参)

posted @ 2021-12-03 20:17  Black空我  阅读(174)  评论(0)    收藏  举报