您的位置:首页 > 编程语言 > Java开发

Java高新技术之反射

2012-01-11 00:13 176 查看
一、反射的基石--Class类
1.xxx.java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物,是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别。Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等。学习反射,首先就要明白Class这个类。

人-->Person类

很多java类-->Class类

每个java类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。

2.Class类代表Java类,它的各个实例对象又分别对应什么呢?

对应各个类在内存中的字节码(一个类在虚拟机中通常只有一份字节码),例如,Person类的字节码,ArrayList类的字节码,等等。

一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?(譬如,都有方法,有字段,有父类,有包。)

3.如何得到各个字节码对应的实例对象( Class类型)

类名.class,例如,System.class

对象.getClass(),例如,new Date().getClass()

Class.forName("类名"),例如,Class.forName("java.util.Date");//要全名, 包名.类名

4.九个预定义Class实例对象:

8个基本数据类型(byte int short double等)加void

Int.class == Integer.TYPE//得到的Integer类里包装的基本数据类型int字节码

System.out.println("void:"+void.class.isPrimitive());//true 是原始数据
System.out.println("int:"+int.class.isPrimitive());//true
System.out.println("int[]:"+int[].class.isPrimitive());//false
System.out.println("int[]:"+int[].class.isArray());//ture
System.out.println(int.class==Integer.class);//false
System.out.println(int.class==Integer.TYPE);//true


二、反射
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。

三、Constructor类
Constructor类代表某个类中的一个构造方法

得到某个类所有的构造方法:

例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();

得到某一个构造方法:

例子: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);//根据构造方法的参数类型得到指定的构造方法

创建实例对象:

通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str = (String)constructor.newInstance(new StringBuffer("abc"));//获得的构造方法使用的参数类型要与原来一致

Class.newInstance()方法:

例子:Date date=(Date)Class.forName("java.util.Date").newInstance();

System.out.println(date);

该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。(由此可见反射是较耗资源的,会降低程序的性能)

四、Field类

Field类代表某个类中的一个成员变量

演示用eclipse自动生成Java类的构造方法

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。

示例代码:

ReflectPoint point = new ReflectPoint(1,7);

Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");

System.out.println(y.get(point));

//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");

Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");

x.setAccessible(true);

System.out.println(x.get(point));

练习:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。

public class ReflectPoint
{
private int x;
public int y;
public String str1="blue";
private String str2="bubble";
public String str3="soso";
public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}

public String toString()
{
return str1+" "+str2+" "+str3;
}
}


public class ReflectTest
{

public static void main(String[] args)throws Exception
{
//		Class cls1=System.class;
//		System.out.println(cls1);
//		Class clsP1=Person.class;
//		Class clsP2=new Person().getClass();
//		System.out.println(clsP1==clsP2);
//		Class cls2=Class.forName("java.lang.String");
//		Class clsP3=Class.forName("day01.Person");
//		System.out.println(clsP1==clsP3);
//
//		System.out.println("void:"+void.class.isPrimitive());//true
//		System.out.println("int:"+int.class.isPrimitive());//true
//		System.out.println("int[]:"+int[].class.isPrimitive());//false
//		System.out.println("int[]:"+int[].class.isArray());//ture
//		System.out.println(int.class==Integer.class);//false
//		System.out.println(int.class==Integer.TYPE);//true

Constructor[]  constructors=String.class.getConstructors();//得到String类的所有构造方法
Constructor constructor=String.class.getConstructor(StringBuffer.class);//得到指定参数的构造方法
//用构造方法创建实例
String s=(String)constructor.newInstance(new StringBuffer("JAVA"));
System.out.println(s);

//Class类里面的无参构造方法
Date date=(Date)Class.forName("java.util.Date").newInstance();
System.out.println(date);

ReflectPoint rp=new ReflectPoint(6,9);
Field fieldY=rp.getClass().getField("y");//注意fieldY不是对象的变量,是所在类的变量
int rpY=(Integer) fieldY.get(rp);//根据对象取得变量的值
System.out.println(rpY);

Field fieldX=rp.getClass().getDeclaredField("x");//因为x被private修饰
fieldX.setAccessible(true);//把私有的变量设置成可以访问
System.out.println(fieldX.get(rp));

changeStringValue(rp);
System.out.println(rp);

}
public static void changeStringValue(Object obj)
{
//getFields()只能获取公有的成员
Field []fields=obj.getClass().getDeclaredFields();//获取该对象所属类的所有字段,包括私有的
for (Field field : fields)
{
if(field.getType()==java.lang.String.class)//判断字段的类型
{
field.setAccessible(true);//非public修饰的字段设置成可访问

try
{
String s = (String)field.get(obj);
field.set(obj, s.replace('b', 'a'));
} catch (IllegalArgumentException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
}

}
}
}

}


五、Method类
@Method类代表某个类中的一个成员方法

得到类中的某一个方法:

例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);

@调用方法:

通常方式:System.out.println(str.charAt(1));

反射方式: System.out.println(charAt.invoke(str, 1));

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!

@jdk1.4和jdk1.5的invoke方法的区别:

Jdk1.5:public Object invoke(Object obj,Object... args)

Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。

用反射方式执行某个类中的main方法

@写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。用普通方式调完后,大家要明白为什么要用反射方式去调啊?用反射的话你要调用什么类的主方法,程序能根据你的输入进行调用。

@问题:启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new
String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。

@解决办法:

mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了

Method mainMethod=Class.forName(args[0]).getMethod("main", String[].class);
//调用方法(静态的用null)
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});//解决一
mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});//解决二


六、数组的反射

具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

int []a1=new int[3];
int []a2=new int[4];
int [][]a3=new int[3][4];
String []a4=new String[3];

System.out.println(a1.getClass()==a2.getClass());//true
System.out.println(a1.getClass()==a3.getClass());//false
System.out.println(a1.getClass()==a4.getClass());//false


代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。

System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
System.out.println(a2.getClass().getSuperclass().getName());//java.lang.Object
System.out.println(a3.getClass().getSuperclass().getName());//java.lang.Object
System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object


基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

Object aObj1 = a1;
Object aObj2 = a4;
//Object[] aObj3 = a1; ERROR!!!
Object[] aObj4 = a3;
Object[] aObj5 = a4;


Arrays.asList()方法处理int[]和String[]时的差异。(为了兼容jdk1.4造成的,jdk1.4中asList(Object []obj) 处理不了int[]只能给jdk1.5+处理,当成一个参数用Object接收了)

int []a1=new int[]{1,2,3};
String []a4=new String[]{"a","b","c"};
System.out.println(Arrays.asList(a1));//[[I@422ede]
System.out.println(Arrays.asList(a4));//[a, b, c]


Array工具类用于完成对数组的反射操作。

public static void printObject(Object obj)
{
Class cls=obj.getClass();
if(cls.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);
}

}


思考题:怎么得到数组中的元素类型?NO

需要取出每个元素对象,然后再对各个对象进行判断【arr[i].getClass().getName();】,因为其中每个具体元素的类型都可以不同,例如Object[] x = new Object[]{“abc”,Integer.Max}。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: