技术 blog15 之 反射 by 八戒

来源:互联网 发布:淘宝网聚划算睡衣 编辑:程序博客网 时间:2024/06/12 01:16

反射的概念

反射就是把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");  

 


0 0
原创粉丝点击