首页 > 代码库 > 《Java特种兵 上册》的问答反馈专页

《Java特种兵 上册》的问答反馈专页

大家好,Java特种兵上册已经正式在9月初上市发售。这本书可能很多人拿到会有不同的感受,大多数在意料之中。


不论如何,如果您有什么问题,可以在该博客反馈我,我会在回复、正文中将其反馈。


以下是近期一些同学向我提出的问题:

【光盘中的代码编译有点问题,如何使用?】:

解释:本书的代码大部分基于JDK 1.6,少部分会依赖于JDK 1.7。不过代码之间的依赖关系不强,因此在切换JDK 版本的时候,将相应的类注释掉应该就OK了。lib目录下有一些依赖包,如果您自己有,就不需要这些包了。


【effective Java中及JVM规范中看到重写equals总是重写hashCode,但胖哥的书上有一句说他们关系不大】:

解释:

1、要注意一点,什么是规范,是一种预定,举个例子,当你为别人提供一个API的时候,某个参数传递了,另一个参数必须传递,这就是一种规范。本书强调个人功底,理解内在到底是咋回事,从本质上讲,它们两个没有强依赖关系,不然的话,Java只需要设计一个语法,而不需要2个,或者在只重写其中之一的时候直接编译不通过,因为没有必要多此一举,呵呵!


2、因此,胖哥在书中有这样一段:“此好比:宝剑是否需要有剑鞘?宝贝是否需要有宝箱?雄性是否必须需要雌性?地球是否需要有月亮?而并非是:树是否需要土壤?生命是否需要食物?鱼儿是否必须需要水?世界是否需要阳光?”意思是什么呢?就是他们确实是天生配对的,但是不是强制依赖,不是树必须依赖于突然、生命需要食物的关系,因为他们的用途本身就是不同的。


3、在文中提到了他们的契合点是在Hash类的算法中,例如HashMap、HashSet、CourrentHashMap等,这就是一种通用的约定,才方便Java写出这些Hash算法的基础组件,当然如果你自己写一种算法也是会利用到hashCode也是可以用类似的方式实现的。


4、如果你那天觉得HashCode这个名字不好听、equals名字不好听,或者这两个方法还无法满足变态的需求,你可以自己定义一组接口,定义一组规范,让别人来重写,哈哈,有点高高在上的感觉!


5、最后总结一句,很多事情不用评价对与不对,我们在与事物在自己的理解中要越来越清楚,要形成自己的深刻认识,这是本书所希望您能理解的。同时重写是一种习惯,但是不能当成一种真理,否则很多同学根本不知道为什么需要重写,本书真正希望你通过理解原理的基础上认识到为什么需要这样做。


【JOIN不好,会轮询死等,但胖哥的书上有这样的代码,不好】:

1、胖哥不否认JOIN在某些时候确实不好,不过大家要理解,JOIN依然存在而没有被注解为:@Deprecated,说明官方并没有说它是一个不推荐的方法,它本身也有它的用途所在。


2、在本书JOIN是用在等待多个线程结束后去做其他的事情,在后面的文章中有更多的方法来完成类似的功能,而且比JOIN做得更好,还可以完成许多JOIN做不到的事情。因此JOIN首先是一个引导,而非胖哥推荐,至少你在写一段测试程序的时候,用JOIN是很方便的。


3、会轮询死等,这个说法有2个点,分别是轮询、死等,其实对于JOIN基本是不成立的,首先来说下轮询,虽然跟踪源码你可以看到这样一段代码:

while (isAlive()) {
 wait(0);
}

这段代码的执行因素是join()方法不传递任何参数的时候,会走到这里(问我的同学认为这是JVM底层C代码,其实不然,它就是Java部分的代码,在source包里面可以直接看到)。虽然是一个while循环,但是这个while循环只是为了保证活着,实际运行时还算不上轮询,因为wait(0)方法,传入的值是0,它会一直等待,直到有interrupt或notify发生,基于线程本身来做wait(0)发生这个notify的概率是极低的(除非是自己故意模拟出来的),一般到线程结束循环只会进去一次,就算是几次,也算不上什么轮询。


关于阻塞,问我的同学认为阻塞就是底层在不断做while循环if(xx) {break} 的动作,这个理解是存在问题的,阻塞从某种意义上将你可以认为是线程不干活,但是在OS里面注册了激活的事件,线程暂时不会得到CPU的调度,当相应事件成熟后,才会继续运行。反过来理解,如果阻塞的底层是在做while(true) {if(xx) {break;}}那么一个阻塞的线程足以让一个CPU跑满,这个大家可以在操作系统上并行几个线程做这样的操作来验证(当然前提是让每个线程都在不断循环)。因此阻塞和内部轮询不是一回事。


死等,join的目的是等线程结束,线程没有结束,那么等待是必然的,用其它的框架如果业务上要求必须等待所有任务执行完成,只要任务还没有跑完,也必然是会死等的,这个是毫无疑问的。只是join的死等是一种自身不可控的等待方式,如果一直等,需要外部去中断它,或许其它框架有更好的方式。


另外,join基于线程结束运行的,很多情况下,我们希望线程被复用,例如基于线程池的任务,只要一些相关的任务结束,就任务可以去做下一件事情了,而不需要线程退出。这些在本书的第五章都会有介绍。


最后,join等待线程结束的另一个弊端是join等待线程结束的顺序,并非线程结束的真正顺序,当你期望得到一批任务结束的顺序时,通过单纯的join是做不到的,除非自己又在线程里面增加一个结束时间,最后再来排序,那岂不是相当麻烦?


【本书正文第1章,第22页有一句话写错】

错误语句:

例如:Integer.valueOf("10",16)返回的值就是16,Integer.valueOf("10",16)返回的值是2,这样就可以实现任意进制之间的转换了。

正确语句:

例如:Integer.valueOf("10",16)返回的值就是16,Integer.valueOf("10",2)返回的值是2,这样就可以实现任意进制之间的转换了。


【本书正文第2章,全文第48页有一句话写错】

错误语句:

这个结论和Cacheline有什么关系呢?因为Java数组在内存中分配是先分配第一维(或者说Java没有真正意义上的二维数组),然后再分配多个第二维子数组,也就是说,a[0][x]和a[0][x]是位于两个不同数组上的,空间也自然不会在一起

正确语句:

这个结论和Cacheline有什么关系呢?因为Java数组在内存中分配是先分配第一维(或者说Java没有真正意义上的二维数组),然后再分配多个第二维子数组,也就是说,a[0][x]和a[1][x]是位于两个不同数组上的,空间也自然不会在一起.


【有没有数据结构、容器类的知识】

1、其实数据结构是在本书的字里行间,相信如果你没有数据结构的基础,看本书会有点糊涂。本书没有专门去讲解数据结构,而是在Java当中通过空间、算法思想等自然带出来的,要知道数据结构最终是服务于我们的程序,帮助我们更好的理解程序,而Java中的数据结构是空间上的具体实现,它会有一些小变化,但是至始至终都没离开过这个话题。

2、容器,这个概念比较泛滥,很多地方到处都引用这个名词,WEB容器、Java的集合类也叫容器、某些中间件和平台也叫容器等。如果说WEB容器,在本书上册的第4章会有一部分源码的讲解,下册还会介绍一些坑。集合类有一些小有意思的介绍,当然是用上介绍并不多,我们更多是从原理上了解它的空间、时间、锁、并发性,不过我更加希望大家在了解基础对象的内存结构后再了解集合类,因为基础对象了解后,集合类的内存结构就是数据结构的结合,游刃有余。


【希望提到优化】:

优化这个话题比较大,其实小胖写这本书,其中一个目的就是希望大家从根本上去认识问题的思路,当你学会从根本上去认识问题后,其实优化就不再是一个大问题,唯一的问题就是你需要大胆去思考以及与实践结合进行各种实验。通过理论推断实践,实践印证理论,理论再指导方向。这样去不断做优化,你对优化本身的理解不仅仅是一个盲目测试的结论,而是有清晰目标的测试结论,你可以去分析具体场景和指导优化方案,即使调试参数压测,也是带有目的性的,不然即使性能上去了,我们也不知道到底还有换一个场景有多少坑。当然坑不可避免,我们提前能预知的可以让我们做得更加专业一点。


【下册有没有一些流行技术的介绍】:

这个可能在本书找不到太多,流行的技术即使在下册也只是会提到一些思想和方法,胖哥认为流行技术太多,讲不完,讲完就过时,但他们都是通过这些基础理论组合而来的,有的更多是大胆的创新和打破自我的想法。我们学习技术者,更加希望通过了解一些本质,让自己对行业的技术有底气,学相关的技术都很快,而且不仅仅是学会用,而是学得比别人好,能比别人更会解决问题,比本人能预见到那里可能会有风险,可能会有坑,从而可以从相对长远和全局的角度来解决问题,也从根本上解决问题。


即使我们遇到的某些坑是“硬伤”(确实存在,因为Java方面依赖的三方包太多,三方包本身有硬伤或你的架构本身有硬伤),这种硬伤是一些变态的操作导致的,如果我们深知一些技术本质后,就像我们对业务细节的了解那样,那么也可以有许多思路将硬伤发生的概率降低到很低很低的程度,即使出现也可以在这个低概率发生时,因为我们可预知,所以可通过某些特殊代码手段处理掉。


另外,感谢大家对本书的支持,目前的出货情况比我自己想想当中好。



《Java特种兵 上册》的问答反馈专页