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

常见的子类有:

  • 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数组[]读-------

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

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

可以看到输出不正确,因为一次性读10个char,

第一次读的是 今天打工你不狠,明天

第二次读的是 地位就不稳\n今天打工

第三次读的是 不勤快,明天社会就淘

第四次输出是 汰勤快,明天社会就淘 ,其实这一次它只读了 一个字符,其中 勤快,明天社会就淘 是上一个数组的内容,因为它是已存在在数组的旧数据。

所以需要new String(chars, 0, len)len 是这次读到的字符长度,只需要截取这次的字符即可。


以上这两个例子中,还需要注意的几个地方:

1、只有在写文件的时候才需要flush()方法,而读是不需要的。

2、读、写 完毕都需要调用close() 方法关闭流。

3、单个字节、字符读写效率较慢,建议使用字节、字符数组读取。

3.3、BufferedInputStream、BufferedOutputStream 缓冲字节流

BufferedInputStream 是带缓冲区的,在复制、移动文件操作会快一点。

建议使用缓冲字节流这不是普通字节流,但构造方法入参还是InputStream和OutputStream。

Java使用IO 读取文件时,会进入核心态,在调用驱动进行IO,本身就会缓存在系统级别的,当你第二次读取时,会由用户态进入核心态,读取系统缓存。BufferedInputStream就一次性读取较多,缓存起来。

这样下次就从缓存中读,而不用在用户态和核心态之间切换,从而提升效率。

eg:

public class InputStrem与BufferenInputStream复制文件 {

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

useInputStreamCopyFile(); //缓冲流复制文件

useBufferenInputStream(); //普通流复制文件

}

static void useInputStreamCopyFile() throws IOException {

File file = new File(“F:\杨超越.png”);

InputStream is = new FileInputStream(file);

File file2 = new File(“F:\杨超越_copy.png”);

OutputStream os = new FileOutputStream(file2);

int len = 0;

byte[] bytes = new byte[1024];

while ((len = is.read(bytes)) != -1) {

os.write(bytes);

}

is.close();

os.close();

}

static void useBufferenInputStream() throws IOException {

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(“F:\杨超越.png”));

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(“F:\杨超越_copy2.png”));

int len = 0;

byte[] bytes = new byte[1024];

while ((len = bis.read(bytes)) != -1) {

bos.write(bytes, 0, len);

}

bos.close();

bis.close();

}

}

结果输出:

3.4、BufferedReader、BufferedWriter 字符缓冲流

BufferedReader 有一个好处,就是它提供了readline()newLine()方法,可以按行读取文件

eg:

public class BufferedReaderTest {

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

useInputStreamCopyFile(); //这种方法适用于任何文件

//下面两种方法copy的文件变大了,因为是使用字符流处理的

useBufferedReaderCopyFile(); //这种方法只适用于字符文件

useFileReaderCopyFile(); //这种方法一步到位,只适用于字符文件

}

static void useInputStreamCopyFile() throws IOException {

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

InputStream is = new FileInputStream(file);

File file2 = new File(“F:\Hello1_copy1.txt”);

OutputStream os = new FileOutputStream(file2);

int len = 0;

byte[] bytes = new byte[1024];

while ((len = is.read(bytes)) != -1) {

os.write(bytes, 0, len);

}

is.close();

os.close();

}

static void useBufferedReaderCopyFile() throws IOException {

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

InputStream is = new FileInputStream(file);

Reader reader = new InputStreamReader(is);

//创建字符流缓冲区,BufferedReader 的构造入参是一个 Reader

BufferedReader bufferedReader = new BufferedReader(reader);

File file2 = new File(“F:\Hello1_copy2.txt”);

OutputStream os = new FileOutputStream(file2);

Writer writer = new OutputStreamWriter(os);

//创建字符流缓冲区,BufferedWriter 的构造入参是一个 Writer

BufferedWriter bufferedWriter = new BufferedWriter(writer);

String line = null;

//readLine()方法 是根据\n 换行符读取的

while ((line = bufferedReader.readLine()) != null) {

bufferedWriter.write(line);

//这里要加换行

bufferedWriter.newLine();

}

bufferedReader.close();

bufferedWriter.close();

}

static void useFileReaderCopyFile() throws IOException {

//使用FileReader、FileWriter 一步到位

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

BufferedReader bufferedReader = new BufferedReader(reader);

Writer writer = new FileWriter(“F:\Hello1_copy3.txt”);

BufferedWriter bufferedWriter = new BufferedWriter(writer);

String line = null;

while ((line = bufferedReader.readLine()) != null) {

bufferedWriter.write(line);

bufferedWriter.newLine();

}

bufferedReader.close();

bufferedWriter.close();

}

}


4、close() 与flush()


先上个例子:

public class FlushTest {

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

FileReader fileReader = new FileReader(“F:\Hello1.txt”); //大文件

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

int readerCount = 0;

//一次读取1024个字符

char[] chars = new char[1024];

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

fileWriter.write(chars, 0, readerCount);

}

}

}

这里并没有调用close()方法。

close()方法包含 flush()方法 ,即close会自动flush

结果:

可以看到,复制的文件变小了。

明显,数据有丢失,丢失的就是缓冲区“残余”的数据

在计算机层面,Java对磁盘进行操作,IO是有缓存的,并不是真正意义上的一边读一边写,底层的落盘(数据真正写到磁盘)另有方法。

所以,最后会有一部分数据在内存中,如果不调用flush()方法,数据会随着查询结束而消失,这就是为什么数据丢失使得文件变小了。

完结

Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。

Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。

rs = new char[1024];

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

fileWriter.write(chars, 0, readerCount);

}

}

}

这里并没有调用close()方法。

close()方法包含 flush()方法 ,即close会自动flush

结果:

可以看到,复制的文件变小了。

明显,数据有丢失,丢失的就是缓冲区“残余”的数据

在计算机层面,Java对磁盘进行操作,IO是有缓存的,并不是真正意义上的一边读一边写,底层的落盘(数据真正写到磁盘)另有方法。

所以,最后会有一部分数据在内存中,如果不调用flush()方法,数据会随着查询结束而消失,这就是为什么数据丢失使得文件变小了。

完结

Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。

Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。

[外链图片转存中…(img-79YgkcIG-1714316469874)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值