2、输入输出流

1、读写字节

1.1、read()方法

InputStream是一个抽象类,有一个抽象方法

     /**
     * Reads the next byte of data from the input stream. The value byte is
     * returned as an {@code int} in the range {@code 0} to
     * {@code 255}. If no byte is available because the end of the stream
     * has been reached, the value {@code -1} is returned. This method
     * blocks until input data is available, the end of the stream is detected,
     * or an exception is thrown.
     *
     * <p> A subclass must provide an implementation of this method.
     *
     * @return     the next byte of data, or {@code -1} if the end of the
     *             stream is reached.
     * @throws     IOException  if an I/O error occurs.
     */
 public abstract int read() throws IOException;

学习一个东西最好的方法就是读好源码和注释

Java文件操作会用到大量本地方法,都是C和操作系统交互的,我们能够用到的API,都是基于C的一个高级抽象。

read()方法读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1.

具体的输入流类,必须覆盖这个方法以提供适用的功能。例如:

  • FileInputStream类中,这个方法将从某个文件中读入一个字符(具体方法在JVM中,用C实现)
  • System.in(他是InputStream子类的一个预定义对象)却是从标注输入中读取信息,即控制台或者重定向文件。(考试常用,例如力扣,牛客网等)
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            System.out.println(scanner.next());
        }

    }

Scanner 如果有空要读透源码,他是考试常用工具,毕竟很多机考都是从控制台做输入输出。

清楚文件系统本质玩转InputStream和OutputStream,以后遇到的其他实现类,或者第三方的流自定义对象都万变不离其宗了。

InputStream还有一些非抽象的方法,可以读入一个字节数组,或者跳过大量的字节。而这些方法的本质却是调用的read()方法

(length | fromIndex | size) < 0 很有意思,实际项目中可以使用

        Objects.checkFromIndexSize(off, len, b.length);
         if ((length | fromIndex | size) < 0 || size > length - fromIndex)
         

    public static void main(String[] args) {

        PrintStream printStream = new PrintStream(System.out);
        printStream.println(123);

    }

1.2、write()方法

面向流编程,Java的很多对象定义都是成对的。

与InputStream的read()方法类似,OutputStream有对应的write()方法

/**
 * Writes the specified byte to this output stream. The general
 * contract for {@code write} is that one byte is written
 * to the output stream. The byte to be written is the eight
 * low-order bits of the argument {@code b}. The 24
 * high-order bits of {@code b} are ignored.
 * <p>
 * Subclasses of {@code OutputStream} must provide an
 * implementation for this method.
 *
 * @param      b   the {@code byte}.
 * @throws     IOException  if an I/O error occurs. In particular,
 *             an {@code IOException} may be thrown if the
 *             output stream has been closed.
 */
public abstract void write(int b) throws IOException;

write()方法向某个输出位置写入一个字节。

流的读写是阻塞模式的

1.3、API

InputStream

方法 参数 说明
public abstract int read() throws IOException; 从流中读取一个数据并返回该字节,遇到结尾返回-1
public int read(byte[] b) throws IOException byte[] b 读入一个字节数组,并返回实际读入的字节数,结尾返回-1,最大返回b.length
public int read(byte[] b, int off, int len) throws IOException b-数据读入的数组
off 第一个读入的字节应该被放置的位置,在b中的偏移量
len 读入字节的最大数量
读入一个字节数组,并返回实际读入的字节数,结尾返回-1
public long skip(long n) throws IOException n 跳过数量 输入流中跳过n个字节
public int available() throws IOException 返回在不阻塞的情况下可获取的字节数
public void close() throws IOException {} 关闭这个输入流
public synchronized void mark(int readlimit) {} readlimit 标记
public synchronized void reset() throws IOException
public boolean markSupported()

OutputStream

方法 参数 说明
public abstract void write(int b) throws IOException; b 写出一个字节的数据
public void write(byte b[]) throws IOException b
public void write(byte b[], int off, int len) throws IOException
public void close() throws IOException
public void flush() throws IOException

1.4、小结

public static void main(String[] args) {

    try (OutputStream out = new FileOutputStream(new File("D:\\workspace\\test\\test.txt"))) {
        for (byte b : "中国".getBytes(StandardCharsets.UTF_8)) {
            out.write(b);
        }
        out.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


    try (InputStream in = new FileInputStream(new File("D:\\workspace\\test\\test.txt"))) {
        while (true) {
            byte[] strByte = new byte[6];
            int t = in.read(strByte);
            if (t == -1) {
                break;
            }
            System.out.println(new String(strByte));
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


}
  • 字节流操作的是字节,以byte为单位,自身没有任何编码格式,所以写入的内容是乱码
  • 读写都是以byte为单位,可以是byte[],这才是常用方式
  • 语法糖自动关闭流是项目实用的真确姿势,原理是实现了Closeable

2、完整的流家族

2.1、InputStream 、OutputStream 字节处理器两个抽象方法结构

上面了解了最基本的流操作的几个方法,下面了解下Java为我们的IO到底设计了多少工具。

处理二进制,无编码格式的输入输出流家族图谱如下:

image

2.2、Reader和Writer两个文本处理器抽象类家族

对于Unicode文本,可以使用抽象类Reader和Writer的子类。

abstract int read()

abstract void write(int c)

read方法返回一个Unicode元码(0~65535之间的整数),碰到文件结尾返回 -1

write方法在被调用时,需要一个Unicode元码参数。

image

2.3、四个功能定义接口

IT领域处理的数据核心是字符,字符串起来起来就是字符串,这个玩意才是人能读懂的东西。

而不同的国家地区有不同的文字,为了接入计算机这个虚拟世界,就要设置各种五花八门的编码,编码技术是一个核心技术,需要去吃透。

除了上述图谱,Java附加了四个接口:

  • Colseable 扩展自java.lang.AutoCloseable,对于实现了colseable接口的对象可以try-with-resource自动关闭,至于到底怎么关闭,这是一个学问。
  • Flushed
  • Readable
  • Appendable

image

Colseable 接口 就一个抽象方法

这个方法到底怎么实现,就没那么简单了,目前只能理解为,底层为我们做了关闭,肯定要和操作系统内核打交道。

public void close() throws IOException;

Flushed接口也就只有一个抽象方法

void flush() throws IOException;

Reader接口也就只有一个抽象方法

public int read(java.nio.CharBuffer cb) throws IOException;

CharBuffer类拥有按顺序和随机地进行读写访问的方法,它表示一个内存中的缓冲区或者一个内存映像的文件,根本原因在于数据结构用的char[]和MemorySegmentProxy

Appendable接口有两个用于添加单个字符和字符序列的方法

Appendable append(char c) throws IOException;
Appendable append(CharSequence csq) throws IOException;
Appendable append(CharSequence csq, int start, int end) throws IOException;

CharSequence接口描述了一个char值序列的基本属性,string,CharBuffer,StringBuffer和StringBuilder都实现了它。

public static void main(String[] args) {
    CharSequence cs = "中国abc";
    cs.codePoints().forEach(value -> {
        System.out.println(value);
    });
    System.out.println(cs.charAt(1));

}

2.4、输入输出的组合过滤器

FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流。

文件流的构造器核心参数就是文件路径,也可以使用相对路径,例如jar获取手工读取某个配置文件

路径核心秘密在于相对路径和绝对路径,相对路径就要知道程序的工作目录

System.out.println(System.getProperty("user.dir"));
public static void main(String[] args) {

    try (OutputStream out = new FileOutputStream("D:\\workspace\\test\\test.txt")) {
        for (byte b : "中国".getBytes(StandardCharsets.UTF_8)) {
            out.write(b);
        }
        out.flush();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


    try (InputStream in = new FileInputStream("D:\\workspace\\test\\test.txt")) {
        while (true) {
            byte[] strByte = new byte[6];
            int t = in.read(strByte);
            if (t == -1) {
                break;
            }
            System.out.println(new String(strByte));
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


}

FileInputStream和FileOutputStream从家族看就是只能操作字节和字节数组。

byte t = (byte)in.read(strByte);

DataInputstream只能读入数据类型

public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream("D:\\workspace\\test\\test.txt");
    DataInputStream din = new DataInputStream(new BufferedInputStream(in));
    System.out.println(din.readLong());
}

FileInputStream核心API

方法 参数 说明
public FileInputStream(String name) throws FileNotFoundException name文件名 构造方法
public FileInputStream(File file) throws FileNotFoundException file用文件对象 构造方法

FileOutputStream核心API

方法 参数 说明
public FileOutputStream(String name) throws FileNotFoundException name文件名 构造方法
public FileOutputStream(String name, boolean append) throws FileNotFoundException append继续写 构造方法
public FileOutputStream(File file) throws FileNotFoundException 构造方法
public FileOutputStream(File file, boolean append) throws FileNotFoundException file用文件对象,append继续写 构造方法

FileInputStream核心API

方法 参数 说明
public BufferedInputStream(InputStream in) in 输入流 创建一个带缓冲区的输入流,带缓冲区的输入流从流中读入字符时不需要每次都对设备访问。当缓冲区空时,回向缓冲区读入一个新的数据库。
posted @ 2022-09-18 20:08  红尘过客2022  阅读(71)  评论(0编辑  收藏  举报