代码改变世界

反射

2019-04-18 16:20  般若Android  阅读(278)  评论(0编辑  收藏  举报

---恢复内容开始---

 

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

  反射能够在程序运行时修改程序的行为

  反射技术应该遵守的规格和限制

  反射入口

  Class 因为java是面向对象的语言,基本上是以类为基础构造了整个程序系统,反射中要求提供的规格说明书就是一个类的规格说明书,它既是Class

  注意Class是首字母大写,不同于class小写,class是定义类的关键字,而Class的本质也是一个类,因为在java中一切都是对象

    public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {}
  

  Class就是一个对象,它用来代表运行在java虚拟机中的类和接口

  Class获取

  反射的入口是Class,但是反射中Class是没有公开的构造方法的,所以就没有办法像创建一个类一样通过new关键字来获取一个Class对象

  获取Class的三种方式

  1.通过Object.getClass()

  Car car=new Car();

  Class clazz=car.getClass();

  对于一个对象而言,如果这个对象可以访问,那么调用getClass方法就可以获取它的相应的Class对象

  值得注意的是,这种方法基本数据类型如int float等等

  2.通过.class标识

  上面的例子中 car是一个类,car是它的对象,通过car.getClass()就获取了Car这个类的Class对象,也就是说通过一个类的实例的getClass()方法就能获取到Class

  如果不想创建这个类的实例,需要.class标识

  Class class1=Car.class;

  Class class2=int.class;

  3.通过Class.forName()方法

  有时候我们没有办法去创建一个类的实例,甚至没有办法用Car.class这样的方式获取一个类的Class对象 这在Android领域中很常见,因为某种目的,Android工程师在一些类中加入@hide注解,所示这些类就没有出现在SDK当中,java提供了Class.foeName(),只要给这个方法中传入一个类的全限名称就好了,那么它就会在java虚拟机中寻找这个类有没有被加载

  Class clz=Class.forName("com.example.Car");

  Class内容清单

  仅仅拿到Class对象还不够,我们感兴趣的是它的内容

  在正常的代码编写中,我们如果要编写一个类,一般会定义它的属性和方法,

  public class Car{

  private String color;

  private Color mColor;

  public enum Color{

    RED,WHITE

      }

    public Car(){

    super();

    }

    public Car(String mBand){

      this.mBand=mBand;

    }

 

  pubic void drive(){

  System.out.println("go");

  }

  }

  Class的名字

  Class对象也有名字,涉及的API有

  Class.getName()

  Class.getSimpleName()

  Class.getCanonicalName()

  区别 因为Class是一个入口,它代表引用、基本数据类型、甚至是数组对象,所以huo他们的方式有点不同

  getName()当Class代表一个引用时返回二进制显示的字符串(com.example.car)

  当代表一个基本数据类型的时候返回的是关键字 int

  当代表基本数据类型数组的时候 int[][][]三维数组 返回的是[[[\

  为什么会这样那 因为java本身对于这一块制定了相应的规则,在元素的类型前面添加相应的数量的[符号,用[个数来提示数组的维度,并且住的注意的是对于基本数据类型或者类都有相应的编码,所谓的编码大多数都是用一个大写字母来指示某种类型规则如下

  需要注意的是类或者接口的类型编码是L类名;的形式

  比如String[].getClass().getName()结果是[LJava.lang.String;.

  getSimpleName()自然去获取simplename对于一个Class而言什么是SimpleName首先从嵌套开始

  public class Outter{

  static class Inner{

  }

  }

 

  Class clz=Outter.Inner.class;

  clz.getName()="com.example.Outter$Inner"

  clz.getSimpleName="Inner"

  Simplename不同

  需要注意的是当获取一个数组的Class中的Simplename时 不同于getName()方法 simplename不是前面加[而是后面加相应数量的[]

  

  Class clz = new Outter.Inner[][][]{}.getClass();

  System.out.println(" Inner Class name:"+clz.getName());
  System.out.println(" Inner Class simple name:"+clz.getSimpleName());

  Inner Class name:[[[Lcom.frank.test.Outter$Inner; Inner Class simple name:Inner[][][]

  还需要注意的是,对于匿名内部类getsimpleName返回的是一个空的字符串

  getCanonicalName

  前两者结合

  Class获取修饰符

  通常Java开发中定义一个类,往往是需要通过许多修饰符来配合使用的大致分为四类

  用来限制作用域 public protected private 

  用来提示子类复写 abstract

  用来标记为静态类static

   注解

  package com.frank.test; public abstract class TestModifier {

  }

  提取修饰符 Class.getModifiers()

  

  System.out.println("modifiers value:"+TestModifier.class.getModifiers());
  System.out.println("modifiers :"+Modifier.toString(TestModifier.class.getModifiers()));
  打印结果

  modifiers value:1025

  modifiers :public abstract

  获取Class成员

  一个类的成员包括属性(字段)方法 对应到Class中就是Field Method Constructor

  获取Field

  获取指定名字的属性的的有2个API

  public Field getDeclaredField(String name)
  throws NoSuchFieldException,
  SecurityException;

  public Field getField(String name)
  throws NoSuchFieldException,
  SecurityException
  两者 的区别是getDeclaredField()获取的是Class中被private修饰的属性。getField()方法获取的是非私有属性,并且getField()在当前Class获取不到时会向祖先类获取

  获取所有属性

  public Field[] getDeclaredFields() throws SecurityException {}

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

    
  类或者接口中的方法对应到Class就是Method

  相应的API如下

  

  public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

  public Method getMethod(String name, Class<?>... parameterTypes)

  public Method[] getDeclaredMethods() throws SecurityException


  public Method getMethod(String name, Class<?>... parameterTypes)

  获取Constructor

  Java反射把构造器从方法中单独拎出来用Constructor表示

 

  public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

  public Constructor<T> getConstructor(Class<?>... parameterTypes)

  public Constructor<?>[] getDeclaredConstructors() throws SecurityException

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

  我们获取了Field Method Constructor 但这不是终点 这正是反射机制中开始的地方,我们运用反射的目的是为了获取和操控Class对象中的这些成员

  Field的操控

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

  public class Son extends Farther {  

  int c;

  private String d;

  protected float e;

  Car car; }

  像 c d e car这些变量都是属性 在反射机制中映射到Class对象中都是Field很显然它们也有对应的类别

  它们是8中基本数据类型  或者引用 所有的引用都是Object的后代

  Field类型的获取

  获取Field的类型通过2个方法

  public Type getGenericType() {}

   public Class<?> getType() {}

  注意,两者返回的类型不一样 getGenericType()方法能够获取泛型类型

  Field内容的读取和赋值

  这个应该是反射机制中对于field的最主要目的

  Field定义了一系列的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

  Field又定义了一系列set方法用来对其自身进行赋值

  方法中的Object  其实就是类的实例引用,

  Class本身不对成员进行存储,它只提供检索,所以需要用Field Method,Constructor对象来承载这些成员,所以,针对成员的操作时,一般需要为成员指定类的实例引 用,Object为了精确定位

  当set和getField时 常规操作对于private修饰的会报异常 需要Field.setAccessible(true)

  Method的操控

  Method对应普通类的方法

  方法的构成

  public int add(int a,int b)

  方法要素

  方法名  

  方法参数

  方法返回值

  方法修饰符

  方法可能抛出异常

  Method获取方法名

  getName()

  Method获取方法参数

  public Parameter[] getParameters() {}

  返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。经常使用的方法有:

  Parameter.java

  // 获取参数名字 public String getName() {}  

  // 获取参数类型 public Class<?> getType() {}

  // 获取参数的修饰符 public int getModifiers() {}

  Method方法的执行

  整个反射机制的核心内容 很多时候使用反射的目的就是为了以非常规手段执行Method

  public Object invoke(Object obj, Object... args) {}

  Method调用invoke的时候 存在很多细节

  1.invoke方法中第一个参数是Object实质上是Method所依附的Class对应的类的实例,如果这个方法是一个静态方法,那么object为null,后面的可变参数Object对应的是参数

  2.invoke返回的对象是Object 所以执行的时候需要强制转换

  3.在对Method调用invoke的时候如果方法本身会抛出异常,那么这个异常就会经过包装,由Method统一抛出InvocationTargeExpection而通过InvocationTargeExpection.getCause

   可以获取真正的异常

  下面同样通过例子来说明,我们新建立一个类,要添加一个 static 修饰的静态方法,一个普通的方法和一个会抛出异常的方法。

   public class TestMethod{

  public static void testStatic(){

    

  }

  private int add(int a,int b){

    return a+b;

  }

 

  public void testExpection() throw IllegalAccessExpection{

    throw new IllegalAccessExpection("“。。。。”)

  }

 

  }

 

  测试代码

  Class testCls=TestMethod.class;

  Method mStatic=testCls.getMethod("testStatic",null);

  mStatic.invoke(null,null);

  TestMethod t=new TestMethod();

  Method mAdd=testCls.getDeclareMethod("add",int.class,int.class)

  mAdd.setAccessible(true)

  mAdd.invoke(t,1,2);

  Method testExcep=testCls.getMethod("testExpection",null);

  testExcep.invoke(t,null)

 

  Constructor的操控

  Constructor同Method差不多,但是它的特别之处在于,它能够创建一个对象

  在Java反射机制中有两种方法可以用来创建类的对象实例 Class.newInstance()和Constructor.newInstance

  官方文档建议开发者使用后面这种方法原因如下

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

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

  

  
  

 

  

 

 

---恢复内容开始---

 

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

  反射能够在程序运行时修改程序的行为

  反射技术应该遵守的规格和限制

  反射入口

  Class 因为java是面向对象的语言,基本上是以类为基础构造了整个程序系统,反射中要求提供的规格说明书就是一个类的规格说明书,它既是Class

  注意Class是首字母大写,不同于class小写,class是定义类的关键字,而Class的本质也是一个类,因为在java中一切都是对象

    public final class Class<T> implements java.io.Serializable,
    GenericDeclaration,
    Type,
    AnnotatedElement {}
  

  Class就是一个对象,它用来代表运行在java虚拟机中的类和接口

  Class获取

  反射的入口是Class,但是反射中Class是没有公开的构造方法的,所以就没有办法像创建一个类一样通过new关键字来获取一个Class对象

  获取Class的三种方式

  1.通过Object.getClass()

  Car car=new Car();

  Class clazz=car.getClass();

  对于一个对象而言,如果这个对象可以访问,那么调用getClass方法就可以获取它的相应的Class对象

  值得注意的是,这种方法基本数据类型如int float等等

  2.通过.class标识

  上面的例子中 car是一个类,car是它的对象,通过car.getClass()就获取了Car这个类的Class对象,也就是说通过一个类的实例的getClass()方法就能获取到Class

  如果不想创建这个类的实例,需要.class标识

  Class class1=Car.class;

  Class class2=int.class;

  3.通过Class.forName()方法

  有时候我们没有办法去创建一个类的实例,甚至没有办法用Car.class这样的方式获取一个类的Class对象 这在Android领域中很常见,因为某种目的,Android工程师在一些类中加入@hide注解,所示这些类就没有出现在SDK当中,java提供了Class.foeName(),只要给这个方法中传入一个类的全限名称就好了,那么它就会在java虚拟机中寻找这个类有没有被加载

  Class clz=Class.forName("com.example.Car");

  Class内容清单

  仅仅拿到Class对象还不够,我们感兴趣的是它的内容

  在正常的代码编写中,我们如果要编写一个类,一般会定义它的属性和方法,

  public class Car{

  private String color;

  private Color mColor;

  public enum Color{

    RED,WHITE

      }

    public Car(){

    super();

    }

    public Car(String mBand){

      this.mBand=mBand;

    }

 

  pubic void drive(){

  System.out.println("go");

  }

  }

  Class的名字

  Class对象也有名字,涉及的API有

  Class.getName()

  Class.getSimpleName()

  Class.getCanonicalName()

  区别 因为Class是一个入口,它代表引用、基本数据类型、甚至是数组对象,所以huo他们的方式有点不同

  getName()当Class代表一个引用时返回二进制显示的字符串(com.example.car)

  当代表一个基本数据类型的时候返回的是关键字 int

  当代表基本数据类型数组的时候 int[][][]三维数组 返回的是[[[\

  为什么会这样那 因为java本身对于这一块制定了相应的规则,在元素的类型前面添加相应的数量的[符号,用[个数来提示数组的维度,并且住的注意的是对于基本数据类型或者类都有相应的编码,所谓的编码大多数都是用一个大写字母来指示某种类型规则如下

  需要注意的是类或者接口的类型编码是L类名;的形式

  比如String[].getClass().getName()结果是[LJava.lang.String;.

  getSimpleName()自然去获取simplename对于一个Class而言什么是SimpleName首先从嵌套开始

  public class Outter{

  static class Inner{

  }

  }

 

  Class clz=Outter.Inner.class;

  clz.getName()="com.example.Outter$Inner"

  clz.getSimpleName="Inner"

  Simplename不同

  需要注意的是当获取一个数组的Class中的Simplename时 不同于getName()方法 simplename不是前面加[而是后面加相应数量的[]

  

  Class clz = new Outter.Inner[][][]{}.getClass();

  System.out.println(" Inner Class name:"+clz.getName());
  System.out.println(" Inner Class simple name:"+clz.getSimpleName());

  Inner Class name:[[[Lcom.frank.test.Outter$Inner; Inner Class simple name:Inner[][][]

  还需要注意的是,对于匿名内部类getsimpleName返回的是一个空的字符串

  getCanonicalName

  前两者结合

  Class获取修饰符

  通常Java开发中定义一个类,往往是需要通过许多修饰符来配合使用的大致分为四类

  用来限制作用域 public protected private 

  用来提示子类复写 abstract

  用来标记为静态类static

   注解

  package com.frank.test; public abstract class TestModifier {

  }

  提取修饰符 Class.getModifiers()

  

  System.out.println("modifiers value:"+TestModifier.class.getModifiers());
  System.out.println("modifiers :"+Modifier.toString(TestModifier.class.getModifiers()));
  打印结果

  modifiers value:1025

  modifiers :public abstract

  获取Class成员

  一个类的成员包括属性(字段)方法 对应到Class中就是Field Method Constructor

  获取Field

  获取指定名字的属性的的有2个API

  public Field getDeclaredField(String name)
  throws NoSuchFieldException,
  SecurityException;

  public Field getField(String name)
  throws NoSuchFieldException,
  SecurityException
  两者 的区别是getDeclaredField()获取的是Class中被private修饰的属性。getField()方法获取的是非私有属性,并且getField()在当前Class获取不到时会向祖先类获取

  获取所有属性

  public Field[] getDeclaredFields() throws SecurityException {}

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

    
  类或者接口中的方法对应到Class就是Method

  相应的API如下

  

  public Method getDeclaredMethod(String name, Class<?>... parameterTypes)

  public Method getMethod(String name, Class<?>... parameterTypes)

  public Method[] getDeclaredMethods() throws SecurityException


  public Method getMethod(String name, Class<?>... parameterTypes)

  获取Constructor

  Java反射把构造器从方法中单独拎出来用Constructor表示

 

  public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)

  public Constructor<T> getConstructor(Class<?>... parameterTypes)

  public Constructor<?>[] getDeclaredConstructors() throws SecurityException

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

  我们获取了Field Method Constructor 但这不是终点 这正是反射机制中开始的地方,我们运用反射的目的是为了获取和操控Class对象中的这些成员

  Field的操控

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

  public class Son extends Farther {  

  int c;

  private String d;

  protected float e;

  Car car; }

  像 c d e car这些变量都是属性 在反射机制中映射到Class对象中都是Field很显然它们也有对应的类别

  它们是8中基本数据类型  或者引用 所有的引用都是Object的后代

  Field类型的获取

  获取Field的类型通过2个方法

  public Type getGenericType() {}

   public Class<?> getType() {}

  注意,两者返回的类型不一样 getGenericType()方法能够获取泛型类型

  Field内容的读取和赋值

  这个应该是反射机制中对于field的最主要目的

  Field定义了一系列的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

  Field又定义了一系列set方法用来对其自身进行赋值

  方法中的Object  其实就是类的实例引用,

  Class本身不对成员进行存储,它只提供检索,所以需要用Field Method,Constructor对象来承载这些成员,所以,针对成员的操作时,一般需要为成员指定类的实例引 用,Object为了精确定位

  当set和getField时 常规操作对于private修饰的会报异常 需要Field.setAccessible(true)

  Method的操控

  Method对应普通类的方法

  方法的构成

  public int add(int a,int b)

  方法要素

  方法名  

  方法参数

  方法返回值

  方法修饰符

  方法可能抛出异常

  Method获取方法名

  getName()

  Method获取方法参数

  public Parameter[] getParameters() {}

  返回的是一个 Parameter 数组,在反射中 Parameter 对象就是用来映射方法中的参数。经常使用的方法有:

  Parameter.java

  // 获取参数名字 public String getName() {}  

  // 获取参数类型 public Class<?> getType() {}

  // 获取参数的修饰符 public int getModifiers() {}

  Method方法的执行

  整个反射机制的核心内容 很多时候使用反射的目的就是为了以非常规手段执行Method

  public Object invoke(Object obj, Object... args) {}

   Constructor 同 Method 差不多,但是它特别的地方在于,它能够创建一个对象。

  Class.newInstance() 只能调用无参的构造方法,而 Constructor.newInstance() 则可以调用任意的构造方法。
  Class.newInstance() 通过构造方法直接抛出异常,而 Constructor.newInstance() 会把抛出来的异常包装到 InvocationTargetException 里面去,这个和 Method 行为一致。
  Class.newInstance() 要求构造方法能够被访问,而 Constructor.newInstance() 却能够访问 private 修饰的构造器。
  例如

  Class clz=Test.Class

  Constructor con=clz.getConstructor(String.class);

  Test test=(Test)con.newInstance("zhao");

 

  总结

   1.Java中的反射是非常规编码方式

  2.Java反射机制的操作入口是获取Class文件,有Class.forName() .class object.getClass();

  3.获取Class对象还不够需要获取它的Members 包括field Method Constructor

  4.Field的操作主要涉及到类别的获取、以及数值的读取与赋值

  5.Method 算是反射机制中最核心的内容,通常的反射都是为了调用某个方法的invoke()

  6.通过Class.newInsatnce和Construction.newInstance都可以创建对象实例,推荐后者

  7.数组和枚举可以被看成普通的Class对待

  

  反射是非常规手段 它会抛弃Java虚拟机的很多优化,所以同样功能的代码。反射要比正常方式要慢,所以要考虑它的时间成本