美文网首页
java基础_反射

java基础_反射

作者: dashingqi | 来源:发表于2020-03-01 19:36 被阅读0次

    什么是反射

    反射是一个很牛的功能,能够在程序运行时修改程序的行为。但是反射是非常规手段,反射有风险,应用需谨慎。

    把程序代码比作一辆车,因为Java是面向对象的语言,车子有自己的型号、车牌号、发动机,有倒车、泊车等功能。

    正常情况下,我们需要为车子配备一个司机,然后按照行为准则规范驾驶。

    反射是非常规手段,正常行驶的时候,车子需要司机的驾驶。但是反射不需要,因为它就是车子的自动驾驶。(非常规嘛)

    • 反射技术通常被用来检测和改变应用程序在 Java 虚拟机中的行为表现。它是一个相对而言比较高级的技术,通常它应用的前提是开发者本身对于 Java 语言特性有很强的理解的基础上。值得说明的是,反射是一种强有力的技术特性,因此可以使得应用程序突破一些藩篱,执行一些常规手段无法企及的目的。

    Class

    Java是面向对象的语言,基本上是以类为基础构造了整个程序。反射中要求提供的规格说明书就是类的规格说明书。

    • 其中小写的class是定义类的关键字。

    • Class本质是一个类。

      public final class Class<T> implements java.io.Serializable,
                                    GenericDeclaration,
                                    Type,
                                    AnnotatedElement,
                                    TypeDescriptor.OfField<Class<?>>,
                                    Constable {}
      
      
    • Class就是一个对象,运行在Java虚拟机中的类和接口。

    Class的获取

    反射的入口是Class,但是反射中Class是没有公开的构造函数,不能通过关键字new来获取Class的对象,所以就有如下3种获取Class的方式

    • Object.getClass()

      • 对于一个对象而言,如果能访问,那么调用getClass()方法就可以获取到它对应的Class对象。
      • 如下这种方法不适合基本类型 :int float
      public class Car {
      }
      
      public class CarTest {
          public static void main(String[] args) {
              Car car = new Car();
              Class aClass = car.getClass();
      
          }
      }
      
    • 通过 .class标识

      • 对于上述方法,Car是一个类,car是一个对象,通过car.getClass()就获取到Car这个类的Class对象。
      • 如果不想通过类的对象的getClass()方式去获取,就可以通过 ‘.class的标识’来获取。
      public class CarTest {
          public static void main(String[] args) {
             
              Class<Car> carClass = Car.class;
              Class<Integer> integerClass = int.class;
              Class<String> stringClass = String.class;
      
          }
      }
      
    • 通过 Class.forName()方法

      • 在Android中,Google的Android工程师把一些类加上了@hide注解,这些被@hide注解修饰的类就没有出现在SDK中。
      • 我们要获取到这个在当前开发环境中不存在的类的Class对象,就可以通过此方法来获取到。
      • 如下代码中,"com.dashingqi.reflect.Car"就是Car类的全限定类名称,包括 包名+类名。
      • 如果找不到该类的时候,会抛出ClassNotFoundException,因为如果这个没有加载到JVM中的时候,需要告诉我们。
      public class CarTest {
          public static void main(String[] args) {
             
              try {
                  Class.forName("com.dashingqi.reflect.Car");
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
          }
      }
      
      

    Class的内容清单

    Class的名字获取

    Class对象也有名字,涉及到API如下

    • Class.getName()
    • Class.getSimpleName()
    • Class.getCanonicalName()

    因为Class是一个入口,它代表引用、基本数据类型以及数组对象,所以获取它们的方式也是不同的。

    getName

    当Class代表一个引用时

    • getName放回的是一个二进制形式的字符串 “com.dashingqi.reflect.Car”

    当Class代表是一个基本数据类型的时候

    • getName返回的就是它们的关键字 比如 int.class的名字就是int

    当Class代表是基本数据类型的数组时(int [] [] [] 这样的3维数组)

    • getName返回的是[[[I 这样的字符串

    • Java在这个地方有相应的规则,在元素类型的前面添加相应数量的[符号,用[符号的数量来表示数组的维度,对于基本数据类型都有相应的编码,都是大写的字母;规则如下

    元素类型 编码
    boolean Z
    byte B
    char C
    Double D
    Float F
    int I
    Long L
    short S
    类或者接口 L类名;(是有;的哟)
    • 代码操作一下

      public class CarTest {
          public static void main(String[] args) {
      
              try {
      
                  Class<?> aClass = Class.forName("getclassinstance.Car");
                  Class<Integer> integerClass = int.class;
                  Class<Float> floatClass = float.class;
                  Class<? extends int[]> aClass1 = new int[]{}.getClass();
                  Class<? extends Car[]> aClass2 = new Car[]{}.getClass();
      
                    //代表一个引用
                  System.out.println("aClass = " + aClass.getName());
                  //基本数据类型
                  System.out.println("integerClass = " + integerClass.getName());
                  System.out.println("floatClass=" + floatClass.getName());
                    //基本数据类型的数组
                  System.out.println("aClass1=" + aClass1.getName());
                 //类或者接的数组
                  System.out.println("aClass2=" + aClass2.getName());
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              }
      
          }
      }
      
      //运行结果如下
      
      aClass = getclassinstance.Car
      integerClass = int
      floatClass=float
      aClass1=[I
      aClass2=[Lgetclassinstance.Car;
      
      
    getSimpleName()
    • 先看一个嵌套类

      public class ClassA {
      
          public static class ClassB{
      
          }
      }
      
      public class ClassTest {
      
          public static void main(String[] args) {
              ClassA.ClassB classB = new ClassA.ClassB();
              String name = classB.getClass().getName();
              String simpleName = classB.getClass().getSimpleName();
              System.out.println("name = " + name);
              System.out.println("simpleName = " + simpleName);
          }
      }
      
      //运行结果
      name = getSimpleName.ClassA$ClassB
      simpleName = ClassB
      
      • 因为是内部类,通过getName得到是的二进制形式的全限定类名,并且类名前面还有一个$符号。
      • getSimpleName()则返回一个ClassB,去掉了包名限定。
    • 当数组的Class获取simpleName的时候,不同于getName,simpleName不在前面加[,而是在后面加[]

      public class ClassTest {
      
          public static void main(String[] args) {
      
              Class<? extends ClassA[]> aClass = new ClassA[]{}.getClass();
              String name1 = aClass.getName();
              System.out.println("name1 = " + name1);
              String simpleName1 = aClass.getSimpleName();
              System.out.println("simpleName1=" + simpleName1);
          }
      }
      
      //运行结果如下
      name1 = [LgetSimpleName.ClassA;
      simpleName1=ClassA[]
      
    • 对于匿名内部类来说,通过getSimpleName()获取的是一个空的字符串

      public class ClassTest {
      
          public static void main(String[] args) {
              Runnable runnable = new Runnable() {
                  @Override
                  public void run() {
      
                  }
              };
      
              System.out.println("name = " + runnable.getClass().getName());
              System.out.println("simple name = " + runnable.getClass().getSimpleName());
          }
      }
      
      // 运行结果如下
      name = getSimpleName.ClassTest$1
      simple name = 
      
    getCanonicalName

    Canonial 是官方、标准的意思,那么该方法就是获取Class对象的官方名字。

    这个CanonicalName是官方规定的,如果Class对象没有canonicalName的话就返回一个null

    getCanonicalName()是getName()与getSimpleName()的结合

    • getCanonicalName()返回的也是全限定类名,但是对于内部类来说,不用$开头,而是用.。

    • getCanonicalName()对于数组的Class,同getSimpleName()也是在后面加[]。

    • getCanonicalName()不同于getSimpleName()的是,如果不存在canonicalName就返回null而不是空字符串。

    • 局部类和匿名内部类不存在canonicalName。

    • 代码

      public class ClassTest {
      
          public static void main(String[] args) {
              ClassA.ClassB classB = new ClassA.ClassB();
      
              Runnable runnable = new Runnable() {
                  @Override
                  public void run() {
      
                  }
              };
              String canonicalName = classB.getClass().getCanonicalName();
              String canonicalName1 = new ClassA.ClassB[][][]{}.getClass().getCanonicalName();
      
              System.out.println("canonicalName = " + canonicalName);
              System.out.println("canonicalName1 = " + canonicalName1);
              System.out.println("run name = " + runnable.getClass().getCanonicalName());
      
      
              /**
               * local是局部类
               */
              class Local {
              }
              ;
              String name = Local.class.getName();
              String simpleName = Local.class.getSimpleName();
              String canonicalName2 = Local.class.getCanonicalName();
      
              System.out.println("local name = " + name);
              System.out.println("local simple name = " + simpleName);
              System.out.println("local canonical name = " + canonicalName2);
      
          }
      }
      
      //运行结果
      
      canonicalName = getSimpleName.ClassA.ClassB
      canonicalName1 = getSimpleName.ClassA.ClassB[][][]
      run name = null
      local name = getSimpleName.ClassTest$1Local
      local simple name = Local
      local canonical name = null
      

    Class获取修饰符

    Java开发中定义一个类,往往要通过很多修饰符来配合使用的。大致分为如下四类

    • 用来限制作用域:publish、protected、private
    • 用来标记为静态:static
    • 注解
    • 用来提示子类来复写:abstract

    Java反射提供了API去获取这些修饰符

    • 定义一个被 abstract 和 publish 修饰的类

      public  abstract class DemoA {
      }
      
    • 我们现在要提取这些修饰符,只需要调用 Class.getModifiers()

      public class Demo {
          public static void main(String[] args) {
                /**
                * public native int getModifiers();
                * 是一个native层的方法
                */
              int modifiers = DemoA.class.getModifiers();
              System.out.println("modifiers = " + modifiers);
              System.out.println("modifiers_str = "+ Modifier.toString(modifiers));
          }
      }
      
      
    • 运行结果如下

      modifiers = 1025
      modifiers_str = public abstract
      
      
    • Java工程师考虑到了位运算,用一个int数值来记录所有的修饰符,然后不同的位对应不同的修饰符,这些修饰符都定义在Modifier类中。

      public static String toString(int mod) {
              StringJoiner sj = new StringJoiner(" ");
      
              if ((mod & PUBLIC) != 0)        sj.add("public");
              if ((mod & PROTECTED) != 0)     sj.add("protected");
              if ((mod & PRIVATE) != 0)       sj.add("private");
      
              /* Canonical order */
              if ((mod & ABSTRACT) != 0)      sj.add("abstract");
              if ((mod & STATIC) != 0)        sj.add("static");
              if ((mod & FINAL) != 0)         sj.add("final");
              if ((mod & TRANSIENT) != 0)     sj.add("transient");
              if ((mod & VOLATILE) != 0)      sj.add("volatile");
              if ((mod & SYNCHRONIZED) != 0)  sj.add("synchronized");
              if ((mod & NATIVE) != 0)        sj.add("native");
              if ((mod & STRICT) != 0)        sj.add("strictfp");
              if ((mod & INTERFACE) != 0)     sj.add("interface");
      
              return sj.toString();
          }
      
          /*
           * Access modifier flag constants from tables 4.1, 4.4, 4.5, and 4.7 of
           * <cite>The Java&trade; Virtual Machine Specification</cite>
           */
      
          /**
           * The {@code int} value representing the {@code public}
           * modifier.
           */
          public static final int PUBLIC           = 0x00000001;
      
          /**
           * The {@code int} value representing the {@code private}
           * modifier.
           */
          public static final int PRIVATE          = 0x00000002;
      
          /**
           * The {@code int} value representing the {@code protected}
           * modifier.
           */
          public static final int PROTECTED        = 0x00000004;
      
          /**
           * The {@code int} value representing the {@code static}
           * modifier.
           */
          public static final int STATIC           = 0x00000008;
      
          /**
           * The {@code int} value representing the {@code final}
           * modifier.
           */
          public static final int FINAL            = 0x00000010;
      
          /**
           * The {@code int} value representing the {@code synchronized}
           * modifier.
           */
          public static final int SYNCHRONIZED     = 0x00000020;
      
          /**
           * The {@code int} value representing the {@code volatile}
           * modifier.
           */
          public static final int VOLATILE         = 0x00000040;
      
          /**
           * The {@code int} value representing the {@code transient}
           * modifier.
           */
          public static final int TRANSIENT        = 0x00000080;
      
          /**
           * The {@code int} value representing the {@code native}
           * modifier.
           */
          public static final int NATIVE           = 0x00000100;
      
          /**
           * The {@code int} value representing the {@code interface}
           * modifier.
           */
          public static final int INTERFACE        = 0x00000200;
      
          /**
           * The {@code int} value representing the {@code abstract}
           * modifier.
           */
          public static final int ABSTRACT         = 0x00000400;
      
          /**
           * The {@code int} value representing the {@code strictfp}
           * modifier.
           */
          public static final int STRICT           = 0x00000800;
      
          // Bits not (yet) exposed in the public API either because they
          // have different meanings for fields and methods and there is no
          // way to distinguish between the two in this class, or because
          // they are not Java programming language keywords
          static final int BRIDGE    = 0x00000040;
          static final int VARARGS   = 0x00000080;
          static final int SYNTHETIC = 0x00001000;
          static final int ANNOTATION  = 0x00002000;
          static final int ENUM      = 0x00004000;
          static final int MANDATED  = 0x00008000;
      

    获取Class的成员

    获取Filed

    获取到指定名字的属性API

    • getDeclaredField()方法获取的是Class中任何修饰符修饰的属性名字,name

      public Field getDeclaredField(String name)
              throws NoSuchFieldException, SecurityException {
          }
      
    • getField(String name):当前 name是被publish修饰 就能获取到,否则抛出异常

      @CallerSensitive
          public Field getField(String name)
             
          }
      

    获取到所有属性

    • getDeclaredFieleds()获取到所有属性,但不包括从父类继承下来的属性

       public Field[] getDeclaredFields() throws SecurityException {
             
          }
      
    • getFields() 获取到自身的所有public属性,包括从父类继承下来的

      public Field[] getFields() throws SecurityException {
              
          }
      
    • 代码实操

      public class MainTest {
      
          public static void main(String[] args) {
              Class<Son> sonClass = Son.class;
      
              try {
                  //获取到属性名字,
                  Field a = sonClass.getDeclaredField("d");
                  System.out.println("getDeclaredField name = " + a.getName());
              } catch (NoSuchFieldException e) {
                  e.printStackTrace();
                  System.out.println("getDeclaredField = " + e.getMessage());
              }
      
              try {
                  //获取 publish修饰的属性名字,如果自己找不到,会去查找父类中的
                  Field c = sonClass.getField("c");
                  System.out.println("getField name = " + c.getName());
              } catch (NoSuchFieldException e) {
                  e.printStackTrace();
              }
      
              //获取到所有属性的名字,不包括父类的
              Field[] declaredFields = sonClass.getDeclaredFields();
              for (Field f : declaredFields){
                  System.out.println("getDeclaredFields name =  "+f.getName());
              }
      
              //获取到自身所有的publish修饰的属性名字,包括从父类继承下来的
              Field[] fields = sonClass.getFields();
              for (Field field : fields){
                  System.out.println("getFields name = "+field.getName());
              }
          }
      }
      
      //运行结果如下 
      
      getDeclaredField name = d
      java.lang.NoSuchFieldException: c
        at java.base/java.lang.Class.getField(Class.java:2004)
        at get_class_filed.MainTest.main(MainTest.java:21)
      getDeclaredFields name =  c
      getDeclaredFields name =  d
      getDeclaredFields name =  f
      getFields name = b
      
    获取Method

    类获取接口中的方法对应到Class就是Method

    • getDeclaredMethod():获取到任何修饰符修饰的方法,不包括从父类继承到的

       public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
             
          }
      
    • getMethod():获取到任何publish修饰符修饰的方法,包括从父类中继承得到的

      public Method getMethod(String name, Class<?>... parameterTypes)
             
          }
      
    • getDeclaredMethods():获取所有方法的名字,不包括从父类继承得到的

      public Method[] getDeclaredMethods() throws SecurityException {
             
          }
      
    • getMethods():获取所有publish修饰方法的名字,包括从父类继承的来的(包括Object)

      public Method[] getMethods() throws SecurityException {
          }
      
    • 代码演示

      public class MainTest {
      
          public static void main(String[] args) {
              Class<Son> sonClass = Son.class;
      
              try {
                  Method methodC = sonClass.getDeclaredMethod("methodF", null);
                  System.out.println("methodC = " + methodC.getName());
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
                  System.out.println(e.getMessage());
              }
      
              try {
                  Method methodA = sonClass.getMethod("methodF", null);
                  System.out.println("methodA name = " + methodA.getName());
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
                  System.out.println(e.getMessage());
              }
      
              Method[] declaredMethods = sonClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("getDeclaredMethods name =  " + method.getName());
              }
      
              Method[] methods = sonClass.getMethods();
              for (Method method : methods) {
                  System.out.println(" getMethods name = " + method.getName());
              }
          }
      }
      
      //运行结果如下 
      
      methodC = methodF
      java.lang.NoSuchMethodException: get_class_filed.Son.methodF()
        at java.base/java.lang.Class.getMethod(Class.java:2113)
        at get_class_filed.MainTest.main(MainTest.java:20)
      get_class_filed.Son.methodF()
      getDeclaredMethods name =  methodC
      getDeclaredMethods name =  methodF
      getDeclaredMethods name =  methodD
      getDeclaredMethods name =  methodG
       getMethods name = methodC
       getMethods name = methodD
       getMethods name = methodA
       getMethods name = wait
       getMethods name = wait
       getMethods name = wait
       getMethods name = equals
       getMethods name = toString
       getMethods name = hashCode
       getMethods name = getClass
       getMethods name = notify
       getMethods name = notifyAll
       
      
    Constructor

    Java把构造器从Method中拎出来,用Constructor来表示

    Constructor不能从父类中继承,所以就没有办法通过getConstructor()来获取到父类的Constructor

    • getDeclaredConstructor():获取任何修饰符修饰的构造方法名字

      public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
              throws NoSuchMethodException, SecurityException
          }
      
    • getConstructor():获取当前Class对象中 publish修饰的构造方法的名字

      public Constructor<T> getConstructor(Class<?>... parameterTypes)
              throws NoSuchMethodException, SecurityException{
          }
      
    • getDeclaredConstructors():获取所有构造方法名字

      public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
      }
      
    • getConstructors():获取所有publish修饰的构造方法名字

      public Constructor<?>[] getConstructors() throws SecurityException {
            
      }
      

    Field的操作

    我们在类中定义字段时,通常是这样的

    其中 c、d、f、g、demo这些变量都是属性,在反射机制中映射到Class对象中都是Field

    它们要么是8中基本数据类型,或者是引用,所有的引用都是Object的子类。

    public class Son extends Father {
        int c;
    
        private String d;
    
        protected float f;
    
        public int g;
    
        Demo demo;
    }
    
    Field类型的获取
    • 获取Field类型有2种方法(首先获取到Class对象种的Field,通过Field的一下两种方法获取Field类型)

      public Type getGenericType() {
          }
      public Class<?> getType() {
          }
      //两种方法返回参数不一样,getGenericType()可以获取到泛型类型
      
    • 代码实操

      public class Son extends Father {
          int c;
      
          private String d;
      
          protected float f;
      
          public int g;
      
          Demo demo;
      
         public List<Demo> mDemoList;
      
          public Map<Integer, Integer> maps;
      }
      
      public static void main(String[] args) {
              Class<Son> sonClass = Son.class;
      
              Field[] fields = sonClass.getFields();
              for (Field field : fields) {
                  System.out.println("getName == " + field.getName());
                  System.out.println("getType = " + field.getType());
                  System.out.println("getGenericType = " + field.getGenericType());
                  System.out.println("======================");
              }
      }
      //运行结果如下
      getName == g
      getType = int
      getGenericType = int
      ======================
      getName == mDemoList
      getType = interface java.util.List
      getGenericType = java.util.List<get_class_filed.Demo>
      ======================
      getName == maps
      getType = interface java.util.Map
      getGenericType = java.util.Map<java.lang.Integer, java.lang.Integer>
      ======================
      getName == b
      getType = int
      getGenericType = int
      ======================
        
      // 我们看到 把自己Class对象种的publish属性和父类中的publish修饰的属性都给找到了
      // getGenericType()把泛型的类型都给打印出来了  ,相比较与getType 更详细
      
    Field修饰符的获取
    • 通过getModifiers()方法就可以获取

      public int getModifiers() {
              return modifiers;
          }
      
    • 代码实操

      public static void main(String[] args) {
              Class<Son> sonClass = Son.class;
      
              Field[] fields = sonClass.getFields();
              for (Field field : fields) {
                  System.out.println("getModifiers = "+ Modifier.toString(field.getModifiers()));
                  System.out.println("======================");
              }
        //运行结果如下
      getModifiers = public
      ======================
      getModifiers = public
      ======================
      getModifiers = public
      ======================
      getModifiers = public
      ======================
      
    Field内容的读取与赋值

    这也是反射机制中对Filed主要的目的

    Filed定义了一些列set和get方法来设置和获取不同类型的值

    • get

      public Object get(Object obj);
      
      public int getInt(Object obj);
      
      public long getLong(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public float getFloat(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public short getShort(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public double getDouble(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public char getChar(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public byte getByte(Object obj)
              throws IllegalArgumentException, IllegalAccessException;
      
      public boolean getBoolean(Object obj)
              throws IllegalArgumentException, IllegalAccessException
      
    • set

      public void set(Object obj, Object value);
      
      public void getInt(Object obj,int value);
      
      public void getLong(Object obj,long value)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getFloat(Object obj,float value)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getShort(Object obj,short value)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getDouble(Object obj,double value)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getChar(Object obj,char value)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getByte(Object obj,byte b)
              throws IllegalArgumentException, IllegalAccessException;
      
      public void getBoolean(Object obj,boolean b)
              throws IllegalArgumentException, IllegalAccessException
      
    • 其中Object就是类的实例引用,Class本身不对成员进行存储,它只提供检索,所以需要Field、Method、Constructor对象来承载这些成员,所以针对成员的操作时,一般需要为成员指定类的实例引用,表示这个成员属于哪个Object的,是为了精准定位。

    • 代码实操

      public class MainTest {
      
          public static void main(String[] args) {
      
              Son son = new Son();
              son.g = 10;
      
              System.out.println("son.g = " + son.g);
              //Class本身不对成员做存储
              Class<Son> sonClass = Son.class;
      
              try {
                  Field g = sonClass.getField("g");
                  int anInt = g.getInt(son);
                  System.out.println(" reflection son.g = " + anInt);
                  g.setInt(son, 16);
                  System.out.println("son.g = " + son.g);
              } catch (NoSuchFieldException | IllegalAccessException e) {
                  e.printStackTrace();
              }
          }
        
        //运行结果如下
      son.g = 10
       reflection son.g = 10
      son.g = 16
      
    • 上述代码是获取publish修饰的属性,这回找个private修饰的属性 h

      Son son = new Son();
              son.setH(10);
      
              System.out.println("son.g = " + son.getH());
              //Class本身不对成员做存储
              Class<Son> sonClass = Son.class;
      
              try {
                  Field h = sonClass.getDeclaredField("h");
                  int anInt = h.getInt(son);
                  System.out.println(" reflection son.g = " + anInt);
                  h.setInt(son, 16);
                  System.out.println("son.g = " + son.getH());
              } catch (NoSuchFieldException | IllegalAccessException e) {
                  e.printStackTrace();
              }
      //运行结果如下
      son.g = 10
      java.lang.IllegalAccessException: class get_class_filed.MainTest cannot access a member of class get_class_filed.Son with modifiers "private"
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:376)
        at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:639)
        at java.base/java.lang.reflect.Field.checkAccess(Field.java:1075)
        at java.base/java.lang.reflect.Field.getInt(Field.java:592)
        at get_class_filed.MainTest.main(MainTest.java:21)
       //报错了
      
    • 这是因为反射过程操作了private属性,如果要消除异常加如下代码

      h.setAccessible(true)
      
      • 运行结果如下

        son.g = 10
         reflection son.g = 10
        son.g = 16
        

    Method操控

    Method对应普通类的方法;

    一般普通类的构成

    public int add(int a, int b){

    ​ return a+b;

    }

    主要包括如下几个方面

    1. 方法名
    2. 方法的返回值类型
    3. 方法的参数
    4. 方法的修饰符
    5. 方法抛出的异常

    Java的反射中针对Method 有相关操作API的来提取相关元素

    获取Method方法名
    • 获取方法名字的通过getName()方法

    • 代码实操

      public class MainClass {
      
          public static void main(String[] args) {
      
              Class<Car> carClass = Car.class;
              Method[] declaredMethods = carClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("method name = " + method.getName());
              }
          }
      }
      //运行结果如下
      method name = getCarName
      method name = setCarName
      method name = getCarId
      method name = setCarId
      
    Method获取方法参数
    • 通过 getParameters()来获取

      public Parameter[] getParameters() {}
      
    • 返回是一个Parameter的数组,在反射中Parameter对象就是用来映射方法中的参数,经常使用的方法有如下

      // 获取参数名字
      public String getName() {}
      
      // 获取参数类型
      public Class<?> getType() {}
      
      // 获取参数的修饰符
      public int getModifiers() {}
      
    • 当然有时我们不需要参数的名字,只需要参数的类型就可以,通过Method下面的方法获取

      //获取所有参数的类型
      public Class<?>[] getParameterTypes() {
          }
      //获取所有参数的类型,包括泛型    
      public Type[] getGenericParameterTypes() {
              return super.getGenericParameterTypes();
          }
      
    • 代码实操

      public class MainClass {
      
          public static void main(String[] args) {
      
              Class<Car> carClass = Car.class;
              Method[] declaredMethods = carClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("method name = " + method.getName());
      
                  //获取到方法中的参数
                  Parameter[] parameters = method.getParameters();
      
                  for (Parameter parameter : parameters) {
                      //获取到参数的名字和参数和参数的修饰符
                      System.out.println("parameter = " + parameter.getName() + " " + parameter.getType().getName() + " Modifiers name = " + Modifier.toString(parameter.getModifiers()));
                  }
      
                  //获取到所有参数的类型
                  Class<?>[] pTypes = method.getParameterTypes();
                  System.out.println("method para types");
                  for (Class type : pTypes) {
                      System.out.println(" type name = " + type.getName());
                  }
      
                  //获取到所有参数的类型,包括泛型
                  Type[] genericParameterTypes = method.getGenericParameterTypes();
                  for (Type type : genericParameterTypes) {
                      System.out.println(" generic type name = " + type.getTypeName());
                  }
      
              }
          }
      }
      
      //运行结果如下
      method name = toString
      method para types
      method name = test
      parameter = arg0 [Ljava.lang.String; Modifiers name = 
      parameter = arg1 java.util.List Modifiers name = 
      parameter = arg2 java.util.HashMap Modifiers name = 
      method para types
       type name = [Ljava.lang.String;
       type name = java.util.List
       type name = java.util.HashMap
       generic type name = java.lang.String[]
       generic type name = java.util.List<java.lang.String>
       generic type name = java.util.HashMap<java.lang.String, java.lang.Integer>
      method name = drive
      method para types
      
    获取Method的返回值类型
    • 与如下两种方法

      // 获取返回值类型
      public Class<?> getReturnType() {}
      
      
      // 获取返回值类型包括泛型
      public Type getGenericReturnType() {}
      
      
    • 代码实操

      • getReturnType()

        public class MainClass {
        
            public static void main(String[] args) {
        
                Class<Car> carClass = Car.class;
                Method[] declaredMethods = carClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    System.out.println("method name = " + method.getName());
        
                    Class<?> returnTypes = method.getReturnType();
                    String name = returnTypes.getName();
                    System.out.println("return type name = " + name);
        
                }
            }
        }
        
        //运行结果如下
        method name = toString
        return type name = java.lang.String
        method name = test
        return type name = void
        method name = drive
        return type name = java.lang.String
        method name = getNames
        return type name = java.util.List
        
      • getGenericReturnType();

        public class MainClass {
        
            public static void main(String[] args) {
        
                Class<Car> carClass = Car.class;
                Method[] declaredMethods = carClass.getDeclaredMethods();
                for (Method method : declaredMethods) {
                    System.out.println("method name = " + method.getName());
        
                    Type genericReturnType = method.getGenericReturnType();
                    System.out.println("genericReturnType name = " + genericReturnType.getTypeName());
        
                }
            }
        }
        //运行结果如下
        method name = toString
        genericReturnType name = java.lang.String
        method name = test
        genericReturnType name = void
        method name = drive
        genericReturnType name = java.lang.String
        method name = getNames
        genericReturnType name = java.util.List<java.lang.String>
        
    Method获取修饰符
    • 通过如下方法获取

      public int getModifiers() {}
      
    • 代码实操

      public class MainClass {
      
          public static void main(String[] args) {
      
              Class<Car> carClass = Car.class;
              Method[] declaredMethods = carClass.getDeclaredMethods();
              for (Method method : declaredMethods) {
                  System.out.println("method name = " + method.getName());
      
                  int modifiers = method.getModifiers();
                  System.out.println("modifiers name = " + Modifier.toString(modifiers));
              }
          }
      }
      //运行结果如下
      method name = toString
      modifiers name = public
      method name = test
      modifiers name = public
      method name = drive
      modifiers name = public
      method name = getNames
      modifiers name = private
      
    Method获取异常类型
    • 通过如下方法获取

      public Class<?>[] getExceptionTypes() {}
      
      public Type[] getGenericExceptionTypes() {}
      
    Method方法的执行

    这应该是整个反射机制的核心内容,我们很多时候运用反射目的就是为了以非常规手段执行Method

    • Method类型,是如下这个方法

      public Object invoke(Object obj, Object... args)
              throws IllegalAccessException, IllegalArgumentException,
                 InvocationTargetException
          {}
      
    • Method在调用invoke()方法的时候,存在许多细节

      • invoke()方法中的第一个参数obj是Method依附的Class对应的类的实例,如果这个方法是一个静态方法,那么obj为null,第二个参数args对应的是方法的参数
      • invoke()方法返回的是Object,所以实际上执行的时候要进行强制转换。
      • 在对Method的invoke()方法调用的时候,如果方法本身会抛出异常,那么这个异常就会进过包装,由Method统一抛出InvocationTargetException。而通过InvocationTargetException.getCause()可以获取真正的异常。
    • 代码实操

      public class TestMethod {
      
          public static void testStatic() {
              System.out.println("test static");
          }
      
          private int add(int a, int b) {
              return a + b;
          }
      
          public void testException() throws IllegalAccessException {
              throw new IllegalAccessException("You have some problem");
          }
      }
      
      public class MainTest {
          public static void main(String[] args) {
              Class<TestMethod> testMethodClass = TestMethod.class;
      
              try {
                  Method testStatic = testMethodClass.getMethod("testStatic", null);
                  //测试静态方法
                  testStatic.invoke(null, null);
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
              }
      
              TestMethod testMethod = new TestMethod();
      
              try {
                  Method add = testMethodClass.getDeclaredMethod("add", int.class, int.class);
                  //通过这行代码才能访问private修饰的method
                  add.setAccessible(true);
                  int addValue = (int) add.invoke(testMethod, 3, 4);
                  System.out.println("addValue = " + addValue);
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
              }
      
              try {
                  Method testException = testMethodClass.getMethod("testException", null);
                  testException.invoke(testMethod, null);
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
                  System.out.println("testException occur some error,Error type is :" + e.getCause().getClass().getName());
                  System.out.println("Error message is : " + e.getCause().getMessage());
              }
          }
      }
      
      // 运行结果如下
      test static
      addValue = 7
      java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:567)
        at method_invoke.MainTest.main(MainTest.java:40)
      Caused by: java.lang.IllegalAccessException: You have some problem
        at method_invoke.TestMethod.testException(TestMethod.java:14)
        ... 5 more
      
    Constructor的操控

    构造器也叫做构造方法,在Java的反射机制中把Constructor与Method分离开来,单独用Constructor这个类来表示,Constructor同Method差不多,但是它的特别的地方在于它能够创建一个对象。

    Java的反射机制中有两种方法可以用来创建类的对象实例:Class.newInstance()和Constructor.newInstance() 官方文档建议开发者使用第二种方法 原因如下

    • Class.newInstance()只能调用无参的构造方法,而Constructor.newInstance()可以调用任意的构造方法。

    • Class.newInstance()通过构造方法直接抛出异常,而Constructor.newInstance()可以把抛出来的异常包装到InvocationtargetException里面去,这个是和Method一致的。

    • Class.newInstance()要求构造方法能够被访问,而Constructor.newInstance()却能够访问private修饰的构造器。

    • 代码实操

      public class TestClass {
      
          private String name;
      
          public TestClass(String name) {
              this.name = name;
          }
      
          public TestClass() {
              name = "zhangqi";
          }
      
          @Override
          public String toString() {
              return "TestClass{" +
                      "name='" + name + '\'' +
                      '}';
          }
      }
      
      public class TestMain {
      
          public static void main(String[] args) {
              Class<TestClass> testClassClass = TestClass.class;
      
              //Class.newInstance() 测试
              try {
                  //在Java 9 中已经标注为过时的API
                  TestClass testClass = testClassClass.newInstance();
                  System.out.println(testClass.toString());
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }
      
              // Constructor.newInstance() 测试
      
              try {
                  Constructor<TestClass> constructor = testClassClass.getConstructor(String.class);
                  TestClass dashingqi = constructor.newInstance("dashingqi");
                  System.out.println("name = "+dashingqi);
              } catch (NoSuchMethodException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (InvocationTargetException e) {
                  e.printStackTrace();
              }
      
          }
      }
      
      //运行结果如下
      TestClass{name='zhangqi'}
      name = TestClass{name='dashingqi'}
      

    反射中的数组

    数组本质上是一个Class,而在Class中存在一个方法用来识别它是否为一个数组

    • public native boolean isArray();
      
    • 代码实操

      public class Arrays {
      
          private int[] array;
          private Car[] cars;
      }
      //我们定义了一个类 Arrays 内部有两个数组 array和cars 。当然array和cars是类Arrays的Field,从Field角度来说,它们是数组类型。
      //我们可以通过一些列的API来获取它们的具体信息
      
      //获取数组的元素对应的编码 同Field中的
      getName()
       //该方法是获取数组里面元素的类型
      getComponentType() array 就是 int cars就是 class operate_array.Car
      
    public class MainTest {
    
        public static void main(String[] args) {
            Class<Arrays> arraysClass = Arrays.class;
    
            Field[] declaredFields = arraysClass.getDeclaredFields();
            for (Field field : declaredFields) {
                Class<?> type = field.getType();
                if (type.isArray()) {
                    System.out.println("Type is " + type.getName());
                    System.out.println("ComponentType type is " + type.getComponentType());
                }
            }
        }
    }
    //运行结果如下
    Type is [I
    ComponentType type is int
    Type is [Loperate_array.Car;
    ComponentType type is class operate_array.Car
    
    反射中动态创建数组
    • 是通过Array.newInstance()方法创建的。

      // Array.java
      public static Object newInstance(Class<?> componentType, int... dimensions)
              throws IllegalArgumentException, NegativeArraySizeException {
              return multiNewArray(componentType, dimensions);
          }
      
      • 第一个参数表示数组内元素类型,第二个参数表示相应维度数组中的长度限制

        //比如我们呢要创建 int [][] ints = new int[2][3];
        Array.newInstance(int.class,2,3);
        
    Array的读取与赋值

    对于Array整体的读取赋值,把它作为一个普通的Field,调用Field中对应的方法即可。

    也就是 Field中提供的 set() 和get

    ublic void set(Object obj,
                    Object value)
             throws IllegalArgumentException,
                    IllegalAccessException;
    
    
    public Object get(Object obj)
               throws IllegalArgumentException,
                      IllegalAccessException;
    

    当数组中指定位置的元素进行读取与赋值,这要涉及到Array提供的一些列 setXXX()和getXXX()方法

    public static native boolean getBoolean(Object array, int index)
            throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    
        public static native byte getByte(Object array, int index)
            throws IllegalArgumentException, ArrayIndexOutOfBoundsException;
    
    • 代码操作

      public class ArrayTest {
      
          public static void main(String[] args) {
              Class<Arrays> arraysClass = Arrays.class;
      
              try {
                  //Constructor 能够创建一个对象
                  //类的实例对象的创建
                  Arrays arrays = arraysClass.newInstance();
      
                  Field fieldArray = arraysClass.getDeclaredField("array");
                  fieldArray.setAccessible(true);
      
                  //创建一个数组
                  Object o = Array.newInstance(int.class, 3);
                  Array.set(o, 0, 1);
                  Array.set(o, 1, 2);
                  Array.set(o, 2, 3);
                  //操作Field中的set
                  fieldArray.set(arrays, o);
      
                  int[] array = arrays.getArray();
                  for (int i = 0; i < array.length; i++) {
                      System.out.println("array index "+i+"value "+array[i]);
                  }
      
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              } catch (NoSuchFieldException e) {
                  e.printStackTrace();
              }
          }
      }
      
      //运行结果如下
      array index 0value 1
      array index 1value 2
      array index 2value 3
      

    反射中的枚举类Enum

    同数组一样,枚举本质上也是一个Class而已,但是反射中还是把它单独提出来了。

    • 定义一个枚举类

      public enum EnumTestState {
          IDLE,
          DRIVING,
          STOPPING,
      
          test();
      
          int test1() {
              return 1;
          }
      }
      
    • 枚举跟类很相似,有自己的修饰符,方法,属性字段,还可以有构造方法。

    • 在Java的反射机制中,提供了3个特别的API用于操控枚举。

      // 用来判定 Class 对象是不是枚举类型
      Class.isEnum()
      
      // 获取所有的枚举常量
      Class.getEnumConstants()
      
      
      // 判断一个 Field 是不是枚举常量
      java.lang.reflect.Field.isEnumConstant()
      
      
    枚举的获取与设定

    应为等同于Class,所以枚举的获取与设定都可以通过Field中的get()和set()方法。

    如果枚举要获取里面的Field、Method、Constructor可以调用Class通用的API

    • 代码实操

      public enum EnumTestState {
          IDLE,
          DRIVING,
          STOPPING,
      
          test();
      
          int test1() {
              return 1;
          }
      }
      
      
      public class EnumTestClass {
      
          private EnumTestState state = EnumTestState.DRIVING;
      
          public EnumTestState getState() {
              return state;
          }
      
          public void setState(EnumTestState state) {
              this.state = state;
          }
      }
      
      public class EnumTestMain {
      
          public static void main(String[] args) {
              //获取到枚举对应的Class对象
              Class<EnumTestState> enumTestStateClass = EnumTestState.class;
              //Class中有判断当前获取的Class对象是否是枚举
              if (enumTestStateClass.isEnum()) {
      
                  System.out.println(Arrays.asList(enumTestStateClass.getEnumConstants()));
      
                  //活到枚举中所有的Field
                  Field[] declaredFields = enumTestStateClass.getDeclaredFields();
                  for (Field field : declaredFields) {
                      //判断一个Field是不是一个枚举常量
                      if (field.isEnumConstant()) {
                          System.out.println("is Enum");
                      } else {
                          System.out.println("is not Enum");
                      }
                  }
      
                  Class<EnumTestClass> enumTestClassClass = EnumTestClass.class;
                  EnumTestClass enumTestClass = new EnumTestClass();
                  try {
                      //获取到名字叫做"state"的Field
                      Field state = enumTestClassClass.getDeclaredField("state");
                      state.setAccessible(true);
      
                      EnumTestState enumTestState = (EnumTestState) state.get(enumTestClass);
                      System.out.println("enumTestState = " + enumTestState);
                      state.set(enumTestClass, EnumTestState.STOPPING);
      
                      System.out.println("State current is " + enumTestClass.getState());
      
                  } catch (NoSuchFieldException e) {
                      e.printStackTrace();
                  } catch (IllegalAccessException e) {
                      e.printStackTrace();
                  }
              }
      
      
          }
      }
      
      //运行结果如下
      [IDLE, DRIVING, STOPPING, test]
      is Enum
      is Enum
      is Enum
      is Enum
      is not Enum
      enumTestState = DRIVING
      State current is STOPPING
      

    总结

    • Java中的反射是非常规编码方式。
    • Java中的反射入口是Class文件,可通过 getClass()、.class、Class.forName()三种方式获取它。
    • 获取到Class对象之后,按照需要还能获取到Field、Constructor、Method。
    • Field的操作主要涉及到类别的获取以及数值的读取与赋值。
    • Constractor:可通过Class.newInstance()和Constructor.newInstance()创建类的对象实例,推荐后者。
    • Method算是反射的最核心的,发射都是为了调用某个Method的invoke方法。
    • 数组和枚举可以看成Class对待。

    反射是非常规手段,它会抛弃Java虚拟机的很多优化,所以同样功能代码,反射要比正常方式要慢,所以考虑到采用反射时,要考虑它的时间成本。
    参考文章:细说反射,Java 和 Android 开发者必须跨越的坎

    知识点总结
    反射机制.png

    相关文章

      网友评论

          本文标题:java基础_反射

          本文链接:https://www.haomeiwen.com/subject/fexkkhtx.html