`
tongchuanwei
  • 浏览: 8851 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
文章分类
社区版块
存档分类
最新评论

java学习笔记3

阅读更多

这已经笔者写的第三篇Java杂记了,庆幸前两篇一直得到论坛朋友们的支持鼓励,还望大家继续指正不足之处。笔者也一直渴望通过这样方式清醒的自审,来寻找自己技术上的不足之处,希望和共同爱好Java的同仁们一起提高。

前两次分别讲述了关于jvmjdkjrecollectionclassLoader和一些Design Pattern的自我理解。这次仍然不准备开始过渡到j2ee中,因为觉得还有一些琐碎的j2se的问题没有总结完毕。

 

1 关于Object类理解

大家都知道Object是所有Java类的基类, 意味着所有的Java类都会继承了Object11个方法。建议大家去看看Object 11个成员函数的源代码,就会知道默认的实现方式。比如equals方法,默认实现就是用"=="来比较,即直接比较内存地址,返回true 或者 false。而toString()方法,返回的串组成方式是??

"getClass().getName() + "@" + Integer.toHexString(hashCode())"

其实不用我过多的解释,大家都能看懂这个串的组成。接下来再看看hashCode()

public native int hashCode()

 

由于是native方法,跟OS的处理方式相关,源代码里仅仅有一个声明罢了。我们有兴趣的话完全可以去深究它的hashCode到底是由OS怎么样产生 的呢?但笔者建议最重要的还是先记住使用它的几条原则吧!首先如果equals()方法相同的对象具有相通的hashCode,但equals ()对象不相通的时候并不保证hashCode()方法返回不同的整数。而且下一次运行同一个程序,同一个对象未必还是当初的那个hashCode() 哦。

其余的方法呢?nofigy()notifyAll()clone()wait()都是native方法的,说明依赖于操作系统的实现。最后一个有 趣的方法是finalize(),类似C++的析构函数,签名是protected,证明只有继承扩展了才能使用,方法体是空的,默示什么也不做。它的作 用据笔者的了解仅仅是通知JVM此对象不再使用,随时可以被销毁,而实际的销毁权还是在于虚拟机手上。那么它真的什么也不做麽?未必,实际上如果是线程对 象它会导致在一定范围内该线程的优先级别提高,导致更快的被销毁来节约内存提高性能。其实从常理来说,我们也可以大概这样猜测出jvm做法的目的。

 

2 关于重载hashCode()Collection框架的关系

笔者曾经听一位搞Java培训多年的前辈说在他看来hashCode方法没有任何意义,仅仅是为了配合证明具有同样的hashCode会导致equals 方法相等而存在的。连有的前辈都犯这样的错误,其实说明它还是满容易被忽略的。那么hashCode()方法到底做什么用?

 

学过数据结构的课程大家都会知道有一种结构叫hash table,目的是通过给每个对象分配一个唯一的索引来提高查询的效率。那么Java也不会肆意扭曲改变这个概念,所以hashCode唯一的作用就是为 支持数据结构中的哈希表结构而存在的,换句话说,也就是只有用到集合框架的 HashtableHashMapHashSet的时候,才需要重载hashCode()方法,

这样才能使得我们能人为的去控制在哈希结构中索引是否相等。笔者举一个例子:

曾经为了写一个求解类程序,需要随机列出1,2,3,4组成的不同排列组合,所以笔者写了一个数组类用int[]来存组合结果,然后把随机产生的组合加入 一个HashSet中,就是想利用HashSet不包括重复元素的特点。可是HashSet怎么判断是不是重复的元素呢?当然是通过 hashCode()返回的结果是否相等来判断啦,可做一下这个实验:

int[] A = {1,2,3,4};

int[] B = {1,2,3,4};

System.out.println(A.hashCode());

System.out.println(B.hashCode());

 

这明明是同一种组合,却是不同的hashCode,加入Set的时候会被当成不同的对象。这个时候我们就需要自己来重写hashCode()方法了,如何 写呢?其实也是基于原始的hashCode(),毕竟那是操作系统的实现, 找到相通对象唯一的标识,实现方式很多,笔者的实现方式是:

首先重写了toString()方法:

return A[0]+ A[1]+ A[2]+ A[3]; //显示上比较直观

然后利用toString()来计算hashCode()

return this.toString().hashCode()

这样上述AB返回的就都是”1234,在测试toString().hashCode(),由于String在内存中的副本是一样的,”1234.hashCode()返回的一定是相同的结果。

 

说到这,相信大家能理解得比我更好,今后千万不要再误解hashCode()方法的作用。

 

3 关于Class类的成员函数与Java反射机制

很早刚接触Java就听很多老师说过Java的动态运行时机制、反射机制等。确实它们都是Java的显著特点,运行时加载笔者在第一篇介绍过了,现在想讲 讲反射机制。在Java中,主要是通过java.lang包中的Class类和Method类来实现内存反射机制的。

熟悉C++的人一定知道下面这样在C++中是做不到的: 运行时以字符串参数传递一个类名,就可以得到这个类的所有信息,包括它所有的方法,和方法的详细信息。还可以实例化一个对象,并通过查到的方法名来调用该 对象的任何方法。这是因为Java的类在内存中除了C++中也有的静态动态数据区之外,还包括一份对类自身的描述,也正是通过这描述中的信息,才能帮助我 们才运行时读取里面的内容,得到需要加载目标类的所有信息,从而实现反射机制。大家有没有想过当我们需要得到一个JavaBean的实例的时候,怎么知道 它有哪些属性呢?再明显简单不过的例子就是自己写一个JavaBean的解析器:

 

a. 通过Class.forName(Bean的类名”)得到Class对象,例如叫ABeanClass

b. 通过ABeanClassgetMethods()方法,得到Method[]对象

c. 按照规范所有get方法名后的单词就代表着该Bean的一个属性

d. 当已经知道一个方法名,可以调用newInstance()得到一个实例,然后通过invoke()方法将方法的名字和方法需要用的参数传递进去,就可以动态调用此方法。

 

当然还有更复杂的应用,这里就不赘述,大家可以参考Class类和Method类的方法。

 

4 坦言Synchronize的本质

Synchronize大家都知道是同步、加锁的意思,其实它的本质远没有大家想得那么复杂。声明Synchronize的方法被调用的时候,锁其实是加 载对象上,当然如果是静态类则是加在类上的锁,调用结束锁被解除。它的实现原理很简单,仅仅是不让第二把锁再次被加在同一个对象或类上,仅此而已。一个简 单的例子足以说明问题:

class A{

synchronized void f(){}

void g(){}

}

 

A的一个对象a被第一个线程调用其f()方法的时候,第二个线程不能调用asynchronized方法例如f(),因为那是在试图在对象上加第二把锁。但调用g()却是可以的,因为并没有在同一对象上加两把锁的行为产生。

这样大家能理解了麽?明白它的原理能更好的帮助大家设计同步机制,不要滥用加锁。

 

PS:下篇笔者计划开始对J2ee接触到的各个方面来进行总结,谈谈自己的经验和想法。希望大家还能一如既往的支持笔者写下去,指正不足之处。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics