您的位置:首页 > 职场人生

黑马程序员---Java高新技术(三)--反射

2012-09-26 10:25 405 查看
------- android培训java培训、期待与您交流! ----------
15、反射的基础Class类
Class
类的实例表示正在运行的
Java应用程序中的类和接口。枚举是一种类,注释是一种接口。
1、什么是Class?
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。(比如:众多的人用Person表示,众多的Java类用Class表示)。
2、Class类的各个实例对象是什么?
1、Person类的实例对象是张菲这样的人,而Class实例对应各个类在内存中的字节码(例如Person类的字节码,String类的字节码)
2、一个类被加载到内存中,占用一定的存储空间,这个空间里面的内容就是类的字节码。不同的类字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象来表示,这些对象显然具有相同的类型,而这个类型是什么呢?(我想应该是字节码)
3、获取字节码的实例对象(三种方式)
      1、类名.class;例如:int[].class整型数组的字节码
      2、对象.getClass(
);例如:new Integer(7).getClass()获取整型保证类对象的字节码
3、Class.forName(“类名”);例如:Class.forName(“com.mysql.jdbc.Driver”)获取mysql驱动字节码
4、九个预定义Class实例对象
表示八个基本类型和 void。
基本的 Java类型(
boolean
byte
char
short
int
long
float
double
)和关键字
void
也可以表示为
Class
对象。例如:Class
cls = void.class,就表示一个对象。Class的对象就是一个字节码。比如:Class a = A.class就是代表A类的一个字节码,而这个字节码的类名就是Class。p1.getClass(
)就表示获取p1对象的字节码。
1、 Class.
isPrimitive():判定指定的
Class
对象是否表示一个基本类型。
2、 Integer.TYPE
== int.class//true,在Integer中Integer.TYPE表示基本类型
int

Class实例。
总之,只要在源程序中出现的类型,都有各自的Class实例对象。例如int[],void。
5、面试题:Class.forName( )的作用?
就是返回字节码,返回的方式有两种:1、字节码曾经被加载过,已经呆在虚拟机里,直接返回;2、java虚拟机里还没有这个字节码,则用类记载器去加载,把加载的字节码缓存在虚拟机里面。
反射的精辟理解:就是把Java类中的各种成分映射成相应的java类

示例代码:

package staticImport.cn.itcast.day1;

import java.lang.reflect.Constructor;

publicclass
RefectTest {

        publicstaticvoid
main(String args[])throws Exception {

                  String str =
"abc" ;

                  Class cls1 = str.getClass();//获取字节码的第一种方式

                  Class cls2 = Class.forName("java.lang.String");//获取字节码的第二种方式,比较常用

                  Class cls3 = String.class;//获取字节码的第三种方式

                  System.out.println(cls1
==cls2);//true

                  System.out.println(cls1
== cls3);//ture

                  System.out.println(cls3.isPrimitive());//false,判断是否是原始数据类型

                  System.out.println(int.class.isPrimitive());//true

                  //基本数据类型的包装类也不是原始数据类型

                  System.out.println(Integer.class.isPrimitive());//false

                  System.out.println(Integer.class
== int.class);//false

                  //包装类的type返回包装的基本数据类型的Class实例

                  System.out.println(Integer.TYPE
== int.class);//true

                  //数组类型的字节码对象也不是基本数据类型

                  System.out.println(int[].class.isArray());//true

                  

        }

}
16、构造方法的反射应用Constructor
      1、Constructor类代表某个类中的一个构造方法
      2、得到某个类中所有的构造方法
             Constructor[ ]constructors = Class.forName(“java.lang.String”).getConstructors();
3、 得到某一个构造方法
Constructor constructor = Class.forName(“java.lang.String”)
                                   .getConstructor(StringBuffer.class );

//获取方法时,要用到参数的类型,当然这个类型也是字节码
4、 创建对象实例
通常方式:String str = new String(new StringBuffer(“abc”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
//调用获得的方法时,要用到上面相同类型的实例对象
5、 Class.newInstance()方法
例如:String obj =(String)Class.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例。
方法内部是怎样编写的呢?利用了缓存机制来保存默认构造方法的实例对象
示例代码:

package staticImport.cn.itcast.day1;

import java.lang.reflect.Constructor;

publicclass
RefectTest {

        publicstaticvoid
main(String args[])throws Exception {

                  //new String(new StringBuffer("abc"))用反射的原理来实现

                  //实现--->拿着字节码--->搞一对象--->用搞出的对象,调用newInstance(),步骤详细解说:

//搞出的某种类型的(比如String类型)构造方法对象,到底对应着这种类型的(比如String类型)哪一个构造方法

                  //如下三条语句有很多异常。而且getConstructor利用了jdk1.5新特性,可变参数列表

                  Constructor constructor = Class.forName("java.lang.String")

                                                                                   .getConstructor(StringBuffer.class);

                  String string = (String)constructor.newInstance(new
StringBuffer("abc"));//【2】

                  System.out.println(string.charAt(0));

                  //两个StringBuffer:第一个StringBuffer表示选择哪个构造方法,

                  //第二个StringBuffer表示,用这个构造方法的时候还得传递一个StringBuffer的对象进去

//为什么要强制类型转换?1、因为程序在编译的时候,查看等号左右两边的类型,左边为String,

//右边函数返回Object所以类型不一致,导致编译失败(1的解释不正确)2、程序在编译的时候,

//严格进行语法检查,没有执行等号右边的东西,不知道constructor是String类型的还是其它类型的,

//所以在执行【2】的时候,constructor对象创建的实例对象是什么类型的人家也不知道,所以要类型转换

                  //告诉编译器我的返回值类型是String

        }

}
17、成员变量反射(Field类)
1、 Filed类代表某个类中的一个成员变量
2、 得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?
答:是对应到类上面的变量。字段fieldX代表的是x的定义,而不是具体的x变量。
3、getField方法只能访问public修饰的成员。如果想访问访问非public成员,就用getDeclaredField方法,并且用setAccessible(true)来修饰。
示例代码:

package staticImport.cn.itcast.day1;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

publicclass
RefectTest {

 publicstaticvoid
main(String args[])throws Exception {

           /**Field类**/

           ReflectPoint point =
new ReflectPoint(3,5);

           Field fieldY = point.getClass().getField("y");

           //只是fieldY的值是多少?5,错!这里的fieldY代表类字节码身上的变量,是类身上的,

//没有对应到对象身上。不代表某一个对象的属性值。如果想要获取某一个对象的属性值,就用get方法

/*老师总结:fieldY不是对象身上的变量,而是类上的,要用它去取某个对象上对应的属性值*/

           System.out.println(fieldY);

           System.out.println("获取point对象的y属性:"+fieldY.get(point));

           /*如果x的属性用private修饰,将抛出则该方法将抛出一个
IllegalAccessException异常

           Field fieldX = point.getClass().getField("x");

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

           如果要想访问:则调用Class中的getDeclaredField()*/

           Field fieldX = point.getClass().getDeclaredField("x");

           fieldX.setAccessible(true);

           System.out.println("获取point对象的x属性:"+fieldX.get(point));

/**问题:将任意一个对象中的所有String类型的成员变量,所对应的字段的内容中的b改为a*/

           changeStringValue(point);

           System.out.println(point);

 }

 publicstaticvoid
changeStringValue(Object obj)throws Exception {

           //getFields方法可以访问可访问的公共字段,当然protect、private的都不可以访问

           Field[] fields = obj.getClass().getFields();

           for(Field
field:fields){

                    System.out.println("1111"+field.get(obj));

                    if(field.getType()
==String.class){

                             String oldValue = (String)field.get(obj);

                             String newValue = oldValue.replace('a','b');

                             field.set(obj, newValue);

                    }

           }                

 }

}

class ReflectPoint{

 privateintx;

 publicinty;

 public
Stringstr1 ="basketball";

 protected
Stringstr2 ="aaaaaaaaaaCCCCCCCC";

 public
Stringstr3 ="ababababa";

 public
ReflectPoint(int x,
int y) {

           super();

           this.x
= x;

           this.y
= y;

 }

 public
String toString(){

           returnstr1+" "+str2+" "+str3;

 }
}
18、成员方法的反射(Method类)
强调一下,Method类的实例对象是类中的方法,而不是对象中的方法
1、 Method类代表某个类中的一个成员方法
2、 得到类中的某个方法
Method charAt = Class.forName(“java.lang.String”).getMethod(“charAt”,int.class);
3、 调用方法
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1))
如果传递给Method对象的invoke()方法的第一个参数为null,那就说明Method对象对应的是一个静态方法
4、 jdk1.4与jdk1.5在invoke方法上的区别
JDK1.5:public
Object invoke(Object obj, Object……args)
JDK1.4:public
Object invoke(Object obj,Object[ ]args)
Jdk1.5为了能够兼容jdk1.4,所以jdk1.5也会按照jdk1.4的语法使用invoke方法。需要将一个数组作为参数传递给invoke方法时,数组中每个元素分别对应被调用方法中的一个元素。故,调用charAt方法的代码也可以用jdk1.4改写为charAt.invoke(
“str”,new Object[]{1})
总结:Jdk1.4与Jdk1.5在反射的地方许多方法都不相同,1.4用数组,1.5用可变参数列表
专家模式:谁拥有数据,谁就是干这个事儿的专家,那么这个方法就应该分配给谁。

示例代码:

/**Method类*/RefectTest.java类中

        Method charAt = str.getClass().getMethod("charAt",int.class);

        System.out.println(charAt.invoke(str,
1));

System.out.println(charAt.invoke(str,new
Object[]{2}));//用jdk1.4的方式进行调用,Object数组长度为1

        //invoke:对带有指定参数的指定对象调用由此
Method 对象表示的底层方法。

//如果底层方法是静态的,那么可以忽略指定的obj
参数。该参数可以为 null。因为静态方法时不需要对象的!
        System.out.println(str.getClass().getMethod("valueOf",float.class).invoke(null,
12.3f));//12.3
19、对接收数组参数的成员方法进行反射(深入理解Method的调用)
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法?此处不用普通方式调用,而用反射方式调用,为什么?
答:关于那个问号的解释:我的源程序根本就不知道执行那个类,而执行的类是根据参数来决定的,你告诉我执行A类,我就执行A类,你告诉我执行B类,我就执行B类。一句话总结:用反射的方式去调用某一个类的main方法的时候,这个类是未知的,可变的,而不是固定的,在这种情况下用普通的方式调用某个类的主方法是行不通的!
问题:invoke方法中的参数问题?
答:启动Java程序的main方法的参数是一个字符串数组,即public
static void main(String args[]),通过反射采用者个main方法时,如果为invoke方法传递参数呢?按jdk1.5的说法,整个数组时一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底用那种语法惊醒处理呢?jdk1.5要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用main
Method.invoke(null,new String[]{“XXX”},javac只能把它当做jdk1.4的语法进行理解,而不是把它当做jdk1.5的语法进行解释,因此会出现参数类型不对的问题
解决方案

//第一种:将字符串转换成Object对象。注意每一个的数组父类都是Object。
        //就相当于跟编译器说:我是一个Object对象不是一个数组,不要给我拆包了!
      mainMethod.invoke(null,
(Object)new String[]{"abc","def","ghi"});
        //第二种:将字符串数组作为Object数组中的一个元素
        mainMethod.invoke(null,new
Object[]{new String[]{"abc","def","ghi"}});
      之所以能这么包装,原因是,所有的数组类型的父类都是Object。
示例代码:

package staticImport.cn.itcast.day1;

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

publicclass
RefectTest {

        publicstaticvoid
main(String args[])throws Exception {

                  /**Method类*/

                  Method charAt = str.getClass().getMethod("charAt",int.class);

                  System.out.println(charAt.invoke(str,
1));

                  System.out.println(charAt.invoke(str,new
Object[]{2}));//用jdk1.4的方式进行调用,Object数组长度为1

                  //invoke:对带有指定参数的指定对象调用由此
Method 对象表示的底层方法。

                  //如果底层方法是静态的,那么可以忽略指定的obj
参数。该参数可以为 null。因为静态方法时不需要对象的!

                  System.out.println(str.getClass().getMethod("valueOf",float.class).invoke(null,
12.3f));

                  /**Method的高级应用:

                   写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法?*/

                  //常用方式:TestArguments.main(new
String[]{"abc","def","ghi"});

                  String startingMainClass = args[0];

                  //参数为将要运行的java类名,main方法的参数为字符串数组

                  Method mainMethod = Class.forName(startingMainClass).getMethod("main",
String[].class);

                  //invoke为方法的调用,main方法为static方法,所以第一个参数为null

                  //第一种:将字符串转换成Object对象。注意每一个的数组父类都是Object。

                  //就相当于跟编译器说:我是一个Object对象不是一个数组,不要给我拆包了!

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

                  //第二种:将字符串数组作为Object数组中的一个元素

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

        }

}

class TestArguments{

        publicstaticvoid
main(String args[]){

                  for(String
arg :args){

                           System.out.println(arg);

                  }

        }
}

------- android培训java培训、期待与您交流! ----------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息