從0開始復習java(9)--IO

Java的io通過java.io包下的類和接口支持。主要有輸入、輸出流,又分為字節流和字符流。Java的io流使用了一種裝飾器設計模式,他將io流分為底層節點流和上層處理流,節點流用于和底層的物理存儲結點直接關聯。

Java7在java.nio及其子包下提供了一系列全新的api,是對原有新io的升級。

一、File類

File類是java.io包下代表與平臺無關的文件和目錄。File能新建、刪除、重命名文件和目錄,不能訪問文件本身內容。

訪問文件和目錄

訪問文件名的方法

  1. String getName()
  2. String getPath()
  3. File getAbsoluteFile()
  4. String getAbsolutePath()
  5. String getParent()
  6. boolean renameTo(FIle newName)

文件檢測相關的方法

  1. boolean exists()
  2. boolean canWrite()
  3. boolean canRead()
  4. boolean isFile()
  5. boolean isDirectory()
  6. boolean isAbsolute()

獲取常規文件信息

  1. long lastModified()
  2. long length()

文件操作相關的方法

  1. boolean createNewFile()
  2. boolean delete()
  3. static File createTempFile(String prefix, String suffix)
  4. static File CreateTempFIle(String prefix, String suffix, File directory)
  5. void deleteOnExit()

目錄操作相關的方法

  1. boolean mkdir()
  2. String[] list()
  3. File[] listFiles()
  4. static File[] listRoots()

文件過濾器

File類的list()方法可以接收一個FilenameFilter參數,通過該參數可以只列出符合條件的文件。

FilenameFilter接口和javax.swing.filechooser包下的FileFilter抽象類的功能非常相似。

二、Java的IO流

流的分類

輸入流和輸出流

抽象基類:

InputStream和Reader

OutputStream和Writer

字節流和字符流

字節流:8位,InputStream和OutputStream

字符流:16位,Reader和Writer

節點流和處理流

向一個特定的io設備讀寫數據的流為節點流,Low Level Stream

處理流對于已有的節點流進行連接和封裝,通過封裝后的流實現數據讀/寫功能。High Level Stream

處理流是一種典型的裝飾器設計模式,也稱為包裝流

流的概念模型

io流涉及40多個類。

三、字節流和字符流

InputStream和Reader

InputStream接口的方法:

  • int read()
  • int read(byte[] b)
  • int read(byte[] b, int off, int len)

Reader接口的方法:

  • int read()
  • int read(char[] cbuf)
  • int read(char[] cbuf, int off, int len)

讀取文件:FileInputStream,FileReader

還有一些移動指針的方法:

  • void mark(int readAheadLimit)
  • boolean markSupported()
  • void reset()
  • long skip(long n)

OutputStream和Writer

提供的方法

  • void write(int c)
  • void write(byte[]/char[]/String buf)
  • void write(byte[]/char[]/String buf, int off, int len)

Writer可以使用String代替char[]

四、輸入/輸出流體系

處理流的用法

使用處理流包裝節點流,程序通過處理流執行輸入/輸出功能,讓節點流與底層的IO設備、文件交互。

所有節點流都是以物理io節點作為構造器參數的。其他的流為處理流。

處理流的優點

  1. 操作簡單
  2. 執行效率更高

PrintStream的輸出功能很強大,System.out的類型就是PrintStream

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

輸入/輸出流體系

輸入輸出流分類
輸入輸出流分類

規則:

輸入輸出是文本,則考慮使用字符流;是二進制,則使用字節流

轉換流

InputStreamReader,OutputStreamWriter

將字節流轉換為字符流。使處理更方便。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Test{
    public static void main(String[] args) {
        try(
            InputStreamReader reader = new InputStreamReader(System.in);
            BufferedReader br = new BufferedReader(reader);
        ){
            String line = null;
            while((line = br.readLine() != null){
                if (line.equals("exit")){
                    System.exit(1);
                }
                System.out.println(line);
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

BufferedReader有一個readLine()方法,很方便,所以經常把讀取文本內容的流包裝成BufferedReader

推回輸入流

PushbackInputStream和PushbackReader。

提供的方法:

  1. void unread(byte[]/char[] buf)
  2. void unread(byte[]/char[] b, int off, int len)
  3. void unread(int b)

五、重定向標準輸入/輸出

Java的標準輸入/輸出分別通過SysteminSystem.out代表。在默認情況下代表鍵盤和顯示器。

System類里包含三個重定向標準輸入/輸出的方法:

  • static void setErr(PrintStream err)
  • static void setIn(InputStream in)
  • static void setOut(OutputStream out)
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

public class Test{
    public static void main(String[] args) {
        try(
            PrintStream ps = new PrintStream(new FileOutputStream("out.txt"));
        ){
            System.setOut(ps);
            System.out.println();
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Scanner;

public class Test{
    public static void main(String[] args) {
        try(
            FileInputStream fis = new FileInputStream("test.txt");
        ){
            System.setIn(fis);
            Scanner s = new Scanner(System.in);
            s.useDelimiter('\n');
            while(s.hasNext()){
                System.out.println(s.next());
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

六、RandomAccessFile

可以讀寫文件。支持"隨機訪問"。

只訪問文件的部分內容,可以選擇使用該流。

追加內容也應該選擇。

只能讀寫文件。

  • long getFilePointer()

返回文件記錄指針的當前位置

  • void seek(long pos)

將文件記錄指針定位到pos位置

包含了InputStream的read方法和OutputStream的write方法。

創建RandomAccessFile除了需要一個StringFile還需要一個mode參數。

  • "r":只讀。
  • "rw":讀寫
  • "rws":讀寫,每個更新同步寫入到底層存儲設備
  • "rwd":讀寫,同步寫入到底層設備。
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void main(String[] args) {
        try(
            RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
        ){
            ra.seek(300);
            byte[] buf = new byte[1024];
            int hasRead = 0;
            while((hasRead = ra.read(buf))>0){
                System.out.println(new String(buf, 0, hasRead));
            }
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

追加內容:

import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void main(String[] args) {
        try(
            RandomAccessFile ra = new RandomAccessFile("Test.java", "r");
        ){
            ra.seek(ra.length());
            ra.write("\r\nHello world".getBytes());
        }
        catch(IOException e){
            e.printStackTrace();
        }
    }
}

向指定文件、指定位置插入內容:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Test{
    public static void insert(String filename, long pos, String content) throws IOException{
        File temp = File.createTempFile("temp", null);
        temp.deleteOnExit();
        try(
            RandomAccessFile ra = new RandomAccessFile(filename, "rw");
            FileOutputStream fos = new FileOutputStream(temp);
            FileInputStream fis = new FileInputStream(temp);
        ){
            raf.seek(pos);
            byte[] buf = new byte[64];
            int hasRead = 0;
            while((hasRead=ra.read(buf))>0){
                fos.write(buf, 0, hasRead);
            }
            raf.seek(pos);
            raf.write(content.getBytes());
            while((hasRead=fis.read(buf))>0){
                raf.write(buf, 0, hasRead);
            }
        }
    }
    public static void main(String[] args) {
        try{
            Test.insert("test.java", 45, "Hello World\r\n");
        }
        catch(IOException e){
            e.printStackTrace();
        }
        
    }
}

七、NIO

Java7對原來的io進行了重大改進:

  • 提供了全面的文件IO和文件系統訪問支持
  • 基于異步Channel的IO

第一個體現為java.nio.file包及其子包

第二個體現為java.nio.channels包下面增加了多個以Asynchronous開頭的Channel接口和類。

Path、Paths和Files核心api

早期只有File訪問文件系統,但File類的功能比較有限,不能利用特定文件系統的特性;FIle提供的方法的性能不高,其方法在出錯時返回失敗,但不提供異常信息。

NIO為了彌補這種不足,引入了一個Path接口,代表一個平臺無關的平臺路徑。FilesPaths兩個工具類。

Files包含大量靜態工具方法來操作文件,Paths包含兩個返回Path的靜態工廠方法。

import java.nio.file.Path;
import java.nio.file.Paths;

public class Test{
    public static void main(String[] args) {
        Path path = Paths.get(".");
        System.out.println(path.getNameCount());
        Path abPath = path.toAbsolutePath();
        System.out.println(abPath.getRoot());
        //c:/test
        Path path2 = Paths.get("c", "test");
    }
}
import java.io.FileOutputStream;
import java.nio.charset.Charset;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test{
    public static void main(String[] args) {
        Files.copy(Paths.get("Test.java"), new FileOutputStream("a.txt"));
        System.out.println(Files.isHidden(Pahts.get("Test.java")));
        List<String> lines = Files.readAllLines(Paths.get("Test.java"), Charset.forName("gbk"));
        System.out.println(Files.size(Paths.get("test.java")));
        Files.write(Paths.get("test.txt"), lines, Charset.forName("gbk"));
        Files.list(Paths.get("."));
        Files.lines(Paths.get("Test.java"), Charset.forName("gbk"));
        FileStore cStore = Files.getFileStore(Paths.get("c:"));
        cStore.getTotalSpace();
        cStore.getUsableSpace();
    }
}

使用FileVisitor遍歷文件和目錄

  • walkFileTree(Path start, FileVisitor<? super Path> visitor)
  • walkFileTree(Path start, Set<FileVisitOpeion> options, int maxDepth, FileVisitor<? super Path> visitor)

FileVisitor定義了如下4個方法:

  • FileVisitResult postVisitDirectory(T dir, IOException exc)
  • FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
  • FileVisitResult visitFIle(T file, BasicFileAttributes attrs)
  • FileVisitResult visitFileFailed(T file, BasicFileAttributes attrs)

FileVisitResult 是一個枚舉類,代表了訪問之后的后續行為。

  • CONTINUE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE
  • TERMINATE

實際編程可以通過繼承SimpleFileVisitor(FileVisitor的實現類)實現自己的文件訪問器可以根據需要選擇性重寫指定方法。

import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

public class Test{
    public static void main(String[] args) throws IOException{
        //c:/test
        Files.walkFileTree(Paths.get("c:", "test"), new SimpleFileVisitor<Path>(){
            //訪問文件時觸發的方法
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException{
                return FileVisitResult.CONTINUE;
            }
            //開始訪問目錄時觸發該方法
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException{
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

使用WatchService監控文件變化

早期需要后臺啟用一個線程每隔一段時間去遍歷一次指定目錄的文件。繁瑣,性能不好

Path類提供了如下一個方法監控文件系統的變化:

  • register(WatchService watcher, WatchEvent.Kind<?>... events)

WatchService代表一個文件系統監聽服務,它負責監聽path代表目錄下的文件變化。一旦使用register方法完成注冊,接下來可以調用WatchService的如下三個方法獲取監聽目錄的文件變化:

  • WatchKey poll()

獲取一個WatchKey,如果沒有WatchKey發生,則返回null

  • WatchKey poll(long timeout, TimeUnit unit)

嘗試等待timeout時間去獲取下一個WatchKey

  • WatchKey take()

獲取下一個WatchKey,如果沒有則一直等待

如果程序需要一直監控,應該選用take方法,否則可以選用poll方法

import java.nio.file.FileSystems;
import java.nio.file.WatchService;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;

public class Test{
    public static void main(String[] args) {
        WatchService watchService = FileSystems.getDefault().newWatchService();

        Paths.get("c:/").register(watcherService, 
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_MODIFY);
        while(true){
            WatchKey key = watchService.take();
            for(WatchEvent<?> event:key.pollEvents()){
                System.out.println(event.context()+" 文件發生了 "+event.kind()+" 事件!");
            }
            //重設watchkey
            boolean valid = key.reset();
            if(!valid){
                break;
            }
        }
        
    }
}

訪問文件屬性

早期的File類可以訪問一些簡單的文件屬性。獲取或者修改更多會很難。

Java7下面的java.nio.file.attribute包下面提供了大量的工具類,通過這些工具類,可以簡單方便的讀取、修改文件屬性。這些工具類主要分為一下兩類:

  • XxxAttributeView

代表某種文件屬性的"視圖"

  • XxxAttributes

代表某種文件屬性的"集合",程序一般通過XxxAttributeView獲取XxxAttributes

FileAttributeView是其他view的父接口。

AclFileAttributeView

可以為特定文件設置ACL(access control list)及文件所有屬性。getAcl(),setAcl(List)

BasicFileAttributeView

獲取和修改文件的基本屬性,包括文件的最后修改時間、最后訪問時間、創建時間、大小、是否為目錄、是否為符號鏈接等。readAttributes()放回BasicFileAttributes對象

DosFileAttributeView

主要用于獲取或修改文件dos相關屬性。是否只讀、是否隱藏、是否為系統文件、是否是存檔文件等。

FileOwnerAttributeView

獲取或修改文件的所有者

PosixFileAttributeView

主要用于修改POSIX(portable operating system interface of unix)屬性。

UserDefinedFileAttributeView

讓開發者為文件設置一些自定義屬性

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,738評論 18 399
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,836評論 18 139
  • Java 語言支持的類型分為兩類:基本類型和引用類型。整型(byte 1, short 2, int 4, lon...
    xiaogmail閱讀 1,364評論 0 10
  • 一、 1、請用Java寫一個冒泡排序方法 【參考答案】 public static void Bubble(int...
    獨云閱讀 1,408評論 0 6
  • 電影最迷人的地方就是,可以讓人沉浸其中,經歷一段現實中不可能經歷的人生。 看完《愛樂之城》,唏噓之余,只覺得“愛情...
    煙行閱讀 597評論 0 5