黑马程序员之基础加强之反射

来源:互联网 发布:防伪软件哪个好 编辑:程序博客网 时间:2024/05/23 19:19

学习概述:深入学习Java反射概念和原理以及应用

学习目标:熟练掌握Java反射原理,比较流畅的在以后的开发中应用到反射,对于Java.lang.reflect包中的类例如Class,Method,Package等和类中的方法要熟练掌握

1.反射的基石-Class类

      (1)首先我们必须理解什么叫做类。类到底是什么样的一种事物?

      Java类用于描述一类事物的共性,该类有什么属性,没有什么属性,但是属性的具体值则有这个类的实例对象决定,不同的实例对象有不同的属性值。特别要注意大写Class与小写Class的区别

    前面我们在介绍面向对象时提到:类是对某一类对象的抽象,类是概念层次的东西。但是我们也可以进一步的考虑:其实类也是一种对象。就想我们所说的概念主要用于定义,描述其他事物的,但概念本身也是一种食物,那么概念本身也需要被描述,而系统中所有的类,他们实际上也是对象,它们都是java.lang.Class的实例。

  提问:Class和class的区别

 举例回答:假设Person类代表人,张三,李四代表实例对象,也就是一个一个具体的人,Class类代表Java类,那么它的实例对象又代表什么呢

   什么是字节码?

     回答:字节码就是一堆二进制代码,当我们在开发过程中需要一个类时,需要先将该类的二进制代码加载到内存中来,然后在创建类的对象。

       <1>对应各个类在内存中的字节码

      <2>一个类被加载到内存空间时,占用一片存储空间,这个空间里的内容就是字节码,不同类的字节码是不同的,说以他们在内存中的类型是不同的

    三种得到各个字节码对应类对象的方法

      <1>类名.class

     <2>对象.getClass()

     <3>Class.forName(类名)

     面试题:Class.forName(完整类名)的作用?回答:作用就是返回字节码,返回的方式有两种,如果JVM缓存里已经存在,那么将直接返回字节码,如果没有,那么类加载器                    会将字节码加载进JVM,并且一同缓存!

2. 必须要理解反射的概念

   JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制通俗点来讲Java反射就是将Java类中的各个成分映射成各个Java类(什么叫类的各个成分,比如说一个类它属于哪个包,它有哪些属性,它有哪些方法,等等)。就想一辆汽车一样汽车有发动机,轮胎等等部件,实际上发动机,轮胎也是一个个类啊。我们把一个Java类中每一个成分都解析成相关类。这是我听过的最形象的讲反射概念的话

 注意:使用Java反射的后果是会让系统性能下降很多。

  新概念:暴力反射

         Java类中由于某些字段是私有的,所欲必须将这些字段用setAccessible()方法设置成可处理成状态,强行拿到这些字段,所以被称为暴力反射。

(1) 构造方法的反射,关键字:Constructor

    四个方法用于访问Class对应的类所包含的的构造器

     <1> Constructor<T> getConstructor(Class<?>....parameterTypes):返回此Class对象所表示的类的指定的public构造器

     <2> Constructor<T>[] getConstructor:返回此Class对象所表示的类的所有的public构造器

     <3> Constructor<T> getDeclaredConstructor(Class<?>....parameterTypes):返回此Class对象所表示的类的指定的构造器,与访问级别无关

     <4>Constructor<T>[]getDeclaredConstructor:返回此Class对象所表示的类的所有构造器,与访问级别无关

(2) 成员变量的反射,关键字Field

     <1>Field getField(String name):返回Class对象所表示的类的指定public属性

     <2>Field[ ] getFields():返回Class对象所表示的类的所有public属性

     <3>Field getDeclaredField(String name):返回Class对象所表示的类的指定属性,与访问级别无关。

      <4>Field[ ] getDeclaredField():返回Class对象所表示的类的所有public属性,与访问级别无关。

       新知识:暴利反射:setAccessoble()方法。采用该方法可以拿到某个对象的私有属性的具体值。

      作业:将任意一个对象中所有的String类型变量所对应的字符串内容中的"b"变成"a"

   

package com.lee.homework;import java.lang.reflect.Field;public class StringRegulate {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubDog dog = new Dog("ouby","blue",4);Field[] fields = dog.getClass().getDeclaredFields();for(Field field:fields){if(field.getType()==String.class){try {String value = (String) field.get(dog);String newValue=value.replace('b', 'a');field.set(dog, newValue);} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}System.out.println(dog.dogName);System.out.println(dog.color);}}


(3) 获取类的方法的原理与构造方法和成员变量原理基本相同,关键字Method

     得到方法:Method method = 类名.getMethod("方法名",参数列表);

      调用方法:Object invoke(object obj,Object args);该方法中的obj是执行该方法的主调,后面args的执行方法是传入该方法的参数

     注意JDK1.4 JDK1.5的invoke方法区别

      <1> 调用某个程序中的main方法

          思考题:main方法的参数是一系列字符串数组,如何为invoke方法传递字符串数组呢?

               按照jdk1.5的语法,整个数组是一个参数,当吧一个字符串数组作为参数。按照1.4的语法,整个数组中的每个元素分别对应一个参数,javac到底按那种语法进行处理  呢。所以在调用main方法时不能使用代码。解决的办法:mainMethod.invoke(nulll,(object)new String[]{...}),否则会出现 wrong number of argument错误

         练习:编写一个程序调用另外一个类中的main方法

      

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class MainTest {/** * 调用另外一个类中的main方法 */public static void main(String[] args) {// TODO Auto-generated method stubMainReflect mr = new MainReflect();try {Method method = mr.getClass().getMethod("main", String[].class);try {method.invoke(null, (Object)new String[]{"a","bc"});} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();}}

      <2> 注意的地方:通过反射调用累的静态方法时,不需要给invoke传入对象,传入null就可以

3.数组的反射

    数组同样可以当做基本类型来看待,可以通过反射来操作数组。相对于其他类型数组的反射简单了很多,因为Java已经给我们提供了一个Array类,这个类位于reflect包下。通过这个类我们可以非常方便的对数组进行一些列反射操作。

Array提供了几个主要的类方法:

     <1> static Object newInstance(Class<?> componentType,int ...length):创建一个指定元素类型和维度的数组,注意第二个参数,我们可以创建多维数组此外这个方法还要注意返回值是Object。注意:基本类型不是Object,例如int[ ]数组不是Object数组。基本类型不是Object。不过数组类型是Object的。

                    根据Java api的解释:具有相同类型以及相同维数的数组反射的类型才相同

     <2> static XXX getXxx(Object array,int index):返回数组中第index个元素

实例代码如下:

    

package com.lee.reflect;import java.lang.reflect.Array;/* * 数组的反射应用举例 */public class ArrayReflectDemo {/** * @param args */public static void main(String[] args) {//创建一个长度为10的char型数组Object array = Array.newInstance(Character.class, 10);//分别为数组的前三个元素赋值Array.set(array, 0, 'a');Array.set(array, 1, 'b');Array.set(array, 2, 'c');//取出数组首元素,注意返回值Object ch =  Array.get(array, 0);//如果是这样,编译器就会报错,因为定义数组时是Character类型,并不是char类型char ch1 =Array.getChar(array, 0);System.out.println(ch1);}}


4.深入理解hashcode

  equals()相等的两个对象,hashcode()一定相等; 
equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。(我的理解是由于哈希码在生成的时候产生冲突造成的)。 
反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解释下第3点的使用范围,我的理解是在object、String等类中都能使用。在object类中,hashcode()方法是本地方法,返回的是对象的地址值,而object类中的equals()方法比较的也是两个对象的地址值,如果equals()相等,说明两个对象地址值也相等,当然hashcode()也就相等了;在String类中,equals()返回的是两个对象内容的比较,当两个对象内容相等时, 
Hashcode()方法根据String类的重写(第2点里面已经分析了)代码的分析,也可知道hashcode()返回结果也会相等。以此类推,可以知道Integer、Double等封装类中经过重写的equals()和hashcode()方法也同样适合于这个原则。当然没有经过重写的类,在继承了object类的equals()和hashcode()方法后,也会遵守这个原则。 

一道反复被面的面试题:

     说说hashcode方法的作用?

     回答:hashcode用来提高集合元素中的查找效率,只有类的实例对象被要求采用哈希算法进行存储时,这个类才被要求覆盖hashcode方法。即使程序可能暂时不会用到当前类的hashcode方法,他是它提供了一个hashcode方法也没什么不好,没准以后什么时候又用到这个方法了,所以通常要求hashcode和equals方法一同被覆盖。

    要点:实际上hashcode主要对hashset,hashtable,hashmap这几个集合类有意义。

5.反射的作用--框架

 框架的要解决的问题到底是什么?

  一个很形象的回答:假设我要做房子卖给用户,但是有用户自己安装门窗,那么我做的房子就是框架,用户需要使用我的框架将他们的门窗插入房子。框架与工具类的区别,    工具类是被用户类调用,而框架则是调用用户类。因为在写程序是才知道要调用的类名,所以在程序中无法直接new出实例对象,所以要采用反射的方式。

学习总结:对反射原理有了初步的认识,可以熟练的采用反射方式获得类的各个成员,还可以用反射方式写一个迷你型框架。