Java笔记:流I/O

Posted arseneyao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java笔记:流I/O相关的知识,希望对你有一定的参考价值。

一、基础知识

Java通过流实现I/O,流是一种可以产生或使用信息的抽象。

Java定义了两种类型的流:

  • 字节流:处理字节的输入和输出,例如读写二进制数据。
  • 字符流:处理字符的输入和输出。

在底层所有I/O仍然是面向字节的,字符流知识为处理字符提供更高效的方法。

 

二、字节流

FileInputStream和FileOutputStream提供了文件字节的读写能力。

技术分享图片技术分享图片
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        try (FileInputStream fis = new FileInputStream("file.txt")) {
            int available = fis.available();
            for (int i = 0; i < available; i++)
                System.out.print((char) fis.read());
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }

        System.out.println();
        try (FileInputStream fis = new FileInputStream("file.txt")) {
            int available = fis.available();
            byte[] arr = new byte[available];
            if (fis.read(arr) == available)
                for (byte b : arr)
                    System.out.print(b + " ");
            else System.out.println("Cannot read file");
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }

        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            String str = "Hello World";
            byte[] buf = str.getBytes();
            fos.write(buf);
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
    }
}
View Code

ByteArrayInputStream和ByteArrayOutputStream提供了字节数组的读写能力。

技术分享图片技术分享图片
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] arr = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(arr);
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            int available = in.available();
            for (int i = 0; i < available; i++)
                fos.write(in.read());
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
        in.reset();//重置流指针位置

        
        
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            out.write(arr);//写入缓冲区
        } catch (IOException exc) {
            System.out.println("Error writing to arr");
        }
        byte[] cpy = out.toByteArray();
        for (byte i : cpy)
            System.out.println(i);
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            out.writeTo(fos);
        } catch (IOException exc) {
            System.out.println("Error writing to file");
        }
        out.reset();
    }
}
View Code

BufferedInputStream和BufferedOutputStream通过缓冲减少实际读写次数来提高性能。

技术分享图片技术分享图片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] arr = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(arr);

        try (BufferedInputStream bis = new BufferedInputStream(in)) {
            for (int i = 0; i < 5; i++)
                System.out.print(bis.read() + " ");
            bis.mark(32);//向后读取32个以内字符标记有效
            while (bis.available() != 0)
                System.out.print(bis.read() + " ");

            System.out.println();
            bis.reset();//回到标记位置
            while (bis.available() != 0)
                System.out.print(bis.read() + " ");
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }


        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            BufferedOutputStream out = new BufferedOutputStream(fos);
            out.write(arr);//写入缓冲
            out.flush();//刷新缓冲
            out.close();
        } catch (IOException exc) {
            System.out.println("Cannot open file");
        }
    }
}
View Code

PushbackInputStream利用缓冲实现了回推。回推用于输入流,以允许读取字节然后将它们返回到流中。

技术分享图片技术分享图片
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PushbackInputStream;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        byte[] buf = str.getBytes();
        ByteArrayInputStream in = new ByteArrayInputStream(buf);
        try (PushbackInputStream pis = new PushbackInputStream(in)) {
            int c;
            while ((c = pis.read()) != -1) {//Hello.World
                if (c == ‘ ‘)//遇到空格则回推小数点
                    pis.unread(‘.‘);
                else System.out.print((char) c);
            }
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

SequenceInputStream允许连接多个InputStream对象。

技术分享图片技术分享图片
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

class InputStreamEnumerator implements Enumeration<FileInputStream> {
    private Enumeration<String> files;//Enumeration已被迭代器取代

    public InputStreamEnumerator(Vector<String> files) {
        this.files = files.elements();
    }

    @Override
    public boolean hasMoreElements() {
        return files.hasMoreElements();
    }

    @Override
    public FileInputStream nextElement() {
        try {
            return new FileInputStream(files.nextElement().toString());
        } catch (IOException exc) {
            return null;
        }
    }
}

class Solution {
    public static void main(String[] args) {
        Vector<String> files = new Vector<>();
        files.addElement("fileA.txt");
        files.addElement("fileB.txt");
        files.addElement("fileC.txt");

        InputStreamEnumerator ise = new InputStreamEnumerator(files);
        InputStream in = new SequenceInputStream(ise);

        try {
            int c;
            while ((c = in.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading file");
        } finally {
            try {
                in.close();
            } catch (IOException exc) {
                System.out.println("Error closing file");
            }
        }
    }
}
View Code

PrintStream使其它流能够方便地打印各种数据表示形式。

技术分享图片技术分享图片
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;

class Solution {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("file.txt")) {
            try (PrintStream ps = new PrintStream(fos)) {
                ps.printf("Hello %s", "World");
            }
        } catch (IOException exc) {
            System.out.println("Error opening file");
        }
    }
}
View Code

DataInputStream和DataOutputStream允许向流中写入或读取基本类型数据。

技术分享图片技术分享图片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("file.txt"))) {
            dos.writeBoolean(true);
            dos.writeInt(1);
            dos.writeChar(‘A‘);
            dos.writeDouble(1.0);
        } catch (IOException exc) {
            System.out.println("Error writing file");
        }

        try (DataInputStream dis = new DataInputStream(new FileInputStream("file.txt"))) {
            //读取顺序必须和写入顺序相同
            System.out.println(dis.readBoolean());
            System.out.println(dis.readInt());
            System.out.println(dis.readChar());
            System.out.println(dis.readDouble());
        } catch (IOException exc) {
            System.out.println("Error reading file");
        }
    }
}
View Code

 

三、字符流类

字符流提供了直接对Unicode字符进行操作的功能。

FileReader和FileWriter提供了文件字符的读写能力。

技术分享图片技术分享图片
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        try (FileReader fr = new FileReader("file.txt")) {
            int c;
            while ((c = fr.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading file");
        }
        
        try (FileWriter fw = new FileWriter("file.txt")) {
            String str = "Hello World";
            fw.write(str);
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

CharArrayReader和charArrayWriter提供了字符数组的读写能力。

技术分享图片技术分享图片
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.FileWriter;
import java.io.IOException;

class Solution {
    public static void main(String[] args) {
        String str = "Hello World";
        char[] arr = str.toCharArray();
        try (CharArrayReader cr = new CharArrayReader(arr)) {
            int c;
            while ((c = cr.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("Error reading");
        }

        try (CharArrayWriter cw = new CharArrayWriter()) {
            cw.write(arr);
            char[] cpy = cw.toCharArray();
            try (FileWriter fw = new FileWriter("file.txt")) {
                cw.writeTo(fw);
            }
            cw.reset();
        } catch (IOException exc) {
            System.out.println("Error writing");
        }
    }
}
View Code

BufferedReader和BufferedWriter通过缓冲减少实际读写次数提高性能。

技术分享图片技术分享图片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
            int c;
            for (int i = 0; (c = br.read()) != -1; i++) {
                if (i == 5) br.mark(32);
                System.out.print((char) c);
            }
            br.reset();
            while ((c = br.read()) != -1)
                System.out.print((char) c);
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
        
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("file.txt"))) {
            bw.write("Hello World");
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

PushbackReader允许将字符返回到输入流。

技术分享图片技术分享图片
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;

class Solution {
    public static void main(String[] args) {
        try (PushbackReader pr = new PushbackReader(new FileReader("file.txt"))) {
            int c;
            for (int i = 0; (c = pr.read()) != -1; i++) {
                if (i % 2 == 0)
                    pr.unread(‘ ‘);
                System.out.print((char) c);
            }
        } catch (IOException exc) {
            System.out.println("IO exception caught");
        }
    }
}
View Code

PrintWriter本质上是PrintStream的面向字符版本,可输出内容到控制台。

技术分享图片技术分享图片
import java.io.PrintWriter;

class Solution {
    public static void main(String[] args) {
        try (PrintWriter pw = new PrintWriter(System.out)) {
            pw.write("Hello World");
            pw.flush();
        }
        try (PrintWriter pw = new PrintWriter(System.out, true)) {//自动刷新
            pw.println("Hello World");
        }
    }
}
View Code

Console的主要目的是简化与控制台的交互。

技术分享图片技术分享图片
import java.io.Console;

class Solution {
    public static void main(String[] args) {
        Console console = System.console();
        if (console != null) {
            String str = console.readLine();
            console.printf(str);
        }
    }
}
View Code


四、文件系统

File类用于描述文件本身的属性,可获取和操作文件关联的信息。例如权限、时间、日期及目录路径,还可以浏览子目录。

技术分享图片技术分享图片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        File fileA = new File("/");//目录路径
        File fileB = new File("/", "file.txt");//目录路径中的文件

        System.out.println(fileB.getName());
        System.out.println(fileB.getPath());
        System.out.println(fileB.getAbsolutePath());
        System.out.println(fileB.getParent());
        System.out.println(fileB.exists());
        System.out.println(fileB.canRead());
        System.out.println(fileB.canWrite());
        System.out.println(fileB.isDirectory());
        System.out.println(fileB.isFile());
        System.out.println(fileB.isAbsolute());
        System.out.println(fileB.lastModified());
        System.out.println(fileB.length());
    }
}
View Code

获取目录文件字符串列表。

技术分享图片技术分享图片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        String dir = "/";
        File file = new File(dir);
        System.out.println(file.getName());
        if (file.isDirectory()) {
            String[] str = file.list();
            for (String s : str) {
                File sub = new File(dir + "/" + s);
                System.out.println(sub.getName());
            }
        }
    }
}
View Code

使用FilenameFilter接口过滤文件。

技术分享图片技术分享图片
import java.io.File;
import java.io.FilenameFilter;

class OnlyExt implements FilenameFilter {
    private String ext;

    OnlyExt(String ext) {
        this.ext = ext;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(ext);
    }
}

class Solution {
    public static void main(String[] args) {
        File file = new File("/");
        FilenameFilter onlyExt = new OnlyExt(".dat");
        String[] str = file.list(onlyExt);//指定过滤器
        for (String s : str)
            System.out.println(s);
    }
}
View Code

获取目录文件列表。

技术分享图片技术分享图片
import java.io.File;
import java.io.FilenameFilter;

class OnlyExt implements FilenameFilter {
    private String ext;

    OnlyExt(String ext) {
        this.ext = ext;
    }

    @Override
    public boolean accept(File dir, String name) {
        return name.endsWith(ext);
    }
}

class Solution {
    public static void main(String[] args) {
        File file = new File("/");
        FilenameFilter onlyExt = new OnlyExt(".dat");
        File[] list = file.listFiles(onlyExt);
        for (File dat : list)
            System.out.println(dat.getName());
    }
}
View Code

创建目录。

技术分享图片技术分享图片
import java.io.File;

class Solution {
    public static void main(String[] args) {
        File dir = new File("/Hello/World");
        if (dir.mkdir())//无法自动创建父目录 创建失败
            System.out.println(dir.getAbsolutePath());
        if (dir.mkdirs())//自动创建父目录
            System.out.println(dir.getAbsolutePath());//C:\Hello\World

    }
}
View Code

RandomAccessFile封装了随机访问文件的功能

技术分享图片技术分享图片
import java.io.*;

class Solution {
    public static void main(String[] args) {
        File file = new File("file.txt");
        try {
            RandomAccessFile rafA = new RandomAccessFile(file, "r");//只读模式
            RandomAccessFile rafB = new RandomAccessFile(file, "rw");//读写模式
            rafA.seek(5);//文件指针置为距离开头5字节的位置
            int c;
            while ((c = rafA.read()) != -1)
                System.out.print((char) c);

        } catch (FileNotFoundException exc) {
            System.out.println("File not found");
        } catch (IOException exc) {
            System.out.println("Error setting pointer");
        }
    }
}
View Code

 

五、串行化

串行化是将对象的状态写入字节流的过程,将程序的状态保存到永久性存储区域中。

只有实现了Serializable接口的类才能通过串行化功能进行保存和恢复。Serializable接口没有定义成员,仅用于指示类可以被串行化。如果一个类可以被串行化,那么其所有子类都是可以串行化的。声明为transient的变量不会被串行化。

ObjectOutputStream实现了ObjectOutput接口,可将对象写入流中。

ObjectInputStream实现了ObjectInput接口,可从流中读取对象。

技术分享图片技术分享图片
import java.io.*;

class Solution {
    static class MyClass implements Serializable {
        String str;
        transient Integer i;

        MyClass(String str, int i) {
            this.str = str;
            this.i = i;
        }
    }

    public static void main(String[] args) {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file.txt"))) {
            for (int i = 0; i < 10; i++)
                out.writeObject(new MyClass(Integer.toString(i), i));
        } catch (IOException exc) {
            System.out.println("Error writing file");
        }

        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file.txt"))) {
            MyClass[] arr = new MyClass[10];
            for (int i = 0; i < 10; i++)
                arr[i] = (MyClass) in.readObject();
            for (MyClass c : arr)
                System.out.println(c.str + " " + c.i);//i为null
        } catch (IOException | ClassNotFoundException exc) {
            System.out.println("Error reading file");
        }
    }
}
View Code

 

六、流接口

从集合获取流的方法及简单操作。

技术分享图片技术分享图片
import java.util.ArrayList;
import java.util.Optional;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        for (int i = 1000; i >= 0; i--)
            arrayList.add(i);

        Stream<Integer> stream = arrayList.stream();
        Optional<Integer> minVal = stream.min(Integer::compare);
        System.out.println(minVal.orElse(-1));

        Stream<Integer> sortedStream = arrayList.stream().sorted();
        Stream<Integer> oddStream = sortedStream.filter((i) -> i % 2 == 1);
        oddStream.forEach((i) -> System.out.println(i + " "));
    }
}
View Code

将流转换为值的操作称为缩减操作。流接口泛化了这种概念,提供了reduce方法,通过该方法可基于任意条件从流中获取值。

调用集合对象的parallelStream方法可获得并行流,并行流的运算可运算在多线程上。

技术分享图片技术分享图片
import java.util.LinkedList;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        for (int i = 1; i <= 10; i++)
            linkedList.add(i);

        Stream<Integer> stream = linkedList.parallelStream();
        int factorial = stream.reduce(1, (i, j) -> i * j);//指定运算单位元为1
        System.out.println(factorial);
    }
}
View Code

流的映射操作。

技术分享图片技术分享图片
import java.util.ArrayList;
import java.util.stream.Stream;

class Email {
    String name;
    String addr;

    Email(String name, String addr) {
        this.name = name;
        this.addr = addr;
    }
}

class Solution {
    public static void main(String[] args) {
        ArrayList<Email> arrayList = new ArrayList<>();

        arrayList.add(new Email("Durant", "[email protected]"));
        arrayList.add(new Email("Tompson", "[email protected]"));
        arrayList.add(new Email("Curry", "[email protected]"));

        Stream<Email> stream = arrayList.parallelStream();
        Stream<String> addrStream = stream.map((email) -> email.addr);
        addrStream.forEach((addr) -> System.out.println(addr));
    }
}
View Code

基本类型流。

技术分享图片技术分享图片
import java.util.ArrayList;
import java.util.stream.DoubleStream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 100; i++)
            arrayList.add(Math.random());

        DoubleStream stream = arrayList.stream().mapToDouble((i) -> i);
        stream.forEach((i) -> System.out.println(i));
    }
}
View Code

从流中获取集合。

技术分享图片技术分享图片
import java.util.ArrayList;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 100; i++)
            arrayList.add(Math.random());

        Stream<Double> stream = arrayList.stream();
        Set<Double> set = stream.collect(Collectors.toSet());
        System.out.println(set);
    }
}
View Code

使用流迭代器。

技术分享图片技术分享图片
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Spliterator;

class Solution {
    public static void main(String[] args) {
        ArrayList<Double> arrayList = new ArrayList<>();
        for (int i = 0; i < 10; i++)
            arrayList.add(Math.random());

        Iterator<Double> itr = arrayList.stream().iterator();
        while (itr.hasNext())
            System.out.println(itr.next());

        Spliterator splitr = arrayList.stream().spliterator();
        while (splitr.tryAdvance((i) -> System.out.println(i))) ;
    }
}
View Code

 


以上是关于Java笔记:流I/O的主要内容,如果未能解决你的问题,请参考以下文章

笔记:I/O流-字符集

笔记:I/O流-文件操作

Java I/O流学习笔记

Java学习笔记6.1.1 字节流 - 数据字节输入流与数据字节输出流

学习笔记-java IO流总结 转载

Java之I/O流