Java 的 IO 很复杂?用思路带领你去battle他!

Java中,是通过 处理IO的,这种处理模式称为 IO流,IO流是一种顺序读写数据的模式。

你可以想象它是一根水管,数据就像水一样, 起点—终点 可互相流动。

1.1、流的特点:

  1. 先进先出:最先写入输出流的数据最先被输入流读取到。

  2. 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)

  3. 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

1.2、IO流的分类

1.1.1、按方向分

按数据流的方向分为 输入流、输出流,是相对内存来说的。

  • 输入流:从外部(数据源)把数据输入到程序(内存)。

  • 输出流:把程序的数据(内存)输出到外部(数据源)。

1.1.2、按处理数据类型分

按处理的数据类型可分为 字节流、字符流

1字符 = 2字节 、 1字节(byte) = 8位(bit)

  • 字节流:每次读 (写)一个字节,当传输的资源文件有中文时,就会出现乱码,读写的单位是byte,在InputStream/OutputStream中单向流动

  • 字符流:每次读取(写出)两个字节,有中文时,使用该流就可以正确传输显示中文,读写的单位是char,在Reader/Writer中单向流动

字节流和字符流的原理是相同的,只不过处理的单位不同而已。后缀是Stream是字节流,而后缀是Reader,Writer是字符流。

为什么要有字符流?

Java中字符是采用Unicode标准,Unicode 编码中,一个英文为一个字节,一个中文为两个字节。但是编码不同,中文字符占的字节数不一样,而在UTF-8编码中,一个中文字符是3个字节。

如果统一使用字节流处理中文,因为读写是一个字节一个字节,这样就会对中文字符有影响,就会出现乱码。

为了更方便地处理中文这些字符,Java就推出了字符流。

字节流和字符流的其他区别:

  1. 字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。

用一句话说就是:字节流可以处理一切文件,而字符流只能处理纯文本文件。

  1. 字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。

1.1.3、按功能分

按功能不同分为 节点流、处理流

  • 节点流:以从或向一个特定的地方(节点)读写数据。如FileInputStream

  • 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装.

1.1.4、按有无缓冲分

还有一种流是缓冲流,区别于没有缓冲的流。

因为程序和内存交互很快,而程序和磁盘交互是很慢的,这样会导致程序出现性能问题。

为了减少程序与磁盘的交互,是提升程序效率,引入了缓冲流

普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。

有缓冲的流,类名前缀是带有Buffer的,比如BufferedInputStreamBufferedReader


2、Java IO 流对象详解


以上说了这么多流,看起来很复杂,但其实只需要记住以下四种流即可:

这四个都是抽象类,都位于 java.io 包目录。

我们平时使用流去处理数据,都是通过这四个流的子类展开的

挑一些常用的放在下面一一讲解。

2.1、InputStream ——字节流输入流

InputStream 这个抽象类是表示以上输入字节流的所有类的超类(父类)。

InputStream 中的三个基本的读方法:

  • abstract int read() :读取一个字节数据,并返回读到的数据,如果返回 -1,表示读到了输入流的末尾。

  • int read(byte[] b) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。

  • int read(byte[] b, int off, int len) :将数据读入一个字节数组,同时返回实际读取的字节数。如果返回 -1,表示读到了输入流的末尾。off 指定在数组 b 中存放数据的起始偏移位置;len 指定读取的最大字节数。

read()方法 如果已读到末尾,返回 -1表示不能继续读取了。

InputStream 的子类有:

  • ByteArrayInputStream

  • FileInputStream

  • FilterInputStream

  • PushbackInputStream

  • DataInputStream

  • BufferedInputStream

  • LineNumberInputStream

  • ObjectInputStream

  • PipedInputStream

  • SequenceInputStream

  • StringBufferInputStream

这么多子类不需要每一个都记住,只需要记住两个:

FileInputStream

FileInputStream是文件字节输入流,就是对文件数据以字节的方式来处理,如音乐、视频、图片等。

BufferedInputStream

使用方式基本和FileInputStream一致。

BufferedInputStream有一个内部缓冲区数组,一次性读取较多的字节缓存起来,默认读取defaultBufferSize = 8192,作用于读文件时可以提高性能。

2.2、OutputStream——字节输出流

OutputStream 是相对 InputStream 的,既然有输入就有输出。OutputStream 这个抽象类是表示以上输出字节流的所有类的超类(父类)。

OutputStream 中的三个基本的写方法:

  • abstract void write(int b):往输出流中写入一个字节。

  • void write(byte[] b) :往输出流中写入数组b中的所有字节。

  • void write(byte[] b, int off, int len) :往输出流中写入数组 b 中从偏移量 off 开始的 len 个字节的数据。

其它重要方法:

  • void flush() :刷新输出流,强制缓冲区中的输出字节被写出。

  • void close() :关闭输出流,释放和这个流相关的系统资源。

OutputStream 的子类有:

  • ByteArrayOutputStream

  • FileOutputStream

  • FilterOutputStream

  • BufferedOutputStream

  • DataOutputStream

  • PrintStream

  • ObjectOutputStream

  • PipedOutputStream

StringBufferInputStream 和 StringBufferInputStream 已经过时了,这里不介绍了

FileOutputStream、BufferedOutputStream 和 FileInputStream、BufferedInputStream 是相对的。

2.3、Reader——字符输入流

Reader 是所有的输入字符流的父类,它是一个抽象类。

常见的子类有:

  • BufferedReader

  • LineNumberReader

  • CharArrayReader

  • FilterReader

  • PushbackReader

  • InputStreamReader

  • FileReader

  • PipedReader

  • StringReader

总结:

  1. BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它 Reader 对象。

  2. InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。

Reader 基本的三个读方法(和字节流对应):

(1) public int read() throws IOException; 读取一个字符,返回值为读取的字符。

(2) public int read(char cbuf[]) throws IOException; 读取一系列字符到数组 cbuf[]中,返回值为实际读取的字符的数量。

(3) public abstract int read(char cbuf[],int off,int len) throws IOException; 读取 len 个字符,从数组 cbuf[] 的下标 off 处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现。

2.4、Writer——字符输出流

Writer 是所有的输出字符流的父类,它是一个抽象类。

常见的子类有:

  • BufferedWriter

  • CharArrayWriter

  • FilterWriter

  • OutputStreamWriter

  • FileWriter

  • PipedWriter

  • PrintWriter

  • StringWriter

总结:

  1. OutputStreamWriter 是 OutputStream 到 Writer 转换的桥梁,它的子类 FileWriter 其实就是一个实现此功能的具体类。

  2. BufferedWriter 是一个装饰器为 Writer 提供缓冲功能。

writer 的主要写方法:

  1. public void write(int c) throws IOException; //写单个字符

  2. public void write(char cbuf[]) throws IOException; //将字符数组 cbuf[] 写到输出流 。

  3. public abstract void write(char cbuf[],int off,int len) throws IOException; //将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流 。

  4. public void write(String str) throws IOException; //将字符串str中的字符写入输出流 。

  5. public void write(String str,int off,int len) throws IOException; //将字符串 str 中从索引 off 开始处的 len 个字符写入输出流 。


3、使用方法


3.1、FileOutputStream写文件、FileInputStream读文件

分别为 单个字节写、字节数字写、单个字节读取、字节数组读取、一次性读取:

public class OutputStreamTest {

public static void main(String[] args) throws IOException {

writeFile(); //单个字节写、字节数字写

readFile1();//单个字节读取

readFile2();//字节数组读取

readFile3();//一次性读取

}

static void writeFile() throws IOException {

//1、第一种方法写,单个字节写

//会自动创建文件,目录不存在会报错, true 表示 追加写,默认是false

FileOutputStream fileOutputStream = new FileOutputStream(“F:\hello.txt”, false);

//往文件里面一个字节一个字节的写入数据

fileOutputStream.write((int) ‘H’);

fileOutputStream.write((int) ‘a’);

fileOutputStream.write((int) ‘C’);

//2、第二种方法写 字节数组写

String s = " HelloCoder";

//入文件里面一个字节数组的写入文件,文件为UTF_8格式

fileOutputStream.write(s.getBytes(StandardCharsets.UTF_8));

//刷新流

fileOutputStream.flush();

//关闭流

fileOutputStream.close();

}

static void readFile1() throws IOException {

//1、第一种读的方法,但字节读

System.out.println(“------一个字节读------”);

//传文件夹的名字来创建对象

FileInputStream fileInputStream = new FileInputStream(“F:\hello.txt”);

int by = 0;

//一个字节一个字节的读出数据

while ((by = fileInputStream.read()) != -1) {

System.out.print((char) by);

}

//关闭流

fileInputStream.close();

}

static void readFile2() throws IOException {

//2、第二种读的方法,字节数组读

System.out.println();

System.out.println(“------字节数组读------”);

FileInputStream fileInputStream = new FileInputStream(“F:\hello.txt”);

//通过File对象来创建对象

fileInputStream = new FileInputStream(new File(“F:\hello.txt”));

int by = 0;

byte[] bytes = new byte[10];

//一个字节数组的读出数据,高效

while ((by = fileInputStream.read(bytes)) != -1) {

for (int i = 0; i < by; i++) {

System.out.print((char) bytes[i]);

}

}

//关闭流

fileInputStream.close();

}

static void readFile3() throws IOException {

//3、第三种读方法,一次性读

System.out.println();

System.out.println(“------一次性读文件------”);

FileInputStream fileInputStream = new FileInputStream(“F:\hello.txt”);

fileInputStream = new FileInputStream(new File(“F:\hello.txt”));

//一次性读文件

int iAvail = fileInputStream.available();

int by = 0;

byte[] bytesAll = new byte[iAvail];

while ((by = fileInputStream.read(bytesAll)) != -1) {

for (int i = 0; i < by; i++) {

System.out.print((char) bytesAll[i]);

}

}

fileInputStream.close();

}

}

输出:

------一个字节读------

HaC HelloCoder

------字节数组读------

HaC HelloCoder

------一次性读文件------

HaC HelloCoder

这里介绍了三种方法读一个文件,详细的介绍都写在了注释里。

⚠️ 字符串如果包含中文,就会出现乱码,这是因为FileOutputStream是字节流,将文本按字节写入。

3.2、FileWriter写文件、FileReader读文件

分别为 字符串写、单字符读、字符数组读:

public class ReaderTest {

public static void main(String[] args) throws IOException {

write(); //字符串写

read1();//

read2();//

}

static void write() throws IOException {

FileWriter fileWriter = new FileWriter(“F:\Hello1.txt”);

//为防止乱码,可以这样写,字符流和字节流互转

// Writer fileWriter = new BufferedWriter(new OutputStreamWriter(

// new FileOutputStream(“F:\Hello1.txt”), StandardCharsets.UTF_8));

fileWriter.write(“今天打工你不狠,明天地位就不稳\n” +

“今天打工不勤快,明天社会就淘汰”);

// 如果没有刷新,也没有关闭流的话 数据是不会写入文件的

fileWriter.flush();

fileWriter.close();

}

static void read1() throws IOException {

System.out.println(“------一个一个char读-------”);

FileReader fileReader = new FileReader(“F:\Hello1.txt”);

int ch = 0;

String str = “”;

//一个一个char读

while ((ch = fileReader.read()) != -1) {

str += (char) ch;

}

System.out.println(str);

}

static void read2() throws IOException {

System.out.println(“------char数组[]读-------”);

FileReader fileReader = new FileReader(new File(“F:\Hello1.txt”));

int len = 0;

char[] chars = new char[10];

while ((len = fileReader.read(chars)) != -1) {

//这种读有误

// System.out.print(new String(chars));

System.out.print((new String(chars, 0, len)));

}

fileReader.close();

}

}

输出:

------一个一个char读-------

今天打工你不狠,明天地位就不稳

今天打工不勤快,明天社会就淘汰

------char数组[]读-------

今天打工你不狠,明天地位就不稳

今天打工不勤快,明天社会就淘汰

FileWriterFileReader 可以用来读写一个含中文字符的文件。

注意点:

1、流转换

// Writer fileWriter = new BufferedWriter(new OutputStreamWriter(

// new FileOutputStream(“F:\Hello1.txt”), StandardCharsets.UTF_8));

这里其实是把字节流转换为字符流,用来解决乱码。

2、读的位置

这里的写法需要注意,因为这里读写是一次性读10个char类型的字符,如果换成以下

int len = 0;

char[] chars = new char[10];

while ((len = fileReader.read(chars)) != -1) {

//不能这样写

System.out.print(new String(chars));

//System.out.print((new String(chars, 0, len)));

}

则输出:

------char数组[]读-------

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

image.png

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

image.png

  • Kafka的集群
  • 第一个Kafka程序
  • image.png

afka的生产者

image.png

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

image.png

image.png

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

image.png

  • Kafka实战之削峰填谷

image.png

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
4671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

Kafka实战笔记

关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图

[外链图片转存中…(img-dzpO4bdg-1712110807859)]

  • Kafka入门
  • 为什么选择Kafka
  • Karka的安装、管理和配置

[外链图片转存中…(img-P9X9JqBd-1712110807859)]

  • Kafka的集群
  • 第一个Kafka程序
  • [外链图片转存中…(img-D0fu3S2c-1712110807859)]

afka的生产者

[外链图片转存中…(img-wo9vfavj-1712110807860)]

  • Kafka的消费者
  • 深入理解Kafka
  • 可靠的数据传递

[外链图片转存中…(img-jXtQjOH8-1712110807860)]

[外链图片转存中…(img-YrKtbWX0-1712110807860)]

  • Spring和Kalka的整合
  • Sprinboot和Kafka的整合
  • Kafka实战之削峰填谷
  • 数据管道和流式处理(了解即可)

[外链图片转存中…(img-qHnKog7d-1712110807861)]

  • Kafka实战之削峰填谷

[外链图片转存中…(img-ClrwF4oh-1712110807861)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值