【重學Java】IO流


IO流的UML類圖

image

image

File類

File類概述和構造方法【應用】

  • File類介紹

    • 它是文件和目錄路徑名的抽象表示
    • 文件和目錄是可以通過File封裝成對象的
    • 對於File而言,其封裝的並不是一個真正存在的文件,僅僅是一個路徑名而已.它可以是存在的,也可以是不存在的.將來是要通過具體的操作把這個路徑的內容轉換為具體存在的
  • File類的構造方法

    方法名 說明
    File(String pathname) 通過將給定的路徑名字符串轉換為抽象路徑名來創建新的 File實例
    File(String parent, String child) 從父路徑名字符串和子路徑名字符串創建新的 File實例
    File(File parent, String child) 從父抽象路徑名和子路徑名字符串創建新的 File實例
  • 示例代碼

  public class FileDemo01 {
      public static void main(String[] args) {
          //File(String pathname): 通過將給定的路徑名字符串轉換為抽象路徑名來創建新的 File實例
          File f1 = new File("E:\\itcast\\java.txt");
          System.out.println(f1);
  
          //File(String parent, String child): 從父路徑名字符串和子路徑名字符串創建新的 File實例
          File f2 = new File("E:\\itcast","java.txt");
          System.out.println(f2);
  
          //File(File parent, String child): 從父抽象路徑名和子路徑名字符串創建新的 File實例
          File f3 = new File("E:\\itcast");
          File f4 = new File(f3,"java.txt");
          System.out.println(f4);
      }
  }

絕對路徑和相對路徑【理解】

  • 絕對路徑

    是一個完整的路徑,從盤符開始

  • 相對路徑

    是一個簡化的路徑,相對當前項目下的路徑

  • 示例代碼

  public class FileDemo02 {
      public static void main(String[] args) {
          // 是一個完整的路徑,從盤符開始
          File file1 = new File("D:\\itcast\\a.txt");
  
          // 是一個簡化的路徑,從當前項目根目錄開始
          File file2 = new File("a.txt");
          File file3 = new File("模塊名\\a.txt");
      }
  }

File類創建功能【應用】

  • 方法分類

    方法名 說明
    public boolean createNewFile() 當具有該名稱的文件不存在時,創建一個由該抽象路徑名命名的新空文件。若存在該名稱的文件則返回false。
    不管調用者有沒有后綴名,只能創建文件
    public boolean mkdir() 創建由此抽象路徑名命名的目錄
    只能重建單級文件夾,不能創建多級文件夾
    不管有沒有后綴名,只能創建單級文件夾(一般不用該方法,mkdirs可以代替)
    public boolean mkdirs() 創建由此抽象路徑名命名的目錄,包括任何必需但不存在的父目錄
    創建一個多級文件夾 也可以創建一個單級文件夾
  • 示例代碼

  public class FileDemo02 {
      public static void main(String[] args) throws IOException {
          //需求1:我要在E:\\itcast目錄下創建一個文件java.txt
          File f1 = new File("E:\\itcast\\java.txt");
          System.out.println(f1.createNewFile());
          System.out.println("--------");
  
          //需求2:我要在E:\\itcast目錄下創建一個目錄JavaSE
          File f2 = new File("E:\\itcast\\JavaSE");
          System.out.println(f2.mkdir());
          System.out.println("--------");
  
          //需求3:我要在E:\\itcast目錄下創建一個多級目錄JavaWEB\\HTML
          File f3 = new File("E:\\itcast\\JavaWEB\\HTML");
  //        System.out.println(f3.mkdir());
          System.out.println(f3.mkdirs());
          System.out.println("--------");
  
          //需求4:我要在E:\\itcast目錄下創建一個文件javase.txt
          File f4 = new File("E:\\itcast\\javase.txt");
  //        System.out.println(f4.mkdir());
          System.out.println(f4.createNewFile());
      }
  }

File類刪除功能【應用】

  • 方法分類

    方法名 說明
    public boolean delete() 刪除由此抽象路徑名表示的文件或目錄,刪除的文件不進入回收站
    如果刪除的是文件,那么直接刪除;如果刪除的是文件夾,那么只能刪除空文件夾
    如果要刪除一個有內容的文件夾,只能先進入該文件夾中,把里面的內容刪除完畢才能再次刪除這個文件夾
    簡單來說:只能刪除文件和空文件夾
  • 示例代碼

  public class FileDemo03 {
      public static void main(String[] args) throws IOException {
  //        File f1 = new File("E:\\itcast\\java.txt");
          //需求1:在當前模塊目錄下創建java.txt文件
          File f1 = new File("myFile\\java.txt");
  //        System.out.println(f1.createNewFile());
  
          //需求2:刪除當前模塊目錄下的java.txt文件
          System.out.println(f1.delete());
          System.out.println("--------");
  
          //需求3:在當前模塊目錄下創建itcast目錄
          File f2 = new File("myFile\\itcast");
  //        System.out.println(f2.mkdir());
  
          //需求4:刪除當前模塊目錄下的itcast目錄
          System.out.println(f2.delete());
          System.out.println("--------");
  
          //需求5:在當前模塊下創建一個目錄itcast,然后在該目錄下創建一個文件java.txt
          File f3 = new File("myFile\\itcast");
  //        System.out.println(f3.mkdir());
          File f4 = new File("myFile\\itcast\\java.txt");
  //        System.out.println(f4.createNewFile());
  
          //需求6:刪除當前模塊下的目錄itcast
          System.out.println(f4.delete());
          System.out.println(f3.delete());
      }
  }

File類判斷和獲取功能【應用】

  • 判斷功能

    方法名 說明
    public boolean isDirectory() 測試此抽象路徑名表示的File是否為目錄
    public boolean isFile() 測試此抽象路徑名表示的File是否為文件
    public boolean exists() 測試此抽象路徑名表示的File是否存在
  • 獲取功能

    方法名 說明
    public String getAbsolutePath() 返回此抽象路徑名的絕對路徑名字符串
    public String getPath() 將此抽象路徑名轉換為路徑名字符串
    public String getName() 返回由此抽象路徑名表示的文件或目錄的名稱
    如果調用者是文件,那么獲取的是文件名和后綴名
    如果調用者是一個文件夾,那么獲取的是文件夾的名字
    public File[] listFiles() 返回此抽象路徑名表示的目錄中的文件和目錄的File對象數組(包括隱藏文件和隱藏文件夾) image
  • 示例代碼

  public class FileDemo04 {
      public static void main(String[] args) {
          //創建一個File對象
          File f = new File("myFile\\java.txt");
  
  //        public boolean isDirectory():測試此抽象路徑名表示的File是否為目錄
  //        public boolean isFile():測試此抽象路徑名表示的File是否為文件
  //        public boolean exists():測試此抽象路徑名表示的File是否存在
          System.out.println(f.isDirectory());
          System.out.println(f.isFile());
          System.out.println(f.exists());
  
  //        public String getAbsolutePath():返回此抽象路徑名的絕對路徑名字符串
  //        public String getPath():將此抽象路徑名轉換為路徑名字符串
  //        public String getName():返回由此抽象路徑名表示的文件或目錄的名稱
          System.out.println(f.getAbsolutePath());
          System.out.println(f.getPath());
          System.out.println(f.getName());
          System.out.println("--------");
  
  //        public File[] listFiles():返回此抽象路徑名表示的目錄中的文件和目錄的File對象數組
          File f2 = new File("E:\\itcast");
          File[] fileArray = f2.listFiles();
          for(File file : fileArray) {
  //            System.out.println(file);
  //            System.out.println(file.getName());
              if(file.isFile()) {
                  System.out.println(file.getName());
              }
          }
      }
  }

File類練習一【應用】

  • 案例需求

    在當前模塊下的aaa文件夾中創建一個a.txt文件

  • 實現步驟

    • 創建File對象,指向aaa文件夾
    • 判斷aaa文件夾是否存在,如果不存在則創建
    • 創建File對象,指向aaa文件夾下的a.txt文件
    • 創建這個文件
  • 代碼實現

  public class Test1 {
      public static void main(String[] args) throws IOException {
          //練習一:在當前模塊下的aaa文件夾中創建一個a.txt文件
         /* File file = new File("filemodule\\aaa\\a.txt");
          file.createNewFile();*/
          //注意點:文件所在的文件夾必須要存在.
  
        	//1.創建File對象,指向aaa文件夾
          File file = new File("filemodule\\aaa");
        	//2.判斷aaa文件夾是否存在,如果不存在則創建
          if(!file.exists()){
              //如果文件夾不存在,就創建出來
              file.mkdirs();
          }
        	//3.創建File對象,指向aaa文件夾下的a.txt文件
          File newFile = new File(file,"a.txt");
        	//4.創建這個文件
          newFile.createNewFile();
      }
  }

File類練習二【應用】

  • 案例需求

    刪除一個多級文件夾

  • 實現步驟

    • 定義一個方法,接收一個File對象
    • 遍歷這個File對象,獲取它下邊的每個文件和文件夾對象
    • 判斷當前遍歷到的File對象是文件還是文件夾
    • 如果是文件,直接刪除
    • 如果是文件夾,遞歸調用自己,將當前遍歷到的File對象當做參數傳遞
    • 參數傳遞過來的文件夾File對象已經處理完成,最后直接刪除這個空文件夾
  • 代碼實現

  public class Test2 {
      public static void main(String[] args) {
          //練習二:刪除一個多級文件夾
          //delete方法
          //只能刪除文件和空文件夾.
          //如果現在要刪除一個有內容的文件夾?
          //先刪掉這個文件夾里面所有的內容.
          //最后再刪除這個文件夾
  
          File src = new File("C:\\Users\\apple\\Desktop\\src");
          deleteDir(src);
      }
    
  	//1.定義一個方法,接收一個File對象
      private static void deleteDir(File src) {
          //先刪掉這個文件夾里面所有的內容.
          //遞歸 方法在方法體中自己調用自己.
          //注意: 可以解決所有文件夾和遞歸相結合的題目
          //2.遍歷這個File對象,獲取它下邊的每個文件和文件夾對象
          File[] files = src.listFiles();
          //3.判斷當前遍歷到的File對象是文件還是文件夾
          for (File file : files) {
              //4.如果是文件,直接刪除
              if(file.isFile()){
                  file.delete();
              }else{
                  //5.如果是文件夾,遞歸調用自己,將當前遍歷到的File對象當做參數傳遞
                  deleteDir(file);//參數一定要是src文件夾里面的文件夾File對象
              }
          }
          //6.參數傳遞過來的文件夾File對象已經處理完成,最后直接刪除這個空文件夾
          src.delete();  //這一步必須放在函數里參與遞歸,把每一個空文件夾刪除
      }
  
  }

File類練習三【應用】

  • 案例需求

    統計一個文件夾中每種文件的個數並打印

    打印格式如下:

      		txt:3個
    
      		doc:4個
    
      		jpg:6個
    
      		 …
    
  • 實現步驟

    • 定義一個方法,參數是HashMap集合用來統計次數和File對象要統計的文件夾
    • 遍歷File對象,獲取它下邊的每一個文件和文件夾對象
    • 判斷當前File對象是文件還是文件夾
    • 如果是文件,判斷這種類型文件后綴名在HashMap集合中是否出現過
      • 沒出現過,將這種類型文件的后綴名存入集合中,次數存1
      • 出現過,獲取這種類型文件的后綴名出現的次數,對其+1,在存回集合中
    • 如果是文件夾,遞歸調用自己,HashMap集合就是參數集合,File對象是當前文件夾對象
  • 代碼實現

  public class Test3 {
      public static void main(String[] args) {
          //統計一個文件夾中,每種文件出現的次數.
          //統計 --- 定義一個變量用來統計. ---- 弊端:同時只能統計一種文件
          //利用map集合進行數據統計,鍵 --- 文件后綴名  值 ----  次數
  
          File file = new File("filemodule");
          HashMap<String, Integer> hm = new HashMap<>();
          getCount(hm, file);
          System.out.println(hm);
      }
    
  	//1.定義一個方法,參數是HashMap集合用來統計次數和File對象要統計的文件夾
      private static void getCount(HashMap<String, Integer> hm, File file) {
        	//2.遍歷File對象,獲取它下邊的每一個文件和文件夾對象
          File[] files = file.listFiles();
          for (File f : files) {
            	//3.判斷當前File對象是文件還是文件夾
              if(f.isFile()){
                	//如果是文件,判斷這種類型文件后綴名在HashMap集合中是否出現過
                  String fileName = f.getName();
                  String[] fileNameArr = fileName.split("\\.");
                  if(fileNameArr.length == 2){
                      String fileEndName = fileNameArr[1];
                      if(hm.containsKey(fileEndName)){
                          //出現過,獲取這種類型文件的后綴名出現的次數,對其+1,在存回集合中
                          Integer count = hm.get(fileEndName);
                          //這種文件又出現了一次.
                          count++;
                          //把已經出現的次數給覆蓋掉.
                          hm.put(fileEndName,count);
                      }else{
                          // 沒出現過,將這種類型文件的后綴名存入集合中,次數存1
                          hm.put(fileEndName,1);
                      }
                  }
              }else{
                //如果是文件夾,遞歸調用自己,HashMap集合就是參數集合,File對象是當前文件夾對象代碼實現
                  getCount(hm,f);
              }
          }
      }
    
  }

字節流

IO流概述和分類【理解】

  • IO流介紹
    • IO:輸入/輸出(Input/Output)
    • 流:是一種抽象概念,是對數據傳輸的總稱.也就是說數據在設備間的傳輸稱為流,流的本質是數據傳輸
    • IO流就是用來處理設備間數據傳輸問題的.常見的應用: 文件復制; 文件上傳; 文件下載
  • IO流的分類
    • 按照數據的流向
      • 輸入流:讀數據
      • 輸出流:寫數據
      • image
    • 按照數據類型來分
      • 字節流
        • 字節輸入流
        • 字節輸出流
      • 字符流
        • 字符輸入流
        • 字符輸出流
      • image
  • IO流的使用場景
    • 如果操作的是純文本文件,優先使用字符流
    • 如果操作的是圖片、視頻、音頻等二進制文件,優先使用字節流
    • 如果不確定文件類型,優先使用字節流.字節流是萬能的流
  • close和flush注意事項
    • 所有的流都實現了:java.io.Closeable接口,都是可關閉的,都有close()方法。流畢竟是一個管道,這個是內存和硬盤之間的通道,用完之后一定要關閉,不然會耗費(占用)很多資源。養成好習慣,用完流一定要關閉。

    • 所有的輸出流都實現了:java.io.Flushable接口,都是可刷新的,都有flush()方法。養成一個好習慣,輸出流在最終輸出之后,一定要記得flush()刷新一下。這個刷新表示將通道/管道當中剩余未輸出的數據強行輸出完(清空管道!)刷新的作用就是清空管道。
      注意:如果沒有flush()可能會導致丟失數據。

字節流寫數據【應用】

  • 字節流抽象基類

    • InputStream:這個抽象類是表示字節輸入流的所有類的超類
    • OutputStream:這個抽象類是表示字節輸出流的所有類的超類
    • 子類名特點:子類名稱都是以其父類名作為子類名的后綴
  • 字節輸出流

    • FileOutputStream(String name):創建文件輸出流以指定的名稱寫入文件
  • 使用字節輸出流寫數據的步驟

    1. 創建字節輸出流對象(調用系統功能創建了文件,創建字節輸出流對象,讓字節輸出流對象指向文件)
    2. 調用字節輸出流對象的寫數據方法
    3. 釋放資源(關閉此文件輸出流並釋放與此流相關聯的任何系統資源)
  • 示例代碼

  public class FileOutputStreamDemo01 {
      public static void main(String[] args) throws IOException {
          //創建字節輸出流對象
        	/*
        		注意點:
        				1.如果文件不存在,會幫我們創建
        				2.如果文件存在,會把文件清空
        	*/
        	//FileOutputStream(String name):創建文件輸出流以指定的名稱寫入文件
          FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
  
          //void write(int b):將指定的字節寫入此文件輸出流
          fos.write(97);
  //        fos.write(57);
  //        fos.write(55);
  
          //最后都要釋放資源
          //void close():關閉此文件輸出流並釋放與此流相關聯的任何系統資源。
          fos.close();
      }
  }

字節流寫數據的三種方式【應用】

  • 寫數據的方法分類

    方法名 說明
    void write(int b) 將指定的字節寫入此文件輸出流 一次寫一個字節數據
    void write(byte[] b) 將 b.length字節從指定的字節數組寫入此文件輸出流 一次寫一個字節數組數據
    void write(byte[] b, int off, int len) 將 len字節從指定的字節數組開始,從偏移量off開始寫入此文件輸出流 一次寫一個字節數組的部分數據
  • 示例代碼

  public class FileOutputStreamDemo02 {
      public static void main(String[] args) throws IOException {
          //FileOutputStream(String name):創建文件輸出流以指定的名稱寫入文件
          FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
          //FileOutputStream(File file):創建文件輸出流以寫入由指定的 File對象表示的文件
  //        FileOutputStream fos = new FileOutputStream(new File("myByteStream\\fos.txt"));
  
          //void write(int b):將指定的字節寫入此文件輸出流
  //        fos.write(97);
  //        fos.write(98);
  //        fos.write(99);
  //        fos.write(100);
  //        fos.write(101);
  
  //        void write(byte[] b):將 b.length字節從指定的字節數組寫入此文件輸出流
  //        byte[] bys = {97, 98, 99, 100, 101};
          //byte[] getBytes():返回字符串對應的字節數組
          byte[] bys = "abcde".getBytes();
  //        fos.write(bys);
  
          //void write(byte[] b, int off, int len):將 len字節從指定的字節數組開始,從偏移量off開始寫入此文件輸出流
  //        fos.write(bys,0,bys.length);
          fos.write(bys,1,3);
  
          //釋放資源
          fos.close();
      }
  }

字節流寫數據的兩個小問題【應用】

  • 字節流寫數據如何實現換行

    • windows:\r\n
    • linux:\n
    • mac:\r
  • 字節流寫數據如何實現追加寫入

    • public FileOutputStream(String name,boolean append)
    • 創建文件輸出流以指定的名稱寫入文件。如果第二個參數為true ,則字節將寫入文件的末尾而不是開頭
  • 示例代碼

  public class FileOutputStreamDemo03 {
      public static void main(String[] args) throws IOException {
          //創建字節輸出流對象
  //        FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt");
          FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true);
  
          //寫數據
          for (int i = 0; i < 10; i++) {
              fos.write("hello".getBytes());
              fos.write("\r\n".getBytes());
          }
  
          //釋放資源
          fos.close();
      }
  }

字節流寫數據加異常處理【應用】

  • 異常處理格式

    • try-catch-finally
    try{
    	可能出現異常的代碼;
    }catch(異常類名 變量名){
    	異常的處理代碼;
    }finally{
    	執行所有清除操作;
    }
  • finally特點

    • 被finally控制的語句一定會執行,除非JVM退出
  • 示例代碼

  public class FileOutputStreamDemo04 {
      public static void main(String[] args) {
          //加入finally來實現釋放資源
          FileOutputStream fos = null;
          try {
              fos = new FileOutputStream("myByteStream\\fos.txt");
              fos.write("hello".getBytes());
          } catch (IOException e) {
              e.printStackTrace();
          } finally {
              if(fos != null) {
                  try {
                      fos.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
      }
  }

字節流讀數據(一次讀一個字節數據)【應用】

  • 字節輸入流

    • FileInputStream(String name):通過打開與實際文件的連接來創建一個FileInputStream,該文件由文件系統中的路徑名name命名
  • 字節輸入流讀取數據的步驟

    • 創建字節輸入流對象
    • 調用字節輸入流對象的讀數據方法
    • 釋放資源
  • 示例代碼

  public class FileInputStreamDemo01 {
      public static void main(String[] args) throws IOException {
          //創建字節輸入流對象
          //FileInputStream(String name)
          FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
  
          int by;
          /*
              fis.read():讀數據
              by=fis.read():把讀取到的數據賦值給by
              by != -1:判斷讀取到的數據是否是-1
           */
          while ((by=fis.read())!=-1) {
              System.out.print((char)by);
          }
  
          //釋放資源
          fis.close();
      }
  }

字節流復制文件【應用】

  • 案例需求

    ​ 把“E:\itcast\窗里窗外.txt”復制到模塊目錄下的“窗里窗外.txt” (文件可以是任意文件)

  • 實現步驟

    • 復制文本文件,其實就把文本文件的內容從一個文件中讀取出來(數據源),然后寫入到另一個文件中(目的地)

    • 數據源:

      ​ E:\itcast\窗里窗外.txt --- 讀數據 --- InputStream --- FileInputStream

    • 目的地:

      ​ myByteStream\窗里窗外.txt --- 寫數據 --- OutputStream --- FileOutputStream

  • 代碼實現

  public class CopyTxtDemo {
      public static void main(String[] args) throws IOException {
          //根據數據源創建字節輸入流對象
          FileInputStream fis = new FileInputStream("E:\\itcast\\窗里窗外.txt");
          //根據目的地創建字節輸出流對象
          FileOutputStream fos = new FileOutputStream("myByteStream\\窗里窗外.txt");
  
          //讀寫數據,復制文本文件(一次讀取一個字節,一次寫入一個字節)
          int by;
          while ((by=fis.read())!=-1) {   //(by=fis.read())小括號別忘了
              fos.write(by);
          }
  
          //釋放資源
          fos.close();
          fis.close();
      }
  }

字節流讀數據(一次讀一個字節數組數據)【應用】

  • 一次讀一個字節數組的方法

    • public int read(byte[] b):從輸入流讀取最多b.length個字節的數據
    • 返回的是讀入緩沖區的總字節數,也就是實際的讀取字節個數
  • 示例代碼

  public class FileInputStreamDemo02 {
      public static void main(String[] args) throws IOException {
          //創建字節輸入流對象
          FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
  
          byte[] bys = new byte[1024]; //1024及其整數倍
          int len;  //len表示一次讀取到的有效字節數量
        	//循環讀取
          while ((len=fis.read(bys))!=-1) {
              System.out.print(new String(bys,0,len));
          }
  
          //釋放資源
          fis.close();
      }
  }

字節流復制文件【應用】

  • 案例需求

    ​ 把“E:\itcast\mn.jpg”復制到模塊目錄下的“mn.jpg” (文件可以是任意文件去)

  • 實現步驟

    • 根據數據源創建字節輸入流對象
    • 根據目的地創建字節輸出流對象
    • 讀寫數據,復制圖片(一次讀取一個字節數組,一次寫入一個字節數組)
    • 釋放資源
  • 代碼實現

  public class CopyJpgDemo {
      public static void main(String[] args) throws IOException {
          //根據數據源創建字節輸入流對象
          FileInputStream fis = new FileInputStream("E:\\itcast\\mn.jpg");
          //根據目的地創建字節輸出流對象
          FileOutputStream fos = new FileOutputStream("myByteStream\\mn.jpg");
  
          //讀寫數據,復制圖片(一次讀取一個字節數組,一次寫入一個字節數組)
          byte[] bys = new byte[1024];
          int len;
          while ((len=fis.read(bys))!=-1) {
              fos.write(bys,0,len);
          }
  
          //釋放資源
          fos.close();
          fis.close();
      }
  }

字節緩沖流

字節緩沖流構造方法【應用】

  • 字節緩沖流介紹

    • BufferOutputStream:該類實現緩沖輸出流.通過設置這樣的輸出流,應用程序可以向底層輸出流寫入字節,而不必為寫入的每個字節導致底層系統的調用
    • BufferedInputStream:創建BufferedInputStream將創建一個內部緩沖區數組.當從流中讀取或跳過字節時,內部緩沖區將根據需要從所包含的輸入流中重新填充,一次很多字節
  • 構造方法:

    方法名 說明
    BufferedOutputStream(OutputStream out) 創建字節緩沖輸出流對象
    BufferedInputStream(InputStream in) 創建字節緩沖輸入流對象
  • 底層原理

    • 一次讀取一個字節
      image

    • 一次讀取一個字節數組
      image

    • 創建了一個大小為8192字節的緩沖數組,減少了硬盤與內存之間數據傳遞的次數,提高效率。

  • 示例代碼

  public class BufferStreamDemo {
      public static void main(String[] args) throws IOException {
          //字節緩沖輸出流:BufferedOutputStream(OutputStream out)
          //緩沖流也叫包裝流(處理流),FileOutputStream叫節點流
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\bos.txt"));
          //寫數據
          bos.write("hello\r\n".getBytes());
          bos.write("world\r\n".getBytes());
          //釋放資源
          bos.close();
      
  
          //字節緩沖輸入流:BufferedInputStream(InputStream in)
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("myByteStream\\bos.txt"));
  
          //一次讀取一個字節數據
  //        int by;
  //        while ((by=bis.read())!=-1) {
  //            System.out.print((char)by);
  //        }
  
          //一次讀取一個字節數組數據
          byte[] bys = new byte[1024];
          int len;
          while ((len=bis.read(bys))!=-1) {
              System.out.print(new String(bys,0,len));
          }
  
          //釋放資源
          bis.close();  //包裝流關閉,節點流自動關閉
	/* 源碼如下
	public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (U.compareAndSetReference(this, BUF_OFFSET, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)    //關閉節點流
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }*/
      }
  }

字節緩沖流復制視頻【應用】

  • 案例需求

    把“E:\itcast\字節流復制圖片.avi”復制到模塊目錄下的“字節流復制圖片.avi”

  • 實現步驟

    • 根據數據源創建字節輸入流對象
    • 根據目的地創建字節輸出流對象
    • 讀寫數據,復制視頻
    • 釋放資源
  • 代碼實現

  public class CopyAviDemo {
      public static void main(String[] args) throws IOException {
  
          //復制視頻
  //        method1();
        	 method2();
  
      }
  
      //字節緩沖流一次讀寫一個字節數組
      public static void method2() throws IOException {
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\itcast\\字節流復制圖片.avi"));
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字節流復制圖片.avi"));
  
          byte[] bys = new byte[1024];
          int len;
          while ((len=bis.read(bys))!=-1) {
              bos.write(bys,0,len);
          }
  
          bos.close();
          bis.close();
      }
  
      //字節緩沖流一次讀寫一個字節
      public static void method1() throws IOException {
          BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\itcast\\字節流復制圖片.avi"));
          BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字節流復制圖片.avi"));
  
          int by;
          while ((by=bis.read())!=-1) {
              bos.write(by);
          }
  
          bos.close();
          bis.close();
      }
  
  }

字符流

為什么會出現字符流【理解】

  • 字符流的介紹

    由於字節流操作中文不是特別的方便,所以Java就提供字符流

    字符流 = 字節流 + 編碼表

  • 中文的字節存儲方式

    用字節流復制文本文件時,文本文件也會有中文,但是沒有問題,原因是最終底層操作會自動進行字節拼接成中文,如何識別是中文的呢?

    漢字在存儲的時候,無論選擇哪種編碼存儲,第一個字節都是負數

image

編碼表【理解】

  • 什么是字符集

    是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等

    l計算機要准確的存儲和識別各種字符集符號,就需要進行字符編碼,一套字符集必然至少有一套字符編碼。常見字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

  • 常見的字符集

    • ASCII字符集:

      lASCII:是基於拉丁字母的一套電腦編碼系統,用於顯示現代英語,主要包括控制字符(回車鍵、退格、換行鍵等)和可顯示字符(英文大小寫字符、阿拉伯數字和西文符號)

      基本的ASCII字符集,使用7位表示一個字符,共128字符。ASCII的擴展字符集使用8位表示一個字符,共256字符,方便支持歐洲常用字符。是一個系統支持的所有字符的集合,包括各國家文字、標點符號、圖形符號、數字等

    • GBXXX字符集:

      GBK:最常用的中文碼表。是在GB2312標准基礎上的擴展規范,使用了雙字節編碼方案,共收錄了21003個漢字,完全兼容GB2312標准,同時支持繁體漢字以及日韓漢字等.

      注意GBK編碼以后一個中文以兩個字節的形式存儲。

      Windows默認使用GBK

    • Unicode字符集:

      UTF-8編碼:可以用來表示Unicode標准中任意字符,它是電子郵件、網頁及其他存儲或傳送文字的應用 中,優先采用的編碼。互聯網工程工作小組(IETF)要求所有互聯網協議都必須支持UTF-8編碼。它使用一至四個字節為每個字符編碼。注意UTF-8編碼以后一個中文以三個字節的形式存儲

      編碼規則:

      128個US-ASCII字符,只需一個字節編碼

      拉丁文等字符,需要二個字節編碼

      大部分常用字(含中文),使用三個字節編碼

      其他極少使用的Unicode輔助字符,使用四字節編碼

字符串中的編碼解碼問題【應用】

  • 相關方法

    方法名 說明
    byte[] getBytes() 使用平台的默認字符集將該 String編碼為一系列字節
    byte[] getBytes(String charsetName) 使用指定的字符集將該 String編碼為一系列字節
    String(byte[] bytes) 使用平台的默認字符集解碼指定的字節數組來創建字符串
    String(byte[] bytes, String charsetName) 通過指定的字符集解碼指定的字節數組來創建字符串
  • 代碼演示

  public class StringDemo {
      public static void main(String[] args) throws UnsupportedEncodingException {
          //定義一個字符串
          String s = "中國";
  
          //byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
          //byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
          byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
          System.out.println(Arrays.toString(bys));
  
          //String ss = new String(bys);
          //String ss = new String(bys,"UTF-8");
          String ss = new String(bys,"GBK");
          System.out.println(ss);
      }
  }

image

字符流寫數據【應用】

  • 介紹

    Writer: 用於寫入字符流的抽象父類

    FileWriter: 用於寫入字符流的常用子類

  • 構造方法

    方法名 說明
    FileWriter(File file) 根據給定的 File 對象構造一個 FileWriter 對象
    FileWriter(File file, boolean append) 根據給定的 File 對象構造一個 FileWriter 對象
    FileWriter(String fileName) 根據給定的文件名構造一個 FileWriter 對象
    FileWriter(String fileName, boolean append) 根據給定的文件名以及指示是否附加寫入數據的 boolean 值來構造 FileWriter 對象
  • 成員方法

    方法名 說明
    void write(int c) 寫一個字符
    void write(char[] cbuf) 寫入一個字符數組
    void write(char[] cbuf, int off, int len) 寫入字符數組的一部分
    void write(String str) 寫一個字符串
    void write(String str, int off, int len) 寫一個字符串的一部分
  • 刷新和關閉的方法

    方法名 說明
    flush() 刷新流,之后還可以繼續寫數據
    close() 關閉流,釋放資源,但是在關閉之前會先刷新流。一旦關閉,就不能再寫數據
  • 注意事項

    • image
  • 代碼演示

  public class OutputStreamWriterDemo {
      public static void main(String[] args) throws IOException {
	  //創建字符輸出流對象
          FileWriter fw = new FileWriter("myCharStream\\a.txt");
  
          //void write(int c):寫一個字符
  //        fw.write(97);
  //        fw.write(98);
  //        fw.write(99);
  
          //void writ(char[] cbuf):寫入一個字符數組
          char[] chs = {'a', 'b', 'c', 'd', 'e'};
  //        fw.write(chs);
  
          //void write(char[] cbuf, int off, int len):寫入字符數組的一部分
  //        fw.write(chs, 0, chs.length);
  //        fw.write(chs, 1, 3);
  
          //void write(String str):寫一個字符串
  //        fw.write("abcde");
  
          //void write(String str, int off, int len):寫一個字符串的一部分
  //        fw.write("abcde", 0, "abcde".length());
          fw.write("abcde", 1, 3);
  
          //釋放資源
          fw.close();
      }
  }

字符流讀數據【應用】

  • 介紹

    Reader: 用於讀取字符流的抽象父類

    FileReader: 用於讀取字符流的常用子類

  • 構造方法

    方法名 說明
    FileReader(File file) 在給定從中讀取數據的 File 的情況下創建一個新 FileReader
    FileReader(String fileName) 在給定從中讀取數據的文件名的情況下創建一個新 FileReader
  • 成員方法

    方法名 說明
    int read() 一次讀一個字符數據
    int read(char[] cbuf) 一次讀一個字符數組數據
  • 代碼演示

  public class InputStreamReaderDemo {
      public static void main(String[] args) throws IOException {
     
          FileReader fr = new FileReader("myCharStream\\b.txt");
  
          //int read():一次讀一個字符數據
  //        int ch;
  //        while ((ch=fr.read())!=-1) {
  //            System.out.print((char)ch);
  //        }
  
          //int read(char[] cbuf):一次讀一個字符數組數據
          char[] chs = new char[1024];
          int len;
          while ((len = fr.read(chs)) != -1) {
              System.out.print(new String(chs, 0, len));
          }
  
          //釋放資源
          fr.close();
      }
  }

字符流用戶注冊案例【應用】

  • 案例需求

    將鍵盤錄入的用戶名和密碼保存到本地實現永久化存儲

  • 實現步驟

    • 獲取用戶輸入的用戶名和密碼
    • 將用戶輸入的用戶名和密碼寫入到本地文件中
    • 關流,釋放資源
  • 代碼實現

  public class CharStreamDemo8 {
      public static void main(String[] args) throws IOException {
          //需求: 將鍵盤錄入的用戶名和密碼保存到本地實現永久化存儲
          //要求:用戶名獨占一行,密碼獨占一行
  
          //分析:
          //1,實現鍵盤錄入,把用戶名和密碼錄入進來
          Scanner sc = new Scanner(System.in);
          System.out.println("請錄入用戶名");
          String username = sc.next();
          System.out.println("請錄入密碼");
          String password = sc.next();
  
          //2.分別把用戶名和密碼寫到本地文件。
          FileWriter fw = new FileWriter("charstream\\a.txt");
          //將用戶名和密碼寫到文件中
          fw.write(username);
          //表示寫出一個回車換行符 windows \r\n  MacOS \r  Linux \n
          fw.write("\r\n");
          fw.write(password);
          //刷新流
          fw.flush();
          //3.關流,釋放資源
          fw.close();
      }
  }

字符緩沖流【應用】

  • 字符緩沖流介紹

    • BufferedWriter:將文本寫入字符輸出流,緩沖字符,以提供單個字符,數組和字符串的高效寫入,可以指定緩沖區大小,或者可以接受默認大小。默認值足夠大,可用於大多數用途

    • BufferedReader:從字符輸入流讀取文本,緩沖字符,以提供字符,數組和行的高效讀取,可以指定緩沖區大小,或者可以使用默認大小。 默認值足夠大,可用於大多數用途

  • 構造方法

    方法名 說明
    BufferedWriter(Writer out) 創建字符緩沖輸出流對象
    BufferedReader(Reader in) 創建字符緩沖輸入流對象
  • 代碼演示

  public class BufferedStreamDemo01 {
      public static void main(String[] args) throws IOException {
          //BufferedWriter(Writer out)
          BufferedWriter bw = new BufferedWriter(new                                                            FileWriter("myCharStream\\bw.txt"));
          bw.write("hello\r\n");
          bw.write("world\r\n");
          bw.close();
  
          //BufferedReader(Reader in)
          BufferedReader br = new BufferedReader(new                                                           FileReader("myCharStream\\bw.txt"));
  
          //一次讀取一個字符數據
  //        int ch;
  //        while ((ch=br.read())!=-1) {
  //            System.out.print((char)ch);
  //        }
  
          //一次讀取一個字符數組數據
          char[] chs = new char[1024];
          int len;
          while ((len=br.read(chs))!=-1) {
              System.out.print(new String(chs,0,len));
          }
  
          br.close();
      }
  }

字符緩沖流特有功能【應用】

  • 方法介紹

    BufferedWriter:

    方法名 說明
    void newLine() 寫一行行分隔符,行分隔符字符串由系統屬性定義

    BufferedReader:

    方法名 說明
    String readLine() 讀一行文字。 結果包含行的內容的字符串,不包括任何行終止字符如果流的結尾已經到達,則為null
  • 代碼演示

  public class BufferedStreamDemo02 {
      public static void main(String[] args) throws IOException {
  
          //創建字符緩沖輸出流
          BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\bw.txt"));
  
          //寫數據
          for (int i = 0; i < 10; i++) {
              bw.write("hello" + i);
              //bw.write("\r\n");
              bw.newLine();
              bw.flush();
          }
  
          //釋放資源
          bw.close();
  
          //創建字符緩沖輸入流
          BufferedReader br = new BufferedReader(new FileReader("myCharStream\\bw.txt"));
  
          String line;
          while ((line=br.readLine())!=null) {
              System.out.println(line);
          }
  
          br.close();
      }
  }

字符緩沖流操作文件中數據排序案例【應用】

  • 案例需求

    使用字符緩沖流讀取文件中的數據,排序后再次寫到本地文件

  • 實現步驟

    • 將文件中的數據讀取到程序中
    • 對讀取到的數據進行處理
    • 將處理后的數據添加到集合中
    • 對集合中的數據進行排序
    • 將排序后的集合中的數據寫入到文件中
  • 代碼實現

  public class CharStreamDemo14 {
      public static void main(String[] args) throws IOException {
          //需求:讀取文件中的數據,排序后再次寫到本地文件
          //分析:
          //1.要把文件中的數據讀取進來。
          BufferedReader br = new BufferedReader(new FileReader("charstream\\sort.txt"));
          //輸出流一定不能寫在這里,因為會清空文件中的內容
          //BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
  
          String line = br.readLine();
          System.out.println("讀取到的數據為" + line);
          br.close();
  
          //2.按照空格進行切割
          String[] split = line.split(" ");//9 1 2 5 3 10 4 6 7 8
          //3.把字符串類型的數組變成int類型
          int [] arr = new int[split.length];
          //遍歷split數組,可以進行類型轉換。
          for (int i = 0; i < split.length; i++) {
              String smallStr = split[i];
              //類型轉換
              int number = Integer.parseInt(smallStr);
              //把轉換后的結果存入到arr中
              arr[i] = number;
          }
          //4.排序
          Arrays.sort(arr);
          System.out.println(Arrays.toString(arr));
  
          //5.把排序之后結果寫回到本地 1 2 3 4...
          BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
          //寫出
          for (int i = 0; i < arr.length; i++) {
              bw.write(arr[i] + " ");
              bw.flush();
          }
          //釋放資源
          bw.close();
  
      }
  }

IO流小結【理解】

  • IO流小結

image

轉換流

字符流中和編碼解碼問題相關的兩個類【理解】

image

  • InputStreamReader:是從字節流到字符流的橋梁,父類是Reader

    ​ 它讀取字節,並使用指定的編碼將其解碼為字符

    ​ 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平台的默認字符集

  • OutputStreamWriter:是從字符流到字節流的橋梁,父類是Writer

    ​ 是從字符流到字節流的橋梁,使用指定的編碼將寫入的字符編碼為字節

    ​ 它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平台的默認字符集

轉換流讀寫數據【應用】

  • 構造方法

    方法名 說明
    InputStreamReader(InputStream in) 使用默認字符編碼創建InputStreamReader對象
    InputStreamReader(InputStream in,String chatset) 使用指定的字符編碼創建InputStreamReader對象
    OutputStreamWriter(OutputStream out) 使用默認字符編碼創建OutputStreamWriter對象
    OutputStreamWriter(OutputStream out,String charset) 使用指定的字符編碼創建OutputStreamWriter對象
  • 代碼演示

  public class ConversionStreamDemo {
      public static void main(String[] args) throws IOException {
          //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
          OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"),"GBK");
          osw.write("中國");
          osw.close();
  
          //InputStreamReader isr = new InputStreamReader(new 	FileInputStream("myCharStream\\osw.txt"));
          InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"),"GBK");
          //一次讀取一個字符數據
          int ch;
          while ((ch=isr.read())!=-1) {
              System.out.print((char)ch);
          }
          isr.close();
      }
  }
  • 在jdk11以后可以不使用轉換流,直接使用FileReader或者FileWriter,傳入Charset
  • image
  • 代碼實例
 FileReader fr = new FileReader("D:\\file\\a.txt",Charset.forName("gbk"));

對象操作流

  • 對象操作流介紹
    • 如果把一個對象的數據以流的方式存儲在硬盤某個文件里,那么任何人都可以查看該文件中對象的相關信息,如果該對象包含賬號密碼等信息則會被暴露的體無完膚。而對象流是把對象以字節的形式寫到本地文件,直接打開文件,是讀不懂的,需要再次使用對象操作流讀到內存中去。保證了安全性。
    • image

對象序列化流【應用】

  • 對象序列化介紹

    • 對象序列化:就是將對象保存到磁盤中,或者在網絡中傳輸對象
    • 這種機制就是使用一個字節序列表示一個對象,該字節序列包含:對象的類型、對象的數據和對象中存儲的屬性等信息
    • 字節序列寫到文件之后,相當於文件中持久保存了一個對象的信息
    • 反之,該字節序列還可以從文件中讀取回來,重構對象,對它進行反序列化
  • 對象序列化流: ObjectOutputStream

    • 將Java對象的原始數據類型和圖形寫入OutputStream。 可以使用ObjectInputStream讀取(重構)對象。 可以通過使用流的文件來實現對象的持久存儲。 如果流是網絡套接字流,則可以在另一個主機上或另一個進程中重構對象
  • 構造方法

    方法名 說明
    ObjectOutputStream(OutputStream out) 創建一個寫入指定的OutputStream的ObjectOutputStream
  • 序列化對象的方法

    方法名 說明
    void writeObject(Object obj) 將指定的對象寫入ObjectOutputStream
  • 示例代碼

    學生類

  //如果想要這個類的對象能被序列化,那么這個類必須要實現一個接口.Serializable

  //Serializable 接口的意義
  //稱之為是一個標記性接口,里面沒有任何的抽象方法
  //只要一個類實現了這個Serializable接口,那么就表示這個類的對象可以被序列化.
  public class Student implements Serializable {
      private String name;
      private int age;

      public Student() {
      }

      public Student(String name, int age) {
          this.name = name;
          this.age = age;
      }

      public String getName() {
          return name;
      }

      public void setName(String name) {
          this.name = name;
      }

      public int getAge() {
          return age;
      }

      public void setAge(int age) {
          this.age = age;
      }

      @Override
      public String toString() {
          return "Student{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  '}';
      }
  }

測試類

  public class ObjectOutputStreamDemo {
      public static void main(String[] args) throws IOException {
          //ObjectOutputStream(OutputStream out):創建一個寫入指定的OutputStream的ObjectOutputStream
          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));

          //創建對象
          Student s = new Student("佟麗婭",30);

          //void writeObject(Object obj):將指定的對象寫入ObjectOutputStream
          oos.writeObject(s);

          //釋放資源
          oos.close();
      }
  }
  • 注意事項

    • 一個對象要想被序列化,該對象所屬的類必須必須實現Serializable 接口
    • Serializable是一個標記接口,實現該接口,不需要重寫任何方法

對象反序列化流【應用】

  • 對象反序列化流: ObjectInputStream

    • ObjectInputStream反序列化先前使用ObjectOutputStream編寫的原始數據和對象
  • 構造方法

    方法名 說明
    ObjectInputStream(InputStream in) 創建從指定的InputStream讀取的ObjectInputStream
  • 反序列化對象的方法

    方法名 說明
    Object readObject() 從ObjectInputStream讀取一個對象
  • 示例代碼

  public class ObjectInputStreamDemo {
      public static void main(String[] args) throws IOException, ClassNotFoundException {
          //ObjectInputStream(InputStream in):創建從指定的InputStream讀取的ObjectInputStream
          ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
  
          //Object readObject():從ObjectInputStream讀取一個對象
          Object obj = ois.readObject();
  
          Student s = (Student) obj;
          System.out.println(s.getName() + "," + s.getAge());
  
          ois.close();
      }
  }

serialVersionUID&transient【應用】

  • serialVersionUID

    • 用對象序列化流序列化了一個對象后,假如我們修改了對象所屬的類文件,讀取數據會不會出問題呢?
      • 會出問題,會拋出InvalidClassException異常
    • 如果出問題了,如何解決呢?
      • 重新序列化
      • 給對象所屬的類加一個serialVersionUID
        • private static final long serialVersionUID = 42L;
  • transient

    • 如果一個對象中的某個成員變量的值不想被序列化,又該如何實現呢?
      • 給該成員變量加transient關鍵字修飾,該關鍵字標記的成員變量不參與序列化過程
  • 示例代碼

    學生類

  public class Student implements Serializable {
  
    //serialVersionUID 序列號
    //如果我們自己沒有定義,那么虛擬機會根據類中的信息會自動的計算出一個序列號.
    //問題:如果我們修改了類中的信息.那么虛擬機會再次計算出一個序列號.

    //第一步:把User對象序列化到本地. --- -5824992206458892149
    //第二步:修改了javabean類. 導致 --- 類中的序列號 4900133124572371851
    //第三步:把文件中的對象讀到內存. 本地中的序列號和類中的序列號不一致了.

    //解決?
    //不讓虛擬機幫我們自動計算,我們自己手動給出.而且這個值不要變.
	
      private static final long serialVersionUID = 42L;
      private String name;
  //    private int age;
      private transient int age;
  
      public Student() {
      }
  
      public Student(String name, int age) {
          this.name = name;
          this.age = age;
      }
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public int getAge() {
          return age;
      }
  
      public void setAge(int age) {
          this.age = age;
      }
  
  //    @Override
  //    public String toString() {
  //        return "Student{" +
  //                "name='" + name + '\'' +
  //                ", age=" + age +
  //                '}';
  //    }
  }

測試類

  public class ObjectStreamDemo {
      public static void main(String[] args) throws IOException, ClassNotFoundException {
  //        write();
          read();
      }
  
      //反序列化
      private static void read() throws IOException, ClassNotFoundException {
          ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
          Object obj = ois.readObject();
          Student s = (Student) obj;
          System.out.println(s.getName() + "," + s.getAge());
          ois.close();
      }
  
      //序列化
      private static void write() throws IOException {
          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
          Student s = new Student("佟麗婭", 30);
          oos.writeObject(s);
          oos.close();
      }
  }

對象操作流練習【應用】

  • 案例需求

    創建多個學生類對象寫到文件中,再次讀取到內存中

  • 實現步驟

    • 創建序列化流對象
    • 創建多個學生對象
    • 將學生對象添加到集合中
    • 將集合對象序列化到文件中
    • 創建反序列化流對象
    • 將文件中的對象數據,讀取到內存中
  • 代碼實現

    學生類

  public class Student implements Serializable{
      
      private static final long serialVersionUID = 2L;
  
      private String name;
      private int age;
  
      public Student() {
      }
  
      public Student(String name, int age) {
          this.name = name;
          this.age = age;
      }
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public int getAge() {
          return age;
      }
  
      public void setAge(int age) {
          this.age = age;
      }
  }

測試類

  • 第一種方法(死循環)
  public static void main(String[] args) throws Exception {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("g.txt"));
        Person p1 = new Person("zhangsan",12);
        Person p2 = new Person("lisi",14);
        Person p3 = new Person("wangwu",13);
        oos.writeObject(p1);
        oos.writeObject(p2);
        oos.writeObject(p3);
        oos.close();
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("g.txt"));
        while (true){
            try {
                Person p = (Person)ois.readObject();
                System.out.println(p);
            } catch (Exception e) {
                break;
            }
        }
    }
  • 第二種方法(對象放入集合)
  public class Demo03 {
      /**
       *  read():
       *      讀取到文件末尾返回值是 -1
       *  readLine():
       *      讀取到文件的末尾返回值 null
       *  readObject():
       *      讀取到文件的末尾 直接拋出異常
       *  如果要序列化的對象有多個,不建議直接將多個對象序列化到文件中,因為反序列化時容易出異常
       *      建議: 將要序列化的多個對象存儲到集合中,然后將集合序列化到文件中
       */
      public static void main(String[] args) throws Exception {
          /*// 序列化
          //1.創建序列化流對象
          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myCode\\oos.txt"));
          ArrayList<Student> arrayList = new ArrayList<>();
          //2.創建多個學生對象
          Student s = new Student("佟麗婭",30);
          Student s01 = new Student("佟麗婭",30);
          //3.將學生對象添加到集合中
          arrayList.add(s);
          arrayList.add(s01);
          //4.將集合對象序列化到文件中
          oos.writeObject(arrayList);
          oos.close();*/
  
          // 反序列化
        	//5.創建反序列化流對象
          ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myCode\\oos.txt"));
        	//6.將文件中的對象數據,讀取到內存中
          Object obj = ois.readObject();
          ArrayList<Student> arrayList = (ArrayList<Student>)obj;
          ois.close();
          for (Student s : arrayList) {
              System.out.println(s.getName() + "," + s.getAge());
          }
      }
  }

Properties集合

Properties作為Map集合的使用【應用】

  • Properties介紹

    • 是一個Map體系的集合類
    • Properties可以保存到流中或從流中加載
    • 屬性列表中的每個鍵及其對應的值都是一個字符串
  • Properties基本使用

  public class PropertiesDemo01 {
      public static void main(String[] args) {
          //創建集合對象
  //        Properties<String,String> prop = new Properties<String,String>(); //錯誤
          Properties prop = new Properties();
  
          //增
          prop.put("itcast001", "佟麗婭");
          prop.put("itcast002", "趙麗穎");
          prop.put("itcast003", "劉詩詩");
          
		  //刪
		  prop.remove("itcast001");
		  
		  //改:  put 如果鍵不存在就添加,如果存在就覆蓋原值
		  prop.put("itcast002","趙本山");
		  
		  //查
		  Object value = prop.get("itcast003");
		  
          //遍歷集合
          Set<Object> keySet = prop.keySet();
          for (Object key : keySet) {
              Object value = prop.get(key);
              System.out.println(key + "," + value);
          }
      }
  }

Properties作為Map集合的特有方法【應用】

  • 特有方法

    方法名 說明
    Object setProperty(String key, String value) 設置集合的鍵和值,都是String類型,底層調用 Hashtable方法 put
    String getProperty(String key) 使用此屬性列表中指定的鍵搜索屬性
    Set stringPropertyNames() 從該屬性列表中返回一個不可修改的鍵集,其中鍵及其對應的值是字符串
  • 示例代碼

  public class PropertiesDemo02 {
      public static void main(String[] args) {
          //創建集合對象
          Properties prop = new Properties();
  
          //Object setProperty(String key, String value):設置集合的鍵和值,都是String類型
          prop.setProperty("itcast001", "佟麗婭");
          prop.setProperty("itcast002", "趙麗穎");
          prop.setProperty("itcast003", "劉詩詩");
  
          //String getProperty(String key):使用此屬性列表中指定的鍵搜索屬性
  //        System.out.println(prop.getProperty("itcast001"));
  //        System.out.println(prop.getProperty("itcast0011"));
  
  //        System.out.println(prop);
  
          //Set<String> stringPropertyNames():從該屬性列表中返回一個不可修改的鍵集,其中鍵及其對應的值是字符串
          Set<String> names = prop.stringPropertyNames();
          for (String key : names) {
  //            System.out.println(key);
              String value = prop.getProperty(key);
              System.out.println(key + "," + value);
          }
      }
  }

Properties和IO流相結合的方法【應用】

  • 和IO流結合的方法

    方法名 說明
    void load(Reader reader) 從輸入字符流讀取屬性列表(鍵和元素對)
    void store(Writer writer, String comments) 將此屬性列表(鍵和元素對)寫入此 Properties表中,以適合使用 load(Reader)方法的格式寫入輸出字符流,comments就是注釋信息
  • 示例代碼

  public class PropertiesDemo03 {
      public static void main(String[] args) throws IOException {
          //把集合中的數據保存到文件
  //        myStore();
  
          //把文件中的數據加載到集合
          myLoad();
  
      }
  
      private static void myLoad() throws IOException {
          Properties prop = new Properties();
  
          //void load(Reader reader):
          FileReader fr = new FileReader("myOtherStream\\fw.txt");
          prop.load(fr);
          fr.close();
  
          System.out.println(prop);
      }
  
      private static void myStore() throws IOException {
          Properties prop = new Properties();
  
          prop.setProperty("itcast001","佟麗婭");
          prop.setProperty("itcast002","趙麗穎");
          prop.setProperty("itcast003","劉詩詩");
  
          //void store(Writer writer, String comments):
          FileWriter fw = new FileWriter("myOtherStream\\fw.txt");
          prop.store(fw,null);
          fw.close();
      }
  }

Properties集合練習【應用】

  • 案例需求

    在Properties文件中手動寫上姓名和年齡,讀取到集合中,將該數據封裝成學生對象,寫到本地文件

  • 實現步驟

    • 創建Properties集合,將本地文件中的數據加載到集合中
    • 獲取集合中的鍵值對數據,封裝到學生對象中
    • 創建序列化流對象,將學生對象序列化到本地文件中
  • 代碼實現

    學生類

  public class Student implements Serializable {
      private static final long serialVersionUID = 1L;
  
      private String name;
      private int age;
  
      public Student() {
      }
  
      public Student(String name, int age) {
          this.name = name;
          this.age = age;
      }
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  
      public int getAge() {
          return age;
      }
  
      public void setAge(int age) {
          this.age = age;
      }
  
      @Override
      public String toString() {
          return "Student{" +
                  "name='" + name + '\'' +
                  ", age=" + age +
                  '}';
      }
  }

測試類

  public class Test {
  
      public static void main(String[] args) throws IOException {
        	//1.創建Properties集合,將本地文件中的數據加載到集合中
          Properties prop = new Properties();
          FileReader fr = new FileReader("prop.properties");
          prop.load(fr);
          fr.close();
  		//2.獲取集合中的鍵值對數據,封裝到學生對象中
          String name = prop.getProperty("name");
          int age = Integer.parseInt(prop.getProperty("age"));
          Student s = new Student(name,age);
  		//3.創建序列化流對象,將學生對象序列化到本地文件中
          ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
          oos.writeObject(s);
          oos.close();
      }
  }

打印流

PrintStream 字節打印流

  • 構造方法

    方法名 說明
    PrintStream(File file) 創建具有指定文件且不帶自動行刷新的新打印流。
    PrintStream(File file, String csn) 創建具有指定文件名稱和字符集且不帶自動行刷新的新打印流。
    PrintStream(OutputStream out) 創建新的打印流。
    PrintStream(OutputStream out, boolean autoFlush) 創建新的打印流。
    PrintStream(OutputStream out, boolean autoFlush, String encoding) 創建新的打印流。
    PrintStream(String fileName) 創建具有指定文件名稱且不帶自動行刷新的新打印流。
    PrintStream(String fileName, String csn) 創建具有指定文件名稱和字符集且不帶自動行刷新的新打印流。
  • 常用方法

    方法名 說明
    append 將指定字符添加到此輸出流。可鏈式追加
    print 打印,默認打印到控制台,可改變打印方向

PrintWriter 字符打印流

  • 用法與PrintStream完全類似,不再贅述,可自己查閱文檔。

案例:日志工具類

在日志工具類中利用打印流實現每次調用,都把調用的時間以及信息打印到日志文件log.txt中。
如果log.txt不存在的話,先創建log.txt文件

Logger工具類

public class Logger {
    public static void log(String msg) throws IOException {
        File file = new File("log.txt");
        if(!file.exists()){
            file.createNewFile();
        }
        PrintStream printStream = new PrintStream(new FileOutputStream(file,true));
        Date date = new Date();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-dd-MM HH:mm:ss");
        String s = simpleDateFormat.format(date);  //日期格式化
        System.setOut(printStream);      //輸出重定向
        System.out.println(s+": "+msg);
    }
}

測試類

public static void main(String[] args) throws Exception {
        Logger.log("調用了System類的gc()方法,建議啟動垃圾回收");
        Logger.log("調用了UserService的doSome()方法");
        Logger.log("用戶嘗試進行登錄,驗證失敗");
        Logger.log("我非常喜歡這個記錄日志的工具哦!");
}


免責聲明!

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



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