IO流
IO流的概述,分类等
Java程序中,对于数据的输入输出以stream方式进行,可以看作是一种数据的流动。
IO流中的IO是Input和Output的缩写,是非常实用的技术,用于处理设备之间的数据传输。读写文件,网络通讯等。
(以下视角,都是站在程序(内存)内部角度观察的。)
输入Input:读取外部数据(磁盘,光盘等存储设备的数据)到程序(内存)中
输出Output:将程序(内存)数据输出到磁盘,光盘等存储设备中
流的分类:
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过下列方法输入或输出数据。
-
按数据的流向不同分为:输入流和输出流。
-
- 输入流:把数据从
其他设备
上读取到内存
中的流。(以InputStream / Reader结尾)
- 输入流:把数据从
-
- 输出流:把数据从
内存
中写出到其他设备
上的流。(以OutputStream / Writer结尾)
- 输出流:把数据从
-
按操作数据单位的不同分为:字节流(8 bit)和字符流(16 bit)
-
- 字节流: 以字节为单位,读写数据的流。字节Byte是计算机数据处理的基本单位,通常由8位(bit)组成。(以InputStream, OutputStream结尾)
-
- 字符流:以字符为单位,读写数据的流。字符Character用于表示文本中的单个字母、数字或符号。在Java中,char是一个基本数据类型,char类型占用16位(2字节)。(以Reader Writer结尾)
-
根据IO流的角色不同分为节点流和处理流。
-
- 节点流:直接从数据源或目的地读写数据。
-
- 处理流:不直接连接到数据源或目的地,而是连接在以存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。
流的分类图示:
流的API
java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象类派生出来的。
抽象基类 | 输入流 | 输出流 |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipeInputStream | PipeOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWritter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
FilterInputStream | FilterOutputStream | FilterReader | FilterWriter | |
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
总结一下:
抽象基类(基本类) | 4个节点流(也称文件流,能处理文件) |
---|---|
InputStream | FileInputStream |
OutputStream | FileOutputStream |
Reader | FileReader |
Writer | FileWriter |
说明:本章虽然涉及到的流很多,但是使用流进行数据的读写操作是非常标准和规范的。
练习1:读取hello.txt中的内容,显示在控制台上。
建议使用try-catch-finally的方式处理异常,确保流一定被关闭了,避免内存泄漏。
提示:
File f1= new File("xxx.txt");
FileReader myfr1 = new FileReader(f1)
int mydata = myfr1.read();
if(mydata != -1){ System.out.print((char)mydata); }
//提示:因为当读取数值为-1时候,是文件全部读取完成的标记。
package IOTest;
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderWriterTest {
/*
* 需求:读取hello.txt中的内容,显示在控制台上。
* 建议使用try-catch-finally的方式处理异常,确保流一定被关闭了,避免内存泄漏。
* 举例见如下的test2
* */
@Test
public void test1() throws IOException {
//1.创建File类的对象,对应hello.txt文件
File f1 = new File("hello.txt");
//2.创建输入型字符流,用于读取数据
FileReader fr1 = new FileReader(f1);
//3.读取数据并且显示在控制台上
//方式1:
// int readfr1 = fr1.read();
// while (readfr1 !=-1)
// //一旦等于-1说明读到最末尾了,读完毕了
// {
// System.out.print((char)readfr1);
// readfr1 = fr1.read();
// }
/* 这种写法也是类似的,都可以用
while(true){
int ff1 = fr1.read();
if(ff1 == -1) break;
else System.out.print((char)ff1);
}
*/
//方式2:
int data;
while ((data = fr1.read()) != -1) {
System.out.print((char) data);
}
//4.流资源的关闭操作(必须关闭,否则资源泄漏)
fr1.close();
}
@Test
public void test2(){
//1.创建File类的对象,对应hello.txt文件
File f2 = new File("hello.txt");
//2.创建输入型字符流,用于读取数据
FileReader filereader1= null;
//3.读取数据并且显示在控制台上(读取结果为int类型,当获得-1时说明读到了末尾)
try {
filereader1 = new FileReader(f2);
int data2;
while( ( data2=filereader1.read() ) != -1){
System.out.print((char)data2);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流资源的关闭操作(必须关闭,否则资源泄漏)
try {
if(filereader1 != null)//这里是为了优化添加的,如果fread为null,说明某种原因创建fread失败了,直接关闭资源就可以了。
filereader1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 需求:读取hello.txt中的内容,显示在控制台上。
* 对test2()进行优化,每次读取多个字符串存放到字符数组中,减少与磁盘的交互次数,从而提高效率。
*
* */
@Test
public void test3() throws FileNotFoundException {
FileReader filereader1= null;
try {
//1.创建File类的对象,对应hello.txt文件
File f2 = new File("hello.txt");
//2.创建输入型字符流,用于读取数据
filereader1 = new FileReader(f2);
//3.读取数据,并显示在控制台上
char[] cbuffer =new char[5];
int len= filereader1.read(cbuffer);
//当filereader1.read(cbuffer)一般返回的是读取到数组char[]cbuffer里面的数据个数,当返回-1说明读完了
while(len!=-1){
//遍历数组
//for (int i = 0; i < cbuffer.length; i++) {
for (int i = 0; i < len; i++) {
System.out.print(cbuffer[i]);
}
len = filereader1.read(cbuffer);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//4.流资源的关闭操作(必须关闭,否则资源泄漏)
try {
if(filereader1 != null)//这里是为了优化添加的,如果fread为null,说明某种原因创建fread失败了,直接关闭资源就可以了。
filereader1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果:
helloWorld123
举例2:往文件hello.txt中写入自定义的的内容。
比如原文件hello.txt文件内容为
this is line 1
this is line 2
this is line 3
代码结构类似这样
File myf1 = new File("hello.txt");
filewriter1=new FileWriter(myf1);
filewriter1.write("写进文件的内容");
//注意,如果文件不存在,系统会自动创建。
-
如果想要覆盖原文件内容,直接写
filewriter1=new FileWriter(myf1);
或者写
filewriter1=new FileWriter(myf1,flase);
-
如果想要直接在原文件内容后面直接追加内容:
filewriter1=new FileWriter(myf1,true);
参考代码:
/*
* 需求:将内存中的数据写入到指定文件中
*
* */
package IOTest;
import org.junit.Test;
import java.io.*;
public class FileReaderWriterTest {
@Test
public void test04() {
FileWriter wr1= null;
try {
//1.创建File类的对象,指明要写出的文件的名称
File filedest= new File("hello.txt");
//2.创建输出流
//覆盖文件内容,使用构造器:
//wr1 = new FileWriter(filedest);
// 这一句等同于wr1 = new FileWriter(filedest,false);会覆盖文件内容
//在现有的文件上追加内容使用构造器:
wr1 = new FileWriter(filedest,true);
//3.写出具体过程
wr1.write("写入的内容");
System.out.println("输出完毕");
} catch (IOException e) {
e.printStackTrace();
} finally { //4.关闭资源
try {
if(wr1 != null)
wr1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出结果:
输出完毕
Process finished with exit code 0
/*
*
* 需求:复制一份hello.txt文件(以及其内容),命名为hello_copy.txt
*
* */
提示:
1.创建file文件(src & desination)
File file_src = new File("src_file.txt");
File file_des = new File("des_file.txt");
2. 创建输入输出流
FileReader filereader1 = new FileReader(file_src);
FileWriter filewriter1 = new FileWriter(file_des);
3. 构建数据读入写出char当然这一步也可以不用
char [] cart = new char[5];
4. 读写文件
int len = filereader1.read(cart);
while(len !=-1){
for(i=0;i<len;i++){
filewriter1.write(cart,0,len);
}
len=filereader1.read(cart);
}
filewriter1.close();
filereader1.close();
程序示例:
package IOTest;
import org.junit.Test;
import java.io.*;
public class FileReaderWriterTest {
@Test
public void test05(){
//替代方案(注意这种方法会将原始文件删除):
//File myf1 = new File("hello.txt");
//myf1.renameTo(new File("hello_copy.txt"));
//方式2:
FileReader filereader1 = null;
FileWriter filewriter1 = null;
try {
//1.创建File类的对象
File f_org = new File("hello_copy.txt");
File f_dest = new File("hello_3.txt");
//File f_dest = new File("hello_3.txt",true); //在现有的文件后面直接追加内容,而不是简单覆盖原文件内容
//2.创建输入流,输出流
filereader1 = new FileReader(f_org);
filewriter1 = new FileWriter(f_dest);
//3.数据的读入和写出的过程
char[] charbuff = new char[5];
//原本的程序如下:
int len = filereader1.read(charbuff);//记录每次读到charbuff中的字符个数。
while(len!=-1) {
for (int i = 0; i < len; i++) {
System.out.print(charbuff[i]);
//filewriter1.write(charbuff[i]);
//filewriter1.write(charbuff,0,len);//这样写是错误的。因为这句其实没有用到i,在i=0到len的循环会一遍又一遍写入至少5次同样的信息。正确的写法,这一句话放在for循环的外面就行。
//filewriter1.write(charbuff);//这样写是也错误的。在不是5的倍数情况下,比如12345hi有可能读出12345hi345,
// 因为内存中的345还在,而hi后面又没有数据了。
//
}
filewriter1.write(charbuff,0,len);
len=filereader1.read(charbuff);
}
//可优化为如下:len写入while循环括号内(但是确保是在for循环外面,或者直接不要for循环了)
// int len;
// while((len = filereader1.read(charbuff)) != -1){
// filewriter1.write(charbuff,0,len);//0角标开始写,一共写len个角标
// }
System.out.println("复制成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(filereader1 != null)
filereader1.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(filewriter1 !=null)
filewriter1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果提示,文件内容复制成功:
this is line 1
this is line 2
this is line 3
this is my writting stamp
一二三四物流七八
写入的内容复制成功
Process finished with exit code 0
总结:FileReader\FileWriter的使用
执行步骤:
- 创建读取或写出File类的对象
- 创建输入流或输出流
- 具体的读如或写出的过程
读入:read(char[] charbuffer)
写出:write(String str) / write(char[] chabuffer,0,len) - 关闭流资源,避免内存泄漏
注意:
- 因为涉及到流资源的关闭操作,所以出现异常的话,需要使用try-catch-finally的方式来处理异常。
- 对于输入流来讲,要求File类的对象对应的物理磁盘上的文件必须存在。否则报错FileNotFoundException;
- 对于输出流来讲,File类的对象对应的物理磁盘上的文件可以不存在。
如果此文件不存在,则在此输出的过程中,会自动创建此文件,并写出数据到此文件中。
如果此文件存在,使用FileWriter(File file)或 FileWriter(File file,false):输出数据过程中,会新建同名的文件,对现有的文件进行覆盖。
如果用FileWriter(File file,true):表示输出数据过程中,会在现有的文件的末尾追加写出的内容。