一文讲透Java面试必考点:IO流
Java大联盟 致力于最高效的Java学习
关注
原文链接 https://cloud.tencent.com/developer/article/2318311
选题思路
每当有小白学习JAVA基础的IO流时,总是一看就会,一写就废,就算学过后过段时间也会忘记,特别是在每次要用时,还得上度娘,这种现象其实就说明还是对IO流的理解深度不够。
所以今天将以下原则,关键点,还有逻辑明确,面试时徒手撸IO就不是梦。
创作提纲
1.玩转IO四条基本原则
2.File类是什么
3.什么是IO
4.字节流与字符流的爱恨情仇
5.缓冲流
6.转换流
1.四条基本原则
在动手撸代码前,了解需求是必要的,了解完分析实现思路,无论再怎么复杂按照以下四条思路来写准没错。
(1) 确定数据是数据源还是数据目的,就是说我们要将这些数据是从磁盘读出还是写入
(2) 确定你要操作的数据是文本还是字节,可以提高处理效率。
(3) 确定数据所在的设备。
(4) 确定有无读写效率上的需求,是否要使用到缓冲流,转换流等。
2.File类是什么
java.io.File IO操作中使用最频繁的类,也特别重要,它很好理解只是代表我们要操作的文件夹或者文件对象,但是File跟流无关,所以File类不能直接对文件数据进行读和写也就是输入和输出。
要注意的点:
1.一个File对象代表硬盘中实际存在的一个文件或者目录。
2.File类构造方法不会给你检验这个文件或文件夹是否真实存在,因此无论该路径下是否存在文件或者目录,都不影响File对象的创建。
public static void main(String[] args) {
//代表文件
File f = new File("c:\\aa\\bb.java");
//代表文件夹
File f1 = new File("c:\\aa");
}
}
3.什么是IO
可以简单理解为数据的流动,JAVA中的具体体现就是java.io包下的那些类用来对数据进行输入(读取数据)输出(写出数据),就是IO操作。
可以大致分为一下几类:
1.输入流:将数据从其他设备(如硬盘)上读取到内存上的流
2.输出流:把内存中的数据写到其他设备(也可以是硬盘)上的流。
3.字节流:以字节为最小单位,进行读写操作字节数据的流。
4.字符流:以字符为最小单位,进行读写操作字符数据的流。
根据以上分类可分成以下超级父类:字节输入流 InputStream,字节输出流 OutputStream,字符输入流 Reader,字符输出流 Writer。其他常用的类都是这些超类的子类。
重点:计算机中所有的文件数据不管是文本,音频视频还是图片,全都是保存为二进制的数据,所以都是一个又一个的字节,所以其实字节流可以传输任何的文件数据,(其实字符流的底层也是传输二进制数据,只是如果用字节流还要对照编码表去翻译,不然会乱码)。
4.字节流与字符流的爱恨情仇
字节输入流IputStream:
<font color="red">基本方法功能:</font>
(1) public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
(2)public abstract int read():从输入流读取数据的下一个字节。
(3) public int read(byte[] b):该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1
FileInputStream类是文件输入流,从文件中读取字节,也是最常用的,继承inputStream。
<font color="blue">构造方法:</font>(当你创建这个流对象时,如果你指定的路径下没有该文件,就会抛出FileNotFoundException异常)
1、 FileInputStream(File file):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
2、 FileInputStream(String name):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名name命名。(推荐)
<font color="blue">常用方式:</font>
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class input2 {
public static void main(String args[]){
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("a.txt");//创建流对象
int len = 0 ;
byte[] bys = new byte[1024];//缓冲数组
while ((len = inputStream.read(bys)) != -1) {//循环读取数据
System.out.println(new String(bys,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();//关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节输出流OutputStream:
<font color="red">基本方法功能:</font>
(1) public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
(2) public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
(3 )public void write(byte[] b):将 b.length个字节从指定的字节数组写入此输出流。
(4) public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。也就是说从off个字节数开始读取一直到len个字节结束
(5) public abstract void write(int b) :将指定的字节输出流。
FileOutputStream类是文件输出流,将字节写入文件,也是最常用的,继承outputStream。
<font color="blue">构造方法:</font>(当你创建这个流对象时,如果你指定的路径下没有该文件,就会自动给你新建一个)
1、 public FileOutputStream(File file):根据File对象为参数创建对象。
2、 public FileOutputStream(String name):根据名称字符串为参数创建对象。(推荐)
<font color="blue">常用方式:</font>
public class FOSWrite {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileOutputStream fos = new FileOutputStream("a.txt");
// 字符串转换为字节数组
byte[] b = "可恶的老六".getBytes();
// 写出字节数组数据
fos.write(b);
// 关闭资源
fos.close();
}}输出结果:
可恶的老六
字符输入流Reader:
<font color="red">基本方法功能:</font>
(1)public void close() :关闭此流并释放与此流相关联的任何系统资源。
(2) public int read():从输入流读取一个字符。
(3) public int read(char[] cbuf):从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
<font color="blue">构造方法:</font>
1、FileReader(File file):创建一个新的 FileReader ,给定要读取的File对象。
2、 FileReader(String fileName):创建一个新的 FileReader ,给定要读取的文件的字符串名称。
<font color="blue">常用方式:</font>
public class FRRead {
public static void main(String[] args) throws IOException {
// 使用文件名称创建流对象
FileReader fr = new FileReader("a.txt");
// 定义变量,保存数据
int b ;// 循环读取
while ((b = fr.read())!=-1) {
System.out.println((char)b);
}
// 关闭资源
fr.close();
}
}
字符输出流 Writer:
<font color="red">基本方法功能:</font>
(1)void write(int c) 写入单个字符。
(2)void write(char[] cbuf)写入字符数组。
(3) abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
(4) void write(String str)写入字符串。
(5)void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
(6)void flush()刷新该流的缓冲。
(7)void close() 关闭此流,但要先刷新它。
FileWriter类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
<font color="blue">构造方法:</font>
1、 FileWriter(File file):创建一个新的 FileWriter,给定要读取的File对象。
2、FileWriter(String fileName):创建一个新的 FileWriter,给定要读取的文件的名称。
<font color="blue">常用方式:</font>
public class FlushDemo {
public static void main(String[] args) throws Exception {
//源 也就是输入流【读取流】 读取a.txt文件
FileReader fr=new FileReader("a.txt"); //必须要存在a.txt文件,否则报FileNotFoundException异常
//目的地 也就是输出流
FileWriter fw=new FileWriter("b.txt"); //没有会自动创建b.txt,
int len;
while((len=fr.read())!=-1){
fw.write(len);
}
fr.close();
fw.flush();
fw.close();
}
}
<font color="red">【注意】:</font>关闭资源时,与FileOutputStream不同。仔细看这里的close()方法,并没有进行刷新,所以要先使用flush()方法刷新缓存,再用close()方法关闭流对象。
5.缓冲流
缓冲流也叫高效是对以上四个流的效率增强流。
字节缓冲流:
BufferedInputStream
,BufferedOutputStream
字符缓冲流:
BufferedReader
,BufferedWriter
<font color="red">基本原理:</font>
1、使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。
2、通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。
3、如果用read方法读取字符数据,并存储到另一个容器中,直到读取到了换行符时,将另一个容器临时存储的数据转成字符串返回,就形成了readLine()功能。
<font color="blue">构造方法:</font>
BufferedInputStream(InputStream in) :创建一个新的缓冲输入流,注意参数类型为InputStream。
BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,注意参数类型为OutputStream。
public BufferedReader(Reader in) :创建一个新的缓冲输入流,注意参数类型为Reader。
public BufferedWriter(Writer out):创建一个新的缓冲输出流,注意参数类型为Writer。
<font color="blue">常用方式:</font>
public class BufferedTest {
public static void main(String[] args) throws IOException {
HashMap<String, String> map = new HashMap<>();
// 创建流对象 源
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
//目标
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
// 读取数据
String line = null;
while ((line = br.readLine())!=null) {
// 解析文本
String[] split = line.split("\\.");
// 保存到集合
map.put(split[0],split[1]);
}
// 释放资源
br.close();
// 遍历map集合
for (int i = 1; i <= map.size(); i++) {
String key = String.valueOf(i);
// 获取map中文本
String value = map.get(key);
// 写出拼接文本
bw.write(key+"."+value);
// 写出换行
bw.newLine();
}
// 释放资源
bw.close();
}
}
6.转换流
先了解字符编码跟字符集这两个概念
编码解码:计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。同样的道理有编码就有解码,就是把这些二进制数据按照一定的规则解析成我们能看懂的东西。
字符集:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等这些包含各种各样规则的集合。(万国语翻译器)
<font color="red">基本原理:</font>字符流=字节流+编码表
InputStreamReader类(字节流到字符流转换)
<font color="blue">构造方法:</font>
InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName): 创建一个指定字符集的字符流。
<font color="blue">常用方式:</font>
public class ReaderDemo2 {
public static void main(String[] args) throws IOException {
// 定义文件路径,文件为gbk编码
String FileName = "C:\\a.txt";
// 创建流对象,默认UTF8编码
InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));
// 创建流对象,指定GBK编码
InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");
// 定义变量,保存字符
int read;
// 使用默认编码字符流读取,乱码
while ((read = isr.read()) != -1) {
System.out.print((char)read); // �����ʺ
}
isr.close();
// 使用指定编码字符流读取,正常解析
while ((read = isr2.read()) != -1) {
System.out.print((char)read);
}
isr2.close();
}
}
OutputStreamWriter类(字符流到字节流转换)
<font color="blue">构造方法:</font>
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流。
<font color="blue">常用方式:</font>
-
public class OutputDemo {
public static void main(String[] args) throws IOException {
// 定义文件路径
String FileName = "C:\\s.txt";
// 创建流对象,默认UTF8编码
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
// 写出数据
osw.write("老六"); // 保存为6个字节
osw.close();
// 定义文件路径
String FileName2 = "C:\\a.txt";
// 创建流对象,指定GBK编码
OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
// 写出数据
osw2.write("受死");// 保存为4个字节
osw2.close();
}
}