??xml version="1.0" encoding="utf-8" standalone="yes"?>
finally子句
JVM执行Java字节码时Q它有几U方式可以退Z个代码块Q花括号中间的语句)。其中之一Q就是简单的执行完其中所有的语句Q然后退Z码块。第二种QJVM可能会在代码块中间的M一处,遇到像breakQcontinueQreturn之类的语句,强制它蟩代码块。第三种QJVM可能会在执行q程中,出现了异常,然后它蟩转到匚w的catch子句Q或者没有找到相应的catch子句Q直接退出当前线E。由于单个代码块有如此多的潜在退出点Qexit pointQ,拥有一个简单的方式来表䏀无Z码块以什么方式退出,有些事情总能发生”是很值得的。然后就有了try-finally子句?nbsp; 阅读全文
]]>
异常处理
在程序运行时Q异常让你可以^滑的处理意外状况。ؓ了演CJVM处理异常的方式,考虑NitPickyMathc,它提供对整数q行加,减,乘,除以及取余的操作?br />
NitPickyMath提供的这些操作和Java语言?#8220;+”Q?#8220;-”Q?#8220;*”Q?#8220;/”?#8220;%”是一LQ除了NitPickyMath中的Ҏ在以下情况下会抛出检查型QcheckedQ异常:上溢出,下溢Z及被0除?做除数时QJVM会抛出ArithmeticException异常Q但是上溢出和下溢出不会引发M异常。NitPickyMath中抛出异常的Ҏ定义如下Q?/p>
NitPickyMathcM的remainder()Ҏ是一个抛出和捕获异常的简单方法?/p>
remainder()ҎQ只是简单的对当作参C递进来的2个整数进行取余操作。如果取余操作的除数?Q会引发ArithmeticException异常。remainder()Ҏ捕获q个异常Qƈ重新抛出DivideByZeroException异常?/p>
DivideByZeroException和ArithmeticException的区别是QDivideByZeroException是检查型QcheckedQ异常,而ArithmeticException是非查(uncheckedQ型异常。由于ArithmeticException是非查型异常Q一个方法就会抛出该异常,也不必在其throw子句中声明它。Q何Error或RuntimeException异常的子cd帔R是非查型异常。(ArithmeticException是RuntimeException的子cR)通过捕获ArithmeticException和抛出DivideByZeroExceptionQremainder()Ҏ它的调用者去处理除数?的可能性,要么捕获它,要么在其throw子句中声明DivideByZeroException异常。这是因为,像DivideByZeroExceptionq种在方法中抛出的检查型异常Q要么在Ҏ中捕P要么在其throw子句中声明,二者必选其一。而像ArithmeticExceptionq种非检查型异常Q就不需要去昑ּ捕获和声明?/p>
javac为remainder()Ҏ生成的字节码序列如下Q?/p>
remainder()Ҏ的字节码?个单独的部分。第一部分是该Ҏ的正常执行\径,q部分从W?行开始,到第3行结束。第二部分是从第4行开始,?2行结束的catch子句?/p>
d节码序列中的irem指o可能会抛出ArithmeticException异常。如果异常发生了QJVM通过在异常表中查扑配的异常Q它会知道要跌{到相应的异常处理的catch子句的字节码序列部分。每个捕获异常的ҎQ都跟类文g中与Ҏ字节码一起交付的异常表关联。每一个捕获异常的try块,都是异常表中的一行。每?条信息:开始行PfromQ和l束行号QtoQ,要蟩转的字节码序列行PtargetQ,被捕L异常cȝ帔R池烦引(typeQ。remainder()Ҏ的异常表如下所C:
FROM
|
TO
|
TARGET
|
TYPE
|
---|---|---|---|
0 | 4 | 4 | < Class java.lang.ArithmeticException > |
上面的异常表表明Q行??范围内,ArithmeticException被捕获。异常表中的“to”下面的结束行号始l比异常捕获的最大行号大1Q上表中Q结束行号ؓ4Q而异常捕L最大行h3。行??的字节码序列对应remainder()Ҏ中的try块?#8220;target”列中Q是??的字节码发生ArithmeticException异常时要跌{到的目标行号?/p>
如果Ҏ执行q程中生了异常QJVM会在异常表中查找匚w行。异常表中的匚w行要W合下面的条Ӟ当前pc寄存器的D在该行的表示范围之内Q[from, to)Q且产生的异常是该行所指定的异常类或其子类。JVM按从上到下的ơ序查找异常表。当扑ֈ了第一个匹配行QJVM把pc寄存器设为新的蟩转行P从此行l往下执行。如果找不到匚w行,JVM弹出当前栈Qƈ重新抛出同一个异常。当JVM弹出当前栈Ӟ它会l止当前Ҏ的执行,q回到调用该Ҏ的上一个方法那里。这Ӟ在上一个方法里Qƈ不会l箋正常的执行过E,而是抛出同样的异常,促JVM重新查找该方法的异常表?/p>
JavaE序员可以用throw语句抛出像remainder()Ҏ的catch子句中的异常QDivideByZeroException。下表列Z抛出异常的字节码Q?/p>
OPCODE
|
OPERAND(S)
|
DESCRIPTION
|
---|---|---|
athrow | (none) | pops Throwable object reference, throws the exception |
athrow指o把栈元素弹出,该元素必LThrowable的子cL其自w的对象引用Q而抛出的异常cd由栈弹出的对象引用所指明?/p>
本文译自Q?a target="_blank" style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #21759b;">How the Java virtual machine handles exceptions
本文Q?/strong>码农合作C?/a> ?a style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #21759b;">JVM中的异常处理Q{载请注明?/p>
什么是JVMQؓ什么要有它Q?/strong>
JVMQJava虚拟机)是一个运行已~译JavaE序的抽象计机。之所以说?#8221;虚拟“的,是因为它Z“真正”的硬件^台和操作pȝQ一般以软g的Ş式实现。所有的JavaE序都ؓJVM而编译。因此,在特定^Cq行已编译JavaE序之前Q该q_的JVM必须先要被实现?/p>
JVM在Java的跨q_Ҏ中Qv着中间人的角色。它在已~译JavaE序与底层硬件^台和操作pȝ之间Q提供一个抽象层。JVM对Java的可UL性非常关键,因ؓQ已~译JavaE序q行在JVM之上Qƈ独立于底层JVM的具体实现?/p>
那么Q是什么导致JVM的短精悍?当被实现成Y件时QJMV很小巧。它被设计成q样Q是Z让它能够适用于尽可能多的地方Q比如机盒Q手机和个h电脑。JVM很精悍,是因为它的野心?#8221;无处不在Q?#8220;是它的战斗口受它惌无处不在Qƈ且JavaE序”一ơ编写,到处q行“的程度说明了它的成功?br />
Java字节?/strong>
JavaE序被编译成一U叫做字节码的东东。JVM执行Java字节码,所以字节码可以被认为是JVM的机器语a。Java~译器读取Java源文Ӟ把它译成Java字节码ƈ保存到类文gQ?class文gQ中。编译器会ؓ源码中的每一个类生成一个类文g?/p>
对JVM来说Q字节码就是指令序列。每条指令包含一个单字节的操作码和零个或多个操作数。操作码告诉JVM要执行的操作。如果JVM需要除操作码之外更多的信息L行一Ҏ作,那么Q需要的信息作ؓ操作敎ͼ紧跟在操作码之后?/p>
每个字节码都有一个助记符Q它可被当作JVM的汇~语a。例如,有个指o会让JVM?压到堆栈中。该指o的助记符是iconst_0Q字节码值是0×60。该指o没有操作数。另一个指令让E序的执行在内存中无条g向前向后跌{。这个指令需要一个操作数Q它是一个指明从当前内存地址开始的2字节无符号偏U量。通过把偏U量加到当前内存地址QJVM可以获得要蟩转的目标内存地址。该指o的助记符是gotoQ它的字节码值是0xa7?/p>
虚拟部分
JVM?#8220;虚拟g”可以分ؓ四个部分Q寄存器l,栈区Q垃圾收集堆和方法区。这些部分很抽象Q就像由它们l成的虚拟机一P但是它们必须在每个JVM的实CQ以某种形式存在?/p>
JVM中地址的是32位(4字节Q的Q因此,JVM可以处理4GBQ??2ơ方Q的内存。栈区,垃圾攉堆和Ҏ区处在这4GB内存中的某个地方Q至于它们的具体内存地址Q这取决于每个特定JVM的实现者?/p>
JVM中一个字QwordQ的长度?2位的。JVM中有数几个原始数据cdQbyteQ?位)QshortQ?6位)QintQ?2位)QlongQ?4位)QfloatQ?2位)QdoubleQ?4位)QcharQ?6位)。除了无W号Unicode字符char之外Q其?U数字类型都是有W号的。这些类型可以方便的映射到JavaE序员可用的数据cd。另一个原始类型是对象句柄Q它是一个指向堆中对象的32位地址?/p>
׃包含字节码,ҎZ字节边界寚w。栈和垃圾收集堆以字Q?2位)边界寚w1?/p>
寄存器:我少我自?/strong>
JVM?个程序计数器QcounterQ和3个管理栈的寄存器QregisterQ。它只有很少的寄存器Q是因ؓJVM字节码指令主要操作栈区。这U面向栈的设计,使得JVM指o集和JVM实现很小巧?/p>
JVM使用E序计数器(也叫pc寄存器)Q跟t当前执行指令的内存位置。另?个寄存器Qoptop寄存器,frame寄存器和vars寄存器)指向当前执行Ҏ栈上不同的部位。执行方法的栈持有特定Ҏ调用的状态(本地变量Q即时计结果等Q?/p>
Ҏ区和E序计数?/strong>
Ҏ区是字节码呆的地斏V程序计数器跟踪执行U程。当前字节码指o执行后,E序计数器会包含下一条执行指令的地址Q一条指令执行之后,JVM把程序计数器讄为紧跟上一条指令的指o地址Q除非上一条指令具体指明一ơ蟩转?/p>
Java栈和相关寄存?/strong>
Java栈用来保存字节码指o的参数和执行l果Q给Ҏ传递参数和q回l果Q保存每个方法调用的状态。方法调用的状态被UCؓ调用栈。var寄存器,frame寄存器和optop寄存器指向当前栈帧的不同部位?/p>
Java栈?个区Q本地变量,执行环境和操作数栈。本地变量区Q包含当前方法调用中使用的所有本地变量。它由vars寄存器指向。执行环境区用来l护栈区本n的操作。它被frame寄存器指向。操作数区用来作为字码指令的工作区。正是在q里Q存攄字节码指令的参数和其q回l果。操作数栈区的顶部被optop寄存器指向?/p>
执行环境通常夹在本地变量和操作数栈中间。当前执行方法的操作数栈L在栈区的最上面Q所以optop寄存器L指向整个Java栈的剙?/p>
垃圾攉?/strong>
堆是Java对象生存的地斏VQ何时候,你用new操作W分配的内存Q都来自堆中。Java语言不允怽直接释放分配的内存。运行时环境会跟t堆上每个对象的引用Q自动释N些不被引用的对象所占据的内存,q个q程被称为垃圾收集?/p>
参?/strong>
本文译自Q?a style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #9f9f9f;">The lean, mean, virtual machine
原创文章Q{载请注明Q?/strong> 转蝲?a style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #9f9f9f;">LetsCoding.cn
本文链接地址: 短小_悍的虚拟机QJVM基本l构和功能介l?/span>
{略模式通过装一l相关算法,为Client提供q行时的灉|性。Client可以在运行时Q选择M法Q而不改变使用法的Context。一些流行的{略模式的例子是写那些用算法的代码Q例如加密算法、压~算法、排序算法。另一斚wQ状态模式允许对象,在不同的状态拥有不同的行ؓ。因为现实世界中的对象通常都是有状态的Q所以它们在不同状态,行ؓ也不一栗例如,VMQ自动售货机Q只在hasCoin状态才l你吐商品;你不投币Q它是不会吐的。现在你可以清楚的看出它们的不同之处了:它们的意图是不同的。状态模式帮助对象管理状态,而策略模式允许Client选择不同的行为?/span>
另一个不那么Ҏ能看出来的区别是Q是谁促使了行ؓ的改变。策略模式中Q是Client提供了不同的{略lContextQ状态模式中Q状态{UȝContext或State自己理。另外,如果你在State中管理状态{U,那么它必L有Context的引用。例如,在VM的例子中QState对象需要调用VM的setState()ҎL变它的状态。另一斚wQStrategy从不持有Context的引用,是Client把所选择的Strategy传递给Context。由于状态模式和{略模式的区别,是流行的Java设计原则c面试题之一Q我们将会在本文探讨在Java中,状态模式和{略模式的异同,q可以加׃对它们的理解?/span>
怼之处
如果你看看状态模式和{略模式的UML图,׃发现它们的结构非常相伹{用State对象改变自己行ؓ的对象被UCؓContext对象Q相似的Q用Strategy对象改变自己行ؓ的对象叫Context对象。记住,Client和Context打交道。在状态模式中QContext把方法调用委托给当前的状态对象,而在{略模式中,Context使用的Strategy对象Q是被当做参C递过来的Q或在Context对象被创建时p提供的?/span>
q是专ؓl典的VM问题而设计的状态模式UMLcd。你可以看出QVM的状态是个接口,它有表示不同状态的具体实现。每一个状态都持有Context的引用,用它来管理由Context触发的行为导致的状态{UR?/span>
q是专ؓ实现排序功能而设计的{略模式UMLcd。因为存在很多排序算法,该模式让Client在排序时选择适当的算法。事实上QJava的集合框架就使用q个模式Q实C用来排序的Collections.sort()Ҏ。不同的是,它不允许Client选择排序法Q而是让它传递Comparator或Comparable接口的实例来指定比较{略?/span>
让我们来看看它们之间更多的相g处:
不同之处
现在我们知道Q状态模式和{略模式的结构是怼的,但它们的意图不同。让我们重温一下它们的主要不同之处Q?/span>
本文译自Q?/span>Difference between State and Strategy Design Pattern in Java
原创文章Q{载请注明Q?/strong> 转蝲?/span>LetsCoding.cn
本文链接地址: Java中,状态模式和{略模式的区?/span>
在上世纪九十q代QJava被设计成了OOP语言Q在当时QOOP是Y件开发中的标杆。远在OOPq没有出现的时候,已经产生了FP语言Q例如Lisp和SchemeQ但是它们的益处Qƈ没有受到学术圈外的h重视。最q,FP的重要性被提升了,因ؓ它非帔R合q发~程和事仉动编E。然而,qƈ不意味着OO不好Q相反,好的{略应该是用OOP和FP。就你对ƈ发编E不感兴,q也很有道理。例如,如果~程语言有一个方便写函数表达式的语法Q集合类库就能拥有强大的API?/p>
Java 8中最主要的增强,是把FP的概忉|度整合进OO。在本文中,我将会展C其基本语法以及Q在不同的上下文中,如何使用它。关键点如下Q?/p>
Z么需要Lambda表达式?
Lambda表达式是一个代码块Q你可以l过它,因此它能在稍后执行,仅一ơ或多次。在介绍语法Q甚x奇怪的名称Q之前,让我们后退一步,看看一直以来,你在Java中,cM的代码块会在什么地方用到?/p>
当你惛_一个独立的U程中执行代码时Q你把代码放到Runnable的runҎ中,像q样Q?/p>
然后Q当你想执行q段代码Ӟ你创Z个Worker实例Q把它提交给U程池,或者简单的开始一个新U程Q?/p>
q里的关键点在于QrunҎ中包含你惛_独立U程中执行的代码?/p>
x用自定义的Comparator排序。如果你想以长度Q而不以默认的字典序对字W串排序Q你可以传递一个Comparator对象lsortҎQ?/p>
sortҎ会持l调用compareҎQ重排ؕ序的元素Q直到数l排序完毕。你lsortҎ传递一个比较元素的代码片段Q这D代码被整合q其余的、你也许不想重新d现的排序逻辑。注意:如果 x{于yQInteger.compare(x, y)q回0Qx < yQ返回负敎ͼx > yQ返回正数。这个staticҎ在Java 7中被加入。你千万不能计算x - y来比较它们的大小Q那LL反的大操作数会导致计溢出的?/p>
作ؓ另外一个g后执行的例子Q考虑一个按钮回调。你新徏一个承Listener接口的类Q把回调动作放进其中Q创建它的一个实例,最后把实例注册到按钮。这U场景司I惯,以至于很多程序员都?#8220;匿名cȝ匿名实例”语法Q?/p>
重要的是handleҎ中的代码QQ何时候按钮被点击Q它׃被执行?/p>
因ؓJava 8把JavaFX作ؓSwing GUI工具包的lQ者,我在例子里是使用JavaFX。这些细节ƈ不重要,因ؓ在所有的UI工具包中Q不是SwingQJavaFXQ还是AndroidQ都是你l按钮一些代码,在按钮被点击的时候执行?/p>
在上面的三个例子中,你看C相同的方式。代码块被传递给某hQ线E池QsortҎ或按钮,它将在稍后被调用?/p>
到现在ؓ止,在Java中传递代码块q不单。你不能只是传递代码块QJava是一个OOP语言Q因此你必须先创Z个属于某个类的实例,而这个类拥有我们需要传递的代码块?/p>
在其他语a中,是可能直接用代码块的。在很长一D|间里QJava的设计者们反对增加q个Ҏ,毕竟QJava的伟大力量在于简单性和一致性。如果一个语aQ包含所有的能够产生量更紧凑代码的Ҏ,它就会变成不可维护的一团糟。经如此,在其他语a中,它们q不仅仅是可以更单的启动一个线E,或者注册一个按钮点ȝ处理E序Q它们中的大量API都更加简单,更加一_更加强大。在Java里,Z本应该能够写出类似的APIQ它们用承特定函数的cȝ实例Q但是,q样的APIQ让人用hq不愉快?/p>
一D|间以来,问题变成Qƈ不是要不要在Java中添加FPQ而是怎么L加。在W合Java的设计出来之前,设计者们׃几年的时间来做试验。在本文下一部分Q你会看到Q你是如何在Java 8中用代码块的?/p>
Lambda表达式的语法
再想想上面排序的例子。我们传递比较字W串长度的代码。我们计:
fisrt和second是什么?它们都是字符ԌJava是强cd语言Q我们也必须指明q一点:
你看C你的W一个Lambda表达式。它只是单的代码块和必须传给它的变量说明?/p>
它的名称Lambda是怎么来的呢?很多q前Q还在计机出现之前Q逻辑学家Alonzo Church惌形式化数学函敎ͼ让它h更有效的可计性。(奇怪的事,Z知道有些函数的存在,却没有h知道怎么计算它们的倹{)他用希腊字母lambdaQ?#955;Q来表示函数参数。如果他懂得Jav APIQ他可能会这样写Q?/p>
那ؓ什么是字母λ呢?是Church用完了其他所有的字母了吗Q实际上Q伟大的《数学原理》?#710;来表C由变量,它激发了Church用一个大写的lambdaQ?#955;Q表C参数的灉|。但是最l,他还是用了写版本。自此以后,一个拥有参数的表达式就被称Z“Lambda表达?#8221;?/p>
本文译自Q?a style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #9f9f9f;">Lambda Expressions in Java 8
原创文章Q{载请注明Q?/strong> 转蝲?a style="margin: 0px; padding: 0px; border: 0px; vertical-align: baseline; outline: none; color: #9f9f9f;">LetsCoding.cn
本文链接地址: Java 8QLambda表达式(一Q?/a>