1. 程式人生 > 實用技巧 >Unity 2D Light (4) - 平行光

Unity 2D Light (4) - 平行光

IO流

一、什麼是IO

  • IO又分為流IO(java.io)

  • 塊IO(java.nio)

二、流

java中將輸入輸出抽象稱為流,就好像水管,將兩個容器連線起來。

將資料從外存中讀取到記憶體中的稱為輸入流

將資料從記憶體寫入外存中的稱為輸出流

1. 分類

  • 輸入流、輸出流——流向

    • 源 ----> 目標
      • 程式 <--- 檔案 —— 輸入流
      • 程式 ---> 檔案 —— 輸出流
  • 位元組流、字元流——傳輸資料單位

    • 這四大基流都是抽象類(無法例項化),其他流都是繼承於這四大基流的。
    • 位元組流——資料流中最小的資料單元是位元組
    • 字元流:資料流中最小的資料單元是字元
      • Java中的字元是Unicode編碼,一個字元佔用兩個位元組(無論中文還是英文都是兩個位元組)。
  • 節點流和包裝流——功能

    • 節點流:可以從或向一個特定的地方(節點)讀寫資料,直接連線資料來源。
      • 如最常見的是檔案的FileReader,還可以是陣列、管道、字串,關鍵字分別為ByteArray/CharArray,Piped,String。.
    • 處理流(包裝流):並不直接連線資料來源,是對一個已存在的流的連線和封裝,是一種典型的裝飾器設計模式,使用處理流主要是為了更方便的執行輸入輸出工作
      • 如PrintStream,輸出功能很強大,又如BufferedReader提供快取機制,推薦輸出時都使用處理流包裝。
  • 一些特別的的流型別

    • 轉換流:轉換流只有位元組流轉換為字元流,因為字元流使用起來更方便,我們只會向更方便使用的方向轉化。如:InputStreamReader與OutputStreamWriter。
    • 緩衝流:有關鍵字Buffered,也是一種處理流,為其包裝的流增加了快取功能,提高了輸入輸出的效率,增加緩衝功能後需要使用flush()才能將緩衝區中內容寫入到實際的物理節點。但是,在現在版本的Java中,只需記得關閉輸出流(呼叫close()方法),就會自動執行輸出流的flush()方法,可以保證將緩衝區中內容寫入。
    • 物件流:有關鍵字Object,主要用於將目標物件儲存到磁碟中或允許在網路中直接傳輸物件時使用(物件序列化)

2. IO操作模板

  ①、建立源或目標物件

    輸入:把檔案中的資料流向到程式中,此時檔案是源,程式是目標

    輸出:把程式中的資料流向到檔案中,此時檔案是目標,程式是源

  ②、建立 IO 流物件

    輸入:建立輸入流物件

    輸出:建立輸出流物件

  ③、具體的 IO 操作

  ④、關閉資源

    輸入:輸入流的 close() 方法

    輸出:輸出流的 close() 方法

程式中開啟的檔案 IO 資源不屬於記憶體裡的資源,垃圾回收機制無法回收該資源。如果不關閉該資源,那麼磁碟的檔案將一直被程式引用著,不能刪除也不能更改。所以應該手動呼叫 close() 方法關閉流資源

3. File類

檔案和目錄路徑名的抽象表示。

  • File欄位

    • 為了遮蔽各個平臺之間的分隔符差異

      File.separator//系統分隔符
      
      • UNIX平臺,絕對路徑名的字首始終為"/"
      • 而Windows平臺,相對路徑"/",絕對路徑則用"\\"
      File file1 = new File("src/hello1.txt");
      File file2 = new File("src"+File.separator+"hello1.txt");
      
  • 構造方法

    //從父抽象路徑名和子路徑名字串建立新的 File例項。 
    File file = new File("scr");
    File file1 = new File(file,"hello1.txt");
    System.out.println(file1);//scr\hello1.txt
    //通過將給定的路徑名字串轉換為抽象路徑名來建立新的 File例項。 
    File file2 = new File("src"+File.separator+"hello1.txt");
    System.out.println(file2);//scr\hello1.txt
    //從父路徑名字串和子路徑名字串建立新的 File例項。
    File file3 = new File("scr","hello1.txt");
    System.out.println(file3);//scr\hello1.txt
    
  • 常用方法

    1. 建立方法

      • boolean createNewFile() 不存在返回true 存在返回false

      • boolean mkdir() 建立目錄,如果上一級目錄不存在,則會建立失敗

      • boolean mkdirs() 建立多級目錄,如果上一級目錄不存在也會自動建立

    2. 刪除方法

      • boolean delete() 刪除檔案或目錄,如果表示目錄,則目錄下必須為空才能刪除
      • boolean deleteOnExit() 檔案使用完成後刪除
    3. 判斷方法

      • boolean canExecute()判斷檔案是否可執行
      • boolean canRead()判斷檔案是否可讀
      • boolean canWrite() 判斷檔案是否可寫
      • boolean exists() 判斷檔案或目錄是否存在
      • boolean isDirectory() 判斷此路徑是否為一個目錄
      • boolean isFile()  判斷是否為一個檔案
      • boolean isHidden()  判斷是否為隱藏檔案
      • boolean isAbsolute()判斷是否是絕對路徑 檔案不存在也能判斷
    4. 獲取方法

      • String getName() 獲取此路徑表示的檔案或目錄名稱

      • String getPath() 將此路徑名轉換為路徑名字串

      • String getAbsolutePath() 返回此抽象路徑名的絕對形式

      • String getParent()//如果沒有父目錄返回null

      • long lastModified()//獲取最後一次修改的時間

      • long length() 返回由此抽象路徑名錶示的檔案的長度。

      • boolean renameTo(File f) 重新命名由此抽象路徑名錶示的檔案。

      • File[] liseRoots()//獲取機器碟符

      • String[] list() 返回一個字串陣列,命名由此抽象路徑名錶示的目錄中的檔案和目錄。

      • String[] list(FilenameFilter filter) 返回一個字串陣列,命名由此抽象路徑名錶示的目錄中滿足指定過濾器的檔案和目錄。

        public class TestFile2 {
            public static void main(String[] args) throws IOException {
                File dir = new File("Hello");
                File file = new File(dir,"hello1");
        
                if(!dir.exists()||dir.isDirectory()) {
                    dir.mkdirs();
                    file.createNewFile();
                }
        
                System.out.println(file.getName());
                System.out.println(file.getPath());
                System.out.println(file.getParent());
            }
        }	
        

4. 位元組流

  • InputStream

    用 位元組輸出流 InputStream 的典型實現 FileInputStream

    public class FileDemo extends File{
        public FileDemo() {
            super("src/hello.txt");//helloworld
        }
    }
    
    public class FileInputStreamDemo {
        public FileInputStreamDemo() throws IOException {
            FileDemo file = new FileDemo();
            InputStream in = new FileInputStream(file);
    
            //逐字讀取
            byte[] buffer = new byte[(int)file.length()];
            int count = 0;
            byte temp;
    
            while ((temp = (byte) in.read())!= (-1)) {
                buffer[count++] = temp;
            }
    
            in.close();
            System.out.println(new String(buffer));
        }
    
        public static void main(String[] args) throws IOException {
            new FileInputStreamDemo();
        }
    }
    //helloword
    
  • OutputStream

    OutputStream 的典型實現 FileOutputStream

    public class FileOutputStreamDemo {
        public FileOutputStreamDemo() throws IOException {
            FileDemo file = new FileDemo();
    
            FileOutputStream out = new FileOutputStream(file, true);
            
            //寫入
            out.write("\n".getBytes());
            out.write(65);
            out.write("\n".getBytes());
            out.write("this is a demo\n".getBytes());
            out.write("abcdefghijk\n".getBytes(),1,4);
    
            out.close();
        }
    
        public static void main(String[] args) throws IOException {
            new FileOutputStreamDemo();
            new FileInputStreamDemo();
        }
    }
    /*
    helloworld
    A
    this is a demo
    bcde
    */
    
  • 用位元組流完成檔案的複製

    hello1.txt的內容變成hello.txt中的helloworld

    public class CopeText {
    
        public static void main(String[] args) throws IOException {
            //1.建立源和目標
            File source = new File("src/hello.txt");//helloworld
            File target = new File("src/hello1.txt");//this is hello1
    
            //2.建立輸入輸出物件
            InputStream in = new FileInputStream(source);
            OutputStream out = new FileOutputStream(target);
    
            //3.讀取和寫入操作
            byte[] buffer = new byte[100];
            int count = 0;
            int temp;
            while ((temp = in.read()) != (-1)) {
                buffer[count++] = (byte) temp;
            }
    
            out.write(buffer);
    
            //4.關閉流資源
            in.close();
            out.close();
        }
    }
    

5. 字元流

  • 為什麼要使用字元流?

    ​ 因為使用位元組流操作漢字或特殊符號語言的時候容易亂碼,因為漢字不止一個位元組,為了解決這個問題,建議使用字元流。

  • 什麼情況下使用字元流?

    一般可以用記事本開啟的檔案,我們可以看到內容不亂碼的。就是文字檔案,可以使用字元流。

    ​ 而操作二進位制檔案(比如圖片、音訊、視訊)必須使用位元組流

    • FileWriter

      用字元輸出流 Writer 的典型實現 FileWriter 來介紹這個類的用法

      public class FileWriterDemo {
      
          public static void main(String[] args) throws IOException {
              //1.建立源
              File file = new File("src", "hello.txt");//helloworld
      
              //2.建立輸出物件
              Writer out = new FileWriter(file);
      
              //3.寫入操作
              out.write("春暖花開\n");
              out.write(65);
              out.write("\n".toCharArray());
              out.write("零一二三四五六七".toCharArray(),3,4);
      
              //4.關閉流資源
              out.close();
          }
      
      }
      

      開啟hello.txt檢視內容可以看見,不會顯示亂碼

      春暖花開
      A
      三四五六

    • Reader

      用字元輸入流 Reader 的典型實現 FileReader 來介紹這個類的用法:

      public class FileReaderDemo {
          public static void main(String[] args) throws IOException {
              //1.建立源
              File file = new File("src/hello.txt");
      
              //2.建立流物件
              Reader in = new FileReader(file);
      
              //3.讀取操作
              int len;
      
                  //讀取全部
              while ((len = in.read()) != (-1)) {
                  System.out.print((char)len);
              }
      
                  
              char[] buffer = new char[12]; //將字元讀進字元陣列,每次讀12個字元
              while ((len = in.read(buffer)) != (-1)) {
                  System.out.println(new String(buffer,0,len));
              }
              
                  //每次讀12個字元
              while ((len = in.read(buffer,0,12)) != (-1)) {
                  System.out.println(new String(buffer,0,len));
              }
              //4.關閉流資源
              in.close();
          }
      }
      
    • 用字元流完成檔案的複製

      public class CopeCharText {
          public static void main(String[] args) throws IOException {
              //1.建立源和目標
              File source = new File("src/hello.txt");
              File target = new File("src/hello1.txt");
      
              //2.建立流物件
              Reader in = new FileReader(source);
              Writer out = new FileWriter(target);
      
              //3.讀取和寫入操作
              int len;//表示已經讀取了多少個位元組,如果是 -1,表示已經讀取到檔案的末尾
              char[] buffer = new char[12];//建立一個容量為 12 的字元陣列,儲存已經讀取的資料
              while ((len = in.read(buffer))!=(-1)){
                  out.write(buffer,0,len);
              }
      
              //4.關閉流資源
              in.close();
              out.close();
          }
      }
      

6. 包裝流

包裝流隱藏了底層節點流的差異,並對外提供了更方便的輸入\輸出功能,讓我們只關心這個高階流的操作

使用包裝流包裝了節點流,程式直接操作包裝流,而底層還是節點流和IO裝置操作

關閉包裝流的時候,只需要關閉包裝流即可

  • 緩衝流

    是一個包裝流,目的是快取作用,加快讀取和寫入資料的速度。

    • 位元組緩衝流:BufferedInputStream、BufferedOutputStream

    • 字元緩衝流:BufferedReader、BufferedWriter

    • 沒有緩衝流要實現檔案拷貝 位元組流 字元流

    • 位元組緩衝流 BufferedInputStream、BufferedOutputStream
      public class BufferedInputStreamDemo {
          public static void main(String[] args) throws IOException {
              //1.建立源
              File source = new File("src/hello.txt");
              File target = new File("src/hello1.txt");
      
              //2.建立流物件、包裝
              InputStream ins = new BufferedInputStream(new FileInputStream(source));
              OutputStream outs = new BufferedOutputStream(new FileOutputStream(target));
      
              //3.讀取操作
              byte[] buffer = new byte[1024];
              int len;
              while ((len = ins.read(buffer,0,1024)) != (-1)) {
                  outs.write(buffer,0,len);
              }
      
              //4.關閉io
              in.close();
              out.close();
          }
      }
      
    • 字元緩衝流 BufferedReader、BufferedWriter
      public class BufferedReaderDemo {
          public static void main(String[] args) throws IOException {
              //1. 建立源和目標
              File source = new File("src/hello.txt");
              File target = new File("src/hello1.txt");
      
              //2. 建立流物件
              Reader in = new FileReader(source);
              Writer out = new FileWriter(target);
                  //包裝
              Reader rd = new BufferedReader(in);
              Writer wt = new BufferedWriter(out);
      
              //3. 讀取和寫入操作
              char[] buffer = new char[1024];
              int len = -1;
              while ((len = rd.read(buffer,0,1024)) != (-1)) {
                  wt.write(buffer);
              }
      
              //4. 關閉流資源
              rd.close();
              wt.close();
      
          }
      }
      
  • 轉換流

    InputStreamReader:把位元組輸入流轉換為字元輸入流
    OutputStreamWriter:把位元組輸出流轉換為字元輸出流

    public class InputStreamReaderDemo {
        public static void main(String[] args) throws IOException {
            //1. 建立源和目標
            File source = new File("src/hello.txt");
            File target = new File("src/helllo1.txt");
    
            //2. 建立流物件
            InputStream in = new FileInputStream(source);
            OutputStream out = new FileOutputStream(target);
                //包裝
            Reader rd = new InputStreamReader(in);
            Writer wt = new OutputStreamWriter(out);
    
            //3. 讀取和寫入操作
            char[] buffer = new char[1024];
            int count = 0;
            int len;
            while ((len = rd.read(buffer)) != (-1)) {
                wt.write(buffer,0,1024);
            }
    
            //4. 關閉流資源
            rd.close();
            wt.close();
        }
    }
    
  • 記憶體流

    把資料先臨時存在陣列中,也就是記憶體中。所以關閉記憶體流是無效的,關閉後還是可以呼叫這個類的方法。底層原始碼的 close()是一個空方法,因此不關閉也沒事

    • 位元組記憶體流:ByteArrayOutputStream 、ByteArrayInputStream
      public class ByteArrayOutputStreamDemo {
          public static void main(String[] args) throws IOException {
              //ByteArrayOutputStream
              ByteArrayOutputStream baos = new ByteArrayOutputStream();
              baos.write("hello".getBytes());
                  //臨時儲存
              byte[] temp = baos.toByteArray();
              System.out.println(new String(temp));
      
              //ByteArrayInputStream
              ByteArrayInputStream bais = new ByteArrayInputStream(temp);
              
              byte[] buffer = new byte[10];
              int len = -1;
              while ((len = bais.read(buffer)) != (-1)) {
                  System.out.println(new String(buffer));
              }
              
          }
      }
      
    • 字元記憶體流:CharArrayReader、CharArrayWriter
      public class CharArrayReaderDemo {
          public static void main(String[] args) throws IOException {
              //CharArrayWriter
              CharArrayWriter caw = new CharArrayWriter();
              caw.write("hello");
              caw.write("哈哈哈");
                  //  返回記憶體資料的副本
              char[] temp = caw.toCharArray();
              System.out.println(new String(temp));
      
              //CharArrayReader
              CharArrayReader car = new CharArrayReader(temp);
              char[] buffer = new char[10];
              int len = -1;
              while ((len = car.read(buffer)) != (-1)) {
                  System.out.println(new String(buffer));
              }
          }
      }
      
    • 字串流:StringReader,StringWriter

      把資料臨時儲存到字串中

      public class StringReaderDemo {
          public static void main(String[] args) throws IOException {
              //StringWriter
              StringWriter sw = new StringWriter();
              sw.write("ABCD");
              sw.write("春暖花開");
              System.out.println(sw.toString());//底層採用StringBuffer拼接
      
              //StringReader
              StringReader sr = new StringReader(sw.toString());
              char[] buffer = new char[10];
              int len = -1;
              while ((len = sr.read(buffer)) != (-1)) {
                  System.out.println(new String(buffer,0,len));
              }
      
          }
      }
      
  • 合併流/順序流

    讀取的時候是先讀第一個,讀完了在讀下面一個流。

    public class SequenceInputStreamDemo {
        public static void main(String[] args) throws IOException {
    
            //建立流物件
            InputStream in1 = new FileInputStream("src/hello.txt");//helloworld
            InputStream in2 = new FileInputStream("src/test.txt");//test
                //包裝
            SequenceInputStream seqin = new SequenceInputStream(in1, in2);
    
            //讀取操作
            byte[] buffer = new byte[10];
            int len = -1;
            while ((len = seqin.read(buffer))!=(-1)) {
                System.out.println(new String(buffer,0,len));
            }
    
            //關閉流資源
            seqin.close();
    
        }
    }
    /*
    helloworld
    test
    */
    

7. 總結

  • 位元組流
    • buffer為byte陣列
  • 字元流
    • buffer為char陣列