最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java學習記錄:java的基本程序設(shè)計結(jié)構(gòu)(二)

2022-12-15 19:16 作者:冰靈___Ling  | 我要投稿

輸入與輸出:

為了增加后面示例程序的趣味性,我們希望程序能夠接受輸入,并適當?shù)馗袷交绦蜉敵?。當然,現(xiàn)代的程序都使用GUI收集用戶輸入,然而,編寫這樣一個界面需要使用更多工具與技術(shù),目前還不具備這些條件。我們的第一要務(wù)是熟悉java程序設(shè)計語言,因此我們將使用基本的控制臺來實現(xiàn)輸入和輸出。

讀取輸入:

前面已經(jīng)看到,將輸出打印到“標準輸出流”(即控制臺窗口)是一件非常容易的事情,只需要調(diào)用System.out.println。不過,讀取“標準輸入流”System.in就沒有那么簡單了。要想讀取控制臺輸入,首先需要構(gòu)造一個與“標準輸入流”Sysem.in關(guān)聯(lián)的Scanner對象。

Scanner in = new Scanner(System.in);

(構(gòu)造器和new操作符以后會介紹。)

現(xiàn)在,就可以使用Scanner類的各種方法讀取輸入了。例如,nextLine方法將讀取一行輸入。

System.out.print("What is your name");
String name = in.nextLine();

在這里,使用nextLine方法是因為輸入行中有可能包含空格。要想讀取一個單詞(以空白符作為分隔符),可以調(diào)用next方法。

String firstName = in.next();

要想讀取一個整數(shù),要使用nextInt方法。

System.out.println("How old are you?");
int age = in.nextInt();

與此類似,nextDouble方法可以讀取下一個浮點數(shù)。

最后,在程序的最前面添加一行代碼:

import java.util.*;

Scanner類在java.util包中定義。當使用的類不是定義在基本java.lang包中時,需要使用import指令導(dǎo)入相應(yīng)的包。(有關(guān)于包與import指令的詳細內(nèi)容以后會講)。

?

Scanner方法:

java.util.Scanner????5

·Scanner(InputStrem in)

用給定的輸入流構(gòu)造一個Scanner對象。

·String nextLine()

讀取下一行輸入。

·String next()

讀取輸入的下一個單詞(以空白符作為分隔符)。

·int nextInt()

·double nextDouble()

讀取并轉(zhuǎn)換下一個表示整數(shù)或浮點數(shù)的字符序列。

·boolean hasNext()

檢測輸入中是否還有其他單詞。

·boolean hasNextInt()

·boolean hasNextDouble()

檢測下一個字符序列是否表示一個整數(shù)或浮點數(shù)。

?

【注釋:因為輸入對所有人都可見,所以Scanner類不適用于從控制臺讀取密碼??梢允褂肅onsole類來達到這個目的。要想讀取一個密碼,可以使用以下代碼:

Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");

為安全起見,將返回的密碼存放在一個字符數(shù)組中,而不是字符串中。完成對密碼的處理之后,應(yīng)該馬上用一個填充值覆蓋數(shù)組元素(數(shù)組處理以后會講)。

使用Console對象處理輸入不如使用Scanner方便。必須一次讀取一行輸入,而且Console類沒有提供方法來讀取單個單詞或數(shù)字。】

?

Java.lang.System ??1.0

·static Console console() ??6

如果有可能進行交互操作,就通過控制臺窗口為交互的用戶返回一個console對象,否則返回null。對于任何一個在控制臺窗口啟動的程序,都可使用Console對象。否則,是否可用取決于所使用的系統(tǒng)。

?

Java.io.Console ??6

·static char[] readPassword (String prompt, Object....args)

·static String readLine(String prompt, Object...args)

顯示提示符(prompt)并讀取用戶輸入,直到輸入行結(jié)束。可選的args參數(shù)用來提供格式參數(shù)。(有關(guān)這部分內(nèi)容之后會講)

?

格式化輸出:

可以使用System.out.print(x)語句將數(shù)值x輸出到控制臺。這個命令將以x的類型所允許的最大非0位數(shù)打印x。例如:

double x = 10000.0 / 3.0;
System.out.print(x)

會打?。?/p>

3333.3333333333335

如果希望顯示美元、美分數(shù),這就會有問題。

這個問題可以利用printf方法來解決,它沿用了C語言函數(shù)庫中的古老約定。例如,以下調(diào)用:

System.out.printf("%8.2f",x);

打印x時字段寬度(field width)為8個字符,精度為2個字符。也就是說,結(jié)果包含一個前導(dǎo)的空格和7個字符,如下所示:

·3333.33(用·代替空格表示)

可以為printf提供多個參數(shù),例如:

System.out.printf("Hello, %s. Next year, you'll be %d", name, age);

每一個以%字符開頭的格式說明符(format specifiers)都替換為相應(yīng)的參數(shù)。格式說明符末尾的轉(zhuǎn)換字符(conversion character)指示要格式化的數(shù)值的類型:f表示浮點數(shù),s表示字符串,d表示十進制整數(shù)。

大寫形式會生成大寫字母。例如,“%8.2E”將3333.33格式化為3.33E+03,這里有一個大寫的E。

下面列出了用于printf的轉(zhuǎn)換字符。

轉(zhuǎn)換符

類型

舉例

d

十進制整數(shù)

159

x或X

十六進制整數(shù)。要想對十六進制格式化有更多控制,可以使用HexFormat類

9f

o

八進制整數(shù)

237

f或F

定點浮點數(shù)

15.9

e或E

指數(shù)浮點數(shù)

1.59e+01

g

通用浮點數(shù)(e和f中較短的一個)

_____

a

十六進制浮點數(shù)

0x1.fccdp3

s

字符串

Hello

c

字符

H

?

b

?

布爾

True

h

散列碼

42628b2

tx或Tx

日期時間(T強制大寫)

已經(jīng)過時,應(yīng)該改為使用java.time;類

%

百分號

%

n

與平臺有關(guān)的行分隔符

___

【注釋:可以使用s轉(zhuǎn)換字符格式化任意的對象。如果一個任意對象實現(xiàn)了Formattable接口,會調(diào)用這個對象的formatTo方法。否則,會調(diào)用toString方法將這個對象轉(zhuǎn)換為一個字符串。(toString方法和接口以后會講)】

另外,還可以指定控制格式化輸出外觀的各種標志(flag)。例如,逗號標志會增加分組分隔符。即:

System.out.printf("%,.2f",10000.0 / 3.0);

會打?。?/p>

3,333.33

可以使用多個標志,例如,”%,( .2f”會使用分組分隔符,并將負數(shù)包圍在括號內(nèi)。

下面列出了用于printf的標志:

?

可以使用靜態(tài)的String.format方法創(chuàng)建一個格式化的字符串,而不打印輸出:

String message = String.format("Hello, %s. Next year, you'll be %d", name, age+1);

【注釋:在java15中,可以使用formatted方法,這樣可以少敲5個字符:

String message =“Hello,%s. Next year, you'll be %d".formatted(name, age+1);

?【注釋:格式化規(guī)則是特定于本地化環(huán)境的。例如,在德國,分組分隔符是點號而不是逗號?!?/p>

文件輸入與輸出:

要想讀取一個文件,需要構(gòu)造一個Scanner對象,如下所示:

Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);

如果文件名中包含反斜線符號,記住要在每個反斜線之前再加一個額外的反斜線轉(zhuǎn)義:

”c:\\mydirectory\\myfile.txt”

現(xiàn)在就可以使用之前見過的Scanner方法讀取這個文件了。

【注釋:這里指定了UTF-8字符編碼,這對于互聯(lián)網(wǎng)上的文件很常見(不過并不是普通適用)。讀取一個文本文件時,要知道它的字符編碼(更多信息見下一本書)。如果省略字符編碼,則會使用運行這個Java程序的機器的“默認編碼”。這不是一個好主意,如果在不同的機器上運行這個程序,可能會有不同的表現(xiàn)?!?/span>

【警告:可以提供一個字符串參數(shù)來構(gòu)造一個Scanner,但這個Scanner會把字符串解釋為數(shù)據(jù),而不是文件名。例如,如果調(diào)用:

canner in = new Scanner("myfile.txt");//ERROR?

這個scanner會將參數(shù)看作是包含10個字符(m、yf等)的數(shù)據(jù)。這可能不是我們的原意。】

要想寫入文件,需要構(gòu)造一個PrintWriter對象。在構(gòu)造器(constructor)中,需要提供文件名和字符編碼:

PrintWriter out = new PrintWriter("myfile.txt", StandardCharsets.UTF_8);

如果文件不存在,則創(chuàng)建該文件??梢韵褫敵龅絊ystem.out一樣使用print、println以及printf命令。

【注釋:當指定一個相對文件名,例如,myfile.txt、mydirectory/myfile.txt../myfile.txt,文件將相對于啟動Java虛擬機的那個目錄放置。如果從一個命令shell執(zhí)行以下命令啟動程序:

java MyProg

啟動目錄就是命令shell的當前目錄。不過,如果使用集成開發(fā)環(huán)境,那么啟動目錄將由IDE控制。可以使用下面的調(diào)用找到這個目錄的位置:

String dir = System.getProperty("user.dir");

如果覺得文件定位太麻煩,可以考慮使用絕對路徑名,例如:

c:\\mydirectory\\myfile.txt/home/me/mydirectory/myfile.txt。

如你所見,訪問文件和使用System.in和System.out一樣容易。要記住一點:如果用一個不存在的文件構(gòu)造一個Scanner,或者用一個無法創(chuàng)建的文件名構(gòu)造一個PrintWriter,就會產(chǎn)生異常。Java編譯器認為這些異常比“被零除”異常更嚴重。(之后會學習處理異常的方法)至于現(xiàn)在,只需要告訴編譯器:你已經(jīng)知道有可能出現(xiàn)“輸入/輸出”異常。為此要用一個throws子句標記main方法,如下所示:

public static void main(String[] args) throws IOException
{
????Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);
????....
}

現(xiàn)在你已經(jīng)學習了如何讀寫包含文本數(shù)據(jù)的文件了。對于更高級的內(nèi)容,請見卷二。

【注釋:從命令shell啟動一個程序時,可以利用shell的重定向語法將任意文件關(guān)聯(lián)到System.in和System.out:

java Myprog < myfile.txt > output.txt

這樣就不必擔心處理IOException異常了?!?/span>

?

以下是一個讀寫文本文件代碼示例:

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.*;
public class haihaihai {
????public static void main(String[] args) throws IOException
????{
????????Scanner in = new Scanner(Path.of("C:\\Users\\冰鈴月\\IdeaProjects\\The first java project\\108宿舍檔案.txt"), StandardCharsets.UTF_8);
????????String User1 = in.nextLine();
????????System.out.println(User1);
????????PrintWriter out = new PrintWriter("C:\\Users\\冰鈴月\\IdeaProjects\\The first java project\\test.txt",StandardCharsets.UTF_8);
????????out.print("Hello,fuck you.");
????????out.close();
????}
}

——

控制流程:

Java支持使用條件語句和循環(huán)結(jié)構(gòu)來確定控制流程。這里首先討論條件語句,然后介紹循環(huán)語句,最后介紹switch語句,它可以用來檢測一個表達式的多個值。

【C++注釋:java的控制流程結(jié)構(gòu)與C/C++的控制流程結(jié)構(gòu)基本相同,只有很少幾個例外。Java中沒有g(shù)oto語句,但break語句可以帶標簽,可以利用它從嵌套循環(huán)中跳出(對于這種情況,C語言中可能就要使用goto語句了)。最后,還有以中國變形的for循環(huán),有點類似于C++中基于范圍的for循環(huán)和C#中的foreach循環(huán)?!?/p>

?

塊作用域:

在學習控制結(jié)構(gòu)之前,需要了解塊(block)的概念。

塊(即復(fù)合語句)由若干條Java語句組成,并用一對大括號括起來。塊確定了變量的作用域。一個塊可以嵌套在另一個塊中。下面就是嵌套在main方法塊中的一個塊。

public static void main(String[] args){
????int n;
????...
????{
????????int k;
????????...
????}//k is only defined up to here
}

但是,不能在嵌套的兩個塊中聲明同名的變量。例如,下面的代碼就有錯誤,而無法通過編譯:

public static void main(String[] args){
????int n;
????...
????{
????????int k;
????????int n; // ERROR--can't redeclare n in inner block
????????...
????}
}

【C++注釋:在C++中,可以在嵌套的塊中重定義一個變量。在內(nèi)層定義的變量會遮蔽(shadow)在外層定義的變量。這就有可能帶來編程錯誤,因此Java中不允許這樣做?!?/p>

條件語句:

在Java中,條件語句的形式為:

if (condition) statement

這里的條件必須用小括號括起來。

與大多數(shù)程序設(shè)計語言一樣,在java中,常常希望在某個條件為真時執(zhí)行多條語句。在這種情況下,就可以使用塊語句(block statement),形式如下:

{
????statement1
????statement2
????...
}

例如:

if(yourSales >= target)
{
????performance = "Satisfactory";
????bonus = 100;
}

當yourSales大于或等于target時,將執(zhí)行大括號中的所有語句。

【注釋:使用塊(有時稱為復(fù)合語句)可以在Java程序結(jié)構(gòu)中原本只能放置一條(簡單)語句的地方放置多條語句?!?/p>

在Java中,更一般的條件語句如下所示:

if (condition) statement1 else statement2

例如:

if (yourSales >= target)
{
????performance = "Satisfactory";
????bonus = 100 + 0.01 * (yourSales - target);
}
else
{
????performance = "Unsatisfactory";
????bonus = 0;
}

其中else部分總是可選的。else子句與最鄰近的if構(gòu)成一組。因此,在以下語句中:

if(x <= 0) if(x == 0) sign = 0; else sign = -1;

else與第2個if配對。當然,使用大括號可以讓這段代碼更加清晰:

if(x <= 0) {if(x == 0) sign = 0; else sign = -1;}

反復(fù)使用if....else if....很常見。例如:

if (yourSales >= 2 * target)
{
????performance = "Excellent";
????bonus = 1000;
}
else if(yourSales >= 1.5 * target)
{
????performance = "Fine";
????bonus = 500;
}
else if(yourSales >= target)
{
????performance = "Satisfactory";
????bonus = 100;
}
else
{
????System.out.println("You're fired");
}

?

循環(huán):

while循環(huán)會在條件為true時執(zhí)行一個語句(也可以是一個塊語句)。一般形式如下:

while (condition) statement

如果開始時循環(huán)條件就為false,那么while循環(huán)一次也不執(zhí)行。

While循環(huán)語句在最前面檢測循環(huán)條件。因此,循環(huán)體中的代碼有可能一次都不執(zhí)行。如果希望循環(huán)體至少執(zhí)行一次,需要使用do/while循環(huán)將檢測放在最后,它的語法如下:

do statement while (condition);

這種循環(huán)先執(zhí)行語句(通常是一個語句塊),然后再檢查循環(huán)條件。如果條件為true,就重復(fù)執(zhí)行語句,然后再次檢測循環(huán)條件,依次類推。

?

確定性循環(huán):

for循環(huán)語句是支持迭代的一種通用結(jié)構(gòu),它由一個計數(shù)器或類似的變量控制迭代次數(shù),每次迭代后這個變量將會更新。下面的循環(huán)將在屏幕上顯示出打印數(shù)字1~10:

for(int i = 1; i <=10; i++)
System.out.println(i);

for語句的第1部分通常是對計數(shù)器初始化;第2部分給出每次新一輪循環(huán)執(zhí)行前要檢測的循環(huán)條件;第3部分指定如何更新計數(shù)器。

與C++類似,盡管Java允許在for循環(huán)的各個部分放置任何表達式,但有一條不成文的規(guī)則:for語句的3個部分應(yīng)該對同一個計數(shù)器變量進行初始化、檢測和更新。若不遵守這個規(guī)則,所寫的循環(huán)很可能晦澀難懂。(但是沒成文就是沒成文,該咋用咋用)

即使受這個規(guī)則所限,仍有無盡可能,你可以寫各種各樣的for循環(huán)。例如,可以編寫下面這個倒計數(shù)的循環(huán):

for(int i = 10; i > 0; i--)
????System.out.println("Counting down ..." + i);
System.out.println("Blastoff!")

【警告:在循環(huán)中,檢測兩個浮點數(shù)是否相等需要格外小心。下面的for循環(huán):

for (double x = 0; x!= 10; x += 0.1)...

可能永遠不會結(jié)束。由于存在舍入誤差,可能永遠達不到精度的最終值。在這個例子中,因為0.1無法精確地用二進制表示,所以,x將從9.99999999999998跳到10.09999999999998?!?/p>

在for語句的第1部分中聲明一個變量之后,這個變量的作用域會擴展到這個for循環(huán)體的末尾。

for (int i = 1; i<= 10; i++)
{
????...
}
// i no longer defined here

特別指出,如果在for語句內(nèi)部定義一個變量,這個變量就不能在循環(huán)體之外使用。因此,如果希望在for循環(huán)體之外使用循環(huán)計數(shù)器的最終值,就要確保在循環(huán)體之外聲明這個變量。

int i;
for (i = 1; i<= 10; i++)
{
????...
}
// i is still defined here

另一方面,可以在不同的for循環(huán)中定義同名的變量:

for (int i = 1; i<= 10; i++)
{
????...
}
...
for (int i = 11; i <=20; i++) // OK to define another variable named i
{
????...
}

for循環(huán)語句只是while循環(huán)的一種簡化形式。例如,

for (int i = 10; i > 0; i--)
????System.out.println("Counting down ..." + i);

可以重寫為:

int i = 10;
while ( i > 0)
{
????System.out.println("Counting down ... " + i);
????i--;
}

【注釋:之后會介紹“泛型for循環(huán)”(又稱為for each 循環(huán)),利用這個循環(huán)可以很方便地訪問一個數(shù)組或集合中的所有元素?!?/p>

?

多重選擇:switch語句

在處理同一個表達式的多個選項時,使用if/else結(jié)構(gòu)會顯得有些笨拙。switch語句會讓這個工作變得容易,特別采用Java14引入的形式會更簡單。

例如,如果建立一個簡易的菜單系統(tǒng),其中包含4個選項,可以使用以下代碼:

Scanner in = new Scanner(System.in);
System.out.print("Select an option (1, 2, 3, 4 ) ");
int choice = in.nextInt();
switch(choice)
{
????case 1 ->
????????...
????case 2 ->
????????...
????case 3 ->
????????...
????case 4 ->
????????...
????default ->
????????System.out.println("Bad input");
}

case標簽可以是:

·類型為char、byte、short或int的常量表達式

·枚舉常量

·字符串字面量

·多個字符串,用逗號分隔

例如:

String input = ...;
switch (input.toLowerCase())
{
????case "yes", "y" ->
????????...
????case "no", "n" ->
????????...
????default ->
????????...
}

swicth語句的“經(jīng)典”形式可以追溯到C語言,從Java 1.0開始就支持這種形式。具體形式如下:

int choice = ...;
switch(choice)
{
????case 1:
????????...
????????break;
????case 2:
????????...
????????break;
????case 3:
????????...
????????break;
????case 4:
????????...
????????break;
????default:
????????// bad input
????????...
????????break;
}

switch語句從與選項值相匹配的case標簽開始執(zhí)行,直到遇到下一個break語句,或者執(zhí)行到switch語句結(jié)束。如果沒有匹配的case標簽,則執(zhí)行default子句(如果有default子句)。

警告:有可能觸發(fā)多個分支。如果忘記在一個分支末尾增加break語句,就會接著執(zhí)行下一個分支!這種情況相當危險,常常會引發(fā)錯誤。

為了檢測這種問題,編譯代碼時可以加上 -Xlint:fallthrough選項,如下所示:

javac -Xlint:fallthrouth Test.java

這樣一來,如果某個分支最后缺少一個break語句,編譯器就會給出一個警告。

如果你確實是想使用這種“直通式”(fallthrough)行為,可以為其外圍方法加一個注解@Suppress Warnings(“fallthrough”)。(注解是為編譯器或處理java源文件或類文件的工具提供信息的一種機制。卷II會深入介紹注解)】

?

這兩種switch形式都是語句。之前我們已經(jīng)見過一個switch表達式,它會生成一個值。switch表達式?jīng)]有“直通式”行為。

為了對稱,Java14還引入了一個有直通行為的switch表達式,所以總共有4種不同形式的switch:

?

表達式

語句

無直通行為

int numLetters = switch (seasonName)
{
????case "Spring" ->
????????{
????????????System.out.println("spring time!");
????????????yield 6;
????????}
????case "Summer", "Winter" -> 6;
????case "Fall" -> 4;
????default -> -1;
};

?

switch (seasonName)
{
????case "Spring" ->
????????{
????????????System.out.println("spring time!");
????????????numLetters = 6;
????????}
????case "Summer", "Winter" -> numLetters = 6;
????case "Fall" -> numLetters = 4;
????default -> numLetters = -1;
}

?

有直通行為

int numLetters = switch (seasonName)
{
????case "Spring":
????????{
????????????System.out.println("spring time!");
????????}
????case "Summer", "Winter" :
????????yield 6;
????case "Fall":
????????yield 4;
????default:
????????yield -1;
};

?

switch (seasonName)
{
????case "Spring":
????????{
????????????System.out.println("spring time!");
????????}
????case "Summer", "Winter" :
????????numLetters = 6;
????????break;
????case "Fall":
????????numLetters = 4;
????????break;
????default:
????????numLetters = -1;
}

?

在有直通行為的形式中,每個case以一個冒號結(jié)束。如果case以箭頭”->”結(jié)束,則沒有直通行為。不能在一個switch語句種混合使用冒號和箭頭。

注意switch表達式種的yield關(guān)鍵字。與break類似,yield會終止執(zhí)行。但與break不同的是,yield還會生成一個值,這就是表達式的值。

要在switch表達式的一個分支中使用語句而不想有直通行為,就必須使用大括號和yield。如下將為一個分支增加日志語句:

case "Spring" ->
????{
????????System.out.println("spring tme!");
????????yield 6;
????}

switch表達式的每個分支必須生成一個值。最常見的做法是,各個值跟在一個箭頭”->”后面:

case "Summer", "Winter" -> 6;

如果無法做到,則使用yield語句。

【注釋:完全可以在switch表達式的一個分支中拋出異常。例如:

default -> throw new ILLegaLArgumentException("Not a valid season");

(異常以后會講)】

【警告:switch表達式的關(guān)鍵是生成一個值(或者產(chǎn)生一個異常而失?。?。不允許“跳出”switch表達式:

default -> { return -1;} // ERROR

具體來講,不能在switch表達式中使用return、break或continue語句?!?/p>

switch有這么多種形式,要如何選擇呢?

swicth表達式優(yōu)于語句。如果每個分支會為一個變量賦值或方法調(diào)用計算值,則用一個表達式生成值,然后使用這個值。例如:

??numLetters = switch (seasonName)
{
????case "Spring", "Summer", "Winter" -> 6;
????case "Fall" -> 4;
????default -> -1;
};

要優(yōu)于:

switch (seasonName)
{
????case "Spring", "Summer", "Winter" ->
????????numLetters = 6;
????case "Fall" ->
????????numLetters = 4;
????default ->
????????numLetters =-1;
}

只要在確實需要直通行為時,或者必須為一個switch表達式增加語法時,才需要使用break或yield。不過這些情況非常少見。

?

中斷控制流程的語句:

盡管Java的設(shè)計者將goto仍作為一個保留字,但實際上并不打算在語言中包含goto。通常,使用goto語句會被認為是一種拙劣的程序設(shè)計風格。但在有些情況下,偶爾使用goto跳出循環(huán)還是有益處的。Java設(shè)計者同意這種看法,甚至在Java語言中增加了一條新的語句:帶標簽的break,以此來支持這種程序設(shè)計風格。

下面首先來看不帶標簽的break語句。與用于退出switch語句的break語句一樣,它也可以用于退出循環(huán)。例如:

while (years <= 100)
{
????balance += payment;
????double interest = balance * interestRate / 100;
????balance += interest;
????if (balance >= goal) break;
????years++;
}

循環(huán)開始時,如果years > 100,或者如果循環(huán)中間balance >= goal,則退出循環(huán)。當然,也可以在不使用break的情況下計算years的值,如下所示:

while (years <= 100 && balance < goal)
{
????balance += payment;
????double interest = balance * interestRate / 100;
????balance += interest;
????if (balance < goal) {
????????years++
????}
}

但是需要注意,在這個版本中,檢測了兩次balance < goal。為了避免重復(fù)檢測,有些程序員更偏愛使用break語句。

與C++不同,Java還提供了一種帶標簽的break語句,允許跳出多重嵌套的循環(huán)。有時候,在嵌套很深的循環(huán)語句中會發(fā)生一些不可預(yù)料的事情。此時你可能希望完全跳出所有嵌套循環(huán)。如果只是為各層循環(huán)檢測添加一些額外的條件,這會很不方便。

下面的例子展示了break語句如何工作。請注意,標簽必須放在你想跳出的最外層循環(huán)之前,并且必須緊跟一個冒號。

Scanner in = new Scanner(System.in);
int n;
read_data:
while(...) // this loop statement is tagged with the label
{
????...
????for(...) // this inner loop is not labeled
????{
????????System.out.print("Enter a number >= 0: ");
????????n = n.nextInt();
????????if (n < 0) // should never happen-can't go on
????????????break read_data;
????????????// break out of read_data loop
????????...
????}
}
// this statement is executed immediately after the labeled break
if (n < 0) // check for bad situation
{
????// deal with bad situation
}
else
{
????// carry out normal processing
}

如果輸入有誤,執(zhí)行帶標簽的break會跳轉(zhuǎn)到帶標簽的語句塊末尾。與任何使用break語句的代碼一樣,接下來需要檢測循環(huán)是正常退出,還是由于break提前退出。

【注釋:有意思的是,可以將標簽應(yīng)用到任何語句,甚至可以應(yīng)用到if語句或者塊語句,如下所示:

label:
{
????...
????if (condition) break label; // exits block
????...
}
// jump here when the?break statement executes

因此,如果確實希望使用goto語句,而且一個代碼塊恰好在你想要跳轉(zhuǎn)到的位置之前結(jié)束,就可以使用break語句!當然,我并不提倡使用這種方法。另外需要注意,只能跳出語句塊,而不能跳入語句塊。】

最后,還有一個continue語句。與break語句一樣,它將中斷正常的控制流程。continue語句將控制到最內(nèi)層外圍循環(huán)的首部。例如:

?

Scanner in = new Scanner(System.in);
while (sum < goal)
{
????System.out.print("Enter a number: ");
????n = in.nextInt();
????if (n < 0) continue;
????sum += n; // not executed if n < 0
}

如果n < 0,則continue語句會越過當前循環(huán)體的剩余部分,直接跳到循環(huán)首部。

如果在for循環(huán)中使用continue語句,會跳轉(zhuǎn)到for循環(huán)的“更新”部分。例如:

for ( count = 1; count <= 100; count++)
{
????System.out.print("Enter a number, -1 to quit: ");
????n = in.nextInt();
????if (n < 0) continue;
????sum += n; // not executed if n < 0
}

如果n < 0,則continue語句將跳轉(zhuǎn)到count++語句。

還有一種帶標簽的continue語句,將跳轉(zhuǎn)到有匹配標簽的循環(huán)的首部。

【提示:許多程序員發(fā)現(xiàn)break和continue語句很容易混淆。這些語句完全是可選的,即不使用這些語句也能表達同樣的邏輯。在本書中,所有程序都不會使用break和continue?!?/p>

?

大數(shù):

如果基本的整數(shù)和浮點數(shù)精度不足以滿足需求,那么可以使用java.math包中兩個很有用的類:BigInteger和BigDecimal。這兩個類可以處理包含任意長度數(shù)字序列的數(shù)值。BigInteger類實現(xiàn)任意精度的整數(shù)運算,BigDecimal實現(xiàn)任意精度的浮點數(shù)運算。

使用靜態(tài)的valueOf方法可以將一個普通的數(shù)轉(zhuǎn)換為大數(shù):

BigInteger a = BigInteger.valueOf(100);

對于更長的數(shù),可以使用一個帶字符串參數(shù)的構(gòu)造器:

BigInteger reallyBig = new BigInteger("2222272727282972838292928928923823782792392839283871799819");

另外還有一些常量:BigInteger.ZERO、BigInteger.ONE和BigInteger.TEN,java9之后還增加了BigInteger.TWO。

【警告:對于BIgDecimal類,總是應(yīng)當使用帶一個字符串參數(shù)的構(gòu)造器。還有一個Bigdecimal(double)構(gòu)造器,不過這個構(gòu)造器本質(zhì)上很容易產(chǎn)生舍入誤差,例如,new BigDecimal(0.1)會得到以下數(shù)位:

0.1000000000000000055511151231257827021181583404541015625】

遺憾的是,不能使用人們熟悉的算數(shù)運算符(如:+和*)來組合大數(shù),而需要使用大數(shù)類的add和multiply方法。

BigInteger c = a.add(b); // c = a + b
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c*(b+2)

【C++注釋:與C++不同,Java不能通過編程實現(xiàn)運算符重載。使用BigInteger類的程序員無法重定義+和*運算符來提供BigInteger類的add和multiply運算。Java語言的設(shè)計者重載了+運算符來完成字符串的拼接,但沒有重載其他的運算符,也沒有給Java程序員提供機會在他們自己的類中重載運算符?!?/p>

?

數(shù)組:

數(shù)組是一種數(shù)據(jù)結(jié)構(gòu),用來存儲同一類型值的集合。通過一個整型索引(index,或稱下標)可以訪問數(shù)組中的每一個值。例如,如果a是一個整型數(shù)組,a[i]就是數(shù)組中索引為i的整數(shù)。

在聲明數(shù)組變量時,需要指出數(shù)組類型(元素類型后面緊跟[])和數(shù)組變量名。例如,下面聲明了整型數(shù)組a:

int[] a;

不過,這條語句只聲明了變量a,并沒有將a初始化為一個真正的數(shù)組。應(yīng)該使用new操作符創(chuàng)建數(shù)組。

int[] a = new int[100]; // or var a = new int[100];

這條語句聲明并初始化了一個可以存儲100個整數(shù)的數(shù)組。

數(shù)組長度不要求是常量:new int[n]會創(chuàng)建一個長度為n的數(shù)組。

一旦創(chuàng)建了數(shù)組,就不能再改變它的長度(不過,當然可以改變單個數(shù)組元素)。如果程序運行中需要經(jīng)常擴展數(shù)組的大小,就應(yīng)該使用另一種數(shù)據(jù)結(jié)構(gòu)——數(shù)組列表(array list)。(以后會講)

【注釋:可以使用下面兩種形式定義一個數(shù)組變量:

int[] a;

int a[];

大多數(shù)Java程序員喜歡使用第一種風格,因為它可以將類型int[](整型數(shù)組)與變量名清晰地分開。】

在Java中,提供了一種創(chuàng)建數(shù)組對象并同時提供初始值的簡寫形式。下面是一個例子:(primes:素數(shù)、質(zhì)數(shù))

int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };

請注意,這個語法中不需要使用new,甚至不用指定長度。

最后一個值后面允許有逗號,如果你要不斷為數(shù)組增加值,這樣會很方便:

String[] authors =
????{
????????"James Gosling",
????????"Bill Joy",
????????"Guy Steele",
????????// add more names here and put a comma after each name
????};

還可以聲明一個匿名數(shù)組(anoymous array):

new int[] { 17, 19, 23, 29, 31, 37};

這個表達式會分配一個新數(shù)組并填入大括號中提供的值。它會統(tǒng)計初始值個數(shù),并相應(yīng)地設(shè)置數(shù)組大小??梢允褂眠@種語法重新初始化一個數(shù)組而無須創(chuàng)建新變量。例如:

smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };

這是以下語句的簡寫形式:

int[] anonymous = { 17, 19, 23, 29, 31, 37 };
smallPrimes = anonymous;

【注釋:在Java中,允許有長度為0的數(shù)組。編寫一個結(jié)果為數(shù)組的方法時,如果碰巧結(jié)果為空,這樣一個長度為0的數(shù)組就很有用。可以如下構(gòu)造一個長度為0的數(shù)組:

new elementType[0]

new elementType[] {}

注意,長度為0的數(shù)組與null并不相同?!?/p>

?

訪問數(shù)組元素:

數(shù)組元素從0開始編號。最后一個合法的索引為數(shù)組長度減1.在下面的例子中,索引值為0~99。一旦創(chuàng)建了數(shù)組,就可以在數(shù)組中填入元素。例如,可以使用一個循環(huán):

int[] a = new int[100];
for (int i = 0; i < 100; i++)
????a[i] = i; // fills the array with numbers 0 to 99

創(chuàng)建一個數(shù)字數(shù)組時,所有元素都初始化為0。boolen數(shù)組的元素會初始化為false。對象數(shù)組的元素則初始化為一個特殊值null,表示這些元素(還)未存放任何對象。例如:

String[] names = new String[10];

會創(chuàng)建一個包含10個字符串的數(shù)組,所有字符串都為null。如果希望這個數(shù)組包含空串,則必須為元素提供空串:

for (int i = 0; i < 10; i++)
????names[i] = "";

【警告:如果創(chuàng)建了一個包含100個元素的數(shù)組,然后試圖訪問元素a[100](或在0~99之外的任何其他索引),就會出現(xiàn)“array index out of bounds”(數(shù)組索引越界)異常。】

要想獲得數(shù)組中的元素個數(shù),可以使用array.length。例如,

for (int i = 0; i < a.length; i++)
????System.out.println(a[i]);

?

for each 循環(huán):

Java有一種功能很強的循環(huán)結(jié)構(gòu),可以用來依次處理數(shù)組(或者其他元素集合)中的每個元素,而不必考慮指定索引值。

這種增強的for循環(huán)形式如下:

for (variable : collection) statement

它將給定變量(variable)設(shè)置為集合中的每一個元素,然后執(zhí)行語句(statement)(當然,也可以是語句塊)。collection表達式必須是一個數(shù)組或者是一個實現(xiàn)了Iterable接口的類對象(例如ArrayList)。(數(shù)組列表和Iterable接口以后會講)

例如:

for (int element : a)
????System.out.println(element);

會打印數(shù)組a的每一個元素,一個元素占一行。

這個循環(huán)應(yīng)該讀作“循環(huán)a中的每一個元素”(for each element in a)。Java語言的設(shè)計者也曾考慮過使用諸如foreach和in這樣的關(guān)鍵字,但這種循環(huán)并不是最初就包含在Java語言中國,而是后來添加的,沒有人希望破壞已經(jīng)包含同名方法或變量(例如System.in)的老代碼。

當然,使用傳統(tǒng)的for循環(huán)也可以獲得同樣的效果:

for (int i = 0; i < a.length; i++)
????System.out.println(a[i]);

但是,“for each”循環(huán)更加簡潔、更不易出錯,因為你不必為起始或終止索引值而操心。

【注釋:“for each”循環(huán)的循環(huán)變量將會遍歷數(shù)組中的每個元素,而不是索引值?!?/p>

如果需要處理一個集合中的所有元素,相比傳統(tǒng)循環(huán),“for each”循環(huán)是個讓人欣喜的改進。不過,很多情況下還是需要使用傳統(tǒng)的for循環(huán)。例如,可能不希望遍歷整個集合,或者可能需要在循環(huán)內(nèi)部使用索引值。

【提示:有一個更容易的方法可以打印數(shù)組中的所有值,即利用Arrays類的toString方法。調(diào)用Arrays.toString(a)會返回一個包含數(shù)組元素的字符串,這些元素包圍在中括號內(nèi),并用逗號分隔,例如,”[2,3,5,7,11,13]”。要想打印數(shù)組,只需要調(diào)用:

System.out.println(Arrays.toString(a));

?

數(shù)組拷貝:

在Java中,允許將一個數(shù)組變量拷貝到另一個數(shù)組變量。這時,兩個變量將引用同一個數(shù)組

int[] luckNumbers = smallPrimes;
luckyNumbers[5] = 12; // now smallPrimes[5] is also 12

如果確實希望將一個數(shù)組的所有值拷貝到一個新的數(shù)組中,就要使用Arrays類的copyOf方法:

int[] copiedLuckyNumbers = Arrays.copyOf(luckNumbers, luckyNumbers.length);

第2個參數(shù)是新數(shù)組的長度。

這個方法也通常用來增加數(shù)組的大?。?/p>

luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

如果數(shù)組元素是數(shù)值型,那么新增的元素將填入0;如果數(shù)組元素是布爾型,則填入false。相反,如果長度小于原數(shù)組的長度,則只拷貝前面的值。

【C++注釋:Java數(shù)組與堆棧上的C++數(shù)組有很大不同,但基本上與在堆(heap)上分配的數(shù)組指針一樣。也就是說,

int[] a = new int[100]; // Java

不同于:

int a[100]; // C++

而等同于:

int* a = new int[100]; // C++

Java中的[ ]運算符預(yù)定義為會完成越界檢查(bounds checking)。另外,沒有指針運算,就意味著不能通過a加1得到數(shù)組中的下一個元素?!?/p>

?

命令行參數(shù):

前面已經(jīng)看到一個例子,其中一個Java數(shù)組重復(fù)出現(xiàn)過很多次。每一個Java程序都有一個帶String arg[]參數(shù)的main方法。這個參數(shù)表明main方法將接受一個字符串數(shù)組,也就是命令行上指定的參數(shù)。

例如,來看下面這個程序:

public class firstWar {
????public static void main(String[] args){
????????if (args.length == 0 || args[0].equals("-h"))
????????????System.out.print("Hello,");
????????else if(args[0].equals("-g"))
????????????System.out.print("Goodbye,");
????????// print the other command-line arguments
????????for(int i = 1; i < args.length; i++)
????????????System.out.print(" "+args[i]);
????}
}

如果如下調(diào)用這個程序:

java firstWar -g cruel world

args數(shù)組將包含以下內(nèi)容:

args[0]:"-g"
args[1]:"cruel"
args[2]:"world"

這個程序會顯示下面這個消息:

Goodbye, cruel world!

【C++注釋:在Java程序的main方法中,程序名并不存儲在args中。例如,從命令行如下運行一個程序時:

java Message -h world

args[0]是”-h”,而不是“Message”或”java“?!?/p>

?

數(shù)組排序:

要想對數(shù)值型數(shù)組進行排序,可以使用Arrays類中的sort方法:

int[] a = new int[100];

。。。

Arrays.sort(a);

這個方法使用了優(yōu)化的快速排序(QuickSort)算法。快速排序算法對于大多數(shù)數(shù)據(jù)集都很高效。Arrays類還提供了另外一些很便捷的方法,在API注釋中將介紹這些方法。

Math.random方法將返回一個0到1之間(包含0、不包含1)的隨機浮點數(shù)。用n乘以這個浮點數(shù),可以得到從0到n-1之間的一個隨機數(shù)。

int r = (int) (Math.random() * n);

如果想確保不會再次抽到一個數(shù),可以創(chuàng)建一個范圍數(shù)組,當抽到一個數(shù)時,用數(shù)組中的最后一個數(shù)覆蓋這個元素,并將n減一。

numbers[r] = numbers[n - 1];
n--;

關(guān)鍵在于每次抽取的都是索引,而不是實際的值。這個索引指向一個數(shù)組,其中包含尚未抽取過的值。

?

Arrays類

java.util.Arrays ?1.2

·static String toString(xxx[] a) ?5

返回一個字符串,其中包含a中的元素,這些元素用中括號包圍,并用逗號分隔。在這個方法以及后面的方法中,數(shù)組元素類型xxx可以是int、long、short、char、byte、boolean、float或double。

·static xxx[] copyOf(xxx[] a, int end) ?6

·static xxx[] copyOfRange(xxx[] a, int start, int end) ?6

返回與a類型相同的數(shù)組,其長度為end或者end-start,并填入a的值。如果end大于a.length,結(jié)果會填充0或false值。

·static void short(xxx[] a)

使用優(yōu)化的快速排序算法對數(shù)組進行排序。

·static int binarySearch(xxx[] a, xxx v)

·static int binarySearch(xxx[] a, int start, int end, xxx v) ?6

使用二分查找算法在有序數(shù)組a中查找值v。如果找到v,則返回相應(yīng)的索引;否則,返回一個負數(shù)值r。-r-1是v應(yīng)插入的地方(為保持a有序)。

·static void fill(xxx[] a, xxx v)

將數(shù)組的所有元素設(shè)置為v。

·static boolean equals(xxx[] a, xxx[] b)

如果兩個數(shù)組長度相同,并且相同索引對應(yīng)的元素都相同,則返回true。

多維數(shù)組:

多維數(shù)組使用多個索引訪問數(shù)組元素,它適用于表格或其他更復(fù)雜的排列形式。

假設(shè)需要建立一個數(shù)值表格,用來顯示在不同利率下投資10000美元有多少收益,利息每年兌換并復(fù)投。

可以使用一個二維數(shù)組(也稱為矩陣)來存儲這些信息,名為balances。

在Java中,聲明一個二維數(shù)組相當簡單。例如:

double[][] balances;

其他情況下,如果知道數(shù)組元素,可以不調(diào)用new,而直接使用一種簡寫形式對多維數(shù)組進行初始化。例如:

int[][] magicSquare =
????{
????????{16, 3, 2, 13},
????????{5, 10, 11, 8},
????????{9, 6, 7, 12},
????????{4, 15, 14, 1}
????};

一旦初始化數(shù)組,就可以利用兩對中括號訪問單個元素,例如,balances[i][j]。

【注釋:“for each”循環(huán)語句不會自動循環(huán)處理二維數(shù)組的所有元素。它會循環(huán)處理行,而這些行本身就是一維數(shù)組。要想訪問二維數(shù)組a的所有元素。它會循環(huán)處理行,而這些行本身就是一維數(shù)組。要想訪問二維數(shù)組a的所有元素,需要使用兩個嵌套循環(huán),如下所示:

for (double[] row : a)
????for (double value : row)
????????do something with value

【提示:要想快速地打印一個二維數(shù)組的元素列表,可以調(diào)用:

System.out.println(Arrays.deepToString(a));

輸出格式為:

[[16, 3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]]

?

不規(guī)則數(shù)組:

到目前為止,我們看到的數(shù)組與其他程序設(shè)計語言中的數(shù)組沒有多大區(qū)別。但在底層實際存在著一些細微的差異,有時你可以充分利用這一點:Java實際上沒有多維數(shù)組,只有一維數(shù)組。多維數(shù)組被解釋為“數(shù)組的數(shù)組”

例如,在前面的示例中,balances數(shù)組實際上是一個包含10個元素的數(shù)組,而每個元素又是一個由6個浮點數(shù)組成的數(shù)組。

表達式balances[i]指示第i個子數(shù)組,也就是表格的第i行,它本身也是一個數(shù)組,balances[i][j]指示這個數(shù)組的第j個元素。

由于可以單獨地訪問數(shù)組的某一行,所以可以讓兩行交換。

double[] temp = balances[i];
balances[i] = balances[i + 1];
balances[i + 1] = temp;

還可以很容易地構(gòu)建一個“不規(guī)則”數(shù)組,即數(shù)組的每一行有不同的長度。下面是一個標準的示例。我們將創(chuàng)建一個數(shù)組,第i行第j列的元素將存放“從i個數(shù)中抽取j個數(shù)”可能的結(jié)果數(shù)。

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

1 5 10 10 5 1

1 6 15 20 15 6 1

由于j不可能大于i,所以矩陣是三角形的。第i行有i+1個元素(允許抽取0個元素,這種選擇只有一種可能)。要想創(chuàng)建這樣一個不規(guī)則的數(shù)組,首先需要分配一個數(shù)組包含這些行:

final int NMAX = 10;
int[][] odds = new int[NMAX + 1][];

接下來,分配這些行:

for (int n =0; n <= NMAX; n++)
????odds[n] = new int[n + 1];

分配了數(shù)組之后,可以采用通常的方式訪問其中的元素(前提是沒有超出邊界):

for (int n =0; n <= NMAX; n++)
????for (int k = 0; k < odds[n].length; k++)
????{
????????// compute lotteryOdds
????????...
????????odds[n][k] = lotteryOdds;
????}

C++注釋:在C++中,Java聲明

?double[][] balances = new double[10][6]; // Java

不同于

double balances[10][6]; // C++

也不同于

double (*balances)[6] = new double[10][6]; // C++

【double (*balances)[6]的意思是指向數(shù)組(這里是每個一維數(shù)組含6個元素)的指針。

new了一個二維數(shù)組,那*balances應(yīng)該是一個一維指針數(shù)組,包含10個元素】

而是分配了一個包含10個指針的數(shù)組:

double** balances= ?new double*[10]; // C++

然后,這個指針數(shù)組的每一個元素會填充一個包含6個數(shù)字的數(shù)組:

for (i = 0; i < 10; i++)
????balances[i] = new double[6];

慶幸的是,當調(diào)用new double[10][6]時,這個循環(huán)是自動的。需要不規(guī)則的數(shù)組時,只能單獨地分配行數(shù)組。】

現(xiàn)在,我們已經(jīng)了解了Java語言地基本程序結(jié)構(gòu),下一章將介紹Java面向?qū)ο蟪绦蛟O(shè)計。


【學習參考書籍:《Java核心技術(shù)卷I》】

Java學習記錄:java的基本程序設(shè)計結(jié)構(gòu)(二)的評論 (共 條)

分享到微博請遵守國家法律
保靖县| 凌云县| 新建县| 大理市| 文登市| 张家川| 浮梁县| 喀什市| 嘉黎县| 锡林郭勒盟| 兰西县| 文登市| 盘山县| 姜堰市| 洪湖市| 尉犁县| 固阳县| 大石桥市| 石楼县| 道孚县| 渭南市| 嘉荫县| 柘城县| 上杭县| 榆林市| 营山县| 永州市| 申扎县| 玛纳斯县| 湖口县| 上高县| 靖远县| 岱山县| 普安县| 海淀区| 汶川县| 呼图壁县| 霍城县| 阿拉善盟| 尚义县| 紫金县|