Java基礎14-java進階(5)【IO流】

小管呀發表於2020-11-15

Java進階06 IO流

1 File類

1.1 簡介

  • java.io.File類:檔案和檔案目錄路徑的抽象表示形式,與平臺無關

  • File 能新建、刪除、重新命名檔案和目錄,但 File 不能訪問檔案內容本身。如果需要訪問檔案內容本身,則需要使用輸入/輸出流。

  • 想要在Java程式中表示一個真實存在的檔案或目錄,那麼必須有一個File對 象,但是Java程式中的一個File物件,可能沒有一個真實存在的檔案或目錄。

  • File物件可以作為引數傳遞給流的構造器

1.2 常用構造器

  • public File(String pathname)

    • 以pathname為路徑建立File物件,可以是絕對路徑或者相對路徑,如果 pathname是相對路徑,則預設的當前路徑在系統屬性user.dir中儲存。
      • 絕對路徑:是一個固定的路徑,從碟符開始
      • 相對路徑:是相對於某個位置開始
    • “/”:表示根路徑(或windows碟符)
      • “./”表示當前路徑(idea中表示專案的路徑),在使用“./”獲取當前路徑的檔案時“./”可以省略
      • “…/”表示當前路徑的上一級(idea中表示專案的路徑)
  • public File(String parent,String child)

    • 以parent為父路徑,child為子路徑建立File物件。
  • public File(File parent,String child)

    • 根據一個父File物件和子檔案路徑建立File物件
  • 路徑中的每級目錄之間用一個路徑分隔符隔開。

  • 路徑分隔符和系統有關:

    • windows和DOS系統預設使用“\”來表示
    • UNIX和URL使用“/”來表示
  • Java程式支援跨平臺執行,因此路徑分隔符要慎用。

1.3 常用方法

  • File類的獲取功能

    • public String getAbsolutePath():獲取絕對路徑 (檔案)
    • public String getPath() :獲取路徑 (檔案)
    • public String getName() :獲取名稱 (檔案)
    • public String getParent():獲取上層檔案目錄路徑。若無,返回null (檔案)
    • public long length() :獲取檔案長度(即:位元組數)。不能獲取目錄的長度。 (檔案)
    • public long lastModified() :獲取最後一次的修改時間,毫秒值 (檔案)
    • public String[] list() :獲取指定目錄下的所有檔案或者檔案目錄的名稱陣列 (目錄)
    • public File[] listFiles() :獲取指定目錄下的所有檔案或者檔案目錄的File陣列 (目錄)
  • File類的重新命名功能

    • public boolean renameTo(File dest):把檔案重新命名為指定的檔案路徑 (檔案)
  • File類的判斷功能

    • public boolean isDirectory():判斷是否是檔案目錄 (目錄)
    • public boolean isFile() :判斷是否是檔案 (檔案)
    • public boolean exists() :判斷是否存在 (檔案)
    • public boolean canRead() :判斷是否可讀 (檔案)
    • public boolean canWrite() :判斷是否可寫 (檔案)
    • public boolean isHidden() :判斷是否隱藏 (檔案)
  • File類的建立功能

    • public boolean createNewFile() :建立檔案。若檔案存在,則不建立,返回false (檔案)
    • public boolean mkdir() :建立檔案目錄。如果此檔案目錄存在,就不建立了。 如果此檔案目錄的上層目錄不存在,也不建立。 (目錄)
    • public boolean mkdirs() :建立檔案目錄。如果上層檔案目錄不存在,一併建立 (目錄)
  • File類的刪除功能

    • public boolean delete():刪除檔案或者資料夾 (檔案/目錄)
    • 刪除注意事項: Java中的刪除不走回收站。 要刪除一個檔案目錄,請注意該檔案目錄內不能包含檔案或者檔案目錄

示例程式碼

File dir1 = new File("D:/IOTest/dir1");
if (!dir1.exists()) { // 如果D:/IOTest/dir1不存在,就建立為目錄
dir1.mkdir();
}
// 建立以dir1為父目錄,名為"dir2"的File物件
File dir2 = new File(dir1, "dir2");
if (!dir2.exists()) { // 如果還不存在,就建立為目錄
dir2.mkdirs();
}
File dir4 = new File(dir1, "dir3/dir4");
if (!dir4.exists()) {
dir4.mkdirs();
}
// 建立以dir2為父目錄,名為"test.txt"的File物件
File file = new File(dir2, "test.txt");
if (!file.exists()) { // 如果還不存在,就建立為檔案
file.createNewFile();
}

課堂練習-class01

1. 利用File構造器,new 一個檔案目錄file
	1)在其中建立多個檔案和目錄
	2)編寫方法,實現刪除file中指定檔案的操作
2. 判斷指定目錄下是否有字尾名為.jpg的檔案,如果有,就輸出該檔名稱
3. 遍歷指定目錄所有檔名稱,包括子檔案目錄中的檔案。
    
	擴充1:並計算指定目錄佔用空間的大小
	擴充2:刪除指定檔案目錄及其下的所有檔案

1.4 常見應用

遍歷檔案

public static void listAll(File dir) {
		
	
		System.out.println(dir.getName());
		//獲取指定目錄下當前的所有資料夾或者檔案物件
		
		File[] files = dir.listFiles();
		
		for(int x=0; x<files.length; x++){
			
			if(files[x].isDirectory()){
				listAll(files[x]);
			}
			else
				System.out.println(files[x].getName());
		}	
	}

刪除檔案

public static void main(String[] args) {

		File dir  = new File("e:\\demodir");
//		dir.delete();
		removeDir(dir);
	}

	public static void removeDir(File dir) {
		
		File[] files = dir.listFiles();
		
		for(File file : files){
			
			if(file.isDirectory()){
				removeDir(file);
			}else{
				System.out.println(file+":"+file.delete());
			}
		}
		System.out.println(dir+":"+dir.delete());
	}

2 IO流及其分類

2.1 Java IO原理

  • I/O是Input/Output的縮寫, I/O技術是非常實用的技術,用於處理裝置之間的資料傳輸。如讀/寫檔案,網路通訊等。

  • Java程式中,對於資料的輸入/輸出操作以“流(stream)”的方式進行。

  • java.io包下提供了各種“流”類和介面,用以獲取不同種類的資料,並通過標準的方法輸入或輸出資料。

  • 輸入input:讀取外部資料(磁 盤、光碟等儲存裝置的資料)到程式(記憶體)中。

  • 輸出output:將程式(記憶體) 資料輸出到磁碟、光碟等儲存裝置中。

在這裡插入圖片描述

2.2 流的分類 #重點#

  • 按操作資料單位不同分為:位元組流(8 bit)–>處理非文字檔案,字元流(16 bit)–>處理文字檔案

  • 按資料流的流向不同分為:輸入流,輸出流

  • 按流的角色的不同分為:節點流-->最基本的流,處理流

位元組流字元流
輸入流InputStreamReader
輸出流OutputStreamWriter

在這裡插入圖片描述

  1. Java的IO流共涉及40多個類,實際上非常規則,都是從如下4個抽象基類派生的。

  2. 由這四個類派生出來的子類名稱都是以其父類名作為子類名字尾。

在這裡插入圖片描述

3 節點流–#重點#

3.1 FileReader

//使用異常
FileReader fr = null;
        try {
            //1.例項化File類的物件,指明要操作的檔案
            File file = new File("hello.txt");
            //2.提供具體的流
            fr = new FileReader(file);

            //3.資料的讀入
            int data;
            while((data = fr.read()) != -1){
                System.out.print((char)data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流的關閉操作
            if(fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
//使用陣列的方式--》提高使用的效率


FileReader fr = null;
        try {
            //1.File類的例項化
            File file = new File("hello.txt");

            //2.FileReader流的例項化
            fr = new FileReader(file);

            //3.讀入的操作
            //read(char[] cbuf):返回每次讀入cbuf陣列中的字元的個數。如果達到檔案末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while((len = fr.read(cbuf)) != -1){
                //方式一:
                //正確的寫法
               for(int i = 0;i < len;i++){
                  System.out.print(cbuf[i]);
              }
                //方式二:
                String str = new String(cbuf,0,len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fr != null){
                //4.資源的關閉
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

3.2 FileWriter

//未使用異常
public class FileWriterText {
    public static void main(String[] args) throws IOException {
        //FileWriter
//        1.建立File物件,讓該物件關聯a.txt
        File file=new File("C:\\Users\\小管同學\\Desktop\\readerA.txt");
        FileWriter fileWriter=new FileWriter(file);
        fileWriter.write("fileWriter使用");
        fileWriter.close();
    }
}
//使用異常
public class FileWriterText {
    public static void main(String[] args)  {
        //FileWriter
//        1.建立File物件,讓該物件關聯a.txt
        File file=new File("C:\\Users\\小管同學\\Desktop\\readerA.txt");
        FileWriter fileWriter= null;
        try {
            fileWriter = new FileWriter(file);
            fileWriter.write("fileWriter使用");
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
FileWriter fw = null;
        try {
            //1.提供File類的物件,指明寫出到的檔案
            File file = new File("hello1.txt");

            //2.提供FileWriter的物件,用於資料的寫出
            fw = new FileWriter(file,false);

            //3.寫出的操作
            fw.write("I have a dream!\n");
            fw.write("you need to have a dream!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.流資源的關閉
            if(fw != null){

                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

文字檔案的複製

FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.建立File類的物件,指明讀入和寫出的檔案
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //2.建立輸入流和輸出流的物件
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);


            //3.資料的讀入和寫出操作
            char[] cbuf = new char[5];
            int len;//記錄每次讀入到cbuf陣列中的字元的個數
            while((len = fr.read(cbuf)) != -1){
                //每次寫出len個字元
                fw.write(cbuf,0,len);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.關閉流資源

            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                if(fr != null)
                    fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
//未使用異常
public class CopyCharFile {
    public static void main(String[] args) throws IOException {
//        1.建立原始檔以及目標檔案
        File sourceFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計.md");
        File destFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計(賦值).md");
//        2.建立FileReader和FileWriter
        FileReader reader = new FileReader( sourceFile);
        FileWriter writer = new FileWriter(destFile);
        // 3使用迴圈一遍讀一邊寫(讀一個字元,寫一個字元)
        int read = 0;
        while ((read = reader.read()) != -1){
            writer.write(read);
        }
        // 4關閉流(後建立或者使用的流先關閉)
        writer.close();
        reader.close();
    }
}
//使用異常
public class CopyCharFile {
    public static void main(String[] args) {
//        1.建立原始檔以及目標檔案
        File sourceFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計.md");
        File destFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計(賦值).md");
        FileReader reader = null;
        FileWriter writer = null;
        try {
//        2.建立FileReader和FileWriter
            reader = new FileReader( sourceFile);
            writer = new FileWriter(destFile);
            // 3使用迴圈一遍讀一邊寫(讀一個字元,寫一個字元)
//            int read = 0;
//            while ((read = reader.read()) != -1){
//                writer.write(read);
//            }
            // 3使用快取進行讀寫優化,調成2的倍數(1024)
            // 使用迴圈一遍讀一邊寫2(讀1024個字元,寫1024個字元)優化提高效率
            char[] cbuf=new char[1024];
            int len;
            while ((len=reader.read(cbuf))!=-1){
                writer.write(cbuf,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            // 4關閉流(後建立或者使用的流先關閉)
            writer.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.3 FileInputStream和FileOutputStream

FileInputStream fis = null;
        try {
            //1. 造檔案
            File file = new File("hello.txt");

            //2.造流
            fis = new FileInputStream(file);

            //3.讀資料
            byte[] buffer = new byte[5];
            int len;//記錄每次讀取的位元組的個數
            while((len = fis.read(buffer)) != -1){

                String str = new String(buffer,0,len);
                System.out.print(str);

            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null){
                //4.關閉資源
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

實現對圖片的複製

FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //
            File srcFile = new File("愛情與友情.jpg");
            File destFile = new File("愛情與友情2.jpg");

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //複製的過程
            byte[] buffer = new byte[5];
            int len;
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                //
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

指定路徑下檔案的複製

//指定路徑下檔案的複製
    public void copyFile(String srcPath,String destPath){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //
            File srcFile = new File(srcPath);
            File destFile = new File(destPath);

            //
            fis = new FileInputStream(srcFile);
            fos = new FileOutputStream(destFile);

            //複製的過程
            byte[] buffer = new byte[1024];
            int len;
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                //
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }


    }
//未使用異常
public class ImageCopy {
    public static void main(String[] args) throws Exception {
        File source=new File("C:\\Users\\小管同學\\Desktop\\text\\數小信.png");
        File dest=new File("C:\\Users\\小管同學\\Desktop\\text\\數小信(賦值).png");
        //位元組輸入流和位元組輸出流
        FileInputStream inputStream = new FileInputStream( source) ;
        FileOutputStream outputStream = new FileOutputStream( dest) ;
        //緩衝流
        BufferedInputStream BufferedInputStream=new BufferedInputStream(inputStream);
        BufferedOutputStream bufferedInputStream=new BufferedOutputStream(outputStream);
        long start=System.currentTimeMillis();
        byte[] bytes = new byte[1024];
        int len;
        while ((len = inputStream.read(bytes)) != -1) {
            outputStream.write(bytes, 0,len); ;
        }
        long end=System.currentTimeMillis();
        outputStream.close();
        inputStream.close();
        System.out.println("此次複製公用"+(end-start)+"毫秒");
    }
}

4 緩衝流-提高效率–#重點#

  • 為了提高資料讀寫的速度,Java API提供了帶緩衝功能的流類,在使用這些流類時,會建立一個內部緩衝區陣列,預設使用8192個位元組(8Kb)的緩衝區。

  • 緩衝流要“套接”在相應的節點流之上,根據資料操作單位可以把緩衝流分為:

    • BufferedInputStream BufferedOutputStream
    • BufferedReader BufferedWriter
  • 當讀取資料時,資料按塊讀入緩衝區,其後的讀操作則直接訪問緩衝區

  • 當使用BufferedInputStream讀取位元組檔案時,BufferedInputStream會一次性從檔案中讀取8192個(8Kb),存在緩衝區中,直到緩衝區裝滿了,才重新從檔案中讀取下一個8192個位元組陣列。

  • 向流中寫入位元組時,不會直接寫到檔案,先寫到緩衝區中直到緩衝區寫滿, BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡。使用方法 flush()可以強制將緩衝區的內容全部寫入輸出流

  • 關閉流的順序和開啟流的順序相反。只要關閉最外層流即可,關閉最外層流也會相應關閉內層節點流

  • flush()方法的使用:手動將buffer中內容寫入檔案

  • 如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會在關閉流之前重新整理緩衝區,關閉後不能再寫出

在這裡插入圖片描述

示例程式碼

BufferedReader br = null;
BufferedWriter bw = null;
try {
// 建立緩衝流物件:它是處理流,是對節點流的包裝
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
String str;
while ((str = br.readLine()) != null) { // 一次讀取字元文字檔案的一行字元
bw.write(str); // 一次寫入一行字串
bw.newLine(); // 寫入行分隔符
}
bw.flush(); // 重新整理緩衝區
} catch (IOException e) {
e.printStackTrace();
} finally {
// 關閉IO流物件
try {
if (bw != null) {
bw.close(); // 關閉過濾流時,會自動關閉它所包裝的底層節點流
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
e.printStackTrace();
} }
public class BufferedCopyFile {
    public static void main(String[] args) throws IOException {
//        1.建立原始檔以及目標檔案
        File sourceFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計.md");
        File destFile=new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計(賦值).md");
//        2.節點流
        FileReader reader = new FileReader( sourceFile);
        FileWriter writer = new FileWriter (destFile) ;
        //3帶緩衝功能的字元處理流
        BufferedReader bufferedReader = new BufferedReader(reader);
        BufferedWriter bufferedWriter = new BufferedWriter(writer);
        //readLine()每次讀入一行資料,如果沒有資料,返回null
        String str;
        while ((str=bufferedReader.readLine())!=null){
            bufferedWriter.write(str);
            bufferedWriter.newLine();//增加換行符
        }
        //4關閉流
        bufferedWriter.close();
        bufferedReader. close();
    }
}
public class BufferedCopyFile {
    public static void main(String[] args) throws IOException {
    
        BufferedReader bufferedReader = new BufferedReader( new FileReader( new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計.md")));
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter (new File("C:\\Users\\小管同學\\Desktop\\text\\06 網路程式設計(賦值).md")) );
        //readLine()每次讀入一行資料,如果沒有資料,返回null
        String str;
        while ((str=bufferedReader.readLine())!=null){
            bufferedWriter.write(str);
            bufferedWriter.newLine();//增加換行符
        }
        //4關閉流
        bufferedWriter.close();
        bufferedReader. close();
    }
}

課堂練習

1. 分別使用節點流:FileInputStream、FileOutputStream和緩衝流:BufferedInputStream、BufferedOutputStream實現文字檔案/圖片/視訊檔案的複製。並比較二者在資料複製方面的效率

2. 實現圖片加密操作。
int b = 0;
while((b = fis.read()) != -1){
    fos.write(b ^ 5);
}

5 轉換流 #重點#

  • 轉換流提供了在位元組流和字元流之間的轉換

  • Java API提供了兩個轉換流:

    • InputStreamReader:將InputStream轉換為Reader
    • OutputStreamWriter:將Writer轉換為OutputStream
  • 位元組流中的資料都是字元時,轉成字元流操作更高效。

  • 很多時候我們使用轉換流來處理檔案亂碼問題。實現編碼和解碼的功能。

在這裡插入圖片描述

5.1 InputStreamReader

  • 實現將位元組的輸入流按指定字符集轉換為字元的輸入流。

  • 需要和InputStream“套接”。

  • 構造器

    • public InputStreamReader(InputStream in)
    • public InputSreamReader(InputStream in,String charsetName)
Reader isr = new InputStreamReader(System.in,”gbk”); 
public class TransterTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fileInputStream = new FileInputStream(new File("C:\\Users\\小管同學\\Desktop\\text\\reader.txt"));
        InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream,"utf-8");//位元組到字元的轉換
        int read=0;
        while ((read=inputStreamReader.read())!=-1){
            System.out.print((char)read);
        }
        fileInputStream.close();
    }
}

5.2 OutputStreamWriter

  • 實現將字元的輸出流按指定字符集轉換為位元組的輸出流。

  • 需要和OutputStream“套接”。

  • 構造器

    • public OutputStreamWriter(OutputStream out)
    • public OutputSreamWriter(OutputStream out,String charsetName)

示例程式碼

public void testMyInput() throws Exception {
    FileInputStream fis = new FileInputStream("dbcp.txt");
    FileOutputStream fos = new FileOutputStream("dbcp5.txt");
    InputStreamReader isr = new InputStreamReader(fis, "GBK");
    OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(osw);
    String str = null;
    while ((str = br.readLine()) != null) {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }
    bw.close();
    br.close();
}
public class TransterTest {
    public static void main(String[] args) throws Exception {
        FileInputStream fileInputStream = new FileInputStream(new File("C:\\Users\\小管同學\\Desktop\\text\\reader.txt"));
        FileOutputStream fileOutputStream = new FileOutputStream(new File("C:\\Users\\小管同學\\Desktop\\text\\reader.txt"));

        InputStreamReader inputStreamReader=new InputStreamReader(fileInputStream,"utf-8");//位元組到字元的轉換
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");
        int read=0;
        while ((read=inputStreamReader.read())!=-1){
//            System.out.print((char)read);
            outputStreamWriter.write(read);
        }
//        fileInputStream.close();
        outputStreamWriter.close();
        inputStreamReader.close();
    }

}

6 標準輸入、輸出流

  • System.in和System.out分別代表了系統標準的輸入和輸出裝置

  • 預設輸入裝置是:鍵盤,輸出裝置是:顯示器

  • System.in的型別是InputStream

  • System.out的型別是PrintStream,其是OutputStream的子類 FilterOutputStream 的子類

  • 重定向:通過System類的setIn,setOut方法對預設裝置進行改變。

    • public static void setIn(InputStream in)
    • public static void setOut(PrintStream out)

示例程式碼

從鍵盤輸入字串,要求將讀取到的整行字串轉成大寫輸出。然後繼續進行輸入操作,直至當輸入“e”或者“exit”時,退出程式。
		System.out.println("請輸入資訊(退出輸入e或exit):");
		// 把"標準"輸入流(鍵盤輸入)這個位元組流包裝成字元流,再包裝成緩衝流
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String s = null;
		try {
			while ((s = br.readLine()) != null) { // 讀取使用者輸入的一行資料 --> 阻塞程式
				if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
					System.out.println("安全退出!!");
					break;
				}
				// 將讀取到的整行字串轉成大寫輸出
				System.out.println("-->:" + s.toUpperCase());
				System.out.println("繼續輸入資訊");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (br != null) {
					br.close(); // 關閉過濾流時,會自動關閉它包裝的底層節點流
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

7 物件流

  • ObjectInputStream和OjbectOutputSteam

    • 用於儲存和讀取基本資料型別資料或物件的處理流。它的強大之處就是可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。
  • 序列化:用ObjectOutputStream類儲存基本型別資料或物件的機制

  • 反序列化:用ObjectInputStream類讀取基本型別資料或物件的機制

  • ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變數

物件的序列化–掌握

  • 物件序列化機制允許把記憶體中的Java物件轉換成平臺無關的二進位制流,從而允許把這種二進位制流持久地儲存在磁碟上,或通過網路將這種二進位制流傳輸到另一個網路節點。當其它程式獲取了這種二進位制流,就可以恢復成原來的Java物件

  • 序列化的好處在於可將任何實現了Serializable介面的物件轉化為位元組資料, 使其在儲存和傳輸時可被還原

  • 序列化是 RMI(Remote Method Invoke – 遠端方法呼叫)過程的引數和返 回值都必須實現的機制,而 RMI 是 JavaEE 的基礎。因此序列化機制是JavaEE 平臺的基礎

  • 如果需要讓某個物件支援序列化機制,則必須讓物件所屬的類及其屬性是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一。 否則,會丟擲NotSerializableException異常

    • Serializable
    • Externalizable
  • 凡是實現Serializable介面的類都有一個表示序列化版本識別符號的靜態變數:

    • private static final long serialVersionUID;
    • serialVersionUID用來表明類的不同版本間的相容性。簡言之,其目的是以序列化物件進行版本控制,有關各版本反序列化時是否相容。
    • 如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自動生成的。若類的例項變數做了修改,serialVersionUID 可能發生變化。故建議, 顯式宣告。
  • 簡單來說,Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗 證版本一致性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異 常。(InvalidCastException)

使用物件流序列化物件

  • 若某個類實現了 Serializable 介面,該類的物件就是可序列化的:

    • 建立一個ObjectOutputStream
    • 呼叫ObjectOutputStream物件的writeObject(物件)方法輸出可序列化物件
    • 注意寫出一次,操作flush()一次
  • 反序列化

    • 建立一個ObjectInputStream
    • 呼叫readObject()方法讀取流中的物件

強調:如果某個類的屬性不是基本資料型別或 String 型別,而是另一個引用型別,那麼這個引用型別必須是可序列化的,否則擁有該型別的Field的類也不能序列化

示例程式碼

//序列化:將物件寫入到磁碟或者進行網路傳輸。
//要求物件必須實現序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“data.txt"));
Person p = new Person("韓梅梅", 18, "中華大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:將磁碟中的物件資料來源讀出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“data.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();

8 隨機存取檔案流

  • RandomAccessFile宣告在java.io包下,但直接繼承於java.lang.Object類。並且它實現了DataInput、DataOutput這兩個介面,也就意味著這個類既可以讀也可以寫。

  • RandomAccessFile 類支援 “隨機訪問”的方式,程式可以直接跳到檔案的任意地方來讀、寫檔案

    • 支援只訪問檔案的部分內容
    • 可以向已存在的檔案後追加內容
  • RandomAccessFile 物件包含一個記錄指標,用以標示當前讀寫處的位置。RandomAccessFile 類物件可以自由移動記錄指標:

    • long getFilePointer():獲取檔案記錄指標的當前位置
    • void seek(long pos):將檔案記錄指標定位到 pos 位置
  • 構造器

    • public RandomAccessFile(File file, String mode)
    • public RandomAccessFile(String name, String mode)
  • 建立 RandomAccessFile 類例項需要指定一個 mode 引數,該引數指定 RandomAccessFile 的訪問模式:

    • r:以只讀方式開啟
    • rw:開啟以便讀取和寫入
    • rwd:開啟以便讀取和寫入;同步檔案內容的更新
    • rws:開啟以便讀取和寫入;同步檔案內容和後設資料的更新
  • 如果模式為只讀r。則不會建立檔案,而是會去讀取一個已經存在的檔案,如果讀取的檔案不存在則會出現異常。 如果模式為rw讀寫。如果檔案不存在則會去建立檔案,如果存在則不會建立。

示例程式碼

//讀取檔案內容
RandomAccessFile raf = new RandomAccessFile(“test.txt”, “rw”);
raf.seek(5);
byte [] b = new byte[1024];
int off = 0;
int len = 5;
raf.read(b, off, len);
String str = new String(b, 0, len);
System.out.println(str);
raf.close();
//寫入檔案內容
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
raf.seek(5);
//先讀出來
String temp = raf.readLine();
raf.seek(5);
raf.write("xyz".getBytes());
raf.write(temp.getBytes());
raf.close();

9 流的基本應用小節

  • 流是用來處理資料的。

  • 處理資料時,一定要先明確資料來源,與資料目的地

    • 資料來源可以是檔案,可以是鍵盤。
    • 資料目的地可以是檔案、顯示器或者其他裝置。
  • 處理流是在幫助資料進行傳輸,並對傳輸的資料進行處理,比如過濾處理、轉換處理等。。

相關文章