“ 流是一种抽象概念,它代表了数据的无结构化传递。”
百度百科
I/O的表面意思是什么?
I流 - InputSteam 输入流
O流 - OutPutStream 输出流
这就是我们常说的I/O流,根据其它条件,我们又划分为以下几种类型
按照数据传递方向分为:输入流、输出流
按照数据传递类型分为:字符流、字节流
友善解释一下,字符和字节的区别:
字节(Byte):,计算机处理的基本单位,简写B,1B = 8Bit(位)
字符:指的是计算机中使用过的字母、字、数字、符号等
ASCIIS码:英文字母(不分大小写)= 1B
中文汉字 = 2B
ASCII码 = 1B
UTF-8编码:英文字符 = 1B
英文标点 = 1B
中文(含繁体) = 3B
中文标点 = 3B
Unicode编码:英文字符 = 2B
英文标点 = 2B
中文(含繁体) = 2B
中文标点 = 2B
JAVA中I/O模型为Decorator(装饰者)模式,支持了许多功能的Stream,我们可以按照我们的需求来动态使用。
比如,您要读写一个文件,就可以选择FileInputStream和BufferedInputStream。
另外,我们也看到,I/O流的接口和实现类众多,所以我们选择其中的几个常见常用的来了解一下。
01
—
字节流的输入输出
字节输入流
不包含边界数据的连续流。
百度百科
任何数据的读取或者写入都是有目标的,I/O流也不例外,这里我们以文件为例,进行读取写入操作。
首先,我们在D盘下创建一个文件:student.txt,并写入内容:name:bob
然后,我们选择用FileInputStream这个类获取文件的内容。因为会用到read方法,我们首先来看下他的源码注释
* Reads a byte of data from this input stream. This method blocks * if no input is yet available. * @return the next byte of data, or <code>-1</code> if the end of the * file is reached. * @exception IOException if an I/O error occurs.
大致意思就是:从这个输入流读取数据,如果没有数据,则返回 -1。好了,现在我们已经知道了规则,开始代码吧。
File file = new File("D:/student.txt");
FileInputStream fileInputStream = new FileInputStream(file);
int i = fileInputStream.read();
while (i > 0){
System.out.print((char)i);
i = fileInputStream.read();
}
//输出j结果
name:bob
很幸运,我们第一次成功了。按照程序的执行顺序我们看到,程序读一个写一个,如果文件足够长,岂不是效率会被拖死。那么,有没有一种好的方式解决这个问题呢?
你看,它提供了一个read的有参方法。注释大意就是,参数b作为一个缓冲区,会返回读入缓冲区的字符总数,然后读完返回 -1。
好吧,我们试一试。
File file = new File("D:/study/student.txt");
FileInputStream fileInputStream = new FileInputStream(file);
//代表每四个字节
byte[] b = new byte[4];
int i = fileInputStream.read(b);
while (i > 0){
String str = new String(b);
System.out.println(str);
i = fileInputStream.read(b);
}
输出结果:
我们很清晰的发现,它换行了,说明它每次都会读取四个字节在返回,所以是很实用的。日常工作中,为了保证正常合理的配置,建议将缓存区设置为【1024】,若有特殊需要可自行进行调节。(以下缓存区更换为1024)
可能这是你会问了,如果文件中出现中文,还能正常读吗?说的多不如一做,来,我们实践一下,看看会发生什么结果?
在刚才的【student.txt】中写入 - job:程序猿
运行结果:
乱码了,咋整?没关系,我们可以转化一下
File file = new File("D:/study/student.txt");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] b = new byte[1024];
int i = fileInputStream.read(b);
while (i > 0){
String str = new String(b,"UTF-8");
System.out.print(str);
i = fileInputStream.read(b);
}
你看输出结果:
你看,多好
字节输出流
你用的人家的FileInputStream类,当然要用FileOutputStream类来输出一下,毕竟要表示尊敬,是吧?
我们先理清一下操作逻辑,我们既然要输出一下我们读取的信息,自然要有一个接收方,这里,我们创建一个【teacher.txt】来接收。
首先,我们先看一下它的源码,有哪些方法。
FileInputStream提供了众多的有参构造方法,比如
//创建名称为 name 值的文件
public FileOutputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, false);
}
//创建名称为 name 值的文件,
//append:顾名思义j就是是否增加,如果是ture,内容将在文件的尾部增加,否则则是覆盖
public FileOutputStream(String name, boolean append)
throws FileNotFoundException
{
this(name != null ? new File(name) : null, append);
}
其余的方法不说,我们就用第一个吧,上代码。
File file = new File("D:/study/student.txt");
FileInputStream fileInputStream = new FileInputStream(file);
byte[] b = new byte[1024];
int i = fileInputStream.read(b);
while (i > 0){
String str = new String(b,"UTF-8");
i = fileInputStream.read(b);
}
File outfile = new File("D:/study/teacher.txt");
FileOutputStream fileOutputStream = new FileOutputStream(outfile,false);
//读是read,写当然就是write了
fileOutputStream.write(b);
运行完成,打开我们的【teacher.txt】文件
收工。
02
—
字符流的输入输出
字符输入流
在字节流的基础上,加上编码,形成的数据流
百度百科
Reader :字符输入流常见的几个
- BufferedReader
- FileReader
- InputStremReader
接下来,我们以InputStremReader为例,操作一下。
File file = new File("D:/study/student.txt");
FileInputStream fileInputStream = newFileInputStream(file);
//获取字符输入流
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
char[] c = newchar[1024];
intread = inputStreamReader.read(c);
System.out.println( newString(c));
看看结果呢?
so easy?
字符输出流
Writer:上面,我们用InputStremReader进行了读取,因此,我们也对应的使用OutputStreamWriter进行写入。
File file = new File("D:/study/student.txt");
FileInputStream fileInputStream = newFileInputStream(file);
//获取字符输入流
InputStreamReader inputStreamReader = newInputStreamReader(fileInputStream,"utf-8");
char[] c = newchar[1024];
intread = inputStreamReader.read(c);
File file1 = newFile("D:/study/teacher.txt");
OutputStream outputStream = newFileOutputStream(file1);
OutputStreamWriter outputStreamWriter = newOutputStreamWriter(outputStream,"utf-8");
outputStreamWriter.write(c);
outputStreamWriter.flush();
outputStreamWriter.close();
我们接着去看【teacher.txt】的结果
如你所愿,成功的写入到了你的新文件。
貌似是说完了,但是我们还有两个问题。
1.以上都是文本文件,若是图片,怎么进行读取和写入
其实这个问题,我们首先要考虑的是两种文件的本质读取方式是什么?才会使我们解决的方式更加科学。
文本文件:是指那些内容为纯文本的文件
二进制文件:图形文件及文字处理程序等计算机程序都属于二进制文件。
其实说直白点,图片、音频、视频,这些都是二进制文件,从根本上来说,文本文件也是二进制文件的一种。
然后,我们在去了解一下字节流和字符流的特点。
字节流,是可以操作二进制文件的,因为 1Byte = 8bit,比如数字,字母都是一个字节,但是中文的话,占用两个字节,就会出现读取不全的情况。你可以在上面字节输入输出流的例子中里,把读取写入的长度都调整为4,你在看结果,会有一个很好的展现。
字符流,正好可以解决上面的问题,因为它操作的单位是字符。
所以,我们必须用字节流去处理二进制文件。
File file = new File("D:/study/student.jpg");
BufferedImage bufferedImage = ImageIO.read(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
byte[] bytes = byteArrayOutputStream.toByteArray();
ByteArrayInputStream bais = newByteArrayInputStream(bytes);
BufferedImage bufferedImage2 =ImageIO.read(bais);
//可以是jpg,png,gif格式
File file2 = newFile("D:/study/teacher.png");
ImageIO.write(bufferedImage2, "jpg", file2);
自己运行的时候,只需要把地址换成你的文件地址就可以了。
ByteArrayOutputStream
、
FileOutputStream
是两种基本的介质流,它们分别向Byte 数组
、和本地文件
中写入数据。
ImageIO 提供了读取和写入图片的基本方法
2.Buffered缓冲流的实际应用有什么好处?特点是什么?
不管是字节流还是字符流,都提供了缓冲流。
BufferedInputStrean
BufferedOutputStream
BufferedReader
BufferedWriter
都是常见的缓冲流,缓冲流的作用,就是避免频繁读写硬盘。
缓冲流中最具有特色的方法就是它的:readLine方法。另外,前面两种流中,都增加了synchronized关键字读写方法。我们来分别看一下它的源码。
//BufferedInputStrean
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
//BufferedOutputStream
public synchronized void write(int b) throws IOException {
if (count >= buf.length) {
flushBuffer();
}
buf[count++] = (byte)b;
}
BufferedInputStrean
BufferedOutputStream
我们看到这两种流支持了读写的线程安全操作。
BufferedReader:
//BufferedReader
public String readLine() throws IOException {
return readLine(false);
}
再去看它的注释:
读取该行的文本并返回。
以上都是一些缓冲流的独特的方法,那么他们在现实意义中有什么作用呢?其实可以把它们当比喻我们秋收玉米。
正常的读取文件的方式,类似于,你掰一个玉米,放一次家里,然后接着在进行往复的重复操作,这样做,大量的时间消耗在路上,大大降低了效率。
缓冲流,就是你掰好了玉米,然后用拖拉机拉走,一拉一车,然后重复操作,这样就大大的减少了往返的时间,提高了效率。