反射总结:
本文根据张孝祥老师Java高新技术和自己的理解总结了反射的主要知识点
a. 会遇到这样的需求,在编写代码时,基础代码需要调用后期才编写的类, 静态的代码写法是不能满足这点需求的
b. 所以,我们希望提供一种方式,可以在运行时期,动态地调用所需要的类,而不是更改早期编写的代码
c. 动态的特点是应用的信息一般是一个抽象的概念,静态的特点是应用的信息一般是一个抽象概念的具体个体,比如 Person类 (动态)类名=(静态)Person
d. 静态地调用类,可以理解为将调用通过类的自有的信息写死在程序中,比如 Person类,需要调用 Person类中的静态方法foo: Person.foo;
e. 动态地调用类,可以理解为将锁需调用的类的抽象的信息写在程序中,而自有的信息在运行时被传入, 比如 c中的例子, 类名.foo(后面可以通过反射的方法实现)
f. 根据e我们也可以发现,反射的另外一个好处, 通用性(框架性), 通过写一个程序就可以在后期运行时通过配置信息以实现对多个不同类的调用
g. 根据f,也可以理解了,反射的应用就像是我们设计了一个房间,什么地方摆床,衣柜,沙发都已经设计好了,但是摆什么牌子\尺寸的床,衣柜,沙发是后期住户自己选购安置的
2. 反射可以做什么,是怎么实现的?
a. 根据1, 我们知道,反射一定需要得到一个类的抽象的信息,这些信息是什么,不妨考虑一下类是怎么被定义的
b. 定义一个类: 写一段代码(Class)包含要有——构造函数(Constructor),成员变量(Field),成员方法(Method)——类\类中的构造函数及成员又有不同的: 成员修饰符(Modifier),又有注解(Annotation)
c. 得到b中的这些信息,都需要这些信息的载体,比如,这个类本身的编译信息字节码
d.所以,反射是可以通过得到一个类的字节码进而可以查找到一个构成一个类的构造函数,成员变量,成员方法,以及以上所有的修饰符和注解这些框架性(通用性)的信息
3. Java反射的结构是什么样的?
a. Class类——得到一个定义了的类的字节码并且将这些信息以一个类的形式体现, Xxx.class类——Class类是反射的基础——java.lang包
b. Constructor,Field,Method类——基于Class类得到某个Xxx.class类中的构造函数,成员变量,成员函数的信息——java.lang.reflect包
c. Modifier类——基于Class,Constructor,Field,Method类,得到其修饰符——java.lang.reflect
d. Annotation不是java.lang.reflect包中,不过Class,Constructor,Method类中的方法可以获得这类和类中属性有关的类的信息——java.lang.annotation包
e. reflect包中的这些类提供了丰富的获取getXxx、判断isXxx、以及调用(newInstance,invoke...)的方法, Class, Constructor, Method, Modifier没有提供也不应该提供对所调用类进行修改(setXxx)的方法
f. Field提供了修改(set之类)的方法,其更改是说在内存中更改了字节码内的值,class文件中,没有更改
g. Array类——java单独定义了Array类来处理和数组有关的反射——java.lang.reflect
4. 如何得到一个Class类?
a. 三种方法:
a.1 Class cls = Person.class; //这是一种写死的方式,因为要得到这个类必须提前知道这个类的名字,有一定的使用局限性
a.2 obj.getClass(); // 需要有一个类对象,通过这个对象来得到类的字节码,和a.1一样,有其局限性,更多是为了得到类的信息而使用
a.3 Class.forName(str); //Class类中的静态方法,通过一个类的名字来的到类的字节码, 这种是反射时最常用的,因为可以通过配置文件的方式读入str,局限性小,注意ClassNotFoundException
b. 8个基本元素+void是java中预定义的xxx.class对象
b.1 int.class!=Integer.class
b.2 int class==Integer.TYPE
5. 如何通过Class类获得Constructor/Field/Method/Modifier对象?
a. 得到一个Constructor
a.1 Constructor<T> constructor= T.class.getConstructor(Class<?> ... parameterType); //根据constructor的参数列表的parameterType.class类对象得到对应的constructor对象,类型要对应
a.2 Constructor<T>[ ] constructors=T.class.getConstructors(); //得到T.class中所有的构造函数对象
a.3 getDeclareConstructor(...)/getDeclareConstructors();//得到声明过的constructor,共有私有都可以得到, getConstructor只能得到accessible的constructor
a.4 T t=constructor.newInstance(new InitialArgs()...); //T是a中 Constructor指定的泛型, 如果不指定泛型,默认是Object
a.5 非共有的的constructor对象在使用newInstance前需要 t.setAccessible(true);
b. 得到一个Field
b.1 Field fieldOfName=T.class.getField(String name); //根据变量的名字得到T.class内的一个Field对象
b.2 Field[] fields=T.class.getField(); //得到所有声明可获取的成员变量
b.3 Object valueOfField=fieldbyClass.get(Object obj); //通过反射从类的对象的某个field得到这个field对应的值
b.4 field.set(Object obj, Object newValueOfField); // void 通过field更改一个对象obj的成员的值
b.5 getDeclaredField(s)及setAccessible和Constructor类似
c. 得到一个Method
c.1 getMethod(s)/getDeclaredMethod(s) setAccessible 和constructor里面对应的使用方法一致的
c.2 需要注意的是getMethod(methodName,Class<T> ...parameterType); // 需要方法的名字和参数列表.class
c.3 methodGetByReflection.invoke(Object obj, Object initalAgrs...); //通过反射对一个对象obj调用其methodGetByReflection方法 (类似newInstance/get)
6. Class类中还有那些方法,他们有什么用?
7. 数组也是一个类吗, 如何对数组进行反射操作?a. Enum, Class, Interface, Array, Annotation Primitive ... // Class提供了多种方法去对这些类别进行判断(isXxx),而判断是对各个类别进行相应操作的前提
b. Object newInstance(); // 给类创造一个实例对象
c. String getName(); //得到一个类的名字
d. Class<?> getComponentType();// 得到一个数组类型的元素类型的类
a. 数组也是一个类,其类名为 [ Xxx, 比较奇怪, 其父类是java.lang.Objectb. 对一个数组进行反射操作, 一般先判断传入的是否是Array, obj.isArray();
c. 对数组进行专门反射操作有一个类Array——全是静态方法——java.lang.reflect包
c.1 创造数组: Object newInstance(Class<?>, int length/int ... dimensions); //根据长度和类型
c.2 获取数组: Object get(Object array, int index); //参看getInt 等
c.3 更改数组: void set(Object array, int index, Object value); //参看setInt 等c.4 用反射操作数组的一个好处是, 器函数返回值是 Object; 返回类型方便数组去做类型转换