Java 教程

Java 控制語句

面向物件程式設計

Java 內建類

Java 檔案處理

Java 錯誤與異常

Java 多執行緒

Java 同步

Java 網路程式設計

Java 集合

Java 介面

Java 資料結構

Java 集合演算法

高階 Java

Java 雜項

Java APIs 與框架

Java 類引用

Java 有用資源

Java 快速指南



Java - 概述

Java 程式語言最初由 Sun Microsystems 開發,由 James Gosling 發起,於 1995 年作為 Sun Microsystems Java 平臺 (Java 1.0 [J2SE]) 的核心元件釋出。

Java 標準版的最新版本是 Java SE 8。隨著 Java 的發展及其廣泛流行,構建了多種配置以適應各種型別的平臺。例如:用於企業應用程式的 J2EE,用於移動應用程式的 J2ME。

新的 J2 版本分別更名為 Java SE、Java EE 和 Java ME。Java 保證了 **一次編寫,隨處執行。**

Java 是 −

  • **面向物件的** − 在 Java 中,一切都是物件。Java 基於物件模型,因此易於擴充套件。

  • **平臺無關的** − 與包括 C 和 C++ 在內的許多其他程式語言不同,Java 編譯後,不會編譯成特定於平臺的機器碼,而是編譯成平臺無關的位元組碼。此位元組碼透過網路分發,並在執行它的任何平臺上由虛擬機器 (JVM) 解釋。

  • **簡單的** − Java 的設計易於學習。如果您理解 OOP Java 的基本概念,那麼掌握它就很容易。

  • **安全的** − Java 的安全特性使其能夠開發無病毒、防篡改的系統。身份驗證技術基於公鑰加密。

  • **與架構無關的** − Java 編譯器生成與架構無關的物件檔案格式,這使得編譯後的程式碼在許多處理器上都可以執行,前提是存在 Java 執行時系統。

  • **可移植的** − 由於架構中立且規範中沒有與實現相關的方面,因此 Java 是可移植的。Java 中的編譯器是用 ANSI C 編寫的,具有清晰的可移植性邊界,這是一個 POSIX 子集。

  • **健壯的** − Java 透過主要強調編譯時錯誤檢查和執行時檢查來努力消除易出錯的情況。

  • **多執行緒的** − 利用 Java 的多執行緒特性,可以編寫可以同時執行許多工的程式。此設計特性允許開發人員構建可以平穩執行的互動式應用程式。

  • **解釋型的** − Java 位元組碼會即時轉換為本機機器指令,並且不會儲存在任何地方。由於連結是一個增量且輕量級的過程,因此開發過程更快且更具分析性。

  • **高效能的** − 透過使用即時編譯器,Java 實現了高效能。

  • **分散式的** − Java 是為網際網路的分散式環境而設計的。

  • **動態的** − Java 比 C 或 C++ 更動態,因為它旨在適應不斷變化的環境。Java 程式可以攜帶大量的執行時資訊,這些資訊可用於驗證和解決執行時對物件的訪問。

使用 Java 程式設計的 Hello World。

為了讓您對 Java 程式設計有一點興奮感,我將為您提供一個小型傳統的 C 程式設計 Hello World 程式,您可以使用演示連結進行嘗試。

public class MyFirstJavaProgram {

   /* This is my first java program.
    * This will print 'Hello World' as the output
    */

   public static void main(String []args) {
      System.out.println("Hello World"); // prints Hello World
   }
}

Java 的歷史

James Gosling 於 1991 年 6 月啟動了 Java 語言專案,用於他的眾多機頂盒專案之一。這種語言最初被稱為“Oak”(Gosling 辦公室外的一棵橡樹),也稱為“Green”,後來從一個隨機詞列表中更名為 Java。

Sun公司於1995年釋出了第一個公開實現版本Java 1.0。它承諾實現一次編寫,到處執行(WORA),在流行的平臺上提供免費的執行時環境。

2006年11月13日,Sun公司在GNU通用公共許可證(GPL)的條款下,將大部分Java程式碼作為自由和開放原始碼軟體釋出。

2007年5月8日,Sun公司完成了這一程序,使所有Java核心程式碼都成為自由和開放原始碼,除了Sun公司不擁有版權的一小部分程式碼。

您需要的工具

要執行本教程中討論的示例,您需要一臺至少具有64MB RAM(推薦128MB RAM)的奔騰200MHz計算機。

您還需要以下軟體:

  • Linux 7.1或Windows xp/7/8作業系統
  • Java JDK 8
  • Microsoft Notepad或任何其他文字編輯器

本教程將提供建立使用Java的GUI、網路和Web應用程式的必要技能。

下一步是什麼?

下一章將指導您如何獲取Java及其文件。最後,它將指導您如何安裝Java並準備開發Java應用程式的環境。

Java - 環境設定

線上即時演示選項

我們已線上設定了Java程式設計環境,以便您可以線上編譯和執行所有可用的示例。這使您可以確信您正在閱讀的內容,並使您可以使用不同的選項驗證程式。隨意修改任何示例並在線執行。

嘗試使用以下示例,在下面示例程式碼框的右上角使用即時演示選項:

public class MyFirstJavaProgram {
   public static void main(String []args) {
      System.out.println("Hello World");
   }
}

對於本教程中給出的大多數示例,您會在我們網站程式碼部分的右上角找到一個嘗試選項,該選項將帶您到線上編譯器。因此,只需使用它並享受您的學習。

本地環境設定

如果您想為Java程式語言設定自己的環境,那麼本節將指導您完成整個過程。請按照以下步驟設定您的Java環境。

Java SE可免費下載。要下載,請點選此處,請下載與您的作業系統相容的版本。

按照說明下載Java,並執行.exe檔案以將Java安裝到您的計算機上。將Java安裝到您的計算機後,您需要設定環境變數以指向正確的安裝目錄。

為Windows 2000/XP設定路徑

假設您已將Java安裝在c:\Program Files\java\jdk目錄下:

  • 右鍵單擊“我的電腦”,然後選擇“屬性”。

  • 單擊“高階”選項卡下的“環境變數”按鈕。

  • 現在,編輯“Path”變數,並在其末尾新增指向Java可執行檔案目錄的路徑。例如,如果當前路徑設定為C:\Windows\System32,則按如下方式編輯:

    C:\Windows\System32;c:\Program Files\java\jdk\bin.

為Windows 95/98/ME設定路徑

假設您已將Java安裝在c:\Program Files\java\jdk目錄下:

  • 編輯“C:\autoexec.bat”檔案,並在末尾新增以下行:

    SET PATH=%PATH%;C:\Program Files\java\jdk\bin

為Linux、UNIX、Solaris、FreeBSD設定路徑

應將環境變數PATH設定為指向已安裝Java二進位制檔案的路徑。如果您遇到問題,請參考您的shell文件。

例如,如果您使用bash作為您的shell,則您會在.bashrc檔案的末尾新增以下行:

    export PATH=/path/to/java:$PATH

常用的Java編輯器

要編寫Java程式,您需要一個文字編輯器。市場上甚至還有更復雜的IDE。下面簡要介紹最常用的IDE:

  • Notepad − 在Windows機器上,您可以使用任何簡單的文字編輯器,如Notepad(本教程推薦)或WordPad。Notepad++也是一個免費的文字編輯器,具有增強的功能。

  • Netbeans − 它是一個開源且免費的Java IDE,可以從www.netbeans.org/index.html下載。

  • Eclipse − 它也是一個由Eclipse開源社群開發的Java IDE,可以從www.eclipse.org下載。

IDE或整合開發環境,提供所有常用的工具和功能來幫助程式設計,例如原始碼編輯器、構建工具和偵錯程式等。

下一步是什麼?

下一章將教你如何編寫和執行你的第一個Java程式,以及開發應用程式所需的一些重要的Java基本語法。

Java - 基本語法

當我們考慮一個Java程式時,它可以定義為一組透過呼叫彼此的方法來通訊的物件。現在讓我們簡要地瞭解一下類、物件、方法和例項變數的含義。

  • 物件 − 物件具有狀態和行為。例如:狗具有狀態——顏色、名字、品種,以及行為,如搖尾巴、吠叫、吃東西。物件是類的例項。

  • − 類可以定義為一個模板/藍圖,它描述了其型別物件支援的行為/狀態。

  • 方法 − 方法基本上是一種行為。一個類可以包含許多方法。在方法中編寫邏輯,操作資料並執行所有操作。

  • 例項變數 − 每個物件都有自己唯一的一組例項變數。物件的狀體是由分配給這些例項變數的值建立的。

第一個Java程式

讓我們來看一段簡單的程式碼,它將列印單詞Hello World

示例

public class MyFirstJavaProgram {

   /* This is my first java program.
    * This will print 'Hello World' as the output
    */

   public static void main(String []args) {
      System.out.println("Hello World"); // prints Hello World
   }
}

讓我們看看如何儲存檔案、編譯和執行程式。請按照以下步驟操作:

  • 開啟記事本並新增上面的程式碼。

  • 將檔案儲存為:MyFirstJavaProgram.java。

  • 開啟一個命令提示符視窗,並轉到儲存類的目錄。假設它是C:\。

  • 鍵入“javac MyFirstJavaProgram.java”並按Enter鍵編譯程式碼。如果程式碼中沒有錯誤,命令提示符將帶您進入下一行(假設:已設定path變數)。

  • 現在,鍵入“java MyFirstJavaProgram”來執行您的程式。

  • 您將能夠看到視窗上列印的“Hello World”。

輸出

C:\> javac MyFirstJavaProgram.java
C:\> java MyFirstJavaProgram 
Hello World

基本語法

關於Java程式,記住以下幾點非常重要。

  • 大小寫敏感性 − Java 區分大小寫,這意味著識別符號Hellohello在Java中具有不同的含義。

  • 類名 − 所有類名的第一個字母都應大寫。如果使用多個單詞來構成類名,則每個內部單詞的第一個字母都應大寫。

    示例:class MyFirstJavaClass

  • 方法名 − 所有方法名都應以小寫字母開頭。如果使用多個單詞來構成方法名,則每個內部單詞的第一個字母都應大寫。

    示例:public void myMethodName()

  • 程式檔名 − 程式檔名應與類名完全匹配。

    儲存檔案時,應使用類名儲存它(記住Java區分大小寫),並在名稱末尾附加“.java”(如果檔名和類名不匹配,則程式將無法編譯)。

    但請注意,如果您在檔案中沒有公共類,則檔名可以與類名不同。在檔案中也不必一定要有公共類。

    示例:假設“MyFirstJavaProgram”是類名。然後檔案應儲存為'MyFirstJavaProgram.java'

  • public static void main(String args[]) − Java程式處理從main()方法開始,它是每個Java程式的必需部分。

Java識別符號

所有Java元件都需要名稱。用於類、變數和方法的名稱稱為識別符號

在Java中,關於識別符號,有幾點需要注意。如下所示:

  • 所有識別符號都應以字母(A到Z或a到z)、貨幣字元($)或下劃線(_)開頭。

  • 第一個字元之後,識別符號可以包含任何字元組合。

  • 關鍵字不能用作識別符號。

  • 最重要的是,識別符號區分大小寫。

  • 合法識別符號示例:age, $salary, _value, __1_value。

  • 非法識別符號示例:123abc, -salary。

Java修飾符

與其他語言一樣,可以使用修飾符來修改類、方法等。修飾符分為兩類:

  • 訪問修飾符 − default, public, protected, private

  • 非訪問修飾符 − final, abstract, strictfp

我們將在下一節中更詳細地瞭解修飾符。

Java變數

以下是Java中的變數型別:

  • 區域性變數
  • 類變數(靜態變數)
  • 例項變數(非靜態變數)

Java陣列

陣列是儲存相同型別多個變數的物件。但是,陣列本身是堆上的一個物件。我們將在接下來的章節中學習如何宣告、構造和初始化。

Java列舉

列舉在Java 5.0中引入。列舉將變數限制為僅具有幾個預定義值中的一個。此列舉列表中的值稱為列舉。

使用列舉可以減少程式碼中的錯誤數量。

例如,如果我們考慮一個鮮榨果汁店的應用程式,則可以將杯子大小限制為小、中、大。這將確保不允許任何人訂購小、中、大以外的任何尺寸。

示例

class FreshJuice {
   enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
   FreshJuiceSize size;
}

public class FreshJuiceTest {

   public static void main(String args[]) {
      FreshJuice juice = new FreshJuice();
      juice.size = FreshJuice.FreshJuiceSize.MEDIUM ;
      System.out.println("Size: " + juice.size);
   }
}

輸出

Size: MEDIUM

注意 − 列舉可以單獨宣告或在類內宣告。方法、變數、建構函式也可以在列舉內定義。

Java關鍵字

以下列表顯示了Java中的保留字。這些保留字不能用作常量或變數或任何其他識別符號名稱。

序號 保留字和描述
1 abstract

根據字典,抽象是指處理思想而不是事件的特性。

2 assert

assert關鍵字用於在Java中定義斷言。斷言是在Java中的一種語句,它確保程式中所做的任何假設的正確性。

3 boolean

布林型資料型別是 Java 支援的八種基本資料型別之一。它提供了一種建立布林型別變數的方法,這些變數可以接受 true 或 false 的布林值。

4 break

Java 程式語言中的break語句有以下兩種用法:

  • 當在迴圈內遇到break語句時,迴圈會立即終止,程式控制會在迴圈後的下一條語句處繼續。

  • 它可以用來終止switch語句中的一個 case。

5 byte

byte 資料型別是 Java 支援的八種基本資料型別之一。它提供了一種建立 byte 型別變數的方法,這些變數可以接受 byte 值。

6 case

case關鍵字是switch語句的一部分,它允許將變數與一系列值進行相等性測試。

7 catch

異常(或異常事件)是在程式執行過程中出現的錯誤。

8 char

char 資料型別是 Java 支援的八種基本資料型別之一。

9 class

Java 是一種面向物件的語言。它具有面向物件的特性。

10 const

final關鍵字用於在 Java 中定義常量值或 final 方法/類。

11 continue

continue關鍵字可以用於任何迴圈控制結構。

12 default

default關鍵字是switch語句的一部分,它允許將變數與一系列值進行相等性測試。

13 do

do...while迴圈類似於while迴圈,不同之處在於do...while迴圈至少執行一次。

14 double

double 資料型別是 Java 支援的八種基本資料型別之一。

15 if

if語句後面可以跟一個可選的else語句,當布林表示式為false時執行。

16 enum

Java Enum類是所有 Java 語言列舉型別的公共基類。

17 extends

extends是用於繼承類屬性的關鍵字。以下是extends關鍵字的語法。

18 final

final關鍵字用於在 Java 中定義常量值或 final 方法/類。

19 finally

finally關鍵字用於定義finally塊。finally塊跟在try塊或catch塊之後。finally塊中的程式碼總是執行,無論是否發生異常。

20 float

float 資料型別是 Java 支援的八種基本資料型別之一。它提供了一種建立 float 型別變數的方法,這些變數可以接受 float 值。

21 for

for迴圈是一種重複控制結構,允許你高效地編寫需要執行特定次數的迴圈。

22 goto

Java目前不支援goto語句。它被保留為將來使用的保留關鍵字。作為替代,Java支援帶有break和continue語句的標籤。

23 if

if語句由一個布林表示式和一個或多個語句組成。

24 implements

通常,implements關鍵字與類一起使用,以繼承介面的屬性。

25 import

import關鍵字用於包的上下文中。

26 instanceof

instanceof關鍵字是一個運算子,僅用於物件引用變數。

27 int

int 資料型別是 Java 支援的八種基本資料型別之一。

28 interface

介面是 Java 中的一種引用型別。它類似於類。它是抽象方法的集合。

29 long

long 資料型別是 Java 支援的八種基本資料型別之一。

30 package

在 Java 中使用包是為了防止命名衝突,控制訪問,使查詢/定位和使用類、介面、列舉和註解更容易等等。

31 private

宣告為 private 的方法、變數和建構函式只能在其宣告的類中訪問。

32 protected

protected 訪問修飾符不能應用於類和介面。

33 public

宣告為 public 的類、方法、建構函式、介面等可以從任何其他類訪問。

34 short

透過為變數分配不同的資料型別,你可以在這些變數中儲存整數、小數或字元。

35 static

static關鍵字用於建立獨立於為類建立的任何例項存在的變數。

36 super

super關鍵字類似於this關鍵字。

37 switch

switch語句允許將變數與一系列值進行相等性測試。

38 this

this關鍵字是一個非常重要的關鍵字,用於標識物件。以下是此關鍵字的用法。

39 throw

如果方法不處理已檢查異常,則該方法必須使用throws關鍵字宣告它。

40 transient

序列化是一個概念,使用它我們可以將物件的狀體寫入位元組流中,以便我們可以透過網路傳輸它(使用JPA和RMI等技術)。

41 try

方法使用trycatch關鍵字的組合來捕獲異常。

42 while

Java 程式語言中的while迴圈語句重複執行目標語句,只要給定的條件為真。

Java中的註釋

Java支援單行和多行註釋,與C和C++非常相似。Java編譯器會忽略任何註釋中包含的所有字元。

示例

public class MyFirstJavaProgram {

   /* This is my first java program.
    * This will print 'Hello World' as the output
    * This is an example of multi-line comments.
    */

   public static void main(String []args) {
      // This is an example of single line comment
      /* This is also an example of single line comment. */
      System.out.println("Hello World");
   }
}

輸出

Hello World

使用空行

僅包含空格(可能帶有註釋)的行稱為空行,Java完全忽略它。

繼承

在 Java 中,類可以從類派生。基本上,如果你需要建立一個新類,並且這裡已經存在一個類具有你所需的一些程式碼,那麼可以從已經存在的程式碼派生你的新類。

這個概念允許你重用現有類的欄位和方法,而無需在新類中重寫程式碼。在這種情況下,現有類稱為超類,派生類稱為子類

介面

在 Java 語言中,介面可以定義為物件之間關於如何相互通訊的契約。在繼承的概念中,介面起著至關重要的作用。

介面定義了派生類(子類)應該使用的方法。但是方法的實現完全取決於子類。

下一步是什麼?

下一節將解釋 Java 程式設計中的物件和類。在本節結束時,你將能夠清楚地瞭解 Java 中的物件和類是什麼。

Java - 變數型別

變數為我們提供了程式可以操作的命名儲存。Java 中的每個變數都有一個特定的型別,它決定變數記憶體的大小和佈局;可以儲存在該記憶體中的值的範圍;以及可以應用於變數的操作集。

必須在使用變數之前宣告它們。以下是變數宣告的基本形式:

data type variable [ = value][, variable [ = value] ...] ;

這裡資料型別是 Java 的資料型別之一,變數是變數的名稱。要宣告多個指定型別變數,可以使用逗號分隔的列表。

以下是 Java 中變數宣告和初始化的有效示例:

示例

int a, b, c;         // Declares three ints, a, b, and c.
int a = 10, b = 10;  // Example of initialization
byte B = 22;         // initializes a byte type variable B.
double pi = 3.14159; // declares and assigns a value of PI.
char a = 'a';        // the char variable a iis initialized with value 'a'

本章將解釋 Java 語言中提供的各種變數型別。Java 中有三種變數:

  • 區域性變數
  • 例項變數
  • 類/靜態變數

區域性變數

  • 區域性變數在方法、建構函式或程式碼塊中宣告。

  • 進入方法、建構函式或程式碼塊時建立區域性變數,一旦退出方法、建構函式或程式碼塊,變數就會被銷燬。

  • 訪問修飾符不能用於區域性變數。

  • 區域性變數僅在其宣告的方法、建構函式或程式碼塊中可見。

  • 區域性變數在內部以堆疊級別實現。

  • 區域性變數沒有預設值,因此區域性變數應該在第一次使用之前宣告並賦值。

示例

這裡,age是一個區域性變數。它在pupAge()方法內部定義,其作用域僅限於此方法。

public class Test {
   public void pupAge() {
      int age = 0;
      age = age + 7;
      System.out.println("Puppy age is : " + age);
   }

   public static void main(String args[]) {
      Test test = new Test();
      test.pupAge();
   }
}

輸出

Puppy age is: 7

示例

下面的示例在未初始化的情況下使用age,因此在編譯時會報錯。

public class Test {
   public void pupAge() {
      int age;
      age = age + 7;
      System.out.println("Puppy age is : " + age);
   }

   public static void main(String args[]) {
      Test test = new Test();
      test.pupAge();
   }
}

輸出

Test.java:4:variable number might not have been initialized
age = age + 7;
         ^
1 error

例項變數

  • 例項變數在類中宣告,但在方法、建構函式或任何程式碼塊之外宣告。

  • 當在堆中為物件分配空間時,會為每個例項變數值建立一個槽。

  • 使用關鍵字'new'建立物件時建立例項變數,物件銷燬時銷燬例項變數。

  • 例項變數儲存必須被多個方法、建構函式或程式碼塊引用的值,或者物件狀態的必要部分,這些部分必須存在於整個類中。

  • 例項變數可以在類級別在使用之前或之後宣告。

  • 可以為例項變數提供訪問修飾符。

  • 例項變數對類中的所有方法、建構函式和程式碼塊可見。通常,建議將這些變數設為私有(訪問級別)。但是,可以使用訪問修飾符為子類提供這些變數的可見性。

  • 例項變數具有預設值。對於數字,預設值為 0;對於布林值,為 false;對於物件引用,為 null。可以在宣告期間或在建構函式中賦值。

  • 可以透過在類內部呼叫變數名來直接訪問例項變數。但是,在靜態方法中(當例項變數具有可訪問性時),應該使用完全限定名來呼叫它們。ObjectReference.VariableName

示例

import java.io.*;

public class Employee {

   // this instance variable is visible for any child class.
   public String name;

   // salary  variable is visible in Employee class only.
   private double salary;

   // The name variable is assigned in the constructor.
   public Employee (String empName) {
      name = empName;
   }

   // The salary variable is assigned a value.
   public void setSalary(double empSal) {
      salary = empSal;
   }

   // This method prints the employee details.
   public void printEmp() {
      System.out.println("name  : " + name );
      System.out.println("salary :" + salary);
   }

   public static void main(String args[]) {
      Employee empOne = new Employee("Ransika");
      empOne.setSalary(1000);
      empOne.printEmp();
   }
}

輸出

name  : Ransika
salary :1000.0

類/靜態變數

  • 類變數也稱為靜態變數,它們在類中使用 static 關鍵字宣告,但在方法、建構函式或程式碼塊之外宣告。

  • 無論從類中建立多少個物件,每個類變數都只有一個副本。

  • 靜態變數很少使用,除非宣告為常量。常量是宣告為 public/private、final 和 static 的變數。常量變數的值永遠不會改變。

  • 靜態變數儲存在靜態記憶體中。除了宣告為 final 並用作公共或私有常量之外,很少使用靜態變數。

  • 程式啟動時建立靜態變數,程式停止時銷燬靜態變數。

  • 可見性與例項變數相似。但是,大多數靜態變數都宣告為 public,因為它們必須可供類的使用者使用。

  • 預設值與例項變數相同。對於數字,預設值為 0;對於布林值,為 false;對於物件引用,為 null。可以在宣告期間或在建構函式中賦值。此外,可以在特殊的靜態初始化塊中賦值。

  • 可以使用類名ClassName.VariableName訪問靜態變數。

  • 當將類變數宣告為 public static final 時,變數名(常量)全部大寫。如果靜態變數不是 public 和 final,則命名語法與例項變數和區域性變數相同。

示例

import java.io.*;

public class Employee {

   // salary  variable is a private static variable
   private static double salary;

   // DEPARTMENT is a constant
   public static final String DEPARTMENT = "Development ";

   public static void main(String args[]) {
      salary = 1000;
      System.out.println(DEPARTMENT + "average salary:" + salary);
   }
}

輸出

Development average salary:1000

注意 - 如果從外部類訪問變數,則應作為 Employee.DEPARTMENT 訪問常量。

下一步是什麼?

你已經在本節中使用了訪問修飾符(public 和 private)。下一節將詳細解釋訪問修飾符和非訪問修飾符。

Java - 基本資料型別

變數不過是在記憶體中保留的儲存值的記憶體位置。這意味著當您建立一個變數時,您會在記憶體中保留一些空間。

根據變數的資料型別,作業系統分配記憶體並決定可以在保留的記憶體中儲存什麼。因此,透過為變數分配不同的資料型別,您可以在這些變數中儲存整數、小數或字元。

Java中有兩種資料型別:

  • 原始資料型別
  • 引用/物件資料型別

原始資料型別

Java支援八種原始資料型別。原始資料型別由語言預定義,並由關鍵字命名。現在讓我們詳細瞭解這八種原始資料型別。

byte

  • 位元組資料型別是8位有符號二進位制補碼整數

  • 最小值為 -128 (-2^7)

  • 最大值為 127(包含)(2^7 -1)

  • 預設值為 0

  • 位元組資料型別用於在大型陣列中節省空間,主要用於代替整數,因為位元組的大小是整數的四分之一。

  • 示例 - byte a = 100, byte b = -50

short

  • 短整型資料型別是16位有符號二進位制補碼整數

  • 最小值為 -32,768 (-2^15)

  • 最大值為 32,767(包含)(2^15 -1)

  • 短整型資料型別也可用於像位元組資料型別一樣節省記憶體。短整型是整數大小的一半。

  • 預設值為 0。

  • 示例 - short s = 10000, short r = -20000

int

  • 整型資料型別是32位有符號二進位制補碼整數。

  • 最小值為 -2,147,483,648 (-2^31)

  • 最大值為 2,147,483,647(包含)(2^31 -1)

  • 除非擔心記憶體問題,否則整型通常用作整數值的預設資料型別。

  • 預設值為 0

  • 示例 - int a = 100000, int b = -200000

long

  • 長整型資料型別是64位有符號二進位制補碼整數
  • 最小值為 -9,223,372,036,854,775,808(-2^63)
  • 最大值為 9,223,372,036,854,775,807(包含)(2^63 -1)
  • 當需要比int更寬的範圍時使用此型別
  • 預設值為 0L
  • 示例 - long a = 100000L, long b = -200000L

float

  • 浮點型資料型別是單精度32位IEEE 754浮點數

  • 浮點型主要用於節省大型浮點數陣列的記憶體

  • 預設值為 0.0f

  • 浮點型資料型別永遠不要用於精確值,例如貨幣

  • 示例 - float f1 = 234.5f

double

  • 雙精度型資料型別是雙精度64位IEEE 754浮點數

  • 此資料型別通常用作十進位制值的預設資料型別,通常是預設選擇

  • 雙精度型資料型別永遠不要用於精確值,例如貨幣

  • 預設值為 0.0d

  • 示例 - double d1 = 123.4

boolean

  • 布林型資料型別表示一位資訊

  • 只有兩個可能的值:true 和 false

  • 此資料型別用於跟蹤真/假條件的簡單標誌

  • 預設值為 false

  • 示例 - boolean one = true

char

  • 字元型資料型別是單個16位Unicode字元

  • 最小值為 '\u0000' (或 0)

  • 最大值為 '\uffff' (或 65,535 包含)

  • 字元型資料型別用於儲存任何字元

  • 示例 - char letterA = 'A'

示例

以下示例顯示了我們上面討論的各種原始資料型別的用法。我們在數值資料型別上使用了加法運算,而布林型和字元型變數則按原樣列印。

public class JavaTester {
   public static void main(String args[]) {

      byte byteValue1 = 2;
      byte byteValue2 = 4;
      byte byteResult = (byte)(byteValue1 + byteValue2);

      System.out.println("Byte: " + byteResult);

      short shortValue1 = 2;
      short shortValue2 = 4;
      short shortResult = (short)(shortValue1 + shortValue2);

      System.out.println("Short: " + shortResult);

      int intValue1 = 2;
      int intValue2 = 4;
      int intResult = intValue1 + intValue2;

      System.out.println("Int: " + intResult);

      long longValue1 = 2L;
      long longValue2 = 4L;
      long longResult = longValue1 + longValue2;

      System.out.println("Long: " + longResult);

      float floatValue1 = 2.0f;
      float floatValue2 = 4.0f;
      float floatResult = floatValue1 + floatValue2;

      System.out.println("Float: " + floatResult);

      double doubleValue1 = 2.0;
      double doubleValue2 = 4.0;
      double doubleResult = doubleValue1 + doubleValue2;

      System.out.println("Double: " + doubleResult);

      boolean booleanValue = true;

      System.out.println("Boolean: " + booleanValue);

      char charValue = 'A';

      System.out.println("Char: " + charValue);     
   }
}

輸出

Byte: 6
Short: 6
Int: 6
Long: 6
Float: 6.0
Double: 6.0
Boolean: true
Char: A

引用資料型別

  • 引用變數是使用類的已定義建構函式建立的。它們用於訪問物件。這些變數宣告為特定型別,不能更改。例如,Employee、Puppy 等。

  • 類物件和各種型別的陣列變數屬於引用資料型別。

  • 任何引用變數的預設值為 null。

  • 引用變數可以用來引用宣告型別的任何物件或任何相容型別。

  • 示例:Animal animal = new Animal("giraffe");

Java字面量

字面量是固定值的源程式碼表示。它們直接在程式碼中表示,無需任何計算。

字面量可以賦值給任何原始型別變數。例如:

byte a = 68;
char a = 'A';

byte、int、long 和 short 可以用十進位制(基數 10)、十六進位制(基數 16)或八進位制(基數 8)數字系統表示。

使用這些數字系統表示字面量時,字首 0 用於表示八進位制,字首 0x 用於表示十六進位制。例如:

int decimal = 100;
int octal = 0144;
int hexa =  0x64;

Java 中的字串字面量像大多數其他語言一樣,透過用一對雙引號括起字元序列來指定。字串字面量的示例如下:

示例

"Hello World"
"two\nlines"
"\"This is in quotes\""

字串和字元型別的字面量可以包含任何 Unicode 字元。例如:

char a = '\u0001';
String a = "\u0001";

示例

以下示例顯示了我們上面討論的各種字面量的用法。

public class JavaTester {
   public static void main(String args[]) {

      int decimal = 100;
      int octal = 0144;
      int hexa =  0x64;

      System.out.println(decimal);
      System.out.println(octal);
      System.out.println(hexa);

      String msg1 = "Hello World";
      String msg2 = "two\nlines";
      String msg3 = "\"This is in quotes\"";

      System.out.println(msg1);
      System.out.println(msg2);
      System.out.println(msg3);

      char a = '\u0064';
      String msg4 = "\u0064";

      System.out.println(a);
      System.out.println(msg4);   
   }
}

輸出

100
100
100
Hello World
two
lines
"This is in quotes"
d
d

Java 語言也支援一些用於字串和字元字面量的特殊轉義序列。它們是:

表示法 表示的字元
\n 換行符 (0x0a)
\r 回車符 (0x0d)
\f 換頁符 (0x0c)
\b 退格符 (0x08)
\s 空格 (0x20)
\t 製表符
\" 雙引號
\' 單引號
\\ 反斜槓
\ddd 八進位制字元 (ddd)
\uxxxx 十六進位制 UNICODE 字元 (xxxx)

下一步是什麼?

本章解釋了各種資料型別。下一個主題解釋不同的變數型別及其用法。這將使您很好地瞭解如何在 Java 類、介面等中使用它們。

Java - 基本運算子

Java 提供了一套豐富的運算子來操作變數。我們可以將所有 Java 運算子劃分為以下幾組:

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

算術運算子

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

假設整數變數 A 保持 10,變數 B 保持 20,則:

示例

運算子 描述 示例
+ (加法) 將運算子兩側的值相加。 A + B 將得到 30
- (減法) 從左運算元中減去右運算元。 A - B 將得到 -10
* (乘法) 將運算子兩側的值相乘。 A * B 將得到 200
/ (除法) 將左運算元除以右運算元。 B / A 將得到 2
% (取模) 將左運算元除以右運算元並返回餘數。 B % A 將得到 0
++ (自增) 將運算元的值增加 1。 B++ 得到 21
-- (自減) 將運算元的值減少 1。 B-- 得到 19

關係運算符

Java 語言支援以下關係運算符。

假設變數 A 保持 10,變數 B 保持 20,則:

示例

運算子 描述 示例
== (等於) 檢查兩個運算元的值是否相等,如果相等則條件為真。 (A == B) 為假。
!= (不等於) 檢查兩個運算元的值是否相等,如果不相等則條件為真。 (A != B) 為真。
> (大於) 檢查左運算元的值是否大於右運算元的值,如果是則條件為真。 (A > B) 為假。
< (小於) 檢查左運算元的值是否小於右運算元的值,如果是則條件為真。 (A < B) 為真。
>= (大於或等於) 檢查左運算元的值是否大於或等於右運算元的值,如果是則條件為真。 (A >= B) 為假。
<= (小於或等於) 檢查左運算元的值是否小於或等於右運算元的值,如果是則條件為真。 (A <= B) 為真。

位運算子

Java 定義了幾個位運算子,這些運算子可以應用於整數型別、long、int、short、char 和 byte。

位運算子作用於位並執行逐位運算。假設 a = 60 且 b = 13;現在以二進位制格式,它們將如下所示:

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
^ (按位異或) 二進位制 XOR 運算子如果位在一個運算元中設定但不在兩個運算元中都設定,則複製該位。 (A ^ B) 將得到 49,即 0011 0001
~ (按位非) 二進位制反碼運算子是單目運算子,其作用是“翻轉”位。 (~A) 將得到 -61,由於是有符號二進位制數,因此在二進位制補碼形式下為 1100 0011。
<< (左移) 二進位制左移運算子。左運算元的值向左移動右運算元指定的位數。 A << 2 將得到 240,即 1111 0000
>> (右移) 二進位制右移運算子。左運算元的值向右移動右運算元指定的位數。 A >> 2 將得到 15,即 0000 1111
>>> (零填充右移) 右移零填充運算子。左運算元的值向右移動右運算元指定的位數,並用零填充移位的值。 A >>> 2 將得到 15,即 0000 1111

邏輯運算子

下表列出了邏輯運算子:

假設布林變數 A 保持 true,變數 B 保持 false,則:

示例

運算子 描述 示例
&& (邏輯與) 稱為邏輯 AND 運算子。如果兩個運算元都非零,則條件為真。 (A && B) 為假
|| (邏輯或) 稱為邏輯或運算子。如果兩個運算元中任何一個非零,則條件為真。 (A || B) 為真
! (邏輯非) 稱為邏輯非運算子。用於反轉其運算元的邏輯狀態。如果條件為真,則邏輯非運算子將使其為假。 !(A && B) 為真

賦值運算子

以下是 Java 語言支援的賦值運算子:

示例

運算子 描述 示例
= 簡單賦值運算子。將右側運算元的值賦給左側運算元。 C = A + B 將 A + 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 <<= 2 與 C = C << 2 相同
>>= 右移賦值運算子。 C >>= 2 與 C = C >> 2 相同
&= 按位與賦值運算子。 C &= 2 與 C = C & 2 相同
^= 按位異或賦值運算子。 C ^= 2 與 C = C ^ 2 相同
|= 按位或賦值運算子。 C |= 2 與 C = C | 2 相同

其他運算子

Java 語言還支援一些其他運算子。

條件運算子 (? :)

條件運算子也稱為**三元運算子**。此運算子包含三個運算元,用於評估布林表示式。運算子的目標是決定應為變數賦值哪個值。運算子寫為:

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

以下是一個示例:

示例

在此示例中,我們建立了兩個變數 a 和 b,並使用**三元運算子**確定了 b 的值並打印出來。

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 );
   }
}

輸出

Value of b is : 30
Value of b is : 20

instanceof 運算子

此運算子僅用於物件引用變數。運算子檢查物件是否為特定型別(類型別或介面型別)。instanceof 運算子寫為:

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

如果運算子左側變數引用的物件透過右側類/介面型別的 IS-A 檢查,則結果為真。以下是一個示例:

示例

在此示例中,我們建立了一個 String 變數 name,然後使用**instanceof 運算子**檢查 name 是否為 String 型別。

public class Test {

   public static void main(String args[]) {

      String name = "James";

      // following will return true since name is type of String
      boolean result = name instanceof String;
      System.out.println( result );
   }
}

輸出

true

如果被比較的物件與右側的型別賦值相容,則此運算子仍將返回 true。以下是一個額外的示例:

示例

在此示例中,我們建立了一個 Vehicle 類的變數**a**,然後使用**instanceof 運算子**檢查 name 是否為 Car 型別。

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 運算子的優先順序

運算子優先順序決定了表示式中項的組合方式。這會影響表示式的計算方式。某些運算子比其他運算子具有更高的優先順序;例如,乘法運算子的優先順序高於加法運算子:

例如,x = 7 + 3 * 2;此處 x 賦值為 13,而不是 20,因為運算子 * 的優先順序高於 +,所以它首先與 3 * 2 相乘,然後加到 7 中。

此處,優先順序最高的運算子出現在表的上方,優先順序最低的出現在下方。在表示式中,將首先計算優先順序較高的運算子。

類別 運算子 結合性
字尾 expression++ expression-- 從左到右
一元 ++expression --expression +expression -expression ⁓ ! 從右到左
乘法 * / % 從左到右
加法 + - 從左到右
移位 << >> >>> 從左到右
關係 < > <= >= instanceof 從左到右
相等 == != 從左到右
按位與 & 從左到右
按位異或 ^ 從左到右
按位或 | 從左到右
邏輯與 && 從左到右
邏輯或 || 從左到右
條件 ?: 從右到左
賦值 = += -= *= /= %= ^= |= <<= >>= >>>= 從右到左

下一步是什麼?

下一章將解釋 Java 程式設計中的迴圈控制。本章將描述各種型別的迴圈以及如何在 Java 程式開發中使用這些迴圈以及它們的使用目的。

Java - 迴圈控制

可能需要多次執行一段程式碼的情況。通常,語句按順序執行:函式中的第一個語句首先執行,然後是第二個語句,依此類推。

程式語言提供各種控制結構,允許更復雜的執行路徑。

**迴圈**語句允許我們多次執行語句或語句組,以下是大多數程式語言中迴圈語句的一般形式:

Loop Architecture

Java 程式語言提供以下型別的迴圈來處理迴圈需求。點選以下連結檢視詳細資訊。

序號 迴圈和描述
1 while 迴圈

在給定條件為真時重複語句或語句組。它在執行迴圈體之前測試條件。

2 for 迴圈

多次執行一系列語句,並縮寫管理迴圈變數的程式碼。

3 do...while 迴圈

類似於 while 語句,不同之處在於它在迴圈體末尾測試條件。

迴圈控制語句

迴圈控制語句會更改其正常順序的執行。當執行離開作用域時,在該作用域中建立的所有自動物件都會被銷燬。

Java 支援以下控制語句。點選以下連結檢視詳細資訊。

序號 控制語句和描述
1 break 語句

終止**迴圈**或**switch**語句,並將執行轉移到迴圈或switch之後的語句。

2 continue 語句

導致迴圈跳過其主體其餘部分,並在重新迭代之前立即重新測試其條件。

Java 中的增強型 for 迴圈

從 Java 5 開始,引入了增強型 for 迴圈。這主要用於遍歷集合元素,包括陣列。

語法

以下是增強型 for 迴圈的語法:

for(declaration : expression) {
   // Statements
}
  • **宣告** - 新宣告的塊變數,其型別與您要訪問的陣列的元素相容。該變數將在 for 塊中可用,其值將與當前陣列元素相同。

  • **表示式** - 此表示式計算出您需要迴圈遍歷的陣列。表示式可以是陣列變數或返回陣列的方法呼叫。

示例

在此示例中,我們展示了使用 foreach 迴圈列印陣列內容的方法。在這裡,我們建立了一個整數陣列 numbers 併為其初始化了一些值。然後使用 foreach 迴圈列印每個數字。建立另一個字串陣列 names 並迭代它以使用 foreach 迴圈列印每個元素。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};

      for(int x : numbers ) {
         System.out.print( x );
         System.out.print(",");
      }
      System.out.print("\n");
      String [] names = {"James", "Larry", "Tom", "Lacy"};

      for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
      }
   }
}

輸出

10, 20, 30, 40, 50,
James, Larry, Tom, Lacy,

更多關於For each Loop的示例

下一步是什麼?

在下一章中,我們將學習 Java 程式設計中的決策語句。

Java - 決策制定

決策結構具有一個或多個條件,由程式進行評估或測試,以及如果確定條件為真則要執行的語句或語句,以及可選地,如果確定條件為假則要執行的其他語句。

以下是大多數程式語言中典型的決策結構的一般形式:

Decision Making

Java 程式語言提供以下型別的決策語句。點選以下連結檢視詳細資訊。

序號 語句和描述
1 if 語句

**if 語句**由一個布林表示式和一個或多個語句組成。

2 if...else 語句

**if 語句**可以後跟可選的**else 語句**,當布林表示式為假時執行。

3 巢狀 if 語句

可以在另一個**if**或**else if**語句中使用一個**if**或**else if**語句。

4 switch 語句

switch語句允許將變數與一系列值進行相等性測試。

? : 運算子

我們在上一章中介紹了**條件運算子 ? :**,它可以用來代替**if...else**語句。它具有以下一般形式:

Exp1 ? Exp2 : Exp3;

其中 Exp1、Exp2 和 Exp3 是表示式。注意冒號的使用和位置。

要確定整個表示式的值,首先計算 exp1。

  • 如果 exp1 的值為真,則 Exp2 的值將成為整個表示式的值。

  • 如果 exp1 的值為假,則計算 Exp3,其值成為整個表示式的值。

示例

在此示例中,我們建立了兩個變數 a 和 b,並使用**三元運算子**確定了 b 的值並打印出來。

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 );
   }
}

輸出

Value of b is : 30
Value of b is : 20

下一步是什麼?

在下一章中,我們將討論 Java 語言中的 Number 類(在 java.lang 包中)及其子類。

我們將研究一些使用這些類的例項而不是原始資料型別的情況,以及在使用數字時需要了解的格式化、數學函式等類。

Java - if-else 語句

if語句後面可以跟一個可選的else語句,當布林表示式為false時執行。

語法

以下是 if...else 語句的語法:

if(Boolean_expression) {
   // Executes when the Boolean expression is true
}else {
   // Executes when the Boolean expression is false
}

如果布林表示式計算結果為真,則執行 if 程式碼塊,否則執行 else 程式碼塊。

流程圖

If Else Statement

示例 1

在此示例中,我們展示了 if else 語句的使用。我們建立了一個變數 x 並將其初始化為 30。然後在 if 語句中,我們用 20 檢查 x。由於 if 語句為假,因此執行 else 塊中的語句。

public class Test {

   public static void main(String args[]) {
      int x = 30;

      if( x < 20 ) {
         System.out.print("This is if statement");
      }else {
         System.out.print("This is else statement");
      }
   }
}

輸出

This is else statement

if...else if...else 語句

if 語句可以後跟可選的else if...else語句,這對於使用單個 if...else if 語句測試各種條件非常有用。

使用 if、else if、else 語句時,需要注意以下幾點。

  • if 可以有零個或一個 else,並且它必須放在任何 else if 之後。

  • if 可以有零到多個 else if,並且它們必須放在 else 之前。

  • 一旦 else if 成功,將不會測試任何剩餘的 else if 或 else。

語法

以下是 if...else 語句的語法:

if(Boolean_expression 1) {
   // Executes when the Boolean expression 1 is true
}else if(Boolean_expression 2) {
   // Executes when the Boolean expression 2 is true
}else if(Boolean_expression 3) {
   // Executes when the Boolean expression 3 is true
}else {
   // Executes when the none of the above condition is true.
}

示例 2

在此示例中,我們展示了 if...else if...else 語句的使用。我們建立了一個變數 x 並將其初始化為 30。然後在 if 語句中,我們用 10 檢查 x。由於 if 語句為假,控制跳轉到 else if 語句,用 x 檢查另一個值,依此類推。

public class Test {

   public static void main(String args[]) {
      int x = 30;

      if( x == 10 ) {
         System.out.print("Value of X is 10");
      }else if( x == 20 ) {
         System.out.print("Value of X is 20");
      }else if( x == 30 ) {
         System.out.print("Value of X is 30");
      }else {
         System.out.print("This is else statement");
      }
   }
}

輸出

Value of X is 30

示例 3

在此示例中,我們展示了 if...else if...else 語句的使用。我們建立了一個變數 x 並將其初始化為 30.0。然後在 if 語句中,我們用 10.0 檢查 x。由於 if 語句為假,控制跳轉到 else if 語句,用 x 檢查另一個值,依此類推。

public class Test {

   public static void main(String args[]) {
      double x = 30.0;

      if( x == 10.0 ) {
         System.out.print("Value of X is 10.0");
      }else if( x == 20.0 ) {
         System.out.print("Value of X is 20.0");
      }else if( x == 30.0 ) {
         System.out.print("Value of X is 30.0");
      }else {
         System.out.print("This is else statement");
      }
   }
}

輸出

Value of X is 30.0

Java - switch 語句

**switch**語句允許測試變數與值的列表是否相等。每個值都稱為一個 case,並且會針對每個 case 檢查正在切換的變數。

語法

增強型 switch 語句的語法為:

switch(expression) {
   case value :
      // Statements
      break; // optional
   
   case value :
      // Statements
      break; // optional
   
   // You can have any number of case statements.
   default : // Optional
      // Statements
}

以下規則適用於**switch**語句:

  • switch 語句中使用的變數只能是整數、可轉換整數(byte、short、char)、字串和列舉。

  • switch 中可以有任意數量的 case 語句。每個 case 後跟要比較的值和一個冒號。

  • case的值必須與switch語句中變數的資料型別相同,並且必須是常量或字面量。

  • 當被切換的變數等於某個case時,緊跟在這個case後面的語句將被執行,直到遇到break語句。

  • 遇到break語句時,switch語句終止,控制流跳轉到switch語句後的下一行。

  • 並非每個case都需要包含break。如果沒有break,控制流將貫穿到後續的case,直到遇到break。

  • switch語句可以有一個可選的default case,它必須出現在switch的末尾。當沒有一個case為真時,可以使用default case執行任務。default case不需要break。

流程圖

Switch Statement

示例 1

在這個例子中,我們展示了switch語句的使用,其中case基於char型別。我們建立了一個變數grade。根據grade的值,檢查每個case。如果一個case滿足條件並且存在break語句,則不會檢查後面的case。

public class Test {

   public static void main(String args[]) {
      char grade = 'C';

      switch(grade) {
         case 'A' :
            System.out.println("Excellent!"); 
            break;
         case 'B' :
         case 'C' :
            System.out.println("Well done");
            break;
         case 'D' :
            System.out.println("You passed");
         case 'F' :
            System.out.println("Better try again");
            break;
         default :
            System.out.println("Invalid grade");
      }
      System.out.println("Your grade is " + grade);
   }
}

使用各種命令列引數編譯並執行上述程式。這將產生以下結果:

輸出

Well done
Your grade is C

示例 2

在這個例子中,我們展示了switch語句的使用,其中case基於int型別。我們建立了一個變數grade。根據grade的值,檢查每個case。如果一個case滿足條件並且存在break語句,則不會檢查後面的case。

public class Test {

   public static void main(String args[]) {
      int grade = 3;

      switch(grade) {
         case 1 :
            System.out.println("Excellent!"); 
            break;
         case 2 :
         case 3 :
            System.out.println("Well done");
            break;
         case 4 :
            System.out.println("You passed");
         case 5 :
            System.out.println("Better try again");
            break;
         default :
            System.out.println("Invalid grade");
      }
      System.out.println("Your grade is " + grade);
   }
}

使用各種命令列引數編譯並執行上述程式。這將產生以下結果:

輸出

Well done
Your grade is 3

示例 3

在這個例子中,我們展示了switch語句的使用,其中case基於String型別。我們建立了一個變數grade。根據grade的值,檢查每個case。如果一個case滿足條件並且存在break語句,則不會檢查後面的case。

public class Test {

   public static void main(String args[]) {
      String grade = "C";

      switch(grade) {
         case "A" :
            System.out.println("Excellent!"); 
            break;
         case "B" :
         case "C" :
            System.out.println("Well done");
            break;
         case "D" :
            System.out.println("You passed");
         case "F" :
            System.out.println("Better try again");
            break;
         default :
            System.out.println("Invalid grade");
      }
      System.out.println("Your grade is " + grade);
   }
}

使用各種命令列引數編譯並執行上述程式。這將產生以下結果:

輸出

Well done
Your grade is C

Java - for迴圈

for迴圈是一種重複控制結構,允許你高效地編寫需要執行特定次數的迴圈。

當你知道任務需要重複多少次時,for迴圈非常有用。

語法

for迴圈的語法如下:

for(initialization; Boolean_expression; update) {
   // Statements
}

以下是for迴圈的控制流程:

  • 初始化步驟首先執行,並且只執行一次。此步驟允許你宣告和初始化任何迴圈控制變數,此步驟以分號(;)結尾。

  • 接下來,布林表示式將被評估。如果為真,則執行迴圈體。如果為假,則不會執行迴圈體,控制流跳轉到for迴圈後的下一條語句。

  • for迴圈體執行完畢後,控制流跳回到更新語句。此語句允許你更新任何迴圈控制變數。此語句可以留空,並在末尾新增分號。

  • 布林表示式現在再次被評估。如果為真,則迴圈執行,並重復此過程(迴圈體,然後更新步驟,然後布林表示式)。布林表示式為假後,for迴圈終止。

流程圖

Java For Loop

示例 1

在這個例子中,我們展示了for迴圈的使用,用於列印從10到19的數字。在這裡,我們在for迴圈的初始化塊中用值10初始化了一個int變數x。然後在表示式塊中,我們檢查x是否小於20,最後在更新塊中,我們將x加1。在for迴圈體中,我們列印x的值。for迴圈將執行直到x變為20。一旦x為20,迴圈將停止執行,程式退出。

public class Test {

   public static void main(String args[]) {

      for(int x = 10; x < 20; x = x + 1) {
         System.out.print("value of x : " + x );
         System.out.print("\n");
      }
   }
}

輸出

value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19

示例 2

在這個例子中,我們展示了for迴圈的使用,用於列印陣列的內容。在這裡,我們建立一個整數陣列numbers並初始化一些值。我們建立了一個名為index的變數來表示for迴圈中陣列的索引,將其與陣列的大小進行比較,並將其加1。在for迴圈體中,我們使用索引表示法列印陣列的元素。一旦index與陣列大小相同,for迴圈退出,程式結束。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};

      for(int index = 0; index < numbers.length; index++) {
         System.out.print("value of item : " + numbers[index] );
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50

示例 3

在這個例子中,我們展示了使用for迴圈實現無限迴圈。它將一直列印數字,直到你按下ctrl+c終止程式。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      for( ;; ) {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 11
value of item : 12
value of item : 13
value of item : 14
...
ctrl+c

Java - foreach迴圈

foreach迴圈是一種特殊的重複控制結構,允許你高效地編寫需要執行特定次數的迴圈。

即使你不知道任務需要重複多少次,foreach迴圈也很有用。

語法

以下是增強型 for 迴圈的語法:

for(declaration : expression) {
   // Statements
}
  • **宣告** - 新宣告的塊變數,其型別與您要訪問的陣列的元素相容。該變數將在 for 塊中可用,其值將與當前陣列元素相同。

  • **表示式** - 此表示式計算出您需要迴圈遍歷的陣列。表示式可以是陣列變數或返回陣列的方法呼叫。

示例 1

在這個例子中,我們展示了foreach迴圈的使用,用於列印整數列表的內容。在這裡,我們建立一個整數列表numbers並初始化一些值。然後使用foreach迴圈列印每個數字。

import java.util.Arrays;
import java.util.List;

public class Test {

   public static void main(String args[]) {
      List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);

      for(Integer x : numbers ) {
         System.out.print( x );
         System.out.print(",");
      }
   }
}

輸出

10, 20, 30, 40, 50,

示例 2

在這個例子中,我們展示了foreach迴圈的使用,用於列印字串列表的內容。在這裡,我們建立一個字串陣列names並初始化一些值。然後使用foreach迴圈列印每個name。

import java.util.Arrays;
import java.util.List;

public class Test {

   public static void main(String args[]) {
      List<String> names = Arrays.asList("James", "Larry", "Tom", "Lacy");

      for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
      }
   }
}

輸出

James, Larry, Tom, Lacy,

示例 3

在這個例子中,我們展示了foreach迴圈的使用,用於列印Student物件的陣列內容。在這裡,我們建立一個Student物件的陣列students並初始化一些值。然後使用foreach迴圈列印每個name。

public class Test {

   public static void main(String args[]) {
      Student[] students = { new Student(1, "Julie"), new Student(3, "Adam"), new Student(2, "Robert") };

      for( Students student : students ) {
         System.out.print( student );
         System.out.print(",");
      }
   }
}
class Student {
   int rollNo;
   String name;

   Student(int rollNo, String name){
      this.rollNo = rollNo;
      this.name = name;
   }

   @Override
   public String toString() {
      return "[ " + this.rollNo + ", " + this.name + " ]";
   }
}

輸出

[ 1, Julie ],[ 3, Adam ],[ 2, Robert ],

Java - while迴圈

Java 程式語言中的while迴圈語句重複執行目標語句,只要給定的條件為真。

語法

while迴圈的語法如下:

while(Boolean_expression) {
   // Statements
}

這裡,語句可以是單個語句或語句塊。條件可以是任何表示式,非零值表示真。

執行時,如果布林表示式結果為真,則將執行迴圈內的操作。只要表示式的結果為真,這將繼續進行。

當條件變為假時,程式控制將傳遞到緊跟在迴圈後面的行。

流程圖

Java While Loop

這裡,while迴圈的關鍵點是迴圈可能永遠不會執行。當表示式被測試且結果為假時,迴圈體將被跳過,並將執行while迴圈後的第一條語句。

示例 1

在這個例子中,我們展示了while迴圈的使用,用於列印從10到19的數字。在這裡,我們用值10初始化了一個int變數x。然後在while迴圈中,我們檢查x是否小於20,在while迴圈內,我們列印x的值並將x的值加1。while迴圈將執行直到x變為20。一旦x為20,迴圈將停止執行,程式退出。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      while( x < 20 ) {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }
   }
}

輸出

value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19

示例 2

在這個例子中,我們展示了while迴圈的使用,用於列印陣列的內容。在這裡,我們建立一個整數陣列numbers並初始化一些值。我們建立了一個名為index的變數來表示迭代陣列時的陣列索引。在while迴圈中,我們檢查index是否小於陣列的大小,並使用索引表示法列印陣列的元素。index變數加1,迴圈繼續直到index變為陣列大小,迴圈退出。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};
      int index = 0;

      while( index < 5 ) {
         System.out.print("value of item : " + numbers[index] );
         index++;
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50

示例 3

在這個例子中,我們展示了使用while迴圈實現無限迴圈。它將一直列印數字,直到你按下ctrl+c終止程式。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      while( true ) {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50
...
ctrl+c

Java - do...while迴圈

do...while迴圈類似於while迴圈,不同之處在於do...while迴圈至少執行一次。

語法

do...while迴圈的語法如下:

do {
   // Statements
}while(Boolean_expression);

請注意,布林表示式出現在迴圈的末尾,因此迴圈中的語句將在測試布林表示式之前執行一次。

如果布林表示式為真,控制流將跳回到do語句,迴圈中的語句將再次執行。這個過程重複直到布林表示式為假。

流程圖

Java Do While Loop

示例 1

在這個例子中,我們展示了while迴圈的使用,用於列印從10到19的數字。在這裡,我們用值10初始化了一個int變數x。然後在do while迴圈中,我們在do while迴圈體之後檢查x是否小於20。在do while迴圈體中,我們列印x的值並將x的值加1。while迴圈將執行直到x變為20。一旦x為20,迴圈將停止執行,程式退出。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      do {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }while( x < 20 );
   }
}

輸出

value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 15
value of x : 16
value of x : 17
value of x : 18
value of x : 19

示例 2

在這個例子中,我們展示了do while迴圈的使用,用於列印陣列的內容。在這裡,我們建立一個整數陣列numbers並初始化一些值。我們建立了一個名為index的變數來表示迭代陣列時的陣列索引。在do while迴圈中,我們在迴圈體之後檢查index是否小於陣列的大小,並使用索引表示法列印陣列的元素。在迴圈體內,index變數加1,迴圈繼續直到index變為陣列大小,迴圈退出。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};
      int index = 0;

      do {
         System.out.print("value of item : " + numbers[index] );
         index++;
         System.out.print("\n");
      } while( index < 5 );
   }
}

輸出

value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50

示例 3

在這個例子中,我們展示了使用while迴圈實現無限迴圈。它將一直列印數字,直到你按下ctrl+c終止程式。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      do {
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      } while( true );
   }
}

輸出

value of item : 10
value of item : 20
value of item : 30
value of item : 40
value of item : 50
...
ctrl+c

Java - break語句

Java 程式語言中的break語句有以下兩種用法:

  • 當在迴圈內遇到break語句時,迴圈會立即終止,程式控制會在迴圈後的下一條語句處繼續。

  • 它可以用來終止switch語句中的case(在下一章中介紹)。

語法

break的語法是在任何迴圈內的一個單一語句:

break;

流程圖

Java Break Statement

示例 1

在這個例子中,我們展示了break語句的使用,用於中斷while迴圈,列印從10到14的數字,否則將列印到19的元素。在這裡,我們用值10初始化了一個int變數x。然後在while迴圈中,我們檢查x是否小於20,在while迴圈內,我們列印x的值並將x的值加1。while迴圈將執行直到x變為15。一旦x為15,break語句將中斷while迴圈,程式退出。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      while( x < 20 ) {
         if(x == 15){
            break;		 
         }	     
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }
   }
}

輸出

value of x : 10
value of x : 11
value of x : 12
value of x : 13
value of x : 14

示例 2

在這個例子中,我們展示了break語句在for迴圈中的使用,用於列印陣列的幾個元素而不是所有元素。在這裡,我們建立一個整數陣列numbers並初始化一些值。我們建立了一個名為index的變數來表示for迴圈中陣列的索引,將其與陣列的大小進行比較,並將其加1。在for迴圈體中,我們使用索引表示法列印陣列的元素。一旦遇到值30,break語句將中斷for迴圈的流程,程式結束。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};

      for(int index = 0; index < numbers.length; index++) {
         if(numbers[index] == 30){
            break;
         }
         System.out.print("value of item : " + numbers[index] );         
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 20

示例 3

在這個例子中,我們展示了break語句用於中斷使用while迴圈的無限迴圈。它將一直列印數字,直到x的值變為15。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      while( true ) {
         System.out.print("value of x : " + x );
         x++;
         if(x == 15) {
            break;
         }
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 11
value of item : 12
value of item : 13
value of item : 14

Java - continue語句

continue關鍵字可以在任何迴圈控制結構中使用。它導致迴圈立即跳轉到迴圈的下一個迭代。

  • 在for迴圈中,continue關鍵字導致控制立即跳轉到更新語句。

  • 在while迴圈或do/while迴圈中,控制立即跳轉到布林表示式。

語法

continue的語法是在任何迴圈內的一個單一語句:

continue;

流程圖

Java Continue Statement

示例 1

在這個例子中,我們展示了continue語句的使用,用於跳過while迴圈中的元素15,該迴圈用於列印從10到19的元素。在這裡,我們用值10初始化了一個int變數x。然後在while迴圈中,我們檢查x是否小於20,在while迴圈內,我們列印x的值並將x的值加1。while迴圈將執行直到x變為15。一旦x為15,continue語句將跳過while迴圈,同時跳過主體的執行,迴圈繼續。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      while( x < 20 ) {
         x++;
         if(x == 15){
            continue;		 
         }   
         System.out.print("value of x : " + x );
         System.out.print("\n");
      }
   }
}

輸出

value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 16
value of x : 17
value of x : 18
value of x : 19
value of x : 20

示例 2

本例展示瞭如何在 for 迴圈中使用 continue 語句跳過陣列元素的列印。這裡我們建立了一個整數陣列 numbers 並初始化了一些值。我們建立了一個名為 index 的變數來表示 for 迴圈中陣列的索引,將其與陣列的大小進行比較,並將其遞增 1。在 for 迴圈體中,我們使用索引表示法列印陣列的元素。一旦遇到值為 30,continue 語句就會跳轉到 for 迴圈的更新部分,迴圈繼續。

public class Test {

   public static void main(String args[]) {
      int [] numbers = {10, 20, 30, 40, 50};

      for(int index = 0; index < numbers.length; index++) {
         if(numbers[index] == 30){
            continue;
         }
         System.out.print("value of item : " + numbers[index] );         
         System.out.print("\n");
      }
   }
}

輸出

value of item : 10
value of item : 20
value of item : 40
value of item : 50

示例 3

本例展示瞭如何在 do while 迴圈中使用 continue 語句跳過元素 15,該迴圈用於列印從 10 到 19 的元素。這裡我們用值 10 初始化了一個 int 變數 x。然後在 do while 迴圈中,我們在迴圈體之後檢查 x 是否小於 20,在 while 迴圈中,我們列印 x 的值並將 x 的值遞增 1。while 迴圈將執行直到 x 變成 15。一旦 x 為 15,continue 語句將跳過 while 迴圈,跳過迴圈體的執行,迴圈繼續。

public class Test {

   public static void main(String args[]) {
      int x = 10;

      do {
         x++;
         if(x == 15){
            continue;		 
         }   
         System.out.print("value of x : " + x );
         System.out.print("\n");
      } while( x < 20 );
   }
}

輸出

value of x : 11
value of x : 12
value of x : 13
value of x : 14
value of x : 16
value of x : 17
value of x : 18
value of x : 19
value of x : 20

Java - 物件和類

Java 是一種面向物件的語言。作為一種具有面向物件特性的語言,Java 支援以下基本概念:

  • 多型性
  • 繼承
  • 封裝
  • 抽象
  • 物件
  • 例項
  • 方法
  • 訊息傳遞

本章我們將深入研究類和物件這兩個概念。

  • 物件 - 物件具有狀態和行為。例如:狗具有狀態——顏色、名字、品種,以及行為——搖尾巴、吠叫、吃東西。物件是類的例項。

  • - 類可以定義為一種模板/藍圖,它描述了其型別物件支援的行為/狀態。

Java 中的物件

現在讓我們深入瞭解一下什麼是物件。如果我們考慮現實世界,我們可以發現我們周圍有很多物件,例如汽車、狗、人等等。所有這些物件都具有狀態和行為。

如果我們考慮一隻狗,那麼它的狀態是——名字、品種、顏色,而行為是——吠叫、搖尾巴、奔跑。

如果你將軟體物件與現實世界中的物件進行比較,它們具有非常相似的特徵。

軟體物件也具有狀態和行為。軟體物件的狀態儲存在欄位中,行為透過方法顯示。

因此,在軟體開發中,方法操作物件的內部狀態,物件與物件之間的通訊透過方法完成。

Java 中的類

類是一個藍圖,從中建立單個物件。

下面是一個類的示例。

示例

public class Dog {
   String breed;
   int age;
   String color;

   void barking() {
   }

   void hungry() {
   }

   void sleeping() {
   }
}

類可以包含以下任何型別的變數。

  • 區域性變數 - 在方法、建構函式或程式碼塊內定義的變數稱為區域性變數。該變數將在方法內宣告和初始化,並在方法完成後銷燬。

  • 例項變數 - 例項變數是在類內但在任何方法之外的變數。這些變數在類例項化時初始化。例項變數可以從該特定類的任何方法、建構函式或程式碼塊中訪問。

  • 類變數 - 類變數是在類內、任何方法之外使用 static 關鍵字宣告的變數。

類可以擁有任意數量的方法來訪問各種方法的值。在上面的例子中,barking()、hungry() 和 sleeping() 是方法。

在研究 Java 語言的類時,需要討論以下一些重要主題。

建構函式

在討論類時,最重要的子主題之一就是建構函式。每個類都有一個建構函式。如果我們沒有顯式地為類編寫建構函式,Java 編譯器將為該類構建一個預設建構函式。

每次建立一個新物件時,至少會呼叫一個建構函式。建構函式的主要規則是它們應該與類名相同。一個類可以有多個建構函式。

下面是一個建構函式的例子:

示例

public class Puppy {
   public Puppy() {
   }

   public Puppy(String name) {
      // This constructor has one parameter, name.
   }
}

Java 還支援單例類,你只能建立一個類的例項。

注意 - 我們有兩種不同型別的建構函式。我們將在後續章節中詳細討論建構函式。

建立物件

如前所述,類為物件提供藍圖。所以基本上,物件是由類建立的。在 Java 中,new 關鍵字用於建立新物件。

從類建立物件時,有三個步驟:

  • 宣告 - 使用物件型別的變數名宣告變數。

  • 例項化 - 使用 'new' 關鍵字建立物件。

  • 初始化 - 'new' 關鍵字後跟對建構函式的呼叫。此呼叫初始化新物件。

下面是建立物件的示例:

示例

在這個例子中,我們建立了一個名為 Puppy 的類。在 Puppy 類的建構函式中,列印幼犬的名字,以便在建立物件時列印其名字。在 main 方法中,使用 new 運算子建立一個物件。

public class Puppy {
   public Puppy(String name) {
      // This constructor has one parameter, <i>name</i>.
      System.out.println("Passed Name is :" + name );
   }

   public static void main(String []args) {
      // Following statement would create an object myPuppy
      Puppy myPuppy = new Puppy( "tommy" );
   }
}

如果我們編譯並執行上面的程式,它將產生以下結果:

輸出

Passed Name is :tommy

訪問例項變數和方法

例項變數和方法透過建立的物件訪問。要訪問例項變數,完整的限定路徑如下:

/* First create an object */
ObjectReference = new Constructor();

/* Now call a variable as follows */
ObjectReference.variableName;

/* Now you can call a class method as follows */
ObjectReference.MethodName();

示例

在這個例子中,我們建立了一個名為 Puppy 的類。在 Puppy 類的建構函式中,列印幼犬的名字,以便在建立物件時列印其名字。添加了一個例項變數 puppyAge,並使用 getter/setter 方法,我們可以操作年齡。在 main 方法中,使用 new 運算子建立一個物件。使用 setAge() 方法更新年齡,並使用 getAge() 列印年齡。

public class Puppy {
   int puppyAge;

   public Puppy(String name) {
      // This constructor has one parameter, <i>name</i>.
      System.out.println("Name chosen is :" + name );
   }

   public void setAge( int age ) {
      puppyAge = age;
   }

   public int getAge( ) {
      System.out.println("Puppy's age is :" + puppyAge );
      return puppyAge;
   }

   public static void main(String []args) {
      /* Object creation */
      Puppy myPuppy = new Puppy( "tommy" );

      /* Call class method to set puppy's age */
      myPuppy.setAge( 2 );

      /* Call another class method to get puppy's age */
      myPuppy.getAge( );

      /* You can access instance variable as follows as well */
      System.out.println("Variable Value :" + myPuppy.puppyAge );
   }
}

如果我們編譯並執行上面的程式,它將產生以下結果:

輸出

Name chosen is :tommy
Puppy's age is :2
Variable Value :2

原始檔宣告規則

作為本節的最後一部分,現在讓我們來看看原始檔宣告規則。在原始檔中宣告類、import 語句和package 語句時,這些規則至關重要。

  • 每個原始檔只能有一個公共類。

  • 原始檔可以有多個非公共類。

  • 公共類名也應該與原始檔名相同,原始檔名末尾應附加.java。例如:類名是public class Employee{},則原始檔應為 Employee.java。

  • 如果類在包內定義,則包語句應該是原始檔中的第一條語句。

  • 如果存在 import 語句,則必須將它們寫在包語句和類宣告之間。如果沒有包語句,則 import 語句應該是原始檔中的第一行。

  • Import 和 package 語句將應用於原始檔中所有存在的類。不可能為原始檔中的不同類宣告不同的 import 和/或 package 語句。

類有幾個訪問級別,並且有不同型別的類;抽象類、最終類等。我們將在訪問修飾符章節中解釋所有這些。

除了上述型別的類之外,Java 還有一些特殊的類,稱為內部類和匿名類。

Java 包

簡單來說,它是一種對類和介面進行分類的方法。在使用 Java 開發應用程式時,將編寫數百個類和介面,因此對這些類進行分類是必須的,並且使生活更輕鬆。

匯入語句

在 Java 中,如果給出了一個完全限定名(包括包名和類名),則編譯器可以輕鬆找到原始碼或類。匯入語句是為編譯器提供正確位置以查詢特定類的一種方法。

例如,以下行將要求編譯器載入 java_installation/java/io 目錄中可用的所有類:

import java.io.*;

一個簡單的案例研究

對於我們的案例研究,我們將建立兩個類。它們是 Employee 和 EmployeeTest。

首先開啟記事本並新增以下程式碼。記住這是一個 Employee 類,並且該類是一個公共類。現在,將此原始檔儲存為 Employee.java。

Employee 類有四個例項變數 - name、age、designation 和 salary。該類有一個顯式定義的建構函式,它接受一個引數。

示例

import java.io.*;
public class Employee {

   String name;
   int age;
   String designation;
   double salary;

   // This is the constructor of the class Employee
   public Employee(String name) {
      this.name = name;
   }

   // Assign the age of the Employee  to the variable age.
   public void empAge(int empAge) {
      age = empAge;
   }

   /* Assign the designation to the variable designation.*/
   public void empDesignation(String empDesig) {
      designation = empDesig;
   }

   /* Assign the salary to the variable	salary.*/
   public void empSalary(double empSalary) {
      salary = empSalary;
   }

   /* Print the Employee details */
   public void printEmployee() {
      System.out.println("Name:"+ name );
      System.out.println("Age:" + age );
      System.out.println("Designation:" + designation );
      System.out.println("Salary:" + salary);
   }
}

如本教程前面提到的,處理從 main 方法開始。因此,為了執行此 Employee 類,應該有一個 main 方法,並且應該建立物件。我們將為這些任務建立一個單獨的類。

以下是EmployeeTest 類,它建立 Employee 類的兩個例項,並呼叫每個物件的方法為每個變數賦值。

將以下程式碼儲存在 EmployeeTest.java 檔案中。

import java.io.*;
public class EmployeeTest {

   public static void main(String args[]) {
      /* Create two objects using constructor */
      Employee empOne = new Employee("James Smith");
      Employee empTwo = new Employee("Mary Anne");

      // Invoking methods for each object created
      empOne.empAge(26);
      empOne.empDesignation("Senior Software Engineer");
      empOne.empSalary(1000);
      empOne.printEmployee();

      empTwo.empAge(21);
      empTwo.empDesignation("Software Engineer");
      empTwo.empSalary(500);
      empTwo.printEmployee();
   }
}

現在,編譯這兩個類,然後執行EmployeeTest 以檢視如下結果:

輸出

C:\> javac Employee.java
C:\> javac EmployeeTest.java
C:\> java EmployeeTest
Name:James Smith
Age:26
Designation:Senior Software Engineer
Salary:1000.0
Name:Mary Anne
Age:21
Designation:Software Engineer
Salary:500.0

下一步是什麼?

在下一節中,我們將討論 Java 中的基本資料型別以及在開發 Java 應用程式時如何使用它們。

Java - 方法

Java 方法是將語句組合在一起以執行操作的語句集合。例如,當你呼叫 System.out.println() 方法時,系統實際上執行多個語句以在控制檯上顯示訊息。

現在,你將學習如何建立帶有或不帶有返回值的方法,呼叫帶有或不帶有引數的方法,以及在程式設計中應用方法抽象。

建立方法

考慮以下示例來解釋方法的語法:

語法

public static int methodName(int a, int b) {
   // body
}

這裡,

  • public static - 修飾符

  • int - 返回型別

  • methodName - 方法名稱

  • a, b - 形式引數

  • int a, int b - 引數列表

方法定義由方法頭和方法體組成。在以下語法中顯示相同的內容:

語法

modifier returnType nameOfMethod (Parameter List) {
   // method body
}

上面顯示的語法包括:

  • 修飾符 - 它定義方法的訪問型別,使用是可選的。

  • returnType - 方法可能返回一個值。

  • nameOfMethod − 這是方法名。方法簽名由方法名和引數列表組成。

  • 引數列表 − 引數的列表,包括引數的型別、順序和數量。引數是可選的,方法可以不包含任何引數。

  • 方法體 − 方法體定義了方法使用語句執行的操作。

示例

以下是上面定義的名為min()的方法的原始碼。此方法接受兩個引數num1和num2,並返回兩者之間的最大值:

/** the snippet returns the minimum between two numbers */

public static int minFunction(int n1, int n2) {
   int min;
   if (n1 > n2)
      min = n2;
   else
      min = n1;

   return min; 
}

方法呼叫

要使用方法,必須先呼叫它。呼叫方法有兩種方式:方法返回值或不返回值(無返回值)。

方法呼叫的過程很簡單。當程式呼叫一個方法時,程式控制權會轉移到被呼叫的方法。然後,被呼叫的方法會在兩種情況下將控制權返回給呼叫者:

  • 執行return語句。
  • 到達方法結束的閉合大括號。

返回void的方法被視為對語句的呼叫。讓我們來看一個例子:

System.out.println("This is tutorialspoint.com!");

返回有值的方法可以透過以下示例理解:

int result = sum(6, 9);

以下示例演示瞭如何定義方法以及如何呼叫方法:

示例

public class ExampleMinNumber {
   
   public static void main(String[] args) {
      int a = 11;
      int b = 6;
      int c = minFunction(a, b);
      System.out.println("Minimum Value = " + c);
   }

   /** returns the minimum of two numbers */
   public static int minFunction(int n1, int n2) {
      int min;
      if (n1 > n2)
         min = n2;
      else
         min = n1;

      return min; 
   }
}

輸出

Minimum value = 6

void關鍵字

void關鍵字允許我們建立不返回值的方法。在下面的示例中,我們考慮一個void方法`methodRankPoints`。此方法是一個void方法,不返回任何值。對void方法的呼叫必須是一個語句,例如`methodRankPoints(255.7);`。這是一個Java語句,以分號結尾,如下例所示。

示例

public class ExampleVoid {

   public static void main(String[] args) {
      methodRankPoints(255.7);
   }

   public static void methodRankPoints(double points) {
      if (points >= 202.5) {
         System.out.println("Rank:A1");
      }else if (points >= 122.4) {
         System.out.println("Rank:A2");
      }else {
         System.out.println("Rank:A3");
      }
   }
}

輸出

Rank:A1

按值傳遞引數

在呼叫過程中,需要傳遞引數。這些引數的順序應與其在方法規範中相應引數的順序相同。引數可以按值傳遞或按引用傳遞。

按值傳遞引數意味著使用引數呼叫方法。透過這種方式,引數值將傳遞給引數。

示例

下面的程式顯示了一個按值傳遞引數的示例。即使在方法呼叫之後,引數的值也保持不變。

public class swappingExample {

   public static void main(String[] args) {
      int a = 30;
      int b = 45;
      System.out.println("Before swapping, a = " + a + " and b = " + b);

      // Invoke the swap method
      swapFunction(a, b);
      System.out.println("\n**Now, Before and After swapping values will be same here**:");
      System.out.println("After swapping, a = " + a + " and b is " + b);
   }

   public static void swapFunction(int a, int b) {
      System.out.println("Before swapping(Inside), a = " + a + " b = " + b);
      
      // Swap n1 with n2
      int c = a;
      a = b;
      b = c;
      System.out.println("After swapping(Inside), a = " + a + " b = " + b);
   }
}

輸出

Before swapping, a = 30 and b = 45
Before swapping(Inside), a = 30 b = 45
After swapping(Inside), a = 45 b = 30

**Now, Before and After swapping values will be same here**:
After swapping, a = 30 and b is 45

方法過載

當一個類具有兩個或多個同名但引數不同的方法時,這稱為方法過載。它與重寫不同。在重寫中,方法具有相同的方法名、型別、引數數量等。

讓我們考慮前面討論的用於查詢整數型別最小數字的示例。如果,假設我們想要找到雙精度型別的最小數字。然後將引入過載的概念來建立兩個或多個同名但引數不同的方法。

以下示例解釋了這一點:

示例

public class ExampleOverloading {

   public static void main(String[] args) {
      int a = 11;
      int b = 6;
      double c = 7.3;
      double d = 9.4;
      int result1 = minFunction(a, b);
      
      // same function name with different parameters
      double result2 = minFunction(c, d);
      System.out.println("Minimum Value = " + result1);
      System.out.println("Minimum Value = " + result2);
   }

   // for integer
   public static int minFunction(int n1, int n2) {
      int min;
      if (n1 > n2)
         min = n2;
      else
         min = n1;

      return min; 
   }
   
   // for double
   public static double minFunction(double n1, double n2) {
     double min;
      if (n1 > n2)
         min = n2;
      else
         min = n1;

      return min; 
   }
}

輸出

Minimum Value = 6
Minimum Value = 7.3

過載方法使程式更易讀。這裡給出了兩個同名但引數不同的方法。結果是從整數和雙精度型別中獲得的最小數字。

使用命令列引數

有時,您可能希望在執行程式時向程式傳遞一些資訊。這是透過向`main()`傳遞命令列引數來實現的。

命令列引數是在執行程式時直接跟在程式名稱後面的資訊。在Java程式中訪問命令列引數非常容易。它們作為字串儲存在傳遞給`main()`的String陣列中。

示例

下面的程式顯示了它呼叫的所有命令列引數:

public class CommandLine {

   public static void main(String args[]) { 
      for(int i = 0; i<args.length; i++) {
         System.out.println("args[" + i + "]: " +  args[i]);
      }
   }
}

嘗試按如下所示執行此程式:

$java CommandLine this is a command line 200 -100

輸出

args[0]: this
args[1]: is
args[2]: a
args[3]: command
args[4]: line
args[5]: 200
args[6]: -100

this關鍵字

this是Java中的一個關鍵字,用作當前類物件的引用,位於例項方法或建構函式中。使用`this`,您可以引用類的成員,例如建構函式、變數和方法。

注意 − `this`關鍵字僅在例項方法或建構函式中使用。

This

通常,`this`關鍵字用於:

  • 在建構函式或方法中,如果例項變數和區域性變數具有相同的名稱,則區分它們。

class Student {
   int age;   
   Student(int age) {
      this.age = age;	
   }
}
  • 在一個類中從另一個建構函式(引數化建構函式或預設建構函式)呼叫一種型別的建構函式。這稱為顯式建構函式呼叫。

class Student {
   int age
   Student() {
      this(20);
   }
   
   Student(int age) {
      this.age = age;	
   }
}

示例

以下是一個使用`this`關鍵字訪問類成員的示例。將以下程式複製並貼上到名為This_Example.java的檔案中。

public class This_Example {
   // Instance variable num
   int num = 10;
	
   This_Example() {
      System.out.println("This is an example program on keyword this");	
   }

   This_Example(int num) {
      // Invoking the default constructor
      this();
      
      // Assigning the local variable <i>num</i> to the instance variable <i>num</i>
      this.num = num;	   
   }
   
   public void greet() {
      System.out.println("Hi Welcome to Tutorialspoint");
   }
      
   public void print() {
      // Local variable num
      int num = 20;
      
      // Printing the local variable
      System.out.println("value of local variable num is : "+num);
      
      // Printing the instance variable
      System.out.println("value of instance variable num is : "+this.num);
      
      // Invoking the greet method of a class
      this.greet();     
   }
   
   public static void main(String[] args) {
      // Instantiating the class
      This_Example obj1 = new This_Example();
      
      // Invoking the print method
      obj1.print();
	  
      // Passing a new value to the num variable through parametrized constructor
      This_Example obj2 = new This_Example(30);
      
      // Invoking the print method again
      obj2.print(); 
   }
}

輸出

This is an example program on keyword this 
value of local variable num is : 20
value of instance variable num is : 10
Hi Welcome to Tutorialspoint
This is an example program on keyword this 
value of local variable num is : 20
value of instance variable num is : 30
Hi Welcome to Tutorialspoint

可變引數(var-args)

JDK 1.5允許您向方法傳遞相同型別的可變數量的引數。方法中的引數宣告如下:

typeName... parameterName

在方法宣告中,您指定型別後跟省略號(...)。在一個方法中只能指定一個變長引數,並且此引數必須是最後一個引數。任何常規引數都必須在其前面。

示例

public class VarargsDemo {

   public static void main(String args[]) {
      // Call method with variable args  
	  printMax(34, 3, 3, 2, 56.5);
      printMax(new double[]{1, 2, 3});
   }

   public static void printMax( double... numbers) {
      if (numbers.length == 0) {
         System.out.println("No argument passed");
         return;
      }

      double result = numbers[0];

      for (int i = 1; i <  numbers.length; i++)
      if (numbers[i] >  result)
      result = numbers[i];
      System.out.println("The max value is " + result);
   }
}

輸出

The max value is 56.5
The max value is 3.0

finalize()方法

可以定義一個方法,該方法將在垃圾收集器最終銷燬物件之前被呼叫。此方法稱為finalize(),可用於確保物件乾淨地終止。

例如,您可以使用`finalize()`來確保關閉該物件擁有的開啟的檔案。

要向類新增終結器,只需定義`finalize()`方法即可。Java執行時會在它即將回收該類的物件時呼叫該方法。

在`finalize()`方法中,您將指定在銷燬物件之前必須執行的操作。

`finalize()`方法具有以下一般形式:

protected void finalize( ) {
   // finalization code here
}

這裡,關鍵字`protected`是一個說明符,它阻止其類外部定義的程式碼訪問`finalize()`。

這意味著您無法知道何時甚至是否會執行`finalize()`。例如,如果您的程式在垃圾收集發生之前結束,則`finalize()`將不會執行。

Java - 建構函式

建構函式在建立物件時對其進行初始化。它的名稱與其類相同,並且在語法上類似於方法。但是,建構函式沒有顯式的返回型別。

通常,您將使用建構函式為類定義的例項變數賦初始值,或執行建立完整物件所需的任何其他啟動過程。

所有類都有建構函式,無論您是否定義了一個建構函式,因為Java都會自動提供一個預設建構函式,該建構函式將所有成員變數初始化為零。但是,一旦您定義了自己的建構函式,預設建構函式將不再使用。

語法

以下是建構函式的語法:

class ClassName {
   ClassName() {
   }
}

Java允許兩種型別的建構函式:

  • 無引數建構函式
  • 引數化建構函式

無引數建構函式

顧名思義,Java的無引數建構函式不接受任何引數,而是使用這些建構函式,方法的例項變數將為所有物件初始化為固定值。

示例

Public class MyClass {
   Int num;
   MyClass() {
      num = 100;
   }
}

您將按如下方式呼叫建構函式來初始化物件:

public class ConsDemo {
   public static void main(String args[]) {
      MyClass t1 = new MyClass();
      MyClass t2 = new MyClass();
      System.out.println(t1.num + " " + t2.num);
   }
}

輸出

100 100

引數化建構函式

大多數情況下,您需要一個接受一個或多個引數的建構函式。引數新增到建構函式的方式與新增到方法的方式相同,只需在建構函式名稱後的括號內宣告它們。

示例

這是一個使用建構函式的簡單示例:

// A simple constructor.
class MyClass {
   int x;
   
   // Following is the constructor
   MyClass(int i ) {
      x = i;
   }
}

您將按如下方式呼叫建構函式來初始化物件:

public class ConsDemo {
   public static void main(String args[]) {
      MyClass t1 = new MyClass( 10 );
      MyClass t2 = new MyClass( 20 );
      System.out.println(t1.x + " " + t2.x);
   }
}

輸出

10 20

Java - 訪問修飾符

Java提供許多訪問修飾符來設定類、變數、方法和建構函式的訪問級別。四個訪問級別是:

  • 對包可見,預設為此。不需要修飾符。
  • 僅對類可見(private)。
  • 對全世界可見(public)。
  • 對包和所有子類可見(protected)。

預設訪問修飾符 - 無關鍵字

預設訪問修飾符意味著我們沒有為類、欄位、方法等顯式宣告訪問修飾符。

未宣告任何訪問控制修飾符的變數或方法可用於同一包中的任何其他類。介面中的欄位隱式為`public static final`,介面中的方法預設為`public`。

示例

變數和方法可以不帶任何修飾符宣告,如下例所示:

String version = "1.5.1";

boolean processOrder() {
   return true;
}

私有訪問修飾符 - private

宣告為 private 的方法、變數和建構函式只能在其宣告的類中訪問。

私有訪問修飾符是最嚴格的訪問級別。類和介面不能是私有的。

如果類中存在公共 getter 方法,則宣告為私有的變數可以在類外部訪問。

使用`private`修飾符是物件封裝自身並隱藏外部世界資料的首要方式。

示例

以下類使用私有訪問控制:

public class Logger {
   private String format;

   public String getFormat() {
      return this.format;
   }

   public void setFormat(String format) {
      this.format = format;
   }
}

在這裡,`Logger`類的`format`變數是私有的,因此其他類無法直接檢索或設定其值。

因此,為了使此變數對外部世界可用,我們定義了兩個公共方法:`getFormat()`,它返回`format`的值,以及`setFormat(String)`,它設定其值。

公共訪問修飾符 - public

宣告為公共的類、方法、建構函式、介面等可以從任何其他類訪問。因此,在公共類中宣告的欄位、方法、塊可以從屬於Java宇宙的任何類訪問。

但是,如果我們嘗試訪問的公共類位於不同的包中,則仍然需要匯入公共類。由於類繼承,類的所有公共方法和變數都由其子類繼承。

示例

以下函式使用公共訪問控制:

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

應用程式的`main()`方法必須是公共的。否則,Java直譯器(例如java)將無法呼叫它來執行該類。

受保護訪問修飾符 - protected

在超類中宣告為受保護的變數、方法和建構函式只能由其他包中的子類或受保護成員類的包中的任何類訪問。

`protected`訪問修飾符不能應用於類和介面。可以宣告方法和欄位為`protected`,但是介面中的方法和欄位不能宣告為`protected`。

`protected`訪問使子類有機會使用輔助方法或變數,同時阻止不相關的類嘗試使用它。

示例

以下父類使用受保護的訪問控制,以允許其子類覆蓋`openSpeaker()`方法:

class AudioPlayer {
   protected boolean openSpeaker(Speaker sp) {
      // implementation details
   }
}

class StreamingAudioPlayer extends AudioPlayer {
   boolean openSpeaker(Speaker sp) {
      // implementation details
   }
}

在這裡,如果我們將`openSpeaker()`方法定義為私有的,那麼除了`AudioPlayer`之外,任何其他類都無法訪問它。如果我們將其定義為公共的,那麼它將對整個外部世界可見。但是我們的目的是僅將其方法暴露給其子類,這就是我們使用`protected`修飾符的原因。

訪問控制和繼承

強制執行以下繼承方法的規則:

  • 在超類中宣告為公共的方法也必須在所有子類中是公共的。

  • 在超類中宣告為受保護的方法在子類中必須是受保護的或公共的;它們不能是私有的。

  • 私有方法根本不會被繼承,因此沒有針對它們的規則。

示例

在這個例子中,我們建立了一個類,其中包含一個私有變數age和一個具有預設作用域的變數name。使用setter/getter方法,我們正在更新age並獲取值,而name則直接更新。

public class Puppy {
   private int age;
   String name;

   public Puppy() {      
   }

   public void setAge( int age ) {
      this.age = age;
   }

   public int getAge( ) {
      return age;
   }

   public static void main(String []args) {
      Puppy myPuppy = new Puppy();

      // update age variable using method call
      myPuppy.setAge( 2 );

      // update name directly
      myPuppy.name = "Tommy";
      System.out.println("Age: " + myPuppy.getAge() +", name: " + myPuppy.name );
   }
}

輸出

Age: 2, name: Tommy

Java - 繼承

繼承可以定義為一個類獲取另一個類的屬性(方法和欄位)的過程。透過使用繼承,資訊以分層順序進行管理。

繼承其他類屬性的類稱為子類(派生類、子類),其屬性被繼承的類稱為超類(基類、父類)。

extends關鍵字

extends是用於繼承類屬性的關鍵字。以下是extends關鍵字的語法。

語法

class Super {
   .....
   .....
}
class Sub extends Super {
   .....
   .....
}

示例程式碼

下面是一個演示Java繼承的例子。在這個例子中,你可以看到兩個類,分別是Calculation和My_Calculation。

使用extends關鍵字,My_Calculation繼承了Calculation類的addition()和Subtraction()方法。

將下面的程式複製貼上到名為My_Calculation.java的檔案中

示例

class Calculation {
   int z;
	
   public void addition(int x, int y) {
      z = x + y;
      System.out.println("The sum of the given numbers:"+z);
   }
	
   public void Subtraction(int x, int y) {
      z = x - y;
      System.out.println("The difference between the given numbers:"+z);
   }
}

public class My_Calculation extends Calculation {
   public void multiplication(int x, int y) {
      z = x * y;
      System.out.println("The product of the given numbers:"+z);
   }
	
   public static void main(String args[]) {
      int a = 20, b = 10;
      My_Calculation demo = new My_Calculation();
      demo.addition(a, b);
      demo.Subtraction(a, b);
      demo.multiplication(a, b);
   }
}

編譯並執行上面的程式碼,如下所示。

javac My_Calculation.java
java My_Calculation

程式執行後,將產生以下結果:

輸出

The sum of the given numbers:30
The difference between the given numbers:10
The product of the given numbers:200

在給定的程式中,當建立My_Calculation類的物件時,會在其中建立超類內容的副本。這就是為什麼可以使用子類物件訪問超類的成員。

Inheritance

超類引用變數可以持有子類物件,但是使用該變數只能訪問超類的成員,因此為了訪問兩個類的成員,建議始終為子類建立引用變數。

如果你考慮上面的程式,你可以像下面這樣例項化類。但是使用超類引用變數(本例中為cal)你不能呼叫屬於子類My_Calculation的方法multiplication()

Calculation demo = new My_Calculation();
demo.addition(a, b);
demo.Subtraction(a, b);

注意:子類繼承其超類的所有成員(欄位、方法和巢狀類)。建構函式不是成員,因此不會被子類繼承,但是可以從子類呼叫超類的建構函式。

super關鍵字

super關鍵字類似於this關鍵字。以下是使用super關鍵字的場景。

  • 它用於區分超類的成員和子類的成員,如果它們具有相同的名稱。

  • 它用於從子類呼叫超類的建構函式。

區分成員

如果一個類繼承另一個類的屬性,並且超類的成員與子類的名稱相同,為了區分這些變數,我們使用super關鍵字,如下所示。

super.variable
super.method();

示例程式碼

本節提供一個演示super關鍵字用法的程式。

在給定的程式中,你有兩個類,分別是Sub_classSuper_class,兩者都有一個名為display()的方法,但實現不同,還有一個名為num的變數,值也不同。我們呼叫這兩個類的display()方法並列印這兩個類的變數num的值。在這裡你可以觀察到我們使用了super關鍵字來區分超類的成員和子類的成員。

將程式複製貼上到名為Sub_class.java的檔案中。

示例

class Super_class {
   int num = 20;

   // display method of superclass
   public void display() {
      System.out.println("This is the display method of superclass");
   }
}

public class Sub_class extends Super_class {
   int num = 10;

   // display method of sub class
   public void display() {
      System.out.println("This is the display method of subclass");
   }

   public void my_method() {
      // Instantiating subclass
      Sub_class sub = new Sub_class();

      // Invoking the display() method of sub class
      sub.display();

      // Invoking the display() method of superclass
      super.display();

      // printing the value of variable num of subclass
      System.out.println("value of the variable named num in sub class:"+ sub.num);

      // printing the value of variable num of superclass
      System.out.println("value of the variable named num in super class:"+ super.num);
   }

   public static void main(String args[]) {
      Sub_class obj = new Sub_class();
      obj.my_method();
   }
}

使用以下語法編譯並執行上述程式碼。

javac Super_Demo
java Super

執行程式後,你將得到以下結果:

輸出

This is the display method of subclass
This is the display method of superclass
value of the variable named num in sub class:10
value of the variable named num in super class:20

呼叫超類建構函式

如果一個類繼承另一個類的屬性,子類會自動獲取超類的預設建構函式。但是,如果你想呼叫超類的引數化建構函式,你需要使用super關鍵字,如下所示。

super(values);

示例程式碼

本節給出的程式演示瞭如何使用super關鍵字呼叫超類的引數化建構函式。此程式包含一個超類和一個子類,其中超類包含一個接受整數值的引數化建構函式,我們使用super關鍵字呼叫超類的引數化建構函式。

將以下程式複製貼上到名為Subclass.java的檔案中

示例

class Superclass {
   int age;

   Superclass(int age) {
      this.age = age; 		 
   }

   public void getAge() {
      System.out.println("The value of the variable named age in super class is: " +age);
   }
}

public class Subclass extends Superclass {
   Subclass(int age) {
      super(age);
   }

   public static void main(String args[]) {
      Subclass s = new Subclass(24);
      s.getAge();
   }
}

使用以下語法編譯並執行上述程式碼。

javac Subclass
java Subclass

輸出

The value of the variable named age in super class is: 24

IS-A關係

IS-A是一種說法:這個物件是一種那種物件。讓我們看看如何使用extends關鍵字實現繼承。

public class Animal {
}

public class Mammal extends Animal {
}

public class Reptile extends Animal {
}

public class Dog extends Mammal {
}

現在,根據上面的例子,在面向物件的術語中,以下說法是正確的:

  • Animal是Mammal類的超類。
  • Animal是Reptile類的超類。
  • Mammal和Reptile是Animal類的子類。
  • Dog是Mammal和Animal類的子類。

現在,如果我們考慮IS-A關係,我們可以說:

  • Mammal IS-A Animal
  • Reptile IS-A Animal
  • Dog IS-A Mammal
  • 因此:Dog IS-A Animal

使用extends關鍵字,子類將能夠繼承超類的所有屬性,除了超類的私有屬性。

我們可以用instance運算子來確保Mammal實際上是一個Animal。

示例

class Animal {
}

class Mammal extends Animal {
}

class Reptile extends Animal {
}

public class Dog extends Mammal {

   public static void main(String args[]) {
      Animal a = new Animal();
      Mammal m = new Mammal();
      Dog d = new Dog();

      System.out.println(m instanceof Animal);
      System.out.println(d instanceof Mammal);
      System.out.println(d instanceof Animal);
   }
}

輸出

true
true
true

既然我們已經很好地理解了extends關鍵字,讓我們看看如何使用implements關鍵字來獲得IS-A關係。

通常,implements關鍵字與類一起使用以繼承介面的屬性。介面永遠不能被類擴充套件。

示例

public interface Animal {
}

public class Mammal implements Animal {
}

public class Dog extends Mammal {
}

instanceof關鍵字

讓我們使用instanceof運算子來檢查Mammal是否實際上是Animal,以及dog是否實際上是Animal。

示例

interface Animal{}
class Mammal implements Animal{}

public class Dog extends Mammal {

   public static void main(String args[]) {
      Mammal m = new Mammal();
      Dog d = new Dog();

      System.out.println(m instanceof Animal);
      System.out.println(d instanceof Mammal);
      System.out.println(d instanceof Animal);
   }
}

輸出

true
true
true

HAS-A關係

這些關係主要基於用法。這決定了某個類是否HAS-A某些東西。這種關係有助於減少程式碼重複和錯誤。

讓我們來看一個例子:

示例

public class Vehicle{}
public class Speed{}

public class Van extends Vehicle {
   private Speed sp;
} 

這表明Van類HAS-A Speed。透過為Speed建立一個單獨的類,我們不必將屬於speed的整個程式碼放在Van類中,這使得可以在多個應用程式中重用Speed類。

在面向物件特性中,使用者不需要關心哪個物件正在執行實際工作。為了實現這一點,Van類向Van類的使用者隱藏了實現細節。所以,基本上發生的事情是使用者會要求Van類執行某個動作,而Van類會自己執行工作,或者要求另一個類執行該動作。

繼承的型別

如下所示,繼承有多種型別。

Types of Inheritance

需要記住一個非常重要的事實是,Java不支援多重繼承。這意味著一個類不能擴充套件多個類。因此,以下是非法的:

示例

public class extends Animal, Mammal{} 

但是,一個類可以實現一個或多個介面,這幫助Java擺脫了多重繼承的不可能。

Java - 多型

多型性是物件能夠採用多種形式的能力。OOP中多型性的最常見用法是當父類引用用於引用子類物件時。

任何能夠透過多個IS-A測試的Java物件都被認為是多型的。在Java中,所有Java物件都是多型的,因為任何物件都將透過其自身型別和Object類的IS-A測試。

重要的是要知道,訪問物件的唯一可能方法是透過引用變數。引用變數只能是一種型別。一旦宣告,引用變數的型別就不能更改。

如果引用變數沒有宣告為final,則可以將其重新分配給其他物件。引用變數的型別將決定它可以在物件上呼叫的方法。

引用變數可以引用其宣告型別的任何物件或其宣告型別的任何子型別。引用變數可以宣告為類或介面型別。

示例

讓我們來看一個例子。

public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}

現在,Deer類被認為是多型的,因為它具有多重繼承。對於上面的例子,以下說法是正確的:

  • A Deer IS-A Animal
  • A Deer IS-A Vegetarian
  • A Deer IS-A Deer
  • A Deer IS-A Object

當我們將引用變數事實應用於Deer物件引用時,以下宣告是合法的:

示例

Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;

所有引用變數d、a、v、o都引用堆中的同一個Deer物件。

示例

在這個例子中,我們透過建立Deer的物件並將相同的物件分配給超類或已實現介面的引用來展示上述概念。

interface Vegetarian{}
class Animal{}
public class Deer extends Animal implements Vegetarian{
	public static void main(String[] args) {
		Deer d = new Deer();
		Animal a = d;
		Vegetarian v = d;
		Object o = d;
		
		System.out.println(d instanceof Deer);
		System.out.println(a instanceof Deer);
		System.out.println(v instanceof Deer);
		System.out.println(o instanceof Deer);
	}	
}

輸出

true
true
true
true

虛方法

在本節中,我將向您展示Java中重寫方法的行為如何使您能夠在設計類時利用多型性。

我們已經討論過方法重寫,其中子類可以重寫其父類中的方法。重寫的方法實質上隱藏在父類中,除非子類在重寫方法中使用super關鍵字,否則不會呼叫。

示例

/* File name : Employee.java */
public class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }

   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String newAddress) {
      address = newAddress;
   }

   public int getNumber() {
      return number;
   }
}

現在假設我們擴充套件Employee類如下:

/* File name : Salary.java */
public class Salary extends Employee {
   private double salary; // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName()
      + " with salary " + salary);
   }
   
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

現在,仔細研究以下程式,並嘗試確定其輸出:

/* File name : VirtualDemo.java */
public class VirtualDemo {

   public static void main(String [] args) {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
      System.out.println("Call mailCheck using Salary reference --");   
      s.mailCheck();
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
   }
}

class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }

   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String newAddress) {
      address = newAddress;
   }

   public int getNumber() {
      return number;
   }
}

class Salary extends Employee {
   private double salary; // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName()
      + " with salary " + salary);
   }
   
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

輸出

Constructing an Employee
Constructing an Employee

Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0

在這裡,我們例項化兩個Salary物件。一個使用Salary引用s,另一個使用Employee引用e

呼叫s.mailCheck()時,編譯器在編譯時看到Salary類中的mailCheck(),JVM在執行時呼叫Salary類中的mailCheck()。

e上的mailCheck()則完全不同,因為e是Employee引用。當編譯器看到e.mailCheck()時,編譯器會看到Employee類中的mailCheck()方法。

在這裡,在編譯時,編譯器使用Employee中的mailCheck()來驗證此語句。然而,在執行時,JVM呼叫Salary類中的mailCheck()。

這種行為被稱為虛方法呼叫,這些方法被稱為虛方法。重寫的方法是在執行時呼叫的,無論在編譯時的原始碼中使用了什麼資料型別的引用。

Java - 方法覆蓋

在上一章中,我們討論了超類和子類。如果一個類從其超類繼承了一個方法,那麼就有機會重寫該方法,前提是該方法沒有標記為final。

重寫的優點是:能夠定義特定於子類型別的行為,這意味著子類可以根據其需求實現父類方法。

在面向物件的術語中,重寫意味著重寫現有方法的功能。

示例

讓我們來看一個例子。

class Animal {
   public void move() {
      System.out.println("Animals can move");
   }
}

class Dog extends Animal {
   public void move() {
      System.out.println("Dogs can walk and run");
   }
}

public class TestDog {

   public static void main(String args[]) {
      Animal a = new Animal();   // Animal reference and object
      Animal b = new Dog();   // Animal reference but Dog object

      a.move();   // runs the method in Animal class
      b.move();   // runs the method in Dog class
   }
}

輸出

Animals can move
Dogs can walk and run

在上面的例子中,你可以看到,即使b是Animal型別,它也會執行Dog類中的move方法。原因是:在編譯時,檢查是在引用型別上進行的。但是,在執行時,JVM會確定物件型別,並執行屬於該特定物件的方法。

因此,在上面的例子中,程式將正確編譯,因為Animal類具有move方法。然後,在執行時,它執行特定於該物件的方法。

考慮以下示例:

示例

class Animal {
   public void move() {
      System.out.println("Animals can move");
   }
}

class Dog extends Animal {
   public void move() {
      System.out.println("Dogs can walk and run");
   }
   public void bark() {
      System.out.println("Dogs can bark");
   }
}

public class TestDog {

   public static void main(String args[]) {
      Animal a = new Animal();   // Animal reference and object
      Animal b = new Dog();   // Animal reference but Dog object

      a.move();   // runs the method in Animal class
      b.move();   // runs the method in Dog class
      b.bark();
   }
}

輸出

TestDog.java:26: error: cannot find symbol
      b.bark();
       ^
  symbol:   method bark()
  location: variable b of type Animal
1 error

此程式將丟擲編譯時錯誤,因為b的引用型別Animal沒有名為bark的方法。

方法重寫的規則

  • 引數列表必須與被重寫方法的引數列表完全相同。

  • 返回型別必須與超類中原始重寫方法中宣告的返回型別相同或為其子型別。

  • 訪問級別不能比重寫方法的訪問級別更嚴格。例如:如果超類方法宣告為public,則子類中的重寫方法不能為private或protected。

  • 例項方法只能在子類繼承它們時才能被重寫。

  • 宣告為final的方法不能被重寫。

  • 宣告為static的方法不能被重寫,但可以被重新宣告。

  • 如果一個方法不能被繼承,那麼它就不能被重寫。

  • 與例項的超類位於同一個包中的子類可以重寫任何未宣告為private或final的超類方法。

  • 不同包中的子類只能重寫宣告為public或protected的非final方法。

  • 重寫方法可以丟擲任何未檢查的異常,無論重寫方法是否丟擲異常。但是,重寫方法不應丟擲比重寫方法宣告的新異常或更廣泛的異常。重寫方法可以丟擲比重寫方法更窄或更少的異常。

  • 建構函式不能被重寫。

使用 super 關鍵字

呼叫被重寫方法的超類版本時,使用super關鍵字。

示例

class Animal {
   public void move() {
      System.out.println("Animals can move");
   }
}

class Dog extends Animal {
   public void move() {
      super.move();   // invokes the super class method
      System.out.println("Dogs can walk and run");
   }
}

public class TestDog {

   public static void main(String args[]) {
      Animal b = new Dog();   // Animal reference but Dog object
      b.move();   // runs the method in Dog class
   }
}

輸出

Animals can move
Dogs can walk and run

Java - 抽象

根據字典的定義,抽象是處理思想而不是事件的特性。例如,當您考慮電子郵件的情況時,傳送電子郵件後發生的情況、電子郵件伺服器使用的協議等複雜細節對使用者是隱藏的。因此,要傳送電子郵件,您只需要輸入內容、填寫接收者的地址並點擊發送。

同樣,在面向物件程式設計中,抽象是向用戶隱藏實現細節的過程,只向用戶提供功能。換句話說,使用者將瞭解物件的功能,而不是其工作方式。

在Java中,抽象是使用抽象類和介面實現的。

抽象類

在其宣告中包含abstract關鍵字的類稱為抽象類。

  • 抽象類可以包含也可以不包含抽象方法,即沒有方法體的方法(public void get();)

  • 但是,如果一個類至少有一個抽象方法,那麼該類必須宣告為抽象類。

  • 如果一個類被宣告為抽象類,則不能例項化它。

  • 要使用抽象類,必須從另一個類繼承它,併為其中的抽象方法提供實現。

  • 如果您繼承了一個抽象類,則必須為其中的所有抽象方法提供實現。

示例

本節提供了一個抽象類的示例。要建立一個抽象類,只需在類宣告中,在class關鍵字前使用abstract關鍵字。

/* File name : Employee.java */
public abstract class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   
   public double computePay() {
     System.out.println("Inside Employee computePay");
     return 0.0;
   }
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }
 
   public String getAddress() {
      return address;
   }
   
   public void setAddress(String newAddress) {
      address = newAddress;
   }
 
   public int getNumber() {
      return number;
   }
}

您可以觀察到,除了抽象方法外,Employee類與Java中的普通類相同。該類現在是抽象的,但它仍然具有三個欄位、七個方法和一個建構函式。

現在您可以嘗試以下方式例項化Employee類:

/* File name : AbstractDemo.java */
public class AbstractDemo {

   public static void main(String [] args) {
      /* Following is not allowed and would raise error */
      Employee e = new Employee("George W.", "Houston, TX", 43);
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
   }
}

abstract class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   
   public double computePay() {
     System.out.println("Inside Employee computePay");
     return 0.0;
   }
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }
 
   public String getAddress() {
      return address;
   }
   
   public void setAddress(String newAddress) {
      address = newAddress;
   }
 
   public int getNumber() {
      return number;
   }
}

編譯上面的類時,會收到以下錯誤:

Employee.java:46: Employee is abstract; cannot be instantiated
      Employee e = new Employee("George W.", "Houston, TX", 43);
                   ^
1 error

繼承抽象類

我們可以像在具體類中一樣繼承Employee類的屬性,方法如下:

示例

/* File name : Salary.java */
public class Salary extends Employee {
   private double salary;   // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName() + " with salary " + salary);
   }
 
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

在這裡,您不能例項化Employee類,但是您可以例項化Salary類,並使用此例項訪問Employee類的所有三個欄位和七個方法,如下所示。

/* File name : AbstractDemo.java */
public class AbstractDemo {

   public static void main(String [] args) {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
      System.out.println("Call mailCheck using Salary reference --");
      s.mailCheck();
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
   }
}
abstract class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   
   public double computePay() {
     System.out.println("Inside Employee computePay");
     return 0.0;
   }
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }
 
   public String getAddress() {
      return address;
   }
   
   public void setAddress(String newAddress) {
      address = newAddress;
   }
 
   public int getNumber() {
      return number;
   }
}
class Salary extends Employee {
   private double salary;   // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName() + " with salary " + salary);
   }
 
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

輸出

Constructing an Employee
Constructing an Employee
Call mailCheck using Salary reference --
Within mailCheck of Salary class 
Mailing check to Mohd Mohtashim with salary 3600.0

 Call mailCheck using Employee reference--
Within mailCheck of Salary class 
Mailing check to John Adams with salary 2400.0

抽象方法

如果您希望一個類包含特定方法,但又希望該方法的實際實現由子類確定,則可以在父類中將該方法宣告為抽象方法。

  • abstract關鍵字用於將方法宣告為抽象方法。

  • 您必須在方法宣告中的方法名稱之前放置abstract關鍵字。

  • 抽象方法包含方法簽名,但不包含方法體。

  • 抽象方法的結尾使用分號 (;),而不是大括號。

以下是一個抽象方法的示例。

示例

public abstract class Employee {
   private String name;
   private String address;
   private int number;
   
   public abstract double computePay();
   // Remainder of class definition
}

將方法宣告為抽象方法有兩個結果:

  • 包含它的類必須宣告為抽象類。

  • 任何繼承當前類的類都必須重寫抽象方法或將自身宣告為抽象類。

注意 - 最終,子類必須實現抽象方法;否則,您將擁有一個無法例項化的抽象類層次結構。

假設Salary類繼承Employee類,則它應該實現computePay()方法,如下所示:

/* File name : Salary.java */
public class Salary extends Employee {
   private double salary;   // Annual salary
  
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
   // Remainder of class definition
}

示例

下面的示例展示了抽象方法的概念。

/* File name : AbstractDemo.java */
public class AbstractDemo {
   public static void main(String [] args) {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      System.out.println("salary: " + s.computePay());
   }
}
abstract class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }

   public abstract double computePay();
      // Remainder of class definition

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String address) {
      this.address = address;
   }

   public int getNumber() {
      return number;
   }

   public void setNumber(int number) {
      this.number = number;
   }
}
class Salary extends Employee {
   private double salary;   // Annual salary

   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      this.salary = salary;
   }

   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
   // Remainder of class definition
}

輸出

Constructing an Employee
Computing salary pay for Mohd Mohtashim
salary: 69.23076923076923

Java - 封裝

封裝是面向物件程式設計的四個基本概念之一。另外三個是繼承、多型和抽象。

Java中的封裝是一種將資料(變數)和作用於資料(方法)的程式碼包裝在一起作為單個單元的機制。在封裝中,類的變數對其他類是隱藏的,只能透過其當前類的方法訪問。因此,它也被稱為資料隱藏

要在Java中實現封裝:

  • 將類的變數宣告為私有。

  • 提供公共setter和getter方法來修改和檢視變數值。

示例

以下是一個演示如何在Java中實現封裝的示例:

/* File name : EncapTest.java */
public class EncapTest {
   private String name;
   private String idNum;
   private int age;

   public int getAge() {
      return age;
   }

   public String getName() {
      return name;
   }

   public String getIdNum() {
      return idNum;
   }

   public void setAge( int newAge) {
      age = newAge;
   }

   public void setName(String newName) {
      name = newName;
   }

   public void setIdNum( String newId) {
      idNum = newId;
   }
}

公共setXXX()和getXXX()方法是EncapTest類例項變數的訪問點。通常,這些方法被稱為getter和setter。因此,任何想要訪問變數的類都應該透過這些getter和setter訪問它們。

可以使用以下程式訪問EncapTest類的變數:

/* File name : RunEncap.java */
public class RunEncap {

   public static void main(String args[]) {
      EncapTest encap = new EncapTest();
      encap.setName("James");
      encap.setAge(20);
      encap.setIdNum("12343ms");

      System.out.print("Name : " + encap.getName() + " Age : " + encap.getAge());
   }
}

public class EncapTest {
   private String name;
   private String idNum;
   private int age;

   public int getAge() {
      return age;
   }

   public String getName() {
      return name;
   }

   public String getIdNum() {
      return idNum;
   }

   public void setAge( int newAge) {
      age = newAge;
   }

   public void setName(String newName) {
      name = newName;
   }

   public void setIdNum( String newId) {
      idNum = newId;
   }
}

輸出

Name : James Age : 20

封裝的好處

  • 類的欄位可以設定為只讀或只寫。

  • 類可以完全控制其欄位中儲存的內容。

Java - 介面

介面是Java中的引用型別。它類似於類。它是抽象方法的集合。一個類實現一個介面,從而繼承介面的抽象方法。

除了抽象方法外,介面還可以包含常量、預設方法、靜態方法和巢狀型別。方法體僅存在於預設方法和靜態方法中。

編寫介面類似於編寫類。但是類描述物件的屬性和行為。介面包含類實現的行為。

除非實現介面的類是抽象類,否則必須在類中定義介面的所有方法。

介面在以下方面類似於類:

  • 介面可以包含任意數量的方法。

  • 介面以.java副檔名寫在檔案中,介面名稱與檔名匹配。

  • 介面的位元組碼出現在.class檔案中。

  • 接口出現在包中,其對應的位元組碼檔案必須位於與包名匹配的目錄結構中。

但是,介面在幾個方面與類不同,包括:

  • 您不能例項化介面。

  • 介面不包含任何建構函式。

  • 介面中的所有方法都是抽象的。

  • 介面不能包含例項欄位。介面中可以出現的唯一欄位必須同時宣告為靜態和final。

  • 介面不是由類擴充套件的;它是類實現的。

  • 介面可以擴充套件多個介面。

宣告介面

interface關鍵字用於宣告介面。以下是一個宣告介面的簡單示例:

示例

以下是一個介面的示例:

/* File name : NameOfInterface.java */
import java.lang.*;
// Any number of import statements

public interface NameOfInterface {
   // Any number of final, static fields
   // Any number of abstract method declarations\
}

介面具有以下屬性:

  • 介面隱式地是抽象的。宣告介面時不需要使用abstract關鍵字。

  • 介面中的每個方法也是隱式抽象的,因此不需要abstract關鍵字。

  • 介面中的方法隱式地是公共的。

示例

/* File name : Animal.java */
interface Animal {
   public void eat();
   public void travel();
}

實現介面

當一個類實現一個介面時,您可以認為該類簽署了一份合同,同意執行介面的特定行為。如果一個類沒有執行介面的所有行為,則該類必須將自身宣告為抽象類。

類使用implements關鍵字來實現介面。implements關鍵字出現在類宣告中extends部分之後。

示例

/* File name : MammalInt.java */
public class MammalInt implements Animal {

   public void eat() {
      System.out.println("Mammal eats");
   }

   public void travel() {
      System.out.println("Mammal travels");
   } 

   public int noOfLegs() {
      return 0;
   }

   public static void main(String args[]) {
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
} 
interface Animal {
   public void eat();
   public void travel();
}

輸出

Mammal eats
Mammal travels

重寫介面中定義的方法時,需要遵循以下幾條規則:

  • 除了介面方法宣告的檢查異常或介面方法宣告的子類外,不應該在實現方法上宣告檢查異常。

  • 重寫方法時,應保持介面方法的簽名和相同或子型別的返回型別。

  • 實現類本身可以是抽象類,如果是這樣,則不需要實現介面方法。

實現介面時,有一些規則:

  • 一個類可以同時實現多個介面。

  • 一個類只能擴充套件一個類,但可以實現多個介面。

  • 介面可以擴充套件另一個介面,就像類可以擴充套件另一個類一樣。

擴充套件介面

介面可以擴充套件另一個介面,就像類可以擴充套件另一個類一樣。extends關鍵字用於擴充套件介面,子介面繼承父介面的方法。

Hockey和Football介面擴充套件了以下Sports介面。

示例

// Filename: Sports.java
public interface Sports {
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

// Filename: Football.java
public interface Football extends Sports {
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

// Filename: Hockey.java
public interface Hockey extends Sports {
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey介面有四個方法,但它繼承了Sports的兩個方法;因此,實現Hockey的類需要實現所有六個方法。類似地,實現Football的類需要定義Football的三個方法和Sports的兩個方法。

示例

interface Sports {
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

interface Football extends Sports {
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

interface Hockey extends Sports {
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

public class HockeyDemo implements Hockey {

   public void setHomeTeam(String name) {
      System.out.println("Home team: " + name);
   }

   public void setVisitingTeam(String name) {}

   public void homeGoalScored() {}

   public void visitingGoalScored() {}

   public void endOfPeriod(int period) {}

   public void overtimePeriod(int ot) {}

   public static void main(String[] args) {
      Hockey hockeyDemo = new HockeyDemo();
      hockeyDemo.setHomeTeam("India");
   }
}

輸出

Home team: India

擴充套件多個介面

Java類只能擴充套件一個父類。不允許多重繼承。然而,介面不是類,介面可以擴充套件多個父介面。

extends關鍵字只使用一次,父介面以逗號分隔的列表宣告。

例如,如果Hockey介面擴充套件了Sports和Event,則宣告如下:

示例

public interface Hockey extends Sports, Event

interface Sports {
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

interface Football extends Sports {
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

interface Hockey extends Sports {
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

interface Event {
   public void organize();
}
public class HockeyDemo implements Hockey, Event {

   public void setHomeTeam(String name) {
      System.out.println("Home team: " + name);
   }

   public void setVisitingTeam(String name) {}

   public void homeGoalScored() {}

   public void visitingGoalScored() {}

   public void endOfPeriod(int period) {}

   public void overtimePeriod(int ot) {}

   public static void main(String[] args) {
      HockeyDemo hockeyDemo = new HockeyDemo();
      hockeyDemo.setHomeTeam("India");
      hockeyDemo.organize();
   }

   public void organize() {
      System.out.println("Match organized. ");
   }
}

輸出

Home team: India
Match organized. 

標記介面

擴充套件介面最常見的用法是父介面不包含任何方法。例如,java.awt.event包中的MouseListener介面擴充套件了java.util.EventListener,其定義如下:

示例

package java.util;
public interface EventListener
{}

沒有方法的介面被稱為標記介面。標記介面有兩個基本設計目的:

建立公共父級 - 與EventListener介面一樣,它被Java API中的數十個其他介面擴充套件,您可以使用標記介面在一組介面之間建立公共父級。例如,當介面擴充套件EventListener時,JVM知道這個特定介面將用於事件委託場景。

向類新增資料型別 - 這就是術語“標記”的來源。實現標記介面的類不需要定義任何方法(因為介面沒有任何方法),但是類透過多型性成為介面型別。

Java - 包

在 Java 中使用包是為了防止命名衝突,控制訪問,使查詢/定位和使用類、介面、列舉和註解更容易等等。

可以定義為相關型別(類、介面、列舉和註釋)的組合,提供訪問保護和名稱空間管理。

Java中的一些現有包包括:

  • java.lang - 包含基本類

  • java.io - 此包中捆綁了輸入、輸出功能的類

程式設計師可以定義自己的包來捆綁一組類/介面等。這是一個很好的做法,將您實現的相關類分組,以便程式設計師可以輕鬆確定類、介面、列舉和註釋是相關的。

由於包建立了一個新的名稱空間,因此不會與其他包中的名稱發生任何名稱衝突。使用包,更容易提供訪問控制,也更容易找到相關的類。

建立包

建立包時,應為包選擇一個名稱,並在包含您想要包含在包中的類、介面、列舉和註釋型別的每個原始檔的頂部包含帶有該名稱的package語句。

package語句應為原始檔中的第一行。每個原始檔只能有一個package語句,它適用於檔案中的所有型別。

如果不使用package語句,則類、介面、列舉和註釋型別將放在當前預設包中。

要使用package語句編譯Java程式,必須使用-d選項,如下所示。

javac -d Destination_folder file_name.java

然後,在指定的目的地建立一個具有給定包名稱的資料夾,並將編譯後的類檔案放在該資料夾中。

示例

讓我們來看一個建立名為animals包的例子。最好使用小寫字母為包命名,以避免與類和介面的名稱衝突。

下面的包示例包含一個名為animals的介面:

/* File name : Animal.java */
package animals;

interface Animal {
   public void eat();
   public void travel();
}

現在,讓我們在同一個animals包中實現上述介面:

package animals;
/* File name : MammalInt.java */

public class MammalInt implements Animal {

   public void eat() {
      System.out.println("Mammal eats");
   }

   public void travel() {
      System.out.println("Mammal travels");
   } 

   public int noOfLegs() {
      return 0;
   }

   public static void main(String args[]) {
      MammalInt m = new MammalInt();
      m.eat();
      m.travel();
   }
} 

interface Animal {
   public void eat();
   public void travel();
}

現在,按照以下步驟編譯Java檔案:

$ javac -d . Animal.java 
$ javac -d . MammalInt.java

現在,當前目錄中將建立一個名為animals的包/資料夾,並將這些類檔案放在其中,如下所示。

Packages

您可以執行包內的類檔案並獲得以下結果。

Mammal eats
Mammal travels

import關鍵字

如果一個類想要使用同一個包中的另一個類,則不需要使用包名。同一個包中的類無需任何特殊語法就可以相互找到。

示例

這裡,名為Boss的類被新增到已經包含Employee的payroll包中。然後,Boss可以引用Employee類而無需使用payroll字首,如下面的Boss類所示。

package payroll;
public class Boss {
   public void payEmployee(Employee e) {
      e.mailCheck();
   }
}

如果Employee類不在payroll包中會發生什麼?那麼Boss類必須使用以下技術之一來引用不同包中的類。

  • 可以使用類的完全限定名。例如:
payroll.Employee
  • 可以使用import關鍵字和萬用字元(*)匯入包。例如:

import payroll.*;
  • 可以使用import關鍵字匯入類本身。例如:
import payroll.Employee;

示例

package payroll;

public class Employee {
   public void mailCheck() {
      System.out.println("Pay received.");    
   }
}
package payroll;

import payroll.Employee;

public class Boss {
   public void payEmployee(Employee e) {
      e.mailCheck();
   }
   
   public static void main(String[] args) {
      Boss boss = new Boss();
	   Employee e = new Employee();
      boss.payEmployee(e);
   }
}

輸出

Pay received.

注意 - 一個類檔案可以包含任意數量的import語句。import語句必須出現在package語句之後,類宣告之前。

包的目錄結構

將類放入包中會產生兩個主要結果:

  • 正如我們在上一節中討論的那樣,包名成為類名的一部分。

  • 包名必須與相應的位元組碼所在的目錄結構匹配。

這是一個在Java中管理檔案的簡單方法:

將類、介面、列舉或註解型別的原始碼放在一個文字檔案中,其名稱為型別的簡單名稱,副檔名為.java

例如:

// File Name :  Car.java
package vehicle;

public class Car {
   // Class implementation.   
}

現在,將原始檔放在一個目錄中,該目錄的名稱反映了類所屬的包的名稱:

....\vehicle\Car.java

現在,限定類名和路徑名如下所示:

  • 類名 → vehicle.Car
  • 路徑名 → vehicle\Car.java (在Windows中)

一般來說,公司使用其反轉的網際網路域名作為其包名。

示例 - 如果一家公司的網際網路域名是apple.com,那麼其所有包名都將以com.apple開頭。包名的每個元件對應一個子目錄。

示例 - 如果該公司有一個com.apple.computers包,其中包含一個Dell.java原始檔,它將包含在一系列子目錄中,如下所示:

....\com\apple\computers\Dell.java

在編譯時,編譯器為其中定義的每個類、介面和列舉建立一個不同的輸出檔案。輸出檔案的基名是型別的名稱,其副檔名為.class

例如:

// File Name: Dell.java
package com.apple.computers;

public class Dell {
}

class Ups {
}

現在,使用-d選項編譯此檔案,如下所示:

$javac -d . Dell.java

檔案將按如下方式編譯:

.\com\apple\computers\Dell.class
.\com\apple\computers\Ups.class

您可以按如下方式匯入\com\apple\computers\中定義的所有類或介面:

import com.apple.computers.*;

與.java原始檔一樣,編譯後的.class檔案也應該在一系列反映包名的目錄中。但是,.class檔案的路徑不必與.java原始檔的路徑相同。您可以分別安排源目錄和類目錄,如下所示:

<path-one>\sources\com\apple\computers\Dell.java

<path-two>\classes\com\apple\computers\Dell.class

透過這樣做,可以向其他程式設計師提供對classes目錄的訪問許可權,而無需公開您的原始碼。您還需要以這種方式管理原始檔和類檔案,以便編譯器和Java虛擬機器(JVM)可以找到程式使用到的所有型別。

classes目錄的完整路徑,<path-two>\classes,稱為類路徑,並使用CLASSPATH系統變數設定。編譯器和JVM都透過將包名新增到類路徑來構造到.class檔案的路徑。

假設<path-two>\classes是類路徑,包名是com.apple.computers,那麼編譯器和JVM將在<path-two>\classes\com\apple\computers中查詢.class檔案。

類路徑可能包含多個路徑。多個路徑應以分號(Windows)或冒號(Unix)分隔。預設情況下,編譯器和JVM搜尋當前目錄和包含Java平臺類的JAR檔案,以便這些目錄自動位於類路徑中。

設定CLASSPATH系統變數

要顯示當前CLASSPATH變數,請在Windows和UNIX(Bourne shell)中使用以下命令:

  • 在Windows中 → C:\> set CLASSPATH
  • 在UNIX中 → % echo $CLASSPATH

要刪除CLASSPATH變數的當前內容,請使用:

  • 在Windows中 → C:\> set CLASSPATH =
  • 在UNIX中 → % unset CLASSPATH; export CLASSPATH

要設定CLASSPATH變數:

  • 在Windows中 → set CLASSPATH = C:\users\jack\java\classes
  • 在UNIX中 → % CLASSPATH = /home/jack/java/classes; export CLASSPATH

Java - 內部類

在本章中,我們將討論Java的內部類。

巢狀類

在Java中,就像類的方法一樣,類的變數也可以有另一個類作為其成員。在Java中允許在一個類中編寫另一個類。在內部編寫的類稱為巢狀類,包含內部類的類稱為外部類

語法

以下是編寫巢狀類的語法。這裡,類Outer_Demo是外部類,類Inner_Demo是巢狀類。

class Outer_Demo {
   class Inner_Demo {
   }
}

巢狀類分為兩種型別:

  • 非靜態巢狀類 - 這些是非靜態的類成員。

  • 靜態巢狀類 - 這些是類的靜態成員。

Inner Classes

內部類(非靜態巢狀類)

內部類是Java中的安全機制。我們知道類不能與訪問修飾符private關聯,但是如果我們將類作為其他類的成員,那麼內部類可以設定為private。這也用於訪問類的私有成員。

內部類根據定義方式和位置分為三種類型:它們是:

  • 內部類
  • 方法區域性內部類
  • 匿名內部類

內部類

建立內部類非常簡單。您只需要在一個類中編寫一個類即可。與類不同,內部類可以是私有的,一旦將內部類宣告為私有的,就不能從類外部的物件訪問它。

以下程式建立了一個內部類並訪問它。在給定的示例中,我們將內部類設定為private,並透過方法訪問該類。

示例

class Outer_Demo {
   int num;
   
   // inner class
   private class Inner_Demo {
      public void print() {
         System.out.println("This is an inner class");
      }
   }
   
   // Accessing he inner class from the method within
   void display_Inner() {
      Inner_Demo inner = new Inner_Demo();
      inner.print();
   }
}
   
public class My_class {

   public static void main(String args[]) {
      // Instantiating the outer class 
      Outer_Demo outer = new Outer_Demo();
      
      // Accessing the display_Inner() method.
      outer.display_Inner();
   }
}

在這裡您可以觀察到Outer_Demo是外部類,Inner_Demo是內部類,display_Inner()是在其中例項化內部類的那個方法,並且這個方法是從main方法呼叫的。

如果您編譯並執行上述程式,您將得到以下結果:

輸出

This is an inner class.

訪問私有成員

如前所述,內部類也用於訪問類的私有成員。假設一個類具有私有成員來訪問它們。在其中編寫一個內部類,從內部類中的一個方法(例如getValue())返回私有成員,最後從另一個類(您想從中訪問私有成員的類)呼叫內部類的getValue()方法。

要例項化內部類,首先必須例項化外部類。此後,使用外部類的物件,以下是例項化內部類的方法。

Outer_Demo outer = new Outer_Demo();
Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();

以下程式演示瞭如何使用內部類訪問類的私有成員。

示例

class Outer_Demo {
   // private variable of the outer class
   private int num = 175;  
   
   // inner class
   public class Inner_Demo {
      public int getNum() {
         System.out.println("This is the getnum method of the inner class");
         return num;
      }
   }
}

public class My_class2 {

   public static void main(String args[]) {
      // Instantiating the outer class
      Outer_Demo outer = new Outer_Demo();
      
      // Instantiating the inner class
      Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
      System.out.println(inner.getNum());
   }
}

如果您編譯並執行上述程式,您將得到以下結果:

輸出

This is the getnum method of the inner class: 175

方法區域性內部類

在Java中,我們可以在方法中編寫一個類,這將是一個區域性型別。與區域性變數一樣,內部類的作用域也限制在方法內。

方法區域性內部類只能在其定義的方法內例項化。以下程式演示瞭如何使用方法區域性內部類。

示例

public class Outerclass {
   // instance method of the outer class 
   void my_Method() {
      int num = 23;

      // method-local inner class
      class MethodInner_Demo {
         public void print() {
            System.out.println("This is method inner class "+num);	   
         }   
      } // end of inner class
	   
      // Accessing the inner class
      MethodInner_Demo inner = new MethodInner_Demo();
      inner.print();
   }
   
   public static void main(String args[]) {
      Outerclass outer = new Outerclass();
      outer.my_Method();	   	   
   }
}

如果您編譯並執行上述程式,您將得到以下結果:

輸出

This is method inner class 23

匿名內部類

未宣告類名的內部類稱為匿名內部類。對於匿名內部類,我們同時宣告和例項化它們。通常,每當需要重寫類或介面的方法時,都會使用它們。匿名內部類的語法如下:

語法

AnonymousInner an_inner = new AnonymousInner() {
   public void my_method() {
      ........
      ........
   }   
};

以下程式演示瞭如何使用匿名內部類重寫類的方法。

示例

abstract class AnonymousInner {
   public abstract void mymethod();
}

public class Outer_class {

   public static void main(String args[]) {
      AnonymousInner inner = new AnonymousInner() {
         public void mymethod() {
            System.out.println("This is an example of anonymous inner class");
         }
      };
      inner.mymethod();	
   }
}

如果您編譯並執行上述程式,您將得到以下結果:

輸出

This is an example of anonymous inner class

同樣,您可以使用匿名內部類重寫具體類和介面的方法。

匿名內部類作為引數

通常,如果方法接受介面、抽象類或具體類的物件,那麼我們可以實現介面、擴充套件抽象類並將物件傳遞給方法。如果是類,我們可以直接將其傳遞給方法。

但在所有三種情況下,都可以將匿名內部類傳遞給方法。以下是將匿名內部類作為方法引數傳遞的語法:

obj.my_Method(new My_Class() {
   public void Do() {
      .....
      .....
   }
});

以下程式演示瞭如何將匿名內部類作為方法引數傳遞。

示例

// interface
interface Message {
   String greet();
}

public class My_class {
   // method which accepts the object of interface Message
   public void displayMessage(Message m) {
      System.out.println(m.greet() +
         ", This is an example of anonymous inner class as an argument");  
   }

   public static void main(String args[]) {
      // Instantiating the class
      My_class obj = new My_class();

      // Passing an anonymous inner class as an argument
      obj.displayMessage(new Message() {
         public String greet() {
            return "Hello";
         }
      });
   }
}

如果您編譯並執行上述程式,它將給出以下結果:

輸出

Hello, This is an example of anonymous inner class as an argument

靜態巢狀類

靜態內部類是一個巢狀類,它是外部類的靜態成員。可以使用其他靜態成員訪問它,而無需例項化外部類。就像靜態成員一樣,靜態巢狀類無法訪問外部類的例項變數和方法。靜態巢狀類的語法如下:

語法

class MyOuter {
   static class Nested_Demo {
   }
}

靜態巢狀類的例項化與內部類的例項化略有不同。以下程式演示瞭如何使用靜態巢狀類。

示例

public class Outer {
   static class Nested_Demo {
      public void my_method() {
         System.out.println("This is my nested class");
      }
   }
   
   public static void main(String args[]) {
      Outer.Nested_Demo nested = new Outer.Nested_Demo();	 
      nested.my_method();
   }
}

如果您編譯並執行上述程式,您將得到以下結果:

輸出

This is my nested class

Java - 字元類

通常,當我們處理字元時,我們使用原始資料型別char。

示例

char ch = 'a';

// Unicode for uppercase Greek omega character
char uniChar = '\u039A'; 

// an array of chars
char[] charArray ={ 'a', 'b', 'c', 'd', 'e' }; 

然而,在開發過程中,我們會遇到需要使用物件而不是原始資料型別的情況。為了實現這一點,Java 為原始資料型別 char 提供了包裝類Character

Character 類提供了一些有用的類(即靜態)方法來操作字元。您可以使用 Character 建構函式建立 Character 物件:

Character ch = new Character('a');

在某些情況下,Java 編譯器也會為您建立 Character 物件。例如,如果您將原始 char 傳遞給期望物件的某個方法,編譯器會自動為您將 char 轉換為 Character。如果轉換反過來進行,則此特性稱為自動裝箱或拆箱。

示例

// Here following primitive char 'a'
// is boxed into the Character object ch
Character ch = 'a';

// Here primitive 'x' is boxed for method test,
// return is unboxed to char 'c'
char c = test('x');

轉義序列

以反斜槓 (\) 開頭的字元是轉義序列,對編譯器具有特殊含義。

換行符 (\n) 在本教程中經常用於 System.out.println() 語句,以在字串列印後換到下一行。

下表顯示了 Java 轉義序列:

轉義序列 描述
\t 在此處文字中插入製表符。
\b 在此處文字中插入退格符。
\n 在此處文字中插入換行符。
\r 在此處文字中插入回車符。
\f 在此處文字中插入換頁符。
\' 在此處文字中插入單引號字元。
\" 在此處文字中插入雙引號字元。
\\ 在此處文字中插入反斜槓字元。

當在列印語句中遇到轉義序列時,編譯器會相應地解釋它。

示例

如果要在引號內新增引號,則必須在內部引號上使用轉義序列 \":

public class Test {

   public static void main(String args[]) {
      System.out.println("She said \"Hello!\" to me.");
   }
}

輸出

She said "Hello!" to me.

字元方法

以下是 Character 類所有子類實現的重要例項方法列表:

序號 方法和描述
1 isLetter()

確定指定的 char 值是否為字母。

2 isDigit()

確定指定的 char 值是否為數字。

3 isWhitespace()

確定指定的 char 值是否為空格。

4 isUpperCase()

確定指定的 char 值是否為大寫。

5 isLowerCase()

確定指定的 char 值是否為小寫。

6 toUpperCase()

返回指定 char 值的大寫形式。

7 toLowerCase()

返回指定 char 值的小寫形式。

8 toString()

返回表示指定字元值的 String 物件,即一個字元的字串。

有關完整的方法列表,請參閱 java.lang.Character API 規範。

下一步是什麼?

在下一節中,我們將學習 Java 中的 String 類。您將學習如何高效地宣告和使用字串,以及 String 類中一些重要的方法。

Java - 檔案和 I/O

java.io 包包含幾乎所有可能需要的類,用於在 Java 中執行輸入和輸出 (I/O)。所有這些流都代表輸入源和輸出目標。java.io 包中的流支援多種資料,例如基本型別、物件、本地化字元等。

流可以定義為資料序列。有兩種型別的流:

  • InputStream - InputStream 用於從源讀取資料。

  • OutputStream - OutputStream 用於將資料寫入目標。

Streams

Java 為與檔案和網路相關的 I/O 提供了強大而靈活的支援,但本教程涵蓋了與流和 I/O 相關的非常基本的功能。我們將逐一檢視最常用的示例:

位元組流

Java 位元組流用於執行 8 位位元組的輸入和輸出。雖然有許多與位元組流相關的類,但最常用的類是FileInputStreamFileOutputStream。以下是一個示例,它使用這兩個類將輸入檔案複製到輸出檔案:

示例

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFile {

   public static void main(String args[]) throws IOException {  
      FileInputStream in = null;
      FileOutputStream out = null;

      try {
         in = new FileInputStream("input.txt");
         out = new FileOutputStream("output.txt");
         
         int c;
         while ((c = in.read()) != -1) {
            out.write(c);
         }
      }finally {
         if (in != null) {
            in.close();
         }
         if (out != null) {
            out.close();
         }
      }
   }
}

現在讓我們有一個名為input.txt的檔案,其內容如下:

This is test for copy file.

下一步,編譯上述程式並執行它,這將導致建立一個 output.txt 檔案,其內容與 input.txt 中的內容相同。所以讓我們將上述程式碼放在 CopyFile.java 檔案中並執行以下操作:

$javac CopyFile.java
$java CopyFile

字元流

Java 位元組流用於執行 8 位位元組的輸入和輸出,而 Java 字元流用於執行 16 位 Unicode 的輸入和輸出。雖然有許多與字元流相關的類,但最常用的類是FileReaderFileWriter。雖然 FileReader 在內部使用 FileInputStream,FileWriter 使用 FileOutputStream,但這裡的主要區別在於 FileReader 一次讀取兩個位元組,而 FileWriter 一次寫入兩個位元組。

我們可以重寫上面的例子,它使用這兩個類將一個輸入檔案(包含 Unicode 字元)複製到一個輸出檔案:

示例

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CopyFile {

   public static void main(String args[]) throws IOException {
      FileReader in = null;
      FileWriter out = null;

      try {
         in = new FileReader("input.txt");
         out = new FileWriter("output.txt");
         
         int c;
         while ((c = in.read()) != -1) {
            out.write(c);
         }
      }finally {
         if (in != null) {
            in.close();
         }
         if (out != null) {
            out.close();
         }
      }
   }
}

現在讓我們有一個名為input.txt的檔案,其內容如下:

This is test for copy file.

下一步,編譯上述程式並執行它,這將導致建立一個 output.txt 檔案,其內容與 input.txt 中的內容相同。所以讓我們將上述程式碼放在 CopyFile.java 檔案中並執行以下操作:

$javac CopyFile.java
$java CopyFile

標準流

所有程式語言都支援標準 I/O,其中使用者的程式可以從鍵盤獲取輸入,然後在計算機螢幕上產生輸出。如果您瞭解 C 或 C++ 程式語言,那麼您一定了解三個標準裝置 STDIN、STDOUT 和 STDERR。類似地,Java 提供以下三個標準流:

  • 標準輸入 - 用於將資料饋送到使用者程式,通常使用鍵盤作為標準輸入流,表示為System.in

  • 標準輸出 - 用於輸出使用者程式產生的資料,通常使用計算機螢幕作為標準輸出流,表示為System.out

  • 標準錯誤 - 用於輸出使用者程式產生的錯誤資料,通常使用計算機螢幕作為標準錯誤流,表示為System.err

以下是一個簡單的程式,它建立InputStreamReader 來讀取標準輸入流,直到使用者鍵入“q”:

示例

import java.io.InputStreamReader;

public class ReadConsole {

   public static void main(String args[]) throws IOException {
      InputStreamReader cin = null;

      try {
         cin = new InputStreamReader(System.in);
         System.out.println("Enter characters, 'q' to quit.");
         char c;
         do {
            c = (char) cin.read();
            System.out.print(c);
         } while(c != 'q');
      }finally {
         if (cin != null) {
            cin.close();
         }
      }
   }
}

讓我們將上述程式碼儲存在 ReadConsole.java 檔案中,並嘗試編譯和執行它,如下面的程式所示。此程式會繼續讀取和輸出相同的字元,直到我們按下 'q':

$javac ReadConsole.java
$java ReadConsole
Enter characters, 'q' to quit.
1
1
e
e
q
q

讀取和寫入檔案

如前所述,流可以定義為資料序列。InputStream 用於從源讀取資料,OutputStream 用於將資料寫入目標。

這是一個處理輸入和輸出流的類層次結構。

Files IO

兩個重要的流是FileInputStreamFileOutputStream,本教程將討論它們。

FileInputStream

此流用於從檔案讀取資料。可以使用關鍵字new 建立物件,並且有幾種型別的建構函式可用。

以下建構函式採用檔名作為字串,以建立用於讀取檔案的輸入流物件:

InputStream f = new FileInputStream("C:/java/hello");

以下建構函式採用檔案物件來建立用於讀取檔案的輸入流物件。首先,我們使用 File() 方法建立一個檔案物件,如下所示:

File f = new File("C:/java/hello");
InputStream f = new FileInputStream(f);

一旦您掌握了InputStream 物件,就可以使用一系列輔助方法來讀取流或對流執行其他操作。

序號 方法和描述
1

public void close() throws IOException{}

此方法關閉檔案輸出流。釋放與檔案關聯的任何系統資源。丟擲 IOException。

2

protected void finalize()throws IOException {}

此方法清理與檔案的連線。確保當不再引用此流時,呼叫此檔案輸出流的 close 方法。丟擲 IOException。

3

public int read(int r)throws IOException{}

此方法從 InputStream 讀取指定位元組的資料。返回一個 int。返回下一位元組資料,如果檔案結束則返回 -1。

4

public int read(byte[] r) throws IOException{}

此方法將從輸入流讀取 r.length 個位元組到陣列中。返回讀取的總位元組數。如果是檔案結尾,則返回 -1。

5

public int available() throws IOException{}

給出可以從此檔案輸入流讀取的位元組數。返回一個 int。

FileOutputStream

FileOutputStream 用於建立檔案並將資料寫入其中。如果該檔案尚不存在,則流會在開啟它進行輸出之前建立它。

以下有兩個建構函式可用於建立 FileOutputStream 物件。

以下建構函式採用檔名作為字串,以建立用於寫入檔案的輸入流物件:

OutputStream f = new FileOutputStream("C:/java/hello") 

以下建構函式採用檔案物件來建立用於寫入檔案的輸出流物件。首先,我們使用 File() 方法建立一個檔案物件,如下所示:

File f = new File("C:/java/hello");
OutputStream f = new FileOutputStream(f);

一旦您掌握了OutputStream 物件,就可以使用一系列輔助方法來寫入流或對流執行其他操作。

序號 方法和描述
1

public void close() throws IOException{}

此方法關閉檔案輸出流。釋放與檔案關聯的任何系統資源。丟擲 IOException。

2

protected void finalize()throws IOException {}

此方法清理與檔案的連線。確保當不再引用此流時,呼叫此檔案輸出流的 close 方法。丟擲 IOException。

3

public void write(int w)throws IOException{}

此方法將指定的位元組寫入輸出流。

4

public void write(byte[] w)

將提到的位元組陣列中的 w.length 個位元組寫入 OutputStream。

示例

以下示例演示了 InputStream 和 OutputStream:

import java.io.OutputStream;

public class fileStreamTest {

   public static void main(String args[]) {
   
      try {
         byte bWrite [] = {11,21,3,40,5};
         OutputStream os = new FileOutputStream("test.txt");
         for(int x = 0; x < bWrite.length ; x++) {
            os.write( bWrite[x] );   // writes the bytes
         }
         os.close();
     
         InputStream is = new FileInputStream("test.txt");
         int size = is.available();

         for(int i = 0; i < size; i++) {
            System.out.print((char)is.read() + "  ");
         }
         is.close();
      } catch (IOException e) {
         System.out.print("Exception");
      }	
   }
}

上述程式碼將建立檔案 test.txt 並以二進位制格式寫入給定的數字。標準輸出螢幕上的輸出也將相同。

Java 中的目錄

目錄是一個 File,它可以包含其他檔案和目錄的列表。您可以使用File 物件來建立目錄,列出目錄中可用的檔案。有關完整詳細資訊,請檢視可以在 File 物件上呼叫的所有方法列表以及與目錄相關的哪些方法。

建立目錄

有兩個有用的File 實用程式方法可用於建立目錄:

  • mkdir( ) 方法建立一個目錄,成功時返回 true,失敗時返回 false。失敗表示 File 物件中指定的路徑已存在,或者由於整個路徑尚不存在而無法建立目錄。

  • mkdirs() 方法建立目錄及其所有父目錄。

以下示例建立“/tmp/user/java/bin”目錄:

示例

import java.io.File;

public class CreateDir {

   public static void main(String args[]) {
      String dirname = "/tmp/user/java/bin";
      File d = new File(dirname);
      
      // Create directory now.
      d.mkdirs();
   }
}

編譯並執行上述程式碼以建立“/tmp/user/java/bin”。

注意 - Java 根據約定自動處理 UNIX 和 Windows 上的路徑分隔符。如果您在 Windows 版本的 Java 上使用正斜槓 (/),路徑仍然可以正確解析。

列出目錄

您可以使用File物件提供的list( )方法列出目錄中所有可用的檔案和目錄,如下所示:

示例

import java.io.File;

public class ReadDir {

   public static void main(String[] args) {
      File file = null;
      String[] paths;
  
      try {      
         // create new file object
         file = new File("/tmp");

         // array of files and directory
         paths = file.list();

         // for each name in the path array
         for(String path:paths) {
            // prints filename and directory name
            System.out.println(path);
         }
      } catch (Exception e) {
         // if any error occurs
         e.printStackTrace();
      }
   }
}

這將根據您的/tmp目錄中可用的目錄和檔案產生以下結果:

輸出

test1.txt
test2.txt
ReadDir.java
ReadDir.class

Java - 異常

異常(或異常事件)是在程式執行期間出現的問題。當發生異常時,程式的正常流程會被中斷,程式/應用程式會異常終止,這是不推薦的,因此,這些異常需要被處理。

異常可能由於許多不同的原因發生。以下是發生異常的一些場景。

  • 使用者輸入了無效資料。

  • 需要開啟的檔案找不到。

  • 網路連線在通訊過程中斷開,或者JVM記憶體不足。

其中一些異常是由使用者錯誤引起的,另一些是由程式設計師錯誤引起的,還有一些是由以某種方式發生故障的物理資源引起的。

基於這些,我們有三種類型的異常。您需要了解它們才能知道Java中的異常處理是如何工作的。

  • 已檢查異常 - 已檢查異常是在編譯時由編譯器檢查(通知)的異常,這些也稱為編譯時異常。這些異常不能簡單地被忽略,程式設計師應該處理這些異常。

例如,如果您在程式中使用FileReader類從檔案讀取資料,如果其建構函式中指定的檔案不存在,則會發生FileNotFoundException,編譯器會提示程式設計師處理該異常。

示例

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {

   public static void main(String args[]) {		
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file); 
   }
}

如果您嘗試編譯上述程式,將會得到以下異常。

輸出

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(file);
                      ^
1 error

注意 - 由於FileReader類的read()close()方法丟擲IOException,您可以觀察到編譯器通知處理IOException以及FileNotFoundException。

  • 未檢查異常 - 未檢查異常是在執行時發生的異常。這些也稱為執行時異常。這些包括程式設計錯誤,例如邏輯錯誤或API的錯誤使用。執行時異常在編譯時被忽略。

例如,如果您在程式中聲明瞭一個大小為5的陣列,並嘗試呼叫陣列的第6個元素,則會發生ArrayIndexOutOfBoundsException異常。

示例

public class Unchecked_Demo {
   
   public static void main(String args[]) {
      int num[] = {1, 2, 3, 4};
      System.out.println(num[5]);
   }
}

如果您編譯並執行上述程式,將會得到以下異常。

輸出

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
  • 錯誤 - 這根本不是異常,而是超出使用者或程式設計師控制範圍的問題。在您的程式碼中通常會忽略錯誤,因為您很少能夠處理錯誤。例如,如果發生堆疊溢位,則會發生錯誤。它們在編譯時也會被忽略。

異常層次結構

所有異常類都是java.lang.Exception類的子型別。異常類是Throwable類的子類。除了異常類之外,還有一個名為Error的子類,它派生自Throwable類。

錯誤是在嚴重故障情況下發生的異常情況,Java程式不處理這些錯誤。生成錯誤是為了指示執行時環境生成的錯誤。例如:JVM記憶體不足。通常,程式無法從錯誤中恢復。

Exception類有兩個主要的子類:IOException類和RuntimeException類。

Exceptions1

以下是大多數常見的已檢查和未檢查Java內建異常列表。

異常方法

以下是Throwable類中可用的一些重要方法列表。

序號 方法和描述
1

public String getMessage()

返回有關發生的異常的詳細訊息。此訊息在Throwable建構函式中初始化。

2

public Throwable getCause()

返回由Throwable物件表示的異常原因。

3

public String toString()

返回類的名稱與getMessage()的結果連線在一起。

4

public void printStackTrace()

將toString()的結果以及堆疊跟蹤列印到System.err(錯誤輸出流)。

5

public StackTraceElement [] getStackTrace()

返回一個包含堆疊跟蹤中每個元素的陣列。索引為0的元素代表呼叫堆疊的頂部,陣列中的最後一個元素代表呼叫堆疊底部的​方法。

6

public Throwable fillInStackTrace()

用當前堆疊跟蹤填充此Throwable物件的堆疊跟蹤,新增到堆疊跟蹤中的任何先前資訊。

捕獲異常

方法使用trycatch關鍵字的組合來捕獲異常。try/catch塊放置在可能生成異常的程式碼周圍。try/catch塊內的程式碼稱為受保護程式碼,使用try/catch的語法如下所示:

語法

try {
   // Protected code
} catch (ExceptionName e1) {
   // Catch block
}

易於發生異常的程式碼放在try塊中。當發生異常時,該異常由與其關聯的catch塊處理。每個try塊都應該緊跟一個catch塊或finally塊。

catch語句涉及宣告您試圖捕獲的異常型別。如果在受保護的程式碼中發生異常,則檢查try後面的catch塊(或塊)。如果發生的異常型別在catch塊中列出,則異常將像引數傳遞到方法引數一樣傳遞到catch塊。

示例

在下面的示例中,聲明瞭一個包含2個元素的陣列。然後程式碼嘗試訪問陣列的第3個元素,這將引發異常。

// File Name : ExcepTest.java
import java.io.*;

public class ExcepTest {

   public static void main(String args[]) {
      try {
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

輸出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多個Catch塊

try塊可以後跟多個catch塊。多個catch塊的語法如下所示:

語法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}

前面的語句演示了三個catch塊,但是您可以在單個try之後擁有任意數量的catch塊。如果在受保護的程式碼中發生異常,則異常將被拋到列表中的第一個catch塊。如果丟擲的異常的資料型別與ExceptionType1匹配,則它將在此處被捕獲。如果不是,則異常將傳遞到第二個catch語句。這種情況將持續下去,直到異常被捕獲或穿過所有catch塊,在這種情況下,當前方法將停止執行,並且異常將被拋到呼叫堆疊上的前一個方法。

示例

以下程式碼段顯示瞭如何使用多個try/catch語句。

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
} catch (IOException i) {
   i.printStackTrace();
   return -1;
} catch (FileNotFoundException f) // Not valid! {
   f.printStackTrace();
   return -1;
}

捕獲多種型別的異常

從Java 7開始,您可以使用單個catch塊處理多個異常,此功能簡化了程式碼。以下是執行此操作的方法:

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

Throws/Throw關鍵字

如果方法不處理已檢查異常,則該方法必須使用throws關鍵字宣告它。throws關鍵字出現在方法簽名末尾。

您可以使用throw關鍵字丟擲異常,無論是新例項化的異常還是您剛剛捕獲的異常。

嘗試理解throws和throw關鍵字之間的區別,throws用於推遲已檢查異常的處理,而throw用於顯式呼叫異常。

以下方法宣告它丟擲RemoteException:

示例

import java.io.*;
public class className {

   public void deposit(double amount) throws RemoteException {
      // Method implementation
      throw new RemoteException();
   }
   // Remainder of class definition
}

方法可以宣告它丟擲多個異常,在這種情況下,異常以逗號分隔的列表的形式宣告。例如,以下方法宣告它丟擲RemoteException和InsufficientFundsException:

示例

import java.io.*;
public class className {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Method implementation
   }
   // Remainder of class definition
}

Finally塊

finally塊位於try塊或catch塊之後。無論是否發生異常,finally塊中的程式碼始終都會執行。

使用finally塊允許您執行任何您想要執行的清理型別語句,無論受保護程式碼中發生了什麼。

finally塊出現在catch塊的末尾,並具有以下語法:

語法

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}finally {
   // The finally block always executes.
}

示例

public class ExcepTest {

   public static void main(String args[]) {
      int a[] = new int[2];
      try {
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }finally {
         a[0] = 6;
         System.out.println("First element value: " + a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

輸出

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

注意以下幾點:

  • catch子句不能沒有try語句。

  • 無論何時出現try/catch塊,都不強制要求使用finally子句。

  • try塊不能沒有catch子句或finally子句。

  • try、catch、finally塊之間不能存在任何程式碼。

try-with-resources

通常,當我們使用任何資源(如流、連線等)時,我們必須使用finally塊顯式關閉它們。在下面的程式中,我們使用FileReader從檔案讀取資料,並使用finally塊關閉它。

示例

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

   public static void main(String args[]) {
      FileReader fr = null;		
      try {
         File file = new File("file.txt");
         fr = new FileReader(file); char [] a = new char[50];
         fr.read(a);   // reads the content to the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }finally {
         try {
            fr.close();
         } catch (IOException ex) {		
            ex.printStackTrace();
         }
      }
   }
}

try-with-resources(也稱為自動資源管理)是Java 7中引入的一種新的異常處理機制,它會自動關閉try catch塊中使用的資源。

要使用此語句,您只需在括號內宣告所需的資源,建立的資源將在塊末尾自動關閉。以下是try-with-resources語句的語法。

語法

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

以下是使用try-with-resources語句讀取檔案資料的程式。

示例

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://file.txt")) {
         char [] a = new char[50];
         fr.read(a);   // reads the contentto the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

使用try-with-resources語句時,需要注意以下幾點。

  • 要將類與try-with-resources語句一起使用,它必須實現AutoCloseable介面,並且它的close()方法會在執行時自動呼叫。

  • 您可以在try-with-resources語句中宣告多個類。

  • 當您在try-with-resources語句的try塊中宣告多個類時,這些類將以相反的順序關閉。

  • 除了在括號內宣告資源外,其他所有內容都與try塊的普通try/catch塊相同。

  • try中宣告的資源在try塊開始之前被例項化。

  • 在try塊中宣告的資源隱式宣告為final。

使用者定義的異常

您可以在Java中建立自己的異常。編寫自己的異常類時,請記住以下幾點:

  • 所有異常都必須是Throwable的子類。

  • 如果您想編寫一個由Handle or Declare規則自動強制執行的已檢查異常,則需要擴充套件Exception類。

  • 如果您想編寫執行時異常,則需要擴充套件RuntimeException類。

我們可以如下定義我們自己的異常類:

class MyException extends Exception {
}

您只需要擴充套件預定義的Exception類即可建立您自己的異常。這些被認為是已檢查異常。以下InsufficientFundsException類是一個使用者定義的異常,它擴充套件了Exception類,使其成為已檢查異常。異常類與任何其他類一樣,包含有用的欄位和方法。

示例

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

為了演示使用我們使用者定義的異常,以下CheckingAccount類包含一個丟擲InsufficientFundsException的withdraw()方法。

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount {
   private double balance;
   private int number;
   
   public CheckingAccount(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

以下BankDemo程式演示了呼叫CheckingAccount的deposit()和withdraw()方法。

// File Name BankDemo.java
public class BankDemo {

   public static void main(String [] args) {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      
      try {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      } catch (InsufficientFundsException e) {
         System.out.println("Sorry, but you are short $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

編譯以上三個檔案並執行BankDemo。這將產生以下結果:

輸出

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
         at CheckingAccount.withdraw(CheckingAccount.java:25)
         at BankDemo.main(BankDemo.java:13)

常見異常

在Java中,可以定義兩類異常和錯誤。

  • JVM異常 - 這些是由JVM專門或邏輯上丟擲的異常/錯誤。例如:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException。

  • 程式設計異常 - 這些異常是由應用程式或API程式設計師顯式丟擲的。例如:IllegalArgumentException、IllegalStateException。

Java - 多執行緒

Java是一種多執行緒程式語言,這意味著我們可以使用Java開發多執行緒程式。多執行緒程式包含兩個或多個可以併發執行的部分,每個部分可以同時處理不同的任務,從而最佳地利用可用資源,尤其是在您的計算機具有多個CPU的情況下。

根據定義,多工處理是指多個程序共享公共處理資源(如CPU)。多執行緒將多工處理的概念擴充套件到應用程式,您可以在單個應用程式中將特定操作細分為各個執行緒。每個執行緒都可以並行執行。作業系統不僅在不同的應用程式之間劃分處理時間,還在應用程式中的每個執行緒之間劃分處理時間。

多執行緒允許您編寫程式,使其中的多個活動能夠併發進行。

執行緒的生命週期

執行緒在其生命週期中會經歷不同的階段。例如,一個執行緒會誕生、啟動、執行,然後消亡。下圖顯示了執行緒的完整生命週期。

Java Thread Life Cycle

以下是生命週期的各個階段:

  • 新建 - 新執行緒從新建狀態開始其生命週期。它保持在此狀態,直到程式啟動執行緒。它也被稱為新生執行緒

  • 可執行 - 新生執行緒啟動後,執行緒變為可執行狀態。處於此狀態的執行緒被認為正在執行其任務。

  • 等待 - 有時,當執行緒等待另一個執行緒執行任務時,執行緒會轉換到等待狀態。只有當另一個執行緒向等待執行緒發出繼續執行的訊號時,執行緒才會轉換回可執行狀態。

  • 計時等待 - 可執行執行緒可以進入計時等待狀態,持續指定的時間間隔。處於此狀態的執行緒在該時間間隔到期或其等待的事件發生時轉換回可執行狀態。

  • 終止(死亡) - 可執行執行緒在其任務完成或以其他方式終止時進入終止狀態。

執行緒優先順序

每個 Java 執行緒都有一個優先順序,這有助於作業系統確定執行緒排程的順序。

Java 執行緒優先順序的範圍在 MIN_PRIORITY(常量值為 1)和 MAX_PRIORITY(常量值為 10)之間。預設情況下,每個執行緒都被賦予 NORM_PRIORITY(常量值為 5)的優先順序。

優先順序較高的執行緒對程式更重要,應在優先順序較低的執行緒之前分配處理器時間。但是,執行緒優先順序不能保證執行緒執行的順序,並且很大程度上依賴於平臺。

透過實現 Runnable 介面建立執行緒

如果您的類旨在作為執行緒執行,那麼您可以透過實現Runnable介面來實現這一點。您需要遵循三個基本步驟:

步驟 1

第一步,您需要實現Runnable介面提供的 run() 方法。此方法為執行緒提供了一個入口點,您將在此方法中放入完整的業務邏輯。以下是 run() 方法的簡單語法:

public void run( )

步驟 2

第二步,您將使用以下建構函式例項化一個Thread物件:

Thread(Runnable threadObj, String threadName);

其中,threadObj 是實現Runnable介面的類的例項,threadName 是賦予新執行緒的名稱。

步驟 3

建立 Thread 物件後,您可以透過呼叫start()方法啟動它,該方法會呼叫 run() 方法。以下是 start() 方法的簡單語法:

void start();

示例

這是一個建立新執行緒並啟動執行它的示例:

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

輸出

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

透過擴充套件 Thread 類建立執行緒

建立執行緒的第二種方法是建立一個擴充套件Thread類的新的類,使用以下兩個簡單的步驟。這種方法在處理使用 Thread 類中可用方法建立的多個執行緒時提供了更大的靈活性。

步驟 1

您需要重寫 Thread 類中可用的run()方法。此方法為執行緒提供了一個入口點,您將在此方法中放入完整的業務邏輯。以下是 run() 方法的簡單語法:

public void run( )

步驟 2

建立 Thread 物件後,您可以透過呼叫start()方法啟動它,該方法會呼叫 run() 方法。以下是 start() 方法的簡單語法:

void start( );

示例

以下是重寫為擴充套件 Thread 的前面程式:

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

輸出

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

執行緒方法

以下是 Thread 類中可用的一些重要方法。

序號 方法和描述
1

public void start()

在單獨的執行路徑中啟動執行緒,然後在此 Thread 物件上呼叫 run() 方法。

2

public void run()

如果此 Thread 物件是使用單獨的 Runnable target 例項化的,則會在該 Runnable 物件上呼叫 run() 方法。

3

public final void setName(String name)

更改 Thread 物件的名稱。還有一個 getName() 方法用於檢索名稱。

4

public final void setPriority(int priority)

設定此 Thread 物件的優先順序。可能的值介於 1 和 10 之間。

5

public final void setDaemon(boolean on)

值為 true 表示此執行緒為守護執行緒。

6

public final void join(long millisec)

當前執行緒在此第二個執行緒上呼叫此方法,導致當前執行緒阻塞,直到第二個執行緒終止或指定的毫秒數過去。

7

public void interrupt()

中斷此執行緒,如果它由於任何原因被阻塞,則使其繼續執行。

8

public final boolean isAlive()

如果執行緒處於活動狀態,則返回 true,即執行緒啟動後但完成執行之前的任何時間。

前面這些方法是在特定的 Thread 物件上呼叫的。Thread 類中的以下方法是靜態的。呼叫其中一個靜態方法會在當前執行的執行緒上執行操作。

序號 方法和描述
1

public static void yield()

導致當前執行的執行緒讓位於任何其他具有相同優先順序且正在等待排程的執行緒。

2

public static void sleep(long millisec)

導致當前執行的執行緒至少阻塞指定的毫秒數。

3

public static boolean holdsLock(Object x)

如果當前執行緒持有給定物件的鎖,則返回 true。

4

public static Thread currentThread()

返回對當前執行執行緒的引用,該執行緒是呼叫此方法的執行緒。

5

public static void dumpStack()

列印當前執行執行緒的堆疊跟蹤,這在除錯多執行緒應用程式時非常有用。

示例

以下 ThreadClassDemo 程式演示了 Thread 類的一些這些方法。考慮一個實現Runnable的類DisplayMessage

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

以下是另一個擴充套件 Thread 類的類:

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

以下是主程式,它使用了上面定義的類:

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();

      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      } catch (InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}
class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}
class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

輸出

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

主要的 Java 多執行緒概念

在 Java 中進行多執行緒程式設計時,您需要掌握以下概念:

Java - 執行緒同步

當我們在程式中啟動兩個或多個執行緒時,可能會出現多個執行緒試圖訪問相同資源的情況,最終由於併發問題而產生不可預見的結果。例如,如果多個執行緒試圖寫入同一個檔案,那麼它們可能會損壞資料,因為其中一個執行緒可能會覆蓋資料,或者當一個執行緒正在開啟同一個檔案時,另一個執行緒可能正在關閉同一個檔案。

因此,需要同步多個執行緒的操作,並確保在給定時間點只有一個執行緒可以訪問資源。這是使用稱為監視器的概念實現的。Java 中的每個物件都與一個監視器相關聯,執行緒可以鎖定或解鎖該監視器。一次只有一個執行緒可以持有監視器的鎖。

Java 程式語言提供了一種非常方便的方法來建立執行緒並使用synchronized塊同步其任務。您將共享資源儲存在此塊中。以下是 synchronized 語句的一般形式:

語法

synchronized(objectidentifier) {
   // Access shared variables and other shared resources
}

這裡,objectidentifier 是對物件的引用,其鎖與 synchronized 語句表示的監視器相關聯。現在我們將看到兩個示例,我們將使用兩個不同的執行緒列印計數器。當執行緒不同步時,它們列印的計數器值不是按順序的,但是當我們將計數器放在 synchronized() 塊內列印時,它會為兩個執行緒按順序列印計數器。

無同步的多執行緒示例

這是一個簡單的示例,它可能會也可能不會按順序列印計數器值,並且每次執行它時,它都會根據 CPU 對執行緒的可用性產生不同的結果。

示例

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      PD.printCount();
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {
   public static void main(String args[]) {

      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
         try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

每次執行此程式都會產生不同的結果:

輸出

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   5
Counter   ---   2
Counter   ---   1
Counter   ---   4
Thread Thread - 1  exiting.
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

帶同步的多執行緒示例

這是一個相同的示例,它按順序列印計數器值,並且每次執行它時,它都會產生相同的結果。

示例

class PrintDemo {
   public void printCount() {
      try {
         for(int i = 5; i > 0; i--) {
            System.out.println("Counter   ---   "  + i );
         }
      } catch (Exception e) {
         System.out.println("Thread  interrupted.");
      }
   }
}

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   PrintDemo  PD;

   ThreadDemo( String name,  PrintDemo pd) {
      threadName = name;
      PD = pd;
   }
   
   public void run() {
      synchronized(PD) {
         PD.printCount();
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo T1 = new ThreadDemo( "Thread - 1 ", PD );
      ThreadDemo T2 = new ThreadDemo( "Thread - 2 ", PD );

      T1.start();
      T2.start();

      // wait for threads to end
      try {
         T1.join();
         T2.join();
      } catch ( Exception e) {
         System.out.println("Interrupted");
      }
   }
}

每次執行此程式都會產生相同的結果:

輸出

Starting Thread - 1
Starting Thread - 2
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.

Java - 執行緒間通訊

如果您瞭解程序間通訊,那麼理解執行緒間通訊將很容易。當您開發兩個或多個執行緒交換某些資訊的應用程式時,執行緒間通訊非常重要。

有三種簡單的方法和一個小技巧可以實現執行緒通訊。所有三種方法都列在下面:

序號 方法和描述
1

public void wait()

導致當前執行緒等待,直到另一個執行緒呼叫 notify()。

2

public void notify()

喚醒正在此物件的監視器上等待的單個執行緒。

3

public void notifyAll()

喚醒所有在同一物件上呼叫 wait() 的執行緒。

這些方法已在 Object 中實現為final方法,因此它們在所有類中都可用。所有三種方法只能在synchronized上下文中呼叫。

示例

此示例顯示了兩個執行緒如何使用wait()notify()方法進行通訊。您可以使用相同的概念建立複雜的系統。

class Chat {
   boolean flag = false;

   public synchronized void Question(String msg) {
      if (flag) {
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
      System.out.println(msg);
      flag = true;
      notify();
   }

   public synchronized void Answer(String msg) {
      if (!flag) {
         try {
            wait();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }

      System.out.println(msg);
      flag = false;
      notify();
   }
}

class T1 implements Runnable {
   Chat m;
   String[] s1 = { "Hi", "How are you ?", "I am also doing fine!" };

   public T1(Chat m1) {
      this.m = m1;
      new Thread(this, "Question").start();
   }

   public void run() {
      for (int i = 0; i < s1.length; i++) {
         m.Question(s1[i]);
      }
   }
}

class T2 implements Runnable {
   Chat m;
   String[] s2 = { "Hi", "I am good, what about you?", "Great!" };

   public T2(Chat m2) {
      this.m = m2;
      new Thread(this, "Answer").start();
   }

   public void run() {
      for (int i = 0; i < s2.length; i++) {
         m.Answer(s2[i]);
      }
   }
}
public class TestThread {
   public static void main(String[] args) {
      Chat m = new Chat();
      new T1(m);
      new T2(m);
   }
}

輸出

Hi
Hi
How are you ?
I am good, what about you?
I am also doing fine!
Great!

Java - 執行緒死鎖

死鎖描述了一種情況,其中兩個或多個執行緒永遠阻塞,互相等待。當多個執行緒需要相同的鎖但以不同的順序獲取它們時,就會發生死鎖。Java 多執行緒程式可能會出現死鎖情況,因為synchronized關鍵字會導致執行執行緒在等待與指定物件關聯的鎖或監視器時阻塞。這是一個例子。

示例

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock2) {
            System.out.println("Thread 2: Holding lock 2...");
            
            try { Thread.sleep(10); }
            catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 1...");
            
            synchronized (Lock1) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

編譯並執行上述程式時,您會發現死鎖情況,以下是程式生成的輸出:

輸出

Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...

上述程式將永遠掛起,因為沒有一個執行緒能夠繼續執行並且互相等待釋放鎖,因此您可以透過按 CTRL+C 來退出程式。

死鎖解決方案示例

讓我們更改鎖的順序和同一程式的執行,看看這兩個執行緒是否仍然互相等待:

示例

public class TestThread {
   public static Object Lock1 = new Object();
   public static Object Lock2 = new Object();
   
   public static void main(String args[]) {
      ThreadDemo1 T1 = new ThreadDemo1();
      ThreadDemo2 T2 = new ThreadDemo2();
      T1.start();
      T2.start();
   }
   
   private static class ThreadDemo1 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");
            
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 1: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 1: Holding lock 1 & 2...");
            }
         }
      }
   }
   private static class ThreadDemo2 extends Thread {
      public void run() {
         synchronized (Lock1) {
            System.out.println("Thread 2: Holding lock 1...");
           
            try {
               Thread.sleep(10);
            } catch (InterruptedException e) {}
            System.out.println("Thread 2: Waiting for lock 2...");
            
            synchronized (Lock2) {
               System.out.println("Thread 2: Holding lock 1 & 2...");
            }
         }
      }
   } 
}

只需更改鎖的順序就可以防止程式陷入死鎖情況,並以以下結果完成:

輸出

Thread 1: Holding lock 1...
Thread 1: Waiting for lock 2...
Thread 1: Holding lock 1 & 2...
Thread 2: Holding lock 1...
Thread 2: Waiting for lock 2...
Thread 2: Holding lock 1 & 2...

上面的例子只是為了使概念清晰,但是,這是一個複雜的概念,在開發應用程式以處理死鎖情況之前,您應該深入研究它。

Java - 執行緒控制

核心 Java 提供對多執行緒程式的完全控制。您可以開發一個多執行緒程式,該程式可以根據您的需求被掛起、恢復或完全停止。您可以對執行緒物件使用各種靜態方法來控制其行為。下表列出了這些方法:

序號 方法和描述
1

public void suspend()

此方法將執行緒置於掛起狀態,可以使用 resume() 方法恢復。

2

public void stop()

此方法完全停止執行緒。

3

public void resume()

此方法恢復使用 suspend() 方法掛起的執行緒。

4

public void wait()

導致當前執行緒等待,直到另一個執行緒呼叫 notify()。

5

public void notify()

喚醒正在此物件的監視器上等待的單個執行緒。

請注意,最新版本的 Java 已棄用 suspend()、resume() 和 stop() 方法的使用,因此您需要使用可用的替代方法。

示例

class RunnableDemo implements Runnable {
   public Thread t;
   private String threadName;
   boolean suspended = false;

   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 10; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(300);
            synchronized(this) {
               while(suspended) {
                  wait();
               }
            }
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }

   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
   
   void suspend() {
      suspended = true;
   }
   
   synchronized void resume() {
      suspended = false;
      notify();
   }
}

public class TestThread {

   public static void main(String args[]) {

      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();

      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();

      try {
         Thread.sleep(1000);
         R1.suspend();
         System.out.println("Suspending First Thread");
         Thread.sleep(1000);
         R1.resume();
         System.out.println("Resuming First Thread");
         
         R2.suspend();
         System.out.println("Suspending thread Two");
         Thread.sleep(1000);
         R2.resume();
         System.out.println("Resuming thread Two");
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }try {
         System.out.println("Waiting for threads to finish.");
         R1.t.join();
         R2.t.join();
      } catch (InterruptedException e) {
         System.out.println("Main thread Interrupted");
      }
      System.out.println("Main thread exiting.");
   }
}

上述程式產生以下輸出:

輸出

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 10
Running Thread-2
Thread: Thread-2, 10
Thread: Thread-1, 9
Thread: Thread-2, 9
Thread: Thread-1, 8
Thread: Thread-2, 8
Thread: Thread-1, 7
Thread: Thread-2, 7
Suspending First Thread
Thread: Thread-2, 6
Thread: Thread-2, 5
Thread: Thread-2, 4
Resuming First Thread
Suspending thread Two
Thread: Thread-1, 6
Thread: Thread-1, 5
Thread: Thread-1, 4
Thread: Thread-1, 3
Resuming thread Two
Thread: Thread-2, 3
Waiting for threads to finish.
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.
Main thread exiting.

Java - 網路程式設計

術語網路程式設計是指編寫跨多個裝置(計算機)執行的程式,其中所有裝置都使用網路相互連線。

J2SE API 的 java.net 包包含一組類和介面,這些類和介面提供了低階通訊細節,允許您編寫專注於解決當前問題的程式。

java.net 包為兩種常見的網路協議提供支援:

  • TCP − TCP 代表傳輸控制協議 (Transmission Control Protocol),它允許兩個應用程式之間進行可靠的通訊。TCP 通常在網際網路協議 (Internet Protocol) 之上使用,稱為 TCP/IP。

  • UDP − UDP 代表使用者資料報協議 (User Datagram Protocol),這是一個無連線的協議,允許在應用程式之間傳輸資料包。

本章將對以下兩個主題進行深入講解:

  • 套接字程式設計 (Socket Programming) − 這是網路中最常用的概念,本章將對其進行詳細解釋。

  • URL 處理 − 這將在單獨章節中介紹。點選此處學習 Java 語言中的 URL 處理

套接字程式設計

套接字使用 TCP 提供了兩個計算機之間通訊的機制。客戶端程式在其通訊端建立一個套接字,並嘗試將該套接字連線到伺服器。

連線建立後,伺服器在其通訊端建立一個套接字物件。客戶端和伺服器現在可以透過寫入和讀取套接字來進行通訊。

java.net.Socket 類表示一個套接字,java.net.ServerSocket 類為伺服器程式提供了一種監聽客戶端並與之建立連線的機制。

使用套接字在兩臺計算機之間建立 TCP 連線時,會發生以下步驟:

  • 伺服器例項化一個 ServerSocket 物件,指定通訊將發生在哪個埠號上。

  • 伺服器呼叫 ServerSocket 類的 accept() 方法。此方法將等待直到客戶端連線到給定埠上的伺服器。

  • 伺服器等待後,客戶端例項化一個 Socket 物件,指定要連線到的伺服器名稱和埠號。

  • Socket 類的建構函式嘗試將客戶端連線到指定的伺服器和埠號。如果建立了通訊,則客戶端現在擁有一個能夠與伺服器通訊的 Socket 物件。

  • 在伺服器端,accept() 方法返回對伺服器上連線到客戶端套接字的新套接字的引用。

建立連線後,可以使用 I/O 流進行通訊。每個套接字都具有 OutputStream 和 InputStream。客戶端的 OutputStream 連線到伺服器的 InputStream,客戶端的 InputStream 連線到伺服器的 OutputStream。

TCP 是一種雙向通訊協議,因此資料可以同時透過這兩個流傳送。以下是提供實現套接字的完整方法集的一些有用類。

ServerSocket 類方法

java.net.ServerSocket 類用於伺服器應用程式獲取埠並監聽客戶端請求。

ServerSocket 類有四個建構函式:

序號 方法和描述
1

public ServerSocket(int port) throws IOException

嘗試建立一個繫結到指定埠的伺服器套接字。如果埠已被另一個應用程式繫結,則會發生異常。

2

public ServerSocket(int port, int backlog) throws IOException

與之前的建構函式類似,backlog 引數指定在等待佇列中儲存多少個傳入客戶端。

3

public ServerSocket(int port, int backlog, InetAddress address) throws IOException

與之前的建構函式類似,InetAddress 引數指定要繫結的本地 IP 地址。InetAddress 用於可能具有多個 IP 地址的伺服器,允許伺服器指定其要接受客戶端請求的哪個 IP 地址。

4

public ServerSocket() throws IOException

建立一個未繫結的伺服器套接字。使用此建構函式時,當準備好繫結伺服器套接字時,請使用 bind() 方法。

如果 ServerSocket 建構函式沒有丟擲異常,則表示您的應用程式已成功繫結到指定的埠,並已準備好接收客戶端請求。

以下是 ServerSocket 類的一些常用方法:

序號 方法和描述
1

public int getLocalPort()

返回伺服器套接字正在偵聽的埠。如果您在建構函式中傳入 0 作為埠號並讓伺服器為您查詢埠,則此方法很有用。

2

public Socket accept() throws IOException

等待傳入客戶端。此方法會阻塞,直到客戶端連線到指定埠上的伺服器或套接字超時(假設已使用 setSoTimeout() 方法設定超時值)。否則,此方法將無限期阻塞。

3

public void setSoTimeout(int timeout)

設定伺服器套接字在 accept() 期間等待客戶端的時間超時值。

4

public void bind(SocketAddress host, int backlog)

將套接字繫結到 SocketAddress 物件中指定的伺服器和埠。如果您使用無引數建構函式例項化了 ServerSocket,則使用此方法。

當 ServerSocket 呼叫 accept() 時,該方法會在客戶端連線之前不會返回。客戶端連線後,ServerSocket 會在未指定的埠上建立一個新的 Socket,並返回對這個新 Socket 的引用。現在客戶端和伺服器之間存在 TCP 連線,可以開始通訊。

Socket 類方法

java.net.Socket 類表示客戶端和伺服器用於相互通訊的套接字。客戶端透過例項化一個 Socket 物件來獲得它,而伺服器則從 accept() 方法的返回值中獲得一個 Socket 物件。

Socket 類有五個建構函式,客戶端可以使用它們來連線到伺服器:

序號 方法和描述
1

public Socket(String host, int port) throws UnknownHostException, IOException.

此方法嘗試連線到指定埠上的指定伺服器。如果此建構函式沒有丟擲異常,則連線成功,並且客戶端已連線到伺服器。

2

public Socket(InetAddress host, int port) throws IOException

此方法與之前的建構函式相同,只是主機由 InetAddress 物件表示。

3

public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException.

連線到指定的主機和埠,在本地主機上指定地址和埠建立套接字。

4

public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException.

此方法與之前的建構函式相同,只是主機由 InetAddress 物件而不是 String 表示。

5

public Socket()

建立一個未連線的套接字。使用 connect() 方法將此套接字連線到伺服器。

當 Socket 建構函式返回時,它不僅會例項化一個 Socket 物件,還會嘗試連線到指定的伺服器和埠。

這裡列出了一些 Socket 類中令人感興趣的方法。請注意,客戶端和伺服器都具有 Socket 物件,因此客戶端和伺服器都可以呼叫這些方法。

序號 方法和描述
1

public void connect(SocketAddress host, int timeout) throws IOException

此方法將套接字連線到指定的主機。僅當您使用無引數建構函式例項化 Socket 時,才需要此方法。

2

public InetAddress getInetAddress()

此方法返回此套接字連線到的另一臺計算機的地址。

3

public int getPort()

返回套接字在遠端計算機上繫結的埠。

4

public int getLocalPort()

返回套接字在本地計算機上繫結的埠。

5

public SocketAddress getRemoteSocketAddress()

返回遠端套接字的地址。

6

public InputStream getInputStream() throws IOException

返回套接字的輸入流。輸入流連線到遠端套接字的輸出流。

7

public OutputStream getOutputStream() throws IOException

返回套接字的輸出流。輸出流連線到遠端套接字的輸入流。

8

public void close() throws IOException

關閉套接字,這使得此 Socket 物件不再能夠再次連線到任何伺服器。

InetAddress 類方法

此類表示網際網路協議 (IP) 地址。以下是一些在進行套接字程式設計時可能需要用到的方法:

序號 方法和描述
1

static InetAddress getByAddress(byte[] addr)

給定原始 IP 地址,返回一個 InetAddress 物件。

2

static InetAddress getByAddress(String host, byte[] addr)

基於提供的主機名和 IP 地址建立一個 InetAddress。

3

static InetAddress getByName(String host)

根據主機名確定主機的 IP 地址。

4

String getHostAddress()

以文字形式返回 IP 地址字串。

5

String getHostName()

獲取此 IP 地址的主機名。

6

static InetAddress InetAddress getLocalHost()

返回本地主機。

7

String toString()

將此 IP 地址轉換為字串。

套接字客戶端示例

下面的 GreetingClient 是一個客戶端程式,它使用套接字連線到伺服器,傳送問候語,然後等待響應。

示例

// File Name GreetingClient.java
import java.net.*;
import java.io.*;

public class GreetingClient {

   public static void main(String [] args) {
      String serverName = args[0];
      int port = Integer.parseInt(args[1]);
      try {
         System.out.println("Connecting to " + serverName + " on port " + port);
         Socket client = new Socket(serverName, port);
         
         System.out.println("Just connected to " + client.getRemoteSocketAddress());
         OutputStream outToServer = client.getOutputStream();
         DataOutputStream out = new DataOutputStream(outToServer);
         
         out.writeUTF("Hello from " + client.getLocalSocketAddress());
         InputStream inFromServer = client.getInputStream();
         DataInputStream in = new DataInputStream(inFromServer);
         
         System.out.println("Server says " + in.readUTF());
         client.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

套接字伺服器示例

下面的 GreetingServer 程式是一個伺服器應用程式示例,它使用 Socket 類在命令列引數指定的埠號上監聽客戶端:

示例

// File Name GreetingServer.java
import java.net.*;
import java.io.*;

public class GreetingServer extends Thread {
   private ServerSocket serverSocket;
   
   public GreetingServer(int port) throws IOException {
      serverSocket = new ServerSocket(port);
      serverSocket.setSoTimeout(10000);
   }

   public void run() {
      while(true) {
         try {
            System.out.println("Waiting for client on port " + 
               serverSocket.getLocalPort() + "...");
            Socket server = serverSocket.accept();
            
            System.out.println("Just connected to " + server.getRemoteSocketAddress());
            DataInputStream in = new DataInputStream(server.getInputStream());
            
            System.out.println(in.readUTF());
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress()
               + "\nGoodbye!");
            server.close();
            
         } catch (SocketTimeoutException s) {
            System.out.println("Socket timed out!");
            break;
         } catch (IOException e) {
            e.printStackTrace();
            break;
         }
      }
   }
   
   public static void main(String [] args) {
      int port = Integer.parseInt(args[0]);
      try {
         Thread t = new GreetingServer(port);
         t.start();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

編譯客戶端和伺服器,然後啟動伺服器,如下所示:

$ java GreetingServer 6066
Waiting for client on port 6066...

檢查客戶端程式,如下所示:

輸出

$ java GreetingClient localhost 6066
Connecting to localhost on port 6066
Just connected to localhost/127.0.0.1:6066
Server says Thank you for connecting to /127.0.0.1:6066
Goodbye!

Java - URL 處理

URL 代表統一資源定位符 (Uniform Resource Locator),它表示全球資訊網上的資源,例如網頁或 FTP 目錄。

本節將向您展示如何編寫與 URL 通訊的 Java 程式。URL 可以分解成以下幾個部分:

protocol://host:port/path?query#ref

協議示例包括 HTTP、HTTPS、FTP 和 File。路徑也稱為檔名,主機也稱為授權。

以下是協議為 HTTP 的網頁的 URL:

https://www.amrood.com/index.htm?language=en#j2se

請注意,此 URL 未指定埠,在這種情況下,將使用協議的預設埠。對於 HTTP,預設埠為 80。

建構函式

java.net.URL 類表示一個 URL,並有一套完整的用於在 Java 中操作 URL 的方法。

URL 類有幾個用於建立 URL 的建構函式,包括以下這些:

序號 建構函式和描述
1

public URL(String protocol, String host, int port, String file) throws MalformedURLException

透過組合給定的部分建立一個URL。

2

public URL(String protocol, String host, String file) throws MalformedURLException

與之前的建構函式相同,只是使用了給定協議的預設埠。

3

public URL(String url) throws MalformedURLException

根據給定的字串建立一個URL。

4

public URL(URL context, String url) throws MalformedURLException

透過解析URL和字串引數來建立一個URL。

URL類包含許多用於訪問URL各個部分的方法。URL類中的一些方法包括:

序號 方法和描述
1

public String getPath()

返回URL的路徑。

2

public String getQuery()

返回URL的查詢部分。

3

public String getAuthority()

返回URL的授權部分。

4

public int getPort()

返回URL的埠。

5

public int getDefaultPort()

返回URL協議的預設埠。

6

public String getProtocol()

返回URL的協議。

7

public String getHost()

返回URL的主機。

8

public String getHost()

返回URL的主機。

9

public String getFile()

返回URL的檔名。

10

public String getRef()

返回URL的引用部分。

11

public URLConnection openConnection() throws IOException

開啟到URL的連線,允許客戶端與資源通訊。

示例

下面的URLDemo程式演示了URL的各個部分。在命令列中輸入一個URL,URLDemo程式將輸出給定URL的每個部分。

// File Name : URLDemo.java
import java.io.IOException;
import java.net.URL;

public class URLDemo {

   public static void main(String [] args) {
      try {
         URL url = new URL("https://tutorialspoint.tw/index.htm?language=en#j2se");
         
         System.out.println("URL is " + url.toString());
         System.out.println("protocol is " + url.getProtocol());
         System.out.println("authority is " + url.getAuthority());
         System.out.println("file name is " + url.getFile());
         System.out.println("host is " + url.getHost());
         System.out.println("path is " + url.getPath());
         System.out.println("port is " + url.getPort());
         System.out.println("default port is " + url.getDefaultPort());
         System.out.println("query is " + url.getQuery());
         System.out.println("ref is " + url.getRef());
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

該程式的示例執行將產生以下結果:

輸出

URL is https://tutorialspoint.tw/index.htm?language=en#j2se
protocol is https
authority is www.tutorialspoint.com
file name is /index.htm?language=en
host is www.tutorialspoint.com
path is /index.htm
port is -1
default port is 443
query is language=en
ref is j2se

URLConnection類的方法

openConnection()方法返回一個**java.net.URLConnection**,這是一個抽象類,其子類代表各種型別的URL連線。

例如:

  • 如果連線到協議為HTTP的URL,則openConnection()方法返回一個HttpURLConnection物件。

  • 如果連線到表示JAR檔案的URL,則openConnection()方法返回一個JarURLConnection物件,等等。

URLConnection類有很多方法可以設定或確定有關連線的資訊,包括:

序號 方法和描述
1

Object getContent()

檢索此URL連線的內容。

2

Object getContent(Class[] classes)

檢索此URL連線的內容。

3

String getContentEncoding()

返回content-encoding頭欄位的值。

4

int getContentLength()

返回content-length頭欄位的值。

5

String getContentType()

返回content-type頭欄位的值。

6

int getLastModified()

返回last-modified頭欄位的值。

7

long getExpiration()

返回expired頭欄位的值。

8

long getIfModifiedSince()

返回此物件的ifModifiedSince欄位的值。

9

public void setDoInput(boolean input)

傳入true表示連線將用於輸入。預設值為true,因為客戶端通常從URLConnection讀取。

10

public void setDoOutput(boolean output)

傳入true表示連線將用於輸出。預設值為false,因為許多型別的URL不支援寫入。

11

public InputStream getInputStream() throws IOException

返回用於從資源讀取的URL連線的輸入流。

12

public OutputStream getOutputStream() throws IOException

返回用於寫入資源的URL連線的輸出流。

13

public URL getURL()

返回此URLConnection物件連線到的URL。

示例

下面的URLConnectionDemo程式連線到從命令列輸入的URL。

如果URL表示HTTP資源,則連線被轉換為HttpURLConnection,並且資源中的資料一次讀取一行。

// File Name : URLConnDemo.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

   public static void main(String [] args) {
      try {
         URL url = new URL("https://tutorialspoint.tw");
         URLConnection urlConnection = url.openConnection();
         HttpURLConnection connection = null;
         if(urlConnection instanceof HttpURLConnection) {
            connection = (HttpURLConnection) urlConnection;
         }else {
            System.out.println("Please enter an HTTP URL.");
            return;
         }
         
         BufferedReader in = new BufferedReader(
            new InputStreamReader(connection.getInputStream()));
         String urlString = "";
         String current;
         
         while((current = in.readLine()) != null) {
            urlString += current;
         }
         System.out.println(urlString);
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

該程式的示例執行將產生以下結果:

輸出

$ java URLConnDemo

.....a complete HTML content of home page of tutorialspoint.com.....

Java - 泛型

如果我們可以編寫一個單一的排序方法,可以對Integer陣列、String陣列或任何支援排序型別的陣列中的元素進行排序,那就太好了。

Java的**泛型**方法和泛型類使程式設計師能夠用單個方法宣告指定一組相關方法,或者用單個類宣告指定一組相關型別。

泛型還提供編譯時型別安全,允許程式設計師在編譯時捕獲無效型別。

使用Java泛型概念,我們可以編寫一個用於排序物件陣列的泛型方法,然後使用Integer陣列、Double陣列、String陣列等呼叫泛型方法來排序陣列元素。

泛型方法

您可以編寫單個泛型方法宣告,可以使用不同型別的引數呼叫該方法。根據傳遞給泛型方法的引數型別,編譯器將適當地處理每個方法呼叫。以下是定義泛型方法的規則:

  • 所有泛型方法宣告都有一個型別引數部分,該部分由尖括號(<和>)分隔,位於方法的返回型別之前(在下面的示例中為< E >)。

  • 每個型別引數部分包含一個或多個用逗號分隔的型別引數。型別引數,也稱為型別變數,是一個識別符號,它指定一個泛型型別名稱。

  • 型別引數可以用來宣告返回型別,並作為傳遞給泛型方法的引數型別的佔位符,這些引數稱為實際型別引數。

  • 泛型方法的主體與任何其他方法的宣告方式相同。請注意,型別引數只能表示引用型別,不能表示原始型別(如int、double和char)。

示例

以下示例說明如何使用單個泛型方法列印不同型別的陣列:

public class GenericMethodTest {
   // generic method printArray
   public static < E > void printArray( E[] inputArray ) {
      // Display array elements
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
      // Create arrays of Integer, Double and Character
      Integer[] intArray = { 1, 2, 3, 4, 5 };
      Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
      Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

      System.out.println("Array integerArray contains:");
      printArray(intArray);   // pass an Integer array

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);   // pass a Double array

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);   // pass a Character array
   }
}

輸出

Array integerArray contains:
1 2 3 4 5 

Array doubleArray contains:
1.1 2.2 3.3 4.4 

Array characterArray contains:
H E L L O

有界型別引數

有時您可能希望限制允許傳遞給型別引數的型別的種類。例如,對數字進行操作的方法可能只想接受Number或其子類的例項。這就是有界型別引數的目的。

要宣告有界型別引數,請列出型別引數的名稱,後跟extends關鍵字,然後是其上限。

示例

以下示例說明extends如何以一般意義上表示“extends”(如類)或“implements”(如介面)。此示例是一個泛型方法,用於返回三個Comparable物件的最大的一個:

public class MaximumTest {
   // determines the largest of three Comparable objects
   
   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;   // assume x is initially the largest
      
      if(y.compareTo(max) > 0) {
         max = y;   // y is the largest so far
      }
      
      if(z.compareTo(max) > 0) {
         max = z;   // z is the largest now                 
      }
      return max;   // returns the largest object   
   }
   
   public static void main(String args[]) {
      System.out.printf("Max of %d, %d and %d is %d\n\n", 
         3, 4, 5, maximum( 3, 4, 5 ));

      System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n",
         6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ));

      System.out.printf("Max of %s, %s and %s is %s\n","pear",
         "apple", "orange", maximum("pear", "apple", "orange"));
   }
}

輸出

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Max of pear, apple and orange is pear

泛型類

泛型類宣告看起來像非泛型類宣告,只是類名後面跟著一個型別引數部分。

與泛型方法一樣,泛型類的型別引數部分可以包含一個或多個用逗號分隔的型別引數。這些類被稱為引數化類或引數化型別,因為它們接受一個或多個引數。

示例

以下示例說明如何定義泛型類:

public class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }

   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();
    
      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

輸出

Integer Value :10
String Value :Hello World

Java - 集合框架

在Java 2之前,Java提供了一些臨時類,如**Dictionary、Vector、Stack**和**Properties**來儲存和操作物件組。儘管這些類非常有用,但它們缺乏一個核心統一的主題。因此,使用Vector的方式與使用Properties的方式不同。

集合框架的設計目標包括:

  • 框架必須是高效能的。基本集合(動態陣列、連結串列、樹和雜湊表)的實現必須非常高效。

  • 框架必須允許不同型別的集合以類似的方式工作,並具有高度的互操作性。

  • 框架必須能夠輕鬆地擴充套件和/或適配集合。

為此,整個集合框架都是圍繞著一組標準介面設計的。提供了幾個這些介面的標準實現,例如**LinkedList、HashSet**和**TreeSet**,您可以按原樣使用,也可以根據需要實現您自己的集合。

集合框架是表示和操作集合的統一架構。所有集合框架都包含以下內容:

  • **介面** - 這些是表示集合的抽象資料型別。介面允許獨立於其表示細節來操作集合。在面向物件的語言中,介面通常形成層次結構。

  • **實現,即類** - 這些是集合介面的具體實現。從本質上說,它們是可重用的資料結構。

  • **演算法** - 這些是在實現集合介面的物件上執行有用計算(如搜尋和排序)的方法。演算法被稱為多型的:也就是說,相同的方法可以用於許多不同實現的相應集合介面。

除了集合之外,框架還定義了幾個對映介面和類。對映儲存鍵/值對。雖然對映並非集合(按術語的正確用法),但它們與集合完全整合。

集合介面

集合框架定義了幾個介面。本節概述每個介面:

序號 介面和描述
1 Collection介面

這使您可以使用物件組;它位於集合層次結構的頂部。

2 List介面

這擴充套件了**Collection**,List的例項儲存有序的元素集合。

3 Set

這擴充套件了Collection來處理集合,集合必須包含唯一元素。

4 SortedSet

這擴充套件了Set來處理排序的集合。

5 Map

這將唯一鍵對映到值。

6 Map.Entry

這描述了對映中的一個元素(鍵/值對)。這是Map的內部類。

7 SortedMap

這擴充套件了Map,以便鍵以升序維護。

8 Enumeration

這個遺留介面定義了您可以列舉(一次獲取一個)物件集合中的元素的方法。這個遺留介面已被Iterator取代。

集合類

Java提供了一組標準集合類來實現集合介面。一些類提供完整的實現,可以按原樣使用,而另一些類是抽象類,提供骨架實現,用作建立具體集合的起點。

標準集合類在以下表格中總結:

序號 類和描述
1

AbstractCollection

實現了Collection介面的大部分內容。

2

AbstractList

擴充套件AbstractCollection並實現了List介面的大部分內容。

3

AbstractSequentialList

擴充套件AbstractList供使用集合,該集合使用其元素的順序訪問而不是隨機訪問。

4

AbstractSet

擴充套件AbstractCollection並實現了Set介面的大部分內容。

5

AbstractMap

實現了Map介面的大部分內容。

AbstractCollection、AbstractSet、AbstractList、AbstractSequentialListAbstractMap類提供了核心集合介面的骨架實現,以最大限度地減少實現它們的所需工作量。

在上一章中討論了java.util定義的以下遺留類:

序號 類和描述
1 Queue

佇列介面在java.util包中提供,它實現了Collection介面。佇列實現FIFO,即先進先出。這意味著先輸入的元素是先刪除的元素。

集合演算法

集合框架定義了幾個可以應用於集合和對映的演算法。這些演算法在Collections類中定義為靜態方法。

一些方法可能會丟擲ClassCastException異常,該異常發生在嘗試比較不相容的型別時;或者丟擲UnsupportedOperationException異常,該異常發生在嘗試修改不可修改的集合時。

集合定義了三個靜態變數:EMPTY_SET、EMPTY_LIST和EMPTY_MAP。所有這些都是不可變的。

序號 演算法與描述
1 集合演算法

以下是所有演算法實現的列表。

如何使用迭代器?

通常,您可能希望遍歷集合中的元素。例如,您可能想要顯示每個元素。

最簡單的方法是使用迭代器,迭代器是一個實現Iterator或ListIterator介面的物件。

迭代器使您可以遍歷集合,獲取或刪除元素。ListIterator擴充套件了Iterator,允許雙向遍歷列表和修改元素。

序號 迭代器方法與描述
1 使用Java迭代器

以下是Iterator和ListIterator介面提供的所有方法及其示例的列表。

如何使用比較器?

TreeSet和TreeMap都按排序順序儲存元素。但是,正是比較器定義了“排序順序”的確切含義。

此介面允許我們以任意多種不同的方式對給定集合進行排序。此介面還可以用於對任何類的任何例項進行排序(甚至是我們無法修改的類)。

序號 迭代器方法與描述
1 使用Java比較器

以下是Comparator介面提供的所有方法及其示例的列表。

如何使用可比較物件?

TreeSet和TreeMap都按排序順序儲存元素。我們可以使用Comparable介面來精確定義“排序順序”的含義。

此介面允許我們以任意多種不同的方式對給定集合進行排序。此介面還可以用於對任何類的任何例項進行排序(甚至是我們無法修改的類)。

序號 迭代器方法與描述
1 使用Java Comparable

以下是Comparable介面提供的所有方法及其示例的列表。

總結

Java集合框架使程式設計師可以訪問預打包的資料結構以及用於操作它們的方法。

集合是可以儲存對其他物件的引用的物件。集合介面宣告可以對每種型別的集合執行的操作。

集合框架的類和介面位於java.util包中。

Java - List介面

List介面擴充套件了Collection,並聲明瞭儲存元素序列的集合的行為。

  • 可以使用基於零的索引按其在列表中的位置插入或訪問元素。

  • 列表可以包含重複元素。

  • 除了Collection定義的方法外,List還定義了一些它自己的方法,這些方法在下面的表中進行了總結。

  • 如果集合無法修改,則列表的幾個方法將丟擲UnsupportedOperationException異常;當一個物件與另一個物件不相容時,將生成ClassCastException異常。

序號 方法和描述
1

void add(int index, Object obj)

將obj插入到呼叫列表中index指定的位置。插入點及其後的任何現有元素都將向上移動。因此,不會覆蓋任何元素。

2

boolean addAll(int index, Collection c)

c的所有元素插入到呼叫列表中index指定的位置。插入點及其後的任何現有元素都將向上移動。因此,不會覆蓋任何元素。如果呼叫列表發生更改,則返回true;否則返回false。

3

Object get(int index)

返回儲存在呼叫集合中指定索引處的物件。

4

int indexOf(Object obj)

返回obj在呼叫列表中第一次出現的索引。如果obj不是列表的元素,則返回-1。

5

int lastIndexOf(Object obj)

返回obj在呼叫列表中最後一次出現的索引。如果obj不是列表的元素,則返回-1。

6

ListIterator listIterator( )

返回指向呼叫列表開頭的迭代器。

7

ListIterator listIterator(int index)

返回指向呼叫列表的迭代器,該迭代器從指定的索引開始。

8

Object remove(int index)

從呼叫列表中刪除index位置的元素並返回已刪除的元素。生成的列表將被壓縮。也就是說,後續元素的索引將遞減1。

9

Object set(int index, Object obj)

將obj賦值給呼叫列表中index指定的位置。

10

List subList(int start, int end)

返回一個列表,其中包含呼叫列表中從start到end-1的元素。返回列表中的元素也由呼叫物件引用。

示例 1

以上介面已使用ArrayList實現。以下示例解釋了上述集合方法的各種類實現中的幾種方法:

import java.util.ArrayList;
import java.util.List;
public class CollectionsDemo {

   public static void main(String[] args) {
      List<String> a1 = new ArrayList<>();
      a1.add("Zara");
      a1.add("Mahnaz");
      a1.add("Ayan");      
      System.out.println(" ArrayList Elements");
      System.out.print("\t" + a1);
   }
}

輸出

 ArrayList Elements
   [Zara, Mahnaz, Ayan]

示例 2

以上介面已使用LinkedList實現。以下示例解釋了上述集合方法的各種類實現中的幾種方法:

import java.util.LinkedList;
import java.util.List;
public class CollectionsDemo {

   public static void main(String[] args) {
      List<String> a1 = new LinkedList<>();
      a1.add("Zara");
      a1.add("Mahnaz");
      a1.add("Ayan");      
      System.out.println(" LinkedList Elements");
      System.out.print("\t" + a1);
   }
}

輸出

 LinkedList Elements
   [Zara, Mahnaz, Ayan]

示例 3

以上介面已使用ArrayList實現。以下另一個示例解釋了上述集合方法的各種類實現中的幾種方法:

import java.util.ArrayList;
import java.util.List;
public class CollectionsDemo {

   public static void main(String[] args) {
      List<String> a1 = new ArrayList<>();
      a1.add("Zara");
      a1.add("Mahnaz");
      a1.add("Ayan");      
      System.out.println(" ArrayList Elements");
      System.out.print("\t" + a1);
      
      // remove second element
      a1.remove(1);
      
      System.out.println("\n ArrayList Elements");
      System.out.print("\t" + a1);
   }
}

輸出

 ArrayList Elements
   [Zara, Mahnaz, Ayan]
 ArrayList Elements
   [Zara, Ayan]

Java - Queue 介面

Queue介面位於java.util包中,它實現了Collection介面。佇列實現FIFO,即先進先出。這意味著先輸入的元素是先刪除的元素。佇列通常用於在處理元素之前儲存元素。一旦處理完一個元素,它就會從佇列中刪除,然後選擇下一個專案進行處理。

宣告

public interface Queue<E>
   extends Collection<E>

佇列方法

以下是Queue介面的所有實現類都實現的重要佇列方法列表:

序號 方法和描述
1 boolean add(E e)

如果可以在不違反容量限制的情況下立即將指定的元素插入到此佇列中,則此方法插入指定的元素,成功時返回true,如果沒有空間可用則丟擲IllegalStateException異常。

2 E element()

此方法檢索但不刪除此佇列的頭部。

3 boolean offer(E e)

此方法如果可以在不違反容量限制的情況下立即將指定的元素插入到此佇列中,則插入指定的元素。

4 E peek()

此方法檢索但不刪除此佇列的頭部,或者如果此佇列為空,則返回null。

5 E poll()

此方法檢索並刪除此佇列的頭部,或者如果此佇列為空,則返回null。

6 E remove()

此方法檢索並刪除此佇列的頭部。

繼承的方法

此介面繼承自以下介面的方法:

  • java.util.Collection
  • java.lang.Iterable

示例

在此示例中,我們使用LinkedList例項來顯示佇列的add、peek和size操作。

package com.tutorialspoint;

import java.util.LinkedList;
import java.util.Queue;

public class QueueDemo {
   public static void main(String[] args) {
      Queue<Integer> q = new LinkedList<>();
      q.add(6);
      q.add(1);
      q.add(8);
      q.add(4);
      q.add(7);
      System.out.println("The queue is: " + q);
      int num1 = q.remove();
      System.out.println("The element deleted from the head is: " + num1);
      System.out.println("The queue after deletion is: " + q);
      int head = q.peek();
      System.out.println("The head of the queue is: " + head);
      int size = q.size();
      System.out.println("The size of the queue is: " + size);
   }
}

輸出

The queue is: [6, 1, 8, 4, 7]
The element deleted from the head is: 6
The queue after deletion is: [1, 8, 4, 7]
The head of the queue is: 1
The size of the queue is: 4

Java - Map介面

Map介面將唯一的鍵對映到值。鍵是您以後用來檢索值的物件。

  • 給定一個鍵和一個值,您可以將值儲存在Map物件中。儲存值後,您可以使用其鍵檢索它。

  • 當呼叫Map中不存在專案時,一些方法會丟擲NoSuchElementException異常。

  • 當物件與Map中的元素不相容時,將丟擲ClassCastException異常。

  • 如果嘗試使用空物件並且Map不允許空值,則將丟擲NullPointerException異常。

  • 如果嘗試更改不可修改的Map,則將丟擲UnsupportedOperationException異常。

序號 方法和描述
1

void clear( )

從呼叫Map中刪除所有鍵值對。

2

boolean containsKey(Object k)

如果呼叫Map包含k作為鍵,則返回true。否則,返回false。

3

boolean containsValue(Object v)

如果Map包含v作為值,則返回true。否則,返回false。

4

Set entrySet( )

返回一個包含Map中條目的Set。該Set包含Map.Entry型別的物件。此方法提供呼叫Map的Set檢視。

5

boolean equals(Object obj)

如果obj是一個Map並且包含相同的條目,則返回true。否則,返回false。

6

Object get(Object k)

返回與鍵k關聯的值。

7

int hashCode( )

返回呼叫Map的雜湊碼。

8

boolean isEmpty( )

如果呼叫Map為空,則返回true。否則,返回false。

9

Set keySet( )

返回一個包含呼叫Map中鍵的Set。此方法提供呼叫Map中鍵的Set檢視。

10

Object put(Object k, Object v)

將一個條目放入呼叫Map中,覆蓋與該鍵關聯的任何先前值。鍵和值分別為k和v。如果鍵不存在,則返回null。否則,返回與該鍵連結的先前值。

11

void putAll(Map m)

m中的所有條目放入此Map中。

12

Object remove(Object k)

刪除鍵等於k的條目。

13

int size( )

返回Map中鍵值對的數量。

14

Collection values( )

返回一個包含Map中值的集合。此方法提供Map中值的集合檢視。

示例 1

Map在各種類中都有實現,例如HashMap。以下是一個示例,用於解釋Map的功能:

import java.util.HashMap;
import java.util.Map;
public class CollectionsDemo {

   public static void main(String[] args) {
      Map<String, String> m1 = new HashMap<>(); 
      m1.put("Zara", "8");
      m1.put("Mahnaz", "31");
      m1.put("Ayan", "12");
      m1.put("Daisy", "14");

      System.out.println();
      System.out.println(" Map Elements");
      System.out.print("\t" + m1);
   }
}

輸出

Map Elements
	{Daisy = 14, Ayan = 12, Zara = 8, Mahnaz = 31}

示例 2

Map在各種類中都有實現,例如TreeMap,它根據鍵對條目進行排序。以下是一個示例,使用TreeMap解釋Map的功能:

import java.util.Map;
import java.util.TreeMap;

public class CollectionsDemo {

   public static void main(String[] args) {
      Map<String, String> m1 = new TreeMap<>(); 
      m1.put("Zara", "8");
      m1.put("Mahnaz", "31");
      m1.put("Ayan", "12");
      m1.put("Daisy", "14");

      System.out.println();
      System.out.println(" Map Elements");
      System.out.print("\t" + m1);
   }
}

輸出

 Map Elements
	{Ayan=12, Daisy=14, Mahnaz=31, Zara=8}

示例 3

Map在各種類中都有實現,例如HashMap。以下是一個示例,使用HashMap解釋Map函式,以向Map新增和刪除元素:

import java.util.HashMap;
import java.util.Map;
public class CollectionsDemo {

   public static void main(String[] args) {
      Map<String, String> m1 = new HashMap<>(); 
      m1.put("Zara", "8");
      m1.put("Mahnaz", "31");
      m1.put("Ayan", "12");
      m1.put("Daisy", "14");

      System.out.println();
      System.out.println(" Map Elements");
      System.out.print("\t" + m1);
	  
      m1.remove("Daisy");
	  System.out.println(" Map Elements");
      System.out.print("\t" + m1);
   }
}

輸出

 Map Elements
	{Daisy=14, Ayan=12, Zara=8, Mahnaz=31} Map Elements
	{Ayan=12, Zara=8, Mahnaz=31}

Java - SortedMap介面

SortedMap介面擴充套件了Map。它確保條目按升序鍵順序維護。

當呼叫Map中沒有專案時,一些方法會丟擲NoSuchElementException異常。當物件與Map中的元素不相容時,將丟擲ClassCastException異常。如果嘗試使用空物件而Map不允許空值,則將丟擲NullPointerException異常。

SortedMap宣告的方法在下面的表中進行了總結:

序號 方法和描述
1

Comparator comparator( )

返回呼叫SortedMap的比較器。如果對呼叫Map使用自然排序,則返回null。

2

Object firstKey( )

返回呼叫Map中的第一個鍵。

3

SortedMap headMap(Object end)

返回一個SortedMap,用於那些鍵小於end的Map條目。

4

Object lastKey( )

返回呼叫Map中的最後一個鍵。

5

SortedMap subMap(Object start, Object end)

返回一個對映,其中包含鍵大於或等於start且小於end的條目。

6

SortedMap tailMap(Object start)

返回一個對映,其中包含鍵大於或等於start的條目。

示例 1

以下是一個示例,展示如何使用TreeMap獲取SortedMap的值:

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

public class MapDemo {

   public static void main(String args[]) {
      // Create a hash map
      SortedMap<String, Double> map = new TreeMap<>();

      // Put elements to the map
      map.put("Zara", Double.valueOf(3434.34));
      map.put("Mahnaz", Double.valueOf(123.22));
      map.put("Ayan", Double.valueOf(1378.00));
      map.put("Daisy", Double.valueOf(99.22));
      map.put("Qadir", Double.valueOf(-19.08));
      
      // Get a set of the entries
      Set<Map.Entry<String, Double>> set = map.entrySet();
      
      // Get an iterator
      Iterator<Map.Entry<String, Double>> i = set.iterator();
     
      // Display elements 
      while(i.hasNext()) {
         Map.Entry<String, Double> me = i.next();
         System.out.print(me.getKey() + ": ");
         System.out.println(me.getValue());
      }
   }
}

輸出

Daisy: 99.22
Ayan: 1378.0
Zara: 3434.34
Qadir: -19.08
Mahnaz: 123.22

示例 2

以下是一個示例,展示如何使用TreeMap設定SortedMap的值:

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

public class MapDemo {

   public static void main(String args[]) {
      // Create a hash map
      SortedMap<String, Double> map = new TreeMap<>();

      // Put elements to the map
      map.put("Zara", Double.valueOf(3434.34));
      map.put("Mahnaz", Double.valueOf(123.22));
      map.put("Ayan", Double.valueOf(1378.00));
      map.put("Daisy", Double.valueOf(99.22));
      map.put("Qadir", Double.valueOf(-19.08));
      
      // Get a set of the entries
      Set<Map.Entry<String, Double>> set = map.entrySet();
      
      // Get an iterator
      Iterator<Map.Entry<String, Double>> i = set.iterator();
     
      // Display elements 
      while(i.hasNext()) {
         Map.Entry<String, Double> me = i.next();
         me.setValue(me.getValue() * 10);
         System.out.print(me.getKey() + ": ");
         System.out.println(me.getValue());
      }
   }
}

輸出

Daisy: 992.2
Ayan: 13780.0
Zara: 34343.4
Qadir: -190.79999999999998
Mahnaz: 1232.2

示例 3

以下是一個示例,展示如何使用TreeMap獲取SortedMap條目的鍵:

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

public class MapDemo {

   public static void main(String args[]) {
      // Create a hash map
      SortedMap<String, Double> map = new TreeMap<>();

      // Put elements to the map
      map.put("Zara", Double.valueOf(3434.34));
      map.put("Mahnaz", Double.valueOf(123.22));
      map.put("Ayan", Double.valueOf(1378.00));
      map.put("Daisy", Double.valueOf(99.22));
      map.put("Qadir", Double.valueOf(-19.08));
      
      // Get a set of the entries
      Set<Map.Entry<String, Double>> set = map.entrySet();
      
      // Get an iterator
      Iterator<Map.Entry<String, Double>> i = set.iterator();
     
      // Display elements 
      while(i.hasNext()) {
         Map.Entry<String, Double> me = i.next();
         System.out.println(me.getKey());
      }
   }
}

輸出

Daisy
Ayan
Zara
Qadir
Mahnaz

Java - Set介面

Set是一個不能包含重複元素的集合。它模擬了數學集合的抽象。

Set介面只包含從Collection繼承的方法,並增加了不允許重複元素的限制。

Set還對equals和hashCode操作的行為增加了更強的約定,允許即使Set例項的實現型別不同也能進行有意義的比較。

Set宣告的方法總結在下表中:

序號 方法和描述
1

add( )

將物件新增到集合中。

2

clear( )

刪除集合中的所有物件。

3

contains( )

如果指定的物件是集合中的元素,則返回true。

4

isEmpty( )

如果集合沒有元素,則返回true。

5

iterator( )

返回集合的Iterator物件,可用於檢索物件。

6

remove( )

從集合中刪除指定的物件。

7

size( )

返回集合中元素的數量。

示例 1

Set在HashSet、TreeSet、LinkedHashSet等各種類中都有實現。以下是一個使用HashSet解釋Set功能的示例:

import java.util.HashSet;
import java.util.Set;

public class SetDemo {

  public static void main(String args[]) { 
      int count[] = {34, 22,10,60,30,22};
      Set<Integer> set = new HashSet<>();
      try {
         for(int i = 0; i < 5; i++) {
            set.add(count[i]);
         }
         System.out.println(set);
      }
      catch(Exception e) {}
   }
} 

輸出

[34, 22, 10, 60, 30]
The sorted list is:
[10, 22, 30, 34, 60]
The First element of the set is: 10
The last element of the set is: 60

示例 2

Set在HashSet、TreeSet、LinkedHashSet等各種類中都有實現。以下是一個使用TreeSet解釋Set功能的示例:

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetDemo {

  public static void main(String args[]) { 
      int count[] = {34, 22,10,60,30,22};
      Set<Integer> set = new HashSet<>();
      try {
         for(int i = 0; i < 5; i++) {
            set.add(count[i]);
         }
         System.out.println(set);

         TreeSet<Integer> sortedSet = new TreeSet<>(set);
         System.out.println("The sorted list is:");
         System.out.println(sortedSet);

         System.out.println("The First element of the set is: "+ (Integer)sortedSet.first());
         System.out.println("The last element of the set is: "+ (Integer)sortedSet.last());
      }
      catch(Exception e) {}
   }
} 

輸出

[34, 22, 10, 60, 30]
The sorted list is:
[10, 22, 30, 34, 60]
The First element of the set is: 10
The last element of the set is: 60

示例 3

Set在HashSet、TreeSet、LinkedHashSet等各種類中都有實現。以下是一個使用TreeSet操作解釋Set功能的示例:

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class SetDemo {

  public static void main(String args[]) { 
      int count[] = {34, 22,10,60,30,22};
      Set<Integer> set = new HashSet<>();
      try {
         for(int i = 0; i < 5; i++) {
            set.add(count[i]);
         }
         System.out.println(set);

         TreeSet<Integer> sortedSet = new TreeSet<>(set);
         System.out.println("The sorted list is:");
         System.out.println(sortedSet);

         sortedSet.clear();
         System.out.println("The sorted list is:");
         System.out.println(sortedSet);
      }
      catch(Exception e) {}
   }
} 

輸出

[34, 22, 10, 60, 30]
The sorted list is:
[10, 22, 30, 34, 60]
The sorted list is:
[]

Java - SortedSet介面

SortedSet介面擴充套件了Set,並聲明瞭按升序排序的集合的行為。除了Set定義的方法外,SortedSet介面還聲明瞭下表中總結的方法:

當呼叫集合中不包含任何專案時,幾個方法會丟擲NoSuchElementException異常。當物件與集合中的元素不相容時,會丟擲ClassCastException異常。

如果嘗試使用空物件並且集合中不允許為空,則會丟擲NullPointerException異常。

序號 方法和描述
1

Comparator comparator( )

返回呼叫SortedSet的比較器。如果對該集合使用自然排序,則返回null。

2

Object first( )

返回呼叫SortedSet中的第一個元素。

3

SortedSet headSet(Object end)

返回一個SortedSet,其中包含呼叫SortedSet中小於end的元素。返回的SortedSet中的元素也由呼叫SortedSet引用。

4

Object last( )

返回呼叫SortedSet中的最後一個元素。

5

SortedSet subSet(Object start, Object end)

返回一個SortedSet,其中包含start和end之間的元素。1. 返回的集合中的元素也由呼叫物件引用。

6

SortedSet tailSet(Object start)

返回一個SortedSet,其中包含SortedSet中大於或等於start的元素。返回的集合中的元素也由呼叫物件引用。

示例 1

SortedSet在TreeSet等各種類中都有實現。以下是一個帶有add操作的TreeSet類的示例:

import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

public class SortedSetDemo {

   public static void main(String[] args) {
      // Create the sorted set
      SortedSet<String> set = new TreeSet<>();  

      // Add elements to the set
      set.add("b");
      set.add("c");
      set.add("a");

      // Iterating over the elements in the set
      Iterator it = set.iterator();

      while (it.hasNext()) {
         
         // Get element
         Object element = it.next();
         System.out.println(element.toString());
      }
   }
}

輸出

a
b
c

示例 2

SortedSet在TreeSet等各種類中都有實現。以下是一個帶有add和remove操作的TreeSet類的示例:

import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

public class SortedSetDemo {

   public static void main(String[] args) {
      // Create the sorted set
      SortedSet<String> set = new TreeSet<>();  

      // Add elements to the set
      set.add("b");
      set.add("c");
      set.add("a");
      set.add("d");
      set.add("e");
      set.add("f");
      
      // remove elements
      set.remove("c");
      set.remove("f");

      // Iterating over the elements in the set
      Iterator it = set.iterator();

      while (it.hasNext()) {
         // Get element
         Object element = it.next();
         System.out.println(element.toString());
      }
   }
}

輸出

a
b
d
e

示例 3

SortedSet在TreeSet等各種類中都有實現。以下是一個帶有add和clear操作的TreeSet類的示例:

import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;

public class SortedSetDemo {

   public static void main(String[] args) {
      // Create the sorted set
      SortedSet<String> set = new TreeSet<>();  

      // Add elements to the set
      set.add("b");
      set.add("c");
      set.add("a");
      set.add("d");
      set.add("e");
      set.add("f");
      
      System.out.println(set);
      
      // remove elements
      set.clear();
      
      System.out.println(set);
   }
}

輸出

[a, b, c, d, e, f]
[]

Java - 資料結構

Java實用程式包提供的資料結構非常強大,並且執行各種功能。這些資料結構包括以下介面和類:

  • Enumeration
  • BitSet
  • Vector
  • Stack
  • Dictionary
  • Hashtable
  • Properties

所有這些類現在都是遺留類,Java-2引入了名為Collections Framework的新框架,這將在下一章中討論。

Enumeration

Enumeration介面本身並不是資料結構,但在其他資料結構的上下文中非常重要。Enumeration介面定義了一種從資料結構中檢索連續元素的方法。

例如,Enumeration定義了一個名為nextElement的方法,用於獲取包含多個元素的資料結構中的下一個元素。

示例

以下是一個示例,展示了Enumeration在Vector中的用法。

import java.util.Vector;
import java.util.Enumeration;

public class EnumerationTester {

   public static void main(String args[]) {
      Enumeration<String> days;
      Vector<String> dayNames = new Vector<>();
      
      dayNames.add("Sunday");
      dayNames.add("Monday");
      dayNames.add("Tuesday");
      dayNames.add("Wednesday");
      dayNames.add("Thursday");
      dayNames.add("Friday");
      dayNames.add("Saturday");
      days = dayNames.elements();
      
      while (days.hasMoreElements()) {
         System.out.println(days.nextElement()); 
      }
   }
}

輸出

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

要了解更多關於此介面的資訊,請檢視Enumeration

BitSet

BitSet類實現了一組位或標誌,可以分別設定和清除。

當需要跟蹤一組布林值時,此類非常有用;只需為每個值分配一位,並根據需要設定或清除它。

示例

以下程式說明了此資料結構支援的幾種方法:

import java.util.BitSet;
public class BitSetDemo {

  public static void main(String args[]) {
      BitSet bits1 = new BitSet(16);
      BitSet bits2 = new BitSet(16);
      
      // set some bits
      for(int i = 0; i < 16; i++) {
         if((i % 2) == 0) bits1.set(i);
         if((i % 5) != 0) bits2.set(i);
      }
     
      System.out.println("Initial pattern in bits1: ");
      System.out.println(bits1);
      System.out.println("\nInitial pattern in bits2: ");
      System.out.println(bits2);

      // AND bits
      bits2.and(bits1);
      System.out.println("\nbits2 AND bits1: ");
      System.out.println(bits2);

      // OR bits
      bits2.or(bits1);
      System.out.println("\nbits2 OR bits1: ");
      System.out.println(bits2);

      // XOR bits
      bits2.xor(bits1);
      System.out.println("\nbits2 XOR bits1: ");
      System.out.println(bits2);
   }
}

輸出

Initial pattern in bits1:
{0, 2, 4, 6, 8, 10, 12, 14}

Initial pattern in bits2:
{1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14}

bits2 AND bits1:
{2, 4, 6, 8, 12, 14}

bits2 OR bits1:
{0, 2, 4, 6, 8, 10, 12, 14}

bits2 XOR bits1:
{}

Vector

Vector類類似於傳統的Java陣列,不同之處在於它可以根據需要增長以容納新元素。

與陣列一樣,Vector物件中的元素可以透過向量中的索引訪問。

使用Vector類的好處是,不必擔心在建立時將其設定為特定大小;它會在必要時自動收縮和增長。

示例

以下程式說明了此集合支援的幾種方法:

import java.util.*;
public class VectorDemo {

   public static void main(String args[]) {
      // initial size is 3, increment is 2
      Vector v = new Vector(3, 2);
      System.out.println("Initial size: " + v.size());
      System.out.println("Initial capacity: " + v.capacity());
      
      v.addElement(new Integer(1));
      v.addElement(new Integer(2));
      v.addElement(new Integer(3));
      v.addElement(new Integer(4));
      System.out.println("Capacity after four additions: " + v.capacity());

      v.addElement(new Double(5.45));
      System.out.println("Current capacity: " + v.capacity());
      
      v.addElement(new Double(6.08));
      v.addElement(new Integer(7));
      System.out.println("Current capacity: " + v.capacity());
      
      v.addElement(new Float(9.4));
      v.addElement(new Integer(10));
      System.out.println("Current capacity: " + v.capacity());
      
      v.addElement(new Integer(11));
      v.addElement(new Integer(12));
      System.out.println("First element: " + (Integer)v.firstElement());
      System.out.println("Last element: " + (Integer)v.lastElement());
      
      if(v.contains(new Integer(3)))
         System.out.println("Vector contains 3.");
         
      // enumerate the elements in the vector.
      Enumeration vEnum = v.elements();
      System.out.println("\nElements in vector:");
      
      while(vEnum.hasMoreElements())
         System.out.print(vEnum.nextElement() + " ");
      System.out.println();
   }
}

輸出

Initial size: 0
Initial capacity: 3
Capacity after four additions: 5
Current capacity: 5
Current capacity: 7
Current capacity: 9
First element: 1
Last element: 12
Vector contains 3.

Elements in vector:
1 2 3 4 5.45 6.08 7 9.4 10 11 12

Stack

Stack類實現了一個後進先出(LIFO)的元素堆疊。

可以將堆疊從字面上理解為物件的垂直堆疊;新增新元素時,它會堆疊在其他元素之上。

從堆疊中提取元素時,它會從頂部取出。換句話說,新增到堆疊中的最後一個元素是第一個返回的元素。

示例

以下程式說明了此集合支援的幾種方法:

import java.util.*;
public class StackDemo {

   static void showpush(Stack st, int a) {
      st.push(new Integer(a));
      System.out.println("push(" + a + ")");
      System.out.println("stack: " + st);
   }

   static void showpop(Stack st) {
      System.out.print("pop -> ");
      Integer a = (Integer) st.pop();
      System.out.println(a);
      System.out.println("stack: " + st);
   }

   public static void main(String args[]) {
      Stack st = new Stack();
      System.out.println("stack: " + st);
      showpush(st, 42);
      showpush(st, 66);
      showpush(st, 99);
      showpop(st);
      showpop(st);
      showpop(st);
      try {
         showpop(st);
      } catch (EmptyStackException e) {
         System.out.println("empty stack");
      }
   }
}

輸出

stack: [ ]
push(42)
stack: [42]
push(66)
stack: [42, 66]
push(99)
stack: [42, 66, 99]
pop -> 99
stack: [42, 66]
pop -> 66
stack: [42]
pop -> 42
stack: [ ]
pop -> empty stack

Dictionary

Dictionary類是一個抽象類,它定義了一個用於將鍵對映到值的資料結構。

這在需要透過特定鍵而不是整數索引訪問資料的情況下非常有用。

由於Dictionary類是抽象類,它只提供鍵對映資料結構的框架,而不是具體的實現。

示例

以下示例顯示了Java Dictionary keys()方法的用法。我們使用Integer, Integer的Hashtable物件建立一個字典例項。然後我們向其中添加了一些元素。使用keys()方法檢索列舉,然後迭代列舉以列印字典的鍵。

package com.tutorialspoint;

import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;

public class DictionaryDemo {
   public static void main(String[] args) {

      // create a new hashtable
      Dictionary<Integer, Integer> dictionary = new Hashtable<>();

      // add 2 elements
      dictionary.put(1, 1);
      dictionary.put(2, 2);

      Enumeration<Integer> enumeration = dictionary.keys();

      while(enumeration.hasMoreElements()) {
         System.out.println(enumeration.nextElement());
      }
   }
}

輸出

2
1

Hashtable

Hashtable類提供了一種基於某些使用者定義的鍵結構組織資料的方法。

例如,在地址列表雜湊表中,可以根據郵政編碼等鍵儲存和排序資料,而不是根據人的姓名。

關於雜湊表而言,鍵的具體含義完全取決於雜湊表的用法及其包含的資料。

示例

以下示例顯示了Java Hashtable contains()方法的用法,用於檢查Hashtable中是否存在值。我們建立了一個Integer,Integer的Hashtable物件。然後添加了一些條目,打印表,並使用contains()檢查表中的兩個值。

package com.tutorialspoint;

import java.util.Hashtable;

public class HashtableDemo {
   public static void main(String args[]) {
      
      // create hash table
      Hashtable<Integer,Integer> hashtable = new Hashtable<>();

      // populate hash table
      hashtable.put(1, 1);
      hashtable.put(2, 2);
      hashtable.put(3, 3); 

      System.out.println("Initial table elements: " + hashtable);
      System.out.println("Hashtable contains 2 as value: " + hashtable.contains(2));
      System.out.println("Hashtable contains 4 as value: " + hashtable.contains(4));
   }    
}

輸出

Initial table elements: {3=3, 2=2, 1=1}
Hashtable contains 2 as value: true
Hashtable contains 4 as value: false

Properties

Properties是Hashtable的子類。它用於維護值的列表,其中鍵是字串,值也是字串。

許多其他Java類都使用Properties類。例如,它是System.getProperties( )在獲取環境值時返回的物件型別。

示例

以下示例顯示了Java Properties getProperty(String key)方法的用法,該方法用於從Properties中根據鍵獲取值。我們建立了一個Properties物件。然後添加了一些條目。使用getProperty()方法,檢索並列印值。

package com.tutorialspoint;

import java.util.Properties;

public class PropertiesDemo {
   public static void main(String[] args) {
      Properties properties = new Properties();

      //populate properties object
      properties.put("1", "tutorials");
      properties.put("2", "point");
      properties.put("3", "is best");

      System.out.println("Properties elements: " + properties);
      System.out.println("Value: " + properties.getProperty("1"));
   }
}

輸出

Properties elements: {1=tutorials, 2=point, 3=is best}
Value: tutorials

Java - Enumeration介面

Enumeration介面定義了可以列舉(一次獲取一個)物件集合中的元素的方法。

這個遺留介面已被Iterator取代。雖然沒有被棄用,但Enumeration被認為對於新程式碼來說已經過時。但是,它被Vector和Properties等遺留類定義的幾種方法使用,被其他幾個API類使用,並且目前在應用程式程式碼中被廣泛使用。

Enumeration宣告的方法總結在下表中:

序號 方法和描述
1

boolean hasMoreElements( )

實現時,如果仍有更多元素要提取,則必須返回true;如果所有元素都已列舉,則返回false。

2

Object nextElement( )

這將列舉中的下一個物件作為通用Object引用返回。

示例 1

以下是一個示例,展示了Enumeration在Vector中的用法。

import java.util.Vector;
import java.util.Enumeration;

public class EnumerationTester {

   public static void main(String args[]) {
      Enumeration<String> days;
      Vector<String> dayNames = new Vector<>();
      
      dayNames.add("Sunday");
      dayNames.add("Monday");
      dayNames.add("Tuesday");
      dayNames.add("Wednesday");
      dayNames.add("Thursday");
      dayNames.add("Friday");
      dayNames.add("Saturday");
      days = dayNames.elements();
      
      while (days.hasMoreElements()) {
         System.out.println(days.nextElement()); 
      }
   }
}

輸出

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

示例 2

以下是一個示例,展示了Enumeration在Properties中的用法,用於列印值。

import java.util.Vector;
import java.util.Enumeration;
import java.util.Properties;

public class EnumerationTester {

   public static void main(String args[]) {
      Enumeration<Object> days;
      Properties dayNames = new Properties();
      
      dayNames.put(1, "Sunday");
      dayNames.put(2,"Monday");
      dayNames.put(3,"Tuesday");
      dayNames.put(4,"Wednesday");
      dayNames.put(5,"Thursday");
      dayNames.put(6,"Friday");
      dayNames.put(7,"Saturday");
      days = dayNames.elements();
      
      while (days.hasMoreElements()) {
         System.out.println(days.nextElement()); 
      }
   }
}

輸出

Sunday
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday

Java - 集合演算法

集合框架定義了幾種可以應用於集合和對映的演算法。

這些演算法在Collections類中定義為靜態方法。一些方法可能會丟擲**ClassCastException**異常(當嘗試比較不相容的型別時發生),或者丟擲**UnsupportedOperationException**異常(當嘗試修改不可修改的集合時發生)。

集合框架演算法中定義的方法總結在下表中:

序號 方法和描述
1

static int binarySearch(List list, Object value, Comparator c)

根據**c**在列表中搜索value。返回value在列表中的位置,如果未找到value,則返回-1。

2

static int binarySearch(List list, Object value)

在列表中搜索value。列表必須已排序。返回value在列表中的位置,如果未找到value,則返回-1。

3

static void copy(List list1, List list2)

將list2的元素複製到list1。

4

static Enumeration enumeration(Collection c)

返回**c**的列舉。

5

static void fill(List list, Object obj)

將obj分配給列表的每個元素。

6

static int indexOfSubList(List list, List subList)

在列表中搜索subList的第一次出現。返回第一次匹配的索引,如果未找到匹配項,則返回-1。

7

static int lastIndexOfSubList(List list, List subList)

在列表中搜索subList的最後一次出現。返回最後一次匹配的索引,如果未找到匹配項,則返回-1。

8

static ArrayList list(Enumeration enum)

返回一個包含enum元素的ArrayList。

9

static Object max(Collection c, Comparator comp)

返回**c**中由comp確定的最大元素。

10

static Object max(Collection c)

返回**c**中由自然排序確定的最大元素。集合不必排序。

11

static Object min(Collection c, Comparator comp)

返回**c**中由comp確定的最小元素。集合不必排序。

12

static Object min(Collection c)

返回**c**中由自然排序確定的最小元素。

13

static List nCopies(int num, Object obj)

返回包含在不可變列表中的num個obj副本。num必須大於或等於零。

14

static boolean replaceAll(List list, Object old, Object new)

替換列表中所有 old 物件為 new 物件。如果至少替換了一個物件,則返回 true;否則返回 false。

15

static void reverse(List list)

反轉列表中的序列。

16

static Comparator reverseOrder( )

返回一個反向比較器。

17

static void rotate(List list, int n)

將列表向右旋轉 n 個位置。要向左旋轉,請對 n 使用負值。

18

static void shuffle(List list, Random r)

使用 r 作為隨機數源,對列表中的元素進行洗牌(即隨機化)。

19

static void shuffle(List list)

對列表中的元素進行洗牌(即隨機化)。

20

static Set singleton(Object obj)

將 obj 作為不可變集合返回。這是一種將單個物件轉換為集合的簡便方法。

21

static List singletonList(Object obj)

將 obj 作為不可變列表返回。這是一種將單個物件轉換為列表的簡便方法。

22

static Map singletonMap(Object k, Object v)

將鍵值對 k/v 作為不可變對映返回。這是一種將單個鍵值對轉換為對映的簡便方法。

23

static void sort(List list, Comparator comp)

根據 comp 對列表的元素進行排序。

24

static void sort(List list)

根據列表元素的自然順序對其進行排序。

25

static void swap(List list, int idx1, int idx2)

交換列表中由 idx1 和 idx2 指定的索引處的元素。

26

static Collection synchronizedCollection(Collection c)

返回一個由 c 支援的執行緒安全集合。

27

static List synchronizedList(List list)

返回一個由 list 支援的執行緒安全列表。

28

static Map synchronizedMap(Map m)

返回一個由 m 支援的執行緒安全對映。

29

static Set synchronizedSet(Set s)

返回一個由 s 支援的執行緒安全集合。

30

static SortedMap synchronizedSortedMap(SortedMap sm)

返回一個由 sm 支援的執行緒安全排序對映。

31

static SortedSet synchronizedSortedSet(SortedSet ss)

返回一個由 ss 支援的執行緒安全排序集合。

32

static Collection unmodifiableCollection(Collection c)

返回一個由 c 支援的不可修改集合。

33

static List unmodifiableList(List list)

返回一個由 list 支援的不可修改列表。

34

static Map unmodifiableMap(Map m)

返回一個由 m 支援的不可修改對映。

35

static Set unmodifiableSet(Set s)

返回一個由 s 支援的不可修改集合。

36

static SortedMap unmodifiableSortedMap(SortedMap sm)

返回一個由 sm 支援的不可修改排序對映。

37

static SortedSet unmodifiableSortedSet(SortedSet ss)

返回一個由 ss 支援的不可修改排序集合。

示例 1

以下是一個示例,它演示了使用 LinkedList 新增元素並按反向順序對其元素進行排序的各種演算法。使用迭代器迭代列表,然後進行洗牌,最後檢索並列印最小值和最大值。

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class AlgorithmsDemo {

   public static void main(String args[]) {
      
      // Create and initialize linked list
      List<Integer< ll = new LinkedList<<();
      ll.add(Integer.valueOf(-8));
      ll.add(Integer.valueOf(20));
      ll.add(Integer.valueOf(-20));
      ll.add(Integer.valueOf(8));
      
      // Create a reverse order comparator
      Comparator<Integer< r = Collections.reverseOrder();
      
      // Sort list by using the comparator
      Collections.sort(ll, r);
      
      // Get iterator
      Iterator<Integer< li = ll.iterator();
      System.out.print("List sorted in reverse: ");
      
      while(li.hasNext()) {
         System.out.print(li.next() + " ");
      }
      System.out.println();
   }
}

輸出

List sorted in reverse: 20 8 -8 -20

示例 2

以下是一個示例,它演示了使用 LinkedList 新增元素並按自然順序對其元素進行排序的各種演算法。使用迭代器迭代列表,然後進行洗牌,最後檢索並列印最小值和最大值。

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class AlgorithmsDemo {

   public static void main(String args[]) {
      
      // Create and initialize linked list
      List<Integer> ll = new LinkedList<>();
      ll.add(Integer.valueOf(-8));
      ll.add(Integer.valueOf(20));
      ll.add(Integer.valueOf(-20));
      ll.add(Integer.valueOf(8));
      
      
      // Sort list by using the default comparator
      Collections.sort(ll);
      
      // Get iterator
      Iterator<Integer> li = ll.iterator();
      System.out.print("List sorted: ");
      
      while(li.hasNext()) {
         System.out.print(li.next() + " ");
      }
      System.out.println();
      Collections.shuffle(ll);
      
      // display randomized list
      li = ll.iterator();
      System.out.print("List shuffled: ");
      
      while(li.hasNext()) {
         System.out.print(li.next() + " ");
      }

      System.out.println();
      System.out.println("Minimum: " + Collections.min(ll));
      System.out.println("Maximum: " + Collections.max(ll));
   }
}

輸出

List sorted: -20 -8 8 20 
List shuffled: -20 -8 20 8 
Minimum: -20
Maximum: 20

示例 3

以下是一個示例,它演示了使用 LinkedList 新增字串元素並按自然順序對其元素進行排序的各種演算法。列印列表,然後進行洗牌,最後檢索並列印最小值和最大值。

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class AlgorithmsDemo {

   public static void main(String args[]) {

      // Create and initialize linked list
      List<String> list = new LinkedList<>();
      list.add("Ram");
      list.add("Mohan");
      list.add("Julie");
      list.add("Raheem");

      // Sort list by using the default comparator
      Collections.sort(list);

      // print the sorted list
      System.out.println("Sorted List: " + list);

      // shuffle the list
      Collections.shuffle(list);

      // print the shuffled list
      System.out.println("Shuffled List: " + list);

      System.out.println("Minimum: " + Collections.min(list));
      System.out.println("Maximum: " + Collections.max(list));
   }
}

輸出

Sorted List: [Julie, Mohan, Raheem, Ram]
Shuffled List: [Mohan, Raheem, Julie, Ram]
Minimum: Julie
Maximum: Ram

Java - 如何使用迭代器?

通常,您需要遍歷集合中的元素。例如,您可能需要顯示每個元素。最簡單的方法是使用迭代器,它是一個實現 Iterator 或 ListIterator 介面的物件。

迭代器使您可以遍歷集合,獲取或刪除元素。ListIterator 擴充套件了 Iterator,允許對列表進行雙向遍歷以及修改元素。

在您可以透過迭代器訪問集合之前,必須先獲取一個迭代器。每個集合類都提供一個 iterator( ) 方法,該方法返回指向集合開頭的迭代器。透過使用此迭代器物件,您可以一次訪問集合中的每個元素。

一般來說,要使用迭代器遍歷集合的內容,請按照以下步驟操作:

  • 透過呼叫集合的 iterator( ) 方法,獲取指向集合開頭的迭代器。

  • 設定一個迴圈,該迴圈呼叫 hasNext( )。只要 hasNext( ) 返回 true,迴圈就一直迭代。

  • 在迴圈中,透過呼叫 next( ) 獲取每個元素。

對於實現 List 的集合,您還可以透過呼叫 ListIterator 獲取迭代器。

Iterator宣告的方法

序號 方法和描述
1

boolean hasNext( )

如果還有更多元素,則返回 true;否則返回 false。

2

Object next( )

返回下一個元素。如果沒有下一個元素,則丟擲 NoSuchElementException。

3

void remove( )

刪除當前元素。如果嘗試呼叫 remove( ) 之前沒有呼叫 next( ),則丟擲 IllegalStateException。

ListIterator宣告的方法

序號 方法和描述
1

void add(Object obj)

在下一個 next( ) 呼叫將返回的元素前面,將 obj 插入列表中。

2

boolean hasNext( )

如果存在下一個元素,則返回 true;否則返回 false。

3

boolean hasPrevious( )

如果存在前一個元素,則返回 true;否則返回 false。

4

Object next( )

返回下一個元素。如果沒有下一個元素,則丟擲 NoSuchElementException。

5

int nextIndex( )

返回下一個元素的索引。如果沒有下一個元素,則返回列表的大小。

6

Object previous( )

返回前一個元素。如果沒有前一個元素,則丟擲 NoSuchElementException。

7

int previousIndex( )

返回前一個元素的索引。如果沒有前一個元素,則返回 -1。

8

void remove( )

從列表中刪除當前元素。如果在呼叫 next( ) 或 previous( ) 之前呼叫 remove( ),則丟擲 IllegalStateException。

9

void set(Object obj)

將 obj 分配給當前元素。這是上次透過呼叫 next( ) 或 previous( ) 返回的元素。

示例 1

這是一個演示 Iterator 的示例。它使用 ArrayList 物件,但一般原則適用於任何型別的集合。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {

   public static void main(String args[]) {
      // Create an array list
      List<String> al = new ArrayList<>();
      
      // add elements to the array list
      al.add("C");
      al.add("A");
      al.add("E");
      al.add("B");
      al.add("D");
      al.add("F");

      // Use iterator to display contents of al
      System.out.print("Original contents of al: ");
      Iterator itr = al.iterator();
      
      while(itr.hasNext()) {
         Object element = itr.next();
         System.out.print(element + " ");
      }
      System.out.println();
   }
}

輸出

Original contents of al: C A E B D F

示例 2

這是一個演示 ListIterator 的示例。它使用 ArrayList 物件,但一般原則適用於任何型別的集合。

當然,只有那些實現 List 介面的集合才可以使用 ListIterator。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IteratorDemo {

   public static void main(String args[]) {
      // Create an array list
      List<String> al = new ArrayList<>();
      
      // add elements to the array list
      al.add("C");
      al.add("A");
      al.add("E");
      al.add("B");
      al.add("D");
      al.add("F");

      // Use iterator to display contents of al
      System.out.print("Original contents of al: ");
      Iterator<String> itr = al.iterator();
      
      while(itr.hasNext()) {
         Object element = itr.next();
         System.out.print(element + " ");
      }
   }
}

輸出

Original contents of al: C A E B D F 

示例 3

這是一個演示 ListIterator 在迭代時修改列表的示例。它使用 ArrayList 物件,但一般原則適用於任何型別的集合。

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class IteratorDemo {

   public static void main(String args[]) {
      // Create an array list
      List<String> al = new ArrayList<>();
      
      // add elements to the array list
      al.add("C");
      al.add("A");
      al.add("E");
      al.add("B");
      al.add("D");
      al.add("F");

      // Use iterator to display contents of al
      System.out.print("Original contents of al: ");
      Iterator<String> itr = al.iterator();
      
      while(itr.hasNext()) {
         Object element = itr.next();
         System.out.print(element + " ");
      }
      System.out.println();
      
      // Modify objects being iterated
      ListIterator<String> litr = al.listIterator();
      
      while(litr.hasNext()) {
         Object element = litr.next();
         litr.set(element + "+");
      }
      System.out.print("Modified contents of al: ");
      itr = al.iterator();
      
      while(itr.hasNext()) {
         Object element = itr.next();
         System.out.print(element + " ");
      }
      System.out.println();

      // Now, display the list backwards
      System.out.print("Modified list backwards: ");
      
      while(litr.hasPrevious()) {
         Object element = litr.previous();
         System.out.print(element + " ");
      }
      System.out.println();
   }
}

輸出

Original contents of al: C A E B D F 
Modified contents of al: C+ A+ E+ B+ D+ F+ 
Modified list backwards: F+ D+ B+ E+ A+ C+ 

Java - 如何使用 Comparator?

TreeSet 和 TreeMap 都以排序順序儲存元素。但是,正是比較器定義了“排序順序”的確切含義。

Comparator 介面定義了兩個方法:compare( ) 和 equals( )。此處顯示的 compare( ) 方法比較兩個元素的順序:

compare 方法

int compare(Object obj1, Object obj2)

obj1 和 obj2 是要比較的物件。如果物件相等,此方法返回零。如果 obj1 大於 obj2,則返回正值;否則返回負值。

透過覆蓋 compare( ),您可以更改物件的排序方式。例如,要按反向順序排序,您可以建立一個反轉比較結果的比較器。

equals 方法

此處顯示的 equals( ) 方法測試物件是否等於呼叫比較器:

boolean equals(Object obj)

obj 是要測試相等性的物件。如果 obj 和呼叫物件都是 Comparator 物件並使用相同的排序,則該方法返回 true;否則返回 false。

無需覆蓋 equals( ),大多數簡單的比較器都不會這樣做。

示例 1

在此示例中,我們使用 Comparator 介面根據比較條件對自定義物件 Dog 進行排序。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class Dog implements Comparator<Dog>, Comparable<Dog> {
   private String name;
   private int age;
   Dog() {
   }

   Dog(String n, int a) {
      name = n;
      age = a;
   }

   public String getDogName() {
      return name;
   }

   public int getDogAge() {
      return age;
   }

   // Overriding the compareTo method
   public int compareTo(Dog d) {
      return (this.name).compareTo(d.name);
   }

   // Overriding the compare method to sort the age 
   public int compare(Dog d, Dog d1) {
      return d.age - d1.age;
   }

   @Override
   public String toString() {
      return this.name + "," + this.age;
   }
}

public class ComparatorDemo {

   public static void main(String args[]) {
      // Takes a list o Dog objects
      List<Dog> list = new ArrayList<>();

      list.add(new Dog("Shaggy", 3));
      list.add(new Dog("Lacy", 2));
      list.add(new Dog("Roger", 10));
      list.add(new Dog("Tommy", 4));
      list.add(new Dog("Tammy", 1));

      Collections.sort(list);   // Sorts the array list
      System.out.println("Sorted by name:");
      // printing the sorted list of names
      System.out.print(list);

      // Sorts the array list using comparator
      Collections.sort(list, new Dog());
      System.out.println(" ");

      System.out.println("Sorted by age:");
      // printing the sorted list of ages
      System.out.print(list);
   }
}

輸出

Sorted by name:
[Lacy,2, Roger,10, Shaggy,3, Tammy,1, Tommy,4] 
Sorted by age:
[Tammy,1, Lacy,2, Shaggy,3, Tommy,4, Roger,10]

注意 - Arrays 類的排序與 Collections 相同。

示例 2

在此示例中,我們使用 Comparator 介面對 Dog 物件進行反向排序。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class Dog implements Comparator<Dog>, Comparable<Dog> {
   private String name;
   private int age;
   Dog() {
   }

   Dog(String n, int a) {
      name = n;
      age = a;
   }

   public String getDogName() {
      return name;
   }

   public int getDogAge() {
      return age;
   }

   // Overriding the compareTo method
   public int compareTo(Dog d) {
      return (this.name).compareTo(d.name);
   }

   // Overriding the compare method to sort the age 
   public int compare(Dog d, Dog d1) {
      return d.age - d1.age;
   }

   @Override
   public String toString() {
      return this.name + "," + this.age;
   }
}

public class ComparatorDemo {

   public static void main(String args[]) {
      // Takes a list o Dog objects
      List<Dog> list = new ArrayList<>();

      list.add(new Dog("Shaggy", 3));
      list.add(new Dog("Lacy", 2));
      list.add(new Dog("Roger", 10));
      list.add(new Dog("Tommy", 4));
      list.add(new Dog("Tammy", 1));

      Collections.sort(list, Collections.reverseOrder());   // Sorts the array list
      System.out.println("Sorted by name in reverse order:");
      // printing the sorted list of names
      System.out.print(list);
   }
}

輸出

Sorted by name in reverse order:
[Tommy,4, Tammy,1, Shaggy,3, Roger,10, Lacy,2]

在此示例中,我們使用 Comparator 介面按反向順序對字串值進行排序。

示例 3

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ComparatorDemo {

   public static void main(String args[]) {
      // Takes a list o Dog objects
      List<String> list = new ArrayList<>();

      list.add("Shaggy");
      list.add("Lacy");
      list.add("Roger");
      list.add("Tommy");
      list.add("Tammy");

      Collections.sort(list, Collections.reverseOrder());   // Sorts the array list
      System.out.println("Sorted by name in reverse order:");
      // printing the sorted list of names
      System.out.print(list);
   }
}

輸出

Sorted by name in reverse order:
[Tommy, Tammy, Shaggy, Roger, Lacy]

Java - 正則表示式

Java 提供 java.util.regex 包用於使用正則表示式進行模式匹配。Java 正則表示式與 Perl 程式語言非常相似,非常容易學習。

正則表示式是一系列特殊的字元序列,它可以幫助您使用模式中包含的專門語法來匹配或查詢其他字串或字串集。它們可用於搜尋、編輯或操作文字和資料。

java.util.regex 包主要包含以下三個類:

  • Pattern 類 - Pattern 物件是正則表示式的編譯表示形式。Pattern 類沒有提供公共建構函式。要建立模式,您必須首先呼叫其公共靜態 compile() 方法之一,然後它將返回一個 Pattern 物件。這些方法接受正則表示式作為第一個引數。

  • Matcher 類 - Matcher 物件是解釋模式並對輸入字串執行匹配操作的引擎。與 Pattern 類一樣,Matcher 沒有定義公共建構函式。您可以透過在 Pattern 物件上呼叫 matcher() 方法來獲得 Matcher 物件。

  • PatternSyntaxException - PatternSyntaxException 物件是一個未經檢查的異常,它指示正則表示式模式中的語法錯誤。

捕獲組

捕獲組是一種將多個字元視為單個單元的方法。它們是透過將要分組的字元放在一組括號內來建立的。例如,正則表示式 (dog) 建立一個包含字母“d”、“o”和“g”的單個組。

捕獲組的編號是根據其左括號從左到右的順序進行計數的。例如,在表示式 ((A)(B(C))) 中,共有四個這樣的組:

  • ((A)(B(C)))
  • (A)
  • (B(C))
  • (C)

要找出表示式中存在多少個組,請對匹配器物件呼叫 groupCount 方法。groupCount 方法返回一個 **int** 值,表示匹配器模式中存在的捕獲組的數量。

還有一個特殊的組,即第 0 組,它始終代表整個表示式。此組未包含在 groupCount 報告的總數中。

示例

以下示例演示如何從給定的字母數字字串中查詢數字字串:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {

   public static void main( String args[] ) {
      // String to be scanned to find the pattern.
      String line = "This order was placed for QT3000! OK?";
      String pattern = "(.*)(\\d+)(.*)";

      // Create a Pattern object
      Pattern r = Pattern.compile(pattern);

      // Now create matcher object.
      Matcher m = r.matcher(line);
      if (m.find( )) {
         System.out.println("Found value: " + m.group(0) );
         System.out.println("Found value: " + m.group(1) );
         System.out.println("Found value: " + m.group(2) );
      }else {
         System.out.println("NO MATCH");
      }
   }
}

輸出

Found value: This order was placed for QT3000! OK?
Found value: This order was placed for QT300
Found value: 0

正則表示式語法

以下是 Java 中所有可用正則表示式元字元語法的列表:

子表示式 匹配
^ 匹配行首。
$ 匹配行尾。
. 匹配除換行符以外的任何單個字元。使用 **m** 選項允許它也匹配換行符。
[...] 匹配括號中任何單個字元。
[^...] 匹配括號中不存在的任何單個字元。
\A 整個字串的開頭。
\z 整個字串的結尾。
\Z 整個字串的結尾,但不包括允許的最終行終止符。
re* 匹配前一個表示式的 0 次或多次出現。
re+ 匹配前一個表示式的 1 次或多次出現。
re? 匹配前一個表示式的 0 次或 1 次出現。
re{ n} 匹配前一個表示式的恰好 n 次出現。
re{ n,} 匹配前一個表示式的 n 次或多次出現。
re{ n, m} 匹配前一個表示式的至少 n 次至多 m 次出現。
a| b 匹配 a 或 b。
(re) 對正則表示式進行分組並記住匹配的文字。
(?: re) 對正則表示式進行分組,但不記住匹配的文字。
(?> re) 匹配獨立模式,不回溯。
\w 匹配單詞字元。
\W 匹配非單詞字元。
\s 匹配空格。等效於 [\t\n\r\f]。
\S 匹配非空格。
\d 匹配數字。等效於 [0-9]。
\D 匹配非數字。
\A 匹配字串的開頭。
\Z 匹配字串的結尾。如果存在換行符,則匹配換行符之前的部分。
\z 匹配字串的結尾。
\G 匹配上次匹配結束的位置。
\n 對捕獲組編號為“n”的組的反向引用。
\b 在括號外部匹配單詞邊界。在括號內部匹配退格鍵 (0x08)。
\B 匹配非單詞邊界。
\n, \t, etc. 匹配換行符、回車符、製表符等。
\Q 轉義(引用)直到 \E 的所有字元。
\E 結束以 \Q 開始的引用。

Matcher 類的使用方法

以下是常用例項方法的列表:

索引方法

索引方法提供有用的索引值,精確地顯示匹配在輸入字串中找到的位置:

序號 方法和描述
1

public int start()

返回上一次匹配的起始索引。

2

public int start(int group)

返回在上一次匹配操作期間由給定組捕獲的子序列的起始索引。

3

public int end()

返回匹配的最後一個字元之後的偏移量。

4

public int end(int group)

返回在上一次匹配操作期間由給定組捕獲的子序列的最後一個字元之後的偏移量。

研究方法

研究方法檢查輸入字串,並返回一個布林值,指示是否找到模式:

序號 方法和描述
1

public boolean lookingAt()

嘗試從區域的開頭開始,將輸入序列與模式匹配。

2

public boolean find()

嘗試查詢輸入序列中與模式匹配的下一個子序列。

3

public boolean find(int start)

重置此匹配器,然後嘗試查詢輸入序列中與模式匹配的下一個子序列,從指定的索引開始。

4

public boolean matches()

嘗試將整個區域與模式匹配。

替換方法

替換方法是用於替換輸入字串中文字的有用方法:

序號 方法和描述
1

public Matcher appendReplacement(StringBuffer sb, String replacement)

實現非終端追加和替換步驟。

2

public StringBuffer appendTail(StringBuffer sb)

實現終端追加和替換步驟。

3

public String replaceAll(String replacement)

將輸入序列中與模式匹配的每個子序列替換為給定的替換字串。

4

public String replaceFirst(String replacement)

將輸入序列中與模式匹配的第一個子序列替換為給定的替換字串。

5

public static String quoteReplacement(String s)

返回指定字串的字面替換字串。此方法生成一個字串,該字串將在 Matcher 類的 appendReplacement 方法中用作字面替換 **s**。

start 和 end 方法

以下示例計算單詞“cat”在輸入字串中出現的次數:

示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {

   private static final String REGEX = "\\bcat\\b";
   private static final String INPUT = "cat cat cat cattie cat";

   public static void main( String args[] ) {
      Pattern p = Pattern.compile(REGEX);
      Matcher m = p.matcher(INPUT);   // get a matcher object
      int count = 0;

      while(m.find()) {
         count++;
         System.out.println("Match number "+count);
         System.out.println("start(): "+m.start());
         System.out.println("end(): "+m.end());
      }
   }
}

輸出

Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
Match number 4
start(): 19
end(): 22

您可以看到,此示例使用單詞邊界來確保字母“c”、“a”、“t”不僅僅是較長單詞中的子字串。它還提供了一些關於匹配在輸入字串中發生位置的有用資訊。

start 方法返回在上一次匹配操作期間由給定組捕獲的子序列的起始索引,end 方法返回最後一個匹配字元的索引加一。

matches 和 lookingAt 方法

matches 和 lookingAt 方法都嘗試將輸入序列與模式匹配。但是,區別在於 matches 要求匹配整個輸入序列,而 lookingAt 不需要。

兩種方法始終從輸入字串的開頭開始。以下示例解釋了其功能:

示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {

   private static final String REGEX = "foo";
   private static final String INPUT = "fooooooooooooooooo";
   private static Pattern pattern;
   private static Matcher matcher;

   public static void main( String args[] ) {
      pattern = Pattern.compile(REGEX);
      matcher = pattern.matcher(INPUT);

      System.out.println("Current REGEX is: "+REGEX);
      System.out.println("Current INPUT is: "+INPUT);

      System.out.println("lookingAt(): "+matcher.lookingAt());
      System.out.println("matches(): "+matcher.matches());
   }
}

輸出

Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false

replaceFirst 和 replaceAll 方法

replaceFirst 和 replaceAll 方法替換與給定正則表示式匹配的文字。顧名思義,replaceFirst 替換第一個匹配項,replaceAll 替換所有匹配項。

以下示例解釋了其功能:

示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {

   private static String REGEX = "dog";
   private static String INPUT = "The dog says meow. " + "All dogs say meow.";
   private static String REPLACE = "cat";

   public static void main(String[] args) {
      Pattern p = Pattern.compile(REGEX);
      
      // get a matcher object
      Matcher m = p.matcher(INPUT); 
      INPUT = m.replaceAll(REPLACE);
      System.out.println(INPUT);
   }
}

輸出

The cat says meow. All cats say meow.

appendReplacement 和 appendTail 方法

Matcher 類還提供 appendReplacement 和 appendTail 方法用於文字替換。

以下示例解釋了其功能:

示例

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexMatches {

   private static String REGEX = "a*b";
   private static String INPUT = "aabfooaabfooabfoob";
   private static String REPLACE = "-";
   public static void main(String[] args) {

      Pattern p = Pattern.compile(REGEX);
      
      // get a matcher object
      Matcher m = p.matcher(INPUT);
      StringBuffer sb = new StringBuffer();
      while(m.find()) {
         m.appendReplacement(sb, REPLACE);
      }
      m.appendTail(sb);
      System.out.println(sb.toString());
   }
}

輸出

-foo-foo-foo-

PatternSyntaxException 類方法

PatternSyntaxException 是一個未經檢查的異常,指示正則表示式模式中的語法錯誤。PatternSyntaxException 類提供以下方法來幫助您確定哪裡出了錯:

序號 方法和描述
1

public String getDescription()

檢索錯誤的描述。

2

public int getIndex()

檢索錯誤索引。

3

public String getPattern()

檢索錯誤的正則表示式模式。

4

public String getMessage()

返回一個多行字串,其中包含語法錯誤及其索引的描述、錯誤的正則表示式模式以及模式中錯誤索引的可視指示。

Java - 序列化

Java 提供了一種稱為物件序列化的機制,其中物件可以表示為位元組序列,其中包括物件的資料以及有關物件型別和儲存在物件中的資料型別的資訊。

將序列化物件寫入檔案後,可以從檔案讀取並反序列化該物件,也就是說,表示物件及其資料的資訊和位元組型別可以用來在記憶體中重新建立該物件。

最令人印象深刻的是,整個過程與 JVM 無關,這意味著物件可以在一個平臺上序列化,並在完全不同的平臺上反序列化。

**ObjectInputStream** 和 **ObjectOutputStream** 類是包含序列化和反序列化物件方法的高階流。

ObjectOutputStream 類包含許多用於寫入各種資料型別的方法,但有一種方法尤其突出:

public final void writeObject(Object x) throws IOException

上述方法序列化一個物件並將其傳送到輸出流。類似地,ObjectInputStream 類包含以下用於反序列化物件的方法:

public final Object readObject() throws IOException, ClassNotFoundException

此方法從流中檢索下一個物件並將其反序列化。返回值為 Object,因此您需要將其強制轉換為其適當的資料型別。

為了演示 Java 中的序列化工作方式,我將使用我們在本書前面討論過的 Employee 類。假設我們有以下實現了 Serializable 介面的 Employee 類:

示例

public class Employee implements java.io.Serializable {
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + name + " " + address);
   }
}

請注意,為了成功序列化一個類,必須滿足兩個條件:

  • 該類必須實現 java.io.Serializable 介面。

  • 該類中的所有欄位都必須是可序列化的。如果某個欄位不可序列化,則必須將其標記為 **transient**。

如果您想知道 Java 標準類是否可序列化,請檢查該類的文件。測試很簡單:如果該類實現了 java.io.Serializable,則它是可序列化的;否則,它不可序列化。

序列化物件

ObjectOutputStream 類用於序列化物件。以下 SerializeDemo 程式例項化一個 Employee 物件並將其序列化到檔案。

程式執行完畢後,將建立一個名為 employee.ser 的檔案。程式不會生成任何輸出,但請研究程式碼並嘗試確定程式在做什麼。

**注意** - 將物件序列化到檔案時,Java 中的標準約定是為檔案提供 **.ser** 副檔名。

示例

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializeDemo {

   public static void main(String [] args) {
      Employee e = new Employee();
      e.name = "Reyan Ali";
      e.address = "Phokka Kuan, Ambehta Peer";
      e.SSN = 11122333;
      e.number = 101;
      
      try {
         FileOutputStream fileOut = new FileOutputStream("employee.ser");
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf("Serialized data is saved in /tmp/employee.ser");
      } catch (IOException i) {
         i.printStackTrace();
      }
   }
}
class Employee implements java.io.Serializable {
   private static final long serialVersionUID = 1L;
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + name + " " + address);
   }
}

輸出

Serialized data is saved in employee.ser

反序列化物件

以下 DeserializeDemo 程式反序列化在前面程式中建立的 Employee 物件。研究程式並嘗試確定其輸出:

示例

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializeDemo {

   public static void main(String [] args) {
      Employee e = null;
      try {
         FileInputStream fileIn = new FileInputStream("employee.ser");
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      } catch (IOException i) {
         i.printStackTrace();
         return;
      } catch (ClassNotFoundException c) {
         System.out.println("Employee class not found");
         c.printStackTrace();
         return;
      }

      System.out.println("Deserialized Employee...");
      System.out.println("Name: " + e.name);
      System.out.println("Address: " + e.address);
      System.out.println("SSN: " + e.SSN);
      System.out.println("Number: " + e.number);
   }
}
class Employee implements java.io.Serializable {

   private static final long serialVersionUID = 1L;
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck() {
      System.out.println("Mailing a check to " + name + " " + address);
   }
}

輸出

Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101

以下是需要注意的重要幾點:

  • try/catch 塊嘗試捕獲 ClassNotFoundException,該異常由 readObject() 方法宣告。對於 JVM 能夠反序列化物件,它必須能夠找到該類的位元組碼。如果 JVM 在物件的序列化過程中找不到類,則它會丟擲 ClassNotFoundException。

  • 請注意,readObject() 的返回值強制轉換為 Employee 引用。

  • 物件序列化時 SSN 欄位的值為 11122333,但由於該欄位是瞬態的,因此該值未傳送到輸出流。反序列化的 Employee 物件的 SSN 欄位為 0。

Java - 傳送郵件

使用您的 Java 應用程式傳送電子郵件非常簡單,但首先您應該在您的機器上安裝**JavaMail API** 和 **Java Activation Framework (JAF)**。

下載並解壓縮這些檔案,在新建的頂級目錄中,您將找到這兩個應用程式的多個 jar 檔案。您需要將 **mail.jar** 和 **activation.jar** 檔案新增到您的 CLASSPATH 中。

傳送簡單的電子郵件

這是一個從您的機器傳送簡單電子郵件的示例。假設您的 **localhost** 已連線到網際網路,並且能夠傳送電子郵件。

示例

// File Name SendEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendEmail {

   public static void main(String [] args) {    
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Now set the actual message
         message.setText("This is actual message");

         // Send message
         Transport.send(message);
         System.out.println("Sent message successfully....");
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
}

輸出

編譯並執行此程式以傳送簡單的電子郵件:

$ java SendEmail
Sent message successfully....

如果您想向多個收件人傳送電子郵件,則可以使用以下方法指定多個電子郵件 ID:

void addRecipients(Message.RecipientType type, Address[] addresses)
   throws MessagingException

以下是引數的描述:

  • **type** - 這將設定為 TO、CC 或 BCC。其中 CC 代表抄送,BCC 代表密送。例如:Message.RecipientType.TO

  • **addresses** - 這是一個電子郵件 ID 陣列。在指定電子郵件 ID 時,您需要使用 InternetAddress() 方法。

傳送 HTML 電子郵件

這是一個從您的機器傳送 HTML 電子郵件的示例。這裡假設您的 **localhost** 已連線到網際網路,並且能夠傳送電子郵件。

此示例與前一個示例非常相似,不同之處在於這裡我們使用 setContent() 方法來設定內容,其第二個引數是“text/html”,用於指定郵件中包含 HTML 內容。

使用此示例,您可以傳送任意大小的 HTML 內容。

示例

// File Name SendHTMLEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendHTMLEmail {

   public static void main(String [] args) {
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));

         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Send the actual HTML message, as big as you like
         message.setContent("<h1>This is actual message</h1>", "text/html");

         // Send message
         Transport.send(message);
         System.out.println("Sent message successfully....");
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
}

編譯並執行此程式以傳送 HTML 電子郵件:

輸出

$ java SendHTMLEmail
Sent message successfully....

在電子郵件中傳送附件

這是一個從您的機器傳送包含附件的電子郵件的示例。這裡假設您的 **localhost** 已連線到網際網路,並且能夠傳送電子郵件。

示例

// File Name SendFileEmail.java

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.*;

public class SendFileEmail {

   public static void main(String [] args) {     
      // Recipient's email ID needs to be mentioned.
      String to = "abcd@gmail.com";

      // Sender's email ID needs to be mentioned
      String from = "web@gmail.com";

      // Assuming you are sending email from localhost
      String host = "localhost";

      // Get system properties
      Properties properties = System.getProperties();

      // Setup mail server
      properties.setProperty("mail.smtp.host", host);

      // Get the default Session object.
      Session session = Session.getDefaultInstance(properties);

      try {
         // Create a default MimeMessage object.
         MimeMessage message = new MimeMessage(session);

         // Set From: header field of the header.
         message.setFrom(new InternetAddress(from));

         // Set To: header field of the header.
         message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));

         // Set Subject: header field
         message.setSubject("This is the Subject Line!");

         // Create the message part 
         BodyPart messageBodyPart = new MimeBodyPart();

         // Fill the message
         messageBodyPart.setText("This is message body");
         
         // Create a multipar message
         Multipart multipart = new MimeMultipart();

         // Set text message part
         multipart.addBodyPart(messageBodyPart);

         // Part two is attachment
         messageBodyPart = new MimeBodyPart();
         String filename = "file.txt";
         DataSource source = new FileDataSource(filename);
         messageBodyPart.setDataHandler(new DataHandler(source));
         messageBodyPart.setFileName(filename);
         multipart.addBodyPart(messageBodyPart);

         // Send the complete message parts
         message.setContent(multipart );

         // Send message
         Transport.send(message);
         System.out.println("Sent message successfully....");
      } catch (MessagingException mex) {
         mex.printStackTrace();
      }
   }
}

編譯並執行此程式以傳送 HTML 電子郵件:

輸出

$ java SendFileEmail
Sent message successfully....

使用者身份驗證部分

如果需要向電子郵件伺服器提供使用者 ID 和密碼進行身份驗證,則可以按如下方式設定這些屬性:

props.setProperty("mail.user", "myuser");
props.setProperty("mail.password", "mypwd");

其餘的電子郵件傳送機制將保持如上所述。

Java - Applet 基礎

**Applet** 是一個在 Web 瀏覽器中執行的 Java 程式。Applet 可以是一個功能齊全的 Java 應用程式,因為它可以使用整個 Java API。

Applet 和獨立 Java 應用程式之間存在一些重要區別,包括以下幾點:

  • Applet 是擴充套件 java.applet.Applet 類的 Java 類。

  • 不會在 Applet 上呼叫 main() 方法,並且 Applet 類不會定義 main()。

  • Applet 設計為嵌入在 HTML 頁面中。

  • 當用戶檢視包含 Applet 的 HTML 頁面時,Applet 的程式碼將下載到使用者的機器上。

  • 檢視 Applet 需要 JVM。JVM 可以是 Web 瀏覽器的外掛或單獨的執行時環境。

  • 使用者機器上的 JVM 建立 Applet 類的例項,並在 Applet 的生命週期中呼叫各種方法。

  • Applet 具有由 Web 瀏覽器強制執行的嚴格安全規則。Applet 的安全性通常被稱為沙箱安全性,將 Applet 比作在沙箱中玩耍的孩子,必須遵守各種規則。

  • Applet 需要 的其他類可以打包在一個 Java 歸檔 (JAR) 檔案中下載。

Applet 的生命週期

Applet 類中的四種方法為您構建任何嚴肅的 Applet 提供了框架:

  • **init** - 此方法用於 Applet 所需的任何初始化。在處理 applet 標記內的 param 標記後呼叫它。

  • **start** - 在瀏覽器呼叫 init 方法後,此方法會自動呼叫。當用戶在訪問其他頁面後返回包含 Applet 的頁面時,也會呼叫它。

  • **stop** - 當用戶離開包含 Applet 的頁面時,此方法會自動呼叫。因此,它可以在同一個 Applet 中重複呼叫。

  • **destroy** - 此方法僅在瀏覽器正常關閉時呼叫。因為 Applet 旨在存在於 HTML 頁面上,所以使用者離開包含 Applet 的頁面後,您通常不應留下資源。

  • **paint** - 在 start() 方法之後立即呼叫,以及任何時候 Applet 需要在瀏覽器中重新繪製自身時。paint() 方法實際上是從 java.awt 繼承而來的。

一個“Hello, World”Applet

下面是一個名為 HelloWorldApplet.java 的簡單 Applet:

import java.applet.*;
import java.awt.*;

public class HelloWorldApplet extends Applet {
   public void paint (Graphics g) {
      g.drawString ("Hello World", 25, 50);
   }
}

這些 import 語句將類引入到我們 Applet 類的範圍內:

  • java.applet.Applet
  • java.awt.Graphics

如果沒有這些 import 語句,Java 編譯器將無法識別 Applet 類引用的 Applet 和 Graphics 類。

Applet 類

每個 Applet 都是 _java.applet.Applet 類_ 的擴充套件。基本的 Applet 類提供派生 Applet 類可以呼叫的方法,以從瀏覽器上下文獲取資訊和服務。

這些包括執行以下操作的方法:

  • 獲取 Applet 引數
  • 獲取包含 Applet 的 HTML 檔案的網路位置
  • 獲取 Applet 類目錄的網路位置
  • 在瀏覽器中列印狀態訊息
  • 獲取影像
  • 獲取音訊剪輯
  • 播放音訊剪輯
  • 調整 Applet 大小

此外,Applet 類提供了一個介面,檢視器或瀏覽器可以透過該介面獲取有關 Applet 的資訊並控制 Applet 的執行。檢視器可以:

  • 請求有關 Applet 的作者、版本和版權的資訊
  • 請求 Applet 識別的引數的描述
  • 初始化 Applet
  • 銷燬 Applet
  • 啟動 Applet 的執行
  • 停止 Applet 的執行

Applet 類為每種方法提供了預設實現。可以根據需要覆蓋這些實現。

“Hello, World”Applet 現狀已完整。唯一被覆蓋的方法是 paint 方法。

呼叫 Applet

可以透過在 HTML 檔案中嵌入指令並在 Applet 檢視器或支援 Java 的瀏覽器中檢視該檔案來呼叫 Applet。

<applet> 標記是將 Applet 嵌入 HTML 檔案的基礎。以下是一個呼叫“Hello, World”Applet 的示例:

<html>
   <title>The Hello, World Applet</title>
   <hr>
   <applet code = "HelloWorldApplet.class" width = "320" height = "120">
      If your browser was Java-enabled, a "Hello, World"
      message would appear here.
   </applet>
   <hr>
</html>

**注意** - 您可以參考 HTML Applet Tag 以瞭解有關從 HTML 呼叫 Applet 的更多資訊。

<applet> 標記的 code 屬性是必需的。它指定要執行的 Applet 類。寬度和高度也是必需的,用於指定 Applet 執行的面板的初始大小。Applet 指令必須用 </applet> 標記關閉。

如果 Applet 需要引數,則可以透過在 <applet></applet> 之間新增 <param> 標記來傳遞引數的值。瀏覽器忽略 applet 標記之間的文字和其他標記。

不支援 Java 的瀏覽器不會處理 <applet></applet>。因此,標記之間出現的任何與 Applet 無關的內容都可以在不支援 Java 的瀏覽器中看到。

檢視器或瀏覽器會在文件的位置查詢已編譯的 Java 程式碼。要指定其他位置,請使用 <applet> 標記的 codebase 屬性,如下所示:

<applet codebase = "https://amrood.com/applets" code = "HelloWorldApplet.class"
   width = "320" height = "120">

如果 Applet 位於預設包以外的包中,則必須使用句點字元 (.) 在 code 屬性中指定儲存包,以分隔包/類元件。例如:

<applet  = "mypackage.subpackage.TestApplet.class" 
   width = "320" height = "120">

獲取 Applet 引數

以下示例演示如何使 Applet 對文件中指定的安裝引數做出響應。此 Applet 顯示黑色和第二種顏色的棋盤圖案。

第二種顏色和每個正方形的大小可以作為文件中 Applet 的引數指定。

CheckerApplet 在 init() 方法中獲取其引數。它也可以在 paint() 方法中獲取其引數。但是,在 Applet 啟動時一次獲取值並儲存設定,而不是在每次重新整理時獲取值和儲存設定,這很方便且高效。

Applet 檢視器或瀏覽器會呼叫其執行的每個 Applet 的 init() 方法。檢視器會呼叫 init() 一次,立即載入 Applet 後。(Applet.init() 實現不執行任何操作。)覆蓋預設實現以插入自定義初始化程式碼。

Applet.getParameter() 方法獲取給定引數名稱的引數(引數的值始終是字串)。如果該值是數字或其他非字元資料,則必須分析該字串。

以下是 CheckerApplet.java 的框架:

import java.applet.*;
import java.awt.*;

public class CheckerApplet extends Applet {
   int squareSize = 50;   // initialized to default size
   public void init() {}
   private void parseSquareSize (String param) {}
   private Color parseColor (String param) {}
   public void paint (Graphics g) {}
}

以下是 CheckerApplet 的 init() 和私有 parseSquareSize() 方法:

public void init () {
   String squareSizeParam = getParameter ("squareSize");
   parseSquareSize (squareSizeParam);
   
   String colorParam = getParameter ("color");
   Color fg = parseColor (colorParam);
   
   setBackground (Color.black);
   setForeground (fg);
}

private void parseSquareSize (String param) {
   if (param == null) return;
   try {
      squareSize = Integer.parseInt (param);
   } catch (Exception e) {
      // Let default value remain
   }
}

Applet 呼叫 parseSquareSize() 來分析 squareSize 引數。parseSquareSize() 呼叫庫方法 Integer.parseInt(),該方法分析字串並返回整數。每當其引數無效時,Integer.parseInt() 都會引發異常。

因此,parseSquareSize() 會捕獲異常,而不是允許 Applet 因無效輸入而失敗。

Applet 呼叫 parseColor() 將顏色引數解析為 Color 值。parseColor() 執行一系列字串比較,以將引數值與預定義顏色的名稱匹配。您需要實現這些方法才能使此 Applet 工作。

指定 Applet 引數

以下是一個 HTML 檔案的示例,其中嵌入了 CheckerApplet。HTML 檔案透過 <param> 標記指定 Applet 的兩個引數。

示例

<html>
   <title>Checkerboard Applet</title>
   <hr>
   <applet code = "CheckerApplet.class" width = "480" height = "320">
      <param name = "color" value = "blue">
      <param name = "squaresize" value = "30">
   </applet>
   <hr>
</html>

**注意** - 引數名稱不區分大小寫。

應用程式轉換為 Applet

可以輕鬆地將圖形化 Java 應用程式(即使用 AWT 並且可以使用 Java 程式啟動器啟動的應用程式)轉換為可以嵌入網頁的 Applet。

以下是將應用程式轉換為 Applet 的具體步驟。

  • 建立一個 HTML 頁面,其中包含載入 Applet 程式碼的相應標記。

  • 提供 JApplet 類的子類。使此類為 public。否則,將無法載入 Applet。

  • 消除應用程式中的 main 方法。不要為應用程式構造框架視窗。您的應用程式將在瀏覽器內顯示。

  • 將任何初始化程式碼從框架視窗建構函式移動到 Applet 的 init 方法。您無需顯式構造 Applet 物件。瀏覽器會為您例項化它並呼叫 init 方法。

  • 刪除對 setSize 的呼叫;對於 Applet,大小調整是使用 HTML 檔案中的 width 和 height 引數完成的。

  • 刪除對 setDefaultCloseOperation 的呼叫。無法關閉 Applet;瀏覽器退出時,它會終止。

  • 如果應用程式呼叫 setTitle,則消除對該方法的呼叫。Applet 不能有標題欄。(當然,您可以使用 HTML title 標記為網頁本身新增標題。)

  • 不要呼叫 setVisible(true)。Applet 會自動顯示。

事件處理

Applet 從 Container 類繼承一組事件處理方法。Container 類定義了幾個方法,例如 processKeyEvent 和 processMouseEvent,用於處理特定型別的事件,然後是一個名為 processEvent 的通配方法。

為了對事件做出反應,Applet 必須覆蓋相應的特定於事件的方法。

示例

import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.applet.Applet;
import java.awt.Graphics;

public class ExampleEventHandling extends Applet implements MouseListener {
   StringBuffer strBuffer;

   public void init() {
      addMouseListener(this);
      strBuffer = new StringBuffer();
      addItem("initializing the apple ");
   }

   public void start() {
      addItem("starting the applet ");
   }

   public void stop() {
      addItem("stopping the applet ");
   }

   public void destroy() {
      addItem("unloading the applet");
   }

   void addItem(String word) {
      System.out.println(word);
      strBuffer.append(word);
      repaint();
   }

   public void paint(Graphics g) {
      // Draw a Rectangle around the applet's display area.
      g.drawRect(0, 0, 
      getWidth() - 1,
      getHeight() - 1);

      // display the string inside the rectangle.
      g.drawString(strBuffer.toString(), 10, 20);
   }

   
   public void mouseEntered(MouseEvent event) {
   }
   public void mouseExited(MouseEvent event) {
   }
   public void mousePressed(MouseEvent event) {
   }
   public void mouseReleased(MouseEvent event) {
   }
   public void mouseClicked(MouseEvent event) {
      addItem("mouse clicked! ");
   }
}

現在,讓我們如下呼叫此 Applet:

<html>
   <title>Event Handling</title>
   <hr>
   <applet code = "ExampleEventHandling.class" 
      width = "300" height = "300">
   </applet>
   <hr>
</html>

小程式最初會顯示“正在初始化小程式。正在啟動小程式”。然後,單擊矩形內部後,還會顯示“滑鼠單擊”。

顯示影像

小程式可以顯示 GIF、JPEG、BMP 等格式的影像。要在小程式中顯示影像,可以使用 `java.awt.Graphics` 類中的 `drawImage()` 方法。

下面是一個顯示影像所有步驟的示例:

示例

import java.applet.*;
import java.awt.*;
import java.net.*;

public class ImageDemo extends Applet {
   private Image image;
   private AppletContext context;
   
   public void init() {
      context = this.getAppletContext();
      String imageURL = this.getParameter("image");
      if(imageURL == null) {
         imageURL = "java.jpg";
      }
      try {
         URL url = new URL(this.getDocumentBase(), imageURL);
         image = context.getImage(url);
      } catch (MalformedURLException e) {
         e.printStackTrace();
         // Display in browser status bar
         context.showStatus("Could not load image!");
      }
   }
   
   public void paint(Graphics g) {
      context.showStatus("Displaying image");
      g.drawImage(image, 0, 0, 200, 84, null);
      g.drawString("www.javalicense.com", 35, 100);
   }  
}

現在,讓我們如下呼叫此 Applet:

<html>
   <title>The ImageDemo applet</title>
   <hr>
   <applet code = "ImageDemo.class" width = "300" height = "200">
      <param name = "image" value = "java.jpg">
   </applet>
   <hr>
</html>

播放音訊

小程式可以播放由 `java.applet` 包中的 `AudioClip` 介面表示的音訊檔案。`AudioClip` 介面具有三個方法,包括:

  • `public void play()` - 從頭開始播放音訊剪輯一次。

  • `public void loop()` - 使音訊剪輯連續重播。

  • `public void stop()` - 停止播放音訊剪輯。

要獲得 `AudioClip` 物件,必須呼叫 `Applet` 類的 `getAudioClip()` 方法。`getAudioClip()` 方法會立即返回,無論 URL 是否解析為實際的音訊檔案。只有在嘗試播放音訊剪輯時才會下載音訊檔案。

下面是一個播放音訊所有步驟的示例:

示例

import java.applet.*;
import java.awt.*;
import java.net.*;

public class AudioDemo extends Applet {
   private AudioClip clip;
   private AppletContext context;
   
   public void init() {
      context = this.getAppletContext();
      String audioURL = this.getParameter("audio");
      if(audioURL == null) {
         audioURL = "default.au";
      }
      try {
         URL url = new URL(this.getDocumentBase(), audioURL);
         clip = context.getAudioClip(url);
      } catch (MalformedURLException e) {
         e.printStackTrace();
         context.showStatus("Could not load audio file!");
      }
   }
   
   public void start() {
      if(clip != null) {
         clip.loop();
      }
   }
   
   public void stop() {
      if(clip != null) {
         clip.stop();
      }
   }
}

現在,讓我們如下呼叫此 Applet:

<html>
   <title>The ImageDemo applet</title>
   <hr>
   <applet code = "ImageDemo.class" width = "0" height = "0">
      <param name = "audio" value = "test.wav">
   </applet>
   <hr>
</html>

您可以使用電腦上的 test.wav 檔案測試上述示例。

Java - 文件註釋

Java 語言支援三種類型的註釋:

序號 註釋和描述
1

/* text */

編譯器會忽略從 /* 到 */ 之間的所有內容。

2

//text

編譯器會忽略從 // 到行尾的所有內容。

3

/** documentation */

這是一個文件註釋,通常稱為 **doc 註釋**。**JDK javadoc** 工具在準備自動生成的文件時會使用 *doc 註釋*。

本章將詳細解釋 Javadoc。我們將瞭解如何使用 Javadoc 為 Java 程式碼生成有用的文件。

什麼是 Javadoc?

Javadoc 是 JDK 附帶的一個工具,用於根據 Java 原始碼生成 HTML 格式的 Java 程式碼文件,這需要以預定義格式編寫文件。

下面是一個簡單的示例,其中 /*….*/ 內部的行是 Java 多行註釋。類似地,//之前的行是 Java 單行註釋。

示例

/**
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31 
*/
public class HelloWorld {

   public static void main(String[] args) {
      // Prints Hello, World! on standard output.
      System.out.println("Hello World!");
   }
}

輸出

Hello World!

您可以在描述部分包含所需的 HTML 標籤。例如,以下示例使用 <h1>....</h1> 用於標題,<p> 用於建立段落換行:

示例

/**
* <h1>Hello, World!</h1>
* The HelloWorld program implements an application that
* simply displays "Hello World!" to the standard output.
* <p>
* Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
* 
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31 
*/
public class HelloWorld {

   public static void main(String[] args) {
      // Prints Hello, World! on standard output.
      System.out.println("Hello World!");
   }
}

輸出

Hello World!

Javadoc 標籤

Javadoc 工具識別以下標籤:

標籤 描述 語法
@author 新增類的作者。 @author name-text
{@code} 以程式碼字型顯示文字,而不將其解釋為 HTML 標記或巢狀的 javadoc 標籤。 {@code text}
{@docRoot} 表示從任何生成的頁面到生成的文件根目錄的相對路徑。 {@docRoot}
@deprecated 新增一條註釋,指示不再應使用此 API。 @deprecated deprecatedtext
@exception 在生成的文件中新增一個 **Throws** 小標題,其中包含類名和描述文字。 @exception class-name description
{@inheritDoc} 從最近的可繼承類或可實現介面繼承註釋。 從直接父類繼承註釋。
{@link} 插入一個內聯連結,其可見文字標籤指向指定包、類或引用的類的成員名稱的文件。 {@link package.class#member label}
{@linkplain} 與 {@link} 相同,只是連結的標籤以純文字而不是程式碼字型顯示。 {@linkplain package.class#member label}
@param 在“引數”部分新增一個引數,其中包含指定的 parameter-name 後跟指定的描述。 @param parameter-name description
@return 新增一個包含描述文字的“返回”部分。 @return description
@see 新增一個“另請參見”標題,其中包含指向引用的連結或文字條目。 @see reference
@serial 用於預設可序列化欄位的 doc 註釋中。 @serial field-description | include | exclude
@serialData 記錄由 writeObject( ) 或 writeExternal( ) 方法寫入的資料。 @serialData data-description
@serialField 記錄 ObjectStreamField 元件。 @serialField field-name field-type field-description
@since 在生成的文件中新增一個包含指定 since-text 的“自”標題。 @since release
@throws @throws 和 @exception 標籤是同義詞。 @throws class-name description
{@value} 當 {@value} 用於靜態欄位的 doc 註釋中時,它會顯示該常量的值。 {@value package.class#field}
@version 當使用 -version 選項時,在生成的文件中新增一個包含指定 version-text 的“版本”小標題。 @version version-text

示例

以下程式使用了一些可用於文件註釋的重要標籤。您可以根據需要使用其他標籤。

關於 AddNum 類的文件將生成在 HTML 檔案 AddNum.html 中,但同時也會建立一個名為 index.html 的主檔案。

import java.io.*;

/**
* <h1>Add Two Numbers!</h1>
* The AddNum program implements an application that
* simply adds two given integer numbers and Prints
* the output on the screen.
* <p>
* <b>Note:</b> Giving proper comments in your program makes it more
* user friendly and it is assumed as a high quality code.
*
* @author  Zara Ali
* @version 1.0
* @since   2014-03-31
*/
public class AddNum {
   /**
   * This method is used to add two integers. This is
   * a the simplest form of a class method, just to
   * show the usage of various javadoc Tags.
   * @param numA This is the first paramter to addNum method
   * @param numB  This is the second parameter to addNum method
   * @return int This returns sum of numA and numB.
   */
   public int addNum(int numA, int numB) {
      return numA + numB;
   }

   /**
   * This is the main method which makes use of addNum method.
   * @param args Unused.
   * @return Nothing.
   * @exception IOException On input error.
   * @see IOException
   */

   public static void main(String args[]) throws IOException {
      AddNum obj = new AddNum();
      int sum = obj.addNum(10, 20);

      System.out.println("Sum of 10 and 20 is :" + sum);
   }
}

讓我們編譯並執行上述程式,這將產生以下結果:

Sum of 10 and 20 is :30

現在,使用 javadoc 實用程式處理上述 AddNum.java 檔案,如下所示:

$ javadoc AddNum.java
Loading source file AddNum.java...
Constructing Javadoc information...
Standard Doclet version 1.7.0_51
Building tree for all the packages and classes...
Generating /AddNum.html...
AddNum.java:36: warning - @return tag cannot be used in method with void return type.
Generating /package-frame.html...
Generating /package-summary.html...
Generating /package-tree.html...
Generating /constant-values.html...
Building index for all the packages and classes...
Generating /overview-tree.html...
Generating /index-all.html...
Generating /deprecated-list.html...
Building index for all classes...
Generating /allclasses-frame.html...
Generating /allclasses-noframe.html...
Generating /index.html...
Generating /help-doc.html...
1 warning
$

您可以在此處檢視所有生成的文件:AddNum。如果您使用的是 JDK 1.7,則 javadoc 不會生成很好的 **stylesheet.css**,因此我們建議您從以下地址下載並使用標準樣式表:

https://docs.oracle.com/javase/7/docs/api/stylesheet.css

廣告