Java 学习笔记7-反射

反射

反射即Reflection,指程序可以在运行期间拿到一个对象的所有信息,存在的意义是为了解决在运行时对实例不了解的情况下,如何调用其方法;

Class类

  • class(包括interface)的本质是数据类型Type

  • 获取一个类classClass实例的三种方法:

    • 直接通过一个类class的静态变量class获取;

        Class cls = String.class;
      
    • 若有一个实例变量,则可以通过该实例变量提供的getClass()方法获取;

        String s = "cunyu";
        Class cls = s.getClass();
      
    • 若知道一个类class完整类名,可以通过静态方法Class.forName()获取;

        Class cls = Class.forName("java.lang.String");
      
  • 动态加载

    JVM执行Java程序时,并非一次性将所有类class全部加载到内存中,而是需要用到类class时才加载;

访问字段

  • Class类提供的获取字段的方法:

    • Field getField(name):根据字段名获取某个publicfield(包括父类);

    • Field getDeclaredField(name):根据字段名获取当前类的某个field(不含父类);

    • Field[] getFields():获取所有publicfield(包括父类);

    • Field[] getDeclaredFields():获取当前类的所有field(不含父类);

      public class Main{
        public static void main(String[] args) throws Exception{
            // 通过Student类的静态变量获取Class实例
            Class stdClass = Student.class;
      
            // 获取public字段"score"
            System.out.println(stdClass.getField("score"));
      
            // 获取继承的public字段"name"
            System.out.println(stdClass.getField("name"));
      
            // 获取private字段"grade"
            System.out.println(stdClass.getDeclaredField("grade"));
        }
      }
      
      class Student extends Person{
        public int score;
        private int grade;
      }
      
      class Person{
        public String name;
      }
      
  • 利用反射获取类的字段信息;

      public final class String{
          private final byte[] value;
      }
    
      // 通过反射来获取String的字段的信息
      Field f = String.class.getDeclaredField("value");
      // 获取字段名
      f.getName();
      // 获取字段类型
      f.getType();
      // 获取字段修饰符
      int m = f.getModifiers();
      Modifier.isFinal(m); // true
      Modifier.isPublic(m); // false
      Modifier.isProtected(m); // false
      Modifier.isPrivate(m); // true
      Modifier.isStatic(m); // false
    
  • 通过Field实例可以读取(get(Object))或设置(set(Object, Object))某个对象的字段,若存在访问限制,则需要先调用setAccessible(true)来访问public字段;

调用方法

  • 通过Class实例获取所有Method信息的方法:

    • Method getMethod(name, Class...):获取某publicMethod(包括父类);

    • Method getDeclaredMethod(name, Class...):获取当前类的某一方法(不含父类);

    • Method getMethods(name, Class...):获取所有publicMethod(包括父类);

    • Method getDeclaredMethods(name, Class...):获取当前类所有Method(不含父类);

      public class Main{
        public static void main(String[] args) throws Exception{
            Class stdClass = Student.class;
      
            // 获取包括父类在内的public方法getScore,参数为String
            System.out.println(stdClass.getMethod("getScore", String.class));
      
            // 获取继承的public方法getName,无参数
            System.out.println(stdClass.getMethod("getName"));
      
            // 获取private方法getGrade,参数为int
            System.out.println(stdClass.getDeclaredMethod("getGrade()", int.class));
      
        }
      }
      
      class Student extends Person{
        public int getScore(String type){
            return 100;
        }
      
        private int getGrade(int year){
            return 1;
        }
      }
      
      class Person{
        public int getName(){
            return "Person";
        }
      }
      
  • 一个Method对象包含一个方法的所有信息:

    • getName():返回方法名称;
    • getReturnType():返回方法返回值类型,是一个Class实例;
    • getParameterTypes():返回方法参数类型,是一个Class数组;
    • getModifiers():返回方法修饰符;
  • PS:使用反射调用方法时,仍然遵循多态的原则:即总是调用实际类型的重写方法(如果存在)

调用构造方法

  • 利用反射创建新实例:利用Class提供的newInstance()方法,但此时只能调用该类的public无参构造方法;

      Person p = Person.class.newInstance();
      // 两者等价
      Person p = new Person();
    
  • 调用任意构造方法,可通过Java的反射API提供的Constructor对象,它包含了一个构造方法所有信息,可创建一个实例;

      public class Main{
          public static void main(String[] args) throws Exception{
              // 获取构造方法Integer(int)
              Constructor cons1 = Integer.class.getConstructor(int.class);
              // 调用构造方法
              Integer n1 = (Integer) cons1.newInstance(123);
              System.out.println(n1);
    
              // 获取构造方法Integer(String)
              Constructor cons2 = Integer.class.getConstructor(String.class);
              // 调用构造方法
              Integer n2 = (Integer) cons2.newInstance("345");
              System.out.println(n2);
          }
      }
    
  • 通过Class实例获取Constructor的方法:

    • getConstructor(Class...):获取某一publicConstructor
    • getDeclaredConstructor(Class…):获取某个Constructor
    • getConstructors():获取所有publicConstructor
    • getDeclaredConstructors():获取所有Constructor

获取继承关系

  • 获取父类Class

      public class Main{
          public static void main(String[] args) throws Exception {
              Class thisCls = Integer.class;
              Class superCls = thisCls.getSuperclass();
              System.out.println(superCls);
    
              Class objectCls = superClass.getSuperclass();
              System.out.println(ObjectCls);
    
              System.out.println(ObjectCls.getSuperclass());
          }
      }
    
  • 获取当前类实现的所有接口interface

      public class Main{
          public static void main(String[] args) throws Exception {
              Class thisCls = Integer.class;
              Class[] interCls = thisCls.getInterfaces();
    
              for(Class cls: interCls){
                  System.out.println(cls);            
              }
    
          }
      }
    

动态代理

  • classinterface区别:

    • 可以实例化classabstract);
    • 不能实例化interface;
  • 动态代理(Dynamic Proxy)机制:在运行期间动态创建某个interface的实例;

  • 动态代码:未实现类但是在运行期间动态创建了一个接口对象的方式;

      import java.lang.reflect.InvocationHandler;
      import java.lang.reflect.Method;
      import java.lang.reflect.Proxy;
    
      public class Main {
          public static void main(String[] args) {
              InvocationHandler handler = new InvocationHandler() {
                  @Override
                  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                      System.out.println(method);
                      if (method.getName().equals("morning")) {
                          System.out.println("Good morning, " + args[0]);
                      }
                      return null;
                  }
              };
              Hello hello = (Hello) Proxy.newProxyInstance(
                  Hello.class.getClassLoader(), // 传入ClassLoader
                  new Class[] { Hello.class }, // 传入要实现的接口
                  handler); // 传入处理调用方法的InvocationHandler
              hello.morning("Bob");
          }
      }
    
      interface Hello {
          void morning(String name);
      }
    
  • 通过Proxy.newProxyInstance()创建interface实例所需的3个参数

    • 使用的ClassLoader,通常是接口类的ClassLoader
    • 需要实现的接口数组,至少需要传入一个接口进去;
    • 用于处理接口方法调用InvocationHandler实例;
发布了138 篇原创文章 · 获赞 72 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/github_39655029/article/details/105448502
今日推荐