黑马程序员-Java反射机制

 

反射的概念

反射就是把java类中的各种成分映射成相应的java类。

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示。

 反射的基础:Class-->用来描述java中的类
 
     如何得到各个字节码对应的实例对象
         ->类名.class 或者 类名.TYPE
         ->对象.getClass()
         ->Class.forName("类名")
     总之,只要在源程序中出现的类型,都有各自的Class实例对象。

String str1 = "abc";
		
//三种获取字节码的方法
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);
System.out.println(cls2 == cls3);

  运行结果:true  true

注意:

1. int.class = integer.TYPE

2. 数组类型的class实例对象Class.isArray()为true

3. 反射不是java5的新特性

 

反射的作用

反射机制最重要的内容——检查类的结构。

在java.lang.reflect包中有三个类Field、Method、Constructor分别用于描述类的域、方法和构造器。

这三个类共有方法:

getModifiers   //返回一个整形数值,用不同的位开关描述public和static这样的修饰符使用状况

getName  //用来返回项目的名称                  

 

构造方法的反射应用

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调用成员变量

 

ReflectPiont.java

package com.itheima.reflect;

public class ReflectPoint {
       private int x;
       public int y ;
       
       public ReflectPoint(int x, int y) {
             super();
             this.x = x;
             this.y = y;
      }
}

 

ReflectTest.java

package com.itheima.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            ReflectPoint rp = new ReflectPoint(3, 5);
            Field fieldY = rp.getClass().getField( "y");
            System. out.println(fieldY.get(rp));
            //结果:5
      }
}

  注意:如果想直接通过getField方法获取私有对象,会出现如下错误

Exception in thread "main" java.lang.NoSuchFieldException : x

  解决方法:

Field fieldX = rp.getClass().getDeclaredField("x");
            fieldX.setAccessible(true);

  这种方法称之为暴力反射,使用setAccessible(true)使private类型的成员变量也可以被获取。

 

 

成员方法的反射

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对象对应的是静态方法!

 

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

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

package com.itheima.reflect;

import java.util.Arrays;

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            int [] a1 = new int[3];
            int [] a2 = new int[4];
            int [][] a3 = new int[2][3];
            String[] a4 = new String[3];

            System.out.println(a1.getClass() == a2.getClass());
            //结果:true
            System.out.println(a1.getClass() == a4.getClass());
            //结果:false
            System.out.println(a1.getClass() == a3.getClass());
            //结果:false
       }
}

  

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

3. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以做Object类型使用,又可以当作Object[]类型使用。

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

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            int [] a5 = new int[]{1,2,3};
            String[] a6 = new String[]{"a" ,"b" ,"c" };
            //直接使用System.out.println无法打印出数组的内容
            System. out .println(a5);
            //结果:[I@18a992f
            System. out .println(a6);
            //结果:[Ljava.lang.String;@4f1d0d
           
            //通过Arrays.asList方法打印出数组的内容
            System. out .println(Arrays.asList(a5));
            //结果:[[I@18a992f]
            //原因是因为JDK1.4中为Arrays.asList(Object[] a),JDK1.5中为Arrays.asList(T... a)。
            //a5是int[]类型,JDK1.4中的asList方法处理不了,JDK1.5可以处理。但是JDK1.5将 int数组整体作为一个参数进行处理。
            //因此最终结果就是将 int[]进行了封装,结果类型也就成了[[I。
            System. out .println(Arrays.asList(a6));
            //结果:[a, b, c]
       }
}

 

5. Array工具类用于完成数组的反射操作。

public class ReflectTest {

       public static void main(String[] args) throws Exception {
            String[] a1 = new String[]{"a" ,"b" ,"c" };
            String a2 = "xyz";
           
            printObject(a1);
            printObject(a2);
       }
      
       public static void printObject(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);
         }
       }
}

  

6. ArrayList和HashSet的比较及Hashcode分析。

ArrayList:

           Collection collections = new ArrayList();
           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);
          
           System. out.println(collections.size());
           //结果:4           Collection collections = new ArrayList();
           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);
          
           System. out.println(collections.size());
           //结果:4 

HashSet:

           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);
          
           System. out.println(collections.size());
           //结果:3

 

分析:

由以上两示例可以看到当集合为ArrayList时,其实质是一个数组,因此放入4个元素后,集合size为4。
当集合为HashSet时,需要通过比较hashcode值以及equals方法是否返回true决定是否放入。如果hashcode值相等并且equals方法返回true,那么就不会放入。因此,集合size为3。
如果想让size为2,也就是pt1与pt3作为同一个元素存入HashSet集合,那就需要覆盖ReflectPoint类的hashCode方法以及equals方法。

 

hashCode()和equals()覆盖:

       @Override
       public int hashCode() {
             final int prime = 31;
             int result = 1;
            result = prime * result + x;
            result = prime * result + y;
             return result;
      }

       @Override
       public boolean equals(Object obj) {
             if (this == obj)
                   return true ;
             if (obj == null)
                   return false ;
             if (getClass() != obj.getClass())
                   return false ;
            ReflectPoint other = (ReflectPoint) obj;
             if (x != other.x)
                   return false ;
             if (y != other.y)
                   return false ;
             return true ;
      } 

此时,运行ReflectTest.java结果为2。

 

注意:

当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。

 

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

1. 框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

2. 框架程序怎样调用以后写的类呢?很多时候程序无法知道被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用反射方式来做。

ReflectTest.java

public class ReflectTest {

       public static void main(String[] args) throws Exception {
        
         InputStream is = new FileInputStream("config.properties" );
         Properties props = new Properties();
         props.load(is);
         is.close();
        
         String className = (String)props.get( "className");
         Collection collections = (Collection)Class.forName(className).newInstance();
        
         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);
          
         System. out.println(collections.size());
       }
}

 

config.properties文件直接放在根目录下:

|--config.properties

className = java.util.ArrayList    //结果:4

|--config.properties

className = java.util.HashSet    //结果:3

 

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

         //方式一:采用类加载器进行加载,使用相对路径的方式
         //InputStream is = ReflectTest.class.getClassLoader().getResourceAsStream("com/itheima/day1/config.properties");
        
         //方式二:利用Class方式进行加载,使用相对路径的方式
         //InputStream is = ReflectTest.class.getResourceAsStream("config.properties");
        
         //方式三:利用Class方式进行加载,使用绝对路径的方式
         InputStream is = ReflectTest.class .getResourceAsStream("/com/itheima/day1/config.properties");

  

posted @ 2015-06-01 13:01  troy健  阅读(171)  评论(0编辑  收藏  举报