主頁 > 後端開發 > Java入門

Java入門

2021-01-01 06:14:37 後端開發

Java 入門

Java簡介

Java簡介

https://www.runoob.com/java/java-intro.html

JDK、JRE、JVM

》首先是JDK

JDK(Java Development Kit) 是 Java 語言的軟體開發工具包(SDK), 在JDK的安裝目錄下有一個jre目錄,里面有兩個檔案夾bin和lib,在這里可以認為bin里的就是jvm,lib中則是jvm作業所需要的類別庫,而jvm和 lib合起來就稱為jre,

然后我們來看JRE

JRE(Java Runtime Environment,Java運行環境),包含JVM標準實作及Java核心類別庫,JRE是Java運行環境,并不是一個開發環境,所以沒有包含任何開發工具(如編譯器和除錯器)

最后JVM也一目了然了

JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設備的規范,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實作的,

img

在這里插入圖片描述

開發環境配置

https://www.runoob.com/java/java-environment-setup.html

第一個Java程式

打開文本編輯器,輸入以下代碼:

public class HelloWorld {
    /* 第一個Java程式
     * 它將輸出字串 Hello World
     */
    public static void main(String[] args) {
        System.out.println("Hello World"); // 輸出 Hello World
    }
}

在一個Java程式中,你總能找到一個類似:

public class Hello {
    ...
}

的定義,這個定義被稱為class(類),這里的類名是Hello,大小寫敏感,class用來定義一個類,public表示這個類是公開的,publicclass都是Java的關鍵字,必須小寫,Hello是類的名字,按照習慣,首字母H要大寫,而花括號{}中間則是類的定義,

注意到類的定義中,我們定義了一個名為main的方法:

    public static void main(String[] args) {
        ...
    }

方法是可執行的代碼塊,一個方法除了方法名main,還有用()括起來的方法引數,這里的main方法有一個引數,引數型別是String[],引數名是argspublicstatic用來修飾方法,這里表示它是一個公開的靜態方法,void是方法的回傳型別,而花括號{}中間的就是方法的代碼,

方法的代碼每一行用;結束,這里只有一行代碼,就是:

        System.out.println("Hello, world!");

它用來列印一個字串到螢屏上,

Java規定,某個類定義的public static void main(String[] args)是Java程式的固定入口方法,因此,Java程式總是從main方法開始執行,

注意到Java原始碼的縮進不是必須的,但是用縮進后,格式好看,很容易看出代碼塊的開始和結束,縮進一般是4個空格或者一個tab,

最后,當我們把代碼保存為檔案時,檔案名必須是Hello.java,而且檔案名也要注意大小寫,因為要和我們定義的類名Hello完全保持一致,

注意:類名與檔案名要一致

如何運行Java程式

Java原始碼本質上是一個文本檔案,我們需要先用javacHello.java編譯成位元組碼檔案Hello.class,然后,用java命令執行這個位元組碼檔案:

┌──────────────────┐
│    Hello.java    │<─── source code
└──────────────────┘
          │ compile
          ▼
┌──────────────────┐
│   Hello.class    │<─── byte code
└──────────────────┘
          │ execute
          ▼
┌──────────────────┐
│    Run on JVM    │
└──────────────────┘

因此,可執行檔案javac是編譯器,而可執行檔案java就是虛擬機,

第一步,在保存Hello.java的目錄下執行命令javac Hello.java

$ javac Hello.java

如果源代碼無誤,上述命令不會有任何輸出,而當前目錄下會產生一個Hello.class檔案:

$ ls
Hello.class	Hello.java

第二步,執行Hello.class,使用命令java Hello(這里加.class和不加時一樣的):

$ java Hello
Hello, world!

注意:給虛擬機傳遞的引數Hello是我們定義的類名,虛擬機自動查找對應的class檔案并執行,

如果遇到編碼問題,我們可以使用 -encoding 選項設定 utf-8 來編譯:

javac -encoding UTF-8 HelloWorld.java 
java HelloWorld 

有一些童鞋可能知道,直接運行java Hello.java也是可以的:

$ java Hello.java 
Hello, world!

這是Java 11新增的一個功能,它可以直接運行一個單檔案原始碼!

需要注意的是,在實際專案中,單個不依賴第三方庫的Java原始碼是非常罕見的,所以,絕大多數情況下,我們無法直接運行一個Java原始碼檔案,原因是它需要依賴其他的庫,

使用IDEA運行

https://blog.csdn.net/weixin_43838785/article/details/99105624

IDEA快捷鍵

+ psvm+Enter 快速生成main函式
+ sout+Enter 快速生成`System.out.println();`

小結

一個Java原始碼只能定義一個public型別的class,并且class名稱和檔案名要完全一致;

使用javac可以將.java原始碼編譯成.class位元組碼;

使用java可以運行一個已編譯的Java程式,引數是類名,

Java基礎

注釋

撰寫注釋的原因

撰寫程式時總需要為程式添加一些注釋,用以說明某段代碼的作用,或者說明某個類的用途、某個方法的功能,以及該方法的引數和回傳值的資料型別及意義等,

撰寫注釋的原因及意義如下

  1. 為了更好的閱讀自己撰寫的代碼,建議添加這注釋,自己寫的代碼,可能過一段時間回顧的時候,就變得不熟悉,這個時候,注釋就起到了很好的幫助作用,

  2. 可讀性第一,效率第二,一個軟體一般都是一個團隊協同作戰開發出來的,因此,一個人寫的代碼,需要被整個團隊的其他人所理解,

  3. 代碼即檔案,程式源代碼是程式檔案的重要組成部分,

注釋的語法規則

撰寫Java中的注釋不會出現在可執行程式中,因此,可以在源程式中根據需要添加任意多的注釋,而不必擔心可執行代碼會膨脹,在 Java 中,有三種書寫注釋的方式,

  1. 單行注釋——注釋一行

    以雙斜杠“//”標識,只能注釋一行內容,用在注釋資訊內容少的地方,

    image-20201229170935183

  2. 多行注釋——注釋一段

    包含在“/* ”和“ */”之間,能注釋很多行的內容,為了可讀性比較好,一般首行和尾行不寫注釋資訊(這樣也比較美觀好看),

    image-20201229171103411

    注意:多行注釋可以嵌套單行注釋,但是不能嵌套多行注釋和檔案注釋,

  3. 檔案注釋

    包含在“/** ”和“ */”之間,也能注釋多行內容,一般用在類、方法和變數上面,用來描述其作用,注釋后,滑鼠放在類和變數上面會自動顯示出我們注釋的內容,

    image-20201229171233407

    注意:檔案注釋能嵌套單行注釋,不能嵌套多行注釋和檔案注釋,一般首行和尾行也不寫注釋資訊,

識別符號和關鍵字

Java識別符號定義

  1. 包名、類名、方法名、引數名、變數名等,這些符號被稱為識別符號,

  2. 識別符號可以由字母、數字、下劃線()和美元符號($)組成

  3. 識別符號不能以數字開頭,不能是java中的關鍵字,例如:

    • 正確的識別符號
      Username、username123、user_name、_userName、$username

    • 不正確的識別符號:
      123username、class、87.2、Hello World、num*123

  4. 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下劃線()或數字的任何字符,

  5. 識別符號是大小寫敏感,

Java識別符號規則

  1. 包名所有字母必須小寫,例如:cn.itcast.test
  2. 類名和介面名每個單詞的首字母都要大寫,例如:ArrayList
  3. 常量名所有的字母都大寫,單詞之間用下劃線連接,例如:DAY_OF_MONTH
  4. 變數名和方法名的第一個單詞首字母小寫,從第二個單詞開始,每個單詞首字母大寫,例如:lineName、getLingNumber
  5. 在程式中,應該盡量使用有意義的英文單詞來定義識別符號,使得程式便于閱讀,例如:使用userName表示用戶名,password表示密碼,

Java 關鍵字

  • 下面列出了 Java 關鍵字,這些保留字不能用于常量、變數、和任何識別符號的名稱,
  • 所有的關鍵字都是小寫
  • 程式中的識別符號不能以關鍵字命名
  • const和goto是保留字關鍵字,雖然在java中還沒有任何意義,但在程式中不能用來作為自定義的識別符號,
  • true、false和null不屬于關鍵字,它們是一個單獨標識型別,不能直接使用
類別 關鍵字 說明
訪問控制 private 私有的
訪問控制 protected 受保護的
訪問控制 public 公共的
類、方法和變數修飾符 abstract 宣告抽象
類、方法和變數修飾符 class
類、方法和變數修飾符 extends 擴充,繼承
類、方法和變數修飾符 final 最終值,不可改變的
類、方法和變數修飾符 implements 實作(介面)
類、方法和變數修飾符 interface 介面
類、方法和變數修飾符 native 本地,原生方法(非 Java 實作)
類、方法和變數修飾符 new 新,創建
類、方法和變數修飾符 static 靜態
類、方法和變數修飾符 strictfp 嚴格,精準
類、方法和變數修飾符 synchronized 執行緒,同步
類、方法和變數修飾符 transient 短暫
類、方法和變數修飾符 volatile 易失
程式控制陳述句 break 跳出回圈
程式控制陳述句 case 定義一個值以供 switch 選擇
程式控制陳述句 continue 繼續
程式控制陳述句 default 默認
程式控制陳述句 do 運行
程式控制陳述句 else 否則
程式控制陳述句 for 回圈
程式控制陳述句 if 如果
程式控制陳述句 instanceof 實體
程式控制陳述句 return 回傳
程式控制陳述句 switch 根據值選擇執行
程式控制陳述句 while 回圈
錯誤處理 assert 斷言運算式是否為真
錯誤處理 catch 捕捉例外
錯誤處理 finally 有沒有例外都執行
錯誤處理 throw 拋出一個例外物件
錯誤處理 throws 宣告一個例外可能被拋出
錯誤處理 try 捕獲例外
包相關 import 引入
包相關 package
基本型別 boolean 布爾型
基本型別 byte 位元組型
基本型別 char 字符型
基本型別 double 雙精度浮點
基本型別 float 單精度浮點
基本型別 int 整型
基本型別 long 長整型
基本型別 short 短整型
變數參考 super 父類,超類
變數參考 this 本類
變數參考 void 無回傳值
保留關鍵字 goto 是關鍵字,但不能使用
保留關鍵字 const 是關鍵字,但不能使用
保留關鍵字 null

資料型別

強型別語言

強型別語言也稱為強型別定義語言,是一種總是強制型別定義的語言,要求變數的使用要嚴格符合定義,所有變量都必須先定義后使用,

Java、.Net和C++等一些語言都是強制型別定義的,也就是說,一旦一個變數被指定了某個資料型別,如果不經過強制轉換,那么它就永遠是這個資料型別了,

例如你有一個整數,如果不顯式地進行轉換,你不能將其視為一個字串,

弱型別語言

弱型別語言也稱為弱型別定義語言,與強型別定義相反,像VB,PHP等一些語言就屬于弱型別語言,

簡單理解就是一種變數型別可以被忽略的語言,

比如VBScript是弱型別定義的,在VBScript中就可以將字串'12'和整數3進行連接得到字串'123',然后可以把它看成整數123,而不用顯示轉換,但其實他們的型別沒有改變,VB只是在判斷出一個運算式含有不同型別的變數之后,自動在這些變數前加了一個clong()或(int)()這樣的轉換函式而已,能做到這一點其實是歸功于VB的編譯器的智能化而已,這并非是VB語言本身的長處或短處,

強型別語言和弱型別語言比較

強型別語言在速度上可能略遜色于弱型別語言,但是強型別語言帶來的嚴謹性可以有效地幫助避免許多錯誤,

Java 基本資料型別

變數就是申請記憶體來存盤值,也就是說,當創建變數的時候,需要在記憶體中申請空間,

記憶體管理系統根據變數的型別為變數分配存盤空間,分配的空間只能用來儲存該型別資料,

img

因此,通過定義不同型別的變數,可以在記憶體中儲存整數、小數或者字符,

Java 的兩大資料型別:

  • 內置資料型別

  • 參考資料型別

    image-20201229174340046

內置資料型別

Java語言提供了八種基本型別,六種數字型別(四個整數型,兩個浮點型),一種字符型別,還有一種布爾型,

byte:
  • byte 資料型別是8位、有符號的,以二進制補碼表示的整數;
  • 最小值是 -128(-2^7)
  • 最大值是 127(2^7-1)
  • 默認值是 0
  • byte 型別用在大型陣列中節約空間,主要代替整數,因為 byte 變數占用的空間只有 int 型別的四分之一;
  • 例子:byte a = 100,byte b = -50,
short:
  • short 資料型別是 16 位、有符號的以二進制補碼表示的整數
  • 最小值是 -32768(-2^15)
  • 最大值是 32767(2^15 - 1)
  • Short 資料型別也可以像 byte 那樣節省空間,一個short變數是int型變數所占空間的二分之一;
  • 默認值是 0
  • 例子:short s = 1000,short r = -20000,
int:
  • int 資料型別是32位、有符號的以二進制補碼表示的整數;
  • 最小值是 -2,147,483,648(-2^31)
  • 最大值是 2,147,483,647(2^31 - 1)
  • 一般地整型變數默認為 int 型別;
  • 默認值是 0
  • 例子:int a = 100000, int b = -200000,

long:

  • long 資料型別是 64 位、有符號的以二進制補碼表示的整數;
  • 最小值是 -9,223,372,036,854,775,808(-2^63)
  • 最大值是 9,223,372,036,854,775,807(2^63 -1)
  • 這種型別主要使用在需要比較大整數的系統上;
  • 默認值是 0L
  • 例子: long a = 100000L,Long b = -200000L,
    "L"理論上不分大小寫,但是若寫成"l"容易與數字"1"混淆,不容易分辯,所以最好大寫,
float:
  • float 資料型別是單精度、32位、符合IEEE 754標準的浮點數;
  • float 在儲存大型浮點陣列的時候可節省記憶體空間;
  • 默認值是 0.0f
  • 浮點數不能用來表示精確的值,如貨幣;
  • 例子:float f1 = 234.5f,
double:
  • double 資料型別是雙精度、64 位、符合IEEE 754標準的浮點數;
  • 浮點數的默認型別為double型別;
  • double型別同樣不能表示精確的值,如貨幣;
  • 默認值是 0.0d
  • 例子:double d1 = 123.4,
boolean:
  • boolean資料型別表示一位的資訊;
  • 只有兩個取值:true 和 false;
  • 這種型別只作為一種標志來記錄 true/false 情況;
  • 默認值是 false
  • 例子:boolean one = true,
char:
  • char型別是一個單一的 16 位 Unicode 字符;
  • 最小值是 \u0000(即為 0);
  • 最大值是 \uffff(即為65、535);
  • char 資料型別可以儲存任何字符;
  • 例子:char letter = 'A';
型別默認值

下表列出了 Java 各個型別的默認值:

資料型別 默認值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char 'u0000'
String (or any object) null
boolean false
public class Test {
    static boolean bool;
    static byte by;
    static char ch;
    static double d;
    static float f;
    static int i;
    static long l;
    static short sh;
    static String str;
 
    public static void main(String[] args) {
        System.out.println("Bool :" + bool);
        System.out.println("Byte :" + by);
        System.out.println("Character:" + ch);
        System.out.println("Double :" + d);
        System.out.println("Float :" + f);
        System.out.println("Integer :" + i);
        System.out.println("Long :" + l);
        System.out.println("Short :" + sh);
        System.out.println("String :" + str);
    }
}

image-20201230124053970

對于數值型別的基本型別的取值范圍,我們無需強制去記憶,因為它們的值都已經以常量的形式定義在對應的包裝類中了,請看下面的例子:

public class PrimitiveTypeTest {  
    public static void main(String[] args) {  
        // byte  
        System.out.println("基本型別:byte 二進制位數:" + Byte.SIZE);  
        System.out.println("包裝類:java.lang.Byte");  
        System.out.println("最小值:Byte.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Byte.MIN_VALUE);  
        System.out.println("最大值:Byte.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Byte.MAX_VALUE);  
        System.out.println();  
  
        // short  
        System.out.println("基本型別:short 二進制位數:" + Short.SIZE);  
        System.out.println("包裝類:java.lang.Short");  
        System.out.println("最小值:Short.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Short.MIN_VALUE);  
        System.out.println("最大值:Short.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Short.MAX_VALUE);  
        System.out.println();  
  
        // int  
        System.out.println("基本型別:int 二進制位數:" + Integer.SIZE);  
        System.out.println("包裝類:java.lang.Integer");  
        System.out.println("最小值:Integer.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Integer.MIN_VALUE);  
        System.out.println("最大值:Integer.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Integer.MAX_VALUE);  
        System.out.println();  
  
        // long  
        System.out.println("基本型別:long 二進制位數:" + Long.SIZE);  
        System.out.println("包裝類:java.lang.Long");  
        System.out.println("最小值:Long.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Long.MIN_VALUE);  
        System.out.println("最大值:Long.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Long.MAX_VALUE);  
        System.out.println();  
  
        // float  
        System.out.println("基本型別:float 二進制位數:" + Float.SIZE);  
        System.out.println("包裝類:java.lang.Float");  
        System.out.println("最小值:Float.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Float.MIN_VALUE);  
        System.out.println("最大值:Float.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Float.MAX_VALUE);  
        System.out.println();  
  
        // double  
        System.out.println("基本型別:double 二進制位數:" + Double.SIZE);  
        System.out.println("包裝類:java.lang.Double");  
        System.out.println("最小值:Double.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Double.MIN_VALUE);  
        System.out.println("最大值:Double.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ Double.MAX_VALUE);  
        System.out.println();  
  
        // char  
        System.out.println("基本型別:char 二進制位數:" + Character.SIZE);  
        System.out.println("包裝類:java.lang.Character");  
        // 以數值形式而不是字符形式將Character.MIN_VALUE輸出到控制臺  
        System.out.println("最小值:Character.MIN_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ (int) Character.MIN_VALUE);  
        // 以數值形式而不是字符形式將Character.MAX_VALUE輸出到控制臺  
        System.out.println("最大值:Character.MAX_VALUE="https://www.cnblogs.com/yishiyuntuan/p/+ (int) Character.MAX_VALUE);  
    }  
}

編譯以上代碼輸出結果如下所示:

基本型別:byte 二進制位數:8
包裝類:java.lang.Byte
最小值:Byte.MIN_VALUE=https://www.cnblogs.com/yishiyuntuan/p/-128
最大值:Byte.MAX_VALUE=127

基本型別:short 二進制位數:16
包裝類:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767

基本型別:int 二進制位數:32
包裝類:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647

基本型別:long 二進制位數:64
包裝類:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807

基本型別:float 二進制位數:32
包裝類:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38

基本型別:double 二進制位數:64
包裝類:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308

基本型別:char 二進制位數:16
包裝類:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

資料型別擴展

整數擴展

在JAVA里的進制中

0b開頭二進制 0開頭是八進制 0x開頭是十六進制點數擴展

public static void main(String[] args) {

        int i1 = 10;
        int i2 = 0b10;  //二進制10
        int i3 = 010;   //八進制10
        int i4 = 0x10;  //十六進制10    16進制: 0~9 A~F

        // 全部輸出查看結果
        System.out.println(i1);  //10
        System.out.println(i2); //2
        System.out.println(i3); //8
        System.out.println(i4); //16
        

        System.out.println("二進制輸出" + Integer.toBinaryString(i1));
        System.out.println("八進制輸出" + Integer.toOctalString(i1));
        System.out.printf("八進制輸出" + "%010o\n", i1);
        System.out.printf("十六進制輸出" + "%010x\n", i1);
        System.out.println("十六進制輸出" + Integer.toHexString(i1));
    }

image-20201230130700565

浮點數擴展

  1. 浮點數是一個離散的數,表示的值不精確存在誤差不適合進行數值比較

  2. 浮點數在計算機中是以一種類似于科學計數法的方式(IEEE 754)來存盤和運算的(計算機組成原理),

  3. 浮點數的計算是不精確的,它存在舍入誤差和表示大小溢位的問題,浮點數強制轉型成整數時,會舍棄掉小數,顯示整數的最大值

  4. 浮點數的存盤也是不精確的,它是采用二進制的方式存盤在計算機中,超出精度后溢位的部分就不會被存盤,所以會出現誤差,

    public static void main(String[] args) {
        float f1=0.1F;
        double f2=0.1;
        System.out.println(f1==f2);//false
      
        float f3=888888888888888f;
        float f4=f3+1;
        System.out.println(f3==f4);//ture
    }
    

那么問題也來了,既然我們在程式中使用的浮點數來表示小數的時候,會出現誤差,我們在表示銀行業務的時候,應該用什么來表示賬戶余額才能使其不出現問題呢?
我們可以采用Java中提供的數學工具類BigDecimal類來對其進行表示,

注意:在Java中,最好完全避免使用浮點數來進行比較,

字符擴展

在Java中,我們通常使用''將字符包裹起來用于字符的表示,但實際上,計算機對字符的存盤是一個二進制的整數,每個正整數都對應了一個字符,這一一的對應就是Unicode編碼,Unicode編碼就是一張表,一串列示為數字,一串列示為字符,正是通過這張表將其一一對應,

char ch1 = 'A';
char ch2 = 'a';

System.out.println(ch1);//A
System.out.println((int)ch1);//65

System.out.println(ch2);//a
System.out.println((int)ch2);//97

這里就不得不說到字符中的轉義字符,我們在表示字符的時候,也可以表示成下面的這種形式,其范圍在\u0000~\uFFFF

char ch3 = '\u0061';
System.out.println(ch3);//a

這里的\u就表示Unicode編碼,轉義字符也有很多,常見的有:

\n 回車
\r 換行
\'單引號
\"雙引號
\t水平制表符
\b空格
\\ 反斜杠

布林值擴展

boolean flag = true;
if (flag == true) {}
if (flag) {}

兩個if判斷所表示的意識完全一致,但是后者比前者更為簡潔,者一般就是新手程式員喜歡寫的,而后者一般為老程式員的習慣,我們寫代碼的時候,不僅是要完成業務邏輯的需要,我們寫出的代碼也應該簡潔精煉,在這里就不得不提到Jquery的一句話,write less,do more,寫的更少,做的更多,

型別轉換

自動型別轉換

資料型別自動轉換:將取值范圍小的型別自動轉換為取值范圍大的型別,例如:

一個 int 型別變數和一個 byte 型別變數進行相加,運算的結果是:變數的型別將是 int 型別,

同樣道理,當一個 int 型別變數和一個 double 變數運算時, int 型別將會自動提升為 double 型別進行運算,

img

轉換規則:
范圍小的型別向范圍大的型別提升:byte、short、char 運算時候直接提升為 int,
byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double,

強制型別轉換

強制型別轉換:將 取值范圍大的型別強制轉換成取值范圍小的型別 ,

比較而言,自動轉換是 Java 自動執行的,而強制轉換需要我們自己手動執行,

img

將 1.5 賦值到 int 型別變數:產生編譯失敗,無法賦值,

要修改為:
int i = (int)1.5; //但是這樣會導致 1.5 變成 1,
doubled=(int)2.5; // double型別資料強制轉成int型別,直接去掉小數點,

   public static void main(String[] args) {
        /**
         *  int a=1.5; 編譯失敗,無法賦值
         */
        int i = (int)1.5;
        double d=2.5;
        //int型別和double型別運算,結果是double型別
        //int型別會提升為double型別
        double e = d + i;
        System.out.println(e);
    }

變數、常量、作用域

變數

變數,見名知義,就是可以變化的量,
因為Java是一種強型別的語言,每一個變數都必須宣告它們的型別,它是程式中最基本的存盤單元,其中包括變數名、變數型別和作用域,語法如下:
資料型別 變數名 [= 值] [{,變數名 [ = 值]}...];

//一行中宣告多個同型別的變數 都未賦初始值
int a, b, c;
//一行中宣告多個變數 都賦值
int d = 1, e = 2;
//一行中宣告多個變數 有賦值 有未賦值
int f, g = 3;
//一行中宣告一個變數
int age = 20;
//宣告一個參考型變數
String name = "Ara_Hu";

我們在宣告變數時,為了提高程式的可閱讀性,我們最好不要在一行中宣告多個變數,我們可以一行宣告一個變數,在某些關鍵的變數之前加上注釋,標明這個變數的用途,這樣可以大大提高我們程式的可閱讀性,

注意:

  • 每個變數都需要有型別,型別可以是基本型別,也可以是參考型別
  • 變數名必須是合法的識別符號
  • 變數宣告是一條完整的陳述句,必須以分號結尾
  • 變數在宣告后必須要賦值后才能進行使用

常量

常量,可以理解為一種特殊的變數,它的值初始化之后就不能再進行修改,它是不會變動的值,常量名一般采用大寫字符,

//宣告一個常量
final double PI = 3.14;

我們宣告常量的時候需要使用final關鍵字來對變數進行修飾,在修飾前它是一個變數,但是在通過final是修飾后,它就成為了一個常量,

**注意:常量的值,是不能進行修改的**

作用域

作用域(scope),程式設計概念,通常來說,一段程式代碼中所用到的名字并不總是有效/可用的,而限定這個名字的可用性的代碼范圍就是這個名字的作用域,

變數又分為類變數實體變數區域變數,三種不同型別的變數就對應著不同的作用范圍,就是它們存在著不同的作用域,

  • 類變數:在類中但是獨立于方法之外的變數,用 static 修飾,
  • 實體變數:在類中但是獨立于方法之外的變數,不過沒有 static 修飾,
  • 區域變數:類的方法中的變數,

類變數在本類中任何地方都可直接使用,在其他類中用類名.變數名可使用;
實體變數不論在什么地方都需要先實體化本類,在通過實體化的本類的參考(new的該物件的變數名)來進行使用;
區域變數僅在宣告此變數的那段代碼中可用,一般都是在宣告此變數到最接近宣告此變數之后的}段中有效(比如下面這個圖,變數i僅在兩個紅框之間有效);

image-20201230140522515

類變數:
  • 在本類中可以直接使用,在其他類中可直接通過類名呼叫(ClassName.VariableName)
  • 類變數通常是一個常量,為了方便呼叫,一般在類變數前面添加public和final關鍵字,宣告為公開的常量
  • 類變數在第一次被訪問時創建,在程式結束時銷毀
實體變數:
  • 實體變數在物件創建的時候創建,在物件被銷毀的時候銷毀
  • 實體變數可以通過變數名訪問(就是new出來的物件的變數名)
  • 一般實體變數都會構建為private私有的,然后類中提供對應的getter和setter方法來進行訪問和賦值
區域變數:
  • 僅在宣告它的方法或者代碼塊中有效
  • 當包含區域變數的方法或者代碼塊執行時,變數創建,結束時銷毀
  • 區域變數沒有默認值,所以區域變數在被宣告后,必須初始化才能使用
public class Demo {
    //類變數 在此類加載時就會初始化
    static String name = "Ara_Hu";
    //實體變數 在此類實體化時才會初始化
    int age = 20;
    public static void main(String[] args) {      
        //區域變數 在此方法運行時初始化
        int i = 0;   
    }
}

基本運算子

計算機的最基本用途之一就是執行數學運算,作為一門計算機語言,Java也提供了一套豐富的運算子來操縱變數,我們可以把運算子分成以下幾組:

  • 算術運算子
  • 關系運算子
  • 位運算子
  • 邏輯運算子
  • 賦值運算子
  • 其他運算子

1. 算術運算子

算術運算子在數學運算式中的使用方式與在代數中使用的方式相同,下表列出了算術運算子的使用示例 -

假設整數型別變數A的值為:10,變數B的值為:20,則 -

運算子 描述 示例
+ 加法運算子,第一個運算元加上第二個數運算元 A + B結果為:30
- 減法運算子,從第一個運算元減去第二個運算元 A - B結果為:-10
* 兩個運算元相乘 A * B結果為:200
/ 左運算元除以右運算元回傳模值 B / A結果為:2
% 左運算元除以右運算元回傳余數 B % A結果為:0
++ 將運算元的值增加1 A++,則A的值為:11
-- 將運算元的值減1 A--,則A的值為:9
public class Test {
  public static void main(String[] args) {
     int a = 10;
     int b = 20;
     int c = 25;
     int d = 25;
     System.out.println("a + b = " + (a + b) );
     System.out.println("a - b = " + (a - b) );
     System.out.println("a * b = " + (a * b) );
     System.out.println("b / a = " + (b / a) );
     System.out.println("b % a = " + (b % a) );
     System.out.println("c % a = " + (c % a) );
     System.out.println("a++   = " +  (a++) );
     System.out.println("a--   = " +  (a--) );
     // 查看  d++ 與 ++d 的不同
     System.out.println("d++   = " +  (d++) );
     System.out.println("++d   = " +  (++d) );
  }
}

注意:Java中兩個整數相除,只會輸出結果的整數部分

int a=4;
int b=3;
float c = (float) a/b;
System.out.print(c);//輸出:1

//如果要的到精確的結果,要用下面的方法
int a=4;
int b=3;
float c = (float) a/(float) b;
System.out.print(c);//輸出:1.3333334
自增自減運算子

1、自增(++)自減(--)運算子是一種特殊的算術運算子,在算術運算子中需要兩個運算元來進行運算,而自增自減運算子是一個運算元,

public class selfAddMinus{
    public static void main(String[] args){
        int a = 3;//定義一個變數;
        int b = ++a;//自增運算
        int c = 3;
        int d = --c;//自減運算
        System.out.println("進行自增運算后的值等于"+b);
        System.out.println("進行自減運算后的值等于"+d);
    }
}

運行結果為:

進行自增運算后的值等于4
進行自減運算后的值等于2

決議:

  • int b = ++a; 拆分運算程序為: a=a+1=4; b=a=4, 最后結果為b=4,a=4
  • int d = --c; 拆分運算程序為: c=c-1=2; d=c=2, 最后結果為d=2,c=2

2、前綴自增自減法(++a,--a): 先進行自增或者自減運算,再進行運算式運算,

3、后綴自增自減法(a++,a--): 先進行運算式運算,再進行自增或者自減運算 實體:

public class selfAddMinus{
    public static void main(String[] args){
        int a = 5;//定義一個變數;
        int b = 5;
        int x = 2*++a;
        int y = 2*b++;
        System.out.println("自增運算子前綴運算后a="+a+",x="+x);
        System.out.println("自增運算子后綴運算后b="+b+",y="+y);
    }
}

運行結果為:

自增運算子前綴運算后a=6,x=12
自增運算子后綴運算后b=6,y=10

如何理解?

b=++a就是先執行a=a+1,在執行b=a;

b=*--a是先執行b=a;在執行a=a+1;

 對于以下,有int x = 5, y = 6, z;
題目1:z = ++x + y++;
題目2:z = ++x + x++;
題目3:x = ++x + x++;
對于上面的三道題目,我們下面一一解答,使用的技巧就是:把原始計算式轉化成多個、有先后計算順序的、小的計算式,然后帶入變數的值,進行求解,記住:同一優先級的運算子的計算順序是從右往左,
Q1:z = ++x + y++;  可以轉化為:
              x = x +1;
              z = x + y;
              y = y + 1;
帶入x = 5, y = 6,可得x = 6; z = 12; y = 7; 

Q2:z = ++x + x++;  可以轉化為:
              x = x +1;
              z = x + x;
              x = x + 1;
帶入x = 5,可得x = 6; z = 6+6=12; x = 7;   故x=7,z=12;

Q3:x = ++x + x++;  可以轉化為:
              x = x +1;
              x = x + x;
              x = x + 1;
帶入x = 5,可得x = 5+1=6; x = 6+6=12; x = 12+1=13;   故x=13,

溢位
整數溢位

要特別注意,整數由于存在范圍限制,如果計算結果超出了范圍,就會產生溢位,而溢位不會出錯,卻會得到一個奇怪的結果:

public class Main {
    public static void main(String[] args) {
        int x = 2147483640;
        int y = 15;
        int sum = x + y;
        System.out.println(sum); // -2147483641
    }
}

要解釋上述結果,我們把整數214748364015換成二進制做加法:

  0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
  1000 0000 0000 0000 0000 0000 0000 0111

由于最高位計算結果為1,因此,加法結果變成了一個負數,

要解決上面的問題,可以把int換成long型別,由于long可表示的整型范圍更大,所以結果就不會溢位:

long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum); // 2147483655
浮點數溢位

整數運算在除數為0時會報錯,而浮點數運算在除數為0時,不會報錯,但會回傳幾個特殊值:

  • NaN表示Not a Number
  • Infinity表示無窮大
  • -Infinity表示負無窮大

例如:

double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity

這三種特殊值在實際運算中很少碰到,我們只需要了解即可,

2. 關系運算子

Java語言支持以下關系運算子,假設變數A的值是10,變數B的值是20,則 -

運算子 描述 示例
== 等于運算子,檢查兩個運算元的值是否相等,如果相等,則條件變為真, A==B結果為假,
!= 不等于運算子,檢查兩個運算元的值是否不相等,如果不相等,則條件變為真, A!=B結果為真,
> 大于運算子,檢查左運算元的值是否大于右運算元的值,如果大于,則條件變為真, A>B結果為假,
< 小于運算子,檢查左運算元的值是否小于右運算元的值,如果小于,則條件變為真, A<B結果為真,
>= 大于或等于運算子,檢查左運算元的值是否大于等于右運算元的值,如果大于或等于,則條件變為真, A>=B結果為假,
<= 小于或等于運算子,檢查左運算元的值是否小于或等于右運算元的值,如果小于或等于,則條件變為真, A<=B結果為真,
public class Test {
 
  public static void main(String[] args) {
     int a = 10;
     int b = 20;
     System.out.println("a == b = " + (a == b) );
     System.out.println("a != b = " + (a != b) );
     System.out.println("a > b = " + (a > b) );
     System.out.println("a < b = " + (a < b) );
     System.out.println("b >= a = " + (b >= a) );
     System.out.println("b <= a = " + (b <= a) );
  }
}

a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false

3. 按位運算子

移位運算

在計算機中,整數總是以二進制的形式表示,例如,int型別的整數7使用4位元組表示的二進制如下:

00000000 0000000 0000000 00000111

可以對整數進行移位運算,對整數7左移1位將得到整數14,左移兩位將得到整數28

int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n << 1;  // 00000000 00000000 00000000 00001110 = 14
int b = n << 2;  // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912

左移29位時,由于最高位變成1,因此結果變成了負數,

類似的,對整數28進行右移,結果如下:

int n = 7;       // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1;  // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2;  // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3;  // 00000000 00000000 00000000 00000000 = 0

如果對一個負數進行右移,最高位的1不動,結果仍然是一個負數:

int n = -536870912;
int a = n >> 1;  // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2;  // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1

還有一種無符號的右移運算,使用>>>,它的特點是不管符號位,右移后高位總是補0,因此,對一個負數進行>>>右移,它會變成正數,原因是最高位的1變成了0

int n = -536870912;
int a = n >>> 1;  // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2;  // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1

byteshort型別進行移位時,會首先轉換為int再進行位移,

仔細觀察可發現,左移實際上就是不斷地×2,右移實際上就是不斷地÷2,

位運算

位運算是按位進行與、或、非和異或的運算,

與運算的規則是,必須兩個數同時為1,結果才為1

n = 0 & 0; // 0
n = 0 & 1; // 0
n = 1 & 0; // 0
n = 1 & 1; // 1

或運算的規則是,只要任意一個為1,結果就為1

n = 0 | 0; // 0
n = 0 | 1; // 1
n = 1 | 0; // 1
n = 1 | 1; // 1

非運算的規則是,01互換:

n = ~0; // 1
n = ~1; // 0

異或運算的規則是,如果兩個數不同,結果為1,否則為0

n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0

對兩個整數進行位運算,實際上就是按位對齊,然后依次對每一位進行運算,例如:

// 位運算 
public class Main {
    public static void main(String[] args) {
        int i = 167776589; // 00001010 00000000 00010001 01001101
        int n = 167776512; // 00001010 00000000 00010001 00000000
        System.out.println(i & n); // 167776512
    }
}

	a = 0011 1100
	b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a  = 1100 0011

下面的表中列出了按位運算子,假設整數變數A=60,變數B=13,那么 -

運算子 描述 示例
& 二進制AND運算子,如果存在于兩個運算元中,則它會將結果復制到結果中, A & B的結果為:12,也就是:0000 1100
| 二進制OR運算子,如果存在于任一運算元中,則復制一位, A Ι B 的結果為:61,也就是:0011 1101
^ 二進制異或運算子,如果在一個運算元中設定但不在兩個運算元中設定,則復制該位, A ^ B的結果為:49,也就是:0011 0001
~ 二元一元補充運算子是一元的,具有“翻轉”位的效果, ~A的結果為:-61,也就是:1100 0011
<< 二進制左移運算子,左運算元值向左移動右運算元指定的位數, A << 2的結果為:240,也就是:1111 0000
>> 二進制右移運算子,左運算元值向右移動右運算元指定的位數, A >> 2的結果為:15,也就是:1111
>>> 右移零填充運算子, 左運算元值向右移動右運算元指定的位數,移位值用零填充, A >>>2的結果為:15,也就是:0000 1111

4. 邏輯運算子

下表列出了邏輯運算子 -

假設布爾變數A的值為:true,變數B 的值為:false,則 -

運算子 描述 示例
&& 邏輯AND運算子, 如果兩個運算元都不為零,則條件成立, (A && B)結果為:false
|| 邏輯OR運算子, 如果兩個運算元中的任何一個非零,則條件變為真, (A || B)結果為:true
! 邏輯非運算子,用于反轉其運算元的邏輯狀態, 如果條件為真,則口邏輯NOT運算子將為false !(A && B)結果為:true
短路運算

布爾運算的一個重要特點是短路運算,如果一個布爾運算的運算式能提前確定結果,則后續的計算不再執行,直接回傳結果,

因為false && x的結果總是false,無論xtrue還是false,因此,與運算在確定第一個值為false后,不再繼續計算,而是直接回傳false

public class Main {
    public static void main(String[] args) {
        boolean b = 5 < 3;
        boolean result = b && (5 / 0 > 0);
        System.out.println(result);
    }
}

如果沒有短路運算,&&后面的運算式會由于除數為0而報錯,但實際上該陳述句并未報錯,原因在于與運算是短路運算子,提前計算出了結果false

如果變數b的值為true,則運算式變為true && (5 / 0 > 0),因為無法進行短路運算,該運算式必定會由于除數為0而報錯,可以自行測驗,

類似的,對于||運算,只要能確定第一個值為true,后續計算也不再進行,而是直接回傳true

boolean result = true || (5 / 0 > 0); // true

5. 賦值運算子

以下是Java語言支持的賦值運算子 -

運算子 描述 示例
= 簡單賦值運算子, 將右側運算元的值分配給左側運算元, C = A + BA + B的值分配給C
+= 相加與賦值運算子, 它將右運算元相加到左運算元并將結果分配給左運算元, C += A等于C = C + A
-= 減去與賦值運算子, 它從左運算元中減去右運算元,并將結果賦給左運算元, C -= A等于C = C - A
*= 乘以與賦值運算子, 它將右運算元與左運算元相乘,并將結果賦給左運算元, C *= A等于C = C * A
/= 除以與賦值運算子, 它將左運算元除以右運算元,并將結果賦給左運算元, C /= A等于C = C / A
%= 模數與賦值運算子, 它使用兩個運算元來計算獲取模數,并將結果賦給左運算元, C %= A等于C = C % A
<<= 左移與賦值運算子, C <<= 2C = C << 2相同
>>= 右移與賦值運算子, C >>= 2C = C >> 2相同
&= 按位與賦值運算子, C &= 2C = C & 2相同
^= 按位異或和賦值運算子, C ^= 2C = C ^ 2相同
|= 按位包含或與賦值運算子, C|= 2C = C|2相同

6. 其它運算子

Java語言支持的其他運算子很少,

6.1. 條件運算子(???

條件運算子也稱為三元運算子, 此運算子由三個操作陣列成,用于計算布爾運算式, 運算子的目標是確定應將哪個值賦給變數, 運算子寫成 -

variable x = (expression) ? value if true : value if false

下面是一段示例代碼:

public class Test {
   public static void main(String args[]) {
      int a, b;
      a = 10;
      b = (a == 1) ? 20: 30;
      System.out.println( "Value of b is : " +  b );

      b = (a == 10) ? 20: 30;
      System.out.println( "Value of b is : " + b );
   }
}

執行上面示例代碼,得到以下結果 -

image-20201230202234065

6.2. instanceof運算子
此運算子僅用于物件參考變數, 運算子檢查物件是否屬于特定型別(型別別或介面型別), instanceof運算子寫成 -

( Object reference variable ) instanceof  (class/interface type)

如果運算子左側的變數參考的物件是右側的類/介面型別,則結果為真, 以下是一個例子 -

public class Test {
   public static void main(String args[]) {
      String name = "haha";
      // 當 name 的型別是 String 時,則回傳為:true
      boolean result = name instanceof String;
      System.out.println( result );
   }
}

執行上面示例代碼,得到以下結果:

true

如果要比較的物件與右側型別兼容,則此運算子仍將回傳true, 以下是另一個例子 -

class Vehicle {}
public class Car extends Vehicle {
   public static void main(String args[]) {
      Vehicle a = new Car();
      boolean result =  a instanceof Car;
      System.out.println( result );
   }
}

執行上在示例代碼,得到以下結果:

true

Java運算子優先級

在Java的計算運算式中,運算優先級從高到低依次是:

  • ()
  • ! ~ ++ --
  • * / %
  • + -
  • << >> >>>
  • &
  • |
  • += -= *= /=

記不住也沒關系,只需要加括號就可以保證運算的優先級正確,

下表中具有最高優先級的運算子在的表的最上面,最低優先級的在表的底部,

類別 運算子 關聯性
后綴 () [] . (點運算子) 左到右
一元 expr++ expr-- 從左到右
一元 ++expr --expr + - ~ ! 從右到左
乘性 * /% 左到右
加性 + - 左到右
移位 >> >>> << 左到右
關系 > >= < <= 左到右
相等 == != 左到右
按位與 左到右
按位異或 ^ 左到右
按位或 | 左到右
邏輯與 && 左到右
邏輯或 | | 左到右
條件 ?: 從右到左
賦值 = + = - = * = / =%= >> = << =&= ^ = | = 從右到左
逗號 左到右

流程控制

輸入和輸出

輸出

在前面的代碼中,我們總是使用System.out.println()來向螢屏輸出一些內容,

println是print line的縮寫,表示輸出并換行,因此,如果輸出后不想換行,可以用print()

public class Main {
   public static void main(String[] args) {
       System.out.print("A,");
       System.out.print("B,");
       System.out.print("C.");
       System.out.println();
       System.out.println("END");
   }
}

image-20201230203331449

格式化輸出

Java還提供了格式化輸出的功能,為什么要格式化輸出?因為計算機表示的資料不一定適合人來閱讀:

public class Main {
    public static void main(String[] args) {
        double d = 12900000;
        System.out.println(d); // 1.29E7
    }
}

如果要把資料顯示成我們期望的格式,就需要使用格式化輸出的功能,格式化輸出使用System.out.printf(),通過使用占位符%?printf()可以把后面的引數格式化成指定格式:

public class Main {
    public static void main(String[] args) {
        double d = 3.1415926;
        System.out.printf("%.2f\n", d); // 顯示兩位小數3.14
        System.out.printf("%.4f\n", d); // 顯示4位小數3.1416
    }
}

Java的格式化功能提供了多種占位符,可以把各種資料型別“格式化”成指定的字串:

占位符 說明
%d 格式化輸出整數
%x 格式化輸出十六進制整數
%f 格式化輸出浮點數
%e 格式化輸出科學計數法表示的浮點數
%s 格式化字串

注意,由于%表示占位符,因此,連續兩個%%表示一個%字符本身,

占位符本身還可以有更詳細的格式化引數,下面的例子把一個整數格式化成十六進制,并用0補足8位:

public class Main {
    public static void main(String[] args) {
        int n = 12345000;
        System.out.printf("n=%d, hex=%08x", n, n); // 注意,兩個%占位符必須傳入兩個數
    }
}

詳細的格式化引數請參考JDK檔案java.util.Formatter

輸入

和輸出相比,Java的輸入就要復雜得多,

java.util.Scanner 是 Java5 的新特征,我們可以通過 Scanner 類來獲取用戶的輸入,

下面是創建 Scanner 物件的基本語法(注意:通過(import陳述句匯入java.util.Scanner):

Scanner s = new Scanner(System.in);

有了Scanner物件后,通過 Scanner 類的 next() nextLine() 方法獲取輸入的字串,在讀取前我們一般需要 使用 hasNext hasNextLine 判斷是否還有輸入的資料:

使用 next 方法:
import java.util.Scanner; 
 
public class ScannerDemo {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        // 從鍵盤接收資料
        // next方式接收字串
        System.out.println("next方式接收:");
        // 判斷是否還有輸入
        if (scan.hasNext()) {
            String str1 = scan.next();
            System.out.println("輸入的資料為:" + str1);
        }
        scan.close();//關閉Scanner物件節省資源
    }
}

執行以上程式輸出結果為:

image-20201230205202769

$ javac ScannerDemo.java
$ java ScannerDemo
next方式接收:
runoob com
輸入的資料為:runoob

可以看到 java 字串并未輸出,接下來我們看 nextLine,

使用 nextLine 方法:
import java.util.Scanner;
 
public class ScannerDemo {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        // 從鍵盤接收資料
 
        // nextLine方式接收字串
        System.out.println("nextLine方式接收:");
        // 判斷是否還有輸入
        if (scan.hasNextLine()) {
            String str2 = scan.nextLine();
            System.out.println("輸入的資料為:" + str2);
        }
        scan.close();
    }
}

image-20201230205326791

可以看到 java 字串輸出,

next() 與 nextLine() 區別

next():

  • 1、一定要讀取到有效字符后才可以結束輸入,
  • 2、對輸入有效字符之前遇到的空白,next() 方法會自動將其去掉,
  • 3、只有輸入有效字符后才將其后面輸入的空白作為分隔符或者結束符,
  • next() 不能得到帶有空格的字串,

nextLine():

  • 1、以Enter為結束符,也就是說 nextLine()方法回傳的是輸入回車之前的所有字符,
  • 2、可以獲得空白,

如果要輸入 int 或 float 型別的資料,在 Scanner 類中也有支持,但是在輸入之前最好先使用 hasNextXxx() 方法進行驗證,再使用 nextXxx() 來讀取:

nextBigDecimal()
nextByte()
nextShort()
nextInt()
nextLong()
nextFloat()
nextDouble()
nextBoolean()

這些方法用于讀取不同型別的資料,

import java.util.Scanner;
 
public class ScannerDemo {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        // 從鍵盤接收資料
        int i = 0;
        float f = 0.0f;
        System.out.print("輸入整數:");
        if (scan.hasNextInt()) {
            // 判斷輸入的是否是整數
            i = scan.nextInt();
            // 接收整數
            System.out.println("整數資料:" + i);
        } else {
            // 輸入錯誤的資訊
            System.out.println("輸入的不是整數!");
        }
        System.out.print("輸入小數:");
        if (scan.hasNextFloat()) {
            // 判斷輸入的是否是小數
            f = scan.nextFloat();
            // 接收小數
            System.out.println("小數資料:" + f);
        } else {
            // 輸入錯誤的資訊
            System.out.println("輸入的不是小數!");
        }
        scan.close();
    }
}
輸入總結:

輸入分三步:

  1. 創建Scanner物件
  2. 獲取用戶輸入
  3. 關閉Scanner物件

Java回圈結構

順序結構的程式陳述句只能被執行一次,如果您想要同樣的操作執行多次,,就需要使用回圈結構,

順序結構的程式陳述句只能被執行一次,如果您想要同樣的操作執行多次,,就需要使用回圈結構,

Java中有三種主要的回圈結構:

  • while 回圈
  • do…while 回圈
  • for 回圈

在Java5中引入了一種主要用于陣列的增強型for回圈,

while 回圈

while是最基本的回圈,它的結構為:

while( 布爾運算式 ) { 
  //回圈內容 
}

只要布爾運算式為 true,回圈就會一直執行下去,

public class Main {
    public static void main(String[] args) {
        int sum = 0; // 累加的和,初始化為0
        int n = 1;
        while (n <= 100) { // 回圈條件是n <= 100
            sum = sum + n; // 把n累加到sum中
            n++; // n自身加1
        }
        System.out.println(sum); // 5050
    }
}

注意到while回圈是先判斷回圈條件,再回圈,因此,有可能一次回圈都不做,

對于回圈條件判斷,以及自增變數的處理,要特別注意邊界條件,思考一下將下面的代碼為何沒有獲得正確結果(5051)

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        int n = 0;
        while (n <= 100) {
            n ++;
            sum = sum + n;
        }
        System.out.println(sum);
    }
}

5151

改為下面可得到正確結果

n ++;
sum = sum + n;

如果回圈條件永遠滿足,那這個回圈就變成了死回圈,死回圈將導致100%的CPU占用,用戶會感覺電腦運行緩慢,所以要避免撰寫死回圈代碼,

如果回圈條件的邏輯寫得有問題,也會造成意料之外的結果:

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        int n = 1;
        while (n > 0) {
            sum = sum + n;
            n ++;
        }
        System.out.println(n); // -2147483648
        System.out.println(sum);
    }
}

表面上看,上面的while回圈是一個死回圈,但是,Java的int型別有最大值,達到最大值后,再加1會變成負數,結果,意外退出了while回圈,

do…while 回圈

對于 while 陳述句而言,如果不滿足條件,則不能進入回圈,但有時候我們需要即使不滿足條件,也至少執行一次,

do…while 回圈和 while 回圈相似,不同的是,do…while 回圈至少會執行一次,

do {
       //代碼陳述句
}while(布爾運算式);

注意:布爾運算式在回圈體的后面,所以陳述句塊在檢測布爾運算式之前已經執行了, 如果布爾運算式的值為 true,則陳述句塊一直執行,直到布爾運算式的值為 false,

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        int n = 1;
        do {
            sum = sum + n;
            n ++;
        } while (n <= 100);
        System.out.println(sum);
    }
}

使用do while回圈時,同樣要注意回圈條件的判斷,

for回圈

除了whiledo while回圈,Java使用最廣泛的是for回圈,

for回圈的功能非常強大,它使用計數器實作回圈,for回圈會先初始化計數器,然后,在每次回圈前檢測回圈條件,在每次回圈后更新計數器,計數器變數通常命名為i

for回圈執行的次數是在執行前就確定的,語法格式如下:

for(初始化; 布爾運算式; 更新) {    
  //代碼陳述句 
}

關于 for 回圈有以下幾點說明:

  • 最先執行初始化步驟,可以宣告一種型別,但可初始化一個或多個回圈控制變數,也可以是空陳述句,
  • 然后,檢測布爾運算式的值,如果為 true,回圈體被執行,如果為false,回圈終止,開始執行回圈體后面的陳述句,
  • 執行一次回圈后,更新回圈控制變數,
  • 再次檢測布爾運算式,回圈執行上面的程序,
public class Main {
    public static void main(String[] args) {
        int sum = 0;
        for (int i=1; i<=100; i++) {
            sum = sum + i;
        }
        System.out.println(sum);
    }
}

注意for回圈的初始化計數器總是會被執行,并且for回圈也可能回圈0次,

使用for回圈時,千萬不要在回圈體內修改計數器!在回圈體中修改計數器常常導致莫名其妙的邏輯錯誤,對于下面的代碼:

public class Main {
    public static void main(String[] args) {
        int[] ns = { 1, 4, 9, 16, 25 };
        for (int i=0; i<ns.length; i++) {
            System.out.println(ns[i]);
            i = i + 1;
        }
    }
}

雖然不會報錯,但是,陣列元素只列印了一半,原因是回圈內部的i = i + 1導致了計數器變數每次回圈實際上加了2(因為for回圈還會自動執行i++),因此,在for回圈中,不要修改計數器的值,計數器的初始化、判斷條件、每次回圈后的更新條件統一放到for()陳述句中可以一目了然,

如果希望只訪問索引為奇數的陣列元素,應該把for回圈改寫為:

int[] ns = { 1, 4, 9, 16, 25 };
for (int i=0; i<ns.length; i=i+2) {
    System.out.println(ns[i]);
}

通過更新計數器的陳述句i=i+2就達到了這個效果,從而避免了在回圈體內去修改變數i

使用for回圈時,計數器變數i要盡量定義在for回圈中,如果變數i定義在for回圈外,那么,退出for回圈后,變數i仍然可以被訪問,這就破壞了變數應該把訪問范圍縮到最小的原則,

使用for回圈時,計數器變數i要盡量定義在for回圈中:

int[] ns = { 1, 4, 9, 16, 25 };
for (int i=0; i<ns.length; i++) {
   System.out.println(ns[i]);
}
// 無法訪問i
int n = i; // compile error!

如果變數i定義在for回圈外:

int[] ns = { 1, 4, 9, 16, 25 };
int i;
for (i=0; i<ns.length; i++) {
   System.out.println(ns[i]);
}
// 仍然可以使用i
int n = i;

那么,退出for回圈后,變數i仍然可以被訪問,這就破壞了變數應該把訪問范圍縮到最小的原則,

靈活使用for回圈

for回圈還可以缺少初始化陳述句、回圈條件和每次回圈更新陳述句,例如:

// 不設定結束條件:
for (int i=0; ; i++) {
   ...
}
// 不設定結束條件和更新陳述句:
for (int i=0; ;) {
   ...
}
// 什么都不設定:
for (;;) {
   ...
}

通常不推薦這樣寫,但是,某些情況下,是可以省略for回圈的某些陳述句的,

Java 增強 for 回圈(for each回圈)

Java5 引入了一種主要用于陣列的增強型 for 回圈,

Java 增強 for 回圈語法格式如下:

for(宣告陳述句 : 運算式) {
  //代碼句子 
}

宣告陳述句:宣告新的區域變數,該變數的型別必須和陣列元素的型別匹配,其作用域限定在回圈陳述句塊,其值與此時陣列元素的值相等,

運算式:運算式是要訪問的陣列名,或者是回傳值為陣列的方法,

除了陣列外,for each回圈能夠遍歷所有“可迭代”的資料型別,包括后面會介紹的ListMap等,

break和continue

無論是while回圈還是for回圈,有兩個特別的陳述句可以使用,就是break陳述句和continue陳述句,

break 關鍵字

break 主要用在回圈陳述句或者 switch 陳述句中,用來跳出整個陳述句塊,

在回圈程序中,可以使用break陳述句跳出當前回圈,

break 跳出最里層的回圈,并且繼續執行該回圈下面的陳述句,

public class Main {
    public static void main(String[] args) {
        for (int i=1; i<=10; i++) {
            System.out.println("i = " + i);
            for (int j=1; j<=10; j++) {
                System.out.println("j = " + j);
                if (j >= i) {
                    break;
                }
            }
            // break跳到這里
            System.out.println("breaked");
        }
    }
}

上面的代碼是兩個for回圈嵌套,因為break陳述句位于內層的for回圈,因此,它會跳出內層for回圈,但不會跳出外層for回圈,

continue 關鍵字

break會跳出當前回圈,也就是整個回圈都不會執行了,而continue則是提前結束本次回圈,直接繼續執行下次回圈,

continue 適用于任何回圈控制結構中,作用是讓程式立刻跳轉到下一次回圈的迭代,

在 for 回圈中,continue 陳述句使程式立即跳轉到更新陳述句,

在 while 或者 do…while 回圈中,程式立即跳轉到布爾運算式的判斷陳述句,

public class Main {
    public static void main(String[] args) {
        int sum = 0;
        for (int i=1; i<=10; i++) {
            System.out.println("begin i = " + i);
            if (i % 2 == 0) {
                continue; // continue陳述句會結束本次回圈
            }
            sum = sum + i;
            System.out.println("end i = " + i);
        }
        System.out.println(sum); // 25
    }
}
小結

break陳述句可以跳出當前回圈;

break陳述句通常配合if,在滿足條件時提前結束整個回圈;

break陳述句總是跳出最近的一層回圈;

continue陳述句可以提前結束本次回圈;

continue陳述句通常配合if,在滿足條件時提前結束本次回圈,

Java選擇結構

Java 條件陳述句 - if...else

if

在Java程式中,如果要根據條件來決定是否執行某一段代碼,就需要if陳述句,

if陳述句的基本語法是:

if (條件) {
    // 條件滿足時執行
}

根據if的計算結果(true還是false),JVM決定是否執行if陳述句塊(即花括號{}包含的所有陳述句),

當if陳述句塊只有一行陳述句時,可以省略花括號{},但是,省略花括號并不總是一個好主意,假設某個時候,突然想給if陳述句塊增加一條陳述句時:

public class Main {
    public static void main(String[] args) {
        int n = 50;
        if (n >= 60)
            System.out.println("及格了");
            System.out.println("恭喜你"); // 注意這條陳述句不是if陳述句塊的一部分
        System.out.println("END");
    }
}

由于使用縮進格式,很容易把兩行陳述句都看成if陳述句的執行塊,但實際上只有第一行陳述句是if的執行塊,在使用git這些版本控制系統自動合并時更容易出問題,所以不推薦忽略花括號的寫法,

else

if陳述句還可以撰寫一個else { ... },當條件判斷為false時,將執行else的陳述句塊:

public class Main {
    public static void main(String[] args) {
        int n = 70;
        if (n >= 60) {
            System.out.println("及格了");
        } else {
            System.out.println("掛科了");
        }
        System.out.println("END");
    }
}

注意,else不是必須的,

還可以用多個if ... else if ...串聯,例如:

public class Main {
    public static void main(String[] args) {
        int n = 70;
        if (n >= 90) {
            System.out.println("優秀");
        } else if (n >= 60) {
            System.out.println("及格了");
        } else {
            System.out.println("掛科了");
        }
        System.out.println("END");
    }
}

串聯的效果其實相當于:

if (n >= 90) {
    // n >= 90為true:
    System.out.println("優秀");
} else {
    // n >= 90為false:
    if (n >= 60) {
        // n >= 60為true:
        System.out.println("及格了");
    } else {
        // n >= 60為false:
        System.out.println("掛科了");
    }
}

在串聯使用多個if時,要特別注意判斷順序,觀察下面的代碼:

public class Main {
    public static void main(String[] args) {
        int n = 100;
        if (n >= 60) {
            System.out.println("及格了");
        } else if (n >= 90) {
            System.out.println("優秀");
        } else {
            System.out.println("掛科了");
        }
    }
}

執行發現,n = 100時,滿足條件n >= 90,但輸出的不是"優秀",而是"及格了",原因是if陳述句從上到下執行時,先判斷n >= 60成功后,后續else不再執行,因此,if (n >= 90)沒有機會執行了,

正確的方式是按照判斷范圍從大到小依次判斷:

// 從大到小依次判斷:
if (n >= 90) {
    // ...
} else if (n >= 60) {
    // ...
} else {
    // ...
}

或者改寫成從小到大依次判斷:

// 從小到大依次判斷:
if (n < 60) {
    // ...
} else if (n < 90) {
    // ...
} else {
    // ...
}

使用if時,還要特別注意邊界條件,例如:

public class Main {
    public static void main(String[] args) {
        int n = 90;
        if (n > 90) {
            System.out.println("優秀");
        } else if (n >= 60) {
            System.out.println("及格了");
        } else {
            System.out.println("掛科了");
        }
    }
}

假設我們期望90分或更高為“優秀”,上述代碼輸出的卻是“及格”,原因是>>=效果是不同的,

前面講過了浮點數在計算機中常常無法精確表示,并且計算可能出現誤差,因此,判斷浮點數相等用==判斷不靠譜:

public class Main {
    public static void main(String[] args) {
        double x = 1 - 9.0 / 10;
        if (x == 0.1) {
            System.out.println("x is 0.1");
        } else {
            System.out.println("x is NOT 0.1");
        }
    }
}

正確的方法是利用差值小于某個臨界值來判斷:

public class Main {
    public static void main(String[] args) {
        double x = 1 - 9.0 / 10;
        if (Math.abs(x - 0.1) < 0.00001) {
            System.out.println("x is 0.1");
        } else {
            System.out.println("x is NOT 0.1");
        }
    }
}
判斷參考型別相等

在Java中,判斷值型別的變數是否相等,可以使用==運算子,但是,判斷參考型別的變數是否相等,==表示“參考是否相等”,或者說,是否指向同一個物件,例如,下面的兩個String型別,它們的內容是相同的,但是,分別指向不同的物件,用==判斷,結果為false

public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO".toLowerCase();
        System.out.println(s1);
        System.out.println(s2);
        if (s1 == s2) {
            System.out.println("s1 == s2");
        } else {
            System.out.println("s1 != s2");
        }
    }
}

變數都是直接存盤的數值,所以用==比較的時候結果是true,

  而對于非基本資料型別的變數,在一些書籍中稱作為 參考型別的變數,比如上面的s1就是參考型別的變數,參考型別的變數存盤的并不是 “值”本身,而是于其關聯的物件在記憶體中的地址,

? 用(==)進行比較的時候,比較的是他們在記憶體中的存放地址,所以,除非是同一個new出來的物件,他們的比較后的結果為true,否則比較后結果為false,

要判斷參考型別的變數內容是否相等,必須使用equals()方法:

public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "HELLO".toLowerCase();
        System.out.println(s1);
        System.out.println(s2);
        if (s1.equals(s2)) {
            System.out.println("s1 equals s2");
        } else {
            System.out.println("s1 not equals s2");
        }
    }
}

注意:執行陳述句s1.equals(s2)時,如果變數s1null,會報NullPointerException

public class Main {
    public static void main(String[] args) {
        String s1 = null;
        if (s1.equals("hello")) {
            System.out.println("hello");
        }
    }
}

要避免NullPointerException錯誤,可以利用短路運算子&&

public class Main {
    public static void main(String[] args) {
        String s1 = null;
        if (s1 != null && s1.equals("hello")) {
            System.out.println("hello");
        }
    }
}

還可以把一定不是null的物件"hello"放到前面:例如:if ("hello".equals(s)) { ... }

switch多重選擇

除了if陳述句外,還有一種條件判斷,是根據某個運算式的結果,分別去執行不同的分支,

例如,在游戲中,讓用戶選擇選項:

  1. 單人模式
  2. 多人模式
  3. 退出游戲

這時,switch陳述句就派上用場了,

switch陳述句根據switch (運算式)計算的結果,跳轉到匹配的case結果,然后繼續執行后續陳述句,直到遇到break結束執行,

switch case 陳述句語法格式如下

switch(expression){
    case value :
       //陳述句
       break; //可選
    case value :
       //陳述句
       break; //可選
    //你可以有任意數量的case陳述句
    ······
    default : //可選
       //陳述句
}

switch case 陳述句有如下規則:

  • switch 陳述句中的變數型別可以是: byte、short、int 或者 char,從 Java SE 7 開始,switch 支持字串 String 型別了,同時 case 標簽必須為字串常量或字面量,
  • switch 陳述句可以擁有多個 case 陳述句,每個 case 后面跟一個要比較的值和冒號,
  • case 陳述句中的值的資料型別必須與變數的資料型別相同,而且只能是常量或者字面常量,
  • 當變數的值與 case 陳述句的值相等時,那么 case 陳述句之后的陳述句開始執行,直到 break 陳述句出現才會跳出 switch 陳述句,
  • 當遇到 break 陳述句時,switch 陳述句終止,程式跳轉到 switch 陳述句后面的陳述句執行,case 陳述句不必須要包含 break 陳述句,如果沒有 break 陳述句出現,程式會繼續執行下一條 case 陳述句,直到出現 break 陳述句,
  • switch 陳述句可以包含一個 default 分支,該分支一般是 switch 陳述句的最后一個分支(可以在任何位置,但建議在最后一個),default 在沒有 case 陳述句的值和變數值相等的時候執行,default 分支不需要 break 陳述句,

switch case 執行時,一定會先進行匹配,匹配成功回傳當前 case 的值,再根據是否有 break,判斷是否繼續輸出,或是跳出判斷,

public class Test {
   public static void main(String args[]){
      //char grade = args[0].charAt(0);
      char grade = 'C';
      switch(grade)
      {
         case 'A' :
            System.out.println("優秀"); 
            break;
         case 'B' :
         case 'C' :
            System.out.println("良好");
            break;
         case 'D' :
            System.out.println("及格");
            break;
         case 'F' :
            System.out.println("你需要再努力努力");
            break;
         default :
            System.out.println("未知等級");
      }
      System.out.println("你的等級是 " + grade);
   }
}

以上代碼編譯運行結果如下:

良好
你的等級是 C

對于多個==判斷的情況,使用switch結構更加清晰,

同時注意,上述“翻譯”只有在switch陳述句中對每個case正確撰寫了break陳述句才能對應得上,

使用switch時,注意case陳述句并沒有花括號{},而且,case陳述句具有“穿透性”,漏寫break將導致意想不到的結果:如果 case 陳述句塊中沒有break 陳述句時,JVM 并不會順序輸出每一個case對應的回傳值,而是繼續匹配,匹配不成功則回傳默認 case,匹配成功后,從當前 case 開始,后續所有case的值都會輸出,

public class Test {
   public static void main(String args[]){
      int i = 5;
     //int i = 1;
      switch(i){
         case 0:
            System.out.println("0");
         case 1:
            System.out.println("1");
         case 2:
            System.out.println("2");
         default:
            System.out.println("default");
      }
   }
}

以上代碼編譯運行結果如下:

default

1
2
default
編譯檢查

使用IDE時,可以自動檢查是否漏寫了break陳述句和default陳述句,方法是打開IDE的編譯檢查,

在Idea中,選擇Preferences - Editor - Inspections - Java - Control flow issues,將以下檢查標記為Warning:

  • Fallthrough in 'switch' statement
  • 'switch' statement without 'default' branch

switch陳述句存在問題時,即可在IDE中獲得警告提示,

switch運算式

使用switch時,如果遺漏了break,就會造成嚴重的邏輯錯誤,而且不易在源代碼中發現錯誤,從Java 12開始,switch陳述句升級為更簡潔的運算式語法,使用類似模式匹配(Pattern Matching)的方法,保證只有一種路徑會被執行,并且不需要break陳述句:

public class Main {
    public static void main(String[] args) {
        String fruit = "apple";
        switch (fruit) {
        case "apple" -> System.out.println("Selected apple");
        case "pear" -> System.out.println("Selected pear");
        case "mango" -> {
            System.out.println("Selected mango");
            System.out.println("Good choice!");
        }
        default -> System.out.println("No fruit selected");
        }
    }
}

Selected apple

這樣可以獲得更簡潔的代碼,

yield

大多數時候,在switch運算式內部,我們會回傳簡單的值,

但是,如果需要復雜的陳述句,我們也可以寫很多陳述句,放到{...}里,然后,用yield回傳一個值作為switch陳述句的回傳值:

public class Main {
    public static void main(String[] args) {
        String fruit = "orange";
        int opt = switch (fruit) {
            case "apple" -> 1;
            case "pear", "mango" -> 2;
            default -> {
                int code = fruit.hashCode();
                yield code; // switch陳述句回傳值
            }
        };
        System.out.println("opt = " + opt);
    }
}

Java陣列

陣列對于每一門編程語言來說都是重要的資料結構之一,當然不同語言對陣列的實作及處理也不盡相同,

Java 語言中提供的陣列是用來存盤固定大小的同型別元素,

宣告陣列變數

首先必須宣告陣列變數,才能在程式中使用陣列,下面是宣告陣列變數的語法:

dataType[] arrayRefVar;   // 首選的方法
或
dataType arrayRefVar[];  // 效果相同,但不是首選方法

注意: 建議使用 dataType[] arrayRefVar 的宣告風格宣告陣列變數, dataType arrayRefVar[] 風格是來自 C/C++ 語言 ,在Java中采用是為了讓 C/C++ 程式員能夠快速理解java語言,

創建陣列

Java語言使用new運算子來創建陣列,語法如下:

arrayRefVar = new dataType[arraySize];

上面的語法陳述句做了兩件事:

  • 一、使用 dataType[arraySize] 創建了一個陣列,
  • 二、把新創建的陣列的參考賦值給變數 arrayRefVar

陣列變數的宣告,和創建陣列可以用一條陳述句完成,如下所示:

dataType[] arrayRefVar = new dataType[arraySize];

另外,也可以在定義陣列時直接指定初始化的元素,這樣就不必寫出陣列大小,而是由編譯器自動推算陣列大小,

dataType[] arrayRefVar = new dataType[]{value0, value1, ..., valuek};
//或
dataType[] arrayRefVar = {value0, value1, ..., valuek};

Java的陣列有幾個特點:

  • 陣列所有元素初始化為默認值,整型都是0,浮點型是0.0,布爾型是false
  • 陣列一旦創建后,大小就不可改變,

注意:

  1. 要訪問陣列中的某一個元素,需要使用索引,陣列索引從0開始,例如,5個元素的陣列,索引范圍是0~4
  2. 可以修改陣列中的某一個元素,使用賦值陳述句,例如,ns[1] = 79;
  3. 可以用陣列變數.length獲取陣列大小;
  4. 陣列是參考型別,在使用索引訪問陣列元素時,如果索引超出范圍,運行時將報錯;

注意陣列是參考型別,并且陣列大小不可變,我們觀察下面的代碼:

public class Main {
    public static void main(String[] args) {
        // 5位同學的成績:
        int[] ns;
        ns = new int[] { 68, 79, 91, 85, 62 };
        System.out.println(ns.length); // 5
        ns = new int[] { 1, 2, 3 };
        System.out.println(ns.length); // 3
    }
}

陣列大小變了嗎?看上去好像是變了,但其實根本沒變,

對于陣列ns來說,執行ns = new int[] { 68, 79, 91, 85, 62 };時,它指向一個5個元素的陣列:

     ns
      │
      ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │68 │79 │91 │85 │62 │   │
└───┴───┴───┴───┴───┴───┴───┘

執行ns = new int[] { 1, 2, 3 };時,它指向一個新的3個元素的陣列:

     ns ──────────────────────┐
                              │
                              ▼
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│   │68 │79 │91 │85 │62 │   │ 1 │ 2 │ 3 │   │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

但是,原有的5個元素的陣列并沒有改變,只是無法通過變數ns參考到它們而已,

字串陣列

如果陣列元素不是基本型別,而是一個參考型別,那么,修改陣列元素會有哪些不同?

字串是參考型別,因此我們先定義一個字串陣列:

String[] names = {
    "ABC", "XYZ", "zoo"
};

對于String[]型別的陣列變數names,它實際上包含3個元素,但每個元素都指向某個字串物件:

          ┌─────────────────────────┐
    names │   ┌─────────────────────┼───────────┐
      │   │   │                     │           │
      ▼   │   │                     ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │???│???│???│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

names[1]進行賦值,例如names[1] = "cat";,效果如下:

          ┌─────────────────────────────────────────────────┐
    names │   ┌─────────────────────────────────┐           │
      │   │   │                                 │           │
      ▼   │   │                                 ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │???│???│???│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │ "cat" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

這里注意到原來names[1]指向的字串"XYZ"并沒有改變,僅僅是將names[1]的參考從指向"XYZ"改成了指向"cat",其結果是字串"XYZ"再也無法通過names[1]訪問到了,

陣列操作

有了陣列,我們還需要來操作它,而陣列最常見的一個操作就是遍歷,

通過for回圈就可以遍歷陣列,因為陣列的每個元素都可以通過索引來訪問,因此,使用標準的for回圈可以完成一個陣列的遍歷:

public class Main {
    public static void main(String[] args) {
        int[] ns = { 1, 4, 9, 16, 25 };
        for (int i=0; i<ns.length; i++) {
            int n = ns[i];
            System.out.println(n);
        }
    }
}

為了實作for回圈遍歷,初始條件為i=0,因為索引總是從0開始,繼續回圈的條件為i<ns.length,因為當i=ns.length時,i已經超出了索引范圍(索引范圍是0 ~ ns.length-1),每次回圈后,i++

第二種方式是使用for each回圈,直接迭代陣列的每個元素:

public class Main {
    public static void main(String[] args) {
        int[] ns = { 1, 4, 9, 16, 25 };
        for (int n : ns) {
            System.out.println(n);
        }
    }
}

注意:在for (int n : ns)回圈中,變數n直接拿到ns陣列的元素,而不是索引,

顯然for each回圈更加簡潔,但是,for each回圈無法拿到陣列的索引,因此,到呼叫哪一種for回圈,取決于我們的需要,

列印陣列內容

直接列印陣列變數,得到的是陣列在JVM中的參考地址:

int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(ns); // 類似 [I@7852e922

這并沒有什么意義,因為我們希望列印的陣列的元素內容,因此,使用for each回圈來列印它:

int[] ns = { 1, 1, 2, 3, 5, 8 };
for (int n : ns) {
    System.out.print(n + ", ");
}

使用for each回圈列印也很麻煩,幸好Java標準庫提供了Arrays.toString(),可以快速列印陣列內容:

陣列排序

Java的標準庫已經內置了排序功能,我們只需要呼叫JDK提供的Arrays.sort()就可以排序:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
        Arrays.sort(ns);
        System.out.println(Arrays.toString(ns));
    }
}

必須注意,對陣列排序實際上修改了陣列本身,

Arrays 類

java.util.Arrays 類能方便地操作陣列,它提供的所有方法都是靜態的,

具有以下功能:

  • 給陣列賦值:通過 fill 方法,
  • 對陣列排序:通過 sort 方法,按升序,
  • 比較陣列:通過 equals 方法比較陣列中元素值是否相等,
  • 查找陣列元素:通過 binarySearch 方法能對排序好的陣列進行二分查找法操作,

陣列的其他用法

陣列作為函式的引數

陣列可以作為引數傳遞給方法,

例如,下面的例子就是一個列印 int 陣列中元素的方法:

public static void printArray(int[] array) {
  for (int i = 0; i < array.length; i++) {
    System.out.print(array[i] + " ");
  }
}

下面例子呼叫 printArray 方法列印出 3,1,2,6,4 和 2:

printArray(new int[]{3, 1, 2, 6, 4, 2});

陣列作為函式的回傳值
public static int[] reverse(int[] list) {
  int[] result = new int[list.length];
  for (int i = 0, j = result.length - 1; i < list.length; i++, j--) {
    result[j] = list[i];
  }
  return result;
}

多維陣列

多維陣列可以看成是陣列的陣列,比如二維陣列就是一個特殊的一維陣列,其每一個元素都是一個一維陣列,例如:

String str[][] = new String[3][4];

二維陣列就是陣列的陣列,

public class Main {
    public static void main(String[] args) {
        int[][] ns = {
            { 1, 2, 3, 4 },
            { 5, 6, 7, 8 },
            { 9, 10, 11, 12 }
        };
        System.out.println(ns.length); // 3
    }
}

因為ns包含3個陣列,因此,ns.length3,實際上ns在記憶體中的結構如下:

                    ┌───┬───┬───┬───┐
         ┌───┐  ┌──>│ 1 │ 2 │ 3 │ 4 │
ns ─────>│???│──┘   └───┴───┴───┴───┘
         ├───┤      ┌───┬───┬───┬───┐
         │???│─────>│ 5 │ 6 │ 7 │ 8 │
         ├───┤      └───┴───┴───┴───┘
         │???│──┐   ┌───┬───┬───┬───┐
         └───┘  └──>│ 9 │10 │11 │12 │
                    └───┴───┴───┴───┘

如果我們定義一個普通陣列arr0,然后把ns[0]賦值給它:

public class Main {
    public static void main(String[] args) {
        int[][] ns = {
            { 1, 2, 3, 4 },
            { 5, 6, 7, 8 },
            { 9, 10, 11, 12 }
        };
        int[] arr0 = ns[0];
        System.out.println(arr0.length); // 4
    }
}

實際上arr0就獲取了ns陣列的第0個元素,因為ns陣列的每個元素也是一個陣列,因此,arr0指向的陣列就是{ 1, 2, 3, 4 },在記憶體中,結構如下:

            arr0 ─────┐
                      ▼
                    ┌───┬───┬───┬───┐
         ┌───┐  ┌──>│ 1 │ 2 │ 3 │ 4 │
ns ─────>│???│──┘   └───┴───┴───┴───┘
         ├───┤      ┌───┬───┬───┬───┐
         │???│─────>│ 5 │ 6 │ 7 │ 8 │
         ├───┤      └───┴───┴───┴───┘
         │???│──┐   ┌───┬───┬───┬───┐
         └───┘  └──>│ 9 │10 │11 │12 │
                    └───┴───┴───┴───┘

訪問二維陣列的某個元素需要使用array[row][col],例如:

System.out.println(ns[1][2]); // 7

二維陣列的每個陣列元素的長度并不要求相同,例如,可以這么定義ns陣列:

int[][] ns = {
    { 1, 2, 3, 4 },
    { 5, 6 },
    { 7, 8, 9 }
};

這個二維陣列在記憶體中的結構如下:

                    ┌───┬───┬───┬───┐
         ┌───┐  ┌──>│ 1 │ 2 │ 3 │ 4 │
ns ─────>│???│──┘   └───┴───┴───┴───┘
         ├───┤      ┌───┬───┐
         │???│─────>│ 5 │ 6 │
         ├───┤      └───┴───┘
         │???│──┐   ┌───┬───┬───┐
         └───┘  └──>│ 7 │ 8 │ 9 │
                    └───┴───┴───┘

要列印一個二維陣列,可以使用兩層嵌套的for回圈:

for (int[] arr : ns) {
    for (int n : arr) {
        System.out.print(n);
        System.out.print(', ');
    }
    System.out.println();
}

或者使用Java標準庫的Arrays.deepToString()

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] ns = {
            { 1, 2, 3, 4 },
            { 5, 6, 7, 8 },
            { 9, 10, 11, 12 }
        };
        System.out.println(Arrays.deepToString(ns));
    }
}

多維陣列的動態初始化(以二維陣列為例)

  1. 直接為每一維分配空間,格式如下:
type[][] typeName = new type[typeLength1][typeLength2];

type 可以為基本資料型別和復合資料型別,arraylength1arraylength2 必須為正整數,arraylength1 為行數,arraylength2 為列數,

例如:int a[][] = new int[2][3];

  1. 從最高維開始,分別為每一維分配空間,例如:
String s[][] = new String[2][];
s[0] = new String[2];
s[1] = new String[3];
s[0][0] = new String("Good");
s[0][1] = new String("Luck");
s[1][0] = new String("to");
s[1][1] = new String("you");
s[1][2] = new String("!");

image-20201231152856280

多維陣列的參考(以二維陣列為例)

對二維陣列中的每個元素,參考方式為 arrayName[index1][index2],例如:

num[1][0];

命令列引數

Java程式的入口是main方法,而main方法可以接受一個命令列引數,它是一個String[]陣列,

這個命令列引數由JVM接收用戶輸入并傳給main方法:

public class Main {
    public static void main(String[] args) {
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}

我們可以利用接收到的命令列引數,根據不同的引數執行不同的代碼,例如,實作一個-version引數,列印程式版本號:

public class Main {
    public static void main(String[] args) {
        for (String arg : args) {
            if ("-version".equals(arg)) {
                System.out.println("v 1.0");
                break;
            }
        }
    }
}

上面這個程式必須在命令列執行,我們先編譯它:

$ javac Main.java

然后,執行的時候,給它傳遞一個-version引數:

$ java Main -version
v 1.0

這樣,程式就可以根據傳入的命令列引數,作出不同的回應,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/243117.html

標籤:Java

上一篇:為什么 StackOverflow 上的代碼片段會摧毀你的專案?

下一篇:Spring-AOP核心

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more