编程技术是改变世界的力量。
本站
当前位置:网站首页 > 后端语言 > 正文

吃透Java基础十二:IO java基础io全部流详解

gowuye 2024-04-04 11:54 11 浏览 0 评论

一、什么是IO流

Java中将输入输出抽象称为流,就好像水管,将两个容器连接起来。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流。

按数据来源(去向)分类:

  • 文件:FileInputStream、FileOutputStream、FileReader、FileWriter
  • 数组:字节数组(byte[]):ByteArrayInputStream、ByteArrayOutputStream
  • 字符数组(char[]):CharArrayReader、CharArrayWriter
  • 管道操作:PipedInputStream、PipedOutputStream、PipedReader、PipedWriter
  • 基本数据类型:DataInputStream、DataOutputStream
  • 缓冲操作:BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
  • 对象序列化反序列化:ObjectInputStream、ObjectOutputStream
  • 打印:PrintStream、PrintWriter
  • 转换:InputStreamReader、OutputStreWriter

根据处理数据类型不同分类

  • 字节流:数据流中最小的数据单元是字节。
  • 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

字节流读取单个字节,字符流读取单个字符,字节流用来处理二进制文件如:图片、MP3、视频文件等等,字符流用来处理文本文件,可以看做是特殊的二进制文件,使用了某种编码,人可以阅读。

根据数据流向不同分类

  • 输入流: 程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道
  • 输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。

二、源码分析

IO 类虽然很多,但最基本的是 4 个抽象类:InputStream、OutputStream、Reader、Writer。最基本的方法也就是一个读 read() 方法、一个写 write() 方法。方法具体的实现还是要看继承这 4 个抽象类的子类,毕竟我们平时使用的也是子类对象。

字节输入流:InputStream

public abstract class InputStream implements Closeable {
 //读取一个字节数据,并返回读到的数据,如果返回-1,表示读到了输入流的末尾。
 public abstract int read() throws IOException;
 
 //将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
 public int read(byte b[]) throws IOException {
 return read(b, 0, b.length);
 }
 
 //将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
 //off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数。
 public int read(byte b[], int off, int len) throws IOException {
 if (b == null) {
 throw new NullPointerException();
 } else if (off < 0 || len < 0 || len > b.length - off) {
 throw new IndexOutOfBoundsException();
 } else if (len == 0) {
 return 0;
 }
 int c = read();
 if (c == -1) {
 return -1;
 }
 b[off] = (byte)c;
 int i = 1;
 try {
 for (; i < len ; i++) {
 c = read();
 if (c == -1) {
 break;
 }
 b[off + i] = (byte)c;
 }
 } catch (IOException ee) {
 }
 return i;
 }
 
 //在输入流中跳过n个字节,并返回实际跳过的字节数。
 public long skip(long n) throws IOException {
 long remaining = n;
 int nr;
 if (n <= 0) {
 return 0;
 }
 int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
 byte[] skipBuffer = new byte[size];
 while (remaining > 0) {
 nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
 if (nr < 0) {
 break;
 }
 remaining -= nr;
 }
 return n - remaining;
 }
 
 //返回在不发生阻塞的情况下,可读取的字节数。
 public int available() throws IOException {
 return 0;
 }
 //关闭输入流,释放和这个流相关的系统资源。
 public void close() throws IOException {}
 //标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,
 //可以使用 markSupport() 方法判断
 public synchronized void mark(int readlimit) {}
 //返回到上一个标记。
 public synchronized void reset() throws IOException {
 throw new IOException("mark/reset not supported");
 }
 //测试当前流是否支持mark和reset方法。如果支持,返回true,否则返回false。
 public boolean markSupported() {
 return false;
 }
}

字节输出流:OutputStream

public abstract class OutputStream implements Closeable, Flushable {
 //往输出流中写入一个字节。
 public abstract void write(int b) throws IOException;
 //往输出流中写入数组b中的所有字节。
 public void write(byte b[]) throws IOException {
 write(b, 0, b.length);
 }
 //往输出流中写入数组b中从偏移量off开始的len个字节的数据。
 public void write(byte b[], int off, int len) throws IOException {
 if (b == null) {
 throw new NullPointerException();
 } else if ((off < 0) || (off > b.length) || (len < 0) ||
 ((off + len) > b.length) || ((off + len) < 0)) {
 throw new IndexOutOfBoundsException();
 } else if (len == 0) {
 return;
 }
 for (int i = 0 ; i < len ; i++) {
 write(b[off + i]);
 }
 }
 //强制刷新,将缓冲中的数据写入
 public void flush() throws IOException {
 }
 //关闭输出流,释放和这个流相关的系统资源。
 public void close() throws IOException {
 }
}
```
**字符输入流Reader**
```java
public abstract class Reader implements Readable, Closeable {
 //读取字符到字符缓存中
 public int read(java.nio.CharBuffer target) throws IOException {
 int len = target.remaining();
 char[] cbuf = new char[len];
 int n = read(cbuf, 0, len);
 if (n > 0)
 target.put(cbuf, 0, n);
 return n;
 }
 //读取一个字符,返回值为读取的字符
 public int read() throws IOException {
 char cb[] = new char[1];
 if (read(cb, 0, 1) == -1)
 return -1;
 else
 return cb[0];
 }
 //读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量
 public int read(char cbuf[]) throws IOException {
 return read(cbuf, 0, cbuf.length);
 }
 //读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现
 abstract public int read(char cbuf[], int off, int len) throws IOException;
 //跳过指定长度的字符数量
 public long skip(long n) throws IOException {
 if (n < 0L)
 throw new IllegalArgumentException("skip value is negative");
 int nn = (int) Math.min(n, maxSkipBufferSize);
 synchronized (lock) {
 if ((skipBuffer == null) || (skipBuffer.length < nn))
 skipBuffer = new char[nn];
 long r = n;
 while (r > 0) {
 int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
 if (nc == -1)
 break;
 r -= nc;
 }
 return n - r;
 }
 }
 //告诉此流是否已准备好被读取。
 public boolean ready() throws IOException {
 return false;
 }
 //判断当前流是否支持标记流
 public boolean markSupported() {
 return false;
 }
 //标记读取位置,下次还可以从这里开始读取,使用前要看当前流是否支持,
 //可以使用 markSupport() 方法判断
 public void mark(int readAheadLimit) throws IOException {
 throw new IOException("mark() not supported");
 }
 //返回到上一个标记。
 public void reset() throws IOException {
 throw new IOException("reset() not supported");
 }
 //关闭输入流,释放和这个流相关的系统资源。
 abstract public void close() throws IOException;
}

字符输出流Writer

public abstract class Writer implements Appendable, Closeable, Flushable {
 //将整型值c的低16位写入输出流 
 public void write(int c) throws IOException {
 synchronized (lock) {
 if (writeBuffer == null){
 writeBuffer = new char[WRITE_BUFFER_SIZE];
 }
 writeBuffer[0] = (char) c;
 write(writeBuffer, 0, 1);
 }
 }
 //将字符数组cbuf[]写入输出流 
 public void write(char cbuf[]) throws IOException {
 write(cbuf, 0, cbuf.length);
 }
 //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流
 abstract public void write(char cbuf[], int off, int len) throws IOException;
 //将字符串str中的字符写入输出流 
 public void write(String str) throws IOException {
 write(str, 0, str.length());
 }
 //将字符串str 中从索引off开始处的len个字符写入输出流 
 public void write(String str, int off, int len) throws IOException {
 synchronized (lock) {
 char cbuf[];
 if (len <= WRITE_BUFFER_SIZE) {
 if (writeBuffer == null) {
 writeBuffer = new char[WRITE_BUFFER_SIZE];
 }
 cbuf = writeBuffer;
 } else { // Don't permanently allocate very large buffers.
 cbuf = new char[len];
 }
 str.getChars(off, (off + len), cbuf, 0);
 write(cbuf, 0, len);
 }
 }
 //追加写入一个字符序列
 public Writer append(CharSequence csq) throws IOException {
 if (csq == null)
 write("null");
 else
 write(csq.toString());
 return this;
 }
 //追加写入一个字符序列的一部分,从 start 位置开始,end 位置结束
 public Writer append(CharSequence csq, int start, int end) throws IOException {
 CharSequence cs = (csq == null ? "null" : csq);
 write(cs.subSequence(start, end).toString());
 return this;
 }
 //追加写入一个 16 位的字符
 public Writer append(char c) throws IOException {
 write(c);
 return this;
 }
 //强制刷新,将缓冲中的数据写入
 abstract public void flush() throws IOException;
 //关闭输出流,流被关闭后就不能再输出数据了
 abstract public void close() throws IOException;
}

三、运用场景

1、字节流与字符流的转换

  • InputStreamReader:将一个字节流中的字节解码成字符。
  • OutputStreamWriter:将写入的字符编码成字节后写入一个字节流。

字节流和字符流转换看一下例子就行了:

1. 创建一个文本文件:bobo.txt,然后写入内容:波波。

2. 用FileInputStream读出文本内容然后以字节数组的形式缓存在fileInputStream里面。

3. 用InputStreamReader 把fileInputStream缓存的字节数组读取到定义的charArray的字符数组里面,并且输出。

4. 用OutputStreamWriter把字符数组charArray里面的字符写到InputStreamReader的字节数组缓存起来,然后输出。

5. 输出结果如下图所示。



运行输出:



2、字节数组的输入输出

ByteArrayOutputStream :字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中。

ByteArrayInputStream :字节数组输入流在内存中创建一个字节数组缓冲区,从输入流读取的数据保存在该字节数组缓冲区中。



输出:

[1, 2, 3]

3、对象的输入输出

java.io.ObjectOutputStream是实现序列化的关键类,它可以将一个对象转换成二进制流通过write()方法写入到OutputStream输出流中。然后可以通过ObjectInputStream的read()方法将对应的InputStream二进制流还原成对象。



输出

false
list:[1, 2, 3] list1:[1, 2, 3]

4、控制台的输入输出

  • System.out:标准的输出流,out是System类里面的静态变量,其类型是PrintStream,PrintStream继承于FilterOutputStream,也是输出类OutputStream的子类。
  • System.in:标准的输入流,in是System类里面的静态变量,其类型是

InputStream。

我们常用的System.out和System.in作为控制台的输出与输入。

单个字符输入



整行字符输入:



运行输出:

请输入一行字符:
bobo
输入的内容为:bobo

5、二进制文件的输入输出



输出:



6、文本文件的输入输出

用FileWriter和FileReader读取文本文件:



输出:



使用字节流和字符流的转换类 InputStreamReader 和 OutputStreamWriter 可以指定文件的编码,使用 Buffer 相关的类来读取文件的每一行。



运行输出:

编码方式为:GBK
波波测试文件的读写

相关推荐

嵌入式C语言中常量的应用实例(嵌入式c语言中常量的应用实例是什么)

常量,我们都知道,就是数值保持不变的量。在C语言中,常量一旦初始化了,它的值将在整个程序运行周期内,不允许发生任何变化。常量与变量是相对的,我们实际项目中经常会用到它。定义常量的两种方式C语言中主要有...

C语言编程基础知识汇总学习,适合初学者!更新常量知识

(二)整型常量整型常量有3种形式:十进制整型常量、八进制整型常量和十六进制整型常量。(注意:c语言中没有直接表示二进制的整型常量,在c语言源程序中不会出现二进制。)书写方式如下:十进制整型常量:123...

【C语言】第二章第六节:字符串常量

第二章第六节:字符串常量。下表C语言中的常用转义字符。·字符形式功能:ASCIl码(十进制形式)。→\t水平制表(横向跳格:跳到下一个tab位置)。→\b退格8。→\r回车(不换行,光标移到本行行首)...

「GCTT 出品」Go 系列教程——5. 常量

这是我们Golang系列教程的第五篇。定义在Go语言中,术语”常量”用于表示固定的值。比如5、-89、IloveGo、67.89等等。看看下面的代码:varaint=50v...

每日C语言-常量指针、指针常量、指向常量的指针常量

一、常量指针1)什么是常量指针?通过该指针不可以修改其所指向存储单元中的值指针本身即地址可以被修改2)定义:类型说明符const*指针变量;类型说明符表示指针所指向存储单元中的值得数据类型指针...

C语言-符号常量、常变量、变量之我见

更新内容:新增音频。音频和文章一起更配oHello,大家好,又和大家见面了~~相信很多朋友们听了C语言的“符号常量”、“常变量”、“变量”后还是对这三者一脸懵逼吧。不管老师怎么歇斯底里地讲解,同学们迷...

零基础带你学习C语言:四:探索常量与变量

前言常量与变量学习;一:分析:short、float、long类型#include<stdio.h>intmain(){shortage=18;floatweight=12...

C语言中是如何定义常量的?那定义字符串呢?

常量有整型常量、浮点型常量、字符型常量及字符串常量。‘常量定义是指定义符号常量,用一个标识符来代表一个常量,通过宏定义预处理指令来实现。常量的定义:#definecount60这就定义了一个常量...

C语言符号常量的优点,会是那几点?

符号常量是一个常量,是不变量,所以,在编译的时候,就把符号常量出现的地方,替换为符号常量对应的常量。符号常量一般用户定义一个全局使用的数据,而且要改变该数据的时候,只需要改变符号常量的值,代码中引用符...

嵌入式开发- C语言数据类型-常量(c语言嵌入式是干嘛的)

基本数据类型的常量-掌握**整型常量:**常量是指在程序运行期间其数值不发生变化的数据。整型常量通常简称为整数整数可以是十进制数、八进制数、十六进制数八进制06334十六进制0xd1...

c语言解剖课:只读变量、常量、字面量傻傻分不清?

写在前面本篇主题的缘起,是因为一个计算机专业的大学生在和我讨论c语言问题时,说const常量如何如何,我说变量被const修饰了,还是变量,不是“常量”。他给了我一个截图:他说大模型都是这样回答的,变...

C/C++编程笔记:C数组、字符串常量和指针!三分钟弄懂它

想弄懂C语言中数组和指针的关系吗?这篇文章就占据你三分钟时间,看完你肯定会有收获!数组数组声明为数据类型名称[constant-size],并将一个数据类型的一个或多个实例分组到一个可寻址的位...

C语言入门到精通【第008讲】——C语言常量

C语言常量常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量。常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量,或字符串字面值,也有枚举常量。常量就像是常规的变量,只不过常...

这是C语言无法修改得东西,C语言基础教程之常量解析

常量是指程序在执行期间不会改变的固定值。这些固定值也称为文字。常量可以是任何基本数据类型,如整数常量,浮点常量,字符常量或字符串文字,还有枚举常量。常量被视为常规变量,除了它们的值在定义后无法修改。整...

C语言中的单精度、双精度、常量等都有什么意思?

刚接触C语言时,对于常量,变量,浮点,单精度,双精度等问题的理解,大都很模糊不清,其实在程序运行过程中,其值不能改变的量称为常量。如12、0、-3为整型常量,4.6、-1.23为实型常量,'a'、'...

取消回复欢迎 发表评论: