黑马程序员 (高新技术)反射技术的深入理解

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

17.透彻分析反射的基础_Class类

1.Class类   当我们写完一个java源程序后,通过编译就可以得到一个class文件,那么根据万物皆对象的思想,当然这一类事物就可以定义为一种类型,即Class类型,而每一个class文件,就是该类型的对象,class文件又被称为字节码文件,这一种类型,本身具有特点,比如:所属于哪一个包,成员变量,成员函数,构造函数等等。那么我们就可以通过该类性对象的字节码文件,调用该类型的方法,从而获取该类型中的信息。这个就过程又称为放射。

 

总之,只要是在源程序中出现的类型都有自己的Class实例对象,如int[]   Integer 

 

2.获取Class类型的对象的字节码文件的方式有三种:

(1)类名.class   例如:Class cle = System.class;  这样就获取了System类的字节码文件

(2)类的对象来获取   例如:“abc”.getClass()   这样就获取了System类的字节码文件

(3)通过Class类型中的静态方法来获取  例如:Class.forName(“java.lang.System”);同上

 

3.Class类型中的静态方法

boolean IsPrimtive():是否为基本类型字节码

boolean IsArray():是否为数组类型字节码

 

18.理解反射的概念

1.反射就是将一个java类中个各个成分映射为相应的java类

  比如类中一般都会有构造函数,成员变量,成员函数,修饰符,包等等,他们都是各自一类事物,属于各自不同的类型,而各个不同的类型又定义为各个不同的类,如:Methodà成员函数类型,Fieldà变量类型,Constructorà构造函数类型,那么Class类就有一些方法来获取一个类中各类型的信息,

 

19.构造方法的反射应用

Constructor 类

1.得到某一个类所有的构造方法

   例如:Constructor[ ] constructor =Class.forName(“java.lang.System”).getConstructors( );

 

2.得到某一个构造方法,需要传入相应参数类型的字节码文件

例如:Constructor[] constructor =

Class.forName(“java.lang.System”).getConstructor(StringBuffer.class);

 

3.创建实例对象

通常方式:Stringstr = new String(new StringBuffer(“abc”));

反射方式:

Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);

String str =  (String)constructor.newInstance(newStringBuffer(“abc’));

 

4.Class.newInstance()方法

(1)例如:String str = Class.forName(“java.lang.String”).newInstance();

(2)该方法内部先得到默认的构造方法(一般指的是无参数的构造方法),然后用该构造方法创建实例对象

(3)该方法内部的具体代码是怎么样写的呢?用到了缓冲机制,来保存默认构造方法的实例对象

 

20.成员变量的反射

ReflectPoint pt1 = new ReflectPoint(2,5);

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

 //fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上的,要用它取出某//一个对象的值

System.out.println(fieldY.get(pt1));

 

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

//获取私有成员变量

fieldX.setAccessible(true);//设置为可以获取

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

 

获取类中的私有成员变量的时候需要调用的方法

Class调用getDeclareField(变量名)和 Field调用setAccessible(true);

 

21.成员变量反射的综合案例

ClassàgetField (args)  :获取一个指定变量

ClassàgetFields()   :获取类中变量数组

 

FieldàgetType() :获取变量类型,即变量类型字节码文件

Fieldàget(Obj  obj):获取某一个对象中的,变量值

Fieldàset(arg0, arg1):arg0为那一个对象,arg1为设置为那一个值

 

public static void changeFiledValue(Object obj)  throws Exception

    {

        Field[] fields = ReflectPoint.class.getFields();

        for (Field field : fields){

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

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

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

                field.set(obj,newValue);

            }

        }

    }

 

 

22.成员方法的反射

Methodl类的对象代表某个类中的一个成员方法

1.得到类中的方法

类的字节码对象,通过getMethod(arg0,arg1…)方法来获得该类的方法;(arg0,为方法名,arg1…表示参数列表)

Method methodCharAt = String.class.getMethod("charAt", int.class)

 

2.调用获得的方法

通过Method类的对象的invoke(arg0,arg1…)方法来调用方法,arg0要调用的对象,arg1…参数列表

 

//如果invoke的第一个参数为null,说明这个方法是一个静态方法

 

System.out.println( methodCharAt.invoke(str1, 1));//1.5的语法调用

System.out.println( methodCharAt.invoke(str1, newObject(){1}));

//1.4的语法调用

 

3.jdk1.4和jdk1.5的invoke方法的区别;

1.4多个参数通过数组传递,1.5通过的可变参数

 

 

23.对接收数组参数的成员方法进行反射【应用于当不知道调用那一个类的main方法时使用】

String startingClassName =args[0];

Method mainMethod =

 Class.forName(startingClassName).getMethod("main", String[].class);

//mainMethod.invoke

(null, newObject[]{newString[]{"111","222","333"}});

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

 

1.先向调用该功能的类中main方法中传递args,即要调用的类的名字

2.通过22中的步骤或其并调用

3.当把一个字符串数组作为参数传递给invoke方法时,会按jdk1.4的语法进行处理,即把数组打散成若干个单独的参数,所以,在给main方法传递参数时,只能能使用代码

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

javac只把它当作jdk1.4的语法来理解,而不把它当作jdk1.5的语法解释,因此会出现参数类型不对的问题【jdk1.4会自动拆包,所以可以再把要传进的数组在打一次包,或者直接声明自己就是一个Object对象不要拆包】

 

解决办法:

(1)再打一次包:mainMethod.invoke

(null, newObject[]{newString[]{"111","222","333"}});

 

(2)指明自己为一个对象:

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

 

 

24.数组与Object的关系及其反射类型

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

2.代表数组的Class实例对象的getSuperClass方法返回的父类为Object类对应的Class;

3.基本数据类型的一维数组可以看成Object对象,而不能看成Object[]对象,而多维数组就可以看成Object[]对象(如二维数组可以看成是含有一维数组的对象),其他类型的一维数组即可以看成Object对象,又可以看成Object[]对象

4.Arrays.asList()方法处理int[]和String[]时的差异。

    为了兼容jdk1.4,在jdk1.4中Arrays.asList(Object[])中传的参数类型为Object[]类型,所以String[]数组拆包后的元素都是一个个String对象,可以直接打印出来,而int[]则只能看成一个Object对象,其中的基本数据类型也不是Object,那么jvm则会将int[]当成一个对象,作为一个元素放入List集合中,而集合中存入的则是它的哈希值。

 

 

25.数组的反射应用

功能:输入一个Object类型对象,如果为数组类型,则将数组元素打印出来,否则,直接将该对象打印出来

    private static void printObj(Object obj) {

       Class clazz = obj.getClass();

       if(clazz.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);

       }

      

    }

 

 

26.ArrayList_HashSet的比较及Hashcode分析

1.HashSet的存储流程,先比较Hashcode是否相等,再比较equals是否相等

2.HasSet作用是为保证集合中元素的唯一性及Hashcode的作用

为了实现这样的功能和提高效率,在存入元素的时候,先计算出该元素的Hashcode值(其实就类似于各自结合自身情况生成一个门牌号,这样就不用再一个一个和其他元素比较相同了)看看那里有没有元素,如果没有元素就直接存入那个位置,如果有元素,在用equals比较,如果相同,则覆盖原来的元素,如果不相同,则往下顺延存入。当然,在程序开发过程中,只有涉及到HashSet集合的时候,修改自定义类型的Hashcode和equals方法才有用。

 当创建了一个对象的时候,一般情况下,是不赞成修改涉及到Hashcode运算的成员的值的,如果修改了,当再次存入到HashSet集合的时候,则生成的Hashcode值就不同了,就会导致不能覆盖原来的对象,而存入到另外的位置上,这样就可能导致内存溢出。当要删掉的时候,则原来的元素就不能删除,还保存在内存中,这样就会导致内存泄漏。

 

27.框架的概念及用反射技术开发框架的原理

   框架就是将一个程序各个部分已经定义好了,然后调用我们自己做类,从而完成一个完整的软件,这就类似于,开发商做了毛胚房,而后你安装上门,窗等等,这样就一个具有自己风格的房子,框架就类似与房子,而门就类似于自己做的类,【框架调用自己做的类】。而自己做的类又要使用一个工具类,这些工具类就类似与门上的锁。【自己做的类调用工具类】

InputStream ips = new FileInputStream("config.properties");

//真正程序开发,一定要使用完整的绝对路径,但是绝对路径的获取不是硬编码,而//是通过获取的

       Properties props = new Properties();

       props.load(ips);

       ips.close();

       String className = props.getProperty("className");

       Class clazz = Class.forName(className);

       Collectioncollections = (Collection)clazz.newInstance();

 

28.用类加载器的方式管理资源和配置文件

InputStream ips1 =

ReflectTest2.class.getResourceAsStream(arg0);//默认使用的是源文件目录

InputStream ips2 =

ReflectTest2.class.getClassLoader().getResourceAsStream(arg0));

//默认使用包根目录/cn...

 

 

29.由内省引出JavaBean的讲解

1.Introspector(内省)àJavaBeanà特殊的Java类

 

class Person

{

   Privateint x;

   Publicvoid setAge(int x){

     This.x = x;

}

   Publicint getAge(){

     Return x;

}

}

JavaBean中的属性是通过方法名来获取的,如上例所示,该类有一个属性是age而不是x;

 

2.Age -->如果第二个字母是小的,则把第一个字母变成小的àage

如:

gettime-->time

setTime-->time

getCPU-->CPU

 

30.对JavaBean的简单内省操作

【Alt+shift+s  快捷键创建get和set方法】

1.代码解读:

String propertyName = "x";

       //"x"-->"X"-->"getX"-->methodGetX-->那个对象的值,value;

       //属性描述符

PropertyDescriptor pd1 = newPropertyDescriptor(propertyName,pt1.getClass());

通过PropertyDescriptor创建一个属性修饰符对象,其中构造方法中传入是那一个属性名propertyName和那一个对象的字节码文件,从而就获得了关于该属性的get和set方法

Method methodGetX =pd1.getReadMethod();

     通过属性修饰符对象pd1的getReadMethod()方法,获得了该类中该属性的方法;

Object retVal = methodGetX.invoke(pt1,null);

     该类方法获取了以后就可以通过Method类的invoke方法来调用该getX方法,并要传入要调用的对象是谁,并返回该对象的X属性的值,因为不知道返回的值为什么类型的值,所以该值的类型就定义为Object类型;因为是获取值,所以需要传入值,所以,可以将最后传入的参数设置为null,或者省略不写

System.out.println(retVal);

 

2.重构方法,就是如上的所示的代码,其传入了部分的值,并且是要完成某一功能,那么我们就可以将这几行代码抽取出来,生成具有这一功能的方法,我们只需要调用该方法并向其中传入相应类型的值,就可以实现该功能了,从而增强程序的复用性。

【生成步骤】选择要重构的代码à右键àrefactoràextract method à输入方法名,确认即可

 

 

31.对JavaBean的复杂内省操作

以下代码实现了通过javaBean来获取对象中的属性的值;

所用到的类:

BeanInfo:将一个类看成JavaBean,并获得其javaBean信息

Introspector:内省类,自我检测,给以将传入的类通过getBeanInfo,将其看成javaBean并将获取的信息,封装成一个BeanInfo对象;

PropertyDescriptor:属性描述符类,用来描述javaBean中个属性的信息

Method:方法类

程序流程:通过内省(Introspector)获取某一类的信息,通过其自身方法getBeanInfo(),按照javaBean的特点将信息封装成一个bean信息类(BeanInfo)对象;

BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());

该对象中当然就包含有属性信息,将属性信息抽取出来,当然是其本身最清楚怎样抽取,因此,调用其本身方法getPropertyDescriptors(),获得一个属性描述符类型的数组;

       PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();

对属性描述符数组变量,抽取所需要的类型,并通过该对象,来获取该属性的get方法;

并通过Method的invoke来调用该方法。

Object retVal = null;

for(PropertyDescriptorpd : pds){

           if(pd.getName().equals(propertyName)){

              Method methodGetX = pd.getReadMethod();

              retVal = methodGetX.invoke(pt1, null);

              break;

           }

       }

       return retVal;

 

32.使用BeanUtils工具包操作JavaBean

BeanUtils是一个工具包,它的作用是将比较复杂的程序简化,功能强大,

其中get和set的返回和设置的值都是字符串类型的,只要与PropertyUtils中的set和get区别开来,它里面是什么类型就返回什么类型

 

---------------------- ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值