一开始接触反射很头疼,不知道有什么用处,也没有从宏观来看待它,把张老师的视频看了三遍以后,加上查了些资料,有点了解了,还是不太深入,总结下吧。
一、什么是反射
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
总结:就是把类的共性抽取得到更为概括的类,比如类都有类名、构造函数、成员方法、成员方法等等。即就是把Java类中的各种成分映射成相应的java类。
二、反射的作用
因此就可以知道反射的作用了,就是在程序运行的过程中,可以动态地知道所运行对象的类以及类的成员变量、成员方法和通过这个类的构造函数来创建对象。
三、反射相关的API
那么如何来做上述事情呢?需要一些特定的工具,即:
(1)Class
(2)Constructor
(3)Filed
(4)Method
(5)Array
1.Class类
要想对某个类进行反射,首先要得到这个类的字节码,即得到Class的实例对象,而Class类就提供了这个功能。
如何得到各个字节码对应的实例对象
--类名.class,例如:System.class
--对象.getClass(),例如:new Data().getClass
--Class.forName(类名),例如:Class.forName(“java.util.Data”);
package cn.itcast;
public class Reflect {
public static void main(String[] args) throws Exception {
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
/*这三个字节码为同一份*/
System.out.println((cls1 == cls2));
System.out.println((cls2 == cls3));
}
}
通过以上程序分析可以验证,三种方法得到的Class实例对象是同一个。
注意:返回的方式有两种:
一、这份字节码曾经被加载过,已经在Java虚拟机里面了,直接返回;
二、Java虚拟机中还没有这份字节码,则用指定的类加载器去加载,把加载进来的类就缓存在虚拟机里面,以后要懂得到就不用加载了。
2.Constructor类
那怎么由字节码在运行时生成实例呢?就用到了Constructor
(1)得到某个类所有的构造方法
例子:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
(2)得到某一个构造方法:
例子:Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型
(3)创建实例对象:
通常方式:String str = new String(newStringBuffer("abc"));
注:String(StringBuffer buffer) 分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列
反射方式:String str =(String)constructor.newInstance(new StringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
(4)Class.newInstance()方法:
例子:String obj = (Sting)Class.forName("java.lang.String").newInstance();
该方法内部首先得到默认的构造方法,然后用该构造方法穿件实例对象;
该方法的内部的具体代码用到了缓存机制来保存默认构造方法的实例对象。
package cn.itcast;
import java.lang.reflect.Constructor;
public class Java_18_Constructor {
public static void main(String[] args) throws Exception {
/*getConstructor得到指定参数的构造函数
用反射得到 new String(new StringBuffer("abc"));*/
// 第一个StringBuffer表示选择哪一个构造方法
Constructor constructor = String.class.getConstructor(StringBuffer.class);
//第二个StringBuffer表示用这个构造方法的时候传一个StringBuffer的对象
String str = (String)constructor.newInstance(new StringBuffer("abc"));
//输出角标为2的字母
System.out.println(str.charAt(2));
}
}
总结:用getConstructor得到对应字节码上的构造方法,然后再用该构造方法去创建一个实例对象。
3.Field类
代表取得某一个类中的一个成员变量,那么得到的Field对象是对应类上的成员变量还是对应对象上的成员变量呢?package cn.itcast;
import java.lang.reflect.Field;
public class Java_19_ReflectPoint {
public static void main(String[] args) throws Exception {
ReflectPoint pt1 = new ReflectPoint(3, 5);
/*用pt1.getClass()得到字节码,字节码.getField得到字段,
有多个成员变量,用变量名字作为参数区别*/
Field fieldY = pt1.getClass().getField("y");
/*fieldY只代表类上的一个变量,不代表变量在某个对象上的值;
用它提取变量在对象上的值*/
System.out.println(fieldY.get(pt1));
//getDeclaredField 强制私有的可见
Field fieldX = pt1.getClass().getDeclaredField("x");
//暴力反射,不管同不同意强制去取
fieldY.setAccessible(true);
System.out.println(fieldX.get(pt1));
}
}
class ReflectPoint {
private int x;
public int y;
//生成构造方法的源代码shift+alt+s-->o
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
总结:代表的是字节码的一个成员变量,不代表某个对象上的变量,得到字段后可以到对象上去取得对象上的成员变量。
(4)Method类
既然Field可以得到某个类中的一个成员变量,那么自然也可以得到某个类的一个成员方法了,Method类就是这么一个类。
主要方法:
Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法
public Method getMethod(String name, Class<?>... parameterTypes)
例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式:System.out.println(charAt(1));
反射反射:System.out.println(charAt.invoke(str,1));
注意:如果传递给Method对象的方法invoke的第一个参数为null,说明该Method对应的是一个静态方法;
package cn.itcast;
import java.lang.reflect.Method;
public class Java_22_Method {
public static void main(String[] args) throws Exception {
String str = "abc";
/*得到字节码中的某个方法,字节码.getMethod();
再用这个方法作用于对象, invoke() */
Method methodCharAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 1));
}
}
总结:先用反射的方式得到方法,然后再用这个方法作用于某个对象。与Field道理相同
(5)Array
所有具有相同元素类型和维数的数组都共享该Class
对象
package cn.itcast;
public class Java24_Arrays {
public static void main(String[] args) {
int[] a1 = new int[3];
int[] a2 = new int[4];
int[] [] a3 = new int[2][3];
String[] a4 = new String[3];
//所有具有相同元素类型和维数的数组都共享该 Class 对象
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass().getName());
/*父类都是Object*/
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
}
}
总结:所有具有相同元素类型和维数的数组都共享该Class对象
数组反射的应用:package cn.itcast;
import java.lang.reflect.Array;
public class Java_25 {
public static void main(String[] args) {
String[] a4 = {"111","222","333"};
printObject(a4);
}
//将任何类型的数据打印
private static void printObject(Object obj) {
//如果是数组类型,逐个打印
if(obj.getClass().isArray()) {
int len = Array.getLength(obj);
for(int i=0; i<len; i++) {
System.out.println(Array.get(obj, i));
}
}else {
System.out.println(obj);
}
}
}