1. 程式人生 > >Java 的I/O中的類和方法講解

Java 的I/O中的類和方法講解

Java 的FILE類,以及常用方法

  • 檔案和資料夾都是用File代表
  1. 建立一個檔案物件

使用絕對路徑或者相對路徑建立File物件 

// 絕對路徑
        File f1 = new File("d:/LOLFolder");
        System.out.println("f1的絕對路徑:" + f1.getAbsolutePath());
        // 相對路徑,相對於工作目錄,如果在eclipse中,就是專案目錄
        File f2 = new File("LOL.exe");
        System.out.println("f2的絕對路徑:" + f2.getAbsolutePath());
  
        // 把f1作為父目錄建立檔案物件
        File f3 = new File(f1, "LOL.exe");
  
        System.out.println("f3的絕對路徑:" + f3.getAbsolutePath());

2.檔案常用方法

 File f = new File("d:/LOLFolder/LOL.exe");
        System.out.println("當前檔案是:" +f);
        //檔案是否存在
        System.out.println("判斷是否存在:"+f.exists());
         
        //是否是資料夾
        System.out.println("判斷是否是資料夾:"+f.isDirectory());
          
        //是否是檔案(非資料夾)
        System.out.println("判斷是否是檔案:"+f.isFile());
          
        //檔案長度
        System.out.println("獲取檔案的長度:"+f.length());
          
        //檔案最後修改時間
        long time = f.lastModified();
        Date d = new Date(time);
        System.out.println("獲取檔案的最後修改時間:"+d);
        //設定檔案修改時間為1970.1.1 08:00:00
        f.setLastModified(0);
          
        //檔案重新命名
        File f2 =new File("d:/LOLFolder/DOTA.exe");
        f.renameTo(f2);
        System.out.println("把LOL.exe改名成了DOTA.exe");
         
        System.out.println("注意: 需要在D:\\LOLFolder確實存在一個LOL.exe,\r\n才可以看到對應的檔案長度、修改時間等資訊");
// 以字串陣列的形式,返回當前資料夾下的所有檔案(不包含子檔案及子資料夾)
        f.list();
  
        // 以檔案陣列的形式,返回當前資料夾下的所有檔案(不包含子檔案及子資料夾)
        File[]fs= f.listFiles();
  
        // 以字串形式返回獲取所在資料夾
        f.getParent();
  
        // 以檔案形式返回獲取所在資料夾
        f.getParentFile();
        // 建立資料夾,如果父資料夾skin不存在,建立就無效
        f.mkdir();
  
        // 建立資料夾,如果父資料夾skin不存在,就會建立父資料夾
        f.mkdirs();
  
        // 建立一個空檔案,如果父資料夾skin不存在,就會丟擲異常
        f.createNewFile();
        // 所以建立一個空檔案之前,通常都會建立父目錄
        f.getParentFile().mkdirs();
  
        // 列出所有的碟符c: d: e: 等等
        f.listRoots();
  
        // 刪除檔案
        f.delete();
  
        // JVM結束的時候,刪除檔案,常用於臨時檔案的刪除
        f.deleteOnExit();

位元組流

  • InputStream位元組輸入流 
  • OutputStream位元組輸出流 
  • 用於以位元組的形式讀取和寫入資料

所有的資料存放在計算機中都是以數字的形式存放的。 所以字母就需要轉換為數字才能夠存放。 比如A就對應的數字65,a對應的數字97. 不同的字母和符號對應不同的數字,就是一張碼錶。

以位元組流的形式讀取檔案內容

  • InputStream是位元組輸入流,同時也是抽象類,只提供方法宣告,不提供方法的具體實現。
  • FileInputStream 是InputStream子類,以FileInputStream 為例進行檔案讀取
try {
            //準備檔案lol.txt其中的內容是AB,對應的ASCII分別是65 66
            File f =new File("d:/lol.txt");
            //建立基於檔案的輸入流
            FileInputStream fis =new FileInputStream(f);
            //建立位元組陣列,其長度就是檔案的長度
            byte[] all =new byte[(int) f.length()];
            //以位元組流的形式讀取檔案所有內容
            fis.read(all);
            for (byte b : all) {
                //打印出來是65 66
                System.out.println(b);
            }
             
            //每次使用完流,都應該進行關閉
            fis.close();
              
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

以位元組流的形式向檔案寫入資料

try {
            // 準備檔案lol2.txt其中的內容是空的
            File f = new File("d:/lol2.txt");
            // 準備長度是2的位元組陣列,用88,89初始化,其對應的字元分別是X,Y
            byte data[] = { 88, 89 };
 
            // 建立基於檔案的輸出流
            FileOutputStream fos = new FileOutputStream(f);
            // 把資料寫入到輸出流
            fos.write(data);
            // 關閉輸出流
            fos.close();
             
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

字元流

  • Reader字元輸入流 
  • Writer字元輸出流 
  • 專門用於字元的形式讀取和寫入資料

使用字元流讀取檔案

FileReader 是Reader子類,以FileReader 為例進行檔案讀取

File f = new File("d:/lol.txt");
        // 建立基於檔案的Reader
        try (FileReader fr = new FileReader(f)) {
            // 建立字元陣列,其長度就是檔案的長度
            char[] all = new char[(int) f.length()];
            // 以字元流的形式讀取檔案所有內容
            fr.read(all);
            for (char b : all) {
                // 打印出來是A B
                System.out.println(b);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

使用字元流把字串寫入到檔案

FileWriter 是Writer的子類,以FileWriter 為例把字串寫入到檔案

 File f = new File("d:/lol2.txt");
        // 建立基於檔案的Writer
        try (FileWriter fr = new FileWriter(f)) {
            // 以字元流的形式把資料寫入到檔案中
            String data="abcdefg1234567890";
            char[] cs = data.toCharArray();
            fr.write(cs);
  
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

快取流

以介質是硬碟為例,位元組流和字元流的弊端:  在每一次讀寫的時候,都會訪問硬碟。 如果讀寫的頻率比較高的時候,其效能表現不佳。  為了解決以上弊端,採用快取流。  快取流在讀取的時候,會一次性讀較多的資料到快取中,以後每一次的讀取,都是在快取中訪問,直到快取中的資料讀取完畢,再到硬碟中讀取。  就好比吃飯,不用快取就是每吃一口都到鍋裡去鏟。用快取就是先把飯盛到碗裡,碗裡的吃完了,再到鍋裡去鏟  快取流在寫入資料的時候,會先把資料寫入到快取區,直到快取區達到一定的量,才把這些資料,一起寫入到硬碟中去。按照這種操作模式,就不會像位元組流,字元流那樣每寫一個位元組都訪問硬碟,從而減少了IO操作。

使用快取流讀取資料

快取字元輸入流 BufferedReader 可以一次讀取一行資料

 File f = new File("d:/lol.txt");
        // 建立檔案字元流
        // 快取流必須建立在一個存在的流的基礎上
        try (
                FileReader fr = new FileReader(f);
                BufferedReader br = new BufferedReader(fr);
            )
        {
            while (true) {
                // 一次讀一行
                String line = br.readLine();
                if (null == line)
                    break;
                System.out.println(line);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

使用快取流寫出資料

PrintWriter 快取字元輸出流, 可以一次寫出一行資料

 File f = new File("d:/lol2.txt");
          
        try (
                // 建立檔案字元流
                FileWriter fw = new FileWriter(f);
                // 快取流必須建立在一個存在的流的基礎上              
                PrintWriter pw = new PrintWriter(fw);              
        ) {
            pw.println("garen kill teemo");
            pw.println("teemo revive after 1 minutes");
            pw.println("teemo try to garen, but killed again");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

flush

有的時候,需要立即把資料寫入到硬碟,而不是等快取滿了才寫出去。 這時候就需要用到flush

File f =new File("d:/lol2.txt");
        //建立檔案字元流
        //快取流必須建立在一個存在的流的基礎上
        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
            pw.println("garen kill teemo");
            //強制把快取中的資料寫入硬碟,無論快取是否已滿
                pw.flush();           
            pw.println("teemo revive after 1 minutes");
                pw.flush();
            pw.println("teemo try to garen, but killed again");
                pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

資料流

  • DataInputStream 資料輸入流 
  • DataOutputStream 資料輸出流

直接進行字串的讀寫

驟 1 : 

直接進行字串的讀寫

使用資料流的writeUTF()和readUTF() 可以進行資料的格式化順序讀寫 如本例,通過DataOutputStream 向檔案順序寫出 布林值,整數和字串。 然後再通過DataInputStream 順序讀入這些資料。 注: 要用DataInputStream 讀取一個檔案,這個檔案必須是由DataOutputStream 寫出的,否則會出現EOFException,因為DataOutputStream 在寫出的時候會做一些特殊標記,只有DataInputStream 才能成功的讀取。

 private static void read() {
        File f =new File("d:/lol.txt");
        try (
                FileInputStream fis  = new FileInputStream(f);
                DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
             
            System.out.println("讀取到布林值:"+b);
            System.out.println("讀取到整數:"+i);
            System.out.println("讀取到字串:"+str);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
 
    private static void write() {
        File f =new File("d:/lol.txt");
        try (
                FileOutputStream fos  = new FileOutputStream(f);
                DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(300);
            dos.writeUTF("123 this is gareen");
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }

物件流

物件流指的是可以直接把一個物件以流的形式傳輸給其他的介質,比如硬碟  一個物件以流的形式進行傳輸,叫做序列化。 該物件所對應的類,必須是實現Serializable介面

序列化一個物件

  • 建立一個Hero物件,設定其名稱為garen。 
  • 把該物件序列化到一個檔案garen.lol。
  • 然後再通過序列化把該檔案轉換為一個Hero物件
  • 注:把一個物件序列化有一個前提是:這個物件的類,必須實現了Serializable介面
  • Hero.java:
public class Hero implements Serializable {
	//表示這個類當前的版本,如果有了變化,比如新設計了屬性,就應該修改這個版本號
	private static final long serialVersionUID = 1L; 
	public String name;
	public float hp;

}
 Hero h = new Hero();
        h.name = "garen";
        h.hp = 616;
          
        //準備一個檔案用於儲存該物件
        File f =new File("d:/garen.lol");
 
        try(
            //建立物件輸出流
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos =new ObjectOutputStream(fos);
            //建立物件輸入流              
            FileInputStream fis = new FileInputStream(f);
            ObjectInputStream ois =new ObjectInputStream(fis);
        ) {
            oos.writeObject(h);
            Hero h2 = (Hero) ois.readObject();
            System.out.println(h2.name);
            System.out.println(h2.hp);
               
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
            

關閉流的方式:

所有的流,無論是輸入流還是輸出流,使用完畢之後,都應該關閉。 如果不關閉,會產生對資源佔用的浪費。 當量比較大的時候,會影響到業務的正常開展。

在try中關閉

在try的作用域裡關閉檔案輸入流,在前面的示例中都是使用這種方式,這樣做有一個弊端; 如果檔案不存在,或者讀取的時候出現問題而丟擲異常,那麼就不會執行這一行關閉流的程式碼,存在巨大的資源佔用隱患。 不推薦使用。

 try {
            File f = new File("d:/lol.txt");
            FileInputStream fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
            // 在try 裡關閉流
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

在finally中關閉

這是標準的關閉流的方式 1. 首先把流的引用宣告在try的外面,如果宣告在try裡面,其作用域無法抵達finally. 2. 在finally關閉之前,要先判斷該引用是否為空 3. 關閉的時候,需要再一次進行try catch處理 這是標準的嚴謹的關閉流的方式,但是看上去很繁瑣,所以寫不重要的或者測試程式碼的時候,都會採用上面的有隱患try的方式,因為不麻煩~

 File f = new File("d:/lol.txt");
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(f);
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 在finally 裡關閉流
            if (null != fis)
                try {
 
                    fis.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

使用try()的方式

把流定義在try()裡,try,catch或者finally結束的時候,會自動關閉 這種編寫程式碼的方式叫做 try-with-resources, 這是從JDK7開始支援的技術 所有的流,都實現了一個介面叫做 AutoCloseable,任何類實現了這個介面,都可以在try()中進行例項化。 並且在try, catch, finally結束的時候自動關閉,回收相關資源。

File f = new File("d:/lol.txt");
  
        //把流定義在try()裡,try,catch或者finally結束的時候,會自動關閉
        try (FileInputStream fis = new FileInputStream(f)) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
            for (byte b : all) {
                System.out.println(b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

流關係圖: