day18-day21.IO

 

1. File

1.1. File類說明

存儲在變量,數組和對象中的數據是暫時的,當程序終止時他們就會丟失.爲了可以永java

久的保存程序中建立的數據,須要將他們存儲到硬盤或光盤的文件中.這些文件能夠移動,傳送,亦能夠被其餘程序使用.因爲數據存儲在文件中,因此咱們須要學習一個和文件有密切關係的類,叫作File,將要掌握獲取文件的屬性以及刪除和重命名文件.最終如何向文件中寫入數據和從文件中讀取數據.linux

那麼File類關心的是在磁盤上文件的存儲.算法

File類描述的是一個文件或文件夾。(文件夾也能夠稱爲目錄)windows

該類的出現是對文件系統的中的文件以及文件夾進行對象的封裝。能夠經過對象的思想來操做文件以及文件夾。設計模式

能夠用面向對象的處理問題,經過該對象的方法,能夠獲得文件或文件夾的信息方便了對文件與文件夾的屬性信息進行操做。api

文件包含不少的信息:如文件名、建立修改時間、大小、可讀可寫屬性等。數組

1.2. 體驗File

檢驗指定路徑下是否存在指定的目錄或者文件.網絡

// 檢驗指定路徑下是否存在指定的目錄或者文件.app

File file = new File("c:\\a.txt");jvm

System.out.println(file.exists());

// File對象是不是目錄

System.out.println(file.isDirectory());

// 對象是不是文件

    System.out.println(file.isFile());

 

 

結論:File對象也能夠表示不存在的文件。其實表明了一個抽象路徑

構建一個File類的實例並不會在機器上建立一個文件.無論文件是否存在均可以建立任意文件名的File實例,能夠調用File實例的exists方法判斷文件或目錄是否存在

1.3. 構造一個File類實例:

new File(String pathname);

 經過將給定路徑來建立一個新File實例。

new File(String parent, String child);

 根據parent路徑名字符串和child路徑名建立一個新File實例。

         parent是指上級目錄的路徑,完整的路徑爲parent+child.

new File(File parent, String child);

 根據parent抽象路徑名和child路徑名建立一個新File實例。

 parent是指上級目錄的路徑,完整的路徑爲parent.getPath()+child.

說明:

若是指定的路徑不存在(沒有這個文件或是文件夾),不會拋異常,這時file.exists()返回false

 

新建File對象File file=new File();

public static void main(String[] args) {

File file = new File();

}

 

1:建立File對象須要導包, import java.io.File

2:File對象沒有無參數構造.建立對象須要傳參.

根據API文檔提示,傳入一個文件的字符串路徑. String path="c:/a.txt";

(a.txt 文件在c盤下已經存在)

 

//file 是一個文件對象

String path = "c:/a.txt";

File file = new File(path);

 

File類的對象,既能夠表明文件也能夠表明文件夾。

 

public static void main(String[] args) {

//file 是一個文件夾

String path = "c:/test";

File file = new File(path);

}

 

1.4. 路徑:

路徑就是文件或文件夾所在的位置。

 

1.4.1. 路徑分割符:

上下級文件夾之間使用分隔符分開:

Windows中分隔符爲'\',在Unix/Linux中分隔符爲'/'

跨平臺的目錄分隔符

更專業的作法是使用File.separatorChar,這個值就會根據系統獲得的相應的分割符。

例:new File("c:" + File.separatorChar + "a.txt");

注意,若是是使用"\",則須要進行轉義,寫爲"\\"才能夠,若是是兩個"\",則寫爲"\\\\"

 

實驗:

在如下代碼的path處寫不一樣的路徑試一下,並觀察輸出結果。

File file = new File(path);

System.out.println(file.getAbsolutePath());

 

1.4.2. 絕對路徑與相對路徑:

對於UNIX平臺,絕對路徑名的前綴是"/"。相對路徑名沒有前綴。

對於Windows平臺,絕對路徑名的前綴由驅動器號和一個":"組成,例"c:\\..."。相對路徑沒有盤符前綴。

相對路徑:

相對路徑是指相對於某位置的路徑,是指相對於當前目錄。

在執行Java程序時,相對路徑爲執行java命令時當前所在的目錄。

實驗:

在不一樣的路徑下執行java命令運行如下程序,並觀察輸出結果。

File file = new File("a.txt");

System.out.println(file.getAbsolutePath());

通常在使用時,建議用絕對路徑,由於相對路徑容易出問題,很差肯定到底在什麼地方。

//相對路徑

//File file = new File("src/a.txt");

 

 

 

 

 

1.5. File類中經常使用的方法:

建立:

createNewFile() 在指定位置建立一個空文件,成功就返回true,若是已存在就不建立而後返回false

mkdir() 在指定位置建立目錄,這隻會建立最後一級目錄,若是上級目錄不存在就拋異常。

mkdirs() 在指定位置建立目錄,這會建立路徑中全部不存在的目錄。

renameTo(File dest) 重命名文件或文件夾,也能夠操做非空的文件夾,文件不一樣時至關於文件的剪切,剪切時候不能操做非空的文件夾。移動/重命名成功則返回true,失敗則返回false

 

 

刪除:

delete() 刪除文件或一個空文件夾,若是是文件夾且不爲空,則不能刪除,成功返回true,失敗返回false

deleteOnExit() 在虛擬機終止時,請求刪除此抽象路徑名錶示的文件或目錄,保證程序異常時建立的臨時文件也能夠被刪除

 

 

判斷:

exists() 文件或文件夾是否存在。

isFile() 是不是一個文件,若是不存在,則始終爲false

isDirectory() 是不是一個目錄,若是不存在,則始終爲false

isHidden() 是不是一個隱藏的文件或是不是隱藏的目錄。

isAbsolute() 測試此抽象路徑名是否爲絕對路徑名。

 

 

獲取:

getName() 獲取文件或文件夾的名稱,不包含上級路徑。

getPath()       返回絕對路徑,能夠是相對路徑,可是目錄要指定

getAbsolutePath() 獲取文件的絕對路徑,與文件是否存在不要緊

length() 獲取文件的大小(字節數),若是文件不存在則返回0L,若是是文件夾也返回0L

getParent() 返回此抽象路徑名父目錄的路徑名字符串;若是此路徑名沒有指定父目錄,則返回null

lastModified() 獲取最後一次被修改的時間。

文件夾相關:

staic File[] listRoots() 列出全部的根目錄(Window中就是全部系統的盤符)

list() 返回目錄下的文件或者目錄名,包含隱藏文件。對於文件這樣操做會返回null

list(FilenameFilter filter) 返回指定當前目錄中符合過濾條件的子文件或子目錄。對於文件這樣操做會返回null

listFiles() 返回目錄下的文件或者目錄對象(File類實例),包含隱藏文件。對於文件這樣操做會返回null

listFiles(FilenameFilter filter) 返回指定當前目錄中符合過濾條件的子文件或子目錄。對於文件這樣操做會返回null

 

 

 

 

 

1.6. 案例:

1,列出指定目錄中全部的子文件名與全部的子目錄名。

2,列出指定目錄中全部的子文件名與全部的子目錄名,要求目錄名與文件名分開列出,格式以下:

子目錄:

...

...

子文件:

...

...

3,列出指定目錄中全部擴展名爲.java的文件。

4,列出指定目錄中全部擴展名爲.class的文件。

5,思考第3與第4題,代碼是否是重複呢,若是想要列出其中的全部.txt文件,是否是要再寫一個類呢?

a, 請自行設計一個工具方法,能夠傳遞指定的擴展名,工具方法會過列出指定目錄中指定擴展名的全部子文件與子文件夾。

b, 請利用FilenameFilter接口寫一個工具類,能夠傳遞指定的過濾規則。

6,列出指定目錄中全部的子孫文件與子孫目錄名,只須要列出名稱便可。

 

解題: 列出指定目錄中全部的子文件名與全部的子目錄名。

需求1:獲取全部的c:/test test目錄下的全部文件和文件夾

解題思路:

   代碼須要封裝,就須要建立方法,並在main方法中調用和測試. 方法名要有意義: listAllFilesAndDirs

第一步:建立File對象

第二步:查找File類中可用的方法,想要獲取該目錄下的全部子文件和子目錄

    第三步:顯示這些文件和文件夾的名稱

實現:

/**

 * 列出指定目錄中全部包含的子文件與子目錄

 */

public static void listAllFilesAndDirs(String path) {

// 1.建立File對象,表示這個目錄

File dir = new File(path);

// 2.經過list方法獲得所包含的全部子目錄與子文件名稱

String[] names = dir.list();

// 3顯示這些名稱

for (int i = 0; i < names.length; i++) {

System.out.println(names[i]);

}

}

 

解題: 列出指定目錄中全部的子文件名與全部的子目錄名,要求目錄名與文件名分開列出

案例1把文件和文件夾都列了出來,可是沒法區分文件和文件夾.File類有判斷文件和文件夾的方法,可是list方法返回的是String數組,這個list() 方法沒法知足咱們的需求.繼續查找File的方法.查看api找到 File[] listFiles()  發現該方法返回的是一個File數組。

思路:

第一步:建立listAllFilesAndDirs(String path)方法,接受路徑

第二步: 建立File對象表示這個目錄

第三步: 經過listFiles方法獲得所包含的全部子目錄與子文件名稱

第四步: 獲得全部的文件名集合,與全部的文件夾名集合

第五步: 分別顯示文件名與文件夾名

實現

public static void listAllFilesAndDirs2(String path) {

// 1.建立File對象,表示這個目錄

File dir = new File(path);

// 2經過listFiles方法獲得所包含的全部子目錄與子文件名稱

File[] names = dir.listFiles();

// 3,分別顯示文件名與文件夾名

for (int i = 0; i < names.length; i++) {

File file = names[i];

if (file.isFile()) {

System.out.println(("子文件:"));

System.out.println("\t" + file.getName());

} else if (file.isDirectory()) {

System.out.println(("子目錄:"));

System.out.println("\t" + file.getName());

}

}

}

實現二:

public static void listAllFilesAndDirs(String path) {

//1建立File對象表示這個目錄

File dir = new File(path);

 

//2經過listFiles方法獲得所包含的全部子目錄與子文件名稱

File[] names = dir.listFiles();

 

//3,獲得全部的文件名集合,與全部的文件夾名集合

List<File> filesList = new ArrayList<File>();

List<File> dirsList = new ArrayList<File>();

for (int i = 0; i < names.length; i++) {

File file = names[i];

if (file.isFile()) {

filesList.add(file);

} else if (file.isDirectory()) {

dirsList.add(file);

}

}

 

//4,分別顯示文件名與文件夾名

System.out.println("子文件:");

for (int i = 0; i < filesList.size(); i++) {

System.out.println("\t" + filesList.get(i).getName());

}

System.out.println("子目錄:");

for (int i = 0; i < dirsList.size(); i++) {

System.out.println("\t" + dirsList.get(i).getName());

}

}

 

練習3,列出指定目錄中全部擴展名爲.java的文件。

需求: 從指定目錄中找到指定擴展名的文件,並列出來

思路

第一步: 建立lsitAllFiles方法,接受路徑和文件後綴名

第二步: 獲取全部的子文件和子文件夾

第三步: 從中找出符合條件的文件並顯示出來

 

注意:不一樣系統對於路徑的 windows系統使用斜線做爲路徑分隔符 "\" linux 系統使用反斜線做爲路徑分隔符"/"java是跨平臺的語言,java程序若是部署到linux系統上,若是程序中有File對象, 可使用FileseparatorChar (字段)

 

public class FileTest2 {

public static void main(String[] args) {

String path = "c:" + File.separatorChar + "test";

File file = new File(path);

listtAllFiles(file, "java");

}

 

/**

 * 從指定目錄中找到指定擴展名的文件,並列出來

 *

 */

public static void listtAllFiles(File dir, String extension) {

// 1.獲取全部的子文件和子文件夾

File[] files = dir.listFiles();

 

// 2.從中找出符合條件的文件並顯示出來

for (int i = 0; i < files.length; i++) {

File file = files[i];

// 3.須要以指定文件後綴結尾纔算符合條件

if (file.getName().endsWith(extension)) {

System.out.println(file.getName());

}

}

}

}

 

練習4:

public class FileTest2 {

public static void main(String[] args) {

String path = "c:" + File.separatorChar + "test";

File file = new File(path);

listtAllFiles2(file, "txt");

}

 

 

/**

 * FilenameFilter接口寫一個工具類,能夠傳遞指定的過濾規則。

     *  從指定目錄中找到指定擴展名的文件,並列出來

     *

 * */

public static void listtAllFiles2(File dir, String name) {

// 1.獲取全部的子文件和子文件夾

String[] files = dir.list(new DirFilter("txt"));

 

// 2顯示名稱

for (int i = 0; i < files.length; i++) {

System.out.println(files[i]);

}

}

}

 

class DirFilter implements FilenameFilter {

private String extension;

 

public DirFilter() {

 

}

 

public DirFilter(String extension) {

this.extension = extension;

}

 

@Override

public boolean accept(File dir, String name) {

return name.endsWith(extension);

}

}

注意: DirFilter 就是實現了accept 方法.提供給File類的list方法使用.

 

2. IO流體驗與簡介

 

File對象能夠表示存在的文件或文件夾,也能夠表示不存在的。

咱們想要獲得文件的內容怎麼辦,File只是操做文件,文件的內容如何處理就須要使用io流技術了。

例如在C盤下有一個名稱爲a.txt的文本文件.想要經過Java程序讀出來文件中的內容,須要使用IO流技術.一樣想要將程序中的數據,保存到硬盤的文件中,也須要IO流技術.

讀和寫文件文件示例:

public class IoTest {

public static void main(String[] args) throws FileNotFoundException,

IOException {

writFileTest();

 

readFileTest();

}

 

private static void writFileTest() throws FileNotFoundException,

IOException {

// 建立文件對象

File file = new File("c:\\a.txt");

// 建立文件輸出流

FileOutputStream fos = new FileOutputStream(file);

fos.write('g');

fos.write('z');

fos.write('i');

fos.write('t');

fos.write('c');

fos.write('a');

fos.write('s');

fos.write('t');

fos.close();

}

 

private static void readFileTest() throws FileNotFoundException,

IOException {

// 建立文件對象

File file = new File("c:\\a.txt");

// 建立文件輸入流

FileInputStream fis = new FileInputStream(file);

// 有對多長,就讀多少字節。

for (int i = 0; i < file.length(); i++) {

System.out.print((char) fis.read());

}

fis.close();

}

}

 

 

當完成流的讀寫時,應該經過調用close方法來關閉它,這個方法會釋放掉十分有限的操做系統資源.若是一個應用程序打開了過多的流而沒有關閉它們,那麼系統資源將被耗盡.

IO流簡介:(Input/Output

I/O類庫中使用這個抽象概念。Java對設備中數據的操做是經過流的方式。

表示任何有能力產出數據的數據源對象,或者是有能力接受數據的接收端對象。屏蔽了實際的I/O設備中處理數據的細節。IO流用來處理設備之間的數據傳輸。設備是指硬盤、內存、鍵盤錄入、網絡等。

Java用於操做流的對象都在IO包中。IO流技術主要用來處理設備之間的數據傳輸。

因爲Java用於操做流的對象都在IO包中。因此使用IO流須要導包如:import java.io.*;

IO流的分類

流按操做數據類型的不一樣分爲兩種:字節流與字符流。

流按流向分爲:輸入流,輸出流(以程序爲參照物,輸入到程序,或是從程序輸出)

3. 字節流

什麼是字節流

    計算機中都是二進制數據,一個字節是82進制位.字節能夠表示全部的數據,好比文本,音頻,視頻.圖片,都是做爲字節存在的.也就是說字節流處理的數據很是多。

在文本文件中存儲的數據是以咱們能讀懂的方式表示的。而在二進制文件中存儲的數據是用二進制形式表示的。咱們是讀不懂二進制文件的,由於二進制文件是爲了讓程序來讀取而設計的。例如,Java的源程序(.java源文件)存儲在文本文件中,可使用文本編輯器閱讀,可是Java的類(字節碼文件)存儲在二進制文件中,能夠被Java虛擬機閱讀。二進制文件的優點在於它的處理效率比文本文件高。

咱們已經知道File對象封裝的是文件或者路徑屬性,可是不包含向(從)文件讀(寫)數據的方法。爲了實現對文件的讀和寫操做須要學會正確的使用JavaIO建立對象。

字節流的抽象基類:

輸入流:java.io.InputStream

輸出流:java.io.OutputStream

特色:

字節流的抽象基類派生出來的子類名稱都是以其父類名做爲子類名的後綴。

如:FileInputStream, ByteArrayInputStream等。

說明:

字節流處理的單元是一個字節,用於操做二進制文件(計算機中全部文件都是二進制文件)

3.1. InputStream

案例:讀取"c:/a.txt"文件中的全部內容並在控制檯顯示出來。

注意:事先準備一個a.txt並放到c:/下,不要保存中文。

a, 使用read()方法實現。

b, 使用int read(byte[] b)方法實現。

寫代碼讀取"c:/a.txt"文件中的全部的內容並在控制檯顯示出來

實現:

查看api文檔(本身必定要動手)

InputStream read方法,一次讀取一個字節,OutputStreamwrite方法一次寫一個int。發現這兩個類都是抽象類。意味着不能建立對象,那麼須要找到具體的子類來使用。

經過查看api文檔,找到了FileInputStream類,該類正是咱們體驗Io流的一個輸入流。

實現;顯示指定文件內容。

明確使用流,使用哪一類流?使用輸入流,FileInputStream

第一步:

1:打開流(即建立流)

第二步:

2:經過流讀取內容

第三步:

3:用完後,關閉流資源

顯然流是Java中的一類對象,要打開流其實就是建立具體流的對象,因爲是讀取硬盤上的文件,應該使用輸入流。因此找到了InputStream類,可是InputStream是抽象類,須要使用它的具體實現類來建立對象就是FileInputStream。經過new 調用FileInputStream 的構造方法來建立對象。發現FileInputStream的構造方法須要指定文件的來源。查看構造方法,能夠接受字符串也能夠接受File對象。咱們經過構建File對象指定文件路徑。

使用流就像使用水管同樣,要打開就要關閉。因此打開流和關閉流的動做是比不可少的。如何關閉流?使用close方法便可,當完成流的讀寫時,應該經過調用close方法來關閉它,這個方法會釋放掉十分有限的操做系統資源.若是一個應用程序打開了過多的流而沒有關閉它們,那麼系統資源將被耗盡.

如何經過流讀取內容?

查找api文檔經過read方法,查看該方法,發現有返回值,而且是int類型的,該方法一次讀取一個字節(byte

 

3.1.1. 輸入流讀取方式1

read方法()

一次讀取一個字節,讀到文件末尾返回-1.

仔細查看api文檔發現read方法若是讀到文件的末尾會返回-1。那麼就能夠經過read方法的返回值是不是-1來控制咱們的循環讀取。

/**

 * 根據read方法返回值的特性,若是獨到文件的末尾返回-1,若是不爲-1就繼續向下讀。

 * */

private static void showContent(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

int len = fis.read();

while (len != -1) {

System.out.print((char)len);

len = fis.read();

 

}

// 使用完關閉流

fis.close();

}

 

咱們習慣這樣寫:

/**

 * 根據read方法返回值的特性,若是獨到文件的末尾返回-1,若是不爲-1就繼續向下讀。

 * */

private static void showContent(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

int len;

while ((len = fis.read()) != -1) {

System.out.print((char) len);

}

// 使用完關閉流

fis.close();

}

3.1.2. 輸入流讀取方式2

使用read(byte[] b) 方法。使用緩衝區(關鍵是緩衝區大小的肯定)

使用read方法的時候,流須要讀一次就處理一次,能夠將讀到的數據裝入到字節數組中,一次性的操做數組,能夠提升效率

問題1:緩衝區大小

那麼字節數組如何定義?定義多大?

能夠嘗試初始化長度爲5byte數組。經過read方法,往byte數組中存內容

那麼該read方法返回的是往數組中存了多少字節。

/**

 * 使用字節數組存儲讀到的數據

 * */

private static void showContent2(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

// 經過流讀取內容

byte[] byt = new byte[5];

int len = fis.read(byt);

for (int i = 0; i < byt.length; i++) {

System.out.print((char) byt[i]);

}

 

// 使用完關閉流

fis.close();

}

問題1: 緩衝區過小:

數據讀取不完.

測試發現問題,因爲數組過小,只裝了5個字節。而文本的字節大於數組的長度。那麼很顯然能夠將數組的長度定義大一些。例如1024個。

/**

 * 使用字節數組存儲讀到的數據

 * */

private static void showContent2(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

// 經過流讀取內容

byte[] byt = new byte[1024];

int len = fis.read(byt);

for (int i = 0; i < byt.length; i++) {

System.out.print(byt[i]);

}

 

// 使用完關閉流

fis.close();

}

問題三:緩衝區有默認值.

測試,打印的效果打印出了不少0,由於數組數組有默認初始化值,因此,咱們將數組的數據所有都遍歷和出來.如今須要的是取出數組中的部分數據.須要將循環條件修改仔細查看api文檔。發現該方法read(byte[] b)返回的是往數組中存入了多少個字節。就是數組實際存儲的數據個數。

/**

 * 使用字節數組存儲讀到的數據

 * */

private static void showContent2(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

// 經過流讀取內容

byte[] byt = new byte[1024];

int len = fis.read(byt);

for (int i = 0; i <len; i++) {

System.out.print(byt[i]);

}

 

// 使用完關閉流

fis.close();

}

總結:

問題一:爲何打印的不是字母而是數字,

    字母對應的碼值

    如何顯示字符,強轉爲char便可

問題二:注意:回車和換行的問題。

    windows的換車和換行是"\r\n" 對應碼錶是1310

3.1.3. 輸入流讀取方式3

使用read(byte[] b,int off,int len)

查看api文檔,

b顯然是一個byte類型數組,當作容器來使用

off,是指定從數組的什麼位置開始存字節

len,但願讀多少個

其實就是把數組的一部分當作流的容器來使用。告訴容器,從什麼地方開始裝要裝多少。

/**

 * 把數組的一部分當作流的容器來使用

 * read(byte[] b,int off,int len)

 */

private static void showContent3(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

// 經過流讀取內容

byte[] byt = new byte[1024];

// 從什麼地方開始存讀到的數據

int start = 5;

 

// 但願最多讀多少個(若是是流的末尾,流中沒有足夠數據)

int maxLen = 6;

 

// 實際存放了多少個

int len = fis.read(byt, start, maxLen);

 

for (int i = start; i < start + maxLen; i++) {

System.out.print((char) byt[i]);

}

 

// 使用完關閉流

fis.close();

}

 

需求2測試skip方法

經過Io,讀取"c:/a.txt"文件中的第9個字節到最後全部的內容並在控制檯顯示出來。

分析:其實就是要跳過文件中的一部分字節,須要查找API文檔。可使用skip方法skip(long n),參數跟的是要跳過的字節數。

咱們要從第9個開始讀,那麼要跳過前8個便可。

/**

 * skip方法

 *

 * */

private static void showContent4(String path) throws IOException {

// 打開流

FileInputStream fis = new FileInputStream(path);

 

// 經過流讀取內容

byte[] byt = new byte[1024];

fis.skip(8);

int len = fis.read(byt);

System.out.println(len);

System.out.println("**********");

for (int i = 0; i < len; i++) {

System.out.println((char) byt[i]);

}

// 使用完關閉流

fis.close();

}

3.1.4. 輸入流讀取方式4

使用緩衝(提升效率),並循環讀取(讀完全部內容).

總結:讀完文件的全部內容。很顯然可使用普通的read方法,一次讀一個字節直到讀到文件末尾。爲了提升效率可使用read(byte[] byt);方法就是所謂的使用緩衝提升效率。咱們能夠讀取大文本數據測試(大於1K的文本文件.)

/**

 * 使用字節數組當緩衝

 * */

private static void showContent5(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

byte[] byt = new byte[1024];

int len = fis.read(byt);

System.out.println(len);

String buffer = new String(byt, 0, len);

System.out.print(buffer);

}

注意:如何將字節數組轉成字符串? 能夠經過建立字符串對象便可。

發現:一旦數據超過1024個字節,數組就存儲不下。

如何將文件的剩餘內容讀完?

咱們能夠經過經過循環保證文件讀取完

/**

 * 使用字節數組當緩衝

 * */

private static void showContent7(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

byte[] byt = new byte[1024];

int len = 0;

while ((len = fis.read(byt)) != -1) {

System.out.println(new String(byt, 0, len));

}

}

 

3.2. OutputStream

字節輸出流

案例:

1,寫代碼實現把"Hello World!"寫到"c:/a.txt"文件中。

a, c:/a.txt不存在時,測試一下。

b, c:/a.txt存在時,也測試一下。

要寫兩個版本:

a, 使用write(int b) 實現。

b, 使用write(byte[] b) 實現。

2,在已存在的c:/a.txt文本文件中追加內容:「Java IO」

顯然此時須要向指定文件中寫入數據。

使用的就是能夠操做文件的字節流對象。OutputStream。該類是抽象類,須要使用具體的實現類來建立對象查看API文檔,找到了OutputStream的實現類FileOutputStream 建立FileOutputStream 流對象,必須指定數據要存放的目的地。經過構造函數的形式。建立流對象時,調用了系統底層的資源。在指定位置創建了數據存放的目的文件。

流程:

1:打開文件輸出流,流的目的地是指定的文件

2:經過流向文件寫數據

3: 用完流後關閉流

3.2.1. 輸出流寫出方式1

使用write(int b)方法,一次寫出一個字節.

C盤下建立a.txt文本文件

import java.io.FileOutputStream;

import java.io.IOException;

 

public class IoTest2 {

public static void main(String[] args) throws IOException {

String path = "c:\\a.txt";

writeTxtFile(path);

}

 

private static void writeTxtFile(String path) throws IOException {

// 1:打開文件輸出流,流的目的地是指定的文件

FileOutputStream fos = new FileOutputStream(path);

 

// 2:經過流向文件寫數據

fos.write('j');

fos.write('a');

fos.write('v');

fos.write('a');

// 3:用完流後關閉流

fos.close();

 

}

}

 

c盤下的a.txt不存在會怎麼樣?

測試:c盤下的a.txt文件刪除,發現當文件不存在時,會自動建立一個,可是建立不了多級目錄。

注意:使用write(int b)方法,雖然接收的是int類型參數,可是write 的常規協定是:向輸出流寫入一個字節。要寫入的字節是參數 b 的八個低位b 24 個高位將被忽略。

3.2.2. 輸出流寫出方式2

使用write(byte[] b),就是使用緩衝.提升效率.

上述案例中的使用了OutputStram write方法,一次只能寫一個字節。成功的向文件中寫入了內容。可是並不高效,如和提升效率呢?是否應該使用緩衝,根據字節輸入流的緩衝原理,是否能夠將數據保存中字節數組中。經過操做字節數組來提升效率。查找API文檔,在OutputStram類中找到了write(byte[] b)方法,將 b.length 個字節從指定的 byte 數組寫入此輸出流中。

如何將字節數據保存在字節數組中,以字符串爲例,」hello , world」 如何轉爲字節數組。顯然經過字符串的getBytes方法便可。

public class IoTest2 {

public static void main(String[] args) throws IOException {

String path = "c:\\a.txt";

writeTxtFile(path);

}

 

private static void writeTxtFile(String path) throws IOException {

// 1:打開文件輸出流,流的目的地是指定的文件

FileOutputStream fos = new FileOutputStream(path);

 

// 2:經過流向文件寫數據

byte[] byt = "java".getBytes();

fos.write(byt);

// 3:用完流後關閉流

fos.close();

}

}

 

仔細查看a.txt文本文件發現上述程序每運行一次,老的內容就會被覆蓋掉。,那麼如何不覆蓋已有信息,可以往a.txt裏追加信息呢。查看API文檔,發現FileOutputStream類中的構造方法中有一個構造能夠實現追加的功能FileOutputStream(File file, boolean append)  第二個參數,append - 若是爲 true,則將字節寫入文件末尾處,而不是寫入文件開始處

private static void writeTxtFile(String path) throws IOException {

// 1:打開文件輸出流,流的目的地是指定的文件

FileOutputStream fos = new FileOutputStream(path,true);

 

// 2:經過流向文件寫數據

byte[] byt = "java".getBytes();

fos.write(byt);

// 3:用完流後關閉流

fos.close();

}

 

3.3. 字節流文件拷貝

3.3.1. 字節輸入輸出流綜合使用

經過字節輸出流向文件中寫入一些信息,並使用字節輸入流把文件中的信息顯示到控制檯上。

public class IoTest3 {

public static void main(String[] args) throws IOException {

String path = "c:\\b.txt";

String content = "hello java";

 

writeFile(path, content);

 

readFile(path);

}

 

public static void writeFile(String path, String content)

throws IOException {

// 打開文件輸出流

FileOutputStream fos = new FileOutputStream(path);

byte[] buffer = content.getBytes();

// 向文件中寫入內容

fos.write(buffer);

// 關閉流

fos.close();

 

}

 

public static void readFile(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

byte[] byt = new byte[1024];

int len = 0;

while ((len = fis.read(byt)) != -1) {

System.out.println(new String(byt, 0, len));

}

        // 關閉流

fos.close();

 

}

}

 

注意輸出流的細節:

這個輸出流顯然只適合小數據的寫入,若是有大數據想要寫入,咱們的byte數組,該如何定義?

上述案例中咱們將輸入流和輸出流進行和綜合使用,若是嘗試進輸出流換成文本文件就能夠實現文件的拷貝了.

什麼是文件拷貝?很顯然,先開一個輸入流,將文件加載到流中,再開一個輸出流,將流中數據寫到文件中。就實現了文件的拷貝。

分析:

第一步:須要打開輸入流和輸出流

第二步:讀取數據並寫出數據

第三步:關閉流

public class IoTest3 {

 

public static void main(String[] args) throws IOException {

 

String srcPath = "c:\\a.txt";

String destPath = "d:\\a.txt";

copyFile(srcPath, destPath);

}

 

public static void copyFile(String srcPath, String destPath)

throws IOException {

 

}

 

}

3.3.2. 字節流拷貝文件實現1

讀一個字節寫一個字節read write

public class IoTest3 {

 

public static void main(String[] args) throws IOException {

 

String srcPath = "c:\\a.txt";

String destPath = "d:\\a.txt";

copyFile(srcPath, destPath);

}

 

public static void copyFile(String srcPath, String destPath)

throws IOException {

// 打開輸入流,輸出流

FileInputStream fis = new FileInputStream(srcPath);

FileOutputStream fos = new FileOutputStream(destPath);

 

// 讀取和寫入信息

int len = 0;

while ((len = fis.read()) != -1) {

fos.write(len);

}

 

// 關閉流

fis.close();

fos.close();

}

 

}

 

文本文件在計算機中是以二進制形式存在的,能夠經過io流來拷貝,那麼圖片能不能拷貝呢?視頻呢?音頻呢?

public class IoTest3 {

 

public static void main(String[] args) throws IOException {

 

String srcPath = "c:\\.jpg";

String destPath = "d:\\.jpg";

copyFile(srcPath, destPath);

}

 

public static void copyFile(String srcPath, String destPath)

throws IOException {

// 打開輸入流,輸出流

FileInputStream fis = new FileInputStream(srcPath);

FileOutputStream fos = new FileOutputStream(destPath);

 

// 讀取和寫入信息

int len = 0;

while ((len = fis.read()) != -1) {

fos.write(len);

}

 

// 關閉流

fis.close();

fos.close();

}

 

}

測試通通經過,因此字節流能夠操做全部的文件。只是發現程序很慢,須要很長時間。特別是拷貝音頻和視頻文件時。

爲何?由於每次讀一個字節再寫一個字節效率很低。很顯然這樣效率低下的操做不是咱們想要的。有沒有更快更好的方法呢,是否可使用緩衝區來提升程序的效率呢。

 

3.3.3. 字節流拷貝文件實現2

使用字節數組做爲緩衝區

public static void copyFile2(String srcPath, String destPath)

throws IOException {

// 打開輸入流,輸出流

FileInputStream fis = new FileInputStream(srcPath);

FileOutputStream fos = new FileOutputStream(destPath);

 

// 讀取和寫入信息

int len = 0;

 

// 使用字節數組,當作緩衝區

byte[] byt = new byte[1024];

while ((len = fis.read(byt)) != -1) {

fos.write(byt);

}

 

// 關閉流

fis.close();

fos.close();

}

 

問題1: 使用緩衝(字節數組)拷貝數據,拷貝後的文件大於源文件.

測試該方法,拷貝文本文件,仔細觀察發現和源文件不太一致。

打開文件發現拷貝後的文件和拷貝前的源文件不一樣,拷貝後的文件要比源文件多一些內容問題就在於咱們使用的容器,這個容器咱們是重複使用的,新的數據會覆蓋掉老的數據,顯然最後一次讀文件的時候,容器並無裝滿,出現了新老數據並存的狀況。

因此最後一次把容器中數據寫入到文件中就出現了問題。

如何避免?使用FileOutputStream write(byte[] b, int off, int len)

b 是容器,off是從數組的什麼位置開始,len是獲取的個數,容器用了多少就寫出多少。

public static void copyFile2(String srcPath, String destPath)

throws IOException {

// 打開輸入流,輸出流

FileInputStream fis = new FileInputStream(srcPath);

FileOutputStream fos = new FileOutputStream(destPath);

 

// 讀取和寫入信息

int len = 0;

 

// 使用字節數組,當作緩衝區

byte[] byt = new byte[1024];

while ((len = fis.read(byt)) != -1) {

fos.write(byt, 0, len);

}

 

// 關閉流

fis.close();

fos.close();

}

 

使用緩衝拷貝視頻,能夠根據拷貝的需求調整數組的大小,通常是1024的整數倍。發現使用緩衝後效率大大提升。

3.4. 字節流的異常處理

上述案例中全部的異常都只是進行了拋出處理,這樣是不合理的。因此上述代碼並不完善,由於異常沒有處理。

當咱們打開流,讀和寫,關閉流的時候都會出現異常,異常出現後,後面的代碼都不會執行了。假設打開和關閉流出現了異常,那麼顯然close方法就不會再執行。那麼會對程序有什麼影響?

案例:

public class IoTest4 {

public static void main(String[] args) throws IOException,

InterruptedException {

String path = "c:\\b.txt";

readFile(path);

}

 

private static void readFile(String path) throws IOException,

InterruptedException {

FileInputStream fis = new FileInputStream(path);

byte[] byt = new byte[1024];

int len = fis.read(byt);

System.out.println(new String(byt, 0, len));

// 讓程序睡眠,沒法執行到close方法。

Thread.sleep(1000 * 10);

fis.close();

}

}

 

在執行該程序的同時咱們嘗試去刪除b.txt文件。若是在該程序沒有睡醒的話,咱們是沒法刪除b.txt 文件的。由於b.txt還被該程序佔用着,這是很嚴重的問題,因此必定要關閉流。

目前咱們是拋出處理,一旦出現了異常,close就沒有執行,也就沒有釋放資源。那麼爲了保證close的執行該如何處理呢。

那麼就須要使用try{} catch(){}finally{}語句。try中放入可能出現異常的語句,catch是捕獲異常對象,fianlly是必定要執行的代碼

public class IoTest4 {

public static void main(String[] args) throws IOException,

InterruptedException {

String path = "c:\\b.txt";

readFile(path);

}

 

private static void readFile(String path) {

FileInputStream fis = null;

try {

fis = new FileInputStream(path);

byte[] byt = new byte[1024];

int len = fis.read(byt);

System.out.println(new String(byt, 0, len));

} catch (IOException e) {

// 拋出運行時異常

throw new RuntimeException(e);

} finally {

// close方法放入finally中保證必定會執行

// 先判斷是否空指針

if (fis != null) {

try {

fis.close();

} catch (Exception e) {

throw new RuntimeException(e);

}

 

}

 

}

 

}

}

文件拷貝的異常處理:

public static void copyFile(String srcPath, String destPath) {

 

FileInputStream fis = null;

FileOutputStream fos = null;

try {

fis = new FileInputStream(srcPath);

fos = new FileOutputStream(destPath);

 

byte[] byt = new byte[1024 * 1024];

int len = 0;

while ((len = fis.read(byt)) != -1) {

 

fos.write(byt, 0, len);

}

} catch (IOException e) {

throw new RuntimeException(e);

} finally {

if (fis != null) {

try {

fis.close();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

if (fos != null) {

try {

fos.close();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

 

}

 

}

注意:

在最後的close代碼中可能會有問題,兩個close,若是第一個close方法出現了異常,並拋出了運行時異常,那麼程序仍是中止了。下面的close方法就沒有執行到。

那麼爲了保證close的執行,將第二個放到fianlly中便可。

public static void copyFile(String srcPath, String destPath) {

 

FileInputStream fis = null;

FileOutputStream fos = null;

try {

fis = new FileInputStream(srcPath);

fos = new FileOutputStream(destPath);

 

byte[] byt = new byte[1024 * 1024];

int len = 0;

while ((len = fis.read(byt)) != -1) {

 

fos.write(byt, 0, len);

}

} catch (IOException e) {

throw new RuntimeException(e);

} finally {

 

try {

if (fis != null) {

fis.close();

}

} catch (IOException e) {

throw new RuntimeException(e);

} finally {

if (fos != null) {

try {

fos.close();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

 

}

}

 

}

 

3.5. 字節緩衝流

3.5.1. 緩衝流

上述程序中咱們爲了提升流的使用效率,自定義了字節數組,做爲緩衝區.Java其實提供了專門的字節流緩衝來提升效率.

BufferedInputStreamBufferedOutputStream

BufferedOutputStreamBufferedOutputStream類能夠經過減小讀寫次數來提升輸入和輸出的速度。它們內部有一個緩衝區,用來提升處理效率。查看API文檔,發現能夠指定緩衝區的大小。其實內部也是封裝了字節數組。沒有指定緩衝區大小,默認的字節是8192

顯然緩衝區輸入流和緩衝區輸出流要配合使用。首先緩衝區輸入流會將讀取到的數據讀入緩衝區,當緩衝區滿時,或者調用flush方法,緩衝輸出流會將數據寫出。

注意:固然使用緩衝流來進行提升效率時,對於小文件可能看不到性能的提高。可是文件稍微大一些的話,就能夠看到實質的性能提高了。

 

public class IoTest5 {

public static void main(String[] args) throws IOException {

String srcPath = "c:\\a.mp3";

String destPath = "d:\\copy.mp3";

copyFile(srcPath, destPath);

}

 

public static void copyFile(String srcPath, String destPath)

throws IOException {

// 打開輸入流,輸出流

FileInputStream fis = new FileInputStream(srcPath);

FileOutputStream fos = new FileOutputStream(destPath);

 

// 使用緩衝流

BufferedInputStream bis = new BufferedInputStream(fis);

BufferedOutputStream bos = new BufferedOutputStream(fos);

 

// 讀取和寫入信息

int len = 0;

 

while ((len = bis.read()) != -1) {

bos.write(len);

}

 

// 關閉流

bis.close();

bos.close();

}

 

}

 

 

4. 字符流

計算機並不區分二進制文件與文本文件。全部的文件都是以二進制形式來存儲的,所以,從本質上說,全部的文件都是二進制文件。因此字符流是創建在字節流之上的,它可以提供字符層次的編碼和解碼。例如,在寫入一個字符時,Java虛擬機會將字符轉爲文件指定的編碼(默認是系統默認編碼),在讀取字符時,再將文件定的編碼轉化爲字符。

 

常見的碼錶以下:

ASCII 美國標準信息交換碼。用一個字節的7位能夠表示。

ISO8859-1 拉丁碼錶。歐洲碼錶,用一個字節的8位表示。又稱Latin-1(拉丁編碼)西歐語言ASCII碼是包含的僅僅是英文字母,而且沒有徹底佔滿256個編碼位置,因此它以ASCII爲基礎,在空置的0xA0-0xFF的範圍內,加入192個字母及符號,

藉以供使用變音符號的拉丁字母語言使用。從而支持德文,法文等。於是它依然是一個單字節編碼,只是比ASCII更全面。

GB2312  英文佔一個字節,中文佔兩個字節.中國的中文編碼表。

GBK 中國的中文編碼表升級,融合了更多的中文文字符號。

Unicode:  國際標準碼規範,融合了多種文字。全部文字都用兩個字節來表示,Java語言使用的就是unicode

UTF-8 最多用三個字節來表示一個字符。

(咱們之後接觸最多的是iso8859-1gbkutf-8

查看上述碼錶後,很顯然中文的iso8859-1中是沒有對映的編碼的。或者一個字符在2中碼錶中對應的編碼不一樣,例若有一些字在不一樣的編碼中是有交集的,例如bjg5 gbk 中的漢字簡體和繁體多是同樣的,就是有交集,可是在各自碼錶中的數字不同。

例如

使用gbk 將中文保存在計算機中,

 

對映  100  200   若是使用big5 打開

可能     ...   

不一樣的編碼對映的是不同的。

很顯然,咱們使用什麼樣的編碼寫數據,就須要使用什麼樣的編碼來對數據。

ISO8859-1:一個字節

GBK: 兩個字節包含了英文字符和擴展的中文 ISO8859-1+中文字符

UTF-8 萬國碼,推行的。是1~3個字節不等長。英文存的是1個字節,中文存的是3個字節,是爲了節省空間。

 

那麼咱們以前學習的流稱之爲字節流,以字節爲單位進行操做之情的操做全是英文,若是想要操做中文呢?

測試:將指定位置的文件經過字節流讀取到控制檯

public class TestIo {

public static void main(String[] args) throws IOException {

String path = "c:\\a.txt";

writFileTest();

readFileByInputStream(path);

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

 

int len = 0;

while ((len = fis.read()) != -1) {

System.out.print((char) len);

}

}

 

private static void writFileTest() throws FileNotFoundException,

IOException {

// 建立文件對象

File file = new File("c:\\a.txt");

// 建立文件輸出流

FileOutputStream fos = new FileOutputStream(file);

fos.write("中國".getBytes());

fos.close();

}

}

 

 

發現控制檯輸出的信息:

???ú  是這樣的東西,打開a.txt 文本發現漢字中國確實寫入成功。

那麼說明使用字節流處理中文有問題。

仔細分析,咱們的FileInputStream輸入流的read() 一次是讀一個字節的,返回的是一個int顯然進行了自動類型提高。那麼咱們來驗證一下中國對應的字節是什麼

使用:"中國".getBytes() 便可獲得字符串對應的字節數組。是[-42, -48, -71, -6]

一樣,將read方法返回值直接強轉爲byte ,發現結果也是-42, -48, -71, -6

代碼:

public class TestIo {

public static void main(String[] args) throws IOException {

String path = "c:\\a.txt";

writFileTest();

readFileByInputStream(path);

//查看中國對應的編碼

System.out.println(Arrays.toString("中國".getBytes()));

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

while ((len = fis.read()) != -1) {

System.out.println((byte)len);

}

}

 

private static void writFileTest() throws FileNotFoundException,

IOException {

// 建立文件對象

File file = new File("c:\\a.txt");

// 建立文件輸出流

FileOutputStream fos = new FileOutputStream(file);

fos.write("中國\r\n".getBytes());

fos.close();

}

 

}

 

那麼中國 對應的是-42, -48, -71, -64個字節。 那就是一箇中文佔2個字節,(這個和編碼是有關係的)

很顯然,咱們的中文就不可以再一個字節一個字節的讀了。因此字節流處理字符信息時並不方便那麼就出現了字符流。

字節流是 字符流是以字符爲單位。

體驗字符流:

public static void main(String[] args) throws IOException {

 

String path = "c:\\a.txt";

readFileByReader(path);

}

private static void readFileByReader(String path) throws IOException {

FileReader fr = new FileReader(path);

int len = 0;

while ((len = fr.read()) != -1) {

System.out.print((char) len);

}

}

總結:字符流就是:字節流 + 編碼表,爲了更便於操做文字數據。字符流的抽象基類:

Reader Writer

由這些類派生出來的子類名稱都是以其父類名做爲子類名的後綴,如FileReaderFileWriter

4.1. Reader

方法:

1int read()

讀取一個字符。返回的是讀到的那個字符。若是讀到流的末尾,返回-1.

2int read(char[])

將讀到的字符存入指定的數組中,返回的是讀到的字符個數,也就是往數組裏裝的元素的個數。若是讀到流的末尾,返回-1.

3close()

讀取字符其實用的是window系統的功能,就但願使用完畢後,進行資源的釋放

因爲Reader也是抽象類,因此想要使用字符輸入流須要使用Reader的實現類。查看API文檔。找到了FileReader

1,用於讀取文本文件的流對象。

2,用於關聯文本文件。

構造函數:在讀取流對象初始化的時候,必需要指定一個被讀取的文件。

若是該文件不存在會發生FileNotFoundException.

public class IoTest1_Reader {

 

public static void main(String[] args) throws Exception {

String path = "c:/a.txt";

// readFileByInputStream(path);

readFileByReader(path);

}

 

/**

 * 使用字節流讀取文件內容

 *

 * @param path

 */

public static void readFileByInputStream(String path) throws Exception {

InputStream in = new FileInputStream(path);

 

int len = 0;

while ((len = in.read()) != -1) {

System.out.print((char) len);

}

 

in.close();

}

 

/**

 * 使用字符流讀取文件內容

 */

public static void readFileByReader(String path) throws Exception {

Reader reader = new FileReader(path);

int len = 0;

while ((len = reader.read()) != -1) {

System.out.print((char) len);

}

 

reader.close();

}

 

}

4.2. Writer

Writer中的常見的方法:

1write(ch): 將一個字符寫入到流中。

2write(char[]): 將一個字符數組寫入到流中。

3write(String): 將一個字符串寫入到流中。

4flush():刷新流,將流中的數據刷新到目的地中,流還存在。

5close():關閉資源:在關閉前會先調用flush(),刷新流中的數據去目的地。然流關閉。

發現基本方法和OutputStream 相似,有write方法,功能更多一些。能夠接收字符串。

一樣道理Writer是抽象類沒法建立對象。查閱API文檔,找到了Writer的子類FileWriter

1將文本數據存儲到一個文件中。

public class IoTest2_Writer {

 

public static void main(String[] args) throws Exception {

String path = "c:/ab.txt";

 

writeToFile(path);

}

 

/**

 * 寫指定數據到指定文件中

 *

 */

public static void writeToFile(String path) throws Exception {

Writer writer = new FileWriter(path);

writer.write('');

writer.write("世界".toCharArray());

writer.write("中國");

 

writer.close();

}

}

2:追加文件:

默認的FileWriter方法新值會覆蓋舊值,想要實現追加功能須要

使用以下構造函數建立輸出流 append值爲true便可。

FileWriter(String fileName, boolean append)

FileWriter(File file, boolean append)

3:flush方法

若是使用字符輸出流,沒有調用close方法,會發生什麼?

private static void writeFileByWriter(File file) throws IOException {

FileWriter fw = new FileWriter(file);

fw.write('');

fw.flush();

fw.write("中國".toCharArray());

fw.write("世界你好!!!".toCharArray());

fw.write("明天");

// 關閉流資源

//fw.close();

}

程序執行完畢打開文件,發現沒有內容寫入.原來須要使用flush方法. 刷新該流的緩衝。

爲何只要指定claose方法就不用再flush方法,由於close也調用了flush方法.

4.3. 字符流拷貝文件

一個文本文件中有中文有英文字母,有數字。想要把這個文件拷貝到別的目錄中。

咱們可使用字節流進行拷貝,使用字符流呢?確定也是能夠的。

4.3.1. 字符流拷貝文件實現1

public static void main(String[] args) throws Exception {

String path1 = "c:/a.txt";

String path2 = "c:/b.txt";

 

copyFile(path1, path2);

}

 

/**

 * 使用字符流拷貝文件

 */

public static void copyFile(String path1, String path2) throws Exception {

Reader reader = new FileReader(path1);

Writer writer = new FileWriter(path2);

 

int ch = -1;

while ((ch = reader.read()) != -1) {

writer.write(ch);

}

 

reader.close();

writer.close();

}

可是這個一次讀一個字符就寫一個字符,效率不高。把讀到的字符放到字符數組中,再一次性的寫出。

4.3.2. 字符流拷貝文件實現2

public static void main(String[] args) throws Exception {

String path1 = "c:/a.txt";

String path2 = "c:/b.txt";

 

copyFile(path1, path2);

}

 

public static void copyFile3(String path1, String path2) throws Exception {

Reader reader = new FileReader(path1);

Writer writer = new FileWriter(path2);

 

int ch = -1;

char [] arr=new char[1024];

while ((ch = reader.read(arr)) != -1) {

writer.write(arr,0,ch);

}

 

reader.close();

writer.close();

}

 

字節流能夠拷貝視頻和音頻等文件,那麼字符流能夠拷貝這些嗎?

通過驗證拷貝圖片是不行的。發現丟失了信息,爲何呢?

計算機中的全部信息都是以二進制形式進行的存儲(1010)圖片中的也都是二進制

在讀取文件的時候字符流自動對這些二進制按照碼錶進行了編碼處理,可是圖片原本就是二進制文件,不須要進行編碼。有一些巧合在碼錶中有對應,就能夠處理,並非全部的二進制均可以找到對應的。信息就會丟失。因此字符流只能拷貝以字符爲單位的文本文件

(以ASCII碼爲例是127個,並非全部的二進制均可以找到對應的ASCII,有些對不上的,就會丟失信息。)

 

4.4. 字符流的異常處理

public static void main(String[] args) throws Exception {

String path1 = "c:/a.txt";

String path2 = "c:/b.txt";

 

copyFile2(path1, path2);

}

 

/**

 * 使用字符流拷貝文件,有完善的異常處理

 */

public static void copyFile2(String path1, String path2) {

Reader reader = null;

Writer writer = null;

try {

// 打開流

reader = new FileReader(path1);

writer = new FileWriter(path2);

 

// 進行拷貝

int ch = -1;

while ((ch = reader.read()) != -1) {

writer.write(ch);

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

// 關閉流,注意必定要能執行到close()方法,因此都要放到finally代碼塊中

try {

if (reader != null) {

reader.close();

}

} catch (Exception e) {

throw new RuntimeException(e);

} finally {

try {

if (writer != null) {

writer.close();

}

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

}

 

4.5. 字符流的緩衝區

查看Reader 發現Reader,操做的是字符,咱們就不須要進行編碼解碼操做,由字符流讀到二進制,自動進行解碼獲得字符,寫入字符自動編碼成二進制.

Reader有一個子類BufferedReader。子類繼承父類顯然子類能夠重寫父類的方法,也能夠增長本身的新方法。例如一次讀一行就是經常使用的操做.那麼BufferedReader 類就提供了這個方法,能夠查看readLine()方法具有 一次讀取一個文本行的功能。很顯然,該子類能夠對功能進行加強。

體驗BufferedReader

public class IoTest_BufferedReader {

public static void main(String[] args) throws IOException {

readFile("c:\\a.txt");

}

 

private static void readFile(String path) throws IOException {

Reader read = new FileReader(path);

 

BufferedReader br = new BufferedReader(read);

 

String line = null;

while ((line = br.readLine()) != null) {

System.out.println(line);

}

 

}

}

 

注意:

在使用緩衝區對象時,要明確,緩衝的存在是爲了加強流的功能而存在,因此在創建緩衝區對象時,要先有流對象存在.

緩衝區的出現提升了對流的操做效率。原理:其實就是將數組進行封裝。

使用字符流緩衝區拷貝文本文件.

public class Demo7 {

public static void main(String[] args) throws IOException {

// 關聯源文件

File srcFile = new File("c:\\linux大綱.txt");

// 關聯目標文件

File destFile = new File("d:\\linux大綱.txt");

// 實現拷貝

copyFile(srcFile, destFile);

 

}

 

private static void copyFile(File srcFile, File destFile)

throws IOException {

// 建立字符輸入流

FileReader fr = new FileReader(srcFile);

// 建立字符輸出流

FileWriter fw = new FileWriter(destFile);

 

// 字符輸入流的緩衝流

BufferedReader br = new BufferedReader(fr);

// 字符輸出流的緩衝流

BufferedWriter bw = new BufferedWriter(fw);

 

String line = null;

// 一次讀取一行

while ((line = br.readLine()) != null) {

// 一次寫出一行.

bw.write(line);

// 刷新緩衝

bw.flush();

// 進行換行,因爲readLine方法默認沒有換行.須要手動換行

bw.newLine();

}

// 關閉流

br.close();

bw.close();

}

}

 

 

4.6. 裝飾器模式

需求:想要讀取的文件的每一行添加行號。

public class IoTest7_BufferedReader {

 

public static void main(String[] args) throws IOException {

readFile("c:\\a.txt");

}

 

private static void readFile(String path) throws IOException {

Reader read = new FileReader(path);

 

BufferedReader br = new BufferedReader(read);

int count = 0;

String line = null;

while ((line = br.readLine()) != null) {

count++;

System.out.println(count+":"+line);

}

 

}

}

 

很容易的就能夠實現。若是每次使用BufferedReader 輸出時都須要顯示行號呢? 每次都加? 很顯然,咱們的BufferedReader繼承了Reader 對父類進行了功能的加強,那麼咱們也能夠繼承BufferedReader 重寫該類的readLine方法,進行功能的加強.

public class IoTest_BufferedReader {

public static void main(String[] args) throws IOException {

readFile("c:\\a.txt");

}

 

private static void readFile(String path) throws IOException {

Reader read = new FileReader(path);

 

BufferedReader br = new MyBufferedReader(read);

String line = null;

while ((line = br.readLine()) != null) {

System.out.println(line);

}

 

}

}

 

class MyBufferedReader extends BufferedReader {

public MyBufferedReader(Reader read) {

super(read);

}

 

int count;

 

@Override

public String readLine() throws IOException {

String line = super.readLine();

if (line != null) {

count++;

return count + ":" + line;

 

} else {

return null;

}

 

}

}

需求:

要在輸出的一行前加上引號

能夠再定義一個BufferedReader的子類,繼承BufferedReader加強功能.

public class IoTest_BufferedReader {

public static void main(String[] args) throws IOException {

readFile("c:\\a.txt");

}

 

private static void readFile(String path) throws IOException {

Reader read = new FileReader(path);

BufferedReader br = new MyQutoBufferedReader(read);

int count = 0;

String line = null;

while ((line = br.readLine()) != null) {

System.out.println(line);

count++;

}

 

}

}

 

 

// quotation 引號

class MyQutoBufferedReader extends BufferedReader {

 

public MyQutoBufferedReader(Reader reader) {

super(reader);

}

 

public String readLine() throws IOException {

String line = super.readLine();

if (line != null) {

 

return "\"" + line + "\"";

 

} else {

return null;

}

 

}

}

 

需求三:

既想要顯示行號又想要顯示引號

發現,就須要再定義子類,發現這樣比較麻煩,代碼臃腫.並且代碼重複.

能夠換一種方式.以下:

其實就是一個新類要對原有類進行功能加強.

1. 在加強類中維護一個被加強的父類引用變量

 2. 在加強類的構造函數中初始化1中的變量

 3. 建立須要加強的方法,在剛方法中調用被被加強類的方法,並加以加強。

public class IoTest_BufferedReader {

public static void main(String[] args) throws IOException {

readFile("c:\\a.txt");

}

 

private static void readFile(String path) throws IOException {

Reader read = new FileReader(path);

BufferedReader bufferedReader = new BufferedReader(read);

BufferedReader br = new MyQutoBufferedReader2(bufferedReader);

br = new MyLineBufferedReader2(br);

String line = null;

while ((line = br.readLine()) != null) {

System.out.println(line);

}

}

}

 

// quotation 引號

class MyQutoBufferedReader2 extends BufferedReader {

private BufferedReader bufferedReader;

 

public MyQutoBufferedReader2(BufferedReader bufferedReader) {

super(bufferedReader);

this.bufferedReader = bufferedReader;

}

 

public String readLine() throws IOException {

String line = super.readLine();

if (line != null) {

 

return "\"" + line + "\"";

 

} else {

return null;

}

 

}

}

 

class MyLineBufferedReader2 extends BufferedReader {

private BufferedReader bufferedReader;

 

public MyLineBufferedReader2(BufferedReader bufferedReader) {

super(bufferedReader);

this.bufferedReader = bufferedReader;

}

 

int count;

 

@Override

public String readLine() throws IOException {

String line = super.readLine();

if (line != null) {

count++;

return count + ":" + line;

 

} else {

return null;

}

 

}

}

這就是裝飾器模式

 

裝飾器模式:

使用分層對象來動態透明的向單個對象中添加責任(功能)。

裝飾器指定包裝在最初的對象周圍的全部對象都具備相同的基本接口。

某些對象是可裝飾的,能夠經過將其餘類包裝在這個可裝飾對象的四周,來將功能分層。

裝飾器必須具備和他所裝飾的對象相同的接口。

JavaIO中的應用:

Java I/O類庫須要多種不一樣的功能組合,因此使用了裝飾器模式。

FilterXxx類是JavaIO提供的裝飾器基類,即咱們要想實現一個新的裝飾器,就要繼承這些類。

裝飾器與繼承:

問題:

修飾模式作的加強功能按照繼承的特色也是能夠實現的,爲何還要提出修飾設計模式呢?

繼承實現的加強類和修飾模式實現的加強類有何區別?

繼承實現的加強類:

優勢:代碼結構清晰,並且實現簡單

缺點:對於每個的須要加強的類都要建立具體的子類來幫助其加強,這樣會致使繼承體系過於龐大。

修飾模式實現的加強類:

優勢:內部能夠經過多態技術對多個須要加強的類進行加強

缺點:須要內部經過多態技術維護須要加強的類的實例。進而使得代碼稍微複雜。

5. 其餘流

 

5.1. 序列流

也稱爲合併流。

5.1.1. SequenceInputStream

序列流,對多個流進行合併。

SequenceInputStream 表示其餘輸入流的邏輯串聯。它從輸入流的有序集合開始,並從第一個輸入流開始讀取,直到到達文件末尾,接着從第二個輸入流讀取,依次類推,直到到達包含的最後一個輸入流的文件末尾爲止。

注意:

構造函數

SequenceInputStream(InputStream s1, InputStream s2)

SequenceInputStream(InputStream s1, InputStream s2)

 

合併兩個流

使用構造函數SequenceInputStream(InputStream s1, InputStream s2)

private static void testSequenceInputStream() throws IOException {

FileInputStream fis1 = new FileInputStream("c:\\a.txt");

FileInputStream fis2 = new FileInputStream("c:\\b.txt");

 

SequenceInputStream s1 = new SequenceInputStream(fis1, fis2);

int len = 0;

byte[] byt = new byte[1024];

 

FileOutputStream fos = new FileOutputStream("c:\\z.txt");

 

while ((len = s1.read(byt)) != -1) {

fos.write(byt, 0, len);

}

s1.close();

}

 

合併多個流:

 

public static void testSequenceInputStream() throws Exception {

InputStream in1 = new FileInputStream("c:/a.txt");

InputStream in2 = new FileInputStream("c:/b.txt");

InputStream in3 = new FileInputStream("c:/c.txt");

 

LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();

set.add(in1);

set.add(in2);

set.add(in3);

final Iterator<InputStream> iter = set.iterator();

 

SequenceInputStream sin = new SequenceInputStream(

new Enumeration<InputStream>() {

@Override

public boolean hasMoreElements() {

return iter.hasNext();

}

 

@Override

public InputStream nextElement() {

return iter.next();

}

});

 

FileOutputStream out = new FileOutputStream("c:/z.txt");

 

for (int b = -1; (b = sin.read()) != -1;) {

out.write(b);

}

        sin.close();

out.close();

}

案例:map3歌曲文件進行切割拷貝,併合並.

public class Demo2 {

public static void main(String[] args) throws IOException {

 

split(new File("c:\\a.mp3"), 10, new File("c:\\"));

System.out.println("切割完畢");

 

LinkedHashSet<InputStream> hs = new LinkedHashSet<InputStream>();

hs.add(new FileInputStream(new File("c:\\part.1.mp3")));

hs.add(new FileInputStream(new File("c:\\part.2.mp3")));

hs.add(new FileInputStream(new File("c:\\part.3.mp3")));

hs.add(new FileInputStream(new File("c:\\part.4.mp3")));

merage(hs, new File("c:\\merage.mp3"));

System.out.println("合併完畢");

}

 

private static void merage(LinkedHashSet<InputStream> hs, File dest)

throws IOException {

 

final Iterator<InputStream> it = hs.iterator();

FileOutputStream fos = new FileOutputStream(dest);

SequenceInputStream seq = new SequenceInputStream(

new Enumeration<InputStream>() {

 

@Override

public boolean hasMoreElements() {

 

return it.hasNext();

}

 

@Override

public InputStream nextElement() {

return it.next();

}

});

byte[] byt = new byte[1024 * 1024];

int len = 0;

while ((len = seq.read(byt)) != -1) {

fos.write(byt, 0, len);

}

seq.close();

fos.close();

}

 

// 1. 切割文件

/*

 * 切割文件,切割份數, 切割後保存路徑

 */

private static void split(File src, int count, File dir) throws IOException {

FileInputStream fis = new FileInputStream(src);

FileOutputStream fos = null;

byte[] byt = new byte[1024 * 1024];

int len = 0;

for (int i = 1; i <= count; i++) {

len = fis.read(byt);

if (len != -1) {

fos = new FileOutputStream(dir + "part." + i + ".mp3");

fos.write(byt, 0, len);

}

 

// fos.close();

 

}

fis.close();

 

}

}

 

 

 

5.2. 對象的序列化

當建立對象時,程序運行時它就會存在,可是程序中止時,對象也就消失了.可是若是但願對象在程序不運行的狀況下仍能存在並保存其信息,將會很是有用,對象將被重建而且擁有與程序上次運行時擁有的信息相同。可使用對象的序列化。

 對象的序列化:   將內存中的對象直接寫入到文件設備中

 對象的反序列化: 將文件設備中持久化的數據轉換爲內存對象

基本的序列化由兩個方法產生:一個方法用於序列化對象並將它們寫入一個流,另外一個方法用於讀取流並反序列化對象。

ObjectOutput

writeObject(Object obj)

          將對象寫入底層存儲或流。

ObjectInput

readObject()

          讀取並返回對象。

 

5.2.1. ObjectOutputStream

5.2.2. ObjectInputStream

因爲上述ObjectOutputObjectInput是接口,因此須要使用具體實現類。

ObjectOutput

 ObjectOutputStream被寫入的對象必須實現一個接口:Serializable

不然會拋出:NotSerializableException

ObjectInput

     ObjectInputStream    該方法拋出異常:ClassNotFountException  

ObjectOutputStreamObjectInputStream 對象分別須要字節輸出流和字節輸入流對象來構建對象。也就是這兩個流對象須要操做已有對象將對象進行本地持久化存儲。

 

案例:

序列化和反序列化Cat對象。

public class Demo3 {

public static void main(String[] args) throws IOException,

ClassNotFoundException {

Cat cat = new Cat("tom", 3);

FileOutputStream fos = new FileOutputStream(new File("c:\\Cat.txt"));

ObjectOutputStream oos = new ObjectOutputStream(fos);

oos.writeObject(cat);

System.out.println(cat);

oos.close();

// 反序列化

FileInputStream fis = new FileInputStream(new File("c:\\Cat.txt"));

ObjectInputStream ois = new ObjectInputStream(fis);

Object readObject = ois.readObject();

Cat cat2 = (Cat) readObject;

System.out.println(cat2);

fis.close();

}

 

class Cat implements Serializable {

public String name;

public int age;

 

public Cat() {

 

}

 

public Cat(String name, int age) {

 

this.name = name;

this.age = age;

}

 

@Override

public String toString() {

return "Cat [name=" + name + ", age=" + age + "]";

}

 

}

 

例子關鍵點:

  1. 聲明Cat類實現了Serializable接口。是一個標示器,沒有要實現的方法。
  2. 新建Cat對象。
  3. 新建字節流對象(FileOutputStream)進序列化對象保存在本地文件中。
  4. 新建ObjectOutputStream對象,調用writeObject方法序列化Cat對象。
  5. writeObject方法會執行兩個工做:序列化對象,而後將序列化的對象寫入文件中。
  6. 反序列化就是調用ObjectInputStreamreadObject()方法。
  7. 異常處理和流的關閉動做要執行。

5.2.3. Serializable

類經過實現 java.io.Serializable 接口以啓用其序列化功能。未實現此接口的類將沒法使其任何狀態序列化或反序列化。可序列化類的全部子類型自己都是可序列化的。序列化接口沒有方法或字段,僅用於標識可序列化的語義。

因此須要被序列化的類必須是實現Serializable接口,該接口中沒有描述任何的屬性和方法,稱之爲標記接口。

若是對象沒有實現接口Serializable,在進行序列化時會拋出:NotSerializableException 異常。

注意:

保存一個對象的真正含義是什麼?若是對象的實例變量都是基本數據類型,那麼很是簡單。可是若是實例變量是包含對象的引用,會怎麼樣?保存的會是什麼?很顯然在Java中保存引用變量的實際值沒有任何意義,由於Java引用的值是經過JVM的單一實例的上下文中才有意義。經過序列化後,嘗試在JVM的另外一個實例中恢復對象,是沒有用處的。

以下:

首先創建一個Dog對象,也創建了一個Collar對象。Dog中包含了一個Collar(項圈)

如今想要保存Dog對象,可是Dog中有一個Collar,意味着保存Dog時也應該保存Collar。假如Collar也包含了其餘對象的引用,那麼會發生什麼?意味着保存一個Dog對象須要清楚的知道Dog對象的內部結構。會是一件很麻煩的事情。

Java的序列化機制能夠解決該類問題,序列化一個對象時,Java的序列化機制會負責保存對象的全部關聯的對象(就是對象圖),反序列化時,也會恢復全部的相關內容。本例中:若是序列化Dog會自動序列化Collar。可是,只有實現了Serializable接口的類才能夠序列化。若是隻是Dog實現了該接口,而Collar沒有實現該接口。會發生什麼?

Dog類和Collar

import java.io.Serializable;

 

public class Dog implements Serializable {

private Collar collar;

private String name;

 

public Dog(Collar collar, String name) {

 

this.collar = collar;

this.name = name;

}

 

public Collar getCollar() {

return collar;

}

 

}

 

class Collar {

private int size;

 

public int getSize() {

return size;

}

 

public Collar(int size) {

this.size = size;

}

 

}

 

序列化

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.ObjectOutputStream;

 

public class Demo4 {

public static void main(String[] args) throws IOException {

Collar coll = new Collar(10);

Dog dog = new Dog(coll, "旺財");

 

FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));

ObjectOutputStream os = new ObjectOutputStream(fis);

os.writeObject(dog);

}

}

 

執行程序,出現了運行時異常。

Exception in thread "main" java.io.NotSerializableException: Collar

 

因此咱們也必須將Dog中使用的Collar序列化。可是若是咱們沒法訪問Collar的源代碼,或者沒法使Collar可序列化,如何處理?

兩種解決方法:

一:繼承Collar類,使子類可序列化

可是:若是Collarfinal類,就沒法繼承了。而且,若是Collar引用了其餘非序列化對象,也沒法解決該問題。

transient

此時就可使用transient修飾符,能夠將Dog類中的成員變量標識爲transient

那麼在序列化Dog對象時,序列化就會跳過Collar

 

public class Demo4 {

public static void main(String[] args) throws IOException,

ClassNotFoundException {

Collar coll = new Collar(10);

Dog dog = new Dog(coll, "旺財");

System.out.println(dog.getCollar().getSize());

 

FileOutputStream fis = new FileOutputStream(new File("c:\\dog.txt"));

ObjectOutputStream os = new ObjectOutputStream(fis);

os.writeObject(dog);

 

// 反序列化

FileInputStream fos = new FileInputStream(new File("c:\\dog.txt"));

ObjectInputStream ois = new ObjectInputStream(fos);

Object readObject = ois.readObject();

Dog dog2 = (Dog) readObject;

// Collar未序列化。

dog2.getCollar().getSize();

}

}

 

這樣咱們具備一個序列化的Dog和非序列化的Collar

此時反序列化Dog後,訪問Collar,就會出現運行時異常

10

Exception in thread "main" java.lang.NullPointerException

注意:序列化不適用於靜態變量,由於靜態變量並不屬於對象的實例變量的一部分。靜態變量隨着類的加載而加載,是類變量。因爲序列化只適用於對象。

 

基本數據類型能夠被序列化

public class Demo5 {

public static void main(String[] args) throws IOException {

// 建立序列化流對象

FileOutputStream fis = new FileOutputStream(new File("c:\\basic.txt"));

ObjectOutputStream os = new ObjectOutputStream(fis);

// 序列化基本數據類型

os.writeDouble(3.14);

os.writeBoolean(true);

os.writeInt(100);

os.writeInt(200);

 

// 關閉流

os.close();

 

// 反序列化

FileInputStream fos = new FileInputStream(new File("c:\\basic.txt"));

ObjectInputStream ois = new ObjectInputStream(fos);

 

System.out.println(ois.readDouble());

System.out.println(ois.readBoolean());

System.out.println(ois.readInt());

System.out.println(ois.readInt());

 

fos.close();

}

}

 

 

serialVersionUID

用於給類指定一個UID。該UID是經過類中的可序列化成員的數字簽名運算出來的一個long型的值。

只要是這些成員沒有變化,那麼該值每次運算都同樣。

該值用於判斷被序列化的對象和類文件是否兼容。

若是被序列化的對象須要被不一樣的類版本所兼容。能夠在類中自定義UID

定義方式:static final long serialVersionUID = 42L;

 

5.3. Properties.

 

能夠和流相關聯的集合對象Properties.

Map

|--Hashtable

|--Properties

Properties:該集合不須要泛型,由於該集合中的鍵值對都是String類型。

1,存入鍵值對:setProperty(key,value);

2,獲取指定鍵對應的值:value getProperty(key);

3,獲取集合中全部鍵元素:

Enumeration  propertyNames();

jdk1.6版本給該類提供一個新的方法。

Set<String> stringPropertyNames();

4,列出該集合中的全部鍵值對,能夠經過參數打印流指定列出到的目的地。

list(PrintStream);

list(PrintWriter);

例:list(System.out):將集合中的鍵值對打印到控制檯。

list(new PrintStream("prop.txt")):將集合中的鍵值對存儲到prop.txt文件中。

5,能夠將流中的規則數據加載進行集合,並稱爲鍵值對。

load(InputStream):

jdk1.6版本。提供了新的方法。

load(Reader):

注意:流中的數據要是"=" 的規則數據。

6,能夠將集合中的數據進行指定目的的存儲。

store(OutputStram,String comment)方法。

jdk1.6版本。提供了新的方法。

store(Writer ,String comment):

使用該方法存儲時,會帶着當時存儲的時間。

注意:

Properties只加載key=value這樣的鍵值對,與文件名無關,註釋使用#

練習:記錄一個程序運行的次數,當知足指定次數時,該程序就不能夠再繼續運行了。

一般可用於軟件使用次數的限定。

public static void sysPropList() throws IOException {

Properties prop = System.getProperties();

 

// prop.list(System.out);// 目的是控制檯。

// 需求是:將jvm的屬性信息存儲到一個文件中。

prop.list(new PrintStream("java.txt"));

}

 

public static void sysProp() {

Properties prop = System.getProperties();

 

Set<String> keys = prop.stringPropertyNames();

 

for (String key : keys) {

System.out.println(key + ":" + prop.getProperty(key));

}

}

 

Properties類與配置文件

Map

|--Hashtable

|--Properties

注意:是一個Map集合,該集合中的鍵值對都是字符串。該集合一般用於對鍵值對形式的配置文件進行操做.

配置文件:將軟件中可變的部分數據能夠定義到一個文件中,方便之後更改,該文件稱之爲配置文件。

優點: 提升代碼的維護性。

Properties:  該類是一個Map的子類,提供了能夠快速操做配置文件的方法

load()  :    將文件設備數據裝載爲Map集合數據

get(key):    獲取Map中的數據

getProperty()獲取Map中的數據特有方法

案例:

/*

 * 將配置文件中的數據經過流加載到集合中。

 */

public static void loadFile() throws IOException {

// 1,建立Properties(Map)對象

Properties prop = new Properties();

 

// 2.使用流加載配置文件。

FileInputStream fis = new FileInputStream("c:\\qq.txt");

 

// 3。使用Properties 對象的load方法將流中數據加載到集合中。

prop.load(fis);

 

// 遍歷該集合

Set<Entry<Object, Object>> entrySet = prop.entrySet();

Iterator<Entry<Object, Object>> it = entrySet.iterator();

while (it.hasNext()) {

Entry<Object, Object> next = it.next();

Object key = next.getKey();

Object value = next.getValue();

}

// 經過鍵獲取指定的值

Object object = prop.get("jack");

System.out.println(object);

 

// 經過鍵修改值

prop.setProperty("jack", "888888");

 

// 將集合中的數據寫入到配置文件中。

FileOutputStream fos = new FileOutputStream("c:\\qq.txt");

 

// 註釋:

prop.store(fos, "yes,qq");

 

fos.close();

fis.close();

 

}

 

 

獲取記錄程序運行次數:

public class Demo6 {

public static void main(String[] args) throws IOException {

int count = 0;

Properties pro = new Properties();

 

File file = new File("c:\\count.ini");

FileInputStream fis = null;

if (!file.exists()) {

file.createNewFile();

}

fis = new FileInputStream(file);

pro.load(fis);

String str = pro.getProperty("count");

if (str != null) {

count = Integer.parseInt(str);

}

if (count == 3) {

System.out.println("使用次數已到,請付費");

System.exit(0);

}

 

count++;

System.out.println("歡迎使用本軟件" + "你已經使用了:" + count + " ");

 

pro.setProperty("count", count + "");

FileOutputStream fos = new FileOutputStream(new File("c:\\count.ini"));

pro.store(fos, "請保護知識產權");

 

fis.close();

fos.close();

 

}

}

 

 

5.4. 打印流

PrintStream能夠接受文件和其餘字節輸出流,因此打印流是對普通字節輸出流的加強,其中定義了不少的重載的print()println(),方便輸出各類類型的數據。

 

5.4.1. PrintStream

PrintWriter

1,打印流。

PrintStream

是一個字節打印流,System.out對應的類型就是PrintStream

它的構造函數能夠接收三種數據類型的值。

1,字符串路徑。

2File對象。

3OutputStream

public static void main(String[] args) throws IOException {

PrintStream ps = System.out;

 

// 普通write方法須要調用flush或者close方法纔會在控制檯顯示

// ps.write(100);

// ps.close();

 

// 不換行打印

ps.print(100);

ps.print('a');

ps.print(100.5);

ps.print("世界");

ps.print(new Object());

System.out.println("--------------");

// 換行

ps.println(100);

ps.println('a');

ps.println(100.5);

ps.println("世界");

ps.println(new Object());

 

// 重定向打印流

PrintStream ps2 = new PrintStream(new File("c:\\a.txt"));

System.setOut(ps2);

// 換行

ps2.println(100);

ps2.println('a');

ps2.println(100.5);

ps2.println("世界");

ps2.println(new Object());

 

// printf(); 格式化

ps2.printf("%d,%f,%c,%s", 100, 3.14, '', "世界你好!!!");

ps2.printf("%4s%8s 打價格戰", "京東", "蘇寧");

 

 

} }

注意: 打印流的三種方法

void print(數據類型 變量)

println(數據類型 變量)

printf(String format, Object... args) 

能夠自定數據格式

print println方法的區別在於,一個換行一個不換行

print 方法和write方法的卻別在於,print提供自動刷新.

普通的write方法須要調用flush或者close方法才能夠看到數據.

JDK1.5以後JavaPrintStream進行了擴展,增長了格式化輸出方式,可使用printf()重載方法直接格式化輸出。可是在格式化輸出的時候須要指定輸出的數據類型格式。

 

 

5.4.2. PrintWriter

是一個字符打印流。構造函數能夠接收四種類型的值。

1,字符串路徑。

2File對象。

對於12類型的數據,還能夠指定編碼表。也就是字符集。

3OutputStream

4Writer

對於34類型的數據,能夠指定自動刷新。

注意:該自動刷新值爲true時,只有三個方法能夠用:println,printf,format.

若是想要既有自動刷新,又可執行編碼。如何完成流對象的包裝?

PrintWrter pw =

new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"),true);

若是想要提升效率。還要使用打印方法。

PrintWrter pw =

newPrintWriter(new  BufferdWriter(new OutputSteamWriter(

newFileOutputStream("a.txt"),"utf-8")),true);

public static void testPrintWriter() throws Exception {

PrintWriter pw = new PrintWriter("c:/b.txt", "gbk");

 

// pw.append("xxx");

// pw.println(55);

// pw.println('c');

// pw.printf("%.1s%4s打價格戰, %c", "京東","蘇寧", 'a');

 

pw.close();

 

}

 

Scanner

public static void testScanner() throws Exception {

// Scanner scanner = new Scanner(new File("c:/test.txt"));

Scanner scanner = new Scanner(System.in);

 

System.out.println(scanner.nextInt());

System.out.println(scanner.nextBoolean());

 

scanner.close();

}

5.5. 操做數組的流對象

5.5.1. 操做字節數組

ByteArrayInputStream

以及ByteArrayOutputStream

toByteArray();

toString();

writeTo(OutputStream);

public static void testByteArrayInputStream() throws Exception {

InputStream in = new ByteArrayInputStream(new byte[] { 65, 66, 67 });

ByteArrayOutputStream out = new ByteArrayOutputStream();

 

for (int b = -1; (b = in.read()) != -1;) {

out.write(b);

}

 

in.close();

out.close();

 

System.out.println(Arrays.toString(out.toByteArray()));

System.out.println(out);

}

 

5.5.2. 操做字符數組

CharArrayReader

CharArrayWriter

對於這些流,源是內存。目的也是內存。

並且這些流並未調用系統資源。使用的就是內存中的數組。

因此這些在使用的時候不須要close

操做數組的讀取流在構造時,必需要明確一個數據源。因此要傳入相對應的數組。

對於操做數組的寫入流,在構造函數可使用空參數。由於它內置了一個可變長度數組做爲緩衝區。 

public static void testCharArrayReader() throws Exception {

CharArrayReader reader = new CharArrayReader(new char[] { 'A', 'b', 'c' });

CharArrayWriter writer = new CharArrayWriter();

 

for (int b = -1; (b = reader.read()) != -1;) {

writer.write(b);

}

 

reader.close();

writer.close();

 

System.out.println(writer.toCharArray());

}

 

這幾個流的出現其實就是經過流的讀寫思想在操做數組。

相似的對象同理:

StringReader

StringWriter

public static void testStringReader() throws Exception {

StringReader reader = new StringReader("test 中國");

StringWriter writer = new StringWriter();

 

for (int b = -1; (b = reader.read()) != -1;) {

writer.write(b);

}

 

reader.close();

writer.close();

 

System.out.println(writer.toString());

}

5.6. 操做基本數據類型的流對象

5.6.1. DataInputStream

以及DataOutputStream

 

查看API文檔DataInputStream的信息。發現從底層輸入流中讀取基本 Java 數據類型。查看方法,有讀一個字節,讀一個char讀一個double 的方法,

 

DataInputStream 從數據流讀取字節,並將它們轉換爲正確的基本數據類型值或字符串。

該流有操做基本數據類型的方法.

有讀的,那麼一定有對應的寫的就是DataOutputStream 將基本類型的值或字符串轉換爲字節,而且將字節輸出到數據流。

DataInputStream類繼承FilterInputStream類,並實現了DataInput接口。DataOutputStream

類繼承FilterOutputStream 並實現了DataOutput 接口。

例如:

DataInputStream

操做基本數據類型的方法:

int readInt():一次讀取四個字節,並將其轉成int值。

boolean readBoolean():一次讀取一個字節。

short readShort();

long readLong();

剩下的數據類型同樣。

String readUTF():按照utf-8修改版讀取字符。注意,它只能讀writeUTF()寫入的字符數據。

DataOutputStream

DataOutputStream(OutputStream):

操做基本數據類型的方法:

writeInt(int):一次寫入四個字節。

注意和write(int)不一樣。write(int)只將該整數的最低一個8位寫入。剩餘三個8位丟棄。

writeBoolean(boolean);

writeShort(short);

writeLong(long);

剩下是數據類型也也同樣。

writeUTF(String):按照utf-8修改版將字符數據進行存儲。只能經過readUTF讀取。

 

 

測試: DataOutputStream

使用DataOutputStream寫數據文件。

public static void testDataInputStream() throws Exception {

DataOutputStream out = new DataOutputStream(new FileOutputStream(

"c:/a.txt"));

 

out.writeBoolean(true);

out.writeByte(15); // 0x05 1 個字節

out.writeBytes("abc"); // 0x 0041 2個字節

out.writeChar('X'); // ??

out.writeChars("xyz");

out.writeLong(111);

out.writeUTF("中國");

 

out.close();

 

DataInputStream in = new DataInputStream(

new FileInputStream("c:/a.txt"));

System.out.println(in.readBoolean());

System.out.println(in.readByte());

 

System.out.println(in.readByte());

System.out.println(in.readByte());

System.out.println(in.readByte());

 

System.out.println(in.readChar());

 

System.out.println(in.readChar());

System.out.println(in.readChar());

System.out.println(in.readChar());

 

System.out.println(in.readLong());

 

System.out.println(in.readUTF());

in.close();

}

 

 

6. 編碼

什麼是編碼?

計算機中存儲的都是二進制,可是要顯示的時候,就是咱們看到的卻能夠有中國 a  1 等字符

計算機中是沒有存儲字符的,可是咱們卻看到了。計算機在存儲這些信息的時候,根據一個有規則的編號,當用戶輸入a a對映的編號,就將這個編號存進計算機中這就是編碼。

 

計算機只能識別二進制數據。

爲了方便應用計算機,讓它能夠識別各個國家的文字。就將各個國家的文字用數字來表示,並一一對應,造成一張表,這就是編碼表。

例如:漢字  

有一種編碼:

中字在utf 8中對映的編碼

utf-8  -->100  

gbk中呢?有可能就不是100

gbk    -->  150

很顯然同一個信息在不一樣的編碼中對映的數字也不一樣,

不一樣的國家和地區使用的碼錶是不一樣的,

gbk 是中國大陸

bjg5 是臺灣同胞中的繁體字。因此若是給big5一個簡體字是不認識的。

還有ASCII 美國標準信息交換碼

6.1. 碼錶

常見的碼錶以下:

ASCII 美國標準信息交換碼。用一個字節的7位能夠表示。

ISO8859-1 拉丁碼錶。歐洲碼錶,用一個字節的8位表示。又稱Latin-1(拉丁編碼)西歐語言ASCII碼是包含的僅僅是英文字母,而且沒有徹底佔滿256個編碼位置,因此它以ASCII爲基礎,在空置的0xA0-0xFF的範圍內,加入192個字母及符號,

藉以供使用變音符號的拉丁字母語言使用。從而支持德文,法文等。於是它依然是一個單字節編碼,只是比ASCII更全面。

GB2312 中國的中文編碼表。

GBK 中國的中文編碼表升級,融合了更多的中文文字符號。

Unicode:  國際標準碼,融合了多種文字。全部文字都用兩個字節來表示,Java語言使用的就是unicode

UTF-8 最多用三個字節來表示一個字符。

(咱們之後接觸最多的是iso8859-1gbkutf-8

 

查看上述碼錶後,很顯然中文的iso8859-1中是沒有對映的編碼的。或者一個字符在2中碼錶中對應的編碼不一樣,例若有一些字在不一樣的編碼中是有交集的,例如bjg5 gbk 中的漢字簡體和繁體多是同樣的,就是有交集,可是在各自碼錶中的數字不同。

例如

使用gbk 將中文保存在計算機中,

 

對映  100  200   若是使用big5 打開

可能     ...   

不一樣的編碼對映的是不同的。

很顯然,咱們使用什麼樣的編碼寫數據,就須要使用什麼樣的編碼來對數據。

ISO8859-1:一個字節

GBK: 兩個字節包含了英文字符和擴展的中文 ISO8859-1+中文字符

UTF-8 萬國碼,推行的。是1~3個字節不等長。英文存的是1個字節,中文存的是3個字節,是爲了節省空間。

 

6.2. 編碼:

字符串---》字節數組

String類的getBytes() 方法進行編碼,將字符串,轉爲對映的二進制,而且這個方法能夠指定編碼表。若是沒有指定碼錶,該方法會使用操做系統默認碼錶。

注意:中國大陸的Windows系統上默認的編碼通常爲GBK。在Java程序中可使用System.getProperty("file.encoding")方式獲得當前的默認編碼。

6.3. 解碼:

字節數組---》字符串

String類的構造函數完成。

String(byte[] bytes)  使用系統默認碼錶

String(byte[],charset)指定碼錶

注意:咱們使用什麼字符集(碼錶)進行編碼,就應該使用什麼字符集進行解碼,不然頗有可能出現亂碼(兼容字符集不會)。

 

// 編碼操做與解碼操做。

public static void main(String[] args) throws Exception {

String value = System.getProperty("file.encoding");

System.out.println("系統默認的編碼爲 " + value);

 

String str = "";

 

// 編碼操做

byte[] bytes = str.getBytes();

byte[] bytes2 = str.getBytes("gbk");// d6d0

byte[] bytes3 = str.getBytes("utf-8");// e4b8ad

 

System.out.println(Arrays.toString(bytes)); // [-42, -48]

System.out.println(Arrays.toString(bytes2));// [-42, -48]

System.out.println(Arrays.toString(bytes3));// [-28, -72, -83]

 

// 解碼操做

// 編碼gbk,解碼utf-8亂碼。

String str2 = new String(bytes2, "utf-8");

System.out.println(str2);

 

// 編碼utf-8 解碼gbk,亂碼

str2 = new String(bytes3, "gbk");

System.out.println(str2);

// gbk兼容gb2312因此,沒有問題。

str = new String("中國".getBytes("gb2312"), "gbk");

System.out.println(str);

}

 

存文件時可使用各類編碼,可是解碼的時候要對映的採用相同的解碼方式。

咱們的字符流自動的作了編碼和解碼的工做,寫一箇中文,字符流進行了編碼,存到了計算機中讀到了一個字符,字符流進行了解碼,咱們能夠看到字符。由於文件存的都是二進制。

可是拷貝圖片時,是純二進制,不是有意義的字符,因此碼錶沒法轉換。

字符流的弊端:

一:沒法拷貝圖片和視頻。

二:拷貝文件使用字節流而不使用字符流,由於字符流讀文件涉及到解碼,會先解碼,寫文件的時候又涉及到編碼,這些操做多餘,並且讀和寫的碼錶不對應還容易引起問題。

例如FileReader 讀文件,咱們沒有指定編碼時,默認是按照系統編碼gbk進行操做,若是讀到utf-8的文件也是按照gbk編碼進行解碼,那就會出現問題。

 

6.4. 字節流讀取中文

 

public class TestIo {

public static void main(String[] args) throws IOException {

readFileByInputStream2("c:\\a.txt");

}

 

private static void readFileByInputStream2(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

 

while ((len = fis.read()) != -1) {

System.out.print((char) len);

}

 

}

}

這個方法讀取文本文件,中文是沒法正確顯示的。

很顯然這些字節須要解碼,能夠將字節輸入流讀取的信息保存在字節數組中,指定對應的碼錶進行解碼便可。

public class TestIo {

public static void main(String[] args) throws IOException {

readFileByInputStream("c:\\a.txt");

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

byte[] buffer = new byte[1024];

while ((len = fis.read(buffer)) != -1) {

System.out.println(new String(buffer, 0, len, "gbk"));

}

 

}

}

 

 

注意:若是指定的編碼表和解碼錶不對應就會出現問題

public class TestIo {

public static void main(String[] args) throws IOException {

// 該文件默認是gbk編碼

readFileByInputStream("c:\\a.txt");

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

byte[] buffer = new byte[1024];

while ((len = fis.read(buffer)) != -1) {

// 使用utf-8 解碼,解錯。

System.out.println(new String(buffer, 0, len, "utf-8"));

}

 

}

}

 

 

6.5. 字節流寫出中文

須要編碼,能夠指定碼錶。就須要本身把字符串進行編碼操做後,把獲得的二進制內容經過字節流寫入到文件中

使用StringgetBytes方法,無參數的會使用系統默認的碼錶進行編碼,也能夠指定碼錶

系統默認編碼

public class TestIo {

public static void main(String[] args) throws IOException {

 

String path = "c:\\test.txt";

writeFileByOutputStream(path, "世界你好");

readFileByInputStream(path);

}

 

private static void writeFileByOutputStream(String path, String content)

throws IOException {

FileOutputStream fos = new FileOutputStream(path);

 

// 把字符串進行編碼操做,系統默認編碼

byte[] bytes = content.getBytes();

// 內容經過字節流寫入到文件中。

fos.write(bytes);

fos.close();

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

byte[] buffer = new byte[1024];

 

while ((len = fis.read(buffer)) != -1) {

// 二進制解碼,使用系統默認編碼

System.out.println(new String(buffer, 0, len));

}

 

}

}

 

 

使用utf-8進行編碼

public class TestIo {

public static void main(String[] args) throws IOException {

 

String path = "c:\\test.txt";

writeFileByOutputStream(path, "世界你好");

readFileByInputStream(path);

}

 

private static void writeFileByOutputStream(String path, String content)

throws IOException {

FileOutputStream fos = new FileOutputStream(path);

 

// 把字符串進行編碼操做

byte[] bytes = content.getBytes("utf-8");

// 內容經過字節流寫入到文件中。

fos.write(bytes);

fos.close();

}

 

private static void readFileByInputStream(String path) throws IOException {

FileInputStream fis = new FileInputStream(path);

int len = 0;

byte[] buffer = new byte[1024];

 

while ((len = fis.read(buffer)) != -1) {

// 二進制解碼,使用系統默認編碼

System.out.println(new String(buffer, 0, len,"utf-8"));

}

 

}

}

 

 

在明白了字節流也能夠正確的處理中文字符以後,就應該明白字符流其實就是字節流在加上系統默認的碼錶。自動進行了編碼和解碼的操做。底層仍是使用字節流讀取文件。經過轉換流的學習就能夠明白這些道理。

 

6.6. 轉換流

InputStreamReader

查看API文檔,發現是字節流通向字符流的橋樑。查看構造,能夠傳遞字節流,能夠指定編碼,該流能夠實現什麼功能?很顯然能夠包裝咱們的字節流,自動的完成節流編碼和解碼的工做。該流是一個Reader的子類,是字符流的體系。因此將轉換流稱之爲字節流和字符流之間的橋樑。

InputStreamReader 是字節流通向字符流的橋樑

測試InputStreamReader

第一步: 須要專門新建以GBK編碼的文本文件。爲了便於標識,咱們命名爲gbk.txt

和以UFT-8編碼的文本文件,命名爲utf.txt
第二步: 分別寫入漢字」中國」

第三步:編寫測試方法,InputStreamReader 分別使用系統默認編碼,GBK,UTF-8編碼讀取文件.

 

public class Demo4 {

public static void main(String[] args) throws IOException {

File file = new File("c:\\a.txt");

File fileGBK = new File("c:\\gbk.txt");

File fileUTF = new File("c:\\utf.txt");

// 默認編碼

testReadFile(file);

// 傳入gbk編碼文件,使用gbk解碼

testReadFile(fileGBK, "gbk");

// 傳入utf-8文件,使用utf-8解碼

testReadFile(fileUTF, "utf-8");

 

}

 

// 該方法中nputStreamReader使用系統默認編碼讀取文件.

private static void testReadFile(File file) throws 

IOException {

FileInputStream fis = new FileInputStream(file);

InputStreamReader ins = new InputStreamReader(fis);

int len = 0;

while ((len = ins.read()) != -1) {

System.out.print((char) len);

}

ins.close();

fis.close();

}

 

// 該方法使用指定編碼讀取文件

private static void testReadFile(File file, String encod)

throws IOException {

FileInputStream fis = new FileInputStream(file);

InputStreamReader ins = new InputStreamReader(fis, encod);

int len = 0;

while ((len = ins.read()) != -1) {

System.out.print((char) len);

}

ins.close();

}

}

 

 

注意:碼錶不對應
分別測試:

使用系統默認編碼讀取utf-8編碼文件

使用utf-8編碼讀取gbk編碼文件

使用"gbk編碼讀取utf-8文件.

發現都會出現亂碼的問題.

// 使用系統默認編碼讀取utf-8

testReadFile(fileUTF);

// 傳入gbk編碼文件,使用utf-8解碼

testReadFile(fileGBK, "utf-8");

// 傳入utf-8文件,使用"gbk解碼

testReadFile(fileUTF, "gbk"); 

OutputStreamWriter

OutputStreamWriter

有了InputStreamReader 能夠轉換InputStream  

那麼其實還有OutputStreamWriter 能夠轉換OutputStream

OutputStreamWriter 是字符流通向字節流的橋樑

測試OutputStreamWriter 

: 分別使用OutputStreamWriter使用系統默認編碼,GBK,UTF-8相對應的默認編碼文件,GBK編碼文件,UTF-8編碼文件中寫出漢字」中國」.

: 在使用上述案例中的readFile方法傳入相對應碼錶讀取.

public class TestIo {

public class Demo4 {

public static void main(String[] args) throws IOException {

File file = new File("c:\\a.txt");

File fileGBK = new File("c:\\gbk.txt");

File fileUTF = new File("c:\\utf.txt");

 

// 寫入

// 使用系統默認碼錶寫入

testWriteFile(file);

// 使用gbk編碼向gbk文件寫入信息

testWriteFile(fileGBK, "gbk");

// 使用utf-8utf-8文件中寫入信息

testWriteFile(fileUTF, "utf-8");

 

// 讀取

// 默認編碼

testReadFile(file);

// 傳入gbk編碼文件,使用gbk解碼

testReadFile(fileGBK, "gbk");

// 傳入utf-8文件,使用utf-8解碼

testReadFile(fileUTF, "utf-8");

 

}

 

// 使用系統碼錶將信息寫入到文件中

private static void testWriteFile(File file) throws IOException {

FileOutputStream fos = new FileOutputStream(file);

OutputStreamWriter ops = new OutputStreamWriter(fos);

ops.write("中國");

ops.close();

}

 

// 使用指定碼錶,將信息寫入到文件中

private static void testWriteFile(File file, String encod)

throws IOException {

FileOutputStream fos = new FileOutputStream(file);

OutputStreamWriter ops = new OutputStreamWriter(fos, encod);

ops.write("中國");

ops.close();

}

 

// 該方法中nputStreamReader使用系統默認編碼讀取文件.

private static void testReadFile(File file) throws IOException {

FileInputStream fis = new FileInputStream(file);

InputStreamReader ins = new InputStreamReader(fis);

int len = 0;

while ((len = ins.read()) != -1) {

System.out.print((char) len);

}

ins.close();

 

}

 

// 該方法適合用指定編碼讀取文件

private static void testReadFile(File file, String encod)

throws IOException {

FileInputStream fis = new FileInputStream(file);

InputStreamReader ins = new InputStreamReader(fis, encod);

int len = 0;

while ((len = ins.read()) != -1) {

System.out.print((char) len);

}

 

ins.close();

}

 

}

 

注意: 碼錶不對應的問題

分別測試:

GBK文件中寫入utf-8編碼的信息

utf文件中寫入gbk編碼的信息

發現文件都有問題,沒法正常的讀取了.

 

public static void main(String[] args) throws IOException {

File file = new File("c:\\a.txt");

File fileGBK = new File("c:\\gbk.txt");

File fileUTF = new File("c:\\utf.txt");

 

// 寫入

// // 使用系統默認碼錶寫入

// testWriteFile(file);

// // 使用gbk編碼向gbk文件寫入信息

// testWriteFile(fileGBK, "gbk");

// // 使用utf-8utf-8文件中寫入信息

// testWriteFile(fileUTF, "utf-8");

 

testWriteFile(fileGBK);

// GBK文件中寫入utf-8編碼的信息

testWriteFile(fileGBK, "utf-8");

// utf文件中寫入gbk編碼的信息

testWriteFile(fileUTF, "gbk");

 

// 讀取

// 默認編碼

testReadFile(file);

// 傳入gbk編碼文件,使用gbk解碼

testReadFile(fileGBK, "gbk");

// 傳入utf-8文件,使用utf-8解碼

testReadFile(fileUTF, "utf-8");

 

}

 

InputStreamReader:字節到字符的橋樑。

OutputStreamWriter:字符到字節的橋樑。

 

它們有轉換做用,而自己又是字符流。因此在構造的時候,須要傳入字節流對象進來。

構造函數:

InputStreamReader(InputStream)

經過該構造函數初始化,使用的是本系統默認的編碼表GBK

InputStreamReader(InputStream,String charSet)

經過該構造函數初始化,能夠指定編碼表。

OutputStreamWriter(OutputStream)

經過該構造函數初始化,使用的是本系統默認的編碼表GBK

OutputStreamWriter(OutputStream,String charSet)

經過該構造函數初始化,能夠指定編碼表。

注意:

操做文件的字符流對象是轉換流的子類。

Reader

|--InputStreamReader

|--FileReader

Writer

|--OutputStreamWriter

|--FileWriter

 

注意:

在使用FileReader操做文本數據時,該對象使用的是默認的編碼表。

若是要使用指定編碼表時,必須使用轉換流。

 

若是系統默認編碼是GBK的:

FileReader fr = new FileReader("a.txt");//操做a.txt的中的數據使用的本系統默認的GBK

操做a.txt中的數據使用的也是本系統默認的GBK

InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

這兩句的代碼的意義相同。

可是:若是a.txt中的文件中的字符數據是經過utf-8的形式編碼。使用FileReader就無能爲力,那麼在讀取時,就必須指定編碼表。那麼轉換流必須使用。

InputStreamReader isr =

new InputStreamReader(new FileInputStream("a.txt"),"utf-8");

 

 

7. 遞歸

遞歸作爲一種算法在程序設計語言中普遍應用。是指函數/過程/子程序在運行過程當中直接或間接調用自身而產生的重入現象。

(本身調用本身,有結束條件)

注意:遞歸時必定要明確結束條件。

數學中遞歸運算.

對於任何正整數N ,N! (讀做N的階乘)的值定義爲1-N(包括N)的全部的整數的成績.所以3! 就是 3!=3*2*1 =6;

5! 定義爲5!=5*4*3*2*1=120

那麼整數N 的階乘 N! 能夠表示爲

1!=1

N!=N*(N-1)! for N>1

若果N 等於1 那麼1的繼承就是1,其餘全部N! =N*(N-1)!,例如:50!=50*49!

49!=49*48! 48!=48*47! 一直持續到1出現.

如何使用Java程序計算階乘?

public static long recursion(int n) {

if (n == 1) {

return 1;

} else {

return n * recursion(n - 1);

}

}

 

 

7.1. 案例:

1,列出指定目錄中全部的子孫文件與子孫目錄名,只須要列出名稱便可。

2,列出指定目錄中全部的子孫文件與子孫目錄名,要求名稱前面要有相應數量的空格:

第一級前面有0個,第二級前面有1個,第三級前面有2...,以此類推。

3,列出指定目錄中全部的子孫文件與子孫目錄名,要求要是樹狀結構,效果以下所示:

|--src

|   |--cn

|   |   |--itcast

|   |   |   |--a_helloworld

|   |   |   |   |--HelloWorld.java

|   |   |   |--b_for

|   |   |   |   |--ForTest.java

|   |   |   |--c_api

|   |   |   |   |--Student.java

|--bin

|   |--cn

|   |   |--itcast

|   |   |   |--i_exception

|   |   |   |   |--ExceptionTest.class

|   |   |   |--h_linecount

|   |   |   |   |--LineCounter3.class

|   |   |   |   |--LineCounter2.class

|   |   |   |   |--LineCounter.class

|--lib

|   |--commons-io.jar

答案:

案例一:

// 1,列出指定目錄中全部的子孫文件與子孫目錄名,只須要列出名稱便可。

private static void listFile(File file) {

 

File[] listFiles = file.listFiles();

 

for (File f : listFiles) {

if (f.isFile()) {

System.out.println(f.getName());

} else if (f.isDirectory()) {

System.out.println(f.getName());

listFile(f);

}

 

}

}

public static void main(String[] args) {

File file = new File("c:\\abc");

 listFile(file);

}

 

案例二

// 2,列出指定目錄中全部的子孫文件與子孫目錄名,要求名稱前面要有相應數量的空格:

private static void listFile2(File file, String str) {

 

File[] listFiles = file.listFiles();

 

for (int i = 0; i < listFiles.length; i++) {

File f = listFiles[i];

System.out.println(str + f.getName());

 

if (f.isDirectory()) {

listFile2(f, str + "-");

}

}

}

public static void main(String[] args) {

File file = new File("c:\\abc");

String str = "-";

listFile2(file, str);

 

 

 

 

案例三:

// 列出指定目錄中全部的子孫文件與子孫目錄名,要求要是樹狀結構

private static void listFile3(File file, String str) {

 

File[] listFiles = file.listFiles();

 

for (File f : listFiles) {

System.out.println(str + f.getName());

if (f.isDirectory()) {

listFile3(f, "|  " + str);

}

 

}

}

public static void main(String[] args) {

File file = new File("c:\\abc");

file = new File("c:\\day18ide");

file = new File("c:\\MyIo");

str = "|-";

    listFile3(file, str);

 

 

 

7.2. 練習:

1,刪除一個非空的目錄。 

2,移動一個非空的目錄到另外一個地方(剪切)。

3,把File類中的重要方法設計代碼測試一遍。

 

// 1,刪除一個非空的目錄。並增強健壯性

private static void deleteFile(File file) {

if (!file.exists()) {

System.out.println("路徑不存在");

return;

}

if (!file.isDirectory()) {

System.out.println("不是目錄");

return;

}

// 若是當前目錄中有子目錄和文件,先刪除子目錄和文件

File[] listFiles = file.listFiles();

for (File f : listFiles) {

if (f.isFile()) {

f.delete();

} else if (f.isDirectory()) {

deleteFile(f);

}

}

// 刪除當前目錄

file.delete();

 

}

0o

練習2:

使用File類的renameTo 方法和遞歸實現非空目錄的剪切.

public static void main(String[] args) throws IOException {

// 重命名文件(成功)

// File src = new File("c:\\aaa.txt");

// File dest = new File("c:\\bbb.txt");

// src.renameTo(dest);

 

// //移動文件(成功)

// File src = new File("c:\\aaa.txt");

// File dest = new File("d:\\aaa.txt");

// src.renameTo(dest);

 

// 移動一個空目錄(失敗)

// File src = new File("c:\\aaa");

// File dest = new File("d:\\aaa");

// System.out.println(src.renameTo(dest));

 

// 使用File類和遞歸實現文件的剪切.

File src = new File("c:\\abc");

File dest = new File("d:\\");

cutFile(src, dest);

 

}

 

// 移動一個非空的目錄到另外一個地方(剪切)。

private static void cutFile(File srcDir, File dest) throws IOException {

if (!srcDir.exists() || !dest.exists()) {

System.out.println("指定的源目錄或者目標目錄不存在");

return;

}

if (!srcDir.isDirectory() || !dest.isDirectory()) {

System.out.println("指定的源目錄或者目標目錄不是目錄");

return;

}

 

// 獲得源目錄名

String srcDirName = srcDir.getName(); // abc

// 根據源目錄名建立新目錄名

File destDir = new File(dest + srcDirName); // d:\\abc dest 爲父路徑

// srcDirName 爲子路徑

// 建立目標目錄

destDir.mkdir();

 

// 遍歷源目錄

File[] listFiles = srcDir.listFiles();

 

for (File f : listFiles) {

// 若是是子源文件,使用renameTo方法,移動至目標目錄中(該方法同時會刪除源目錄中的文件)

if (f.isFile()) {

f.renameTo(new File(destDir, f.getName())); // 指定目標文件的父目錄,文件名(根據源文件名生成).

} else if (f.isDirectory()) {

// 若是是子目錄,執行重複動做. 將源子目錄 , 目標目錄(父目錄+//)

cutFile(f, new File(destDir, File.separator)); // 指定源目錄,指定目的路徑d:\\abc\\

}

}

// 刪除源目錄

srcDir.delete();

 

}

相關文章
相關標籤/搜索