jdk1.5高新技术(二)


7.反射的基石—Class类

-------结合网络资源整理

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类就是Class类。

Class类代表Java类,它的实例对象对应的是:各个类在内存中的字节码。


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

1.类名.class,例如System.class

2.对象.getClass(),例如new Date().getClass()

3.Class.forName(“类名”),例如Class.forName(“java.util.Date”)

 前2种方法是类字节码已经被加载,第3种类字节码还未加载。


九个预定义Class实例对象

boolean, byte, char, short, int, long, float, double和void是Primitive的Class对象

 int.class==Integer.TYPE

 数组类型的Class实例对象:Class.isArray()


9反射

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

例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。

表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等。 

一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。

什么是Java的反射呢?

       大家都知道,要让Java程序能够运行,那么就得让Java类要被Java虚拟机加载。Java类如果不被Java虚拟机加载,是不能正常运行的。现在我们运行的所有的程序都是在编译期的时候就已经知道了你所需要的那个类的已经被加载了。

Java的反射机制是在编译并不确定是哪个类被加载了,而是在程序运行的时候才加载、探知、自审。使用在编译期并不知道的类。这样的特点就是反射。

Java反射有什么作用呢?

假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。利用Java反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

Java的反射机制它知道类的基本结构,这种对Java类结构探知的能力,我们称为Java类的“自审”。大家都用过Jcreator和eclipse。当我们构建出一个对象的时候,去调用该对象的方法和属性的时候。一按点,编译工具就会自动的把该对象能够使用的所有的方法和属性全部都列出来,供用户进行选择。这就是利用了Java反射的原理,是对我们创建对象的探知、自审。

java的反射机制大大的提高了程序的拓展性.

class中的一些方法:

a)获取构造器

 Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。 
 Constructor<?>[] getDeclaredConstructors() 
          返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法。 
 Constructor<T> getConstructor(Class<?>... parameterTypes) 
          返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 
 Constructor<?>[] getConstructors() 
          返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。 

b)获取成员变量

 Field getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 
 Field[] getDeclaredFields() 
          返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。 
 Field getField(String name) 
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 
 Field[] getFields() 
          返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。 


c)获取成员方法

Method getDeclaredMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。 
 Method[] getDeclaredMethods() 
          返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 
 Method getMethod(String name, Class<?>... parameterTypes) 
          返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 
 Method[] getMethods() 
          返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。 

小结:

Method表示方法,Field表示成员变量,Constructor表示构造方法;后面加s表示一次获取多个。
因为类中的成员如果public修饰符,外部不能直接访问,所以有Declared的方法是可以获取到public和非public的成员的。如果没有Declared则表明获取public的。
因为java中允许有多个同名的方法(普通方法和构造方法),只要其传入参数的类型(包括)不同即可,所以在获取单个方法的时候,不光要指定方法的名称,还要指定参数的类型。


4.获得内部类
 Class<?>[] getDeclaredClasses() 
          返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。 
 Class<?> getDeclaringClass() 
          如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。 
 Class<?>[] getClasses() 
          返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 

重要方法:
newInstance()
//通过newInstance方法获取类的无参构造方法创建实例(不能为private 会报错)  

构造函数Constructor:
如果一个Class对象中有无参构造函数, 那么可以直接使用Class对象的newInstance()方法来创建对象.
但是如果没有无参的构造函数, 那么就需要调用其有参的构造函数来创建对象了.
使用Class类的getConstructor(Class...)方法获取到指定的构造函数.
通过Construtor类的newInstance(Object...)方法来调用构造函数创建对象.

方法Method:
我们可以通过Class对象获取一个类中的方法. 获取到方法之后可以运行该方法.

可见的方法:
通过Class类的getMethod(String, Class...)方法获取指定的方法.
通过Method的invoke(Object, Object...)方法调用该方法.
类中定义的方法(包括不可见的):
通过Class类的getDeclaredMethod(String, Class...)方法获取到该类定义的一个方法
如果该方法是一个不可见的方法, 那么需要设置该方法的可见性, setAccessible(boolean)

属性Field:
我们可以通过Class对象获取该类中的属性, 可以设置或获取该属性的值.
可见的属性:
通过Class对象的getField(String)方法获取到Field对象
通过Field对象的set(Object, Object)设置该属性的值
类中定义的(包括不可见的):
通过Class对象的getDeclaredField(String)方法获取到Field对象
如果该属性是不可见的, 那么需要设置可见性才能操作. setAccessible(boolean)//暴力访问

下面介绍一种jdk1.5出现的问题(可变参数向下兼容问题)
jdk1.5中出现的新特性 可变参数如 public static void show(String str,int... arr)
而在jdk1.5之前是不支持的 只能写成 public static void show(String str,int[] arr)
而jdk1.5为了向下兼容
下面看一段程序:

如图所示 通过反射获得test的main方法 可在传递参数是出现异常

异常分析:

异常提醒传递的参数数量不对

可传的明明是一个string[]数组 没有错啊?

可以看出数组形式传参数是jdk1.5之前的写法 而jdk1.5为了向下兼容  在遇到数组形式传递参数时会内部把它处理成一个个参数(即把数组内的对象取出)

所以会提醒传递的参数数量不正确


再看如下


如图所示把string[]数组强制转换成了object 结果程序就正常运行了

可以想像 当强制转换成了object时相当于告诉jvm 这里只有一个对象 jvm当然不会再去把它拆开


这里还有另一种解决方法

new Object[]{new String[]{"xxx"}}

分析:当jdk1.5遇到数组形式参数 它内部处理(把数组拆开) 即得到了string[]数组 并作为第一个参数 所以不会抛出异常



六、数组的反射(摘自网络资源)

  • 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
[java]  view plain
  1. int []a1=new int[3];  
  2. int []a2=new int[4];  
  3. int [][]a3=new int[3][4];  
  4. String []a4=new String[3];  
  5.   
  6. System.out.println(a1.getClass()==a2.getClass());//true  
  7. System.out.println(a1.getClass()==a3.getClass());//false  
  8. System.out.println(a1.getClass()==a4.getClass());//false  
  • 代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
[java]  view plain
  1. System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object  
  2. System.out.println(a2.getClass().getSuperclass().getName());//java.lang.Object  
  3. System.out.println(a3.getClass().getSuperclass().getName());//java.lang.Object  
  4. System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object  

  • 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
[java]  view 
  1. Object aObj1 = a1;  
  2. Object aObj2 = a4;  
  3. //Object[ ] aObj3 = a1; ERROR!!!  
  4. Object[ ] aObj4 = a3;  //一维数组可以转成object 二维数组转成object[]
  5. Object[ ] aObj5 = a4;  

  • Arrays.asList()方法处理int[]和String[]时的差异。(为了兼容jdk1.4造成的,jdk1.4中asList(Object []obj) 处理不了int[]只能给jdk1.5+处理,当成一个参数用Object接收了)
[java]  view plain
int  []a1= new   int []{ 1 , 2 , 3 };  
  1. String []a4=new String[]{"a","b","c"};  
  2. System.out.println(Arrays.asList(a1));//[[I@422ede]  
  3. System.out.println(Arrays.asList(a4));//[a, b, c]  


  • Array工具类用于完成对数组的反射操作。
[java]  view plai
  1. public static void printObject(Object obj)  
  2. {  
  3.     Class cls=obj.getClass();  
  4.     if(cls.isArray())//判断是否数组  
  5.     {  
  6.         int len=Array.getLength(obj);//获取数组长度  
  7.         for (int i = 0; i <len; i++)  
  8.         {  //通过下标逐个获取数组中的元素  
  9.             System.out.println(Array.get(obj, i));  
  10.         }  
  11.     }  
  12.     else  
  13.     {  
  14.         System.out.println(obj);  
  15.     }  
  16.       
  17. }  
一、hashCode()的作用(摘自网络资源)

hashCode()是用来计算哈希值的,在对数据进行存储的时候,假如有成千上万个数据,每当存入一个数据,就需要比较多次,这样效率就很低,采用哈希值算法得到哈希值后,根据哈希值划分为不同的区域,这样就能大大减少比较次数,提高效率。如图所示:(学过数据结构的人应该都了解hash表)


二、ArrayList_HashSet的比较及Hashcode分析

在自定义类中类型添加到HashSet中通常要要重写hashCode方法和equlas方法,依此来判断元素是否重复。在进行判断元素是否相同时,先调用hashCode方法,如果哈希值一样,再调用equals方法继续比较判断。如果哈希值不同,则不再调用equals方法进行判断。

注意:哈希值相同,两个对象不一定相等,两个对象相等,这两个对象的哈希值一定相等。

内存泄露:对象不需要再使用了,却没有引用指向它,未进行回收。

见代码:

[java]  view plain copy
  1. public class ReflectPoint  
  2. {  
  3.    public int x;  
  4.    public int y;  
  5.   
  6.     public ReflectPoint(int x, int y)  
  7.     {  
  8.         super();  
  9.         this.x = x;  
  10.         this.y = y;  
  11.     }  
  12.       
  13.     public String toString()  
  14.     {  
  15.         return this.x+" "+this.y;  
  16.     }  
  17.       
  18.     @Override  
  19.     public int hashCode() {  
  20.         final int prime = 31;  
  21.         int result = 1;  
  22.         result = prime * result + x;  
  23.         result = prime * result + y;  
  24.         return result;  
  25.     }  
  26.   
  27.   
  28.     @Override  
  29.     public boolean equals(Object obj) {  
  30.         if (this == obj)  
  31.             return true;  
  32.         if (obj == null)  
  33.             return false;  
  34.         if (getClass() != obj.getClass())  
  35.             return false;  
  36.         final ReflectPoint other = (ReflectPoint) obj;  
  37.         if (x != other.x)  
  38.             return false;  
  39.         if (y != other.y)  
  40.             return false;  
  41.         return true;  
  42.     }  
  43. }  

[java]  view plain copy
  1. public class ReflectTest1  
  2. {  
  3.   
  4.     public static void main(String[] args)  
  5.     {  
  6.         Collection al=new ArrayList();  
  7.         ReflectPoint rp1=new ReflectPoint(3,3);  
  8.         ReflectPoint rp2=new ReflectPoint(5,6);  
  9.         ReflectPoint rp3=new ReflectPoint(3,3);  
  10.           
  11.         al.add(rp1);  
  12.         al.add(rp2);  
  13.         al.add(rp3);  
  14.         al.add(rp1);  
  15.         System.out.println("list:"+al.size());//list:4  
  16.           
  17.         Collection hs=new HashSet();  
  18.         hs.add(rp1);  
  19.         hs.add(rp2);  
  20.         hs.add(rp3);  
  21.         hs.add(rp1);  
  22.         //重写了hashCode方法和equals方法,rp1重复添加了,只添加一个rp1,rp1和rp3也重复,不添加rp3  
  23.         System.out.println("hs:"+hs.size());//hs:2 [集合内存放的是rp1和rp2]  
  24.         System.out.println(hs);//[3 3, 5 6]  
  25.           
  26.         //集合中添加了元素rp1后,修改对象rp1的成员变量值,这个成员变量是参与了hashCode()值的运算,进行移除rp1操作  
  27.         rp1.x=100;  
  28.         hs.remove(rp1);//移除元素  
  29.         //发现还是2个元素,remove方法因为哈希值被修改了,无法移除原来的rp1对象,造成内存泄露!!  
  30.         System.out.println(hs);  
  31.           
  32.   
  33.     }  
  34.   
  35. }  

如图所示 在操作集合元素过程中 因为修改了参与hash值计算的元素属性 导致了remove()(根据hash值来执行移除操作)操作失败 造成了内存泄漏!!!

结论:集合一旦添加了元素 就不能再修改参与hash值计算的的元素属性



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值