黑马程序员_java基础加强2

---------------------- 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);//设置值可以访问,MethodConstructor也有类似//函数,但是,此方法都在他们的父类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培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net/heima

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值