14. 反射
14.1 Class类
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
Class.forName(String) 该方法返回字节码。如果字节码曾经被加载过,存在于jvm中,则直接返回。如果jvm中还没有此字节码,则用类加载器去加载,将其缓冲在jvm中
此方法会抛出ClassNotFoundException异常,因为输入的String有可能不是一个对象。
如何得到各个字节码对应的对象(即Class对象,但并不是有具体参数的实例对象):
1) 类名.class 如 Person.class
2) 对象.getClass() 如 new Date().getClass()
3) Class.forName(“类名”) 如Class.forName(“java.util.Date”)
class ReflectTest
{
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");
/*三者得到的都是同一份字节码,对应的都是String类,
而String类可以创造多个不同的实例化对象*/
System.out.println(cls1==cls2); //true
System.out.println(cls1==cls3); //true
System.out.println(cls2==cls3); //true
/*String类不是基本类型,Integer也不是,而Integer封装的TYPE是。
即:boolean、byte、char、short、int、long、float 和 double*/
System.out.println(cls1.isPrimitive()); //false
System.out.println(Integer.class.isPrimitive()); //false
System.out.println(Integer.TYPE==int.class); //true
System.out.println(Void.TYPE.isPrimitive()); //true。Void是一个占位符类
}
}
Class.newInstance():该方法内部先得到默认的空参数的构造方法,然后用该构造方法创建实例对象。该方法内部用了缓存机制来保存默认构造方法的实例对象。
如:Stringobj = (String)Class.forName(“java.lang.String”).newInstance () //得到空字符序列
14.2 反射
1) 反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等
2) 一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。
14.3 Constructor类
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
getConstructor()方法中传入的参数是想要获取的构造方法能够传入的参数,如果该构造方法能传入多个参数,getConstructor()中可以传入一个包含这些参数的数组,也可以利用1.5新特性,直接将各个参数写入,用“ , ”分割
使用Constructor中的newInstance()方法,可以将得到的构造方法实例化(即使用此方法),注意使用时需要进行强转,因为jvm并不知道得到的Constructor对象是哪个类中的构造方法。newInstance()中传入的参数就是对应构造方法传入的参数。
import java.util.*;
import java.lang.reflect.*;
class ConstructorDemo
{
public static void main(String[] args) throws Exception
{
Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
System.out.println(Arrays.toString(constructors));
//获得的构造方法是以StringBuffer对象为参数的。
Constructor constructor =
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
String str = (String)constructor.newInstance(new StringBuffer("abc"));
System.out.println(str);
}
}
14.4 Field类
Field类代表某个类中的一个成员变量
import java.lang.reflect.*;
class FieldDemo
{
public static void main(String[] args) throws Exception
{
ReflectPoint pt1 = new ReflectPoint(3,5);
//获取类中的成员变量
/*Field只代表获取到的类中的某个变量,不代表该变量在某个实例对象中的值
如果该成员变量用private修饰,则此方法会报出NoSuchFieldException异常
*/
Field fieldY = pt1.getClass().getField("y");
//获取类中的成员变量,private修饰的同样可以获取
Field fieldX = pt1.getClass().getDeclaredField("x");
//获取成员变量在某个实例对象中的值
System.out.println(fieldY.get(pt1));
//通过setAccessible(true)方法才能够得到private修饰的成员变量值
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
}
}
class ReflectPoint
{
private int x;
public int y;
ReflectPoint(int x,int y)
{
this.x = x;
this.y = y;
}
}
映射Field类应用:
//将某个对象实例中所有String类型的成员变量中的字符b替换成字符a:
import java.lang.reflect.*;
class FieldTest
{
public static void main(String[] args) throws Exception
{
ReFlectPoint2 pt2 = new ReFlectPoint2("hbhb","bob");
//获取包含所有成员变量的数组
Field[] fields = pt2.getClass().getFields();
for(Field field : fields)
{
//判断成员变量类是否是String 类型的类字节码用==比较,因为同一个类的字节码在虚拟机中是唯一的
if(field.getType() == String.class)
{
//获取该变量在pt2中的值
String oldValue = (String)field.get(pt2);
//将该值中的b字符替换成a字符
String newValue = oldValue.replace('b','a');
//将该变量在pt2中的值设置成新的值。
field.set(pt2,newValue);
}
}
System.out.println(pt2);
}
}
class ReFlectPoint2
{
public String str1;
public String str2;
public String str3 = "itcast";
ReFlectPoint2(String str1,String str2)
{
this.str1 = str1;
this.str2 = str2;
}
public String toString()
{
return str1 + ":" + str2 + ":" + str3;
}
}
14.5 Method类
Mthod类代表某个类中的一个成员方法
import java.lang.reflect.*;
class MethodDemo
{
public static void main(String[] args) throws Exception
{
String str = "abc";
//获得的方法是以一个int类型的值为参数的
Method methodcharAt =
Class.forName("java.lang.String").getMethod("charAt",int.class);
//对str调用此方法,invoke(obj,args)中第二个参数是原方法要传入的参数
System.out.println(methodcharAt.invoke(str,1));
//如果该方法是静态的,则通过Method的invoke方法调用时,第一个参数传null
//Method.invoke(null,args)
}
}
JDK1.4和JDK1.5的invoke方法的区别:
JDK1.5:public Object invoke(Objectobj,Object…args)
JDK1.4:public Object invoke(Objectobj,Object[] args)
则上述的methodcharAt.invoke(str,1)可以写成methodcharAt.invoke(str, new Object[ ]{1})
14.6 数组的反射
1) 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象
2) 代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class
3) 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。
4) Arrays.asList()方法处理int[]和String[]时的差异
import java.util.*;
class ArrayReflect
{
public static void main(String[] args)
{
int[] a1 = new int[]{1,2,3};
int[] a2 = new int[4];
int[][] a3 = new int[2][3];
String[] a4 = new String[]{"a","b","c"};
//a1和a2属于同一个类型
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(Arrays.asList(a1)); // 输出a1的内存地址
System.out.println(Arrays.asList(a4)); // 输出a,b,c
/*
因为需要兼容JDK1.4的方法,所以传入a4时,使用的是asList(Object[] a),
将数组中每一个String传入List中。而传入a1时,因为int不是Object子类,
使用的是asList(T... a),所以将a1作为一个参数(数组对象)传入到List当中。
因此a4可以输出每个字符串对象,而a1只能输出作为一个数组对象的内存地址
*/
}
}
14.7 反射的作用: 实现框架功能
1) 框架:
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类
2) 框架要解决的核心问题:
因为在写程序是无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要同反射方式来做。
利用反射建立的小框架:
框架并不知道需要建立什么样的集合对象,通过读取文件来建立相关的集合
import java.util.*;
import java.io.*;
class FrameworkDemo
{
public static void main(String[] args) throws Exception
{
//尽量面向父类或接口编程,通过读取文件来获取className的值
InputStream ips =
new FileInputStream("d:\\workspace\\java\\config.properties");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
//通过读取到的className建立相关类型的对象
Collection collections =
(Collection)Class.forName(className).newInstance();
ReflectPoint pt1 = new ReflectPoint(3,3);
ReflectPoint pt2 = new ReflectPoint(5,5);
ReflectPoint pt3 = new ReflectPoint(3,3);
collections.add(pt1);
collections.add(pt2);
collections.add(pt3);
collections.add(pt1);
//根据文件中设置className为ArrayList和HashSet得到不同的结果。
System.out.println(collections.size());
}
}