|
常用鏈接
留言簿(8)
隨筆分類
隨筆檔案
相冊
搜索
最新評論

閱讀排行榜
評論排行榜
Powered by: 博客園
模板提供:滬江博客
|
|
|
|
|
發(fā)新文章 |
|
|
import java.sql.*;
public class ConnectionTest {
public static void main(String[] arg) {
try {
Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
String url = "jdbc:oracle:thin:@localhost:1521:TEST";
String username = "SMNA";
String password = "SMNA";
Connection conn = DriverManager.getConnection(url, username,
password);
Statement stmt = conn.createStatement();
String sql = "select * from tb_smna_user_info";
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out
.println("Database test" + rs.getString("column_name") == null ? ""
: rs.getString("column_name"));
}
} catch (Exception e) {
System.out.println("err");
}
}
}
注意要導入
jdbc
的驅動
ojdbc14.jar
到
D:\tomcat\common\lib
下
在裝了
eclipse
后,又裝了
oracle
,然后
eclipse
就啟動不了了,總是報
jdk
的版本問題。 上網(wǎng)查了下才知道是
oracle
的
jdk
版本是
1.3
,
eclipse
用的是jdk
1.4,
需要將
1.4
的
jdk\bin
的路徑在
path
里放在
oracle
的
jdk
路徑的前面才行。
java中的io中的(input/output)stream無非就是包括基于字符的stream、基于字節(jié)的stream和把字節(jié)導向的stream轉換 字符為導向的stream的stream。(很難理解么?) 以字節(jié)為導向的stream------InputStream/OutputStream InputStream 和 OutputStream是兩個abstact類,對于字節(jié)為導向的stream都擴展這兩個雞肋(基類^_^); --InputStream ByteArrayInputStream -- 把內存中的一個緩沖區(qū)作為InputStream使用.
construct---ByteArrayInputStream(byte[])創(chuàng)建一個新字節(jié)數(shù)組輸入流,它從指定字節(jié)數(shù)組中讀取數(shù)據(jù)。 ---ByteArrayInputStream(byte[], int, int) 創(chuàng)建一個新字節(jié)數(shù)組輸入流,它從指定字節(jié)數(shù)組中讀取數(shù)據(jù)。 ---mark::該字節(jié)數(shù)組未被復制。
StringBufferInputStream -- 把一個String對象作為InputStream . 注釋:不推薦使用 StringBufferInputStream 方法。 此類不能將字符正確的轉換為字節(jié)。 同 JDK 1.1 版中的類似,從一個串創(chuàng)建一個流的最佳方法是采用 StringReader 類。
construct---StringBufferInputStream(String) 據(jù)指定串創(chuàng)建一個讀取數(shù)據(jù)的輸入流串。
FileInputStream -- 把一個文件作為InputStream,實現(xiàn)對文件的讀取操作
construct---FileInputStream(File) 創(chuàng)建一個輸入文件流,從指定的 File 對象讀取數(shù)據(jù)。 ---FileInputStream(FileDescriptor) 創(chuàng)建一個輸入文件流,從指定的文件描述器讀取數(shù)據(jù)。 ---FileInputStream(String) 創(chuàng)建一個輸入文件流,從指定名稱的文件讀取數(shù)據(jù)。
method ---- read() 從當前輸入流中讀取一字節(jié)數(shù)據(jù)。 read(byte[]) 將當前輸入流中 b.length 個字節(jié)數(shù)據(jù)讀到一個字節(jié)數(shù)組中。 read(byte[], int, int) 將輸入流中 len 個字節(jié)數(shù)據(jù)讀入一個字節(jié)數(shù)組中。
PipedInputStream:實現(xiàn)了pipe的概念,主要在線程中使用. 管道輸入流是指一個通訊管道的接收端。 一個線程通過管道輸出流發(fā)送數(shù)據(jù),而另一個線程通過管道輸入流讀取數(shù)據(jù), 這樣可實現(xiàn)兩個線程間的通訊。
PipedInputStream() 創(chuàng)建一個管道輸入流,它還未與一個管道輸出流連接。 PipedInputStream(PipedOutputStream) 創(chuàng)建一個管道輸入流, 它已連接到一個管道輸出流。
SequenceInputStream:把多個InputStream合并為一個InputStream .“序列輸入流”類允許應用程序把幾個輸入流連續(xù)地合并起來, 并且使它們像單個輸入流一樣出現(xiàn)。每個輸入流依次被讀取,直到到達該流的末尾。 然后“序列輸入流”類關閉這個流并自動地切換到下一個輸入流。 SequenceInputStream(Enumeration) 創(chuàng)建一個新的序列輸入流,并用指定的輸入流的枚舉值初始化它。 SequenceInputStream(InputStream, InputStream) 創(chuàng)建一個新的序列輸入流,初始化為首先 讀輸入流 s1, 然后讀輸入流 s2。
--OutputSteam
ByteArrayOutputStream:把信息存入內存中的一個緩沖區(qū)中.該類實現(xiàn)一個以字節(jié)數(shù)組形式寫入數(shù)據(jù)的輸出流。 當數(shù)據(jù)寫入緩沖區(qū)時,它自動擴大。用 toByteArray() 和 toString() 能檢索數(shù)據(jù)。
construct --- ByteArrayOutputStream() 創(chuàng)建一個新的字節(jié)數(shù)組輸出流。 --- ByteArrayOutputStream() 創(chuàng)建一個新的字節(jié)數(shù)組輸出流。 --- ByteArrayOutputStream(int) 創(chuàng)建一個新的字節(jié)數(shù)組輸出流,并帶有指定大小字節(jié)的緩沖區(qū)容量。 toString(String) 根據(jù)指定字符編碼將緩沖區(qū)內容轉換為字符串,并將字節(jié)轉換為字符。 write(byte[], int, int) 將指定字節(jié)數(shù)組中從偏移量 off 開始的 len 個字節(jié)寫入該字節(jié)數(shù)組輸出流。 write(int) 將指定字節(jié)寫入該字節(jié)數(shù)組輸出流。 writeTo(OutputStream) 用 out.write(buf, 0, count) 調用輸出流的寫方法將該字節(jié)數(shù)組輸出流的全部內容寫入指定的輸出流參數(shù)。
FileOutputStream:文件輸出流是向 File 或 FileDescriptor 輸出數(shù)據(jù)的一個輸出流。
FileOutputStream(File) 創(chuàng)建一個文件輸出流,向指定的 File 對象輸出數(shù)據(jù)。 FileOutputStream(FileDescriptor) 創(chuàng)建一個文件輸出流,向指定的文件描述器輸出數(shù)據(jù)。 FileOutputStream(String) 創(chuàng)建一個文件輸出流,向指定名稱的文件輸出數(shù)據(jù)。 FileOutputStream(String, boolean) 用指定系統(tǒng)的文件名,創(chuàng)建一個輸出文件。
PipedOutputStream:管道輸出流是指一個通訊管道的發(fā)送端。 一個線程通過管道輸出流發(fā)送數(shù)據(jù), 而另一個線程通過管道輸入流讀取數(shù)據(jù),這樣可實現(xiàn)兩個線程間的通訊。
PipedOutputStream() 創(chuàng)建一個管道輸出流,它還未與一個管道輸入流連接。 PipedOutputStream(PipedInputStream) 創(chuàng)建一個管道輸出流,它已連接到一個管道輸入流。
以字符為導向的stream Reader/Writer
以Unicode字符為導向的stream,表示以Unicode字符為單位從stream中讀取或往stream 中寫入信息。 Reader/Writer 為abstact類 以Unicode字符為導向的stream包括下面幾種類型:
-- Reader
1) CharArrayReader:與ByteArrayInputStream對應 CharArrayReader(char[]) 用指定字符數(shù)組創(chuàng)建一個 CharArrayReader。 CharArrayReader(char[], int, int) 用指定字符數(shù)組創(chuàng)建一個 CharArrayReader。
2) StringReader:與StringBufferInputStream對應 StringReader(String) 創(chuàng)建一新的串讀取者。 3) FileReader:與FileInputStream對應
4) PipedReader:與PipedInputStream對應
-- Writer
1) CharArrayWrite:與ByteArrayOutputStream對應 2) StringWrite:無與之對應的以字節(jié)為導向的stream 3) FileWrite:與FileOutputStream對應 4) PipedWrite:與PipedOutputStream對應
兩種不現(xiàn)導向的stream之間的轉換 InputStreamReader和OutputStreamReader:把一個以字節(jié)為導向的stream轉換成一個以字符為導向的stream。 一個 InputStreamReader 類是從字節(jié)流到字符流的橋梁:它讀入字節(jié),并根據(jù)指定的編碼方式,將之轉換為字符流。 使用的編碼方式可能由名稱指定,或平臺可接受的缺省編碼方式。
InputStreamReader 的 read() 方法之一的每次調用,可能促使從基本字節(jié)輸入流中讀取一個或多個字節(jié)。 為了達到更高效率,考慮用 BufferedReader 封裝 InputStreamReader, BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
InputStreamReader(InputStream) 用缺省的字符編碼方式,創(chuàng)建一個 InputStreamReader。 InputStreamReader(InputStream, String) 用已命名的字符編碼方式,創(chuàng)建一個 InputStreamReader。
OutputStreamWriter 將多個字符寫入到一個輸出流,根據(jù)指定的字符編碼將多個字符轉換為字節(jié)。 每個 OutputStreamWriter 合并它自己的 CharToByteConverter, 因而是從字符流到字節(jié)流的橋梁。
FilterInputStream、RandomAccessFile 見例子。 ObjectInputStream 、 ObjectOutputStream見另外blog。
Java IO的一般使用原則:
一、按數(shù)據(jù)來源(去向)分類: 1、是文件: FileInputStream, FileOutputStream, FileReader, FileWriter 2、是byte[]:ByteArrayInputStream, ByteArrayOutputStream 3、是Char[]: CharArrayReader, CharArrayWriter 4、是String: StringBufferInputStream, StringReader, StringWriter 5、網(wǎng)絡數(shù)據(jù)流:InputStream, OutputStream, Reader, Writer
二、按是否格式化輸出分: 1、要格式化輸出:PrintStream, PrintWriter
三、按是否要緩沖分: 1、要緩沖:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter
四、按數(shù)據(jù)格式分: 1、二進制格式(只要不能確定是純文本的): InputStream, OutputStream及其所有帶Stream結束的子類 2、純文本格式(含純英文與漢字或其他編碼方式);Reader, Writer及其所有帶Reader, Writer的子類
五、按輸入輸出分: 1、輸入:Reader, InputStream類型的子類 2、輸出:Writer, OutputStream類型的子類
六、特殊需要: 1、從Stream到Reader,Writer的轉換類:InputStreamReader, OutputStreamWriter 2、對象輸入輸出:ObjectInputStream, ObjectOutputStream 3、進程間通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter 4、合并輸入:SequenceInputStream 5、更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
決定使用哪個類以及它的構造進程的一般準則如下(不考慮特殊需要): 首先,考慮最原始的數(shù)據(jù)格式是什么: 原則四 第二,是輸入還是輸出:原則五 第三,是否需要轉換流:原則六第1點 第四,數(shù)據(jù)來源(去向)是什么:原則一 第五,是否要緩沖:原則三 (特別注明:一定要注意的是readLine()是否有定義,有什么比read, write更特殊的輸入或輸出方法) 第六,是否要格式化輸出:原則二
設置使用: 客戶須在手機做好相應參數(shù)設置后方可登錄網(wǎng)站。 1)連接名稱: 中國移動GPRS 2)數(shù)據(jù)承載方式: GPRS 3)接入點名稱: cmwap 4)用戶名: 無 5)密碼: 無 6)鑒定: 普通 7)網(wǎng)關IP 地址: 10.0.0.172 8)主頁地址:http://wap.monternet.com 神州行客戶只提供手機上WAP網(wǎng)服務(即:設置APN為cmwap) 。
發(fā)送GPRS15(或者20)到1861開通GPRS服務 發(fā)送QXGPRS到1861取消GPRS |
第一步:主菜單->手機設定->連接->數(shù)據(jù)通信->數(shù)據(jù)賬戶->添加 賬戶-> GPRS 數(shù)據(jù)->隨便建立一個名稱->APN:cmwap,用戶名和密碼不用填寫!?
第二步:再到“數(shù)據(jù)通信”下面的“互聯(lián)網(wǎng)設定”->添加模式->隨便建立一個名稱(建議與第一步所建立的帳戶同名)->連接方式->選擇第一步的那個帳戶->保存!?
第三步:然后在剛才第二部建立的模式按更多—> 設置—> 連接方式(不用理會,其實就是剛才第二部建立的那個)->互聯(lián)網(wǎng)模式:HTTP(如果選:wap,閣下在網(wǎng)上將不能下載大于300kb的文件)->使用代理:是->代理地址:010.000.000.172或者10.0.0.172(其實沒分別)-> 端口號:80->用戶名,密碼不用填寫->保存!
這一章我們討論Java程序的輸入與輸出。Java在I/O方面提供了眾多支持,使我們的工作得到大大的簡化。我們將學習利用這些支持以完成各種復雜的輸入、輸出。
7.1 理解java.io的類繼承關系
首先,讓我們考察Java提供的常用輸出輸出流類(圖7.1)。由于類的數(shù)目較多,沒有列出1.1版本中新增的字符流類。在圖7.2中,我們把字符流類與字節(jié)流類作了對比,在該圖中可以看到字符流類的繼承關系。接口和異常類也被省略了。 ┌BufferedInputStream ├DataInputStream ┌FilterInputStream┼LineNumberInputStream ├FileInputStream └PushbackInputStream ├ByteArrayInputStream ┌InputStream──┼PipedInputStream │ ├SequenceInputStream │ ├StringBufferInputStream │ └ObjectInputStream ┌BufferedOutputStream │ ┌FilterOutputStream┼DataOutputStream Object┤ ├FileOutputStream └PrintStream ├OutputStream──┼ByteArrayOutputStream ├File ├PipedOutputStream ├FileDescriptor └ObjectOutputStream ├ObjdecStreamClass ├RandomAccessFile └StreamTokenizer 圖7.1 java.io包中常用類層次圖(不含字符流類) 圖7.1中包含了許多的輸入和輸出類(這還不包括我們歡天喜地上要講到的字符流輸入輸出類)。為了能正確運用它們,我們必須對它們的功能和關系有個大根式的認識。
7.1.1 字節(jié)流與字符流
第二章中提到了Unicode字符集和ASCII字符集。前者用16位來表示一個字符,而者用8位來表示一個字符。Unicode字符集可表示的符號顯然比ASCII字符集多得多,它可以表示世界上大多數(shù)語言的符號。 在JDK1.0x版本中,只提供了字節(jié)流輸入輸出類。也就是說,輸入輸出的數(shù)據(jù)以字節(jié)為讀寫單位。這就給操作一些雙字節(jié)字符帶來了困難。比如漢字,用一個字節(jié)是不能表示,這就使Java程序的漢化成了問題。例如,用1.0x版的JDK開發(fā)一個文本編輯器,就可能出現(xiàn)這樣的情況:用剪貼板可以把漢字貼進文本域卻無法用鍵盤向文本域輸入漢字字符。這就是標準輸入流每次只接收了一個漢字的第一字節(jié)引起的。 JDK1.1版對輸入輸出作了改進,為字節(jié)流輸入輸出類增加了對應的字符流輸入輸出類這樣,程序員就可以根據(jù)實際情況選用合適的類。 字符流I/O有其顯示而易見的好處。首先它可以適用于世界上大部分語言,從而為Java程序的本地化帶來方便。其次,一次讀一個字符(16位)比讀一個字節(jié)來得快,一般情況下可以彌補將數(shù)據(jù)按當前語言標準編碼、解碼的時間開銷。 字節(jié)流I/O類和字符流I/O類的命名有其對應關系。字節(jié)輸入流類的名字以“InputStream”結尾。而字符輸入流類的名字以“Reader” 結尾。字節(jié)輸出流類的名字后綴為“OutputStream”,而字符輸出流類的名字后綴為“Writer”。 為了在適當?shù)臅r候能把這兩種流類聯(lián)系起來,API中設置了兩個類,充當二者的橋梁。InputStreamReader根據(jù)特定的編碼規(guī)則從字節(jié)流創(chuàng)建相應的字符流,而Output。StreamWriter則根據(jù)編碼規(guī)則從字符流讀取字符,把它們轉化為字節(jié),寫入字節(jié)流中。 下面列出兩種流類的對應關系(圖7.2)。其中,左邊一欄是按繼承關系排列的字符流類,右邊是對應的字節(jié)流類。 Reader InputStream ├BufferedReader BufferedInputStream │ └LineNumberReader LineNumberReader ├CharArrayReader ByteArrayInputStream ├InputStreamReader (none) │ └FileReader FileInputStream ├FilterReader FilterInputStream │ └PushbackReader PushbackInputStream ├PipedReader PipedInputStream └StringReader StringBufferInputStream
Write OutputStream ├BufferedWriter BufferedOutputStream ├CharArrayWriter ByteArrayOutputStream ├OutputStreamWriter (none) │ └FileWriter FileOutputStream ├FilterWriter FilterOutputStream ├PrintWriter PrintStream ├PipedWriter PipedOutputStream └StringWriter (none) 圖7.2字符流類與字節(jié)流類的對應關系
另外,1.1版的API中,對一些1.0x版本中已存在的類也進行了微小的修改,這主要是因為有類對字節(jié)和字符的轉換可能產生錯誤。如以下構造函數(shù)和方法標記為過時: Sting DataInputStream.readLine() InputStream Runtime.getLocalizedInputStream(InputStream) OutputStream Runtime.getLocalizedOutputStream(OutputStream) StreamTokenizer(InputStream) String(byte ascii[],int hibyte,int offset,int count) String(byte ascii[],int hibyte) void String.getBytes(int srcBegin,int srcEnd,byte dst[],int dstBegin) 另外,添加了如下構造函數(shù)和方法: StreamTokenizer(Reader) byte[] String.getBytes() void Throwable.printStackTrace(PrintWriter) 當程序員使用舊的API編程時,可以用 javac -deprecation(文件名) 來進行編譯,這樣編譯器會給出較為詳細的警告信息。編程人員可根據(jù)這些信息查找新文檔,以獲知新版本中的替代方法。 本章的例子都是依據(jù)1.1版本的API編寫的。
7.1.2 輸入輸出類的分類
java.io包中的類各有各的分工,粗略說來可以分為以下幾類: 文件I/O:有三類。對字節(jié)流類來說,包括把文件作為源進行流式輸入的FileInputStream類;把文件作為目的進行流式輸出的 FileOutputStream類;若你想隨機存取文件,即在文件的任意位置讀、數(shù)據(jù),那么可以使用RandomAccessFile類。字符類則有 FileReader和FileWriter類。它們的功能對應于前兩個字節(jié)流類。 除此之外,還有兩個類是與文件訪問有關的,確切地說其功能更近于文件管理。它們是File類,用以訪問文件或目錄;FileDescriptor則封裝了操作系統(tǒng)用以追蹤被訪問文件的信息。 內存緩沖區(qū)I/O:字節(jié)流類有ByteArrayInputStream類,將字節(jié)數(shù)組轉化為輸入流,是從一個字符串創(chuàng)建輸入流,與 ByteArrayInputStream異曲同工,幫也歸入此類別。相應地,字符流類有CharArrayReader, CharArrayWriter,StringReader,此外還多一個StringWriter用來寫字符串。 余下一些類可以不同方式存取流中的數(shù)據(jù)。字節(jié)流類中,DataInputStream和DataOutputStream因其能對流中的不同類的對象分別操作而顯得與眾不同;ObjectInputStream和ObjectOutputStream能把若干完整的對象按選定的格式進行讀寫,但要求被操作對象實現(xiàn)Serializable接口;BufferedInputStream和BufferedOutputStream可以對流數(shù)據(jù)進行緩沖,實現(xiàn)類似“預輸入”、“緩輸出”的功能;LineNumberInputStream跟蹤輸入流中的行數(shù);PusthbackInputStream提供了一個“可推回”的流,從這個流中讀了數(shù)據(jù)后,還可以將它放回流中;PrintStream類提供了許多重載的方法以簡化輸出。對應的字符流類可以從 7.1.1節(jié)的對應關系中查出。 除了上述類以外,Java還有種特殊的I/O類——管道I/O類。它們是專門為線程通訊預備的。管道提供了自動同步機制,可以防止線程通訊中的數(shù)據(jù)混亂。 至引相信讀者已對各個I/O類的功能有所了解。這里再解釋一下過濾器I/O 推廣java.io包中有不少類是過濾器類,它們都是從FilterInputStream或FilterOutputStream之中派生而來(參見圖 7.1)。在字符流中,也有類似的類,但并不像字節(jié)流類一樣必然從某個公共的過濾器父類派生而來。 過濾器(Filter)形成的類對象從一個流中讀入數(shù)據(jù),寫入另一個,就像一個流經(jīng)過過濾產生另一個流一樣。過濾器可以聯(lián)合使用,也就是說“過濾”過的流可以再經(jīng)其它過濾器“過濾”,過濾器型類的共性是: (1)用和種流為參數(shù)的構造,且輸入型過濾器用輸入流,輸出型過濾器用輸出流; (2)無明顯的源/目的限制; (3)流中數(shù)據(jù)的內容“多少”并未改變,只可能性質略有變化。 讀者不妨以這幾條標準去理解過濾器I/O類與其子類,并在以后的示例中加以驗證。
7.2 輸入流與輸出流
字節(jié)輸入流InputStream與字節(jié)輸出流OUtputStream是兩個抽象類。它們?yōu)閖ava.io包中名目繁多的字節(jié)輸入和輸出流打下了基礎。由于是抽象類,它們不能被實例化(也就是說,不能得到其對象),但它們的方法可以被派生類所繼承或重寫。 對于字符流,相應的流類是Reader和Writer。由于它們的方法與InputStream和OutputStream對應,只是把對字節(jié)的操作改為對字符的操作,這里不再重復介紹。但為了讀者能夠對它們的對應關系有個基本認識,在本節(jié)末尾附上Reader類的方法列表,請讀者參照。 InputStream的方示如下: ■public abstract int read() throws IOException ■public int read(byte b[]) throws IOException ■public int read(byte b[],int offset,int length) throws IOException 功能為從輸入流中讀數(shù)據(jù)。這一方法有幾種重載形式,可以讀一個字節(jié)或一組字節(jié)。當遇到文件尾時,返回-1。最后一種形式中的offset是指把結果放在b[]中從第offset個字節(jié)開始的空間,length為長度。 ■public int available() throws IOException 輸入流共有多少字節(jié)可讀。注意此方法對InputStream的各派生類不一定都有效,有時會有返回零字節(jié)的錯誤結果。 ■public void close() throws IOException 關閉輸入流并釋放資源。 ■public boolean markSupperted() 返回布爾值,說明此流能否做標記。 ■public synchronized void mark(int readlimit) 為當前流做標記。其參數(shù)說明在標記失效前可以讀多少字節(jié),這個值通常也就設定了流的緩沖區(qū)大小。 ■public synchronized void reset() throws IOException 返回到上一次做標記處。 ■public long skip (long n) throws IOEnception 從輸入流跳過幾個字節(jié)。返回值為實際跳過的字節(jié)數(shù)。 對于“mark”我們還需解釋一下。輸入流提供“標記”這一機制,使人們可以記錄流中某些特定的位置,并能重復讀部分內容。支持“mark”就必須要求當前流有一定大小的緩沖區(qū),存放部分數(shù)據(jù),即從標記點到當前位置的數(shù)據(jù)。當這一緩沖區(qū)裝滿溢出,我們就無法追蹤到上一個標記處的數(shù)據(jù)了,這就稱之為“標記失效”。若想用reset()返回到一個失效的標記處,將會發(fā)生輸入輸出異常(IOException)。 OutputStream的方法如下。各方法均可能拋出輸入輸出異常(throws IOException)。 ■public abstract void write(int b) ■public void write(byte b[]) ■public void write(byte b[],int offset,int length) 這三個重載形式都是用來向輸出流寫數(shù)據(jù)的。具體每個不甘落后 作用,讀者可根據(jù)前文read()方法對照之。 ■public void flush() 清除緩沖區(qū),將緩沖區(qū)內尚未寫出的數(shù)據(jù)全部輸出。若要繼承OutputStream類,這個方法必須重寫,因為OutputStream中的方法未做任何實物性工作。 ■public void close() 關閉輸出流,釋放資源。 以上提到的這些方法,在下面的章節(jié)中將有不少被運用,讀者可根據(jù)實例領會它們。 附Reader類的方法列表。 構造函數(shù): ■protected Reader() ■protected Reader(object lock) 方法: ■public int read() throws IOException ■public int read(char cbuf[]) throws IOException ■public abstract int read(char cbuf[],int off,int len)throws IOException ■public long skip(long n) throws IOException ■public boolean ready() throws IOException //判斷流是不可以讀 ■public boolean mark(int readAheadLimit)throws IOException ■public void reset() throws IOException ■public abstract void close() throws IOException
7.3 文件I/O
這一節(jié)中我們將結合實例討論File,F(xiàn)ileInputStream,F(xiàn)ileOutputStream,F(xiàn)ileDescriptor和RandomAccessFile類的方法與使用。
7.3.1 一個文件I/O實例
讓我們用一個例子來演示對文件的輸入輸出(例7.1)。圖7.3中列出了這個例子的運行結果。 例7.1 fileIODemo.java。 1:import java.io.*; 2:import java.lang.*; 3: 4: public class fileIODemo{ 5: public static void main(String args[]){ 6: try{ //創(chuàng)建輸入輸出流 7: FileInputStream inStream = new FileInputStream("text.src"); 8: FileOutputStream outStream = new FileOutputStream("text.des"); //讀文并寫入輸出流 9: boolean eof = false; 10: while(!eof){ 11: int c = inStream.read(); 12: if(c==-1) eof = true; 13: outStream.write((char)c); 14: } 15: inStream.close(); 16: outStream.close(); 17: }catch(FileNotFoundException ex){ 18: System.out.println("Error finding the files"); 19: }catch(IOException ex){ 20: System.out.println("IOException occured."); 21: } //獲取文件管理信息 22: File file = new File("text.des"); 23: System.out.println("Parent Directory:"+file.getParent()); 24: System.out.println("Path:"+file.getPath()); 25: System.out.println("File Name:"+file.getName()); 26: try{ //創(chuàng)建RandomAccessFile對象,以便隨機讀寫。"rw"代表可讀可寫 27: RandomAccessFile rafile = new RandomAccessFile("text.des","rw"); //指針置到文件頭 28: rafile.seek(0); 29: boolean eof=false; 30: System.out.println("The content from very head:"); //讀文件 31: while(!eof){ 32: int c = rafile.read(); 33: if(c==-1) eof = true; 34: else System.out.print((char)c); 35: } //下兩行把讀指針置到第三字節(jié) 36: rafile.seek(0); 37: rafile.skipBytes(3); 38: System.out.println("\nThe pointer's position:"+rafile.getFilePointer()); 39: System.out.println("The content from current position:"); 40: eof=false; 41: while(!eof){ 42: int c=rafile.read(); 43: if(c==-1) eof=true; 44: else System.out.print((char)c); 45: } //強制輸出緩沖區(qū)中所有內容 46: System.out.flush(); 47: rafile.close(); 48: }catch(IOException ex){ 49: System.out.println("RandomAccessFile cause IOException!"); 50: } 51: } 52:} 例7.1的運行結果如下: (略) 為了充分展示與文件I/O相關的類的作用,我們的例子中有一些冗余的東西。我們的這個程序位于C:\BookDemo\ch07路徑下(見例7.1行 7),此路徑又有一個子上當text,其中有文件text.src。運行此程序,將在C:\bookDemo\ch07下創(chuàng)建一個新文件 text.des,text.src的內容被寫信此文件。下面的段對File類的演示說明了文件的部分管理信息。然后我們又使用了 RandomAccessFile,試驗了文件在指定位置的讀寫。 第46行的Sytem.out.flush()語句不可以被省略,讀者不妨去掉它試一試。你會發(fā)現(xiàn),有一部分輸出信息不知道到哪兒去了。實際上,flush()的作用就是把緩沖區(qū)中的數(shù)據(jù)全部輸出,我們棣輸出流輸出以后,某些輸出流(有緩沖區(qū)的流)只是把數(shù)據(jù)寫進了緩沖區(qū)而已,不會馬上寫到我們要求的目的地。如果不像例子中一樣強制輸出,部分數(shù)據(jù)可以就來不及在程序結束前輸出了。 細心的讀者或許要問:為什么第一次用ReadomAccessFile讀文件時,輸出語句后面沒有flush()呢?豈非自相矛盾嗎?原來, System.out是PrintStream類的對象(關于PrintStream后有緩沖區(qū)中的內容清除出去。因此許多地方就不必加flush() 了。PrintStream的這個特點,在創(chuàng)建其對象時是可以去掉(disable)的。 這個程序中用到了IOException和FileNotFoundException兩個異常。后者是從前者派生出來的,因此,如果去年程序中的所有try、catch,而在main()方法開頭加上throws IOException,哪樣可以。但這樣不好區(qū)分各種不同的異常情況,即使找不到我們需要的text.src文件,也不會有任何信息顯示。這無疑是一種不良的編程風格。因此我們提倡對各個異常分別處理,這樣對出錯情況可以很地掌握。
7.3.2 文件輸入輸出的類庫支持
下面我們逐一介紹例7.1中用到的各個類。 1.File類 File類的構造函數(shù)有三個。分別根據(jù)文件名、文件路徑與文件名、文件對象(目錄)與文件名創(chuàng)建實例。即: ■public File(String path) ■public File(String path,String name) ■public File(File dir,String name) 除了例子中用到的以外,還有許多方法,下面僅列出較常用的: ■public boolean exists()判斷文件是否存在 ■public boolean canRead()判斷文件是否可讀 ■public long length()返回文件長度 ■public boolean mkdir()創(chuàng)建目錄 ■public boolean renameTo(File dest)文件改名 其中,后三個方法可能拋出I/O異常。 2.FileInputStream類 它是文件輸入流類 構造函數(shù)有三個: ■public FileInputStream(String fileName) throws FileNotFoundException ■public FileInputStream(File file) throws FileNotFoundException ■public FileInputStream(int fd) throws FileNotFoundException 三個構造函數(shù)分別根據(jù)文件名、文件對象、文件描述符創(chuàng)建一個文件輸入流。例子中用的是第一種。 方法: read()、skip()、available() 、close()分別重寫了抽象類InputStream的同名方法,功能如前所述。此外還有: ■public final int getFD() 返回相應的文件描述符。 ■protedted void finalize() throws IOException 關閉輸入流,并收集無用內存空間。 現(xiàn)在我們必須介紹一下文件描述符類FileDescriptor。這個類用于訪問操作系統(tǒng)維護的文件描述符(也稱句柄)。但這個類產不能訪問很多信息。它只提供了兩個方法,即valid(),以判斷文件描述符是否有效;sync(),用以同步系統(tǒng)緩沖區(qū)。 3.FileOutputStream類 文件輸出流。三個構造函數(shù),其參數(shù)、返回值及異常均與FileInputStream的相對應。write()、close()方法重寫了 OutputStream的同名方法。getFD()與finalize()功能與InputStream的類似。 4.ReadomAccessFile類 該類用于隨機訪問文件。 構造函數(shù)有三種: ■public RandomAccessFile(String Filename,String mode) throws IOException ■public RandomAccessFile(int FD) throws IOException ■public RandomAccessFile(File file,String mode)throws IOException 由上可見,我們可以用文件名加讀寫方式、文件描述符、File對象加讀寫方式來創(chuàng)建其對象。其中讀寫方式用“r”表示只讀,“rw”表示可讀寫,等等。用過C語言的讀者對此應當不會陌生。 此類的成員方法很多。除了重寫InputStream的read()方法之外,還可以讀、寫一個布爾值、一個字節(jié)、一個整數(shù)......等對象。這些方法都不可重寫,并且拋出I/O異常(IOException)。訊方法名為“read”加類型名(類型名的第一字母大寫),寫方法名為“write”加類型名。如 readInt()讀一個整型數(shù) writeDouble()寫一個雙精度浮點數(shù) 等。另外還有文件指針的操作,如skipBytes(int n)等。 有了以上這些類的支持,處理文件輸入輸出和維護文件就容易多了。
7.4 內存緩沖區(qū)
內存緩沖區(qū)I/O,對字節(jié)流來說指的是ByteArrayInputStream和ByteArrayOutputStream類的運用。此外, StringBufferInputStream與ByteArrayInputStream用法相似將一并介紹。對字符流不另舉例,它們使用與字節(jié)流類類似。
7.4.1 程序示例
同上一節(jié)一樣,我們還是先看一個例子(例7.2) 例7.2 ByteArrayIODemo.java 1:import java.io.*; 2: 3: public class ByteArrayIODemo{ 4: public static void main(String args[]) throws IOException{ String s ="This a test"; 5: byte buffer[]=s.getBytes(); 6: ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); 7: for(int i=0;i<buffer.length;++i) 8: byteArrayOut.write(buffer[i]); //由字節(jié)數(shù)組創(chuàng)建字節(jié)輸入流 9: ByteArrayInputStream inStream = new ByteArrayInputStream(byteArrayOut.toByteArray()); //讀寫 10: boolean eof=false; 11: while(!eof){ 12: int c=inStream.read(); 13: if(c==-1) eof=true; 14: else System.out.print((char)c); 15: } 16: System.out.println("\nThe'writeTo' method can produce same results."); //用ByteArrayOutputStream的writeTo()方法寫 17: byteArrayOut.writeTo(System.out); 18: byteArrayOut.close(); //available()與reset()的使用 19: System.out.println("\nThe buf of inStream has the length(before seset):"+inStream.available()); 20: inStream.reset(); 21: System.out.println("\nThe buf of inStream has the length:"+inStream.available()); 22: inStream.close(); 23: } 24:} 該程序的運行結果:(略) 這個例子看來相對簡單些。我們先把字節(jié)數(shù)組的內容寫進一個字節(jié)數(shù)組輸出流對象。然后,用一個字節(jié)數(shù)組輸入流對象讀數(shù)據(jù),再用System.out輸出。程序顯示了writeTo()方法的作用。另外,我們還在reset()前、后用了兩疚available()方法,請注意兩方法先后產生的不同結果。這個例子主要用來演示字節(jié)數(shù)組I/O的部分方法。
7.4.2緩沖區(qū)I/O的類庫支持
看過了例子,我們接下來介紹有關的類。 1.ByteArrayInputStream類 這個類用于從一個 字節(jié)數(shù)組取得輸入數(shù)據(jù)。 它有兩個構造函數(shù): ■public ByteArrayInputStream(byte Buf[]) 由字節(jié)數(shù)組創(chuàng)建相應的輸入流。 ■public ByteArrayInputStream(byte buf[],int offset,int length) 由字節(jié)數(shù)組中起點為offset長為length的一段創(chuàng)建輸入流。 成員變量: protected byte buf[]數(shù)據(jù)緩沖區(qū) protected int pos 緩沖區(qū)中當前位置 protected int count緩沖區(qū)中字節(jié)數(shù)目 該類的成員方法都是同步(synchronized)的。 ■public synchronized int read() 讀一個字節(jié)。 ■public synchronized int read(byte b[],int offset,intrlength) 讀取多個字節(jié),返值一般為讀到的字節(jié)數(shù)。但讀到末尾時返回-1。 ■public synchronized long skip(long n) 跳過n個字節(jié)。若返回值不等于n,可能是遇到末尾。 ■public synchronized int available() 求得緩沖區(qū)內字節(jié)數(shù)目。 ■public synchronized void reset() 該指針重新設置到輸入流的開始處。注意,這個reset()與InputStream中的功能不同。它并不作用于標記。 2.ByteArrayOutputStream類 這個類用于把數(shù)據(jù)寫進字節(jié)數(shù)組(緩沖區(qū))。 構造函數(shù): ■public ByteArrayOutputStream() ■public ByteArrayOntput Stream(int size) 其中size指定緩沖區(qū)的初始大小(它是可以動態(tài)增長的)。 成員變量: protected byte buf[]緩沖區(qū) protected int count緩沖區(qū)大小 方法: ■public synchronized void write(int b) 寫一個字節(jié)。 ■public synchronized void write(byte b[],int offset,int length) 把數(shù)組b中由offset開始長為length的一部分寫入緩沖區(qū)。 ■public synchronized void writeTo(OutputStream out)throws IOException 把緩沖區(qū)內容寫到另一輸出流out。 ■public synchronized void reset() 指針定到緩沖區(qū)開始。當然,以后再寫入也就是從緩沖區(qū)的開始位置重寫了,原有的內容就都清掉了。 ■public syschronized byte[] toByteArray() 將緩沖區(qū)內容作為一個字節(jié)數(shù)組返回。 ■public int size() 當前緩沖區(qū)大小。 ■public string toString() ■public string toString(int hibyte) 把緩沖區(qū)內容轉化為字符串。其中hibyte指把字符(通常是8位的ASCII字符)轉為16位的Unicode值時,高八的值。 3.StringBufferInputStream類 它的構造函數(shù)以一個字符串為參數(shù),原型為: ■public StringBufferInputStream(String s) 其余成員變量及方法均與ByteArrayInputStream的同名且基本功能相同,此不贅述。 這三個類的共性是內存中開辟了一段空間來做I/O緩沖區(qū),故稱緩沖區(qū)I/O類。
7.5 過濾器I/O
這一節(jié)涉及的類較多,但我們可以結合幾個例子逐一介紹。 在第一節(jié)中,我們已經(jīng)談了一些過濾器類的特性。過濾器是可以“連接”的,即一個數(shù)據(jù)流經(jīng)過過濾后,其結果可以再次過濾。我們可以使用這樣一串過濾器中的任一個方法來完成某種特殊的操作。關于這一點在第二個例子中有更明白的闡述。
7.5.1 例1:各類數(shù)據(jù)的I/O
第一個例子(例7.3)演示了對各類數(shù)據(jù)的輸入輸出。 例7.3FilterIODemo1.java。 1: import java.io.*; 2: public class FilterIODemo1{ 3: public static void main(String args[]) throws IOException{ //串接過濾器 4: BufferedOutputStream bufOut= 5: new BufferedOutputStream(new FileOutputStream("text.txt")); 6: DataOutputStream dataOut = new DataOutputStream(bufOut); //用DataOutputStream類寫各種數(shù)據(jù) 7: dataOut.writeBoolean(true); 8: dataOut.writeChar('a'); 9: dataOut.writeInt(1); 10: dataOut.writeDouble(3.3); 11: bufOut.close(); 12: dataOut.close(); 13: BufferedInputStream bufIn= 14: new BufferedInputStream(new FileInputStream("text.txt")); 15: DataInputStream dataIn= new DataInputStream(bufIn); //用DataInputStream類讀各種數(shù)據(jù) 16: System.out.println(dataIn.readBoolean()); 17: System.out.println(dataIn.readChar()); 18: System.out.println(dataIn.readInt()); 19: System.out.println(dataIn.readDouble()); 20: bufIn.close(); 21: dataIn.close(); 22: } 23:} 例7.3的運行結果如下:(略) 上述例子演示了DataInputStream、DataOutpurStream、BufferedInputStream和 BufferedOutputStream的使用。該程序中只有一個方法main()。 在方法的開頭,我們實例化了BufferedOutputStream類,得到對象bufOut。注意,我們的數(shù)據(jù)輸出的最終目的地是文件 “Text.txt”。為了能夠利用BufferedOutputStream的緩輸出(把輸出內容先存入緩沖,然后大塊輸出)功能,我們在文件輸出應對上加一個過濾器,形成: 數(shù)據(jù)→過濾器對象bufOut→文件輸出流 這樣,我們用dataOut來寫數(shù)據(jù),就可以直接把各種類型的數(shù)據(jù)寫入文件text.txt。 程序的后半部分幾乎是第一個程序的翻版。我們在輸入流上也加了過濾器,就可以用過濾器的方法來操作輸入流了。 由于BufferedOutputStream和BufferedInputStream沒有提供新的方法,這個例子也許會使讀者產生一種錯覺,好像只有最外層(最接近“數(shù)據(jù)”)的過濾器才能操縱輸入。事實并非如此,我們將在下一個例子中說明這一點。 我們要解釋的問題是,如果我們讀數(shù)據(jù)時,選用的讀方法與寫時不一致會怎么樣呢?讀者可以自行實驗一下。如果我們把 dataIn.readBoolean()換作dataIn.readChar() 讀出的結果就不正確了(注意讀到的并不是字符‘t’),各種類型的數(shù)據(jù)存儲的格式是不同的。雖然我們得到了圖7.4所示的結果,但如果你用type命令看一下 text.txt,將會看到不同的輸出。因此,不要把程序的輸出和數(shù)據(jù)的內部在存儲混為一談。當使用dataI/O時,應當對你要讀的數(shù)據(jù)的類型心中有數(shù)。DataInputStream并不能從一堆數(shù)據(jù)中析取你所需要的那個整數(shù)。
7.5.2 過濾器類家庭
下面我們介紹例1中出現(xiàn)的過濾器類。首先介紹一下它們的父類FilterInputStream和FilterOutputStream。 1.FilterInputStream類 這是一個抽象類。它是所有過濾器輸入類的父類,提供了從一個輸入流創(chuàng)建另一個輸入流的方法。 構造函數(shù): ■public FilterInputStream(InputStream in) 人一個輸入流構造過濾器輸入流。 方法: 重寫了InputStream的同名方法,未提供新的方法。 2.FilterOutputStream類 與FilterOutputStream相對應,提供從一個輸出流創(chuàng)建另一個輸出流的方法。 構造函數(shù): ■public Filer OutputStream(OutputStream out) 由輸出流創(chuàng)始創(chuàng)建一個過濾器輸出流。 方法: 重寫了OutputStream的同名方法。 3.BufferedInputStream類 從這個類開始,我們來介紹例7.3中用到的過濾器子類。BufferedInputStream類提供了一種“預輸入”功能,它把輸入數(shù)據(jù)在其緩沖區(qū)內暫存,在適當?shù)臅r候把較大塊的數(shù)據(jù)提交出去。 構造函數(shù): ■public BufferedInputStream(InputStream in) ■public BufferedInputStream(InputSteam in,int size) 其中size指緩沖區(qū)大小。 方法: 重寫了父類的方法。其中skip()、available()、mark()、reset()均為同步(synchonized)方法。 4. BufferedOutputStream類 提供“緩輸出”功能,把輸出數(shù)據(jù)暫存后,在適當時候大批送出。 構造函數(shù): ■public BufferedOutputStream(OutputStream out) ■public BufferedOutputStream(OutputStream out,int size) 方法: ■public synchronized void write(int b) throws IOException ■public synchronized void write(byte b[],int offset,int length) throws IOException ■public synchronized void flush() throws IOException 以上方法重寫了父類的同名方法。 在BufferedI/O類中,還有一些protect型的成員變量,是關于緩沖區(qū)和標記的,這里就不一一列出了。 5. DataInput接口和DataOutput接口 要介紹Data I/O類,就必須介紹Data I/O接口。 Data I/O類的目的是從流中析取或向流中寫入指定的數(shù)據(jù)對象。一個流可以是純字符流,也可以包含許多類型的數(shù)據(jù)。DataInput接口和 DataOutput接口就提供了從流中析取和寫入數(shù)據(jù)的方法。用于讀的方法除了個別之外都是無參的,寫的方法則往往以被 寫的數(shù)據(jù)類型為參數(shù)。方法的名字也很好記,即為“read”或“write”后接類型名,如readInt(),readUnsignedByte(),writeInt(), writeUnsignedByte()等。這引起方法均可能拋出I/O異常。一般說來,讀時遇文件尾時拋出EOFException(是 IOException的子類),讀寫時發(fā)生其它錯誤拋出IOException。 除了上面所說的名字很有規(guī)律的方法外,Data I/O接口中還有幾個方法: ■public abstract void readFully(byte buffer[]) 讀全部數(shù)據(jù)到buffer[]數(shù)組。讀時系統(tǒng)處于阻塞狀態(tài)。 ■public abstract void readFully(byte buffer[],int offset,int length) 把數(shù)據(jù)讀到數(shù)組buffer[]中從Offset開始長為length的地方。 ■public abstract int skipBytes(int n) 跳過規(guī)定字節(jié)數(shù)。返值為實際跳過的字節(jié)數(shù)。 ■public abstract String readLine() 讀取一行數(shù)據(jù)。 此外,還有我們早已熟悉的write()方法的三種重載形式。 6. DataInputStream類 介紹過兩個數(shù)據(jù)I/O的接口后,介紹數(shù)據(jù)I/O流類就簡單多了。DataInputStream類實現(xiàn)了DataInput接口,因面也就實現(xiàn)了這個接口的所有成員方法。此外,還有兩個read()方法: ■public final int read(byte b[]) ■public final int read(byte b[],int offset,int length) 重寫了FilterInputStream的同名方法。 DataInputStream只有一個構造函數(shù)。像所有過濾器輸入流類一樣,這個構造函數(shù)的參數(shù)是InputStream的一個對象。 7.DataOutputStream類 這個類的成員方法我們都很熟悉了。除了實現(xiàn)DataOutput接口的方法之外,再就是一個flush()方法。write()與flush()重寫了FilterOutputStream類的同名方法。 現(xiàn)在我們可以回過頭來再看一下例7.3,印證一下剛才講過的內容。這個例子的重點之一是演示過濾器的“連接”,另一個是介紹相應的類。
7.5.3 例2:行號與“可推回”的流
在下面的例子(例7.4)中,我們將進一步理解過濾器的連接問題。前面例子基本上用的都是字節(jié)流類,這個例子使用字符流類。 例7.4 FilterIODemo2.java。 1:import java.io.*;
3:public class FilterIODemo2{ 4: public static void main(String args[]) throws IOException{ 5: String s="this is a multi-line string.\n It is \nused to demo filterIO.\n"; 6: char array[]=new char[s.length()]; 7: for(int i=0;i<s.length();++i) 8: array[i]=s.charAt(i); //創(chuàng)建字符流,串接過濾器 9: CharArrayReader charReader = new CharArrayReader(array); 10: PushbackReader pushReader = new PushbackReader(charReader); 11: LineNumberReader lineReader = new LineNumberReader(pushReader); 12: String line; //讀字符流,加行號輸出 13: while((line = lineReader.readLine())!=null){ 14: System.out.println(lineReader.getLineNumber()+":"+line); 15: } //指針置到開頭 16: try{ pushReader.reset();}catch(IOException e){} //讀字符流,每讀到一個'\n'就把它推回 17: boolean eof = false; 18: boolean met = false; 19: while(!eof){ 20: int c=pushReader.read(); 21: if(c==-1) eof=true; 22: else if(((char)c=='\n')&&!met){met =true;pushReader.unread(c);} 23: else met =false; 24: if(c!=-1) System.out.print((char)c); 25: } 26: System.out.flush(); 27: pushReader.close(); 28: charReader.close(); 29: lineReader.close(); 30: } 31:} 該程序的運行結果如下:(略) 這個例子的功能是:給一個字符串加上行號后輸出;把每個換行符都“重復”一次,即每次換行時加一個空行。該例子使用的是字符流I/O,演示了幾個類的使用:CharArrayReader,PushbackReader,LineNumberReader。此外,我們還可以復習一下前面提到的幾個流類。 PushbackReader,顧名思義是是可以把數(shù)據(jù)“推回”輸入流的流類。我們用它來實現(xiàn)對換行符的重復——只要讀完后把“推回去”,下次就可再讀一遍了。LineNumberReader可以追蹤輸入的行數(shù),用它來實現(xiàn)加行號輸出。 現(xiàn)在來講解一下程序。第5行中,在main()方法的開始,定義了一個字符串s。其中,有三個換行符‘\n’。然后創(chuàng)建一個字節(jié)數(shù)組Array[],并在接下來的for循環(huán)(第7、8行)中為它賦值。以此為參數(shù)創(chuàng)建了一個內存緩沖區(qū)輸入流的對象。這就是我們一串過濾器的源點。注意array并不是一個輸入流,相應的CaarArrayReader也不是一個過濾器。 現(xiàn)在考慮選用過濾器。可根據(jù)我們想要的功能來選擇。既然我們要行號,那么顯然最好是一行一行讀數(shù)據(jù)。BufferedReader的readLine ()方法正是我們需要。(readLine()方法本來是DataInputStream類的方法,但在1.1版中過時了。詳細情況在第一節(jié)中已有說明。這里用DataInputStream也是可以的但編譯時會警告信息。)加行號我們可以一行一行地讀,也可以自己高于個變量來累計行數(shù)。當然也可以利用一個現(xiàn)成的類和現(xiàn)在的方法——選擇LineNumbdrReader類及其getLineNumber()方法。由于LineNumbdrReader本身是BuffredReader類的子類,可以直接用它來逐行讀數(shù)據(jù),不必再引入BufferedReader類。為了重復寫回畫換行符可選用 PushbackInputStream類和它的unread()方法。 下面的任務是把它們串起來,如例子所示,可將它些過濾器一個“輸出”作為下一個的“輸入”。第一個while循環(huán)(第13到15行)中做的事很簡單;讀一行信息,取得其行號,然后一些輸出。 第二個while循環(huán)(第19行到25行)的工作是重寫操作符。我們用pushReader來讀數(shù)據(jù)。布爾量eof來標識輸入是否結束,met用來標識當瓣換行符是否被推回過。當輸入沒有結束時,每讀到一個‘\n’ 時,就不會再“推回”了,保證換行符只被重復一次。 正如前面所提到過的,一串過濾器中的任一個都可以操作數(shù)據(jù),無論該過濾器是最先的或最末的或是中間的任何一個。 由于我們是用print()方法來輸出字符的,程序結束時可能還有一部分數(shù)據(jù)在緩沖區(qū)中,沒被寫到屏幕上。因此我們加了一個flush()方法強制顯示到屏幕上。 將用過的流都關閉(第27到29行)是一種好的編輯風格。雖然Java的“垃圾收集”系統(tǒng)可以回收廢棄不用的資源,仍應自覺地打掃“戰(zhàn)場”,把能回收的資源主動回收。
7.5.4 類庫支持
下面詳細介紹一下例7.4中新出現(xiàn)的類。有一點需要解釋,就是字符流I/O類與字節(jié)流I/O類的繼承關系并不是一一對應的。比如,字節(jié)流I/O類中的 PrintStream是FilterOutputStream的子類,而對應的字符流類PrintWriter卻是Writer類的子類。因此,嚴格地說PrintWriter并非過濾器類。但是,為了能夠分六別類地研究這些類,我們不苛求這個差別,而是按照字節(jié)流I/O類的繼承關系,對應地把相應字符流I/O類也看作過濾器類。 1.PushbackReader類 構造函數(shù)兩個: ■public PushbackReader(Reader in,int size) 創(chuàng)建緩沖區(qū)大小為size的一個PushbackReader對象。 ■public PushbackReader(Reader in) 創(chuàng)建緩沖區(qū)大小為一個字符的一個PushbackReader對象。 方法: ■public int read() ■public int read(char cbuf[],int offset,int length) 讀數(shù)據(jù)。 ■public void unread(int ch) 回退一個字符。當緩沖區(qū)滿或發(fā)生其它輸入輸出的異常情況時,拋出I/O異常。 ■public int avaliable() 返回緩沖區(qū)內字節(jié)個數(shù)。 ■public boolean markSupported() 確認輸入流是否支持標記功能。 read()、unread()、available()均可能拋出IOException。 2.LineNumberReader類 構造函數(shù)兩個,與PushbackReader類似。 下面列出方法的原型,其中我們已經(jīng)熟悉的,在此就不給出解釋了。 ■public int read() throws IOException ■public int read(char cbuf[],int offset,int length) throws IOException ■public void setLineNumber(int lineNumber) 設置行號。 ■public int getLineNumber() 讀行號。 ■public long skip(long n) throws IOException ■public int available()throws IOException ■public void mark(int readAheadLimit)throws IOException 在當前位置作標記。從此讀取readAheadLimit個字符后標記變?yōu)闊o效。 ■public void reset()throws IOException 返回到上一標記。 3.PrintStream類和PrintWriter類 PrintStream類是過濾器類中一個不可忽視的成員,最基本的標準輸出就要借助于它——我們常用的System.out變量就是 PrintStream實例。與之對應的字符流類是PrintWriter類。 PrintStream有兩個構造函數(shù)(在新版API中已標記為過時): ■public PrintStream(OutputStream out) ■public PrintStream(OutputStream out,boolean autoFlush) 其中,autoFlush置為true時,每當輸出遇到換行符,緩沖區(qū)的內容就被強制全部輸出,如同調用了一次flush()。但要注意,如果沒遇到換行符,還是會有數(shù)據(jù)“憋”在緩沖區(qū)里。 方法(已熟悉的就不解釋): ■public void write(int b) ■public void write(byte b,int offset,int length) ■public void flush() ■public void close() ■public void print(Object obj) 這個方法功能是非常強大的,它可以輸出任何對象,而不必另加說明。此外print()方法有許多重載形式,即有多種參數(shù)。它們是字符串 (String)、字符數(shù)組(char[])、字符(char)、整數(shù)(int)、長整數(shù)(long)、浮點數(shù)(float)、雙精度浮點數(shù) (double)、布爾值(boolean)。其中,輸出多個數(shù)單位的print()方法(也就是指參數(shù)為String和char[]的)是同步 (synchronized)方法。 ■public void println()輸出一個換行符。 ■public synchronized void println(Object obj) println()方法有9個重載形式,幾乎就是print()方法的翻版。唯一的區(qū)別在于println()方法都是同步的。 ■public boolean checkError() 檢查輸出過程中有什么錯誤,如有,返回true值。只要輸出流中出現(xiàn)一次錯誤,則出錯后的任意對checkError()的調用均會返回真值。 下面介紹PrintWriter類。 如同第二節(jié)中所說,PrintWriter是JDK1.1版增加了與字節(jié)流I/O相對應的字符流I/O。但是,為了保持兼容性,原先的類幾乎沒有改動。再加之調試的需要,PrintStream類被保留,并且System類中的成員變量out、err仍作為它的對象。然而,PrintWriter用于大多數(shù)輸出比PrintStream更為合適。因此1.1版的API中建議新開發(fā)的代碼使用PrintWriter類,并將 PrintStream類的兩個構造函數(shù)標記為過時。這樣,雖然使用System.out輸出不會產生問題,在程序中創(chuàng)建新的PrintStream對象時卻會產生編譯時的警告。 PrintWriter類與PrintStream類的方法是對應的。有一個不同之外需提請讀者注意,就是當前者的自動清空緩沖區(qū)的功能被使能時(構造函數(shù)中autoFlush置為true),僅當println()方法被調用時才自動清緩沖區(qū),而不是像PrintStream一樣遇到一個換行符就清緩沖。 到此為止,我們已介紹了各種類型的過濾器I/O類。適用于字節(jié)流和字符的各種對應過濾器類,其方法也是對應的。因此,對沒有介紹的類讀者可以從其對應類推理其功能。
7.6 管道I/O
管道I/O是專門用于線程通信的。對于字節(jié)流Java提供了兩個類,PipedInputStream類被線程用來寫字節(jié)數(shù)據(jù)。兩個管道I/O流對象可以連接起來,這樣一個線程寫的數(shù)據(jù)就可以被另一個線程來讀。對于字符流也有兩個類,分別叫做PipedReader和PipedWriter。我們只詳細介紹字節(jié)流的管道I/O類。
7.6.1 PipedInputStream類
這個類有兩個構造函數(shù)。一個無參,用它建立起輸入流后,需將它與一個管道輸出流相連接。另一個以管道輸出流(PipedOutputStream)對象為參數(shù),創(chuàng)建一個與該輸出流對象相連接的輸入流。 PipedInputStream類的所有方法均可能拋出IOException。 ■public void connect (PipedOutputStream src) 將輸入流連接到某管道輸出流。 ■public synchronized int read() ■public synchronized int read(byte b[],int offset,int length) 讀數(shù)據(jù)。 ■public void close() 關閉流。
7.6.2 PipedOutputStream類
與PipedInputStream類完全對應,它有兩個構造函數(shù),其中一個以PipedInputStream對象為參數(shù),另一個無參。成員方法也是包括connect(),close(),另外還有兩種形式的write()方法,這里就不細述了。
7.6.3 程序示例
下面用一個示例(例7.5)具體演示管道I/O的使用。 例7.5 PipeIODemo.java 1: import java.lang.*; 2: import java.io.PipedInputStream; 3: import java.io.PipedOutputStream; 4: import java.io.IOException; 5:
6: public class PipeIODemo{ 7: public static void main(String args[]){ //這里的Reader和Writer不是字符流輸入輸出的基本類,而是下文自定義的 8: Reader thread1=new Reader("1"); 9: Writer thread2=new Writer("2"); //聯(lián)接管道 10: try{ 11: thread2.pipeOut.connect(thread1.pipeIn); 12: }catch(IOException ex){ 13: System.out.println("IOException occured when connecting two stream"); 14: } //啟動線程 15: thread1.start(); 16: thread2.start(); //循環(huán),等線程均結束后程序中止 17: do{ 18: }while(thread1.isAlive()||thread2.isAlive()); 19: System.out.println("All over!"); 20: } 21:} //自定義讀者類 22:class Reader extends Thread{ 23: public PipedInputStream pipeIn; 24: String threadName; 25: public Reader(String name){ 26: super(); 27: threadName = name; 28: pipeIn = new PipedInputStream(); 29: } 30: public void run(){ 31: try{ 32: boolean over = false; 33: while(!over){ 34: int ch=pipeIn.read(); 35: try{ 36: Thread.sleep(200); 37: }catch(InterruptedException ex){ 38: System.out.println("Sleep is interrupted!"); 39: } 40: if(ch=='.') over = true; 41: else System.out.println("Thread "+threadName+" read "+(char)ch); 42: } 43: 44: }catch(IOException ex){ 45: System.out.println("IOException occured when try to read data"); 46: } 47: } 48:} //自定義寫者類 49:class Writer extends Thread{ 50: public PipedOutputStream pipeOut; 51: String threadName; //待寫內容 52: String content = "orange apple"; 53: public Writer(String name){ 54: super(); 55: threadName=name; 56: pipeOut = new PipedOutputStream(); 57: } 58: public void run(){ 59: try{ //將字符串內容逐字輸出 60: for(int i=0;i<content.length();++i){ 61: pipeOut.write(content.charAt(i)); 62: try{ 63: Thread.sleep(200); 64: }catch(InterruptedException ex){ 65: System.out.println("Sleep is interrupted!"); 66: } 67: System.out.println("Thread "+threadName+" wrote "+content.charAt(i)); 68: } 69: pipeOut.write('.'); 70: }catch(IOException ex){ 71: System.out.println("IOException occured when try to write data"); 72: } 73: } 74:} 該程序的運行結果如下:(略) 這個例子功能很簡單。兩個線程,一個是讀者,一個是寫者,讀者取寫者所寫的內容。雙方約定以‘.’為結束符。 這個例子演示了管道I/O一般過程,首先是創(chuàng)建管理I/O流類對象。這個工作是在Reader和Writer類的構造函數(shù)中做的(第28、56行)。因此當我們創(chuàng)建了thread1和thread2兩個線程時,pipeIn和pipeOut就被創(chuàng)建了。然后我們把它們連接起來,再啟動兩個線程工作,最后打印“All Over!” 表示運行結束。 可以看出,讀線程與寫線程實際上是不必關心對方的情況的。它們的工作就是讀或寫,每處理一個字符輸出一條信息表明自己做過的工作。我們在pipeIn 的輸出信息中加了一大段空格,這樣的目的是使兩個線程的輸出能容易分辨。另外,讓兩個線程處理一個字符就睡眠(sleep) 一會兒并不是必須的,這樣只是為了增加線程交替執(zhí)行的機會。如果去年這一段,可能執(zhí)行數(shù)次者不出現(xiàn)thread1、thread2交替輸出信息的現(xiàn)象,容易被誤解為兩個線程必須一個死亡才執(zhí)行另一個。另外,作為結束符的“.” 并沒有顯示出來。 這個例子實現(xiàn)的是單向通信。實際上,為每個線程都分別創(chuàng)建輸入流對象和輸出流對象,再分別連接起來,就可以實現(xiàn)雙向通信。讀者有興趣不妨一試。
7.7 java.io包中的其它類
7.7.1 SequenceInputStream類
這個類的功能是合并多個輸入流。其構造函數(shù)有兩個,一個以枚舉(Enumeration)對象為參數(shù),一個以兩個InputStream對象為參數(shù)。方法則有兩個read()方法,分別讀一個字符、讀數(shù)據(jù)入字節(jié)數(shù)組中的一段。再就是一個close()方法。例7.6利用它來實現(xiàn)了兩上文件的并接。其中還使用了ByteArrayOutputStream,用意是將兩個文件并接的結果先在內存緩沖區(qū)中暫存一下。這個例子允許目的的文件是兩個源文件之一。 例7.6 FileCat.java import java.lang.System; import java.io.*;
public class FileCat{ public static void main(String args[]){ SequenceInputStream seqIn; if(args.length!=3){System.out.println("Usage:java FileCat filesrc filesrc filedst");} else{ try{ FileInputStream f1=new FileInputStream(args[0]); FileInputStream f2=new FileInputStream(args[1]); seqIn=new SequenceInputStream(f1,f2); ByteArrayOutputStream byteArrayOut=new ByteArrayOutputStream(); boolean eof=false; int byteCount=0; while(!eof){ int c=seqIn.read(); if(c==-1)eof=true; else{ //將讀到的數(shù)據(jù)寫入字節(jié)數(shù)組輸出流 byteArrayOut.write((char)c); ++byteCount; } } FileOutputStream outStream=new FileOutputStream(args[2]); //將數(shù)據(jù)寫入文件 byteArrayOut.writeTo(outStream); System.out.println(byteCount+" bytes were read."); seqIn.close(); outStream.close(); byteArrayOut.close(); f1.close(); f2.close(); }catch(FileNotFoundException ex){ System.out.println("Cannot open source files.Please check if they"+ "exists and allows freading."); }catch(IOException ex){ System.out.println("IOexception occured!"); } } } }
7.7.2 Streamtokenizer類
這個類是用來構造詞法分析器的。缺省情況下,它可以識別數(shù)值、字母以及字符串。它的構造函數(shù)只有一個,以輸入流(inputStream)對象為參數(shù)。本節(jié)我們給出一個例子(例7.7),并介紹例子中出現(xiàn)的該類的部分方法。 例7.7 TokenIODemo.java。 1:import java.io.IOException ; 2:import java.lang.System; 3:import java.io.InputStreamReader ; 4:import java.io.StreamTokenizer; 5:import java.io.FileInputStream ; 6: 7:public class TokenIODemo{ 8: public static void main(String args[]) throws IOException{ //從文件創(chuàng)建輸入流 9: FileInputStream fileIn = new FileInputStream ("hello.c"); //從字節(jié)流創(chuàng)建字符流 10: InputStreamReader inReader = new InputStreamReader (fileIn); 11: StreamTokenizer tokenStream = new StreamTokenizer (inReader); //設置注釋風格 12: tokenStream.slashStarComments(true); 13: tokenStream.slashSlashComments (true); //識別行結束符;如果參數(shù)為假,將行結束符視作空白符 14: tokenStream.eolIsSignificant (true); //設置引號的符號表示 15: tokenStream.quoteChar ('"'); //將ASCII碼為0-32的字符設為空白符 16: tokenStream.whitespaceChars (0,32); 17: boolean eof = false; 18: do{ 19: int token = tokenStream.nextToken (); 20: switch(token){ //文件結束符 21: case tokenStream.TT_EOF : 22: System.out.print(" EOF "); 23: eof=true; 24: break; //行結束符 25: case tokenStream.TT_EOL : 26: System.out.print (" EOL "); 27: break; //單詞 28: case tokenStream.TT_WORD : 29: System.out.print (" Word "+tokenStream.sval ); 30: break; //數(shù)字 31: case tokenStream.TT_NUMBER : 32: System.out.print(" Number "+tokenStream.nval ); 33: break; 34: default: 35: System.out.print(" "+(char)token); 36: } 37: }while(!eof); 38: System.out.flush(); 39: } 40:} 下面是該例的運行結果: E:\>java TokenIODemo # Word include < Word stdio.h > EOL EOL Word main ( ) { EOL Word print ( " , Number 1234.0 ) ; EOL EOL } EOL EOF E:\> 其中,hello.c程序的源代碼如下: #include <stdio.h> //To say "hello world" main(){ print("hello world %d\n",1234); /* It is a test for TokenIODemo*/ } 例子中我們用到了這樣一些方法: ■public void whitespaceChars(int low,int hi) 把給定范圍的字符設為空格(不可見)字符。類似的方法還有wordChars()(設為單詞字符),ordinaryChars()(設置為除了單詞字符、數(shù)據(jù)字符等有實際含義字符之外的其它字符)。 ■public void slachStarComments(boolean flag) ■public void slachSlashComments(boolean flag) flag為真,則可訓別相應風格的注釋。前者(slashStar)指C風格的注釋(/*...*/)。后者指C++風格的注釋“//”。 ■public int nextToken() 從輸入流取得下一個詞法分析單位。 ■public void eolIsSingnificant(boolean flag) 如果參數(shù)為真,識別行結束符;否則,將行結束符視作空白符。 例子中還用到了一些常量和變量。TT_EOF、TT_EOL、TT_NUMBER、TT_WORD分別表示文件結束符、行結束符、數(shù)值和單詞。public String sval是指字符串值;public double nval指雙精度值。這些常量、變量的使用在例子中已有明確的演示,這里就不多說了。
7.7.3 FilenameFilter接口
這個接口不太常用,只提供了一個方法: ■public abstract boolean accept(File dir,String fileName) 功能是確定某一文件列表是否包含了指定的文件。
7.7.4 Serializable接口
實現(xiàn)這一接口的類可以被“串行化”,即它們的對象可以被轉化為某種形式,該形式可以被輸入輸出,而保存對象的結構。也就是說,只有實現(xiàn)了這一接口,類的對象才能被完整地輸入輸出和存儲。 該接口不含任何方法和變量,它只充當一個標記。編程時只要在類定義時中上: ... implements Serializable 即可使該類的對象具有“串行性” 。
本章小結
在這一章中,我們比較全面地介紹了java.io包中的類和接口,并給出了示例。讀者通過這一章學習,應掌握java的輸入輸出類,并將種I/O手段靈活運用于自編的程序之中。
列出java語言的所有重點
java 2全方位學習 J2ME無線java應用開發(fā)? JAVA手機程序設計入門與應用 1、對于一般PC平臺來說,Java的程序分成兩大類,一個是在PC的操作系統(tǒng)上通過JVM直接運行的Java Application,另一種是通過瀏覽器中附帶的JVM運行的Java Applet。 2、<applet code="要運行的class文件名稱" width="顯示的寬度" height="顯示的高度"></applet>。 3、javac,java,appletviewer。 4、java是用unicode作為字符集的,所以我們在Java程序中使用中文或是英文甚至是其他的語言作為class名稱、變量名稱都可以。 5、JFC-java foundation classes,GUI-graphical uesr interface。 6、java -jar Java2D.jar。 7、PDA-個人數(shù)據(jù)處理。jpda-Java Platform Debugger Architecture。
第4章 程序基本單元 8、關鍵字與保留字的區(qū)別。標志符的魔力。Literal的含義。變量的意義,變量命名的原則。 9、基本數(shù)據(jù)類型:整型,浮點,其他類型。 10、為什么數(shù)值范圍正負值分開,無理數(shù)或是無窮小數(shù)的表示問題。其核心是精度問題。浮點數(shù)不存在0這個數(shù)值,所以會產生誤差。 11、其他數(shù)據(jù)類型:boolean,char,常用轉義字符,特殊字符的表示方法。 12、Java兩種變量的模式:成員變量(member variable),局部變量(local variable)。成員變量初始值在聲明時就指定了。而局部變量則不會,要求用戶自己設定初始值。 13、類型轉換分為自動類型轉換(promotion)和強制類型轉換(casting)兩種。其中每種又分為放大(widening)和縮?。╪arrowing)兩種。放大轉換屬于自動類型轉換,縮小轉換屬于強制類型轉換。 14、數(shù)據(jù)類型的后面加上一個英文字母,是由于Java對于literal默認的數(shù)據(jù)類型有關,基本上Java對整數(shù)的lieral默認為int型,而對于浮點數(shù)的literal默認為double型。 15、Java里有個特殊的類,可以像一般的基本數(shù)據(jù)類型一樣使用,它就是String-字符串。
第5章 Java的表達式 16、5%2與5%-2與-5%2與-5%-2的區(qū)別。 17、比較運算符的結果只有兩種,true和flase。instanceof? 18、邏輯與和一般與的差別。在需要改變變量的值時,用一般與。通常使用邏輯運算符,因為運算速度會快一些。 19、邏輯運算的優(yōu)先級在比較運算符之下。 20、賦值運算符是所有運算符中最低的。賦值運算符從右邊運算到左邊。而算術運算符是從左邊運算到右邊。并且賦值運算符的左邊只能有一個變量存在。 21、位運算符(bitwise)。 &,|,^,~(complement)。位運算符只能用于整型數(shù)據(jù)類型中。位移運算(shift)的用處。位移運算比較難。要理解位移運算的用途。了解減次運算的含義。????? 22、運算符的優(yōu)先級和結合性。
第6章 Java的語句 23、語句有很多種,粗略的把它們分為四類:第一類是一般的語句,第二類是聲明語句,第三類是條件流程控制語句,第四類是循環(huán)控制語句。 24、對象的聲明與變量的聲明是不同的。對象在聲明后,必須進行實例化,而變量聲明是不需要的。 25、?:運算符的使用。 26、Switch的參數(shù)只能是(<byte、short、int或char變量>)的其中一種。? 27、for(<控制循環(huán)變量初始值設置>;<循環(huán)結束判斷條件語句>;<控制循環(huán)變量值改變方法>){<代碼>s}。千萬不使用浮點數(shù)作為控制變量。由于浮點數(shù)誤差問題。 28、while(<循環(huán)結束判斷條件語句>){<代碼>s}。 29、do{<代碼>s} while(<循環(huán)結束判斷條件語句>)。 30、高級循環(huán)控制-嵌套循環(huán)。以及break和continue的使用。 31、標記<token>的使用。以冒號: 作為結束。運用適當?shù)腷reak、continue和token可以增強程序的彈性。但不要亂用token。 32、必須了解注釋語句、if語句、switch語句、循環(huán)語句、break、continue和標記的使用與限制。 ??????????????????????????????????? 第7章 Java面向對象程序設計 33、對象是符合某種類定義所產生出來的實例(instance)。類是抽象的,而對象是實在的。屬性(attribute)是用來形容一個實例對象的,其實就是變量。方法(method)是對象自己的行為或者是使用它們的方法,其實就是函數(shù)。屬性和方法稱為對象的成員。類可以說是藍圖(blueprint),類中會定義許多產生該類對象時,所必須具備的一些屬性與方法。 34、繼承(inheritance)和多態(tài)(polymorphism)是類的另外兩個重要的特性。繼承最主要的目的是為了"擴展"原類的功能、加強或改進原類所沒有定義的屬性及方法。被繼承的類為父類,繼承的類為子類。采用UML(Unified Modeling Language)的表達方式來設計類,可以畫出類關系圖,其中最重要的部件是類圖標和繼承圖標。多態(tài)的概念比較難理解,需要加強理解,其中還有覆蓋(override)的概念。 35、為了增強程序的可讀性和易用性。全世界的java程序師都遵守以下的規(guī)則:(1)Package(包),作為Package名稱的英文單詞全部要小寫;(2)類,每個英文單詞的第一個字母大寫;(3)接口,規(guī)則與類一樣;(4)屬性,每個英文單詞的第一個字母小寫,其他單詞的第一個英文字母大寫;(5)方法,規(guī)則和屬性一樣,不過后面有小括號;(7)常量,英文單詞全部大寫,而且每兩個英文單詞之間用下劃線隔開。 36、Animal和Zoo兩個類只需要對Zoo.java進行編譯即可。這是因為在Zoo.java中所有用到的類如果還沒有進行過編譯的話,在編譯Zoo.java的過程中它們都會被自動編譯。 37、構造函數(shù)(constuctor),除了可以在Java編譯時為我們自動產生之外,還可以自行編寫所需要的構造函數(shù)。構造函數(shù)也是一個方法。 38、在一個類中,有多個構造函數(shù)使用相同的名稱,但是參數(shù)類型與個數(shù)卻各不相同,我們把這樣的一個行為稱為構造函數(shù)重載(overloading)。 39、原則上重載有兩個規(guī)則一定要遵守:一、方法名稱一定要一樣。否則的話,就是兩個不同的方法,不能稱為重載。二、調用的參數(shù)類型一定要不一樣。因為編譯器要通過參數(shù)類型來判斷調用的是哪一個方法。 40、面向對象程序設計中一個非常重要的概念,我們稱為信息的隱藏(information hidding),專用的技術術語為封裝(encapsulatio)。封裝的目的有兩個:一、保護類中的數(shù)據(jù),不讓這些數(shù)據(jù)被錯誤的使用或破壞;二、隱藏不需要讓別人知道的細節(jié),以防別人誤用。封裝還有一些其他的重要的特點:隱藏類的具體細節(jié);強制用戶通過單一接口訪問數(shù)據(jù);程序更加容易維護。 41、屬性訪問方法的命名規(guī)則:設置屬性值的方法以set作為開頭;獲取屬性值的方法以get作為開頭;Boolean數(shù)據(jù)類型值的獲取用isXXX形式來命名。 42、類的多態(tài)的,指類在不同情況下,可以看作是不同的類。 43、類成員和實例成員。前面介紹的屬性和方法,屬于對象等級的,稱為實例成員。類成員必須使用限定詞static。類成員的調用方法為<類名稱>.<類成員名稱>,也可以為<對象名稱>.<類成員名稱>。但是類方法中不能使用實例成員。 44、子類產生對象時,會往上通知它的父類,它的父類又會通知父類的父類,持續(xù)這個操作直到最上層的java.lang.Object類。通知上層父類最主要的目的時,對于那些繼承自父類的屬性或其他的設置做初始化的操作。從而達到 程序代碼的重復使用,這也是繼承的目的。Java在編譯時自動幫我們加上通知父類的程序代碼,是加在構造函數(shù)里面。super();這行的意思必須明白。super是關鍵字。 45、調用super函數(shù)必須注意兩點:一、super調用必須在構造函數(shù)的第1行。二、如果子類中有好幾個不同的構造函數(shù),二父類又沒有不需要參數(shù)的構造函數(shù),那么就必須在子類中的每個構造函數(shù)的第一行加上適當?shù)膕uper調用。 46、構造函數(shù)調用構造函數(shù)用this這個關鍵字。 47、super和this在使用上有一些要注意的地方:一、super和this只能使用在構造函數(shù)程序代碼中的第一行;二、super和this同時只能使用一種;三、super和this的調用只能使用在構造函數(shù)中;四、如果構造函數(shù)中沒有使用super或this,那么Java會自動幫你加上super()調用。 48、屏蔽(shadow)-屬性(繼承關系)、覆蓋(override)-方法(繼承關系)、重載(overload)-方法(同一個類下函數(shù)同名,但參數(shù)不同)。使用屬性的幾種方法必須了解,super、this、強制類型轉換的使用。
第8章 深入Java面向對象程序設計 49、什么是包(package)?包很像我們計算機中的目錄或是文件夾。目錄分隔符,dos用\,unix用/。目錄機制應用于Java面向對象的程序當中就是所謂的包。 50、package語句中,原本的目錄分隔符改用句點[.]來代替。package <package名稱>;必須注意:package語句一定只能寫在程序代碼的第一行。package的UML圖示。除了每一個類的Java文件中的第一行設置package外,程序代碼中用到其他類的地方也一并加上它的package的名稱。package的設置與使用。 51、import語句,必須寫在package語句之后,所有類聲明之前。import語句的通配符(*)。使用通配符只是將該package下的類import進來,不會把子目錄下的其他目錄中的類import進來。 52、classpath的設置。classpath是Java程序在編譯與運行時會使用到的一個【環(huán)境變量】,它的主要用途是告訴編譯器去哪里找到編譯或運行時所需要的類。windows默認情況下classpath為【.】。設置classpath的兩種方法:一、直接設置;二、javac加上-classpath這個參數(shù)。 53、訪問權限的限定詞的使用。protected與default的使用。成員限定詞有四個,而類聲明限定詞只有兩個。 54、final限定詞的使用-用于常量。final和static的使用。 55、抽象類的使用。關鍵字是abstract。能讓繼承的子類一定覆蓋某個特殊的方法,這種機制就是【抽象(abstract)】。在一個類中,我們可以指定某個方法為抽象的,而一個抽象的方法不需要編寫方法的內容,也就是說當方法聲明完后,就直接以分號【;】來結束,不用加上左右大括號。只要有任何一個抽象方法,那么這個類就必須成為一個抽象類,所以我們必須把類的聲明加上abstract這個關鍵字。抽象類不能生成對象實例。abstract只能用在類和方法上。屬性和變量上沒有意義。 56、接口-一種標準、一些規(guī)范。在java中,接口是由一些常量和抽象方法所組成的。關鍵字是【interface】,使用的格式如下:<限定詞>interface<接口名稱>[extends<接口名稱>s]。解決abstract不能解決的問題。接口中方法的語法聲明跟抽象方法的語法聲明是一樣的,就是是只有方法的聲明,而沒有方法本身,簡單的說就是聲明完后直接以分號結束整個語句,而不用再加上大括號。接口中的方法也都全部是抽象方法,只是不需要額外加上abstract這個關鍵字。extends <類名>,implements <接口名>。簡單的說,接口就是一個完全抽象的類。Java用多重接口的方法來完成類的多重繼承機制。implements<接口1>,<接口2>......。java中存在一種特殊的接口,它只有接口的聲明,而內部是空的,也就是說完全沒有任何的常量和方法的聲明。這種特殊大額接口稱為【標記接口(marker interface)】。
第9章 Object類的常用方法介紹 57、對象之間的比較,第一種觀點,對象相等是指對象為同一個。包括使用的內存。直接用==。第二種觀點,是兩個對象的內容是否相等。用equals方法。 58、理解hash code的含義。 59、引用(reference)與復制(clone)的區(qū)別。clone方法的使用。 60、將對象轉為字符的方法【toString】。 61、在設計的類的時候,最好也一并的把這幾個Object的方法覆蓋。
第10章 深入內存 62、變量內存的使用,變量聲明后,編譯器就分配了內存。 63、對象內存的使用,對象聲明后,編譯器只是在內存中產生一個對象的引用(reference),它所存放的并不是一個真正的對象實例 ,因為對象的實例我們還沒生成。所以當一個對象被聲明后,在內存中這個對象引用的初始值會是【null】。我們用new這個關鍵字,配合調用類的構造函數(shù),來生成對象實例。但,此時對象引用與對象實例并沒有產生關聯(lián)。需要使用復制語句使它們關聯(lián)。每個對象引用占用4個字節(jié)的內存空間。對象的引用所存放的是撒對象實例真正在內存中的地址。對象引用實際占用的內存大小,跟系統(tǒng)(JVM)實現(xiàn)的方法有關,不同的系統(tǒng)大小不一定相同。 64、什么是數(shù)組,數(shù)組是存放量大、性質相同且需要做相同處理的數(shù)據(jù)。數(shù)組可以用在基本數(shù)據(jù)類型的變量上,當然也可以用在對象上。數(shù)組與對象有點相似,分成兩個階段——數(shù)組引用的聲明和數(shù)組實例的生成。數(shù)組聲明格式如下:int a[];和int []a;數(shù)組聲明完后,在內存中表現(xiàn)的方法也跟對象一樣,也是個引用,而且初始值也是null。生成數(shù)組實例同樣要用到new關鍵字,在這之后要用賦值語句進行關聯(lián)處理。也可以同時創(chuàng)建和初始化數(shù)組,如 int a[]={1,2,3,4}。 65、數(shù)組的索引(index),是[]里面的數(shù)字,它表示這個數(shù)組中的第幾筆數(shù)據(jù)。數(shù)組的使用比較簡單,就是在數(shù)組變量的名稱后,加上要訪問的索引。索引從0開始到數(shù)組的大小減1。 66、數(shù)組的length屬性獲得數(shù)組的大小。必須注意數(shù)組的大小是不能改變的。使用上比較有彈性。 67、數(shù)組的復制。數(shù)組不是繼承自java.lang.Object類的對象,所以沒有clone這個用來復制對象實例的方法??梢岳醚h(huán)賦值來實現(xiàn)。java中提供了【System.arraycopy】方法。使用這個方法時需要5個參數(shù),依次是源數(shù)組、來源數(shù)組數(shù)據(jù)起始位置、目的數(shù)組、目的數(shù)組數(shù)據(jù)起始位置、復制數(shù)據(jù)的個數(shù)。使用上比較有彈性。arraycopy方法只適用于基本數(shù)據(jù)類型的數(shù)組。相比而言,第二種方法使用JNI的方法,所以速度上會比較快。arraycopy的三種異常,NULLPointerException,ArrayIndexOutOfBoudsException,ArrayStroeException。 68、Java 本機接口(Java Native Interface (JNI))是一個本機編程接口,它是 Java 軟件開發(fā)工具箱(Java Software Development Kit (SDK))的一部分。JNI 允許 Java 代碼使用以其它語言(譬如 C 和 C++)編寫的代碼和代碼庫。Invocation API(JNI 的一部分)可以用來將 Java 虛擬機(JVM)嵌入到本機應用程序中,從而允許程序員從本機代碼內部調用 Java 代碼。 預備知識 所有示例都是使用 Java、C 和 C++ 代碼編寫的,并可以移植到 Windows 和基于 UNIX 的平臺上。要完全理解這些示例,您必須有一些 Java 語言編程經(jīng)驗。此外,您還需要一些 C 或 C++ 編程經(jīng)驗。嚴格來說,JNI 解決方案可以分成 Java 編程任務和 C/C++ 編程任務,由不同的程序員完成每項任務。然而,要完全理解 JNI 是如何在兩種編程環(huán)境中工作的,您必須能夠理解 Java 和 C/C++ 代碼。 系統(tǒng)需求 瀏覽器:Netscape 4.x 或更高版本, 或者 Internet Explorer 4.x 或更高版本 ,支持 JavaScript 。 要運行本教程中的示例,您需要下列工具與組件: Java 編譯器:隨 SDK 一起提供的 javac.exe。 Java 虛擬機(JVM):隨 SDK 一起提供的 java.exe。 本機方法 C 文件生成器:隨 SDK 一起提供的 javah.exe。 定義 JNI 的庫文件和本機頭文件。jni.h C 頭文件、jvm.lib 和 jvm.dll 或 jvm.so 文件,這些文件都是隨 SDK 一起提供的。 能夠創(chuàng)建共享庫的 C 和 C++ 編譯器。最常見的兩個 C 編譯器是用于 Windows 的 Visual C++ 和用于基于 UNIX 系統(tǒng)的 cc。 68、多維數(shù)組在內存中的樣子。必須理解。不規(guī)則數(shù)組的生成。 不規(guī)則數(shù)組是Java語言的一個重要特點,其他的程序語言像C或是Basic,都只能聲明規(guī)則的多維數(shù)組,而且維數(shù)有上限。java沒有這個限制。 69、以上討論基本數(shù)據(jù)類型的數(shù)組,現(xiàn)在來看對象數(shù)組。防止NullPointerException異常的產生。 70、變量的訪問范圍【scope】,有點像類之間屬性及方法訪問的限制,這些限制是由于訪問權限的限定詞、package和繼承這幾種關系組合起來的。變量訪問范圍大致分為四個等級:第一、類級(static);第二、對象實例級;第三、方法級;第四、局域級。怎么樣區(qū)分這幾個級別,必須注意。必須理解這四個等級。訪問范圍和視野的關系剛好相反。內存存在的時間。 71、參數(shù)的傳遞。以前的程序語言概念參數(shù)的傳遞有兩種方法,一是【傳值(call by value)】,另一個是【傳址(call by reference)】。但java里面只有傳值這種方式?;緮?shù)據(jù)類型參數(shù)值傳遞與類對象型參數(shù)值傳遞是不同的。 72、內存回收(garbage collection)。負責運行這個機制的就是【garbage collector】。對象聲明包括兩部分:對象引用和對象實例。如果一個對象實例不被任何對象引用指到的話,但啟動GC時,就會把對象實例回收回去,并把內存釋放調。取消對象引用,只要將它指定為【null】即可。GC是不定時的啟動,也可以手動調用它,方法是【System.gc()】,它會調用【Runtime.getRuntime.gc()】,這兩個方法都可以使用。finalize方法同樣也是Object類常用的一個方法,與GC有關。它是在對象被回收前,GC所調用的方法。回收順序與對象實例的生成順序有關。既是我們手動調用System.gc(),GC也不見得一定運行,GC正確的啟動時間無法得知。??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? 第11章 Application與Applet 73、Application的輸出,System類的out屬性是PrintStream對象,有prinlin方法和print方法,也可以用err屬性是PrintStream對象,有prinlin方法和print方法,但有區(qū)別,out屬性可以重定向(redirected),err屬性只能輸出到默認的設備上。Application默認的就是所打開的命令行窗口,Applet默認的就是Java Console。 可以用>來實現(xiàn)重定向。println和print方法都是屬于重載的方法,除了可以接受八種基本數(shù)據(jù)類型和String類型作為參數(shù)外,還可以接受一般的對象作為參數(shù),編譯器會自動調用這個對象的【toString】方法,char數(shù)組也可以作為這兩個方法的參數(shù)。 74、Application的輸入,分為兩種,一是參數(shù)輸入(命令行參數(shù)),Wrapper類的使用?;緮?shù)據(jù)類型與Wrapper類的對應關系。Wrapper類種有相應的parseXXX方法來實現(xiàn)字符串轉換為基本數(shù)據(jù)類型。二是標準輸入,System類的in屬性是InputStream對象,有read方法來讀取輸入,讀進來是byte對類型,需要轉化為其他類數(shù)據(jù)型。通常使用InputStreamReader類,然后連接到BufferedReader類,用BufferedReader類提供的讀取字符串的方法【readLine】。 75、系統(tǒng)參數(shù)的獲取用【System.getProperties】方法。必須注意系統(tǒng)參數(shù)與命令行參數(shù)的區(qū)別。 76、System類的使用。setXXX(setOut、setErr、setIn),setProperties和SetProperty的區(qū)別。System.exit(n),虛擬機會調用Runtime.getRutime.exit(n)方法。currentTimeMills。 77、Runtime類的使用。可以通過exec這個方法來運行一個外部程序。 78、Appplication必須有一個main的方法,符合四個條件。而一個Applet一定要繼承java.applet.Applet類。main方法是Application運行的起始點,而Applet運行的起始點在init方法上。Applet中,System.out或System.err方法來輸出,但System.in方法不能用。Applet有自己的輸入方式,類似于命令行參數(shù)的方式。在HTML文件中,加上參數(shù)語法,<param name=<參數(shù)名稱> value=<參數(shù)值>>。然后在java程序中,應用java.applet.Applet類的【getParam】方法。 79、Applet基本方法的使用。init、start、stop、destroy、paint。destroy和finalize方法的區(qū)別在于使用地點不同。paint方法的使用。paint方法中有個屬性是Graphics對象,注意Graphics類的【drawString】方法的使用。 80、java的安全性,是指Applet滿足java指定的重重安全規(guī)范。四點限制措施。 81、Application和Applet的結合。Java Web Start是取代Applet的機制。
第12章 異常處理 82、異常是指程序在運行的過程中,由于編寫程序的倏忽,外在環(huán)境的因素,或是電腦系統(tǒng)本身的問題,都可能導致程序運行時產生錯誤,造成死機或是計算的結果不正確,這些突發(fā)的狀況稱為異常。 異常處理是指當程序出現(xiàn)異常時,能夠作出一些應變的處理。 83、java.lang.Throwable類。Exception類(可以控制)和Error類(無法控制)。 84、RuntimeException常見的有以下幾種:ArithmeticException、ArrayIndexOutOfBoundsException、ArrayStoreException、ClassCastException、IllegalArgumentException、NativeArraySizeException、NullPointerException、SecurityException。由于自己編程引起的。 85、CheckedException常見的有以下幾種:ClassNotFoundExecption、FileNotFoundException、InterrupedException、IOException、SQLException。一些外部因素引起的。 86、Error有OutOfMemoryError、StackOverflowError、UnknowError、AWTError、ThreadDeath。系統(tǒng)級且非常嚴重的錯誤。錯誤原因是內存不足或者是運行時掛起。 87、捕捉和處理異常。主要是針對CheckedException類的異常。try、catch、finally三個關鍵字的使用。處理異常包括以下兩個步驟:一、把異常的名稱及一些相關的信息顯示出來,二、用最安全的方法恢復程序的運行。顯示異常信息用到,toString、getLocalizedMessage、getMessage、printStackTrace方法。其中printStackTrace方法有三種不同的重載。彈性(flexibility)。 finally關鍵字的使用注意以下三點:一、沒有異常產生-》進finally區(qū)塊-》方法中剩下未運行的程序代碼。二、有異常產生-》捕捉到-》進catch區(qū)塊-》finally區(qū)塊-》方法中剩下未運行的程序代碼;三、有異常產生-》沒有捕捉到-》進finally區(qū)塊-》方法中剩下未運行的程序代碼。??????????????????? 88、注意異常捕捉的順序。越上層的類,越放在下面。 89、throws關鍵字的使用,在方法聲明上拋出異常。throw關鍵字,在方法內部拋出異常。必須注意拋出RuntimeException和CheckedException兩種異常在使用上的差別。 90、定義自己的Exception。 91、拋出異常方法覆蓋的問題。注意兩點:一、不可拋出原有方法拋出異常類的父類或上層類;二、拋出的異常類數(shù)目不能筆原有的方法拋出的還多。主要是因為編譯時拋出的異常類無法自動轉化為父類中所聲明的異常類。
第13章 容器(Container)與布局(Layout) 92、什么是AWT(Abstract Windowing Toolkit),什么是GUI(Graphical User Interface)圖形用戶接口。AWT包的結構圖。 93、Container包括Frame和Panel。Frame是先構造,然后setSize,然后再setVisible。理解Deprecation。Frame的常用方法。記住【Ctrl】+【C】來停止程序的方法。Panel不能獨立出現(xiàn)在畫面上,必須放在某個Container中才行,例如Frame或瀏覽器里面。Applet本身就是一個panel。add方法的使用。 94、什么是Layout?!緎etLayout(<xxxLayout>)】方法。有五個基本的Layout類。Frame默認的布局是BorderLayout類。Panel默認的布局是FlowLayout。另外還有CardLayout、GridLayout、GridBagLayout。也可以設計自己的Layout類。 95、pack和setSize方法的區(qū)別。????????????????????????????????????????????????????? 96、如果不使用Layout,可以使用【setSize】和【setLocation】方法來代替。最好使用Layout類。?????????????????????????????????????????????????????????????????????????????????????? 第14章 事件處理 97、什么是事件【event】。事件就是別人給予它的一些操作。明白事件處理結構:事件本身、事件產生的來源、誰來處理事件。 98、什么是委托處理模式【Delegation Model】。事件處理的機制。 99、AWT Event類的結構圖。分為兩類:Low-level和senmantic。 100、【Listener】這個接口(interface)與【Adapter】類的相對應。 101、一個對象可以委托好幾個類來處理相同的事件,一個處理事件的類也可以同時處理不同對象所產生的事件。這種情況稱為【multiplexer】。 102、WindowEvent、MouseEvent、KeyEvent類事件處理的接口,類和方法。以及其他常用的Low-level Event類,分別是ContainterEvent和FocusEvent。??????????????????????????? 103、Swing是Java所設計的另外一組更豐富、功能更多的GUI空間。理解Swing和AWT的區(qū)別。
?一.異常
Java對異常的處理同Delphi一樣,不是刻意的去避免它的發(fā)生,而是等它發(fā)生后去補救.
Delphi的異常處理簡單來說就是一下語句
Try Except//異常發(fā)生后就轉入此處執(zhí)行 Finally//不管異常發(fā)不發(fā)生,都轉入此處運行 End
與此相類似,Java的異常處理的基本形式如下
try{ }catch(ExceptionType1 e){ file&://對/異常情況1的處理 }catch(ExceptionType2 e){ file&://對/異常情況2的處理 throw(e)//拋出異常,和Delphi中的raise是一回事 }
要補充的是,對大多數(shù)的異常,假如你要在正常運行的程序中而不是捕捉異常的程序中明確的拋出,Java的編譯器需要你事先對你要拋出的異常作聲明,否則不允許編譯通過.這個任務是由throws來完成的.
二.Java的輸入輸出流
2.1 輸出
System.out.print file&://這/里out是一個靜態(tài)方法哦 System.out.println System.err.print file&://err/和out一樣也是標準輸出,至于有什么不同,我目前還不清楚 System.err.println
2.2 輸入
System.in.read()
2.3 文件的操作
只需要幾個帶注釋的例子就可以了。
第一個是一個顯示文件基本信息的程序
import java.io.*;//調入和io相關的類 class fileinfo{ file&://注/意,main函數(shù)一定是靜態(tài)方法
public static void main(String args[])throws IOException{ File fileToCheck;//使用文件對象創(chuàng)建實例 if (args.length>0){ for (int i=0;i fileToCheck=new File(args[i]);//為文件對象分配空間 info(fileToCheck);//這里引用的info一定要是靜態(tài)方法成員 } } else{ System.out.println("no file given"); } }
public static void info(File f)throws IOException{ System.out.println("Name:"+f.getName()); System.out.println("Path:"+f.getPath()); if (f.exists()){ System.out.println("File exists."); System.out.print((f.canRead()?" and is Readable":""));//判斷函數(shù),如果滿足條件,輸出前者,否則輸出后者 System.out.print((f.canWrite()?"and is Writable":"")); System.out.print("."); System.out.println("File is"+f.length()+"bytes."); } else{ System.out.println("File does not exist."); } } }
第二個例子是一個存儲電話信息的小程序,用戶輸入姓名和電話號碼,程序將其存入phone.numbers文件中,通過FileOutputStream來實現(xiàn)
import java.io.*;
class phones{ static FileOutputStream fos; public static final int lineLength=81; public static void main(String args[])throws IOException{ byte[] phone=new byte[lineLength]; byte[] name=new byte[lineLength]; int i; fos=new FileOutputStream("phone.numbers"); while(true){ System.err.println("Enter a name(enter ‘done‘ to quit)"); readLine(name); if ("done".equalsIgnoreCase(new String(name,0,0,4))){ break; } System.err.println("Enter the phone number"); readLine(phone); for (i=0;phone[i]!=0;i++){ fos.write(phone[i]); } fos.write(‘,‘); for (i=0;name[i]!=0;i++){ fos.write(name[i]); } fos.write(‘n‘); } fos.close(); }
private static void readLine(byte line[])throws IOException{ int i=0,b=0; while((i<(lineLength-1))&&((b=System.in.read())!=‘n‘)){ line[i++]=(byte)b; } line[i]=(byte)(0); } } 2.4 流
無非是兩種
輸出流,讓我們來寫的
輸入流,給我們來讀的
java.io包中有很多種類的輸入輸出流:
1.FileInputStream和FileOutputStream 節(jié)點流
2.BufferedInputStream和BufferedOutputStream 過濾流
3.DataInputStream和DataOutputStream 增強的過濾流
4.PipedInputStream和PipledOutputStream 用于線程的流
掌握了流的概念,就可以開始Sockets的學習了.關于Socket的作用,昨天我已經(jīng)講了.
現(xiàn)在,我們將創(chuàng)建一個簡單的通訊程序,以獲得對Socket的實質性的認識.該程序包括兩個部分,客戶機(RemoteFileClient)和服務器(RemoteFileServer).客戶機向服務器發(fā)出請求,要求讀取服務器上的文件信息.服務器將響應請求,將相應的文件信息傳給客戶機,將相應的文件信息傳給客戶機.
首先我們創(chuàng)建RemoteFileClient類:
import java.io.*;//java.io 包提供對流進行讀寫的工具,也是與 TCP 套接字通信的唯一途徑 import java.net.*;//java.net 包提供套接字工具。
public class RemoteFileClient { protected String hostIp; protected int hostPort; protected BufferedReader socketReader;//負責讀數(shù)據(jù)的對象 protected PrintWriter socketWriter;//負責寫數(shù)據(jù)的對象
file&://類/的構造器有兩個參數(shù):遠程主機的 IP 地址(hostIp)和端口號(hostPort)各一個.構造器將它們賦給實例變量
public RemoteFileClient(String aHostIp, int aHostPort) { hostIp = aHostIp; hostPort = aHostPort; } public static void main(String[] args) { } file&://連/接到遠程服務器 public void setUpConnection() { } file&://向/遠程服務器請求文件信息 public String getFile(String fileNameToGet) { } file&://從/遠程服務器上斷開 public void tearDownConnection() { } }
首先來實現(xiàn)main()
public static void main(String[] args) { RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);//為了方便調試,我們把本地服務器當作遠程服務器 remoteFileClient.setUpConnection();//連接。不能直接使用setUpConnection,因為它是非靜態(tài)變量,需要創(chuàng)建實例后,對實例進行引用,可以看我第一天的日記,上面寫的非常詳細 String fileContents = remoteFileClient.getFile("RemoteFile.txt");//讀取
remoteFileClient.tearDownConnection();//斷開
System.out.println(fileContents);//輸出讀取內容 }
步驟非常清楚.那么我們分別看連接,讀取,斷開是怎么實現(xiàn)的
1.連接
public void setUpConnection() { try { Socket client = new Socket(hostIp, hostPort);//創(chuàng)建Socket對象
OutputStream outToServerStream=client.getOutputStream(); InputStream inFromServerStream=client.getInputStream(); socketReader = new BufferedReader(new InputStreamReader(inFromServerStream)); file&://把/Socket的InputStream包裝進BufferedReader 以使我們能夠讀取流的行
socketWriter = new PrintWriter(outToServerStream); file&://把/Socket的OutputStream包裝進PrintWriter 以使我們能夠發(fā)送文件請求到服務器
} catch (UnknownHostException e) { System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); file&://對/Socket對象創(chuàng)建錯誤的異常處理 } catch (IOException e) { System.out.println("Error setting up socket connection: " + e); file&://對/IO錯誤的異常處理 } }
2.讀取
public String getFile(String fileNameToGet) { StringBuffer fileLines = new StringBuffer();//StringBuffer對象也是String對象,但是比它更靈活,這里是用來存放讀取內容的
try { socketWriter.println(fileNameToGet); socketWriter.flush();//文件存放地址輸出到socketWriter中,然后清空緩沖區(qū),讓這個地址送到服務器中去
String line = null; while ((line = socketReader.readLine()) != null) fileLines.append(line + "n"); file&://既/然已經(jīng)發(fā)送到服務器去了,那我們都要等待響應,這里的程序就是等待服務器把我們所需要的文件內容傳過來 } catch (IOException e) { System.out.println("Error reading from file&: " + fileNameToGet); }
return fileLines.toString();//別忘了把buffer中的內容轉成String再返回 }
3.斷開
public void tearDownConnection() { try { socketWriter.close(); socketReader.close(); } catch (IOException e) { System.out.println("Error tearing down socket connection: " + e); } }
tearDownConnection() 方法只別關閉我們在 Socket 的 InputStream 和 OutputStream 上創(chuàng)建的 BufferedReader 和 PrintWriter。這樣做會關閉我們從 Socket 獲取的底層流,所以我們必須捕捉可能的 IOException
好,現(xiàn)在可以總結一下客戶機程序的創(chuàng)建步驟了
1.用要連接的機器的IP端口號實例化Socket(如有問題則拋出 Exception)。
2.獲取 Socket 上的流以進行讀寫.
3.把流包裝進 BufferedReader/PrintWriter 的實例.
4.對 Socket 進行讀寫.具體說來,就是在Writer上傳送文件地址信息給服務器,在Reader上讀取服務器傳來的文件信息 5.關閉打開的流。
下面是RemoteFileClient 的代碼清單
import java.io.*; import java.net.*;
public class RemoteFileClient { protected BufferedReader socketReader; protected PrintWriter socketWriter; protected String hostIp; protected int hostPort;
public RemoteFileClient(String aHostIp, int aHostPort) { hostIp = aHostIp; hostPort = aHostPort; } public String getFile(String fileNameToGet) { StringBuffer fileLines = new StringBuffer();
try { socketWriter.println(fileNameToGet); socketWriter.flush();
String line = null; while ((line = socketReader.readLine()) != null) fileLines.append(line + "n"); } catch (IOException e) { System.out.println("Error reading from file&: " + fileNameToGet); }
return fileLines.toString(); } public static void main(String[] args) { RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000); remoteFileClient.setUpConnection(); String fileContents = remoteFileClient.getFile("RemoteFile.txt"); remoteFileClient.tearDownConnection();
System.out.println(fileContents); } public void setUpConnection() { try { Socket client = new Socket(hostIp, hostPort);
OutputStream outToServerStream=client.getOutputStream(); InputStream inFromServerStream=client.getInputStream(); socketReader = new BufferedReader(new InputStreamReader(inFromServerStream)); socketWriter = new PrintWriter(outToServerStream);
} catch (UnknownHostException e) { System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort); } catch (IOException e) { System.out.println("Error setting up socket connection: " + e); } } public void tearDownConnection() { try { socketWriter.close(); socketReader.close(); } catch (IOException e) { System.out.println("Error tearing down socket connection: " + e); } } }
好了,現(xiàn)在來看服務器端的程序怎么寫.
創(chuàng)建RemoteClientServer類:
import java.io.*; import java.net.*;
public class RemoteFileServer { protected int listenPort = 3000; public static void main(String[] args) { } public void acceptConnections() { } public void handleConnection(Socket incomingConnection) { } }
跟客戶機中一樣,首先導入 java.net 的 java.io。接著,給我們的類一個實例變量以保存端口,我們從該端口偵聽進入的連接。缺省情況下,端口是 3000。
acceptConnections()將允許客戶機連接到服務器 handleConnection()負責與客戶機 Socket 交互以將您所請求的文件的內容發(fā)送到客戶機。
首先看main()
public static void main(String[] args) { RemoteFileServer server = new RemoteFileServer(); server.acceptConnections(); }
非常簡單,因為主函數(shù)無非是讓服務器進入監(jiān)聽狀態(tài),所以直接調用acceptConnection().需要注意的是,必須先創(chuàng)建RemoteFileServer()的實例,而不是直接調用.
那么服務器是怎樣通過acceptConnection()來監(jiān)聽客戶機的連接呢?并且如果兼聽到了,又怎樣處理呢?我們來看
public void acceptConnections() { try { ServerSocket server = new ServerSocket(listenPort);//同客戶機的Socket對應,在服務器端,我們需要ServerSocket對象,參數(shù)是兼聽的端口號 Socket incomingConnection = null;//創(chuàng)建一個客戶端的Socket變量,以接收從客戶端監(jiān)聽到的Socket while (true) { incomingConnection = server.accept();//調用該 ServerSocket 的 accept() 來告訴它開始偵聽, handleConnection(incomingConnection); } file&://不/斷監(jiān)聽直到來了一個連接請求,然后交由handleConnection處理 } catch (BindException e) { System.out.println("Unable to bind to port " + listenPort); } catch (IOException e) { System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); } }
無論何時如果創(chuàng)建了一個無法綁定到指定端口(可能是因為別的什么控制了該端口)的 ServerSocket,Java 代碼都將拋出一個錯誤。所以這里我們必須捕捉可能的 BindException。同時,與在客戶機端上時一樣,我們必須捕捉 IOException,當我們試圖在 ServerSocket 上接受連接時,它就會被拋出??梢酝ㄟ^用毫秒數(shù)調用 setSoTimeout() 來為 accept() 調用設置超時,以避免實際長時間的等待。調用 setSoTimeout() 將使 accept() 經(jīng)過指定占用時間后拋出 IOException
最關鍵的處理在handleConnection()中,這時已經(jīng)連接到了客戶端的Socket,要從該Socket中讀取客戶端的請求并且響應。
public void handleConnection(Socket incomingConnection) { try { OutputStream outputToSocket = incomingConnection.getOutputStream(); InputStream inputFromSocket = incomingConnection.getInputStream();
file&://首/先獲取同Socket相關聯(lián)的流outputToSocket和InputStream file&://其/中outputToSocket是要返回給客戶端Socket的流 file&://InputStream/是客戶端發(fā)來的請求,在這里就是文件路徑,即"RemoteFile.txt"
BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));
file&://首/先要將InputStream轉換到BufferedReader中
FileReader fileReader = new FileReader(new File(streamReader.readLine())); file&://從/BufferedReader中讀出文件路徑,建立新對象FileReader
BufferedReader bufferedFileReader = new BufferedReader(fileReader);
file&://再/次建立BufferedReader對象,這一次它讀取得是文件里面的內容
PrintWriter streamWriter = new PrintWriter(OutputStream);
file&://把/Socket的outputToSocket流包裝進PrintWriter 以使我們能夠發(fā)送文件信息到客戶端
String line = null; while ((line = bufferedFileReader.readLine()) != null) { streamWriter.println(line); } file&://從/bufferedFileReader中讀出文件信息,再經(jīng)由streamWriter輸出到客戶端
fileReader.close(); streamWriter.close();//注意Socket的兩個流關閉的順序 streamReader.close(); file&://完/成之后關閉所有流
} catch (Exception e) { System.out.println("Error handling a client: " + e); } }
請注意完成所有操作之后關閉流的順序,streamWriter的關閉在streamReader的關閉之前。這不是偶然的,假如將關閉次序顛倒過來,客戶端將不會獲取到任何文件信息,你可以調試一下看看.這是為什么呢?原因是如果你在關閉 streamWriter 之前關閉 streamReader,則你可以以往 streamWriter中寫任何東西,但沒有任何數(shù)據(jù)可以通過通道(通道被關閉了).但奇怪的是,我不是已經(jīng)在之前的streamWriter.println()中輸出了嗎?難道非要等到所有的流關閉之后輸出到客戶端的信息的東西才能到達?我試著將
streamWriter.close(); streamReader.close();
屏蔽掉,看是否依然能夠實現(xiàn)正常的通信,結果發(fā)現(xiàn)不行,程序死機.可能是因為通道沒有閉合導致的.那么至少可以說明,只有將通道按某種順序正常關閉,才能完成通訊數(shù)據(jù)的傳輸,否則客戶端收不到信息.
最后依然是總結一下創(chuàng)建服務器端程序的步驟
1.用一個你想讓它偵聽傳入客戶機連接的端口(比如程序中的3000)來實例化一個 ServerSocket(如有問題則拋出 Exception)。
2.循環(huán)調用ServerSocket的accept()以監(jiān)聽連接
3.獲取客戶端的Socket流以進行讀寫操作
4.包裝流
5.對客戶端的Socket進行讀寫
6.關閉打開的流(切記,永遠不要在關閉 Writer 之前關閉 Reader),完成通信
下面是
RemoteFileServer 的代碼清單
import java.io.*; import java.net.*;
public class RemoteFileServer { int listenPort; public RemoteFileServer(int aListenPort) { listenPort = aListenPort; } public void acceptConnections() { try { ServerSocket server = new ServerSocket(listenPort); Socket incomingConnection = null; while (true) { incomingConnection = server.accept(); handleConnection(incomingConnection); } } catch (BindException e) { System.out.println("Unable to bind to port " + listenPort); } catch (IOException e) { System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort); } } public void handleConnection(Socket incomingConnection) { try { OutputStream outputToSocket = incomingConnection.getOutputStream(); InputStream inputFromSocket = incomingConnection.getInputStream();
BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));
FileReader fileReader = new FileReader(new File(streamReader.readLine()));
BufferedReader bufferedFileReader = new BufferedReader(fileReader); PrintWriter streamWriter = new PrintWriter(outputToSocket); String line = null; while ((line = bufferedFileReader.readLine()) != null) { streamWriter.println(line); }
fileReader.close(); streamWriter.close(); streamReader.close(); } catch (Exception e) { System.out.println("Error handling a client: " + e); } } public static void main(String[] args) { RemoteFileServer server = new RemoteFileServer(3000); server.acceptConnections(); } }
|
喜歡這樣簡約的風格。。以后在這里貼技術貼了。。:)
|
|