SCJP复习指南2

目标一 ifswitch语句

ifswitch编写代码,识别这些语句的合法参数类型

If/else语句

javaIf/else结构和你所了解的其他语言一样,switch/case语句有一些自己的特点

if/else的语法是

if(boolean condition){

//the boolean was true so do this

}else {

//do something else

}

与在Visual Basic语句中不同,Java中不存在"then"关键字

花括号在Java中是一个常用的复合语句的指示器,它可以使你把多行代码作为一些判断语句的一个结果来执行。这可以被看作一个程序块。else部分常常是可选的。你可以像以下这样链接多个if/else语句(但是在链接了几个之后你就要考虑使用case结构来代替了)

int i=1;

if(i==1){

//some code

} else if (i==2){

//some code

} else{

//some code

}

Javaif语句的一个特性是必须带一个boolean类型的值。你不能像使用C/C++习惯的那样使用任何非零的数值来表示true,而用零来表示false

因此,在Java中以下语句将不会被编译

int k =-1;

if(k){//Will not compile!

System.out.println("do something");

}

因为你必须明确的使k的判断语句返回一个boolean类型的值,就像下面的例子

if(k == -1){

System.out.println("do something"); //Compiles OK!

}

当在C/C++中时,你可以去掉花括号,如下

boolean k=true;

if(k)

System.out.println("do something");

这有时候被认为是不好的设计风格,因为如果你稍后要修改代码来包含更多语句,他们就会在条件语句块外,像这样

if(k)

System.out.println("do something");

System.out.println("also do this");


第二个输出语句将总会被执行

switch语句

Peter van der Lindens 对于switch语句的评价概括起来就像他所说的“毁灭于switch语句”因此,这是一个你必须花费更多的精力关注的问题。switch语句的参数必须是一个bytecharshortint类型的变量。你也许会遇到考试题中用float或者longswitch语句的参数。有一个非常普遍的问题似乎就是,关于在执行switch语句的过程中使用break语句。这里有一个这类问题的例子。

int k=10;

switch(k){

case 10:

System.out.println("ten");

case 20:

System.out.println("twenty");

}

常识判断,执行case语句后面的指令,然后碰到另一个case语句,编译器就应该结束执行switch语句。但是,就像程序设计者所熟知的,case语句只在碰到break语句的时候才终止执行。结果,在上面例子中,tentwenty都将被输出。可以作为一个问题提出来的另一个小的特性就是使用default语句。

注意:default语句不是必须在case语句的结尾处出现

按照惯例default语句是放在case选项的结尾处,所以通常代码写成如下形式

int k=10;

switch(k){

case 10:

System.out.println("ten");

break;

case 20:

System.out.println("twenty");

break;

default:

System.out.println("This is the default output");

}

这种方法反映大多数人的思维方式。当你尝试其他可能情况时,会执行default输出。但是,如果不是被要求的话,把defalt语句写在switch语句的顶部,在语法上也是正确的。

int k=10;

switch(k){

default: //Put the default at the bottom, not here

System.out.println("This is the default output");

break;

case 10:

System.out.println("ten");

break;

case 20:

System.out.println("twenty");

break;

}


ifswitch语句的合法参数

正如先前所提到的,if语句只能用boolean类型参数,而switch语句只能用bytecharshort或者int类型作参数。


三项 ?操作符

一些程序员主张三项操作符很有用。我不这么认为。在目标中并没有特别提到它,所以如果在考试中出现的话请告诉我。


其他流程控制语句

虽然公布的目标只提到了if/elsecase语句,考试中也许会涉及do/whilewhile loop语句。

练习

习题1

创建一个文件含有一个公共类叫IfElse。创建一个方法叫go,它接收main方法的字符串数组参数作为它的参数。在这个方法中创建了一个if/else程序块,用来查看来自数组的第一个元素,用字符串的equals方法来判断输出。如果为"true"则打印"ok",如果为"false"则打印"Not ok",如果是truefalse以外的字符串则打印"Invalid command parameter",用一个if/else if/else语句这样的次序进行设计。


习题 2

修改这个IfElse类,使if语句可以检查传到go方法的字符串数组是否是零长度串,使用数组length域来检查。如果长度为零则输出"No parameter supplied",把现有的if/else if/else块放在这个练习的else中,使程序能实现原版本的功能。



答案 1

public class IfElse{

public static void main(String argv[]){

IfElse ie = new IfElse();

ie.go(argv);

}

public void go(String[] sa){

String s = sa[0];

if(s.equals("true")){

System.out.println("OK");

}else if(s.equals("false")){

System.out.println("Not OK");

}else{

System.out.println("Invalid command parameter");

}

}

}


答案 2

public class IfElse{

public static void main(String argv[]){


IfElse ie = new IfElse();

ie.go(argv);

}

public void go(String[] sa){

if(sa.length ==0){

System.out.println("No parameter supplied");

}else{

String s = sa[0];

if(s.equals("true")){

System.out.println("OK");

}else if(s.equals("false")){

System.out.println("Not OK");

}else{

System.out.println("Invalid command parameter");

}

}

}

}


问题

问题1) 编译运行下列代码时会发生什么情况?

public class MyIf{

boolean b;

public static void main(String argv[]){

MyIf mi = new MyIf();

}

MyIf(){

if(b){

System.out.println("The value of b was true");

}

else{

System.out.println("The value of b was false");

}

}

}

1) Compile time error variable b was not initialised

2) Compile time error the parameter to the if operator must evaluate to a boolean

3) Compile time error, cannot simultaneously create and assign value for boolean value

4) Compilation and run with output of false


问题2) 编译运行下列代码时会发生什么情况?

public class MyIf{

public static void main(String argv[]){

MyIf mi = new MyIf();

}

MyIf(){

boolean b = false;

if(b=false){

System.out.println("The value of b is"+b);

}

}

}

1) Run time error, a boolean cannot be appended using the + operator

2) Compile time error the parameter to the if operator must evaluate to a boolean

3) Compile time error, cannot simultaneously create and assign value for boolean value

4) Compilation and run with no output

问题3 ) 编译运行下列代码时会发生什么情况?

public class MySwitch{

public static void main(String argv[]){

MySwitch ms= new MySwitch();

ms.amethod();

}

public void amethod(){

char k=10;

switch(k){

default:

System.out.println("This is the default output");

break;

case 10:

System.out.println("ten");

break;

case 20:

System.out.println("twenty");

break;

}

}

}

1) None of these options

2) Compile time error target of switch must be an integral type

3) Compile and run with output "This is the default output"

4) Compile and run with output "ten"


问题4) 编译运行下列代码时会发生什么情况?

public class MySwitch{

public static void main(String argv[]){

MySwitch ms= new MySwitch();

ms.amethod();

}

public void amethod(){

int k=10;

switch(k){

default: //Put the default at the bottom, not here

System.out.println("This is the default output");

break;

case 10:

System.out.println("ten");

case 20:

System.out.println("twenty");

break;

}

}

}

1) None of these options

2) Compile time error target of switch must be an integral type

3) Compile and run with output "This is the default output"

4) Compile and run with output "ten"

问题5) 下面哪个是不能用于switch语句的参数?

1) byte b=1;

2) int i=1;

3) boolean b=false;

4) char c='c';

答案

答案 1

4) Compilation and run with output of false

因为boolean b在类级中被创建,它不需明确初始化,而且它有默认的booleanfalseif语句判断一个boolean值,所以b符合这个要求。


答案 2

4) Compilation and run with no output

因为bboolean类型,if语句不会产生错误。如果b是任何其他的数据类型,在你试图赋值而不是比较的时候错误就产生了。下列表达

if(b=false)

通常是一个程序员的错误。程序员大多要表现

if (b==false)

如果b的类型是boolea以外的任意类型,会导致编译期错误。if表达式的要求是必须返回一个boolean类型,因为

(b=false )

返回一个boolean类型,所以被接受(如果无用处)。


答案 3

4) Compile and run with output "ten"


答案 4

1) None of these options

因为下句后缺少break语句

case 10;

实际输出结果会是"ten"接着是"twenty"


答案 5

1) byte b=1;

2) int i=1;

4) char c='c';

switch语句可以使用bytecharint作参数。

目标二 循环,breakcontinue


用循环格式编写代码,使用带标签和不带标签的breakcontinue语句,声明循环计数器的值在循环执行中或循环结束时


for语句

最常用的循环方法就是应用for语句。对于for语句在其他的编程语言中有非常相似的结构。比如C/C++perl就有for结构。很多程序员在循环中使用for结构,因为其简洁,自含,容易理解而且不容易混乱。类似C++而与C语言不同,循环控制变量可以在for语句中定义和初始化。如下

public class MyLoop{

public static void main(String argv[]){

MyLoop ml = new MyLoop();

ml.amethod();

}

public void amethod(){

for(int K=0;K<5l;K++){

System.out.println("Outer "+K);

for(int L=0;L<5;L++)

{System.out.println("Inner "+L);}

}

}

}


内循环代码在每次外循环执行时会循环执行五次。所以输出为:

Outer 0;

Inner 0

Inner 1

Inner 2

Inner 3

inner 4

Outer 1;

Inner 0

Inner 2

for语句和Visual Basicfor/next循环一样。你可以认为它的语法是

for(initialization; conditional expression;increment)

其中条件表达式必须是boolean判断,就像if语句的简单形式。在上例的代码中,for语句紧跟着是花括号中的程序块。类似if语句,当不需要使用程序块时,你可以使用下面这样的简单形式

for(int i=0;i<5;i++)

System.out.println(i);


在任何版本中你都不能用分号来结束一个for行,如果你这么做,for循环就会原地打转直到条件满足,然后就会以“直线”的方式执行下面的代码。在此例中你不是必须在for循环中定义变量,但是如果在循环中定义变量,当跳出循环时变量也就跳出了它的作用域。按照变量作用域尽可能小的说法,这可以看作是一个优点。


for中的块为空在语法中也是正确的,这样循环会永远进行下去

for(;;){

System.out.println("forever");

}


但是用while(true)的形式可能会更加简洁

while(true){

System.out.println("true");

}

while循环和do循环,意料之中

whiledo循环的运行就像你想象的一样,和在其他语言中相同。

因此,while会依照判断执行零到多次,而do会执行一到多次。while循环的语法是:

while(condition){

bodyOfLoop;

}

if语句一样,条件是一个boolean类型的判断。同样,你不能像C/C++习惯的那样用零来代表false,而用任意其他值来代表true

所以,你可能会像下面那样创建一个while循环

while(i<4){

i++;

System.out.println("Loop value is :"i);

}

注意,如果变量i4,或者比4大,当你到达while语句时,将没有输出。相反,do循环总是会执行一次。所以,不管进入循环时变量i的值是什么,以下代码总是会得到至少一个输出。

goto语句,科学还是迷信?

Java的设计者决定同意写过著名文章"Goto 有害"的编程领袖Edsger Dijkstra的观点。因为不加选择的使用goto语句会导致“意大利面条似的代码”难以维护,不可使用,而且这被认为是不好的编程风格。“意大利面条似的代码”是指不容易表述逻辑开始和结束的代码。goto语句有时会被说成“无条件跳转”,也就是可能会写这样的代码,不进行判断就从程序的一部分跳转到另外一处。这在某些情况下是有用的,即Javabreakcontinue关键字提供了有标签和无标签两个版本。

public class Br{

public static void main(String argv[]){

Br b = new Br();

b.amethod();

}

public void amethod(){

for(int i=0;i <3;i ++){

System.out.println("i"+i+"/n");

outer://<==Point of this example

if(i>2){

break outer;//<==Point of this example

}//End of if


for(int j=0; j <4 && i<3; j++){

System.out.println("j"+j);

}//End of for

}//End of for

}//end of Br method

}

然后,你需要挑出代码中哪个是要输出的字母组合。顺便说一下,"/n"是输出一个空白行。

跳转到标签

在有些条件下从内循环跳到外循环常被描述,你可以使用带标签的breakcontinue语句来实现它。一个标签是一个简单的非关键字,后面跟一个冒号。通过在breakcontinue后使用标签,你的代码可以跳转到此标签处。这是便捷的实现部分条件循环的方法。你当然可以用if语句,但是一个break语句更方便。按照Elliotte Rusty Harold,一个著名的Java作者所说,“在整个Java1.0.1源代码中,只用了七个continue语句写java包。”这意味着在实际编程中你可能不会得到充分的练习,所以为了考试你要花费更大的精力来学好它。考试题的编写者好像热爱设计费解的网状的带有breakcontinue语句的循环,你可能永远不会遇到有好的设计的代码。

关键概念

break语句完全放弃执行当前循环,continue语句只放弃整个循环中当前本次循环

做下面的例子

public class LabLoop{

public static void main(String argv[]){

LabLoop ml = new LabLoop();

ml.amethod();

}

public void amethod(){

outer:

for(int i=0;i<2;i++){

for(int j=0;j<3;j++){

if(j>1)

//Try this with break instead of continue

continue outer;

System.out.println("i "+ i + " j "+j);

}

}//End of outer for

System.out.println("Continuing");

}

}


这个版本有以下输出

i 0 j 0

i 0 j 1

i 1 j 0

i 1 j 1

Continuing

如果你用break替换continuei计数器会在零处停止,因为外循环会被放弃,而不会简单的进入下一个递增。

问题

问题1) 编译运行一个方法中的下列代码时会发生什么情况?

for(int i=0;i<5;){

System.out.println(i);

i++;

continue;

}

 


1) Compile time error, malformed for statement

2) Compile time error continue within for loop

3) runtime error continue statement not reached

4) compile and run with output 0 to 4


问题2)编译运行下列代码时会发生什么情况?

public class LabLoop{

public static void main(String argv[]){

LabLoop ml = new LabLoop();

ml.amethod();

mainmethod:

System.out.println("Continuing");

}

public void amethod(){

outer:

for(int i=0;i<2;i++){

for(int j=0;j<3;j++){

if(j>1)

break mainmethod;

System.out.println("i "+ i + " j "+j);

}

}//End of outer for

}

}


1)

i 0 j 0

i 0 j 1

Continuing

2)

i 0 j 0

i 0 j 1

i 1 j 0

i 1 j 1

Continuing

3)

Compile time error

4)

i 0 j 0

i 0 j 1

i 1 j 0

i 1 j 1

i 2 j 1

Continuing


问题3)编译运行下列代码时会发生什么情况?

public void amethod(){

outer:

for(int i=0;i<2;i++){

for(int j=0;j<2;j++){

System.out.println("i="+i + " j= "+j);

if(i >0)

break outer;

}

}

System.out.println("Continuing with i set to ="+i);

}

1) Compile time error

2)

i=0 j= 0

i=0 j= 1

i=1 j= 0

3)

i=0 j= 0

i=0 j= 1

i=1 j= 0

i=2 j= 0

4)

i=0 j= 0

i=0 j= 1

问题4)编译运行下列代码时会发生什么情况?

int i=0;

while(i>0){

System.out.println("Value of i: "+i);


}

do{

System.out.println(i);

} while (i <2);


}

1)

Value of i: 0

followed by

0

1

2

2)

0

1

2

3)

Value of i: 0

Followed by continuous output of 0

4) Continuous output of 0


问题5) 编译运行下列代码时会发生什么情况?

public class Anova{

public static void main(String argv[]){

Anova an = new Anova();

an.go();

}

public void go(){

int z=0;

for(int i=0;i<10; i++,z++){

System.out.println(z);

}

for(;;){

System.out.println("go");

}

}

}

1) Compile time error, the first for statement is malformed

2) Compile time error, the second for statement is malformed

3) Output of 0 to 9 followed by a single output of "go"

4) Output of 0 to 9 followed by constant output of "go"


问题6)下列代码的输出结果是什么?

public class MyFor{

public static void main(String argv[]){

int i;

int j;

outer:

for (i=1;i <3;i++)

inner:

for(j=1; j<3; j++) {

if (j==2)

continue outer;

System.out.println("Value for i=" + i + " Value for j=" +j);

}

}

}

1) Value for i=1 value for j=1

2) Value for i=2 value for j=1

3) Value for i=2 value for j=2

4) Value for i=3 value for j=1


答案

答案 1

4) compile and run with output 0 to 4

这是一个很奇怪但是完全正确的语句


答案 2

3) Compile time error

你不能武断的跳入另一个方法,在goto语句中会带来很多有害的结果。


答案 3

1) Compile time error

这实际上不是关于breakcontinue的问题。这段代码不会被编译,因为变量对for循环外部来说永远是不可见的。所以最后的System.out.println语句会引起编译时错误。


答案 4

1) Continuous output of 0

没有值被增加,而且如果第一次判断不为真时while循环将不会执行。


答案 5

4) Output of 0 to 9 followed by constant output of "go"

第一个for循环结构不常用但是完全正确。


答案六

1) Value for i=1 value for j=1

2) Value for i=2 value for j=1

目标三 try/catch和方法重写

编写代码合理使用异常和异常处理机制(try catch finally,定义和重写方法抛出异常.

一个异常情况是当程序进入一个不是很正常的状态.异常捕获有时是指错误捕获.一个典型的异常例子是当程序试图打开一个不存在的文件时或者你试图访问一个数组中不存在的元素时.

trycatch语句是构建Java异常处理的一部份.不论C/C++还是Visua Basic都没有直接对应Java异常处理的结构.C++支持异常,但是是可选的,Visial Basic支持On Error/Goto 错误捕获,这带有早期不灵活的BASIC编程时代的味道。

Java异常是Java语言的一个结构。例如如果你要执行I/O操作,你必须把它放在错误处理中。你当然可以不把它放在处理中,这毫无作用。下面是一个小代码片断,我用Borland/Inprise JBuilder临时停止控制台输出,等待按任意键继续

public class Try{

import java.io.*;

public static void main(String argv[]){

Try t = new Try();

t.go();

}//End of main

public void go(){

try{

InputStreamReader isr = new InputStreamReader(System.in);

BufferedReader br = new BufferedReader(isr);

br.readLine();

} catch(Exception e){

/*Not doing anything when exception occurs*/

} //End of try

System.out.println("Continuing");

}//End of go

}

在这个例子中,错误出现时没有任何处理,但是程序员一定知道错误有可能发生。如果你移去trycatch字句,代码将完全不会被编译。编译器知道I/O方法会引发异常而且需要异常处理代码。

Visal BasicC/C++比较

Visal BasicC/C++允许抛出混合“快且脏”的程序,假装没有错误发生过,Java比它们严格些。记得DOS的最初版本被他的创作者叫做QDOS,因为是快且脏的DOS,看看我们已经在这样的环境下生活了多久。当你开始把快且脏的程序放到try/catch块当中,也就开始了真正的错误跟踪。这不是完全的束缚和编程律条,这只是劝说你“做正确的事”。

方法重写,抛出异常

在子类中一个重写的方法可能只抛出父类中声明过的异常或者异常的子类。这只适用于方法重写而不适用于方法重载。所以如果如果一个方法有完全相同的名称和参数,它只能抛出父类中声明过的异常或者异常的子类。但是它抛出很少或者不抛出异常。所以下面的例子将不被编译


import java.io.*;

class Base{

public static void amethod()throws FileNotFoundException{}

}


public class ExcepDemo extends Base{

//Will not compile, exception not in base version of method

public static void amethod()throws IOException{}

}

如果在父类中有抛出IOException异常的方法,在子类中的方法抛出FileNotFoundException,代码将编译通过。再次,记住只适用于方法重写,在方法重载中没有类似规定。一个在子类中重写的方法可能会抛出异常。

throw子句

我们在代码中需要包含可能抛出异常的try/catch块的一个原因就是,你的代码可以开始展现出什么可能发生,而不是什么应该发生。你可以通过使用throws字句作为方法声明的一部分来把异常放到堆栈中。这就有效的说明“当一个错误发生时,这个方法抛出这个异常,并且这个异常必须被调用它的方法捕获”。


这有一个使用throw子句的例子

import java.io.*;

public class Throws{

public static void main(String argv[]){

Throws t = new Throws();

try{

t.amethod();

}catch (IOException ioe){}

}

public void amethod() throws IOException{

FileInputStream fis = new FileInputStream("Throws.java");

}

}

问题

问题 1) 编译运行以下代码会发生什么情况?

import java.io.*;

class Base{

public static void amethod()throws FileNotFoundException{}

}

public class ExcepDemo extends Base{

public static void main(String argv[]){

ExcepDemo e = new ExcepDemo();

}

public static void amethod(){}

protected ExcepDemo(){

try{

DataInputStream din = new DataInputStream(System.in);

System.out.println("Pausing");

din.readChar();

System.out.println("Continuing");

this.amethod();

}catch(IOException ioe) {}

}

}

1) Compile time error caused by protected constructor

2) Compile time error caused by amethod not declaring Exception

3) Runtime error caused by amethod not declaring Exception

4) Compile and run with output of "Pausing" and "Continuing" after a key is hit


问题2) 编译运行以下代码会发生什么情况?

import java.io.*;

class Base{

public static void amethod()throws FileNotFoundException{}

}

public class ExcepDemo extends Base{

public static void main(String argv[]){

ExcepDemo e = new ExcepDemo();

}

public static void amethod(int i)throws IOException{}

private ExcepDemo(){

try{

DataInputStream din = new DataInputStream(System.in);

System.out.println("Pausing");

din.readChar();

System.out.println("Continuing");

this.amethod();

}catch(IOException ioe) {}

}

}


1) Compile error caused by private constructor

2) Compile error caused by amethod declaring Exception not in base version

3) Runtime error caused by amethod declaring Exception not in base version

4) Compile and run with output of "Pausing" and "Continuing" after a key is hit


问题3) 编译运行以下代码会发生什么情况?

import java.io.*;

class Base{

public static void amethod()throws FileNotFoundException{}

}


public class ExcepDemo extends Base{

public static void main(String argv[]){

ExcepDemo e = new ExcepDemo();

}


public static void amethod(int i)throws IOException{}

private boolean ExcepDemo(){

try{

DataInputStream din = new DataInputStream(System.in);

System.out.println("Pausing");

din.readChar();

System.out.println("Continuing");

this.amethod();

return true;

}catch(IOException ioe) {}

finally{

System.out.println("finally");

}

return false;

}


}


1) Compilation and run with no output.

2) Compilation and run with output of "Pausing", "Continuing" and "finally"

3) Runtime error caused by amethod declaring Exception not in base version

4) Compile and run with output of "Pausing" and "Continuing" after a key is hit


问题4) 以下哪个要求程序员添加外部的try/catch异常处理。

1)Traversing each member of an array

2) Attempting to open a file

3) Attempting to open a network socket

4) Accessing a method in other class


问题5) 编译运行以下代码会发生什么情况?

import java.io.*;

class granary{

public void canal() throws IOException{

System.out.println("canal");

}

}


public class mmill extends granary{

public static void main(String argv[]){

System.out.println("mmill");

}

public void canal(int i) throws Exception{

System.out.println("mmill.canal");

}

public void canal(long i) {

System.out.print("i");

}

}

1) Compile time error

2) Runtime errors

3) Compile error, mmill version of canal throws Exception not in granary version

4) Compilation and run with output of mmill

答案

问题1) 答案

4) Compile and run with output of "Pausing" and "Continuing" after a key is hit

在子类中的重写方法不能抛出在基类中没有抛出的异常。在这个例子中的方法amethod没有抛出异常,所以编译不会出现问题。构造器不能是protect类型的。


问题2) 答案

4) Compile and run with output of "Pausing" and "Continuing" after a key is hit

在这个版本中amethod被重写了,没有限制抛出或不抛出异常。


问题3) 答案

1) Compilation and run with no output.

好的,我有点跑题了,注意构造器有一个返回值。这把它变成了一个普通方法,而且当没有实例被创建时它将不会被调用。


问题4) 答案

2) Attempting to open a file

3) Atempting to open a network socket

通常来说,所有的I/O操作都需要外在的使用try/catch块的异常处理。JDK1.4考试不明确的覆盖I/O,但是也许会提到错误处理的内容。


问题5) 答案

4) Compilation and run with output of mmill

什么样的异常可以被抛出的限制只是应用于被重写的方法,不用于被重载的方法。因为canal方法在mmill版本中被重载(也就是它带有了不同的参数类型),所以不会有编译或运行错误。

目标四 什么情况下产生异常

识别发生在代码片断指定位置的异常产生的结果。注意:异常必须是运行时异常,一个被检查的异常或者一个错误(代码可能包括try,catch或者finally子句,在任何可能的组合中)

目标注释

这个目标要求你理解可控的和不可控异常(一种你要写代码捕获,另一种不用),理解finally子句如何工作。

检查和非检查异常

虽然Java强调你把捕获异常代码插入到他们可能发生的地方像I/O操作等,这样比较方便,但是如果你必须把这些代码插入到程序员应该控制程序状态的地方,就不方便了。这种情况的一个例子就是遍历数组的每一个元素。Java中一个优美的地方就是它不需要程序员的介入而明确的报告这种异常类型的方式。这种自动异常处理是由把异常分为可控和不可控异常实现的。像内存耗尽或者访问到数组末尾这种情况会自动识别,而试图打开不存在的文件就需要明确的try/catch异常捕获。

默认的非检查信息

非检查异常出现的一个默认结果就是一个信息会被发送到控制台。例如下面代码

public class GetArg{

public static void main(String argv[]){

System.out.println(argv[0]);

}

如果编译运行代码而不输入命令行参数,你会在控制台得到一个错误信息

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException

at GetArg.main(GetArg.java:3)


同时这种情况是可以用于学习目的编写的程序的,在实际程序中,用户大概不会访问控制台,也不会理解这样的信息。最好编写代码来检查可能产生非运行异常的数值。所以代码可以修改成

public class GetArg{

public static void main(String argv[]){

if(argv.length ==0){

System.out.println("Usage: GetArg param");

}else{

System.out.println(argv[0]);

}

}

}

检查异常

要求程序员编写代码处理检查异常。如果没有代码处理检查异常可能会出现编译不通过。就像前面的代码带有try/catch块结构,但是写一个空catch块,不对产生的异常进行任何处理也是可以的。当然这通常不是好的设计,当你碰到要在异常时编写代码的问题时,你可能还是要在这些代码中做些有用的事。在catch块常做的两件事是产生出错信息和打印错误跟踪。异常系统提供一个非常方便的方法,通过getMessage方法产生常见的有意义的错误信息。

看看以下代码

import java.io.*;

public class FileOut{

public static void main(String argv[]){

try{

FileReader fr = new FileReader("FileOut.txt");

}catch(Exception e){

System.out.println(e.getMessage());

}

}

}

如果你在一个没有FileOut.txt文件的目录中编译运行这段代码,你会得到一个错误信息

FileOut.txt (No such file or directory)

finally子句

在考试中你可能会被问到,在什么情况try/catch子句后的finally方法会被执行。简单回答就是finally子句总是会被执行,甚至当你觉得它可能不会被执行时。所以说,try/catch/finally语句的执行顺序是你应该注意确认它在什么情况下是怎么执行的。

关键概念

不论在try/catch部分是不是有返回,try/catch块的finally子句总会执行。

少数情况下当有如下调用时,finally子句不会被执行

System.exit(0);

考试往往不会在这个规则上考你。

考试更可能给你一个包括return语句的例子,来误导你认为代码不执行finally语句就返回。不要被误导,finally子句总是会执行。

try/catch子句在它结构正确时一定会捕获错误。所以你不能试图在捕获特殊的IOException之前写一个捕获一般异常的catch,来捕获所有的Exception.

下面代码将不会通过编译

try{

DataInputStream dis = new DataInputStream(System.in);

dis.read();

}catch (Exception ioe) {}

catch (IOException e) {//Compile time error cause}

finally{}

这段代码会在编译时发出错误信息,更特殊的IOException将不会被达到。


问题

问题1) 下列那个需要建立try/catch块或者重新抛出异常?

1) Opening and reading through a file

2) Accessing each element of an array of int values

3) Accessing each element of an array of Objectts

4) Calling a method defined with a throws clause


问题2) 编译运行下列代码会发生什么情况?

import java.io.*;

class Base{

public static void amethod()throws FileNotFoundException{}

}

public class ExcepDemo extends Base{

public static void main(String argv[]){

ExcepDemo e = new ExcepDemo();

}

public boolean amethod(int i){

try{

DataInputStream din = new DataInputStream(System.in);

System.out.println("Pausing");

din.readChar();

System.out.println("Continuing");

this.amethod();

return true;

}catch(IOException ioe) {}

finally{

System.out.println("Doing finally");

}

return false;

}

ExcepDemo(){

amethod(99);

}

}

1) Compile time error amethod does not throw FileNotFoundException

2) Compile, run and output of Pausing and Continuing

3) Compile, run and output of Pausing, Continuing, Doing Finally

4) Compile time error finally clause never reached


答案 1)

打开读一个文件

4) Calling a method defined with a throws clather Resources on this topic

数组元素的类型对错误处理没有任何影响。通过定义throws子句在方法中的使用,可能会抛出一个异常,这个异常类型会被使用它的代码捕获或者再抛出。


答案 2)

3) Compile, run and output of Pausing, Continuing, Doing Finally

finally子句总是会运行。

目标五、六 使用断言

编写正确使用断言的代码,并且区分适当和不适当的断言使用。识别关于断言机制的正确论述。

目标的评论

断言是随着2002年中期JDK1.4考试版本的发布而添加到Sun Certified Java Programmers考试目标中的。由于它们是考试的新特性,你一定会在考试碰到这类题目。断言是其他面向对象语言的特性,在它被加入到Java中的时候一度面临很大的压力。


断言是因JDK1.4的发布而添加到Java语言中的,所以在写著此书的时候没有太多关于这个主题的笔墨。

断言为何存在

C++中可以使用断言,但CVisual Basic(或者据我了解还有Pascal)中没有,所以很多人都没有使用过。如果事情到了很多C++程序都没有使用过它们的地步。断言是一个相当简单的概念,你只需要写一个始终都是true的语句,但是在形式上它们可以从最终的编译版本中去除,所以不会导致运行时开销。使用JDK1.4之前模拟断言功能的结构来书写代码是完全可以的,但是以这种方式做起来会很困难,它们在运行时会被关闭。


C/C++语言中,断言可以使用语言预处理器来创建,在新闻组中有大量关于Java语言是否应该有预处理系统的讨论。观点的分歧在于有人认为预处理宏是恶魔的东西,它带来了创造过于复杂结构的机会,有人认为它会给语言带来不可思议的力量。无论是哪种方式,Java设计者倾向于实现预处理机制,并且在JDK1.4中包含了断言。

如何使用断言

何地以及如何使用断言大概需要一种类似于何地及如何使用注释的判断方式。一些程序员从来不使用注释,这种风格的程序被广泛认为是糟糕的程序。因为大约80%的代码是由其他人而不是原作者维护的,所以注释是很重要的。断言可以被认为是对注释的扩展,因为它相当于告诉人们阅读一段始终都是true的代码的注释。使用断言,而不是通过注释指定一个语句始终为true,你可以使用断言来声明它始终是true的。

然后,如果你运行含有断言的代码,不必像使用注释那样依赖于仔细代码,但运行代码的时候将会检查你的断言是否为true,如果它们不是断言错误就会被抛弃。

正如名字暗示的那样,断言被用来断定某些东西应该始终都是true的。当程序正常运行时,断言就失效了,不会带来性能开销。当程序员在查找问题时,断言可以被激活,如果任何断言语句不为true,断言异常就会被抛出。断言是JDK1.4中的关键部分,而且不需要在源文件中加入额外的import语句。但是,因为过去程序员已经使用了单词assert来创建他们自己的断言,编译过程需要一个命令行的参数来告诉编译器使用JDK1.4中的断言。这需要如下的形式

javac –source1.4 Myprog.java

如果你以如下形式正常运行程序

java Myprog

断言失效了,不会抛出断言异常。如果你后来需要查明一个问题,并确定所有的断言条目都是true,你可以像下面那样激活断言来运行程序。

java –enableassertions Myprog

你应该断言什么为true

断言可以被用在任何你认为应该始终为true的地方。例如,一个人的年纪大于0应该始终是true的。如果一个人的年纪小于0,你的程序或输出就会有很大的问题。另一个例子,如果你正在登记人们死亡的日期,你的程序(或你的道德)可能会有死亡日期是否可以在未来的问题,所以,你可以对未来的死亡日期进行断言。

例如,如果你正面临case语句或一组if/else语句,你可能相信代码始终会在到达最后的测试之前退出。想象一下,如果你有一个处理媒体类型的程序。你的程序希望能够处理jpgmpgavigif文件。你设置了一个根据文件类型分支的case语句。因为你相信文件类型将始终是这些类型之一,如果你到达了case语句的末尾而没有分支了,这将是一个很明显的问题,你可以在缺省选项处放置一个断言语句。

你应该在哪里使用断言?

断言不可以用来强制程序的公共接口。一个最常见的程序公共接口是它的命令行参数。因此,传统上程序员会通过查看从命令行传入的String数组args中的值来检查传递给Java程序的命令行。典型地,如果数组没有包含程序期望类型的值,程序将退出并打印出指明正确命令行格式的消息。断言机制的引入不会改变这些。使用断言来检查程序的命令行参数是不合适的,因为断言永远都不会被激活。

使用断言来检查传递给公共方法的参数是不合 适的。因为你的公共方法可能会被别人写的程序使用,你无法确定他们是否激活了断言,因此正常运行程序会出错。但是,使用断言来检查传递给私有方法的参数是 恰当的,因为这些方法通常是由能够访问源代码的人来调用的。同样的假定可以作用于受保护或同一个包中的受保护方法。如你所见,这些仅仅是指导方针,但是考 试可能会询问基于这些指导方针的问题。

断言语法

断言语句由两种格式

简单的

assert somebooleantest

assert somebooleantest : someinformativemethod

在第一个简单版本中,断言测试某物是true的,如果它不是断言错误则抛弃。例如,如果你正在测试一个人的年龄是否大于0,你可能创建如下形式的断言

assert (iAge>);

复杂的版本可能是如下形式

assert (iAge) : “age must be greater than zero”;

这个例子很简单,因为右手边的表达式只是一个简单的字符串,但是这可以是任何有返回值的函数调用,例如一个除返回值为void以外的方法。


课后测试题

问题1)下面哪些论断是正确?

  1. Using assetions requires importing the java.util.assert package

  2. Assertions should be used to check the parameters of public methods

  3. Assertions can be used as a substitute for the switch/case construct

  4. An assertion that a persons date of death > date of birth is appropriate

问题 2)如果如下代码没有显式激活断言而成功的编译并运行,会发生什么呢?

class Language {

public static final int java = 1;

public static final int pascal = 2;

public static final int csharp = 3;

}

public class Mgos {

static int lang = 0;

public static void main (String argv []) {

switch (lang) {

case Language.java:

System.out.println (“java”);

break;

case Language.pascal:

System.out.println (“pascal”);

break;

case Language.csharp:

System.out.println (“csharp”);

break;

default:

assert false: lang;

}

}

}

  1. An unmatched parameter exception will be thrown

  2. An assert exception will be thrown

  3. The program will run with not output

  4. Output of “csharp”

问题 3)下面哪些是值得使用断言结构的候选?

  1. An input form has a field for a person’s age. If the person entering the date of birth and the date of death enters an age of death that is before the age of birth the assertion mechanism is used to cause a dialog warning box to be shown and the data will not enter the system.

  2. A Text Editing program has a file save mechanism. The assert mechanism is used to check if the drive that is being save to really exists. If the drive does not exist an assertion will be thrown generating a warning to the program operator.

  3. A program is being created for food preparation that involves cooking meat. Code is included so that if the value of a temperature variable reading appears to be negative an assert exception is thrown.

  4. A school attendance system is being created. In the system is code that will throw an assert exception if a child’s age is calculated to be less than zero.

问题 4)如果激活JDK1.4的断言,编译如下代码会发生什么?

public class Bbridge {

int iRunningTotal = 0;

public static void main (String argv []) {

Bbridge bb = new Bbrige ();

bb.go (argv [0]);

}

public void go (String s) {

int i = Integer.parseInt (s);

setRunningTotal (i);

assert (iRunningTotal > 0) : getRunningTotal ();

}

public String getRunningTotal () {

return “Value of iRunningTotal “ + iRunningTotal;

}

public int setRunningTotal (int i) {

iRunningTotal += i;

return iRunningTotal;

}

}

  1. Compile time error, getRunningTotal does not return a Boolean

  2. Compile time error malformed assert statement

  3. Compilation and no output given a command parameter of 1

  4. Compilation and assert error given a command parameter of 0

问题 5)下面的论断哪些是正确的?

  1. The assert system introduces no backward compatibility issues

  2. The assert system should be used to enforce command line usage

  3. Asserts should be used to check for conditions that should never happen

  4. Asserts can be used to enforce argument constraints on private methods.

答案

答案1


4An assertion that a persons date of death > date of birth is appropriate


使用断言不需要引入任何包,但是它确实要求JDK1.4或更高版本,并且需要对JDK工具使用命令行参数。断言不能用来检查方法参数的值,因为在正常(非测试)模式中断言检查会被禁用。一个人死亡的日期比出生日期大的概念始终是正确的,所以使用断言结构是恰当的。


答案 2


3The program will run with no output

运行程序时没有从命令行显式激活断言将不会产生断言错误。

答案 3


3A program is being created for food preparation that involves cooking meat. Code is included so that if the value of a temperature variable reading appears to be negative an assert exception is thrown.

    1. A school attendance system is being created. In the system is code that will throw an assert exception if a child’s age is calculated to be less than zero.

选项12,以及24之间的重要不同点在于,选项12中断言机制在程序正常运行过程中是必需的。断言对于正常运行程序或标准运行时检查不是必要的。当然,对于选项34你可能希望包含运行时检查而不是断言,但是因为描述没有指明程序正常运行的产出依赖于这个测试,所以使用断言是恰当。

答案 4

3Compilation and no output given a command parameter of 1

    1. Compilation and assert error given a command parameter of 0

答案 5

3Asserts should be used to check for conditions that should never happen

    1. Asserts can be used to enforce argument constraints on private methods.

最少的对于程序设计语言运行方式的知识就能指出新特性都会引起向后兼容问题。如果程序员在JDK1.4之前使用了单词assert作为变量,你将需要在使用JDK1.4时传递一个命令行参数来指出这一点。使用断言来检查命令行参数是不合适的,因为断言检查永远都不会被打开。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

光义

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值