java8--IO(java瘋狂講義3復習筆記)


 產生文件

        File file = new File("abc.txt");
        if(!file.exists()){
            System.out.println(file.exists());
            file.createNewFile();
        }
        System.out.println(file.getAbsolutePath());

 

關於臨時文件

1).放在指定目錄,此時已項目為默認目錄

    File file = File.createTempFile("defaultTmp","tmpp",new File("./"));
        System.out.println(file.getAbsolutePath());
--------------------------------------------------

/Users/liuxin/work/workspace2/learnJava/./defaultTmp5487876755372558069tmpp

2).放在默認目錄

File file = File.createTempFile("defaultTmp",".tmpp");
        System.out.println(file.getAbsolutePath());

------------------------------------------------------------------------
/var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp1175907986750188229tmpp

3).虛擬機退出時刪除臨時文件

File file = File.createTempFile("defaultTmp",".tmpp");
        file.deleteOnExit();
        System.out.println(file.getAbsolutePath());

4).關於后綴

File testFile = new File("testFile");
        if(!testFile.exists()){
            testFile.mkdirs();
        }
        testFile.deleteOnExit();
        File file = File.createTempFile("defaultTmp","tmpp");
        System.out.println(file.getAbsolutePath());
        File file2 = File.createTempFile("defaultTmp2",".tmpp",testFile);
        System.out.println(file2.getAbsolutePath());
        File file3 = File.createTempFile("defaultTmp3",null,testFile);
        System.out.println(file3.getAbsolutePath());


=======================================
/var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp5296324826431648502tmpp
/Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp21551336802243345058.tmpp
/Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp33930717872920538957.tmp

 

操作目錄

1.查看目錄下的所有文件

File currentFile = new File(".");
  System.out.println(currentFile.getAbsolutePath());
  for(File file : currentFile.listFiles()){
   System.out.println(file.getName());
  }

 

getParent()的使用方法

 

File file = new File(".");
System.out.println(file.getAbsolutePath());
System.out.println(new File(file.getAbsolutePath()).getParent());

-----------------------
/Users/liuxin/work/workspace2/learnJava/.
/Users/liuxin/work/workspace2/learnJava


getParent()只有在定義文件時有路徑時才起作用.

 

15.2 流

字節流主要由InputStream和OutputStream作為基類,而字符流則主要由Reader和Writer作為基類.

字節流操作的數據單元是8位的字節,而字符流操作的數據單元是16位的字符

節點流和處理流,節點流是低級流,直接跟數據源相接.處理流(也叫包裝流)把節點流包裝了一層,屬於修飾器設計模式.

處理流的功能主要體現在以下兩個方面:

1.性能的提高:主要以增加緩沖的方式來提高輸入輸出的效率

2.操作的便捷:處理流可能提供了一系列便捷的方法來一次輸入輸出大批量的內容,而不是輸入/輸出一個或多個水滴

處理流可以嫁接在任何已存在的流的基礎上.

15.3.1 InputStream和Reader

InputStream和Reader是所有輸入流的抽象基類,本身並不能創建實例來執行.

讀取文件的demo

public static void testFile() throws IOException{
        // 創建字節輸入流
                    FileInputStream fis = new FileInputStream("a.txt");
                    // 創建一個長度為1024的“竹筒”
                    byte[] bbuf = new byte[1024];
                    // 用於保存實際讀取的字節數
                    int hasRead = 0;
                    // 使用循環來重復“取水”過程
                    while ((hasRead = fis.read(bbuf)) > 0 )
                    {
                        // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入!
                        System.out.print(new String(bbuf , 0 , hasRead ));
                    }
                    // 關閉文件輸入流,放在finally塊里更安全
                    fis.close();
    }

 

關於fileFilter

 

File file = new File(".");
        String[] nameList = file.list((dir, name) -> name.endsWith(".java")
            || new File(name).isDirectory());
        for(String name : nameList)
        {
            System.out.println(name);
        }

================================
.settings
a
bin
lib
result
sqlModify
src
testFile

 

15.2 理解java的IO流

用字節流讀取文件並打印

public static void testFile2() throws IOException{
        // 創建字節輸入流
                    FileInputStream fis = new FileInputStream("a.txt");
                    //讀取一個字節,返回的是int,所以要用char強轉
                    System.out.println((char)fis.read());
                    // 創建一個長度為1024的“竹筒”
                    byte[] bbuf = new byte[1024];
                    //讀取5個字節,放在bbuf的index為10的位置
                    fis.read(bbuf,10,10);
//                    從bbuf中index為10的位置讀取5個字節構成字符串
                    System.out.println(new String(bbuf , 10, 5));
                    //轉換成String后,byte[]不變
                    System.out.println(new String(bbuf , 10, 5));
                    // 用於保存實際讀取的字節數
                    int hasRead = 0;
                    // 使用循環來重復“取水”過程
                    while ((hasRead = fis.read(bbuf)) > 0 )
                    {
                        //重新賦值后,byte[]會改變
                        System.out.println(new String(bbuf , 10, 5));
                        // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入!
                        System.out.println("==============================================");
                        String test = new String(bbuf , 0 , hasRead );
                        System.out.println(test);
                        System.out.println("=================="+new String(bbuf , 0 , hasRead ).length()+"============================");
                    }
                    // 關閉文件輸入流,放在finally塊里更安全
                    fis.close();
    }
打印結果如下
< ?xml ?xml ncodi ============================================== on="1.0" encoding="UTF-8"?> <projectDescription> <name>learnJava</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> 我很好啊,看看需不要utf-8 ==================373============================
public static void testFile3() {
        try(
                // 創建字符輸入流
                FileReader fr = new FileReader("a.txt")
            ){
                // 創建一個長度為32的“竹筒”
                char[] cbuf = new char[32];
                System.out.println(fr.read(cbuf,0,32));
                System.out.println(new String(cbuf));
                // 用於保存實際讀取的字符數
                int hasRead = 0;
                // 使用循環來重復“取水”過程
                while ((hasRead = fr.read(cbuf)) > 0 )
                {
                    // 取出“竹筒”中水滴(字符),將字符數組轉換成字符串輸入!
//                    System.out.print(new String(cbuf , 0 , hasRead));
                    System.out.print(new String(cbuf));
                }
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }

    }

 

outPutStream 和 Writer

public static void testFileWriter() {
    //這里a.txt可以不存在,如果不存在回自動生成,但是沒搞明白怎么設置編碼格式
try(FileWriter fw = new FileWriter("a.txt")) { fw.write("錦瑟 - 李商隱\r\n"); fw.write("錦瑟無端五十弦,一弦一柱思華年。\r\n"); fw.write("庄生曉夢迷蝴蝶,望帝春心托杜鵑。\r\n"); fw.write("滄海月明珠有淚,藍田日暖玉生煙。\r\n"); fw.write("此情可待成追憶,只是當時已惘然。\r\n"); } catch (IOException ioe) { ioe.printStackTrace(); } }

 

15.4 輸入輸出流體系

處理流:構造參數是已經存在的流.

節點流:構造參數是物理IO節點

處理流的用法

public static void PrintStream() {
        try(
                FileOutputStream fos = new FileOutputStream("a.txt");
                PrintStream ps = new PrintStream(fos))
            {
                // 使用PrintStream執行輸出
                ps.println("普通字符串");
                // 直接使用PrintStream輸出對象
                ps.println(new LearnIO());
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
    }

輸出結果

普通字符串
learnIO.LearnIO@677327b6

在使用了處理流包裝了底層節點之后,關閉輸入/輸出流資源時,只要關閉最上層的處理流即可。關閉最上層的處理流時,系統會自動關閉被該處理流包裝的節點流。

字符串流的用法,似乎沒什么用,等發現用處再補充

public static void stringNodeTest() {
        String src = "從明天起,做一個幸福的人\n"
                + "喂馬,劈柴,周游世界\n"
                + "從明天起,關心糧食和蔬菜\n"
                + "我有一所房子,面朝大海,春暖花開\n"
                + "從明天起,和每一個親人通信\n"
                + "告訴他們我的幸福\n";
            char[] buffer = new char[32];
            int hasRead = 0;
            try(
                StringReader sr = new StringReader(src))
            {
                // 采用循環讀取的訪問讀取字符串
                while((hasRead = sr.read(buffer)) > 0)
                {
                    System.out.print(new String(buffer ,0 , hasRead));
                }
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
            try(
                // 創建StringWriter時,實際上以一個StringBuffer作為輸出節點
                // 下面指定的20就是StringBuffer的初始長度
                StringWriter sw = new StringWriter())
            {
                // 調用StringWriter的方法執行輸出
                sw.write("有一個美麗的新世界,\n");
                sw.write("她在遠方等我,\n");
                sw.write("哪里有天真的孩子,\n");
                sw.write("還有姑娘的酒窩\n");
                System.out.println("----下面是sw的字符串節點里的內容----");
                // 使用toString()方法返回StringWriter的字符串節點的內容
                System.out.println(sw.toString());
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
    }

 

15.4.3 轉換流

InputStreamReader :將字節輸入流轉換成字符輸入流

OutputStreamWriter:將字節輸出流轉換成字符輸出流

經過測試,下面這個程序是有一定問題的,用輸入法的時候,會重復輸出好多遍

public static void KeyinTest() {
        try(
                // 將Sytem.in對象轉換成Reader對象
                InputStreamReader reader = new InputStreamReader(System.in);
                // 將普通Reader包裝成BufferedReader
                BufferedReader br = new BufferedReader(reader))
            {
                String line = null;
                // 采用循環方式來一行一行的讀取
                while ((line = br.readLine()) != null)
                {
                    // 如果讀取的字符串為"exit",程序退出
                    if (line.equals("exit"))
                    {
                        System.exit(1);
                    }
                    // 打印讀取的內容
                    System.out.println("輸入內容為:" + line);
                }
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
    }

15.4.4 推回輸入流

PushbackInputStream和PushbackReader

用unread方法將內容推回到緩沖區,從而允許重復讀取剛剛讀取的內容.

使用推回流,輸出某分割符號之前的內容。但其實完全不必這么做,讀出來判斷就好了。

public static void pushbackTest() {
        try(
                // 創建一個PushbackReader對象,指定推回緩沖區的長度為64
                PushbackReader pr = new PushbackReader(new FileReader("a.txt") , 64))
            {
                char[] buf = new char[32];
                // 用以保存上次讀取的字符串內容
                String lastContent = "";
                int hasRead = 0;
                // 循環讀取文件內容
                while ((hasRead = pr.read(buf)) > 0)
                {
                    // 將讀取的內容轉換成字符串
                    String content = new String(buf , 0 , hasRead);
                    int targetIndex = 0;
                    // 將上次讀取的字符串和本次讀取的字符串拼起來,
                    // 查看是否包含目標字符串, 如果包含目標字符串
                    if ((targetIndex = (lastContent + content)
                        .indexOf("測試")) > 0)
                    {
                        // 將本次內容和上次內容一起推回緩沖區
                        pr.unread((lastContent + content).toCharArray());
                        // 重新定義一個長度為targetIndex的char數組
                        if(targetIndex > 32)
                        {
                            buf = new char[targetIndex];
                        }
                        // 再次讀取指定長度的內容(就是目標字符串之前的內容)
                        pr.read(buf , 0 , targetIndex);
                        // 打印讀取的內容
                        System.out.print(new String(buf , 0 ,targetIndex));
                        System.exit(0);
                    }
                    else
                    {
                        // 打印上次讀取的內容
                        System.out.print(lastContent);
                        // 將本次內容設為上次讀取的內容
                        lastContent = content;
                    }
                }
            }
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
    }

system類里提供了三個重定向標准輸入輸出的方法。

setErr(PrintStream err)

setIn(InputStream in)

setOut(PrintStream out)

程序可通過重定向標准輸出流,將System.out的輸出重定向到文件輸出.

15.6 Java虛擬機讀寫其他進程的數據

使用Runtime對象的exec()方法可以運行平台上的其他程序,該方法產生一個Process對象,Process對象代表由該Java程序啟動的子進程.Process類提供了如下三個方法,用於讓程序和其子進程進行通信.

InputStream getErrorStream():獲取子進程的錯誤流,

InputStream getInputStream(): 獲取子進程的輸入流.

OutputStream getOutputStream(): 獲取子進程的輸出流.

這里的輸入流和輸出流是相對於該java程序(注意,不是子程序)而言的.這里的子進程相當於物理節點

注意,這里用的是getErrorStream(),因為這不是子進程向本進程傳遞的數據,所以屬於其他,要用ErrorStream

public static void readFromProcess() throws IOException {
        // 運行javac命令,返回運行該命令的子進程
                Process p = Runtime.getRuntime().exec("java -version");
                try(
                    // 以p進程的錯誤流創建BufferedReader對象
                    // 這個錯誤流對本程序是輸入流,對p進程則是輸出流
                    BufferedReader br = new BufferedReader(new
                        InputStreamReader(p.getErrorStream())))
                {
                    String buff = null;
                    // 采取循環方式來讀取p進程的錯誤輸出
                    while((buff = br.readLine()) != null)
                    {
                        System.out.println(buff);
                    }
                }
    }
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

 

java兩個程序之間傳遞信息:這端代碼我沒跑起來,待研究

public class WriteToProcess
{
    public static void main(String[] args)
        throws IOException
    {
        // 運行java ReadStandard命令,返回運行該命令的子進程
        Process p = Runtime.getRuntime().exec("java ReadStandard");
        try(
            // 以p進程的輸出流創建PrintStream對象
            // 這個輸出流對本程序是輸出流,對p進程則是輸入流
            PrintStream ps = new PrintStream(p.getOutputStream()))
        {
            // 向ReadStandard程序寫入內容,這些內容將被ReadStandard讀取
            ps.println("普通字符串");
            ps.println(new WriteToProcess());
        }
    }
}
// 定義一個ReadStandard類,該類可以接受標准輸入,
// 並將標准輸入寫入out.txt文件。
class ReadStandard
{
    public static void main(String[] args)
    {
        try(
            // 使用System.in創建Scanner對象,用於獲取標准輸入
            Scanner sc = new Scanner(System.in);
            PrintStream ps = new PrintStream(
            new FileOutputStream("out.txt")))
        {
            // 增加下面一行將只把回車作為分隔符
            sc.useDelimiter("\n");
            // 判斷是否還有下一個輸入項
            while(sc.hasNext())
            {
                // 輸出輸入項
                ps.println("鍵盤輸入的內容是:" + sc.next());
            }
        }
        catch(IOException ioe)
        {
            ioe.printStackTrace();
        }
    }
}

 

15.7 RandomAccessFile

RandomAccessFile可以自由訪問文件的任意位置,所以如果只需要訪問文件部分內容,而不是把文件從頭讀到尾,使用RamdomAccessFile將是更好的選擇.但是局限是,它只能讀寫文件,不能讀寫其他IO節點.

getFilePointer():返回文件記錄指針的當前位置

seek(long pos):將文件記錄指針定位到pos位置

 

public static void randomAccessFileTest(){
        try(
                RandomAccessFile raf =  new RandomAccessFile(
                    "newFile.txt" , "r"))
            {
                // 獲取RandomAccessFile對象文件指針的位置,初始位置是0
                System.out.println("RandomAccessFile的文件指針的初始位置:"
                    + raf.getFilePointer());
                // 移動raf的文件記錄指針的位置
                raf.seek(30);
                byte[] bbuf = new byte[1024];
                // 用於保存實際讀取的字節數
                int hasRead = 0;
                // 使用循環來重復“取水”過程
                while ((hasRead = raf.read(bbuf)) > 0 )
                {
                    // 取出“竹筒”中水滴(字節),將字節數組轉換成字符串輸入!
                    System.out.print(new String(bbuf , 0 , hasRead ));
                }
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
    }

RandomAccessFile 依然不能向文件的指定位置插入內容,如果直接將文件記錄指針移動到中間某位置后開始輸出,則新輸出的內容會覆蓋文件中原有的內容,如果需要向指定位置插入內容,程序需要先把插入點后面的內容讀入緩沖區,等把需要插入的數據寫入文件后,再將緩沖區的內容追加到文件的后面.

 

public static void insert(String fileName , long pos
            , String insertContent) throws IOException
        {
            File tmp = File.createTempFile("tmp" , null);
            tmp.deleteOnExit();
            try(
                RandomAccessFile raf = new RandomAccessFile(fileName , "rw");
                // 使用臨時文件來保存插入點后的數據
                FileOutputStream tmpOut = new FileOutputStream(tmp);
                FileInputStream tmpIn = new FileInputStream(tmp))
            {
                raf.seek(pos);
                // ------下面代碼將插入點后的內容讀入臨時文件中保存------
                byte[] bbuf = new byte[64];
                // 用於保存實際讀取的字節數
                int hasRead = 0;
                // 使用循環方式讀取插入點后的數據
                while ((hasRead = raf.read(bbuf)) > 0 )
                {
                    // 將讀取的數據寫入臨時文件
                    tmpOut.write(bbuf , 0 , hasRead);
                }
                // ----------下面代碼插入內容----------
                // 把文件記錄指針重新定位到pos位置
                raf.seek(pos);
                // 追加需要插入的內容
                raf.write(insertContent.getBytes());
                // 追加臨時文件中的內容
                while ((hasRead = tmpIn.read(bbuf)) > 0 )
                {
                    raf.write(bbuf , 0 , hasRead);
                }
            }
        }

如果僅僅是追加內容,就簡單多了

public static void main(String[] args)
    {
        try(
            //以讀、寫方式打開一個RandomAccessFile對象
            RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw"))
        {
            //將記錄指針移動到out.txt文件的最后
            raf.seek(raf.length());
            raf.write("追加的內容!\r\n".getBytes());
        }
        catch (IOException ex)
        {
            ex.printStackTrace();
        }
    }

 

15.8 對象序列化(serialize)

對象的序列化指將一個java對象寫入IO流中,與此對應的是,對象的反序列化(Deserialize)則指從IO流中恢復該java對象.

如果需要讓某個對象支持序列化機制,那么必須讓它的類是可序列化的,必須實現如下兩個接口之一

Serializable

Externalizable

程序創建的每個JavaBean類都要實現Serializable

public static void writeObject() {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("obj.txt"));) 
        {
            LearnIO learnIO = new LearnIO("12334444444");
            oos.writeObject(learnIO);

        } catch (IOException ex) {
            // TODO: handle exception
            ex.printStackTrace();
        }
    }


//但是,如果如下

LearnIO learnIO = new LearnIO("12334444444");

 
         

oos.writeObject(learnIO);

learnIO.test="abcdetc";

//不會寫入abcdetc,因為不會序列化號碼相同的東西
oos.writeObject(learnIO);

反序列化

public static void readObject(){
        try(
                ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
        ){
            LearnIO learnIO = (LearnIO)ois.readObject();
            System.out.println(learnIO.test);
        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

反序列化機制無須通過構造器來初始化java對象.

如果使用序列化機制向文件中寫入多個Java對象,使用反序列化機制恢復對象時必須按實際寫入的順序讀取.

當一個可序列化類有多個父類時(包括直接父類和間接父類),這些父類要么有無參數的構造器,要么也是可序列化的--否則反序列化時將拋出InvalidClassException異常.如果父類是不可序列化的,只是帶有無參數的構造器,則該父類中定義的成員變量值不會序列化到二進制流中.

 

15.8.3 對象引用的序列化

如果成員變量是引用類型,那么這個引用類型的類必須是可序列化的.

屬於遞歸序列化

有static修飾和transient修飾的變量不會被序列化

15.8.4 自定義序列化

在實例變量前面使用transient關鍵字修飾,可以指定java序列化時無須理會該實例變量.

更詳細的自定義序列化方法是重寫writeObject和readObject方法.

 

15.8.5 另一種自定義序列化機制

實現Externalizable

 


免責聲明!

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



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