Java高級部分


Java 數據結構

Java工具包提供了強大的數據結構。在Java中的數據結構主要包括以下幾種接口和類:

  • 枚舉(Enumeration)
  • 位集合(BitSet)
  • 向量(Vector)
  • 棧(Stack)
  • 字典(Dictionary)
  • 哈希表(Hashtable)
  • 屬性(Properties)

以上這些類是傳統遺留的,在Java2中引入了一種新的框架-集合框架(Collection),我們后面再討論。


枚舉(Enumeration)

枚舉(Enumeration)接口雖然它本身不屬於數據結構,但它在其他數據結構的范疇里應用很廣。 枚舉(The Enumeration)接口定義了一種從數據結構中取回連續元素的方式。

例如,枚舉定義了一個叫nextElement 的方法,該方法用來得到一個包含多元素的數據結構的下一個元素。


位集合(BitSet)

位集合類實現了一組可以單獨設置和清除的位或標志。

該類在處理一組布爾值的時候非常有用,你只需要給每個值賦值一"位",然后對位進行適當的設置或清除,就可以對布爾值進行操作了。


向量(Vector)

向量(Vector)類和傳統數組非常相似,但是Vector的大小能根據需要動態的變化。

和數組一樣,Vector對象的元素也能通過索引訪問。

使用Vector類最主要的好處就是在創建對象的時候不必給對象指定大小,它的大小會根據需要動態的變化。


棧(Stack)

棧(Stack)實現了一個后進先出(LIFO)的數據結構。

你可以把棧理解為對象的垂直分布的棧,當你添加一個新元素時,就將新元素放在其他元素的頂部。

當你從棧中取元素的時候,就從棧頂取一個元素。換句話說,最后進棧的元素最先被取出。


字典(Dictionary)

字典(Dictionary) 類是一個抽象類,它定義了鍵映射到值的數據結構。

當你想要通過特定的鍵而不是整數索引來訪問數據的時候,這時候應該使用Dictionary。

由於Dictionary類是抽象類,所以它只提供了鍵映射到值的數據結構,而沒有提供特定的實現。


哈希表(Hashtable)

Hashtable類提供了一種在用戶定義鍵結構的基礎上來組織數據的手段。

例如,在地址列表的哈希表中,你可以根據郵政編碼作為鍵來存儲和排序數據,而不是通過人名。

哈希表鍵的具體含義完全取決於哈希表的使用情景和它包含的數據。


屬性(Properties)

Properties 繼承於 Hashtable.Properties 類表示了一個持久的屬性集.屬性列表中每個鍵及其對應值都是一個字符串。

Properties 類被許多Java類使用。例如,在獲取環境變量時它就作為System.getProperties()方法的返回值。

Java 集合框架

早在 Java 2 中之前,Java 就提供了特設類。比如:Dictionary, Vector, Stack, 和 Properties 這些類用來存儲和操作對象組。

雖然這些類都非常有用,但是它們缺少一個核心的,統一的主題。由於這個原因,使用 Vector 類的方式和使用 Properties 類的方式有着很大不同。

集合框架被設計成要滿足以下幾個目標。

  • 該框架必須是高性能的。基本集合(動態數組,鏈表,樹,哈希表)的實現也必須是高效的。

  • 該框架允許不同類型的集合,以類似的方式工作,具有高度的互操作性。

  • 對一個集合的擴展和適應必須是簡單的。

為此,整個集合框架就圍繞一組標准接口而設計。你可以直接使用這些接口的標准實現,諸如: LinkedListHashSet, 和 TreeSet 等,除此之外你也可以通過這些接口實現自己的集合。

從上面的集合框架圖可以看到,Java 集合框架主要包括兩種類型的容器,一種是集合(Collection),存儲一個元素集合,另一種是圖(Map),存儲鍵/值對映射。Collection 接口又有 3 種子類型,List、Set 和 Queue,再下面是一些抽象類,最后是具體實現類,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。

集合框架是一個用來代表和操縱集合的統一架構。所有的集合框架都包含如下內容:

  • 接口:是代表集合的抽象數據類型。例如 Collection、List、Set、Map 等。之所以定義多個接口,是為了以不同的方式操作集合對象

  • 實現(類):是集合接口的具體實現。從本質上講,它們是可重復使用的數據結構,例如:ArrayList、LinkedList、HashSet、HashMap。

  • 算法:是實現集合接口的對象里的方法執行的一些有用的計算,例如:搜索和排序。這些算法被稱為多態,那是因為相同的方法可以在相似的接口上有着不同的實現。

除了集合,該框架也定義了幾個 Map 接口和類。Map 里存儲的是鍵/值對。盡管 Map 不是集合,但是它們完全整合在集合中。

集合框架體系如圖所示

Java 集合框架提供了一套性能優良,使用方便的接口和類,java集合框架位於java.util包中, 所以當使用集合框架的時候需要進行導包。


集合接口

集合框架定義了一些接口。本節提供了每個接口的概述:

序號 接口描述
1 Collection 接口

Collection 是最基本的集合接口,一個 Collection 代表一組 Object,即 Collection 的元素, Java不提供直接繼承自Collection的類,只提供繼承於的子接口(如List和set)。

Collection 接口存儲一組不唯一,無序的對象。

2 List 接口

List接口是一個有序的 Collection,使用此接口能夠精確的控制每個元素插入的位置,能夠通過索引(元素在List中位置,類似於數組的下標)來訪問List中的元素,第一個元素的索引為 0,而且允許有相同的元素。

List 接口存儲一組不唯一,有序(插入順序)的對象。

3 Set

Set 具有與 Collection 完全一樣的接口,只是行為上不同,Set 不保存重復的元素。

Set 接口存儲一組唯一,無序的對象。

4 SortedSet 
繼承於Set保存有序的集合。
5 Map

Map 接口存儲一組鍵值對象,提供key(鍵)到value(值)的映射。

6 Map.Entry 
描述在一個Map中的一個元素(鍵/值對)。是一個Map的內部類。
7 SortedMap
繼承於 Map,使 Key 保持在升序排列。
8 Enumeration
這是一個傳統的接口和定義的方法,通過它可以枚舉(一次獲得一個)對象集合中的元素。這個傳統接口已被迭代器取代。

Set和List的區別

  • 1. Set 接口實例存儲的是無序的,不重復的數據。List 接口實例存儲的是有序的,可以重復的元素。

  • 2. Set檢索效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變 <實現類有HashSet,TreeSet>

  • 3. List和數組類似,可以動態增長,根據實際存儲的數據的長度自動增長List的長度。查找元素效率高,插入刪除效率低,因為會引起其他元素位置改變 <實現類有ArrayList,LinkedList,Vector> 。


集合實現類(集合類)

Java提供了一套實現了Collection接口的標准集合類。其中一些是具體類,這些類可以直接拿來使用,而另外一些是抽象類,提供了接口的部分實現。

標准集合類匯總於下表:

序號 類描述
1 AbstractCollection 
實現了大部分的集合接口。
2 AbstractList 
繼承於AbstractCollection 並且實現了大部分List接口。
3 AbstractSequentialList 
繼承於 AbstractList ,提供了對數據元素的鏈式訪問而不是隨機訪問。
4 LinkedList

該類實現了List接口,允許有null(空)元素。主要用於創建鏈表數據結構,該類沒有同步方法,如果多個線程同時訪問一個List,則必須自己實現訪問同步,解決方法就是在創建List時候構造一個同步的List。例如:

Listlist=Collections.synchronizedList(newLinkedList(...));

LinkedList 查找效率低。

5 ArrayList

該類也是實現了List的接口,實現了可變大小的數組,隨機訪問和遍歷元素時,提供更好的性能。該類也是非同步的,在多線程的情況下不要使用。ArrayList 增長當前長度的50%,插入刪除效率低。

6 AbstractSet 
繼承於AbstractCollection 並且實現了大部分Set接口。
7 HashSet

該類實現了Set接口,不允許出現重復元素,不保證集合中元素的順序,允許包含值為null的元素,但最多只能一個。

8 LinkedHashSet
具有可預知迭代順序的 Set 接口的哈希表和鏈接列表實現。
9 TreeSet

該類實現了Set接口,可以實現排序等功能。

10 AbstractMap 
實現了大部分的Map接口。
11 HashMap 
HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射。
該類實現了Map接口,根據鍵的HashCode值存儲數據,具有很快的訪問速度,最多允許一條記錄的鍵為null,不支持線程同步。
12 TreeMap 
繼承了AbstractMap,並且使用一顆樹。
13 WeakHashMap 
繼承AbstractMap類,使用弱密鑰的哈希表。
14 LinkedHashMap 
繼承於HashMap,使用元素的自然順序對元素進行排序.
15 IdentityHashMap 
繼承AbstractMap類,比較文檔時使用引用相等。

在前面的教程中已經討論通過java.util包中定義的類,如下所示:

序號 類描述
1 Vector 

該類和ArrayList非常相似,但是該類是同步的,可以用在多線程的情況,該類允許設置默認的增長長度,默認擴容方式為原來的2倍。

2 Stack 
棧是Vector的一個子類,它實現了一個標准的后進先出的棧。
3 Dictionary 
Dictionary 類是一個抽象類,用來存儲鍵/值對,作用和Map類相似。
4 Hashtable 

Hashtable 是 Dictionary(字典) 類的子類,位於 java.util 包中。

5 Properties 
Properties 繼承於 Hashtable,表示一個持久的屬性集,屬性列表中每個鍵及其對應值都是一個字符串。
6 BitSet
一個Bitset類創建一種特殊類型的數組來保存位值。BitSet中數組大小會隨需要增加。

集合算法

集合框架定義了幾種算法,可用於集合和映射。這些算法被定義為集合類的靜態方法。

在嘗試比較不兼容的類型時,一些方法能夠拋出 ClassCastException異常。當試圖修改一個不可修改的集合時,拋出UnsupportedOperationException異常。

集合定義三個靜態的變量:EMPTY_SET,EMPTY_LIST,EMPTY_MAP的。這些變量都不可改變。

 

序號 算法描述
1 Collection Algorithms
這里是一個列表中的所有算法實現。

如何使用迭代器

通常情況下,你會希望遍歷一個集合中的元素。例如,顯示集合中的每個元素。

一般遍歷數組都是采用for循環或者增強for,這兩個方法也可以用在集合框架,但是還有一種方法是采用迭代器遍歷集合框架,它是一個對象,實現了Iterator 接口或ListIterator接口。

迭代器,使你能夠通過循環來得到或刪除集合的元素。ListIterator 繼承了Iterator,以允許雙向遍歷列表和修改元素。

 

序號 迭代器方法描述
1 使用 Java Iterator
這里通過實例列出Iterator和listIterator接口提供的所有方法。


如何使用比較器

TreeSet和TreeMap的按照排序順序來存儲元素. 然而,這是通過比較器來精確定義按照什么樣的排序順序。

這個接口可以讓我們以不同的方式來排序一個集合。

 

序號 比較器方法描述
1 使用 Java Comparator
這里通過實例列出Comparator接口提供的所有方法

Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。

 

泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。

假定我們有這樣一個需求:寫一個排序方法,能夠對整型數組、字符串數組甚至其他任何類型的數組進行排序,該如何實現?

答案是可以使用 Java 泛型

使用 Java 泛型的概念,我們可以寫一個泛型方法來對一個對象數組排序。然后,調用該泛型方法來對整型數組、浮點數數組、字符串數組等進行排序。


泛型方法

你可以寫一個泛型方法,該方法在調用時可以接收不同類型的參數。根據傳遞給泛型方法的參數類型,編譯器適當地處理每一個方法調用。

下面是定義泛型方法的規則:

  • 所有泛型方法聲明都有一個類型參數聲明部分(由尖括號分隔),該類型參數聲明部分在方法返回類型之前(在下面例子中的<E>)。
  • 每一個類型參數聲明部分包含一個或多個類型參數,參數間用逗號隔開。一個泛型參數,也被稱為一個類型變量,是用於指定一個泛型類型名稱的標識符。
  • 類型參數能被用來聲明返回值類型,並且能作為泛型方法得到的實際參數類型的占位符。
  • 泛型方法體的聲明和其他方法一樣。注意類型參數只能代表引用型類型,不能是原始類型(像int,double,char的等)。

泛型類

泛型類的聲明和非泛型類的聲明類似,除了在類名后面添加了類型參數聲明部分。

和泛型方法一樣,泛型類的類型參數聲明部分也包含一個或多個類型參數,參數間用逗號隔開。一個泛型參數,也被稱為一個類型變量,是用於指定一個泛型類型名稱的標識符。因為他們接受一個或多個參數,這些類被稱為參數化的類或參數化的類型。

類型通配符

1、類型通配符一般是使用?代替具體的類型參數。例如 List<?> 在邏輯上是List<String>,List<Integer> 等所有List<具體類型實參>的父類。

Java 序列化

Java 提供了一種對象序列化的機制,該機制中,一個對象可以被表示為一個字節序列,該字節序列包括該對象的數據、有關對象的類型的信息和存儲在對象中數據的類型。

將序列化對象寫入文件之后,可以從文件中讀取出來,並且對它進行反序列化,也就是說,對象的類型信息、對象的數據,還有對象中的數據類型可以用來在內存中新建對象。

整個過程都是 Java 虛擬機(JVM)獨立的,也就是說,在一個平台上序列化的對象可以在另一個完全不同的平台上反序列化該對象。

類 ObjectInputStream 和 ObjectOutputStream 是高層次的數據流,它們包含反序列化和序列化對象的方法。

序列化對象

ObjectOutputStream 類用來序列化一個對象,如下的 SerializeDemo 例子實例化了一個 Employee 對象,並將該對象序列化到一個文件中。

該程序執行后,就創建了一個名為 employee.ser 文件。該程序沒有任何輸出,但是你可以通過代碼研讀來理解程序的作用。

 

Java 網絡編程

網絡編程是指編寫運行在多個設備(計算機)的程序,這些設備都通過網絡連接起來。

java.net 包中 J2SE 的 API 包含有類和接口,它們提供低層次的通信細節。你可以直接使用這些類和接口,來專注於解決問題,而不用關注通信細節。

java.net 包中提供了兩種常見的網絡協議的支持:

  • TCP:TCP 是傳輸控制協議的縮寫,它保障了兩個應用程序之間的可靠通信。通常用於互聯網協議,被稱 TCP / IP。

  • UDP:UDP 是用戶數據報協議的縮寫,一個無連接的協議。提供了應用程序之間要發送的數據的數據包。

本教程主要講解以下兩個主題。

  • Socket 編程:這是使用最廣泛的網絡概念,它已被解釋地非常詳細。

  • URL 處理:這部分會在另外的篇幅里講,點擊這里更詳細地了解在 Java 語言中的 URL 處理


Socket 編程

套接字使用TCP提供了兩台計算機之間的通信機制。 客戶端程序創建一個套接字,並嘗試連接服務器的套接字。

當連接建立時,服務器會創建一個 Socket 對象。客戶端和服務器現在可以通過對 Socket 對象的寫入和讀取來進行通信。

java.net.Socket 類代表一個套接字,並且 java.net.ServerSocket 類為服務器程序提供了一種來監聽客戶端,並與他們建立連接的機制。

以下步驟在兩台計算機之間使用套接字建立TCP連接時會出現:

  • 服務器實例化一個 ServerSocket 對象,表示通過服務器上的端口通信。

  • 服務器調用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到服務器上給定的端口。

  • 服務器正在等待時,一個客戶端實例化一個 Socket 對象,指定服務器名稱和端口號來請求連接。

  • Socket 類的構造函數試圖將客戶端連接到指定的服務器和端口號。如果通信被建立,則在客戶端創建一個 Socket 對象能夠與服務器進行通信。

  • 在服務器端,accept() 方法返回服務器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。

連接建立后,通過使用 I/O 流在進行通信,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務器端的輸入流,而客戶端的輸入流連接到服務器端的輸出流。

TCP 是一個雙向的通信協議,因此數據可以通過兩個數據流在同一時間發送.以下是一些類提供的一套完整的有用的方法來實現 socket。


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
使用指定的端口、偵聽 backlog 和要綁定到的本地 IP 地址創建服務器。
4 public ServerSocket() throws IOException
創建非綁定服務器套接字。

創建非綁定服務器套接字。 如果 ServerSocket 構造方法沒有拋出異常,就意味着你的應用程序已經成功綁定到指定的端口,並且偵聽客戶端請求。

這里有一些 ServerSocket 類的常用方法:

序號 方法描述
1 public int getLocalPort()
  返回此套接字在其上偵聽的端口。
2 public Socket accept() throws IOException
偵聽並接受到此套接字的連接。
3 public void setSoTimeout(int timeout)
 通過指定超時值啟用/禁用 SO_TIMEOUT,以毫秒為單位。
4 public void bind(SocketAddress host, int backlog)
將 ServerSocket 綁定到特定地址(IP 地址和端口號)。

Socket 類的方法

java.net.Socket 類代表客戶端和服務器都用來互相溝通的套接字。客戶端要獲取一個 Socket 對象通過實例化 ,而 服務器獲得一個 Socket 對象則通過 accept() 方法的返回值。

Socket 類有五個構造方法.

序號 方法描述
1 public Socket(String host, int port) throws UnknownHostException, IOException.
創建一個流套接字並將其連接到指定主機上的指定端口號。
2 public Socket(InetAddress host, int port) throws IOException
創建一個流套接字並將其連接到指定 IP 地址的指定端口號。
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.
創建一個套接字並將其連接到指定遠程地址上的指定遠程端口。
5 public Socket()
通過系統默認類型的 SocketImpl 創建未連接套接字

當 Socket 構造方法返回,並沒有簡單的實例化了一個 Socket 對象,它實際上會嘗試連接到指定的服務器和端口。

下面列出了一些感興趣的方法,注意客戶端和服務器端都有一個 Socket 對象,所以無論客戶端還是服務端都能夠調用這些方法。

序號 方法描述
1 public void connect(SocketAddress host, int timeout) throws IOException
將此套接字連接到服務器,並指定一個超時值。
2 public InetAddress getInetAddress()
 返回套接字連接的地址。
3 public int getPort()
返回此套接字連接到的遠程端口。
4 public int getLocalPort()
返回此套接字綁定到的本地端口。
5 public SocketAddress getRemoteSocketAddress()
返回此套接字連接的端點的地址,如果未連接則返回 null。
6 public InputStream getInputStream() throws IOException
返回此套接字的輸入流。
7 public OutputStream getOutputStream() throws IOException
返回此套接字的輸出流。
8 public void close() throws IOException
關閉此套接字。

InetAddress 類的方法

這個類表示互聯網協議(IP)地址。下面列出了 Socket 編程時比較有用的方法:

序號 方法描述
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 getLocalHost()
返回本地主機。
7 String toString()
將此 IP 地址轉換為 String。

Socket 客戶端實例

如下的 GreetingClient 是一個客戶端程序,該程序通過 socket 連接到服務器並發送一個請求,然后等待一個響應。

GreetingClient.java 文件代碼:

// 文件名 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("連接到主機:" + serverName + " ,端口號:" + port); Socket client = new Socket(serverName, port); System.out.println("遠程主機地址:" + 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("服務器響應: " + in.readUTF()); client.close(); }catch(IOException e) { e.printStackTrace(); } } }

Socket 服務端實例

如下的GreetingServer 程序是一個服務器端應用程序,使用 Socket 來監聽一個指定的端口。

GreetingServer.java 文件代碼:

// 文件名 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("等待遠程連接,端口號為:" + serverSocket.getLocalPort() + "..."); Socket server = serverSocket.accept(); System.out.println("遠程主機地址:" + server.getRemoteSocketAddress()); DataInputStream in = new DataInputStream(server.getInputStream()); System.out.println(in.readUTF()); DataOutputStream out = new DataOutputStream(server.getOutputStream()); out.writeUTF("謝謝連接我:" + 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.run(); }catch(IOException e) { e.printStackTrace(); } } }
 

Java 發送郵件

使用Java應用程序發送 E-mail 十分簡單,但是首先你應該在你的機器上安裝 JavaMail API 和Java Activation Framework (JAF) 。

  • 您可以從 Java 網站下載最新版本的 JavaMail,打開網頁右側有個 Downloads 鏈接,點擊它下載。
  • 您可以從 Java 網站下載最新版本的 JAF(版本 1.1.1)

你也可以使用本站提供的下載鏈接:

下載並解壓縮這些文件,在新創建的頂層目錄中,您會發現這兩個應用程序的一些 jar 文件。您需要把 mail.jar 和 activation.jar 文件添加到您的 CLASSPATH 中。

如果你使用第三方郵件服務器如QQ的SMTP服務器,可查看文章底部用戶認證完整的實例。


發送一封簡單的 E-mail

下面是一個發送簡單E-mail的例子。假設你的本地主機已經連接到網絡。

SendEmail.java 文件代碼:

// 文件名 SendEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendEmail { public static void main(String [] args) { // 收件人電子郵箱 String to = "abcd@gmail.com"; // 發件人電子郵箱 String from = "web@gmail.com"; // 指定發送郵件的主機為 localhost String host = "localhost"; // 獲取系統屬性 Properties properties = System.getProperties(); // 設置郵件服務器 properties.setProperty("mail.smtp.host", host); // 獲取默認session對象 Session session = Session.getDefaultInstance(properties); try{ // 創建默認的 MimeMessage 對象 MimeMessage message = new MimeMessage(session); // Set From: 頭部頭字段 message.setFrom(new InternetAddress(from)); // Set To: 頭部頭字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 頭部頭字段 message.setSubject("This is the Subject Line!"); // 設置消息體 message.setText("This is actual message"); // 發送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } }

編譯並運行這個程序來發送一封簡單的E-mail:

$ java SendEmail Sent message successfully....

如果你想發送一封e-mail給多個收件人,那么使用下面的方法來指定多個收件人ID:

void addRecipients(Message.RecipientType type, Address[] addresses) throws MessagingException

下面是對於參數的描述:

  • type:要被設置為 TO, CC 或者 BCC,這里 CC 代表抄送、BCC 代表秘密抄送。舉例:Message.RecipientType.TO

  • addresses: 這是 email ID 的數組。在指定電子郵件 ID 時,你將需要使用 InternetAddress() 方法。


發送一封 HTML E-mail

下面是一個發送 HTML E-mail 的例子。假設你的本地主機已經連接到網絡。

和上一個例子很相似,除了我們要使用 setContent() 方法來通過第二個參數為 "text/html",來設置內容來指定要發送HTML 內容。

SendHTMLEmail.java 文件代碼:

// 文件名 SendHTMLEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendHTMLEmail { public static void main(String [] args) { // 收件人電子郵箱 String to = "abcd@gmail.com"; // 發件人電子郵箱 String from = "web@gmail.com"; // 指定發送郵件的主機為 localhost String host = "localhost"; // 獲取系統屬性 Properties properties = System.getProperties(); // 設置郵件服務器 properties.setProperty("mail.smtp.host", host); // 獲取默認的 Session 對象。 Session session = Session.getDefaultInstance(properties); try{ // 創建默認的 MimeMessage 對象。 MimeMessage message = new MimeMessage(session); // Set From: 頭部頭字段 message.setFrom(new InternetAddress(from)); // Set To: 頭部頭字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 頭字段 message.setSubject("This is the Subject Line!"); // 發送 HTML 消息, 可以插入html標簽 message.setContent("<h1>This is actual message</h1>", "text/html" ); // 發送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } }

編譯並運行此程序來發送HTML e-mail:

$ java SendHTMLEmail Sent message successfully....

發送帶有附件的 E-mail

下面是一個發送帶有附件的 E-mail的例子。假設你的本地主機已經連接到網絡。

SendFileEmail.java 文件代碼:

// 文件名 SendFileEmail.java import java.util.*; import javax.mail.*; import javax.mail.internet.*; import javax.activation.*; public class SendFileEmail { public static void main(String [] args) { // 收件人電子郵箱 String to = "abcd@gmail.com"; // 發件人電子郵箱 String from = "web@gmail.com"; // 指定發送郵件的主機為 localhost String host = "localhost"; // 獲取系統屬性 Properties properties = System.getProperties(); // 設置郵件服務器 properties.setProperty("mail.smtp.host", host); // 獲取默認的 Session 對象。 Session session = Session.getDefaultInstance(properties); try{ // 創建默認的 MimeMessage 對象。 MimeMessage message = new MimeMessage(session); // Set From: 頭部頭字段 message.setFrom(new InternetAddress(from)); // Set To: 頭部頭字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 頭字段 message.setSubject("This is the Subject Line!"); // 創建消息部分 BodyPart messageBodyPart = new MimeBodyPart(); // 消息 messageBodyPart.setText("This is message body"); // 創建多重消息 Multipart multipart = new MimeMultipart(); // 設置文本消息部分 multipart.addBodyPart(messageBodyPart); // 附件部分 messageBodyPart = new MimeBodyPart(); String filename = "file.txt"; DataSource source = new FileDataSource(filename); messageBodyPart.setDataHandler(new DataHandler(source)); messageBodyPart.setFileName(filename); multipart.addBodyPart(messageBodyPart); // 發送完整消息 message.setContent(multipart ); // 發送消息 Transport.send(message); System.out.println("Sent message successfully...."); }catch (MessagingException mex) { mex.printStackTrace(); } } }

編譯並運行你的程序來發送一封帶有附件的郵件。

$ java SendFileEmail Sent message successfully....

用戶認證部分

如果需要提供用戶名和密碼給e-mail服務器來達到用戶認證的目的,你可以通過如下設置來完成:

props.put("mail.smtp.auth", "true"); props.setProperty("mail.user", "myuser"); props.setProperty("mail.password", "mypwd");

e-mail其他的發送機制和上述保持一致。

需要用戶名密碼驗證郵件發送實例:

本實例以 QQ 郵件服務器為例,你需要在登錄QQ郵箱后台在"設置"=》賬號中開啟POP3/SMTP服務 ,如下圖所示:

QQ 郵箱通過生成授權碼來設置密碼:

Java 代碼如下:

SendEmail2.java 文件代碼:

// 需要用戶名密碼郵件發送實例 //文件名 SendEmail2.java //本實例以QQ郵箱為例,你需要在qq后台設置 import java.util.Properties; import javax.mail.Authenticator; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; public class SendEmail2 { public static void main(String [] args) { // 收件人電子郵箱 String to = "xxx@qq.com"; // 發件人電子郵箱 String from = "xxx@qq.com"; // 指定發送郵件的主機為 smtp.qq.com String host = "smtp.qq.com"; //QQ 郵件服務器 // 獲取系統屬性 Properties properties = System.getProperties(); // 設置郵件服務器 properties.setProperty("mail.smtp.host", host); properties.put("mail.smtp.auth", "true"); // 獲取默認session對象 Session session = Session.getDefaultInstance(properties,new Authenticator(){ public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("xxx@qq.com", "qq郵箱授權碼"); //發件人郵件用戶名、授權碼 } }); try{ // 創建默認的 MimeMessage 對象 MimeMessage message = new MimeMessage(session); // Set From: 頭部頭字段 message.setFrom(new InternetAddress(from)); // Set To: 頭部頭字段 message.addRecipient(Message.RecipientType.TO, new InternetAddress(to)); // Set Subject: 頭部頭字段 message.setSubject("This is the Subject Line!"); // 設置消息體 message.setText("This is actual message"); // 發送消息 Transport.send(message); System.out.println("Sent message successfully....from runoob.com"); }catch (MessagingException mex) { mex.printStackTrace(); } } }

Java 多線程編程

Java 給多線程編程提供了內置的支持。 一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。

多線程是多任務的一種特別的形式,但多線程使用了更小的資源開銷。

這里定義和線程相關的另一個術語 - 進程:一個進程包括由操作系統分配的內存空間,包含一個或多個線程。一個線程不能獨立的存在,它必須是進程的一部分。一個進程一直運行,直到所有的非守護線程都結束運行后才能結束。

多線程能滿足程序員編寫高效率的程序來達到充分利用 CPU 的目的。


一個線程的生命周期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程。

下圖顯示了一個線程完整的生命周期。

  • 新建狀態:

    使用 new 關鍵字和 Thread 類或其子類建立一個線程對象后,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。

  • 就緒狀態:

    當線程對象調用了start()方法之后,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM里線程調度器的調度。

  • 運行狀態:

    如果就緒狀態的線程獲取 CPU 資源,就可以執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最為復雜,它可以變為阻塞狀態、就緒狀態和死亡狀態。

  • 阻塞狀態:

    如果一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所占用資源之后,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或獲得設備資源后可以重新進入就緒狀態。可以分為三種:

    • 等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。

    • 同步阻塞:線程在獲取 synchronized 同步鎖失敗(因為同步鎖被其他線程占用)。

    • 其他阻塞:通過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程重新轉入就緒狀態。

  • 死亡狀態:

    一個運行狀態的線程完成任務或者其他終止條件發生時,該線程就切換到終止狀態。


線程的優先級

每一個 Java 線程都有一個優先級,這樣有助於操作系統確定線程的調度順序。

Java 線程的優先級是一個整數,其取值范圍是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默認情況下,每一個線程都會分配一個優先級 NORM_PRIORITY(5)。

具有較高優先級的線程對程序更重要,並且應該在低優先級的線程之前分配處理器資源。但是,線程優先級不能保證線程執行的順序,而且非常依賴於平台。


創建一個線程

Java 提供了三種創建線程的方法:

  • 通過實現 Runnable 接口;
  • 通過繼承 Thread 類本身;
  • 通過 Callable 和 Future 創建線程。

通過實現 Runnable 接口來創建線程

創建一個線程,最簡單的方法是創建一個實現 Runnable 接口的類。

為了實現 Runnable,一個類只需要執行一個方法調用 run(),聲明如下:

 

public void run()

你可以重寫該方法,重要的是理解的 run() 可以調用其他方法,使用其他類,並聲明變量,就像主線程一樣。

在創建一個實現 Runnable 接口的類之后,你可以在類中實例化一個線程對象。

Thread 定義了幾個構造方法,下面的這個是我們經常使用的:

Thread(Runnable threadOb,String threadName);

這里,threadOb 是一個實現 Runnable 接口的類的實例,並且 threadName 指定新線程的名字。

新線程創建之后,你調用它的 start() 方法它才會運行。

void start();

通過繼承Thread來創建線程

創建一個線程的第二種方法是創建一個新的類,該類繼承 Thread 類,然后創建一個該類的實例。

繼承類必須重寫 run() 方法,該方法是新線程的入口點。它也必須調用 start() 方法才能執行。

該方法盡管被列為一種多線程實現方式,但是本質上也是實現了 Runnable 接口的一個實例。

 

Thread 方法

下表列出了Thread類的一些重要方法:

序號 方法描述
1 public void start()
使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
2 public void run()
如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作並返回。
3 public final void setName(String name)
改變線程名稱,使之與參數 name 相同。
4 public final void setPriority(int priority)
 更改線程的優先級。
5 public final void setDaemon(boolean on)
將該線程標記為守護線程或用戶線程。
6 public final void join(long millisec)
等待該線程終止的時間最長為 millis 毫秒。
7 public void interrupt()
中斷線程。
8 public final boolean isAlive()
測試線程是否處於活動狀態。

測試線程是否處於活動狀態。 上述方法是被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()
將當前線程的堆棧跟蹤打印至標准錯誤流。

通過 Callable 和 Future 創建線程

  • 1. 創建 Callable 接口的實現類,並實現 call() 方法,該 call() 方法將作為線程執行體,並且有返回值。

  • 2. 創建 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。

  • 3. 使用 FutureTask 對象作為 Thread 對象的 target 創建並啟動新線程。

  • 4. 調用 FutureTask 對象的 get() 方法來獲得子線程執行結束后的返回值。

創建線程的三種方式的對比

  • 1. 采用實現 Runnable、Callable 接口的方式創建多線程時,線程類只是實現了 Runnable 接口或 Callable 接口,還可以繼承其他類。

  • 2. 使用繼承 Thread 類的方式創建多線程時,編寫簡單,如果需要訪問當前線程,則無需使用 Thread.currentThread() 方法,直接使用 this 即可獲得當前線程。


線程的幾個主要概念

在多線程編程時,你需要了解以下幾個概念:

  • 線程同步
  • 線程間通信
  • 線程死鎖
  • 線程控制:掛起、停止和恢復

多線程的使用

有效利用多線程的關鍵是理解程序是並發執行而不是串行執行的。例如:程序中有兩個子系統需要並發執行,這時候就需要利用多線程編程。

通過對多線程的使用,可以編寫出非常高效的程序。不過請注意,如果你創建太多的線程,程序執行的效率實際上是降低了,而不是提升了。

請記住,上下文的切換開銷也很重要,如果你創建了太多的線程,CPU 花費在上下文的切換的時間將多於執行程序的時間!

Java Applet 基礎

Applet 是一種 Java 程序。它一般運行在支持 Java 的 Web 瀏覽器內。因為它有完整的 Java API支持,所以Applet 是一個全功能的 Java 應用程序。

如下所示是獨立的 Java 應用程序和 applet 程序之間重要的不同:

  • Java 中 Applet 類繼承了 java.applet.Applet 類。
  • Applet 類沒有定義 main(),所以一個 Applet 程序不會調用 main() 方法。
  • Applet 被設計為嵌入在一個 HTML 頁面。
  • 當用戶瀏覽包含 Applet 的 HTML 頁面,Applet 的代碼就被下載到用戶的機器上。
  • 要查看一個 Applet 需要 JVM。 JVM 可以是 Web 瀏覽器的一個插件,或一個獨立的運行時環境。
  • 用戶機器上的 JVM 創建一個 Applet 類的實例,並調用 Applet 生命周期過程中的各種方法。
  • Applet 有 Web 瀏覽器強制執行的嚴格的安全規則,Applet 的安全機制被稱為沙箱安全。
  • Applet 需要的其他類可以用 Java 歸檔(JAR)文件的形式下載下來。

Applet的生命周期

Applet 類中的四個方法給我們提供了一個框架,你可以在該框架上開發小程序:

  • init: 該方法的目的是為你的 Applet 提供所需的任何初始化。在 Applet 標記內的 param 標簽被處理后調用該方法。
  • start: 瀏覽器調用 init 方法后,該方法被自動調用。每當用戶從其他頁面返回到包含 Applet 的頁面時,則調用該方法。
  • stop: 當用戶從包含 Applet 的頁面移除的時候,該方法自動被調用。因此,可以在相同的 Applet 中反復調用該方法。
  • destroy: 此方法僅當瀏覽器正常關閉時調用。因為 Applet 只有在 HTML 網頁上有效,所以你不應該在用戶離開包含 Applet 的頁面后遺漏任何資源。
  • paint: 該方法在 start() 方法之后立即被調用,或者在 Applet 需要重繪在瀏覽器的時候調用。paint() 方法實際上繼承於 java.awt。

Applet 類

每一個 Applet 都是 java.applet.Applet 類的子類,基礎的 Applet 類提供了供衍生類調用的方法,以此來得到瀏覽器上下文的信息和服務。

這些方法做了如下事情:

  • 得到 Applet 的參數
  • 得到包含 Applet 的 HTML 文件的網絡位置
  • 得到 Applet 類目錄的網絡位置
  • 打印瀏覽器的狀態信息
  • 獲取一張圖片
  • 獲取一個音頻片段
  • 播放一個音頻片段
  • 調整此 Applet 的大小

除此之外,Applet 類還提供了一個接口,該接口供 Viewer 或瀏覽器來獲取 Applet 的信息,並且來控制 Applet 的執行。

Viewer 可能是:

  • 請求 Applet 作者、版本和版權的信息
  • 請求 Applet 識別的參數的描述
  • 初始化 Applet
  • 銷毀 Applet
  • 開始執行 Applet
  • 結束執行 Applet

Applet 類提供了對這些方法的默認實現,這些方法可以在需要的時候重寫。

獲得applet參數

下面的例子演示了如何使用一個 Applet 響應來設置文件中指定的參數。該 Applet 顯示了一個黑色棋盤圖案和第二種顏色。

第二種顏色和每一列的大小通過文檔中的 Applet 的參數指定。

CheckerApplet 在 init() 方法里得到它的參數。也可以在 paint() 方法里得到它的參數。然而,在 Applet 開始得到值並保存了設置,而不是每一次刷新的時候都得到值,這樣是很方便,並且高效的。

Applet viewer 或者瀏覽器在 Applet 每次運行的時候調用 init() 方法。在加載 Applet 之后,Viewer 立即調用 init() 方法(Applet.init()什么也沒做),重寫該方法的默認實現,添加一些自定義的初始化代碼。

Applet.getParameter() 方法通過給出參數名稱得到參數值。如果得到的值是數字或者其他非字符數據,那么必須解析為字符串類型。

指定 applet 參數

如下的例子是一個HTML文件,其中嵌入了 CheckerApplet 類。HTML文件通過使用 <param> 標簽的方法給 applet 指定了兩個參數。

應用程序轉換成 Applet

將圖形化的 Java 應用程序(是指,使用AWT的應用程序和使用 java 程序啟動器啟動的程序)轉換成嵌入在web頁面里的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 必須重寫合適的事件處理方法。

顯示圖片

Applet 能顯示 GIF,JPEG,BMP 等其他格式的圖片。為了在 Applet 中顯示圖片,你需要使用 java.awt.Graphics 類的drawImage()方法。

播放音頻

Applet 能通過使用 java.applet 包中的 AudioClip 接口播放音頻。AudioClip 接口定義了三個方法:

  • public void play(): 從一開始播放音頻片段一次。
  • public void loop(): 循環播放音頻片段
  • public void stop(): 停止播放音頻片段

為了得到 AudioClip 對象,你必須調用 Applet 類的 getAudioClip() 方法。無論 URL 指向的是否是一個真實的音頻文件,該方法都會立即返回結果。

 

Java 文檔注釋

Java 支持三種注釋方式。前兩種分別是 // 和 /* */,第三種被稱作說明注釋,它以 /** 開始,以 */結束。

說明注釋允許你在程序中嵌入關於程序的信息。你可以使用 javadoc 工具軟件來生成信息,並輸出到HTML文件中。

說明注釋,使你更加方便的記錄你的程序信息。


javadoc 標簽

javadoc 工具軟件識別以下標簽:

標簽 描述 示例
@author 標識一個類的作者 @author description
@deprecated 指名一個過期的類或成員 @deprecated description
{@docRoot} 指明當前文檔根目錄的路徑 Directory Path
@exception 標志一個類拋出的異常 @exception exception-name explanation
{@inheritDoc} 從直接父類繼承的注釋 Inherits a comment from the immediate surperclass.
{@link} 插入一個到另一個主題的鏈接 {@link name text}
{@linkplain} 插入一個到另一個主題的鏈接,但是該鏈接顯示純文本字體 Inserts an in-line link to another topic.
@param 說明一個方法的參數 @param parameter-name explanation
@return 說明返回值類型 @return explanation
@see 指定一個到另一個主題的鏈接 @see anchor
@serial 說明一個序列化屬性 @serial description
@serialData 說明通過writeObject( ) 和 writeExternal( )方法寫的數據 @serialData description
@serialField 說明一個ObjectStreamField組件 @serialField name type description
@since 標記當引入一個特定的變化時 @since release
@throws 和 @exception標簽一樣. The @throws tag has the same meaning as the @exception tag.
{@value} 顯示常量的值,該常量必須是static屬性。 Displays the value of a constant, which must be a static field.
@version 指定類的版本 @version info

javadoc 輸出什么

javadoc 工具將你 Java 程序的源代碼作為輸入,輸出一些包含你程序注釋的HTML文件。

每一個類的信息將在獨自的HTML文件里。javadoc 也可以輸出繼承的樹形結構和索引。

由於 javadoc 的實現不同,工作也可能不同,你需要檢查你的 Java 開發系統的版本等細節,選擇合適的 Javadoc 版本。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM