黑马程序员--------反射

来源:互联网 发布:明道软件怎么使用 编辑:程序博客网 时间:2024/05/04 21:01
-------android培训java培训、期待与您交流! ----------

反射的基石:Class类

           Class 类表示java 类的字节码的类 (java类 本身也是一类事物)。

如何得到各个字节码对应的实例对象( Class类型)

          类名.class,例如,System.class
          对象.getClass(),例如,new Date().getClass()
          Class.forName("类名"),例如,Class.forName("java.util.Date");(反射时主要用第三种变量名)

       九个预定义Class实例对象:(void + 八种基本数据类型)

        参看Class.isPrimitive方法的帮助
        Int.class == Integer.TYPE
        Class.isArray(); 
 
        System.out.println(c1.isPrimitive());
        System.out.println(int.class.isPrimitive());//返回ture
        System.out.println(int[].class.isPrimitive());// 返回false
        System.out.println(int.class==Integer.class);// 返回false
        System.out.println(int.class==Integer.TYPE);// 返回true
        System.out.println(int[].class.isArray());// 返回true
 
        无论用什么方法得到字节码,得到的都是一份字节码(代码示例如下)
 
       Class c1=String.class;
       Class c2= new String().getClass();
       Class c3=null;
       try {
       c3=Class.forName("java.lang.String");
       } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
     System.out.println(c1==c2);// 返回true
     System.out.println(c1==c3);// 返回true

理解反射:

      反射就是把Java类中的各种成分映射成相应的java类。 
      例如,一个Java类用一个Class类的对象来表示,一个类中的组成部分:
      成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
    
      表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,
      这些信息就是用相应类的实例对象来表示,  它们是Field、Method、Contructor、Package等等。 
    
        一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,
       通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?
       这正是学习和应用反射的要点。
       
Constructor类

        Constructor类代表某个类中的一个构造方法

        得到某个类所有的构造方法:
        例子:Constructor [] constructors= Class.forName("java.lang.String").getConstructors();

        得到某一个构造方法:
        例子:   Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.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();
       该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。

Field类

         Field类代表某个类中的一个成员变量
         问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,
        而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变        量。
       示例代码:

        ReflectPoint point = new ReflectPoint(1,7);
        Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
        System.out.println(y.get(point));
        //Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
        Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
         x.setAccessible(true);
        System.out.println(x.get(point));

Method类:
          Method类代表某个类中的一个成员方法
          得到类中的某一个方法:
          例子:   Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);

          调用方法:
          通常方式:System.out.println(str.charAt(1));
          反射方式: System.out.println(charAt.invoke(str, 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})形式。

用反射方式执行某个类中的main方法:
         class.forName("classname").getMethod("main",String[].class);
         启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
        通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,
        整个数组是一个参数,而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数
        传递给invoke方法时,javac会到底按照哪种语法进行处理呢?jdk1.5肯定要兼容jdk1.4的语法,

         会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传递参数时,
        不能使用代码mainMethod.invoke(null,new String[]{“xxx”}),
         javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5的语法解释,
         因此会出现参数类型不对的问题。
         解决办法:
         mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
         mainMethod.invoke(null,(Object)new String[]{"xxx"}); ,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数            组打散成若干 个


数组的反射:

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

        int[] a1=new int[]{1,2,3};
        int[] a2=new int[]{1};
        int[][] a3=new int[2][3];
        String[] a4=new String["aa","bb"];
        Class c1=a1.getClass();
        Class c2=a2.getClass();
        Class c3=a3.getClass();
       Class c4=a4.getClass();
       System.out.println(c1==c2); // 返回true
       System.out.println(c1==c3); // 返回false
       System.out.println(c4==c1); // 返回 false

      代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
      System.out.println(c1.getSuperclass().getName());// 打印java.lang.Object
      System.out.println(c4.getSuperclass().getName());// 打印java.lang.Object
         
         
       基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型       使用,又可以   当Object[]类型使用。

      Arrays.asList()方法处理int[]和String[]时的差异:
      System.out.println(Arrays.asList(a1));//打印[[I@dc8569]
      System.out.println(Arrays.asList(a3)); // [ss, aa]

       代码举例:Array工具类用于完成对数组的反射操作(打印一个对象,如果不是数组就打印一个,如果是对象就分变量打印)。

       public  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);
        }
    }


反射的作用实现框架功能:

        框架与框架要解决的核心问题:

         我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,
         用户需要使用我的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户          提供的类。

        框架要解决的核心问题:
        我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)          呢?
        因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new 某个类的实例对象了,而要用反射方式来做。

     综合案例:

       先直接用new  语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成 ReflectPoint类的equals和hashcode方法,比较        两个集合的运行 果差异。然后改为采用配置文件加反射的方式创建ArrayList和HashSet的实例对象,比较观察运行结果差            异。

代码示例如下:

       fis=new FileInputStream("config.properties"); // config.properties 文件中先后配置ArrayList和HashSet 查看结果的不同
       Properties p=new Properties();
       p.load(fis);
       fis.close();
      String className=p.getProperty("className");
 
      Collection c=(Collection)Class.forName(className).newInstance();
      c.add(p1);
      c.add(p2);
      c.add(p3);
      c.add(p1);
      System.out.println(c.size());// config.properties 配置HashSet 打印3 ArrayList 打印4.

                                                         -------android培训java培训、期待与您交流! ----------