|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
先谈谈我对java的一些认识。我选择java,是因为他语法简单,功能强大,从web,到桌面,到嵌入式,无所不能。但当我进一步了解了java后,感叹,java原来也有许多缺点。功能 尾递回转换能加速使用程序的速率,但不是一切的JVM城市做这类转换,良多算法用尾递回办法暗示会显得分外简明。编译器会主动把这类办法转换成轮回,以进步程序的功能。但在Java言语标准中,并没有请求必定要作这类转换,因而,并非一切的Java假造机(JVM)城市做这类转换。这就意味着在Java言语中接纳尾递回暗示大概招致伟大的内存占用,而这并非我们希冀的了局。EricAllen在本文中论述了静态编译将会坚持言语的语义,而静态编译则一般不会。他申明了为何这是一个主要成绩,并供应了一段代码来匡助判别您的立即(JIT)编译器是不是会在坚持言语语义的同时做尾递回代码转换。
尾递回及其转换
相称多的程序包括有轮回,这些轮回运转的工夫占了程序总运转工夫的很年夜一部分。这些轮回常常要重复更新不止一个变量,而每一个变量的更新又常常依附于别的变量的值。
假如把迭代当作是尾递回函数,那末,就能够把这些变量当作是函数的参数。复杂提示一下:假如一个挪用的前往值被作为挪用函数的值当即前往,那末,这个递回挪用就是尾递回;尾递回不用记着挪用时挪用函数的高低文。
因为这一特性,在尾递回函数和轮回之间有一个很好的对应干系:能够复杂地把每一个递回挪用看做是一个轮回的屡次迭代。但由于一切可变的参数值都一次传给了递回挪用,以是比起轮回来,在尾递回中能够更简单地失掉更新值。并且,难以利用的break语句也经常为函数的复杂前往所替换。
但在Java编程中,用这类体例暗示迭代将招致效力低下,由于大批的递回挪用有招致仓库溢出的伤害。
办理计划对照复杂:由于尾递回函数实践上只是编写轮回的一种更复杂的体例,以是就让编译器把它们主动转换成轮回情势。如许您就同时使用了这两种情势的长处。
可是,只管人人都熟知怎样把一个尾递回函数主动转换成一个复杂轮回,Java标准却不请求做这类转换。不作这类请求的缘故原由也许是:一般在面向对象的言语中,这类转换不克不及静态地举行。相反地,这类从尾递回函数到复杂轮回的转换必需由JIT编译器静态地举行。
要了解为何会是如许,思索上面一个失利的实验:在Integers集上,把Iterator中的元素相乘。
由于上面的程序中有一个毛病,以是在运转时会抛出一个非常。可是,就象在本专栏之前的很多文章中已论证的那样,一个程序抛出的准确非常(跟很棒的毛病范例标识符一样)关于找到毛病躲在程序的甚么中央并没有甚么匡助,我们也不想编译器以这类体例改动程序,以使编译的了局代码抛出一个分歧的非常。
清单1.一个把Integer集的Iterator中的元素相乘的失利实验
importjava.util.Iterator;
publicclassExample{
publicintproduct(Iteratori){
returnproductHelp(i,0);
}
intproductHelp(Iteratori,intaccumulator){
if(i.hasNext()){
returnproductHelp(i,accumulator*((Integer)i.next()).intValue());
}
else{
returnaccumulator;
}
}
}
注重product办法中的毛病。product办法经由过程把accumulator赋值为0挪用productHelp。它的值应为1。不然,在类Example的任何实例上挪用product都将发生0值,不论Iterator是甚么值。
假定这个毛病终究被更正了,但同时,类Example的一个子类也被创立了,如清单2所示:
清单2.试图捕获象清单1如许的不准确的挪用
importjava.util.*;
classExample{
publicintproduct(Iteratori){
returnproductHelp(i,1);
}
intproductHelp(Iteratori,intaccumulator){
if(i.hasNext()){
returnproductHelp(i,accumulator*((Integer)i.next()).intValue());
}
else{
returnaccumulator;
}
}
}
//And,inaseparatefile:
importjava.util.*;
publicclassExample2extendsExample{
intproductHelp(Iteratori,intaccumulator){
if(accumulator<1){
thrownewRuntimeException("accumulatortoproductHelpmustbe>=1");
}
else{
returnsuper.productHelp(i,accumulator);
}
}
publicstaticvoidmain(String[]args){
LinkedListl=newLinkedList();
l.add(newInteger(0));
newExample2().product(l.listIterator());
}
}
类Example2中的被掩盖的productHelp办法试图经由过程当accumulator小于“1”时抛出运转时非常来捕获对productHelp的不准确挪用。不幸的是,如许做将引进一个新的毛病。假如Iterator含有任何0值的实例,都将使productHelp在本身的递回挪用上溃散。
如今请注重,在类Example2的main办法中,创立了Example2的一个实例并挪用了它的product办法。因为传给这个办法的Iterator包括一个0,因而程序将溃散。
但是,您能够看到类Example的productHelp是严厉尾递回的。假定一个静态编译器想把这个办法的注释转换成一个轮回,如清单3所示:
清单3.静态编译不会优化尾挪用的一个示例
intproductHelp(Iteratori,intaccumulator){
while(i.hasNext()){
accumulator*=((Integer)i.next()).intValue();
}
returnaccumulator;
}
因而,最后对productHelp的挪用,了局成了对超类的办法的挪用。超办法将经由过程复杂地在iterator上轮回来盘算其了局。不会抛出任何非常。
用两个分歧的静态编译器来编译这段代码,了局是一个会抛出非常,而另外一个则不会,想一想这是何等让人感应狐疑。
用java开发web只要两本书:一本是关于java基础的,一本是关于jsp、servlet的就可以了。开发周期长,我就来讲句题外话,现在有很多思想都是通过java来展现。 |
|