java的反射机制
一、分析
比如:Person有什么那?姓名,身高,睡觉行为等;
而Class用于描述java的类,那有什么那?类名,所属包名,成员变量,成员方法等;
Class类没有构造方法,它对应的是个各类的字节码文件,也就是.class文件,
同理就是说,每一个类的字节码,都是Class类的实例对象;
Person的字节码是Class类的实例对象;Person.class就表示Person在内存中的字节码
Date的字节码同样也是Class类的实例对象;Date.class
一》得到各个字节码对应的实例对象(Class类型)三种方法
1.类名.class;如:System.class;
2.对象.getClass();如:new Date().getClass();前提必须由对象名;
3.Class.forName(“类名”);如;Class.forName(“java.util.Date”);
Class的静态方法forName();指定类的完整名称;
返回字节码文件。
反射用的通常是这种方式:因为写源程序的时候还不知道类的名字;
通常用一个变量表示;
二》有九个预定义的class对象,8个基本数据类型,1个void.class
三》
String s = "abc";
Class cls1 = s.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
//因为内存中只有一份字节码,所以不管用那种方式创建,都相等;
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
二、构造方法的反射应用(Constructor类):getConstructor()
一、.得到某个类的所有构造函数:所有的构造方法会装到一个数组里面;
Constructor[] constructors = Class.forName(“java.lang.String”).getConstructors();
二、getConstructor()得到某一个构造方法,构造方法那么多,得到的具体是哪一个那?
就要根据参数类型,参数类型用class对象表示
Constructor cons1 =
String.class.getConstructor(StringBuffer.class ,int.class);
就是表示得到String类中,参数为StringBuffer和int的构造方法;
三、Constructor类中有一个newInstance()方法,根据构造方法创建实例对象;
String str = (String)cons1.newInstance(new StringBuffer("abc"));
四、上面是利用String有参数的构造函数创建对象,其实Class类给我们提供了一个创建对象的方法newInstance(),但是这个方法只能调用无参数的构造方法,比较方便,
String s=(String)Class.forName(“java.lang.String”).newInstance();
同样也创建了一个String对象;
三、成员变量的反射(Field类)getField()
public class ReflectPoint {
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
ReflectPoint pt1 = new ReflectPoint(3,5);
//利用反射得到成员Y,用Filed类,先得到该类的字节码。
//然后得到字节码中的成员变量。
Field fieldY = pt1.getClass().getField("y");
//fieldY的值是多少那?5,错,fieldY对应的是该类的字节码中变量y
//fieldY不是对象身上的变量,而是类上,
//要用它去取某个对象上的值;
System.out.println(fieldY);
System.out.println(fieldY.get(pt1));
//获取x变量,因为x是私有的,java中定义了专用的方法
//反射获取私有变量getDeclaredField();
Field fieldX = pt1.getClass().getDeclaredField("x");
//前面已经获取,下面设置访问私有变量;
//强制访问,setAccessible(true)不能省略,否则运行失败,不让访问私有变量
fieldX.setAccessible(true);
System.out.println(fieldX.get(pt1));
..应用一下:
//将任意个对象中的所有String类型的成员变量所对应
//的字符串内容的“b”替换“a”;
public static void changeStringValue(Object obj)throws Exception
{
//获取字节码中所有的成员变量
Field[] fields = obj.getClass().getFields();
for(Field field : fields)
{
//遍历是否是String类型变量
//这里应该用==号,而不是equals,因为只有一份String字节码,他们用的都是同一分
if(field.getType() == String.class)
{
String oldValue = (String)field.get(obj);
//将b替换成a
String newValue = oldValue.replace("b", "a");
//把新值set给对象
field.set(obj, newValue);
}
}
}
public class ReflectPoint {
public String str1 = "ball";
public String str2 = "basketball";
public String str3 = "itcast";
@Override
public String toString()
{
return str1+"::"+str2+"::"+str3;
}
}
四、成员方法的反射(Method类)getMethod();
一》反射成员方法格式:
Method method = String.Class.getMethod(“charAt”,parameterType);
Method类 反射名 = 字节码.getMethod(“方法名”,参数列表);
注意:参数类型用class对象表示;
二》通过反射方法得到字节码里面的方法,再拿着方法作用于某个对象。
String str = "abcd";
//反射格式
Method methodCharAt = String.class.getMethod("charAt", int.class);
//调用方法invoke是方法对象的方法(methodCharAt);
System.out.println(methodCharAt.invoke(str, 3));
//如果对象参数为null,说明这个方法时静态的;
//System.out.println(methodCharAt.invoke(null, 3));
应用:调用某个类的Main方法:
public class ReflectMain {
/**
* @param args
*/
public static void main(String[] args)throws Exception {
// TODO Auto-generated method stub
//为什么要用反射的方式掉用main方法,直接类名.Main(new String[]{"111","222","444"});
//因为我们可能不知道用户给我们传递的是那个类的main方法
//假设args的第一个元素就是那个类名;
String startClassName = args[0];
Method mainMethod = Class.forName(startClassName).getMethod("main", String[].class);
//因为main是static,不通过对象调用,所一null
mainMethod.invoke(null,(Object) new String[]{"111","222"});
}
}
class TestArguments
{
public static void main(String[] args)
{
for(String arg : args)
{
System.out.println(arg);
}
}
}
五、数组的反射应用
一》数组的反射:
相同元素类型和相同维度(一维和二维)的两个数组的字节码类文件相等,也就是同一份字节码;
1.数组与Object的关系
//数组与Object的关系
public static void arrayMethod()
{
int[] arr1 = new int[]{1,2,3};
int[] arr2 = new int[4];
int[][] arr3 = new int[2][3];
String[] arr4 = new String[]{"a","b","c"};
//相同元素类型和相同维度(一维和二维)的两个数组的字节码类文件相等,也就是同一份字节码;
System.out.println(arr1.getClass() == arr2.getClass()); //true 相同元素类型,相同维度
System.out.println(arr1.getClass() == arr3.getClass());//false 相同元素类型,不同维度
System.out.println(arr1.getClass() == arr4.getClass());//false 不同元素类型,相同维度
//输出当前数组名称
System.out.println(arr1.getClass().getName());
//输出父类名称
System.out.println(arr1.getClass().getSuperclass().getName());//Object
System.out.println(arr4.getClass().getSuperclass().getName()); //Object
//所以数组的父类对象是Objec,因为数组是引用类型
Object obj1 = arr1;
Object obj2 = arr3;
Object obj3 = arr4;
//因为基本数据类型(int)不能转换Object数组
//Object[] obj4 = arr1;
//因为int[]是Object,而int[][] ==Object[]
Object[] obj5 = arr3;
//String也是Object,顾String[] == Object【】;
Object[] obj6 = arr4;
//数组不能直接打印,可以遍历,而list列表可以直接打印
//Arrays工具类中asList方法将数组转换成List
//因为asList(Object【】 obj);跟int【】不匹配,
System.out.println(Arrays.asList(arr1));
System.out.println(Arrays.asList(arr4));
}
打印数组:
public static void arrayReflect(Object obj)
{
Class clazz = obj.getClass();
//取出字节码,判断是否是数组
if(clazz.isArray())
{
//得到某个数组对象的长度Array.getLength(obj)
int len = Array.getLength(obj);
for(int i=0;i<len;i++)
{
//获取数组对象的第i个元素Array.get(obj, i);
System.out.println(Array.get(obj, i));
}
}
else
{
System.out.println(obj);
}
}