调整堆大小-谨防眼镜蛇效应
“眼镜蛇效应”源于在当时的英国统治的殖民地印度的轶事。英国政府担心眼镜蛇。因此,政府为打死每条眼镜蛇设有奖励。最初,这是一个成功的策略,大量的蛇被打死。然而最终印度人开始繁殖眼镜蛇来创造收入。
当发现有人以繁殖眼镜蛇来创收后,政府取消了这项奖励。但是饲养的再加上野生的眼镜蛇使眼镜蛇的数量成倍增加。这个明显的解决方案却使事情变得更复杂。
那么印度殖民地的眼镜蛇和调整堆大小有什么关系吗?原谅我,我会引导你通过现实生活中的故事来做类比的方式阐述问题。
你开发了一个非常棒的应用程序,以至于激增的流量使你不得不增加新服务来支撑。性能分析后发现堆大小成为应用程序的瓶颈。
所以你花了些时间调整堆为原先的6倍,测试验证有效后正式上线。当流量开始进来后,应用程序比2G堆时候的响应还要慢,很多用户要等上好几分钟才能等到应用程序的响应。可是到底发生什么了呢?
造成响应慢的原因可能有很多,但是我们的挑重点的抓-堆大小的改变。堆大小的改变可能会造成几种副作用产生,比如延长缓存预热时间,分片问题等。但是从症状来推断,应用程序产生大规模延迟最有可能是由于full GC的停顿造成的。
因为Java是一种垃圾回收的语言,所以当堆空间需要回收的时候是JVM内部去完成回收过程的。想想现实生活中,如果你有一个大房子需要清理,自然而然的就需要花费更多的时间。同样,道理也适用于JVM的垃圾回收器回收无用对象。
当应用程序运行在堆空间小于4G下的时候,不需要深入思考GC的内部机制,但是当堆空间越来越大,达到十几G的时候就不得不考虑full GC长时间“stop-the-world”停顿了。当然在堆空间很小的时候也存在这样的停顿,但是这点儿是时间完全可以被忽略的,原先的几百毫秒而现在则需要停顿近1分钟之久,对应用程序来说这真的是一场灾难。
那么当要增大堆空间的时候我们能做些什么呢?
结论:即使出于美好的愿望做出更改时,也一定要考虑做出更改后的负面影响。正如英国印度殖民政府为打死眼镜蛇设立奖赏,由于当地居民饲养创收进而撤销奖赏后造成眼镜蛇数量激增一样。
文章作者:Nikita Salnikov-Tarnovski 原文链接:http://plumbr.eu/blog/increasing-heap-size-beware-of-the-cobra-effect
选32位还是64位的JVM好呢?
这个问题在我企业级软件开发生涯中不止一次遇到过,每次遇到这种问题的时候我都建议为特定的要求配置个全新的环境。老实说以前回答这个问题很随意,根本没有给出有力的说辞。为此针对这个问题我做了些深入的研究,现在就和大家分享一下。
首先剔除越大越好的思想.如果因为64>32就选择64位的JVM那么这个问题也就变得非常简单了,也就没有讨论的必要了。好了,从现在开始稳住。64位体系结构的缺点是在相同数据结构的情况下会占用更多的内存。而且多了不少,根据我们在不同的JVM版本,操作系统以及硬件架构上的测试结果显示,使用64位JVM占用的堆空间要比使用32位JVM多上30%到50%。增大堆空间还会增加GC停顿时间,从而影响应用程序的响应时间.在4.5G的堆空间上执行full GC肯定要比在3G的堆空间花费更多的时间。
通常换做64位JVM是因为堆大小的限制,不同的平台下32位JVM很快就会受到最大堆大小的限制.下面这个表格说明了不同平台下的限制:
| OS | Max heap | Notes |
|---|---|---|
| Linux | 2GB | 一些特殊的内核是3G,比如hugemem |
| Windows | 1.5GB | 3GB的引导标志和支持LARGEADDRESSAWARE编译的jre才能达到3G |
| Mac OS X | 3.8GB | 注意;因为找不到古老的Mac所以这里只是我的经验 |
看起来很糟糕对吗?总之我敢打赌你肯定见过在16G+内存的机器上使用32位的JVM而且运行状况良好。使用32位的JVM有什么错吗?难道它在Windows平台下真的只能分配不到16G的10%吗?
主要还是地址空间的限制,在32位操作系统上理论上可以为每个进程分配4G空间。那是什么原因使Windows平台上的进程只能处理一半的地址空间呢,另一半的地址空间被谁占用了呢?答案是一半是留给内核使用( 这些地址空间用户进程是没法使用的 ),另一半才是用户使用的。所以不管32位操作系统中不管有多大的RAM,每个进程只能分配2G的空间。更糟糕的是这些地址还必须是连续的,所以实际上Windows可供使用的堆空间只有1.5-1.8G。
有一个技巧可以通过减少内核空间增加用户空间使32位的Windows系统可以为每个用户进程分配3G空间。在boot.ini文件中配置3GB参数,不过只有使用有LARGEADDRESSAWARE开关的JVM进行编译和链接才有效。
不幸的是,情况并非想象中那么理想。至少在 JDK1.7 之前的 HotSpot JVM都没有这个编译参数.如果你运气足够好,刚好使用JRockit 2006之后的版本,那么你就可以体验一把2.8-2.9G的堆空间。
这样我们就可以得出如果应用程序需要使用超过2-3G的堆空间,就可以使用64位的JVM吗?也许吧,在做出决定之前也要考虑一下64位JVM会占用更多的堆空间以及更长的GC停顿.下面我们就来分析下这两个原因:
问题1:64位JVM会多占用30%-50%的堆空间。为什么会这样呢?主要是由于64位JVM架构的内存布局造成的。首先64位JVM对象头占12个字节;其次对象引用可以是4字节或8字节。取决于JVM的标志和堆空间大小。和32位JVM的8个自己和对象头和4个字节的引用相比这无疑是增加了不少的开销。有关计算对象的内存消耗可以在我之前的文章里获得更详细的信息。
问题2:长时间的GC停顿。设置更大的堆空间意味着GC在清理无用对象时要做更多的工作,实际项目中当堆空间大于12-16G的时候,就需要你格外的小心,一次full GC很容易就要暂停几分钟时间,这几分钟的暂停时间是至关重要的,也许你通过微调和测量来优化吞吐量使其可以很好的工作。但是大多数情况下,它都会使事情变得糟糕。
那么当需要更大的堆空间,又不想负担64位JVM带来的额外开销时,有其他方案可以替代吗?当然,在之前我写的几篇文章中有介绍过一些可以让你从堆分配,GC调优等恼人的工作中全身而退的技巧。
最后让我们重新总结一下本篇文章的中心思想:选择64位JVM时要额外注意它带来的负面影响,但也不能因此就对它敬而远之。
文章作者: Vladimir Šor
jQuery选择器
目录
什么是jQuery选择器?
jQuery选择器是jQuery类库最重要功能之一。这些选择器的用法和CSS的语法非常相似,结合jQuery类库的方法你可以很方便快速地定位页面中任何元素。理解jQuery选择器是高效使用jQuery类库的关键。本索引卡就将jQuery选择器强大的功能展现在你眼前。
典型的jQuery语法格式如下:
$(selector).methodName();
selector是一个字符串表达式,用于将符合的DOM元素放到集合中,以供jQuery类库方法使用。
多个jQuery操作可以以链的形式串起来:
$(selector).method1().method2().method3();
比如,要隐藏id为goAway的DOM元素,并且为其添加名为incognito的样式。实现如下:
$('#goAway').hide().addClass('incognito');
使用起来非常简单,这就是选择器表达式的强大之处。
应用中通过选择器获取到的元素集合可以方便的当做JavaScript数组处理。尤其在希望通过数组下表直接访问包装集合中元素的时候。
比如:
var element = $('img')[0];
它会将element指向通过选择器获取到的包装集合的第一个元素。
jQuery选择器种类
jQuery选择器有三种:基本的CSS选择器、位置选择器和自定义选择器。
由于基本选择器用来查找DOM中的元素,又名查找选择器。位置选择器和自定义选择器则用于过滤元素(默认为DOM的全部元素)得名过滤选择器。
基本的CSS选择器
这些选择器遵循标准CSS3的语法和语义。
| 语法 | 描述 |
|---|---|
| * | 匹配所有元素 |
| E | 匹配所有E元素 |
| E F | 匹配所有F元素,F为E元素的后代 |
| E>F | 匹配所有F元素,F作为E元素的直接孩子 |
| E+F | 匹配所有F元素,F为E元素的直接后兄弟(紧挨着E元素后面出现) |
| E~F | 匹配所有F元素,F为E元素的后兄弟 |
| E:has(F) | 匹配所有E元素,E至少有一个后代为F元素 |
| E.c | 匹配所有E元素,E有class名字为c 忽略E的话,与*.c一样 |
| E#i | 匹配所有E元素,E的id属性值为i 忽略E的话,与*#i一样 |
| E[a] | 匹配所有E元素,E有一个a属性,取值不限 |
| E[a=v] | 匹配所有E元素,E有一个a属性,并且值必须为v |
| E[a^=v] | 匹配所有E元素,E有一个a属性,并且属性值以v开头 |
| E[a$=v] | 匹配所有E元素,E有一个a属性,并且属性值以v结束 |
| E[a*=v] | 匹配所有E元素,E有一个a属性,并且属性值中包含v |
示例
- $('div') 获取全部div元素
- $('fieldset a') 获取全部包含在<fieldset>中的<a>元素
- $('li>p') 获取全部<p>元素,<p>元素为<li>元素的直接孩子
- $('div~p') 获取去全部<div>元素,<div>紧挨着<p>元素出现
- $('p:has(b)') 获取全部包含<b>的<p>元素
- $('div.someClass') 获取全部<div>元素,<div>包含样式名为someClass的class
- $('.someClass') 获取全部包含someClass样式名的元素
- $('#testButton') 获取id为testButton的元素
- $('img[alt]') 获取全部拥有alt属性的<img>元素
- $('a[href$=.pdf]') 获取全部<a>元素,<a>元素的href属性以.pdf结尾
- $('button[id*=test]') 获取全部<button>元素, <button>元素id属性值中包含test
在一个$()中可以使用多个选择器,用逗号分隔即可。比如下面获取全部<div>和<p>元素的表达式:
$('div,p')
下面这个则匹配所有有title属性的<div>元素和有alt属性的<img>:
$('div[title],img[alt]')
位置选择器
这些选择器的匹配是基于元素之间的相对位置。这些选择器也可以追加到基本选择器(用B表示)后面,基于位置再次过滤元素。如果不考虑B则等同于(全部元素)。
| 语法 | 描述 |
|---|---|
| B:first | 获取B选择器获取的元素集合中的第一个元素 |
| B:last | 获取B选择器获取的元素集合中的最后一个元素 |
| B:first-child | 获取全部B元素,B为父元素的第一个孩子 |
| B:last-child | 获取全部B元素,B为父元素的最后一个孩子 |
| B:only-child | 获取全部B元素,B为父元素仅有的孩子 |
| B:nth-child(n) | 获取全部B元素,B为父元素的第n个孩子,n从1开始 |
| B:nth-child(odd|even) | 获取全部B元素,B元素为第奇数或偶数个孩子 第一个孩子被算作奇数 |
| B:nth-child(Xn+Y) | 获取全部满足表达式(x*序号+y偏移值)的B元素,y为0则被忽略 |
| B:even | 获取B选择器获取的元素集合中的序号为偶数元素 |
| B:odd | 获取B选择器获取的元素集合中的序号为奇数元素 |
| B:eq(n) | 获取B选择器获取的元素集合中的第n个元素,从0开始 |
| B:gt(n) | 获取B选择器获取的元素集合中的序号大于n的元素,从0开始 |
| B:lt(n) | 获取B选择器获取的元素集合中的序号小于n的元素,从0开始 |
示例
- $('p:first') 获取页面中的第一个<p>
- $('img[src$=.png]:first') 获取第一个scr以.png结尾的<img>元素
- $('button.small:last') 获取最后一个有small样式名的<button>
- $('li:first-child') 获取每个列表中第一个<li>
- $('a:only-child') 获取全部<a>元素,<a>元素为父元素仅有的孩子
- $('li:nth-child(2)') 获取全部列表中第二个<li>元素
- $('tr:nth-child(odd)') 获取表格中序号为偶数的<tr>元素
- $('div:nth-child(5n)') 获取全部序号为5的<div>元素
- $('div:nth-child(5n+1)') 获取全部序号5n后面的元素
- $('.someClass:eq(1)') 获取第二个样式名为someClass的元素
- $('.someClass:gt(1)') 获取除前两个之外所有样式名为someClass的元素
- $('.someClass:lt(4)') 获取前四个样式名为someClass的元素
注意::nth-child选择器获取的结果集合序号从1开始,而:eq,:gt和:lt选择器获取的结果集合从0开始。
jQuery自定义选择器
自定义选择器是为了那些经常使用,并且CSS规范中没有提供而被jQuery支持的。比如位置选择器,这些选择器在基本选择器获取的结果集合的基础上再次过滤。下面来看一些强大的选择器组合。
| 语法 | 描述 |
|---|---|
| B:animated | 获取B选择器获取的元素集合中,并且在jQuery动画方法控制下的元素 |
| B:button | 获取B选择器获取的元素集合中为button类型的元素 包括button,input[type=submit], input[type=reset]和input[type=button] |
| B:checkbox | 获取B选择器获取的元素集合中input[type=checkbox]类型的元素 |
| B:enabled | 获取B选择器获取的元素集合中enabled状态的元素 |
| B:file | 获取B选择器获取的元素集合中input[type=file]类型的元素 |
| B:header | 获取B选择器获取的元素集合中从<h1>到<h6>类型的元素 |
| B:hidden | 获取B选择器获取的元素集合中hidden状态的元素 |
| B:image | 获取B选择器获取的元素集合中input[type=image]类型的元素 |
| B:input | 获取B选择器获取的元素集合中<input>, <select>,<textarea>和<button>类型的元素 |
| B:not(f) | 获取B选择器获取的元素集合中不匹配选择器f的元素 |
| B:parent | 获取B选择器获取的元素集合中含有子元素的元素 |
| B:password | 获取B选择器获取的元素集合中input[type=password]类型的元素 |
| B:radio | 获取B选择器获取的元素集合中input[type=radio]类型的元素 |
| B:reset | 获取B选择器获取的元素集合中input[type=reset]或button[type=reset]类型的元素 |
| B:selected | 获取B选择器获取的元素集合中选中状态的元素,只有<option>元素才有这个状态 |
| B:submit | 获取B选择器获取的元素集合中input[type=submit]或button[type=submit]类型的元素 |
| B:text | 获取B选择器获取的元素集合中input[type=text]类型的元素 |
| B:visible | 获取B选择器获取的元素集合中状态不为hidden的元素 |
示例
- $('img:animated') 获取所有刚刚经历完动画方法调用的<img>元素
- $(':button:hidden') 获取所有被隐藏的button元素
- $('input[name=myRadioGroup]:radio:checked') 获取所有name属性为myRadioGroup并且为checked状态的radio元素
- $(':text:disabled') 获取所有不能编辑的text元素
- $('#xyz p :header') 获取所有包含在id为xyz元素中<p>里面的header类型的元素,注意:header 前面有一个空格
- $('option:not(:selected)') 获取所有没有被选中的<option>元素
- $('#myForm button:not(.someClass)') 获取所有id为myForm的<form>中的<button>元素,并且<button>没有样式名为someClass的样式
- $('select[name=choices] :selected') 获取名字为choices的<select>元素中被选中的<option>元素
- $('p:contains(coffee)') 获取全部包含coffee文本的<p>元素
不论单独还是组合使用jQuery选择器,都可以很方便的获取一组元素,以供jQuery类库方法进行操作。
匹配函数
尽管选择器为我们提供了非常灵活的过滤DOM的手段,但还是有时候单单使用选择器是没法表示匹配规则。而且我们可能也需要在方法调用见调整匹配元素的内容。
基于以上两点,jQuery提供了一些用于调整匹配元素的方法。本节将重点介绍这些方法。
添加新元素
add()方法用于向匹配元素集合中添加新元素。
| add(expression) | |
|---|---|
| expression |
String,如果是一个选择器就表示添加选择器匹配的所有元素,如果是html代码,则会添加新创建的DOM元素 Element,则添加引用的元素 Array,则添加数组中的全部元素 |
add()方法返回一个新的匹配元素集合,原有匹配元素集合再加上参数表达式(不论是直接表达式还是选择器表达式)一起组成新的匹配集合。
来看看这个表达式:
$('div').add('p').css('color','red');
这段代码首先获取一个全部<div>元素的集合,然后又将现有的<div>元素集合加上<p>元素形成一个新的集合(所有的<div>和<p>元素),最后为他们添加color=read的样式。
可是这样的代码完全没有什么意义,因为他和下面这句的效果一样:
$('div,p').css('color','red');
不过下面这句用选择器就搞不定了:
$('div').css('font-weight','bold').add('p').css('color','red');
首先将所有的<div>元素中内容都加粗,最后加入所有<p>元素后形成新的集合,最后增加color=read的样式。
jQuery的方法链可以用很简短的代码实现强大的功能。
再来一个例子:
$('div').add(someElement).css('border','3px solid pink');
$('div').add([element1,element2]).css('border','3px solid pink');
删除匹配元素
如果想冲匹配元素集合中删除元素要怎么办呢?这个正是not()方法的功能:
| not(expression) | |
|---|---|
| expression |
String,从匹配集合中删除选择器参数匹配的元素 Element,从匹配集合中删除参数所引用的元素 Array,从匹配集合中删除数组中的元素 |
和add()方法一样,not()方法也会返回一个新的元素集合,新集合中剔除表达式参数匹配的元素。参数可以使jQuery选择器,也可以是指向元素的引用。
示例
$('body *').css('font-weight','bold').not('p').css('color','red');
将所有元素的内容都加粗,然后把除<p>元素外的所有元素都添加color=read样式。
$('body *').css('font-weight','bold').not(anElement).css('color','red');
同上面那句类似,只不过剔除anElement元素在外。
为了避免初学者误用not方法,请注意,not()方法是在匹配元素集合中删除,而remove()方法则是在HTML的DOM元素的删除。
寻找后代
有时候从匹配集合中搜索后代元素是非常有用的,这个功能可以用find()方法实现:
| find(expression) | |
|---|---|
| expression | 选择器表达式,用于标示哪些后代可以被匹配 |
和前面其他的方法不同find()方法只接受选择器表达式作为参数,匹配集合中元素的后代将被选择器表达式参数再次匹配。原匹配集合中任何元素不参与第二次匹配。
示例:
$('div').css('background-color','blue').find('img').css('border','1px solid aqua');
将所有<div>元素的背景变为蓝色,再将所有这些<div>元素中的<img>子元素加上aqua样式的边框。
过滤匹配集合
当有更细粒度的需求,需要对匹配结果集合进行过滤的时候,就轮到filter()方法出场了:
| filter(expression) | |
|---|---|
| expression |
字符值,选择器表达式用于标示哪些元素可以被保留 函数值,一个函数型参数用来判断一个元素是否应该包含在新的集合中。从0号元素开始作为函数的参数,返回false则表示该元素不能出现在新的集合中。 |
filter()函数可以以选择器(如果有多个则用逗号分隔)或者函数作为入参。当入参为选择器时,就完全与not()函数相反,会保留全部匹配的元素而不是删掉他们。当入参为一个函数时,这个函数会被每个元素调用,来判断是否匹配选择器表达式,从而决定是否能添加到新集合中。
示例
$('.bashful').show().filter('img[src$=.gif]').attr('title','Hi there!');
选择所有拥有bashful样式的元素,确保他们是可见的,并且保留gif格式的<img>元素,最后给这些<img>元素增加title属性。
分割匹配集合
尽管可以使用选择器来获取匹配元素,但是有时候还是想通过匹配集合中元素的位置来分割匹配集合,本节介绍两个方法来完成这项功能:
这两个方法的索引值都是从0开始的。
| slice(being,end) | |
|---|---|
| begin | 数字类型,新集合中第一个元素的索引值 |
| end | 数字类型,最后一个不能添加到新集合中的索引值,如果忽略则从begin到最后的元素都被包含在新集合中 |
示例
$('body *').slice(2).hide();
隐藏body中除了前两个元素之外的所有元素。
$('body *').slice(2,3).hide();
隐藏body中第三个元素。注意:只包含索引值为2的元素,不包含索引值为3的元素。
| eq(position) | |
|---|---|
| position | 数字类型,只有索引值为position的元素会被添加到新集合中 |
eq(n)可以被看做是slice(n, n+1)的简化方式。
通过关系匹配
通过元素之间关系来创建新集合这个需求经常存在,下面这些方法就是完成这项功能的,他们非常相似,所以把他们放进同一个表格:
| 语法 | 描述 |
|---|---|
| children(expression) | 从匹配集合中获取匹配expression的所有子元素 |
| next(expression) | 从匹配集合中获取匹配expression的所有兄弟元素,仅仅是匹配的直接后兄弟元素 |
| nextAll(expression) | 从匹配集合中获取匹配expression的所有兄弟元素,所有匹配的后兄弟元素均可 |
| parent(expression) | 获取所有匹配集合中匹配expression的直接父亲元素 |
| parents(expression) | 获取所有匹配集合中匹配expression的父亲元素 |
| prev(expression) | 从匹配集合中获取匹配expression的所有前兄弟元素,仅仅是匹配的直接前兄弟元素 |
| prevAll(expression) | 从匹配集合中获取匹配expression的所有前兄弟元素,所有匹配的前兄弟元素 |
| siblings(expression) | 从匹配集合中获取匹配expression的所有兄弟元素,所有匹配的兄弟元素 |
| contents() | 获取匹配集合中每个元素的子元素,包括文本。也可以用于获得iframe的内容文档 |
这些方法(除了contents()方法)都接受一个过滤器表达式作为参数,如果不传参数则不进行过滤。
转换元素
当想将匹配集合中每个元素转换为其他类型的元素时,可以使用map()方法。
| map(callback) | |
|---|---|
| callback | 函数类型,匹配集合中每个元素都会调用callback函数。函数的返回结果会被放入数组中作为map()函数的返回值。当前元素作为函数的入参。 |
比如,想获取id为myForm的表单中全部表单元素的值:
var values = $('#myForm :input').map(function(){
return $(this).val();
} );
map()方法返回的是jQuery对象的实例,可以使用无参的get()函数转换为javascript中的数组:
var values = $('#myForm :input').map(function(){
return $(this).val();
} ).get();
这样values就是一个javascript数组而不是jQuery对象。
控制链
前面提到的所有方法都以自己的方式创建新的匹配集合,那么原来的匹配集合哪里去了呢?被回收了吗?当然不是。当新的匹配集合被创建后,就被放到了栈顶,任何方法都可以使用(就像我们在示例中看到的那样)。但是jQuery允许弹出栈顶元素,这样方法就可以使用原来的匹配集合了。这就是end()方法的作用。
再来看一下前面的一个例子:
$('div').add('p').css('color','red');
首先创建一个所有<div>的集合,随后又创建一个包含所有<div>和<p>元素的集合,当css()方法被调用时,第二个集合位于栈顶的位置,所以起作用的为第二个集合。那么这个该怎么理解呢?
$('div').add('p').css('color','red').end().hide();
css()方法调用后,end()方法把原先的匹配集合(全部<div>元素的集合)暴露了出来,然后他们就被隐藏了。
还有一个可以影响匹配集合操作链的方法是andSelf()。
调用andSelf()方法后,会将栈顶的两个元素集合合并成一个全新的集合,这个方法在某些场合下非常有用,比如先通过一个方法创建一个集合,再创建一个不同的新集合,然后在这两个集合的基础上调用一个或多个方法。
$('div').css('background-color','yellow')
.children('img').css('border','4px ridge maroon').andSelf().css('margin','4em');
首先把所有<div>元素的背景色都换成黄色,然后为子元素<img>添加边框,最后为<div>和子<img>元素设置缩进。
jQuery选择器和jQuery方法间,我们可以操作匹配集合,jQuery为我们提供了异常强大的工具来选择DOM元素,还有非常多的jQuery方法可在其基础上进行操作。
本文来自DZone,作者为:Bear Bibeault & Yehuda Katz
三件Java开发者应该知道的事儿
这是一篇有趣的文章,应该符合那些喜欢JavaOne2012大会的人的口味。近期对Java领域专家Heinz Kabutz的一篇采访深深吸引了我,其中他的Java内存谜题程序从Java内存管理角度来看非常具有指导意义。
采访中有一段我印象特别深:Java开发者应该掌握的却至今没有掌握的知识。采访的过程中Heinz提出了很多非常好的观点,今天这篇文章就重新回顾和延展一下那天值得Java开发者关注的知识。
同时Heinz也提出了他对于未来Java8发布版本中去除HotSpot VM PermGen的担忧。
Java并发原则:是否应该注意呢?
Heinz指出,关于这这个主题的讨论通常被大部分开发者回避。除非在只编写单线程程序,否则线程并发以及相关问题是我们必须关注的问题。特别作为Java EE开发者编写的代码都会运行在高并发的环境下,一个小小的编码错误就会造成严重的线程竞争,稳定性和性能方面的问题。同样,缺乏线程相关知识,你也没法对Java EE容器的线程池做出适合的调整。
我还是建议,每个Java开发者都应该学习基本的Java并发原则知识,不论是从编码还是处理类似JVM 线程Dump分析这样的问题来讲都是非常必要的。
提升IDE使用技巧:掌握快捷键
Heinz令一个建议是每个程序员都应该深入了解Java IDE使用技巧。这个建议听起来很正常,但是你可能会惊讶只有很少一部分人可以很了解IDE的使用以及可以通过IDE提高生产效率。这种情况通常是由于缺乏对IDE的快捷键以及能力的探索。
如果你使用Eclipse,那么DZone上这篇讲述Eclipse有用快捷键的文章算是一个不错的入门。
Java内存管理:学会读懂GC logs
最后一个但不止于此:学会读懂GC logs,这条建议是我最喜欢的一个。就像之前我写过的文章中那样,JVM GC logs包含很多有关内存占用以及垃圾回收状况的重要信息。这些数据在JVM调优,以及排除Java堆空间OutOfMemoryError异常相关问题尤为重要。
不过说实话,就连想要拥有Kirk Pepperdine这样的专家一半的知识,需要花费非常多的时间,但是开始分析和了解应用中GC logs和Java内存管理基础来说是个非常好的开始。
译者注:翻译文章有翻译文章的乐趣,过程中会加深对文章内容的理解,而且一篇文章会像宝库一样,会挖掘出各种财宝。
本文翻译自:http://java.dzone.com/articles/three-things-java-developers
相关阅读:(有些文章可能需要翻墙,自备翻墙工具)
- https://blogs.oracle.com/javaone/entry/the_java_specialist_an_interview
- http://javaeesupportpatterns.blogspot.com/2011/10/java-7-features-permgen-removal.html
- http://docs.oracle.com/javase/tutorial/essential/concurrency/
- http://javaeesupportpatterns.blogspot.com/2011/11/how-to-analyze-thread-dump-part-1.html
- http://eclipse.dzone.com/news/effective-eclipse-shortcut-key
- http://javaeesupportpatterns.blogspot.com/2011/10/verbosegc-output-tutorial-java-7.html
- http://javaeesupportpatterns.blogspot.com/2011/11/outofmemoryerror-java-heap-space.html
泛型:List<? extends Animal>和List<Animal>一样吗?
stackoverflow上看见一篇关于java泛型非常好的讨论,所以今天拿出来简单翻译一下。
正文
Q:
我只想弄清楚java泛型中extends关键字,List<? extends Animal>就是任何继承Animal的对象都可以插入到这个List中。它和下面这句难道不一样吗?List<Animal>。谁能帮我解释下这两种用法的不同吗?在我看来extends有些多余啊!
谢谢!
A1:
List<Dog>是List<? extends Animal>的子类型,但不是List<Animal>的子类型。
为什么List<Dog>不是List<Animal>的子类型呢?先看一下这个例子吧:
void mySub(List<Animal> myList) {
myList.add(new Cat());
}
如果允许以List<Dog>为参数传入这个方法,那么会发生运行时异常。
EDIT:如果换做List<? extends Animal>为参数,下面的情况会发生;
void mySub(List< ? extends Animal> myList) {
myList.add(new Cat()); // compile error here
Animal a = myList.get(0); // works fine
}
可以以List<Dog>为参数传入这个函数,但是编译器会发现这样会给你带来很多麻烦。如果用super代替extends(允许传入List<LifeForm>),情况就完全相反了:
void mySub(List< ? super Animal> myList) {
myList.add(new Cat()); // works fine
Animal a = myList.get(0); // compile error here,
//since the list entry could be a Plant
}
背后的原理是:协变性和逆变性
A2:
它们是不一样的,List<Animal>表示它指向的变量值得类型必须是List<Animal>类型的,这并不意味着只能添加Animal对象,还可以添加Animal对象的子类。
List<Number> l = new ArrayList<Number>();
l.add(4); // autoboxing to Integer
l.add(6.7); // autoboxing to Double
如果想构造一个List存储Number对象,并且这个List本身并不需要是List<Number>类型的,可以也是这个List的子类型(比如List<Integer>),这个时候可以使用List<? extends Animal>。
List<? extends Number>这种方式用在方法参数时的含义是:只要是个Number列表即可,List<Double>类型的也可以;而List<Number>作为参数时,可以避免错误的向上类型转换,比如想获得一个基类类型的List,却传入一个子类类型的List。
publid void doSomethingWith(List<Number> l) {
...
}
List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // not working
上面这段代码不起作用是因为参数的类型是List<Number>而不是List<Double>。如果改成List<? extends Number>那么传入List<Double>是没有问题的。
publid void doSomethingWith(List< ? extends Number> l) {
...
}
List<Double> d = new ArrayList<Double>();
doSomethingWith(d); // works
注意:这东西和List中元素的继承关系无关,不管是否使用? extends都可以将Double或Integer添加到List<Number>中。
A3:
看见你已经找到答案了,我还是想补充一下我的理解,希望能帮上忙。
List<? extends Animal>和List<Animal>的区别如下:
List<Animal>就是定义了一个动物的列表,它的元素不仅可以是Animal对象,也可以是Animal的派生类。比如,有一个动物列表,一部分是山羊,还有猫咪等,是这样吗?
下面这个例子证明确实是这样的:
List<Animal> aL= new List<Animal>();
aL.add(new Goat());
aL.add(new Cat());
Animal a = aL.peek();
a.walk();//assuming walk is a method within Animal
顺便提一下下面这样是不合法的:
aL.peek().meow();//we can't do this, as it's
//not guaranteed that aL.peek() will be a Cat
当然如果确定aL.peek()就是返回一个Cat对象,那么可以这样做:
((Cat)aL.peek()).meow();//will generate a runtime error
//if aL.peek() is not a Cat
至于List<? extends Animal>是定义了这个List本身的类型,而不是元素的类型。
比如:
List<? extends Animal> L;
这段代码不是声明L可以拥有什么类型的对象,而是L本身可以指向什么类型的引用。
比如这样:
List<Goat> aL = new ArrayList<Goat>();
L = aL;//remember aL is a List of Animals
经过这样赋值后,编译器就知道L是一个Animal或其子类类型的List了。
所以下面这样做是非法的:
L.add(new Animal());//throws a compiletime error
很明显L是指向Goat类型列表的引用,所以没法将Animal类型的对象加入L。
很好,那因为是什么呢?看这里:
List<Goat> gL = new List<Goat>();//fine
gL.add(new Goat());//fine
gL.add(new Animal());//compiletime error
上面add方法报错是因为把Animal类型的对象转换为Goat对象。如果那样做是合法的,那么在调用headbutt方法时,我们没法确定这个Animal对象是否有那样的方法,所以在编译期就会报错。
原文链接:http://stackoverflow.com/questions/2575363/generics-list-extends-animal-is-same-as-listanimal