Java基礎知識複習
繼續承接上一篇先帖一個導圖:
5.數組
- 數組也沒好說的,即是注意 數組其實也是引用類型數據。許多數據結構的實現是通過數組。比如ArrayList,hashMap。
- 數組是不可變的。數組查詢很快,直接通過下表就能拿到數據。
- 所有的數據類型都可以聲明爲數組。
- 數組是容器
- 最常見的錯誤就是數組越界
- 這裏可以引申出一個 傳值和傳址的問題。
- 基本數據類型傳遞是值,引用類型是傳址。
- 傳遞是對象句柄。傳的是變量的地址,變量存儲的值是 引用對象的地址。
- 數組還用的多的就是 數據結構中的 哈希表,詳細的等複習集合框架再說。
Arrays 類
java.util.Arrays 類能方便地操作數組,它提供的所有方法都是靜態的。
具有以下功能:
給數組賦值:通過 fill 方法。
對數組排序:通過 sort 方法,按升序。
比較數組:通過 equals 方法比較數組中元素值是否相等。
查找數組元素:通過 binarySearch 方法能對排序好的數組進行二分查找法操作。
6輸入輸出流
IO 類設計出來,肯定是爲了解決 IO 相關的操作的,想一想哪裏會有 IO 操作?網絡、磁盤。網絡操作相關的類是在 java.net 包下。提到磁盤,文件操作在 IO 中是比較典型的操作。在 Java 中引入了 “流” 的概念,它表示任何有能力產生數據源或有能力接收數據源的對象。
只要分爲2大類,字節流和字符流。針對大文件的讀寫 還有一個NIO。
字節流和字符流的區別:
字節流讀取單個字節,字符流讀取單個字符(一個字符根據編碼的不同,對應的字節也不同,如 UTF-8 編碼是 3 個字節,中文編碼是 2 個字節。)字節流用來處理二進制文件(圖片、MP3、視頻文件),字符流用來處理文本文件(可以看做是特殊的二進制文件,使用了某種編碼,人可以閱讀)。
關於輸入輸出是針對內存來說的。輸入指的是把內容輸入到流中, 所以是read()意思就是從本地文件中讀取出內容放到流中。輸出指的是把流中的內容寫到本地文件中所以是 write()。
這麼多的輸入輸出類用到了裝飾者模式,裝飾者模式和代理模式也像,裝飾者 主要是去裝飾增加原來的方法。就是包裝的意思。就像劉備硬說自己是皇室後裔。
首先我們寫一個最簡單的拷貝文件代碼:
public static void main(String[] args) {
//測試輸入輸出流
try {
InputStream inputStream=new FileInputStream("E:/Temp.txt");
OutputStream outputStream=new FileOutputStream("E:/Temp_copy.txt");
int count=0;
int data=0;
while ((data=inputStream.read())!=-1) {
count++;
System.out.println("data="+data);
outputStream.write(data);
}
System.out.println("count="+count);
inputStream.close();
outputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
最簡單就是文件Temp拷貝一份。然後定義了一個計算次數的,從運行結果可以知道。
這個read()方法一次只讀了一個字節,但是爲什麼返回的是int呢,這個是爲了防止讀到11111111的時候就直接停止。因爲這個在計算機中的值就是-1.但是如果用int 那就是 255。
接着我再定義一個數組一次讀取1KB=1024字節:
public static void main(String[] args) {
//測試輸入輸出流
try {
InputStream inputStream=new FileInputStream("E:/Temp.txt");
OutputStream outputStream=new FileOutputStream("E:/Temp_copy.txt");
int count=0;
int data=0;
byte[] buffer=new byte[1024];
while ((data=inputStream.read(buffer))!=-1) {
count++;
System.out.println("data="+data);
outputStream.write(buffer);
}
System.out.println("count="+count);
inputStream.close();
outputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
從結果可以看到 這次就是讀取了4次每次最多讀取1024字節。可以大大增加讀取效率。
同時爲了減少訪問磁盤的次數,提高文件讀取性能,Java提供了BufferedInputStream和BufferedOutputStream類。
我再寫個例子看看。
public static void main(String[] args) {
//測試輸入輸出流
try {
InputStream inputStream=new FileInputStream("E:/Temp.txt");
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
OutputStream outputStream=new FileOutputStream("E:/Temp_copy.txt");
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
int count=0;
int data=0;
byte[] buffer=new byte[1024];
while ((data=bufferedInputStream.read(buffer))!=-1) {
count++;
System.out.println("data="+data);
bufferedOutputStream.write(buffer);
}
System.out.println("count="+count);
inputStream.close();
outputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BufferedInputStream和BufferOutputStream
這兩個流是內置了緩衝區流,也就是說內部有一個 字節數組
緩衝思想
字節流一次讀寫一個數組的速度明顯比一次讀寫一個字節的速度快很多,
java本身在設計的時候,加入了數組這樣的緩衝區效果,
也考慮到了這樣的設計思想(裝飾設計模式後面講解),所以提供了字節緩衝區流
BufferedInputStream
BufferedInputStream內置了一個緩衝區(數組)
從BufferedInputStream中讀取一個字節時
BufferedInputStream會一次性從文件中讀取8192個(8Kb), 存在緩衝區中, 返回給程序
程序再次讀取時, 就不用找文件了, 直接從緩衝區中獲取
直到緩衝區中所有的都被使用過, 才重新從文件中讀取8192個
BufferedOutputStream
BufferedOutputStream也內置了一個緩衝區(數組)
程序向流中寫出字節時, 不會直接寫到文件, 先寫到緩衝區中
直到緩衝區寫滿, BufferedOutputStream纔會把緩衝區中的數據一次性寫到文件裏
這個的意思就是 可以減少對磁盤的訪問。建議都可以使用。還有一個讀取到中文亂碼問題,這個是和編碼格式有關。
這一篇說的比較詳細:IO流詳解
其他的都大同小異,字符流主要是處理大文件的,下面放一張全部類圖:
不同的類 在不同的情況下使用。最後還有一個新出的NIO
NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網絡編程NIO。
IO是面向流的,NIO是面向緩衝區的;Java IO的各種流是阻塞的,Java NIO的非阻塞模式;Java NIO的選擇器(Selectors)允許一個單獨的線程來監視多個輸入通道。
Java NIO的核心組件 包括:
通道(Channel)
緩衝區(Buffer)
選擇器(Selectors)
Java NIO中的Buffer用於和NIO通道進行交互。數據是從通道讀入緩衝區,從緩衝區寫入到通道中的。
緩衝區本質上是一塊可以寫入數據,然後可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,
並提供了一組方法,用來方便的訪問該塊內存。
1、Buffer基本用法
使用Buffer讀寫數據一般遵循以下四個步驟:
1)寫入數據到Buffer,一般有可以從Channel讀取到到緩衝區中,也可以調用put方法寫入。
2)調用flip()方法,切換數據模式。
3)從Buffer中讀取數據,一般從緩衝區讀取數據寫入到通道中,也可以調用get方法讀取。
4)調用clear()方法或者compact()方法清空緩衝區。
當向buffer寫入數據時,buffer會記錄下寫了多少數據。一旦要讀取數據,需要通過flip()方法將Buffer從寫模式切換到讀模式。
在讀模式下,可以讀取之前寫入到buffer的所有數據。一旦讀完了所有的數據,就需要清空緩衝區,讓它可以再次被寫入。
有兩種方式能清空緩衝區:
1)clear()方法會清空整個緩衝區。
2)compact()方法只會清除已經讀過的數據。任何未讀的數據都被移到緩衝區的起始處,新寫入的數據將放到緩衝區
未讀數據的後面。
例如:
public static void main(String[] args) {
// 測試輸入輸出流
try {
FileInputStream inputStream = new FileInputStream("E:/Temp.txt");
FileOutputStream outputStream = new FileOutputStream(
"E:/Temp_copy.txt");
FileChannel inChannel = inputStream.getChannel();
FileChannel ouChannel = outputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
// buffer.put((byte)23);
int count=0;
// 3. 將通道中的數據讀取到緩衝區
while (inChannel.read(buffer) != -1) {
count++;
// 切換成讀數據模式
buffer.flip();
// 4. 從緩衝區讀取數據寫入到通道中
ouChannel.write(buffer);
// 清空緩衝區
buffer.clear();
}
// buffer.flip();
// System.out.println("count="+buffer.get());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
大家可以用新的IO去替換傳統的IO。
異常處理
異常是程序中的一些錯誤,但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。
Java 語言定義了一些異常類在 java.lang 標準包中。
標準運行時異常類的子類是最常見的異常類。由於 java.lang 包是默認加載到所有的 Java 程序的,所以大部分從運行時異常類繼承而來的異常都可以直接使用。
Java所有異常的父類都是java.lang.Throwable、無論是內部的異常還是自定義異常。只有直接或者間接集成java.lang.Throwable類,JVM纔會認爲這是異常對象並且處理。
Java異常體系中Error爲錯誤,較Exception嚴重。Error不是由我們程序自身導致的,是JVM運行錯誤導致的,所以暫時不是我們討論的範圍。Exception則是異常的基類,又可以分爲"運行時異常"與"編譯時異常"(又稱爲"非檢查異常和檢查異常")。
非檢查異常RuntimeException; 在編譯階段無法檢查,如ArithmeticException(除0引發)、InputMismatchException(輸入的數據不能被轉換爲int類型引發)。引發非檢查異常大多數原因是編碼錯誤,應該檢查程序。
檢查異常(IOException),在編譯時可以檢查, 需要異常處理。處理方式有二種、(1)函數簽名中throws拋出異常 (2)tryCatch語句捕獲。
有時候我們可以用異常去追蹤一些調用棧。
在 Java 中你可以自定義異常。編寫自己的異常類時需要記住下面的幾點。
所有異常都必須是 Throwable 的子類。
如果希望寫一個檢查性異常類,則需要繼承 Exception 類。
如果你想寫一個運行時異常類,那麼需要繼承 RuntimeException 類。
可以像下面這樣定義自己的異常類:
class MyException extends Exception{
}
只繼承Exception 類來創建的異常類是檢查性異常類。
下面的 InsufficientFundsException 類是用戶定義的異常類,它繼承自 Exception。
一個異常類和其它任何類一樣,包含有變量和方法。
throws /和throw的區別
finally塊
finally塊不管異常是否發生,只要對應的try執行了,則它一定也執行。只有一種方法讓finally塊不執行:System.exit()。因此finally塊通常用來做資源釋放操作:關閉文件,關閉數據庫連接等等。
良好的編程習慣是:在try塊中打開資源,在finally塊中清理釋放這些資源。
需要注意的地方:
1、finally塊沒有處理異常的能力。處理異常的只能是catch塊。
2、在同一try…catch…finally塊中 ,如果try中拋出異常,且有匹配的catch塊,則先執行catch塊,再執行finally塊。如果沒有catch塊匹配,則先執行finally,然後去外面的調用者中尋找合適的catch塊。
3、在同一try…catch…finally塊中 ,try發生異常,且匹配的catch塊中處理異常時也拋出異常,那麼後面的finally也會執行:首先執行finally塊,然後去外圍調用者中尋找合適的catch塊。