【第5、6天】Java中的流程控制、陣列及應用
1 流程控制
1.1 分支結構
1.1.1 if-else
- 語法格式:
if(boolean判斷){
執行語句l;
}else if(boolean判斷){
執行語句2;
}else if(boolean判斷){
執行語句3;
}else{
執行語句4;
}
-
執行順序 由if進入,逐個boolean進行判斷,如果遇到false就向下執行,直到遇到true為止(排他性:一事物不容許其他事物與自己在同一範圍內共存的性質,這個變數只對應一個條件)。所以在設定分支條件時,避免出現條件設定過於寬泛使得需要驗證的變數有多個條件符合的現象。
-
其他需要注意的:
- 當只有if時,如果if所轄括號下面的語句全是else所轄的語句,else{}可以省略;當存在if-else if時必須有else。但是有沒有else只是在字面上有不同,分支是存在的。
- 當if-else裡面只有一個語句的時候,“{}”可以不寫。
- 學會利用if-else的排他特性簡化程式碼(如下面的程式碼),後面的條件裡不能再寫前面條件不成立的等價判斷(如x>30,後面出現x>40,小於30的值也一定小於40),以減少冗餘:
if(month <= 0){
...
}else if(month <= 3){
...
}else if(month <= 6){//month >= 4 && month <= 6
...
}else if(month <= 9){
...
}else if(month <= 12){
...
}else{//month > 12
...
}
- 當if裡面有return語句的時候,else單詞可以被省略。
- 如果遇到類似註釋中這樣只返回true/false的方法,if後面的boolean可以直接返回。
/*
if(x % 2 == 0){
return true;
}else{
return false;
}
*/
return x % 2 == 0;
- 永遠不要拿著一個boolean型別的變數和true做連等比較,比完之後最終的結果都和這個變數的值一模一樣。反之如果與false連等比較,結果相反。
boolean isOk;
//if(isOk == true)
//如果isOk = true -> (true == true) -> true
//如果isOk = false -> (false == true) -> false
//不如直接 ↓↓↓↓↓↓
if(isOk){
// 如果isOk為true
}else{
...
}
=======================================================
//if(isOk == false)
//如果isOk = true -> (true == false) -> false
//如果isOk = false -> (false == false) -> true
//不如直接 ↓↓↓↓↓↓
if(!isOk){
// 如果isOk為false
}else{
...
}
1.1.2 switch-case
- 語法格式:
switch(引數){
case XXX : 執行語句;[break;]
case YYY : 執行語句;[break;]
case ZZZ : 執行語句;[break;[
default : 執行語句;
}
-
執行順序:將引數與每個case進行比對,如果符合條件且語句沒有return/break/continue這樣的終止語句,將向下執行每一個case語句(不管是否符合),如果一直沒有終止語句,將一直執行到default。 所以如果只需要在一個case執行完後出現結果,就一定要加相應的終止語句。
-
終止語句不一定是break,如果是一個有返回值的方法,終止語句可能是return,且此時若有default,其中也要出現return,如果沒有default,switch大括號外面需要有一個return;如果是一個迴圈,終止語句也可能是continue。
-
如果利用得好,上面這個也可以簡化程式碼,提高效率。break共享程式碼,將執行相同操作的程式碼放在一起進行共享:
//定義一個方法 顯示季節
//1-3:春 4-6:夏 7-9:秋 10-12:冬
public static void showSeason(int month){
switch(month){
case 1 :
case 2 :
case 3 :
System.out.println("春");
break;
case 4 :
case 5 :
case 6 :
System.out.println("夏");
break;
case 7 :
case 8 :
case 9 :
System.out.println("秋");
break;
case 10 :
case 11 :
case 12 :
System.out.println("冬");
break;
default :
System.out.println("月份錯誤");
}
}
- 面試題:★ switch-case的引數可以傳哪些資料型別?(重點答版本)
版本 | 資料型別 |
---|---|
jdk1.0及以上 | char、byte、short、int |
jdk5.0及以上 | enum(列舉) |
jdk7.0及以上 | String(字串) |
1.2 迴圈結構
迴圈體中定義的變數為區域性變數,其他流程控制語句中定義的變數也是,結構體完畢後即消亡。
1.2.1 for
如果要執行的迴圈與次數相關,使用for迴圈。
- 語法格式:
for(1;2;3){
4;
}
- 初始化迴圈變數
- 迴圈執行的條件(存放boolean型別的資料,可以使用"&&"、"||"連線判斷)
- 迴圈之後的變化
- 迴圈執行的程式碼
執行順序:1(243243243…243)2,1、2、3為空表示死迴圈。
- 例:
- 列印所有小寫字母
for(char x = 'a';x <= 'z';x++){
System.out.println(x);
}
for(char x = 97;x <= 122;x++){
System.out.println(x);
}
for(int x = 97;x <= 122;x++){
System.out.println((char)x);
}
- 找出100以內8的倍數(效率開發)
for(int x = 1;x <= 100;x++){
if(x % 8 == 0){
System.out.println(x);
}
}
for(int x = 8; x <= 100; x+=8){//儘量減少迴圈次數,提高效率
i++;
}
- for的括號後面不能加“;”,否則會導致空迴圈。
for(int x = 1;x <= 5;x++);{
System.out.println("test");
}
↓↓↓↓↓↓↓↓相當於
for(int x = 1;x <= 5;x++);
{
System.out.println("test");
}
- 當在for/while等條件中使用"&&"、"||"時,需要注意要判斷清楚是否需要迴圈的所有元素都滿足這兩個條件。
//"x&7==0"並不對所有要迴圈值為true,應該放在for內層的if中
for(int x = 2; x <= 100 && ((x & 7) == 0); x++){
System.out.println(x);
}
1.2.2 for-each
用於陣列、集合中。JDK5.0以上才可以使用。
- 語法格式:
for(1 : 2){
3;
}
- 與陣列/集合中的元素資料型別相同的變數,用於取出元素
- 需遍歷的陣列/集合
- 迴圈執行的程式碼
1.2.3 while(當型迴圈)/ do-while(直到型迴圈)
如果要執行的條件相關,使用while/do while迴圈,do-while一般較少使用。
- 語法格式:
//while迴圈
1;
while(2){
3;
4;
}
//do while迴圈
1;
do{
4;
3;
}while(2);
執行順序:
-
while:1(234234234234…)2
-
do while:1(432432432432…)2
- 初始化迴圈變數(一定要在迴圈體外先定義好,不能在括號裡定義,變數在迴圈結束後消亡)
- 迴圈執行的條件(空表示死迴圈,存放boolean型別的資料,可以使用"&&"、"||"連線判斷)
- 迴圈之後的變化
- 迴圈執行的程式碼(3、4沒有順序)
-
例:
- 同for的例題1
//a-z
char y = 'a';
while(y <= 'z'){
System.out.println(y);
y++;
}
-
while和do while之間的區別?
- while先判斷,符合條件再執行
- do while先執行,再判斷,能夠保證程式至少執行一次。
1.2.4 迴圈巢狀、控制和標籤
1.2.4.1 巢狀
一個迴圈定義在另一個迴圈裡面。
- 語法格式:
// 1 2 3
for(int x = 1;x <= 10;x++){
//4
// a b c
for(int y = 1;y <= 10;y++){
//d
}
}
執行順序:124a(bdcbdcbdc…b)324a(bdcbdc…b)324a(bdcbdc…b)32
- 例:
/**打印出
******
******
******
*/
for(int x = 1;x <= 3;x++){//x:行數
for(int y = 1;y <= 6;y++){//y:列數
System.out.print("*");
}//列印一行
System.out.println();//換行
}
/**列印
***** *****
***** *****
***** *****
第1行: 2個空 5個* 1個空 5個*
第2行: 1個空 5個* 3個空 5個*
第3行: 0個空 5個* 5個空 5個*
...
第x行: 3-x空 5個* 2*x-1個空 5個*
*/
for(int x = 1;x <= 3;x++){//行數,內層巢狀按數列看包含哪幾部分列印,按行列印
for(int y = 1;y <= 3-x;y++){
System.out.print(" ");//第一部分 列印(3-x)個空格
}
//for(int y = 1;y <= 5;y++){
// System.out.print("*");
//}
System.out.print("*****");//第二部分,已知每行五個*,固定列印,減少不必要迴圈提高效率
for(int y= 1;y <= 2*x-1;y++){
System.out.print(" ");//第三部分 再列印(2*x-1)個空格
}
//for(int y = 1;y <= 5;y++){
// System.out.print("*");
//}
System.out.print("*****");//第四部分,已知每行五個*,固定列印,減少不必要迴圈提高效率
System.out.println();//換行
}
1.2.4.2 控制
-
continue:表示跳過本次迴圈,開始下一次,跳到所在迴圈的第三部分。
-
break:表示跳出所在的迴圈,跳到所在迴圈的結束部分。
-
例:
for(int x = 1;x <= 6;x++){
if(x == 4){
continue;//表示跳過本次迴圈,開始下一次,跳到所在迴圈的第三部分
}
System.out.println(x);//-->12356
}
for(int x = 1;x <= 6;x++){
if(x == 4){
break;//跳出所在的迴圈 跳到所在迴圈的結束部分
}
System.out.println(x);//-->123
}
注意,continue在dowhile中跳出後,需要判斷的條件語句在最末尾,不要忽略。
/**列印
*******
******
*******
******
*******
continue break
*/
for(int x = 1;x <= 5;x++){//行
for(int y = 1;y <= 7;y++){//列
if(x % 2 == 0 && y == 7){//每到二的倍數行,第七個*總是不列印,跳過本次迴圈,跳到y++開始下一次
continue;
}
System.out.print("*");
}
System.out.println();
}
1.2.4.3 標籤
當我們程式碼寫在內層迴圈想直接操作外層迴圈時,需要給外層迴圈貼標籤(“標籤名:”),然後在內層迴圈裡面“continue/break + 標籤名”。
- 例:
//找出100以內所有質數(找有沒有除了1和本身之外的約數)
//標準
a:for(int x = 2; x <= 100; x++){//被除數
for(int y = 2; y < x; y++){//除數
if(x % y == 0){
//break a 完全停止a所標識的迴圈
//continue a 停止a所標識的迴圈並開始新的迴圈
//如果這裡使用break,只是停止本層迴圈,本層迴圈括號下面(如這裡的System.out)的語句還是會執行
continue a;
}
}
System.out.println(x);
}
//精準,效率高↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
System.out.println(2);//2時最小的質數,直接列印減少迴圈一次
a:for(int x = 3; x <= 100; x+=2){//被除數,質數除了2之外的偶數都不是質數
for(int y = 3; y <= Math.sqrt(x); y+=2){//除數,例:81只需要從3找到9便可以找完所有的約數,且只需要找奇數,因為奇數除以偶數都無法除盡
if(x % y == 0){
continue a;
}
}
System.out.println(x);
}
2 陣列
用來存放一組型別相同,儲存空間連續的資料的容器。不管是區域性還是成員變數,陣列建立時即賦給初值。
- 對於陣列的一些操作:
//如何建立一個數組物件:
int[] data = new int[4]; //5表示底層開闢空間大小
int[] data = new int[]{12,34,56,78};
int[] data = {12,34,56,78};
//輸出這個陣列的值(得到陣列中某個元素)
System.out.println(data[0]);
//輸出陣列長度
System.out.println(data.length);
//如何遍歷陣列物件,如果需要在迴圈時取出指定下標的元素,必須使用for迴圈
//for + 陣列物件下標
for(int x = 0;x < data.length;x++){
//x -> 下標
System.out.println(data[x]);
}
//foreach,使用對應的資料型別變數取出元素
for(int x : data){
//x -> 元素
System.out.println(x);
}
- 陣列的記憶體儲存
作為引用資料型別的其中一種,先理解陣列儲存有助於理解引用資料型別的儲存。Java中的陣列是靜態的,即當陣列被初始化之後,該陣列所佔的記憶體空間、陣列長度都是不可變的。陣列必須經過初始化才可使用。所謂初始化,即建立實際的陣列物件,也就是在記憶體中為陣列物件分配記憶體空間,併為每個陣列元素指定初始值。
//1 僅宣告陣列的引用,但沒有分配記憶體空間,動態初始化
int[] data;
//1 2 宣告陣列的同時,根據指定長度分配記憶體,但此時陣列中沒有值,動態初始化
int[] data = new int[4];
//1 2 3 在面的基礎上宣告陣列並分配記憶體,同時將其初始化,靜態初始化
int[] data = new int[]{12, 34, 56, 78};
//同上
int[] data = {12, 34, 56, 78};
//4
int[] data2 = {12, 34};
上面的程式碼實現的記憶體如圖:
int[] data2 = data;
//輸出此時data2的引用地址
System.out.println(data2);//-->[[email protected]
data2[4] = 20;
System.out.println(data[4]);//-->20
此時的記憶體如下圖,原來的{12,34}陣列會因為不存在指向此物件的引用而被GC執行緒垃圾回收。
-
陣列的複製
- 錯誤的複製方式,只是記憶體裡面的陣列物件多了一個名字而已。
//將data數組裡面的元素賦值到另一個數組裡面
int[] data = new int[]{45,67,82,19};
int[] temp = data;//複製失敗
data[0] = 55;
System.out.println(temp[0]);//-->55
//原因參考前面的記憶體機制,複製完之後兩個陣列不具有獨立性
- 笨重的複製方式,需要逐個手動複製。
int[] data = new int[]{12, 34, 56, 78};
int[] temp = new int[4];
temp[0] = data[0];
temp[1] = data[1];
temp[2] = data[2];
temp[3] = data[3];
data[0] = 55;
System.out.println(temp[0]);//12
- 呼叫"克隆"方法,方法過於死板,不適於正常的應用開發。
int[] data = new int[]{12,34,56,78};
int[] temp = data.clone();
data[0] = 55;
System.out.println(temp[0]);
- System.arraycopy(1,2,3,4,5);
-
引數意義: 1 要複製的原陣列物件 2 原陣列的起始下標位置 3 要複製到的目標陣列 4 目標陣列的起始下標位置 5 原陣列物件需被複制的步長
-
System.arraycopy()的1、3引數可以是同一個陣列物件,從而實現的是一個
-