`
qq272936993
  • 浏览: 3017 次
社区版块
存档分类
最新评论

黑马程序员_java基础反射

阅读更多

java程序中各个java类属于同一类事物,描述的java类名就是Class.

 

Class类代表java类,它的各个实例对象又分别对应什么呢?

       1.对应各个类在内存中的字节码。

       2.一个类呗类加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码.不同的类的字节码

是不同的.这一个个的空间可分别用一个个对象来表示.

 

Class与class的区别:

      Class就是一个类,而class是java语言关键字,class也是每个类的成员变量.

 

Class类描述了哪方面的信息?

     类的名字,类的访问属性,类所属的包名,字段名称的的列表,方法名称的列表.等等。

 

 

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

       类名.class,例如:People.class;

       对象.getClass(),例如: new Date().getClass();

       Class.forName("类名"),例如 : Class.forName("java.util.Date");

 

九个预定义的Class对象(有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float 和 double)

       参看: Class.isPrimitive() 方法帮助(eg: int.class.isPrimitive()   true)

       int.class == Interge.TYPEtrue

       基本类型数组不是已定义Class对象,所以int[].class.isPrimitive();    false

 

总之,只要是在源程序中出现的类型,都是有各自的Class实例对象.

 

 

反射:就是把java类中的各种成分映射成相应的java类。表示java类的Class类显然是提供一系列的方法,来获取其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应的实例对象来表示,他们是Field、Method、Contructor、Package扥等。

 

 

Constructor类:代表某个类张的一个构造方法.

得到某个类所有的构造方法:

        eg: Constructor constructors[] = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

 

得到某一个构造方法:

       eg: 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()方法:

       该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象,用到了缓存机制来保存默认构造方法的实例对象.

 

 

思维拓展:

        new 和newInstance()的区别:

               首先,newInstance()是一个方法,而new是一个关键字。

               其次,Class下的newInstance()的使用有局限,因为它生成对象,只能调用无参构造函数,而是用new关键字生成对象没有限制.

 

 

Field类:

 

public class ReflectPoint {

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


	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}


	public int getX() {
		return x;
	}


	public void setX(int x) {
		this.x = x;
	}
	
}


 

public class TestRun {
	
	public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
		ReflectPoint ptl = new ReflectPoint(3, 5);
		//fieldY不是对象身上的变量,而是类上的,要用它去取某个对象上的值
		
		//说明getFields只是获取public修饰的成员变量/getField也一样.
		Field[] publicFields= ptl.getClass().getFields();	
		for(Field f : publicFields){
			System.out.println(f.toString()+"\t"+f.get(ptl));
		}
		
		System.out.println("===================================>");
		//说明getDeclaredField会获取所有的成员变量/getDeclaredField也一样
		Field[] privateFields = ptl.getClass().getDeclaredFields();
		for(Field f : privateFields){
			f.setAccessible(true);
			System.out.println(f.toString()+"\t"+f.get(ptl));
		}
		System.out.println("==================================>");
		
		
		Field fieldX = ptl.getClass().getDeclaredField("x");
		fieldX.setAccessible(true);	//设置访问权限,常常对private修饰的属性设置
		System.out.println(fieldX.get(ptl));
	}
}

 

 

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 invoke(Object obj,Object ... args);

      jdk1.4:public invoke(Objcet obj,Object[] args);

 

 

用反射方式执行某个类中的main方法.

      由于jdk1.5的,参数为Object...,然而main方法需要传递的为数组,所有通过invoke()只会认为是1.4的写法.

 

解决办法:

       mainMethod.invoke(null,new Object[]{new String[]{"XXX"...}});

       mainMethod.invoke(null,(Object)new String[]{"XXXX"}),编译器会特殊处理,翻译时不把参数当做数组看待,也就不会数组打散成若干个参数了. 

 

 

数组的反射:

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

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

       3.基本类型的一维数组可以代做Object类型使用,不能代做Object[]类型使用,非基本类型的一维数组,

       既可以当做Object类型使用,又可以当做Object[]类型使用.

 

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

         String[]数组中的String对象都是Object对象,而int[]数组中的int,只是基本类型,不是一个Object对象.传递给asList()的实参,如果是String[]数组,那么,数组中的每个元都是Object对象。如果是int[]数组,数组中的元,都是一个基本 类型,不是Object。

          在JDK1.4中,传递给asList()方法的实参,是Object[]数组,

而在JDK1.5以及以上版本中,利用了可变参数来实现。当我们传递String[]数组给形参时,由于上述String[]的特性,即正确的传递了实参给asList()方法。

          但是,当我们传递给int[]数组给形参时,由于int[]数组的上述特性,错误的传递了形参,

那么,返回JDK1.5以上的版本,使之处理,而在该处理中,实参int[]将当做一个Object对象,即只能返回该对象的数组类型和散列码。

 

hashCode方法与HashSet类 

        如果想查找一个集合中是否包含有某个对象,大概的程序代码怎样写呢?当发现某个元素与要查找的对对象进行equals方法比较的结果相等时,则停止继续查找并返回肯定的信息,否则返回否定的信息。如果是一个集合中有很多元素,譬如有一万个元素,并且没有包含要查找的对象时,则意味着你的程序需要从该集合中取出一万个元素进行啄一的比较才能得到结论,有人发明了一种hashCode算法,来提高查找的效率,这种方式将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储在哪个区域,

 

 

         hashSet就是采用哈希算法存储对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组的划分对象的存储区域。Object中的hashCode用来返回对就java对象的哈希码,从而提高查找的效率。

 

         为了保证一个类的实例对象能在HashSet正常存储,要求这个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,也就是说,如果obj1.equals(obj2)的结果为true,那么一下表达式的结果页要为true。

 

obj1.hashCode() == obj2.hashCode()

 

         如果一个类的hashCode()方法没有遵循上述要求,那么,当这个的两个实例对象用equals()方法比较的结果相等时,它们本来应该无法同时存储进Set集合中,但是,如果将它们存储进HashSet集合中时,由于它们的hashCode()方法的返回值不同,第二个对象首先按哈希码计算可能会被放进与第一个对象不同的区域中,这样,它就不可能与第一个对象进行equals方法比较了,也就可能被存储进HashSet集合中了。Object类的hashCode()方法不能满足对象被存入到HashSet中的要求,因为它的返回值是通过对象的内存地址推算出来的,同一个对象在程序运行期间的任何时候返回的哈希值都是始终不变的,所以,只要是两个不同的实例对象,即使它们的equals方法比较结果相等,它们默认的hashCode方法的返回值是不同的。

提示:

 

(1)通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,但反之则不成立,即euqlas方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象的equals方法比较的结果可以不等,例如,字符串“BB”和"Aa"的euqals方法比较结果肯定不相等,但它们的hashCode方法返回值却相等。

 

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

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics