Java IO/NIO(示例)

在Java程序中,对于数据的输入/输出以”流”(Stream)方式进行
java.io 包定义了多个流类型:
  1) 按数据流方向分为 – 输入流和输出流
  2) 按数据单位分为 – 字节流和字符流
  3) 按功能分为 – 节点流和处理流

所有流类型均位于java.io包内,分别继承以下四种抽象流类型:
      字节流      字符流
输入流   InputStream   Reader
输出流  OutputStream    Writer

注:输入输出流,都是站在【程序】的角度上来说,从文件读数据这叫输入流,往文件写数据叫输出流

一、字节 I/O 操作(InputStream和OutputStream)相关类图

这里写图片描述

1) InputStream是一个抽象类,核心方法是read()、read(byte b[])、read(byte b[], int off, int len),从不同的数据源读取数据。
这些数据源有:
 ① 字节数组。 
 ② String对象。 
 ③ 文件。
 ④ “管道”,一端输入,另一端输出。
 ⑤ 一个由其他种类的流组成的序列。 
 ⑥ 其他数据源,如Internet等。
每一种数据源都对应相应的InputStream子类:
 ByteArrayInputStream:缓冲区,处理字节数组,允许将内存的缓冲区当作InuputStream。
 StringBufferInputStream:将String转换成 InputStream,底层使用StringBuffer实现。
 FileInputStream:从文件中读取信息。
 PipedInputStream:从管道读数据,最为多线程中的数据源。
 SequenceInputStream:将多个流对象转换成一个InputStream。
 FilterInputStream:“装饰器”类的基类(这里是装饰器模式),为其它InputStream类提供功能:
   DataInputStream:用于读取基本类型数据。
   BufferedInputStream:使用缓冲区,防止每次读取时都得进行实际写操作。
   LineNumberInputStream:跟踪输入流中的行号,getLineNumber()、setLineNumber(int)。
   PushbackInputStream:具有能弹出一个字节的缓冲区,可以将读到的最后一个字符回退。
 
2) OutputStream是一个抽象类,核心方法是write(int b)、write(byte b[])、write(byte b[], int off, int len),决定输出的目标:字节数组、文件或管道。
 ByteArrayOutputStream:将数据写入一个byte数组缓冲区。
 FileOutputStream:将数据写入文件。
 PipedOutputStream:指定多线程数据的目的地,向与其它线程共用的管道中写入数据,用于多线程之间任务的通信
 FilterOutputStream:“装饰器”类,提供特定的输出流:
   DataOutputStream:与DataInputStream搭配使用,用于写入基本类型数据。
   BufferedOutputStream:使用缓冲区,避免每次发送数据时都进行实际的写操作,可使用 flush() 清空缓冲区。
   PrintStream:产生格式化输出。

二、字符 I/O 操作(Reader和Writer)相关类图

这里写图片描述

Reader和Writer操作的是字符,通过 InputStreamReaderOutputStreamWriter 进行字节和字符的转换

三、基于磁盘的I/O操作(File类)
  File类既能代表一个特定文件的名称,又能代表一个目录下的一组文件的名称。
【1】文件、目录的创建和删除

public class FileOperation {
    public static void main(String[] args) {
        // 使用 File.separator 系统默认名称分隔符,Windows和Linux文件路径分割符不同
        String fileName = "C:" + File.separator + "hello.txt"; 
        File file = new File(fileName);

        // 创建一个新文件
        try {
            // 指定的文件不存在且成功创建返回 true;文件已存在返回 false
            file.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 删除文件
        if(file.exists()){
            // 成功删除文件或目录时返回 true
            file.delete();
        }

        // 创建一个文件夹
        String dirName = "C:" + File.separator + "hello";
        File dirFile = new File(dirName);
        dirFile.mkdir();
        dirFile.isDirectory(); // 判断是否为目录
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

【2】查看目录列表

public class FileOperation {
    public static void main(String[] args) {
        File path = new File("C:");
        String[] list;

        // 查看目录所有内容
        list = path.list();

        // 查看目录指定内容(以 .txt 结尾的)
        final String regStr = ".+.txt";

        // 使用一个匿名的目录过滤器内部类,通过正则表达式验证过滤,list()可以回调accept()方法
        list = path.list(new FilenameFilter() {
            private Pattern pattern = Pattern.compile(regStr); 
            @Override
            public boolean accept(File dir, String name) {
                return pattern.matcher(name).matches();
            }
        });
        for(String dirItem : list){
            System.out.println(dirItem);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

四、I/O流典型使用方式
  尽管可以通过不同的方式组合 I/O 流类,但我们可能也就只用到其中的几种组合。
【1】缓冲输入文件(读取)

public class BufferedInputFile {
    public static String read(String fileName) throws Exception{
        BufferedReader br = new BufferedReader(new FileReader(fileName));
        String str;
        StringBuffer sb = new StringBuffer();
        while((str = br.readLine()) != null){
            sb.append(str + "\n");
        }
        br.close();
        return sb.toString();
    }
    public static void main(String[] args) throws Exception {
        System.out.println(read("src/com/test/BufferedInputFile.java"));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

【2】从内存输入(读取)

public class MemoryInput {
    public static void main(String[] args) throws Exception {
        StringReader in = new StringReader(BufferedInputFile.read("src/com/test/MemoryInput.java"));
        int c;
        while((c = in.read()) != -1){
            System.out.println((char)c);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

read()一个字符一个字符的读取,以int的形式返回下一个字符,需强制转换为char才能正常打印

【3】基本文件输出

/**
 * FileWriter向文件写入数据,使用BufferWriter将其封装缓冲输出,为了格式化装饰成 PrintWriter
 *
 */
public class BasicFileOutput {
    static String file = "BasicFileOutput.out";
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new StringReader(BufferedInputFile.read("src/com/test/BasicFileOutput.java")));
        // PrintWriter有个构造函数,内部实现了BufferedWriter缓冲
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
        //可以简写为 PrintWriter out = new PrintWriter(file);
        int lineCount = 1;
        String s;
        while((s = br.readLine()) != null){
            out.println(lineCount++ + ": " + s);
        }
        out.close();
        System.out.println(BufferedInputFile.read(file));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

【4】存储和恢复数据

/**
 * 存储和恢复数据,使用 DataOutputStream写入数据,并用 DataInputStream 恢复数据
 * 这些流可以是任何形式,以文件为例,对读写都进行缓冲处理
 */
public class StoringAndRecoveringData {
    public static void main(String[] args) throws Exception {
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(
                new FileOutputStream("C:" + File.separator + "Data.txt")));
        out.writeDouble(3.14159265);
        out.writeUTF("That was PI");
        out.writeDouble(1.41413);
        out.writeUTF("Square root of 2");
        out.close();

        DataInputStream in = new DataInputStream(new BufferedInputStream(
                new FileInputStream("C:" + File.separator + "Data.txt")));
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());
        in.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

【5】读写随机访问文件

/**
 * 读写随机访问文件 RandomAccessFile,类似于组合使用 DataInputStream和DataOutputStream
 * 另外可以使用 seek() 可以在文件中到处移动,并修改文件中的某个值
 * double 总是占 8 个字节,故第五个就是  5*8
 */
public class UsingRandomAccessFile {
    static String file = "rtest.dat";
    static void display() throws IOException{
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        for(int i=0; i<7; i++){
            System.out.println("Value " + i + ": " + raf.readDouble());
        }
        System.out.println(raf.readUTF());
        raf.close();
    }
    public static void main(String[] args) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(file, "rw");
        for(int i=0; i<7; i++){
            raf.writeDouble(i * 1.414);
        }
        raf.writeUTF("The end of the file");
        raf.close();
        display();

        raf = new RandomAccessFile(file, "rw");
        raf.seek(5*8);
        raf.writeDouble(47.0001);
        raf.close();
        display();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

【6】文件读写使用工具
  JavaSE5 在PrintWriter 中添加了方便的构造器,可以方便的对一个文本文件进行写入操作,但是其他常见的操作却需要反复执行,写一个工具类用来简化对文件的读写操作。创建一个FileTool对象,它用一个ArrayList来保存文件的若干行,当操纵文件内容时,就可以使用ArrayList的所有功能。

public class FileTool extends ArrayList<String> {
    /**
     * 读取文件
     * @param fileName 文件名
     * @return 返回 String
     */
    public static String read(String fileName) {
        StringBuffer sb = new StringBuffer();
        try{
            BufferedReader br = new BufferedReader(new FileReader(new File(fileName).getAbsoluteFile()));
            try {
                String s;
                while((s = br.readLine()) != null){
                    sb.append(s);
                    sb.append("\n");
                }
            } finally {
                br.close();
            }
        } catch(IOException e) {
            throw new RuntimeException(e);
        }
        return sb.toString();
    }
    /**
     * 往文件中写数据
     * @param fileName 文件名
     * @param text 要写入的文本
     */
    public static void write(String fileName, String text) {
        try {
            PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
            try {
                out.print(text);
            } finally {
                out.close();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 读取文件,并按照指定分隔符分割
     * @param fileName
     * @param splitter 分隔符
     */
    public FileTool(String fileName, String splitter){
        super(Arrays.asList(read(fileName).split(splitter)));
        if(get(0).equals("")) remove(0);
    }
    /**
     * 正常读取文件
     * @param fileName
     */
    public FileTool(String fileName){
        this(fileName, "\n");
    }
    public void write(String fileName){
        try{
            PrintWriter out = new PrintWriter(new File(fileName).getAbsoluteFile());
            try{
                for(String item : this)
                    out.print(item);
            } finally {
                out.close();
            }
        } catch (IOException e){

        }
    }
    // 测试
    public static void main(String[] args) {
        String file = read("src/com/test/FileTool.java");
        System.out.println(file);
        write("fileToolTest.txt", file);
        FileTool fileTool = new FileTool("fileToolTest.txt");
        fileTool.write("fileToolTest.txt");
        TreeSet<String> words = new TreeSet<String>(new FileTool("src/com/test/FileTool.java", "\\W+"));
        System.out.println(words.headSet("a"));
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

【7】读取二进制文件

public class BinaryFile {
    public static byte[] read(File bFile) throws IOException{
        BufferedInputStream bi = new BufferedInputStream(new FileInputStream(bFile));
        try{
            byte[] data = new byte[bi.available()]; // available()用来产生恰当的数组尺寸
            bi.read(data); // read()方法填充数组
            return data;
        } finally {
            bi.close();
        }
    }
    public static byte[] read(String bFile) throws IOException{
        return read(new File(bFile).getAbsoluteFile());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

【7】标准 I/O
  Java提供了System.in、System.out和System.err,System.in是一个没有被包装过未经加工的 InputStream

public class Echo {
    public static void main(String[] args) throws IOException {
        // 将System.in包装成BufferedReader,使用readLine()一次读取一行
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str;
        while((str = br.readLine()) != null && str.length() != 0){
            System.out.println(str);
        }

        // 将System.out转换成PrintWriter
        PrintWriter out = new PrintWriter(System.out, true); // 第二个参数 true 开启自动清空功能
        out.println("Hello, world");

        // 标准 I/O 重定向   操作的是字节流
        // System类提供了一些简单的静态方法,允许对标准输入、输出和错误I/O流进行重定向
        // System.setIn(InputStream)  System.setOut(PrintStream)   System.setErr(PrintStream)
        PrintStream console = System.out; // System.out对象的引用
        BufferedInputStream in = new BufferedInputStream(new FileInputStream("src/com/test/Echo.java"));
        PrintStream _out = new PrintStream(new BufferedOutputStream(new FileOutputStream("test.out")));
        System.setIn(in);
        System.setOut(_out);
        System.setErr(_out);
        str = null;
        while((str = br.readLine()) != null && str.length() != 0){
            System.out.println(str); // 标准输出将重定向到另一个文件
        }
        _out.close();
        System.setOut(console); // 恢复了System.out对象的引用
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

五、Java NIO
  JDK1.4 的java.nio.* 包中引入了新的Java I/O类库,目的在于提高速度。与传统的 IO 相比主要有以下区别:
 ① Java IO是面向的,Java NIO是面向缓冲区(块)的
 ② Java IO各种流是阻塞的,当一线程调用read()write()时,该线程被阻塞,直到有数据被读取或写入;Java NIO是非阻塞的,一线程从某通道请求读取数据,若目前没有数据可用,则不保持线程阻塞,直到有数据可供读取,该线程可继续做其他的事情,在写入的时候也是,不等待它完全写入,线程也可以同时去做其他的事情
  Java NIO 由以下3个核心部分组成:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。
  
(1)Channel(通道)
:Java NIO的通道类似流,但又有些不同:
 ① 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
 ② 通道可以异步地读写。
 ③ 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入

在Java NIO中最重要的通道的实现:
 ① FileChannel:从文件中读写数据
 ② DatagramChannel:能通过UDP读写网络中的数据
 ③ SocketChannel:能通过TCP读写网络中的数据
 ④ ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

这里写图片描述

(2)Buffer(缓冲区):用于和NIO通道进行交互,数据是从通道读入缓冲区,从缓冲区写入到通道中的

这里写图片描述

使用Buffer读写数据一般遵循以下四个步骤:
 ① 写入数据到Buffer
 ② 调用 flip()方法
 ③ 从Buffer中读取数据
 ④ 调用 clear()方法或者 compact()方法

  向Buffer写入数据,当要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式,读取之前写入到buffer的所有数据。一旦数据读取完毕,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据,任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
  
(2.1)Buffer的capacity、position和limit
 ① capacity:不管Buffer处于读模式还是写模式,其返回缓冲区的容量
 ② position:初始值为0,最大值为capacity-1 。写数据到Buffer时,表示当前位置,没插入一条数据,向前移动一个单元;读取数据时,从某个位置读取数据,当Buffer切换为读模式时,position会被重置为0,读取一条数据时,会移动到下一个可读的位置。
 ③ limit:在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
 Buffer的分配,要想获得一个Buffer对象首先要进行分配,如分配48字节capacity的ByteBuffer:ByteBuffer buf = ByteBuffer.allocate(48),分配一个可存储1024个字符的CharBuffer:CharBuffer buf = CharBuffer.allocate(1024);

向Buffer写数据,两种方式:
 ① 从Channel写到Buffer
  int bytesRead = inChannel.read(buf); //read into buffer
 ② 通过Buffer的put()方法写到Buffer里
  buf.put(127);
 
从Buffer中读数据,两种方式:
 ① 从Buffer读取数据到Channel
  int bytesWritten = inChannel.write(buf);
 ② 使用get()方法从Buffer中读取数据
  byte aByte = buf.get();

rewind()方法
Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据。limit保持不变,仍然表示能从Buffer中读取多少个元素(byte、char等)
  
mark()与reset()方法
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position。

buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing. 
buffer.reset();  //set position back to mark.
  • 1
  • 2
  • 3

【1】FileChannel
  用于读取、写入、映射和操作文件的通道,在现版本中,可从现有的 FileInputStream、FileOutputStream 或 RandomAccessFile 对象获得文件通道,方法是调用该对象的 getChannel 方法,这会返回一个连接到相同底层文件的文件通道。

public class GetFileChannel {
    public static void main(String[] args) throws Exception {
        FileChannel fileChannel = new FileOutputStream("data.txt").getChannel();

        // 将 byte 数组包装到缓冲区中,并将数据写入通道(文件)
        fileChannel.write(ByteBuffer.wrap("FileOutputStream--FileChannel Test".getBytes())); 
        fileChannel.close(); // 关闭此通道

        // 向文件末尾增加数据  rw读写  r只读
        fileChannel = new RandomAccessFile("data.txt", "rw").getChannel();

        // 调用position()方法获取FileChannel的当前位置,调用position(long pos)方法设置FileChannel的当前位置
        // FileChannel实例的size()方法将返回该实例所关联文件的大小
        fileChannel.position(fileChannel.size()); //  设置此通道的文件位置,末尾
        fileChannel.write(ByteBuffer.wrap("RandomAccessFile--FileChannel Test".getBytes()));
        fileChannel.close();

        // FileChannel.force(boolean metaData)方法将通道里尚未写入磁盘的数据强制写到磁盘上
        // true 表示是否同时将文件元数据(权限信息等)写到磁盘上
        // 读取文件
        fileChannel = new FileInputStream("data.txt").getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024); // 分配一个缓冲对象
        fileChannel.read(buffer); // 将字节序列从此通道读入给定的缓冲区,返回读取的字节数
        buffer.flip(); // 切换到读模式,让别人做好读取字节的准备
        while(buffer.hasRemaining()){
            System.out.print((char)buffer.get());
        }   
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

  上述代码中,在读取通道数据并输出时,使用(char)buffer.get()每次读取一个字节,然后强制转换为char类型(这种方法显然比较原始),如果不强制转换,将会是乱码,这是因为ByteBuffer 存储的是普通的字节,为了把它们转换成字符,有两种方式可以解决:① 在输入它们的时候对其进行编码;② 从缓冲器输出时对它们进行解码
  从api文档中可以看到CharBuffer有一个toString()方法可以返回缓冲器包含的所有的字符,而ByteBuffer可以调用asCharBuffer()方法作为 char 类型缓冲区

public static void main(String[] args) throws Exception {
    final int BSIZE = 1024;
    FileChannel fileChannel = new RandomAccessFile("data.txt", "rw").getChannel();
    fileChannel.write(ByteBuffer.wrap("some text".getBytes()));
    fileChannel.close();
    ByteBuffer buffer = ByteBuffer.allocate(BSIZE);
    fileChannel = new FileInputStream("data.txt").getChannel();
    fileChannel.read(buffer);
    buffer.flip();
    // 在这里直接调用asCharBuffer()显示不行输出乱码
    System.out.println(buffer.asCharBuffer());
    buffer.rewind(); // 将position设回0,可以重读Buffer中的所有数据

    /** 1) 在输出之前进行解码 Decoding**/
    // 获取系统编码,使用java.nio.charset.Charset进行解码
    // decode(ByteBuffer bb) 返回一个CharBuffer对象       
    String encoding = System.getProperty("file.encoding");
    System.out.println("使用 " + encoding +" 解码输出:" + Charset.forName(encoding).decode(buffer));

    /** 2) 在写入通道之前进行编码,这样输出才有意义 **/
    fileChannel = new FileOutputStream("data1.txt").getChannel();
    // 使用 UTF-8 写入
    fileChannel.write(ByteBuffer.wrap("some text2".getBytes("UTF-16BE")));
    fileChannel.close();
    // 读取
    fileChannel = new FileInputStream("data1.txt").getChannel();
    buffer.clear();
    fileChannel.read(buffer);
    buffer.flip();
    System.out.println(buffer.asCharBuffer());// 正常输出

    /** 3) 在写入通道使用buffer.asCharBuffer() **/
    fileChannel = new FileOutputStream("data3.txt").getChannel();
    buffer = ByteBuffer.allocate(24);
    buffer.asCharBuffer().put("some text3");
    fileChannel.write(buffer);
    fileChannel.close();
    // 读取
    fileChannel = new FileInputStream("data3.txt").getChannel();
    buffer.clear();
    fileChannel.read(buffer);
    buffer.flip();
    System.out.println(buffer.asCharBuffer());// 正常输出
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

猜你喜欢

转载自blog.csdn.net/hukaijun/article/details/80826458