反射
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虚拟机的很多优化,所以同样功能的代码。反射要比正常方式要慢,所以要考虑它的时间成本