---------------------- android培训、java培训、期待与您交流! ----------------------
6..反射:
6.1、得到字节码的方法:
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass()
详细请查看:http://edu.csdn.net/heimaClass.forName("类名"),例如,Class.forName("java.util.Date");
反射中一般用第三种方法,同一对象这三种方法加载的class对象是相同的。
例:
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");//没有.class
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
System.out.println(cls1.isPrimitive());//false
System.out.println(int.class.isPrimitive());//ture
//判断是否为基本类型对象.有九种预定义的 Class 对象,表示八个基本类型和 void。//这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、//char、short、int、long、float 和 double。
System.out.println(int.class == Integer.class);//false
//Integer 类在对象中包装了一个基本类型 int 的值。Integer 类型的对象包含
//int 类型的字段。TYPE表示基本类型 int 的 Class 实例。
System.out.println(int.class == Integer.TYPE);//true
System.out.println(int[].class.isPrimitive());//false
//判断是否为数组类型true
System.out.println(int[].class.isArray());
注意:加载了字节码,并调用了其getMethods之类的方法,但是没有看到类的静态代码块被执行,只有在第一个实例对象被创建时,这个静态代码才会被执行。准确的说,静态代码块不是在类加载时被调用的,而是第一个实例对象被创建时才执行的。
注意从后面示例中总结Class类常用方法。
6.2、构造方法反射:
Constructor常用方法:
boolean equals(Object obj) 将此 Constructor 对象与指定的对象进行比较。
Class<T> getDeclaringClass()
返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类。
Type[] getGenericParameterTypes() 按照声明顺序返回一组 Type 对象,这些对象表示此 Constructor 对象所表示的方法的形参类型。
int getModifiers()
以整数形式返回此 Constructor 对象所表示构造方法的 Java 语言修饰符。
String getName() 以字符串形式返回此构造方法的名称。
Class<?>[] getParameterTypes() 按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。
T newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
String toString() 返回描述此 Constructor 的字符串。
得到某个类所有的构造方法:
例子:Constructor [] constructors=
Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:Constructor constructor = Class.forName(“java.lang.String”).
getConstructor(StringBuffer.class);
向方法中传入类.class用于区分得到哪个构造函数
创建实例对象:
通常方式:String str = new String(new StringBuffer("abc"));
反射方式: String str =
(String)constructor.newInstance(new StringBuffer("abc"));
Class.newInstance()方法:
String obj =
(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
6.3、变量反射:
Field类包括得到指定对象字段的方法、设置指定对象字段的方法、得到指定对象字段名的方法与类型(getType)等,具体参见API。
示例:
Person.class
public class Person {
public String name;
private int age;
public String add;
public Person(String name, int age) {
this.name = name;
this.age = age;}
public Person(String name, int age, String add) {
this(name,age);
this.add = add;}
void printPerson(){
System.out.println("name:"+name+" age:"+age);}}
Person p2=new Person("xiangwang", 38);
Field nameF=p2.getClass().getField("name");//返回指定public修饰的
//Field对象。得到方法和构造函数的函数也是如此。
System.out.println(nameF.get(p2));
nameF.set(p2, "LiSi");
System.out.println(nameF.get(p2));
//暴力反射
Field ageF=p2.getClass().getDeclaredField("age");//返回申明过的指定
//Field对象,包括private修饰的字段,也有类似得到方法和构造函数的函数。
ageF.setAccessible(true);//设置值可以访问,Method和Constructor也有类似//函数,但是,此方法都在他们的父类AccessibleObject中。
System.out.println(ageF.get(p2));
练习:用反射将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"。
答案:
Person p=new Person("bobo", 23, "bbdiyilou");
Class cla=p.getClass();
Field []fields=cla.getFields();
for(Field f:fields){
if(f.getType()==String.class){
System.out.println("old:"+f.get(p));
f.setAccesible(true);
String oldStr=(String) f.get(p);
String newStr=oldStr.replace('b', 'a');
f.set(p, newStr);
System.out.println("new:"+f.get(p));
}
}
cla.getDeclaredMethod("printPerson").invoke(p);
6.4、方法反射:
Method类常用方法:
getName() 以 String 形式返回此 Method 对象表示的方法名称。
Class<?>[] getParameterTypes() 按照声明顺序返回 Class 对象的数组,这些
对象描述了此 Method 对象所表示的方法的形参类型。
Class<?> getReturnType() 返回一个 Class 对象,该对象描述了此 Method 对象所
表示的方法的正式返回类型。
Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
String toGenericString() 返回描述此 Method 的字符串,包括类型参数。
boolean equals(Object obj) 将此 Method 与指定对象进行比较。Constructor和Field
重新过此方法。
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").
getMethod("charAt", int.class);//得到charAt方法,此方法需要一个int类型参数。
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式: System.out.println(charAt.invoke(str, 1));
//调用str对象的charAt方法,传入参数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})形式。
示例:见6.3练习。
练习:用反射方法调用一个类的main方法。
答案:
public class PrintArgs {
public static void main(String[] args) {
for(String arg:args){
System.out.println(arg+" ");}}}
public class InvokeMain {
public static void main(String[] args) throws Exception {
PrintArgs.main(new String[]{"ddd","fffff"});
Method main1=
Class.forName("PrintArgs").getMethod("main",String[].class);
//main1.invoke(null,new String[]{"ddd","ewew"});//错误,解释如下
main1.invoke(null,new Object[]{new String[]{"ddd","ewew"}});}}
错误原因:启动Java程序的main方法的参数是一个字符串数组,通过反射方式来调用这个main方法时,按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,jdk1.5为了要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数传递给main方法。所以,在给main方法传递参数时,不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题。
解决办法:
mainMethod.invoke(null,new Object[]{new String[]{"xxx","ddddd"}});
mainMethod.invoke(null,(Object)new String[]{"xxx","dddd"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了。
7.内存泄露问题:java中存在内存泄露问题。例如:
Collection collections = new HashSet();
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);
//pt1.y = 7;
//collections.remove(pt1);
System.out.println(collections.size());
以上代码ReflectPoint类中的传入参数都参与了hash值的计算。当去掉注释后,pt1删除不了,并且还有引用。同时new ReflectPoint(3,3)也放不进集合中了。
---------------------- android培训、java培训、期待与您交流! ----------------------