首先说明一下本章主要学什么,IO流,其实就是升级版的File文件操作,在本章通过学习一些流,调用相应的方法,就可以实现从程序到文件,再从文件到程序的相应操作,而这一章节的代码也比较模板化,因此只需要记住基本的流,基本的使用方法,具体应用可以在项目中进行实践。
输入流、输出流
字节流、字符流
节点流、处理流
在这里只提供一些常用常见的流
这个表其实也不用记,看一眼,知道有啥东西就行了,重点在后边。
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedOutputStream | BufferedReader | BufferedWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
特殊流 | DataInputStream | DataOutputStream |
模板的输入输出过程我用文件的字节输入输出流演示
①创建File类的对象,指明读取的数据的来源(要求此文件一定存在)
File srcFile = new File("d:\\zyb.jpg");
②创建相应的输入流,将File类的对象作为参数,传入流的构造器中
FileInputStream fis = new FileInputStream(srcFile);
③读入过程:创建相应的byte[]或char[],并调用fis的read方法,放入循环中
byte[]buffer=new byte[10];
int len=0;
while((len=fis.read(buffer))!=-1){
System.out.println(new String(buf,0,len));
}
④关闭流资源
fis.close();
说明:程序中出现的异常需要使用try-catch-finally处理
①创建File对象,指明写出数据的位置(不要求此文件一定要存在)
File destFile = new File("d:\\zyb2.jpg");
②创建相应的输出流,将File类的对象作为参数,传入流的构造器中
FileOutputStream fos = new FileOutputStream(destFile);
③写出(创建buffer数组和len指针变量的过程和输入过程相同)
fos.write(buffer,0,len);
④关闭流资源
fos.close();
说明点:1、read()理解:返回读入的一个字符。如果到达文件末尾,则返回-1.
2、异常处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally
3、读入的文件一定要存在,否则会报FileNotFoundException异常
代码演示:
@Test
public void testFileReader1() throws IOException {
//1.File类的实例化
File file = new File("e:\\news2.txt");
//2.FileReader流的实例化
FileReader fileReader = new FileReader(file);
//3.读入文件的操作
//read(char[] cubf):每次返回读入cubf数组中的字符个数,如果到达文件末尾,则返回-1
char[]cbuf = new char[5];
int len=0;
while((len = fileReader.read(cbuf))!=-1){
String str = new String(cbuf,0,len);
System.out.println(str);
}
//4.资源的关闭
fileReader.close();
}
说明:1.输出到文件的操作,对应的File可以不存在,不会报异常
2.File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
File对应的硬盘中的文件如果存在:
有两种构造器,如果流使用的构造器是FileWriter(file)/FileWriter(file,false)则写入的信息会对原文件进行覆盖,比如原文件中已经含有字符“hello”,而我使用writer写入“world”,则最后文件的内容只有“world”。
如果流使用的构造器是**FileWriter(file,true)**那么进行的是对原文件的续写操作,不会进行覆盖。比如原文件中已经含有字符“hello”,我写入“world”,则最后文件的内容为“hello world”。
代码演示:
@Test
public void testFileWriter() throws IOException {
//1.提供File类的对象,指明写出到的文件
File file = new File("hello1.txt");
//2.提供FileWriter的对象,用于数据的写出
FileWriter fw = new FileWriter(file, false);
//3.写出到文件操作
fw.write("wu zi bu xing\n");
fw.write("edg nb!");
//4.流资源的关闭
fw.close();
}
文本文件的复制操作是通过在一个程序中同时调用输入流和输出流,进而实现创建一个新的文本,并对其进行赋值。
代码如下:
其中,srcFile是被复制的文件,destFile是将要创建,进行复制的文件。
@Test
public void testFileReaderWriter() throws IOException {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello1.txt");
File destFile = new File("hello2.txt");
//2.创建输入流和输出流的对象
FileReader fr = new FileReader(srcFile);
FileWriter fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cubf = new char[5];
int len;
while((len=fr.read(cubf))!=-1){
fw.write(cubf,0,len);
}
//4.关闭流
fw.close();
fr.close();
}
呃,这个和上面的字符流一样,没什么好说的,代码模板都一样,只需要在写入写出的时候把char[]数组换成byte[]数组就行了。。。
比较一下:对于文本文件(.txt,.java,.c,.cpp)之类的都使用字符流处理
对于非文本文件(图片,音频,视频),使用字节流处理
BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
提高流的读取、写入速度
原理:内部提供了一个缓冲区。默认情况下是8kb
该代码是用节点流+缓冲流实现图片的复制
其实缓冲流的代码跟上边节点流的代码模板一样,只不过多了一步将节点流的对象放入缓冲流的构造器中的步骤
@Test
public void BufferedStreamTest() throws IOException {
//1.造文件
File srcFile = new File("d:\\zyb.jpg");
File destFile = new File("d:\\zyb2.jpg");
//2.造节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//3.造缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
//4.复制:用bis读取,用bos写入
byte[]buffer=new byte[10];
int len=0;
while((len=bis.read(buffer))!=-1) {
bos.write(buffer, 0, len);
}
//5.资源关闭
//要求:先关闭外层的流,再关闭内层的流
bos.close();
bis.close();
}
之前一直都是分开写,代码比较多,现在简写一下,试试将造文件的过程和造节点流的过程一起合并到造缓冲流的过程。
@Test
public void testBufferedReaderBufferedWriter() throws IOException {
//1.将造文件,造节点流合成一步
BufferedReader bufferedReader = new BufferedReader(new FileReader(new File("e:\\news1.txt")));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new File("e:\\news2.txt")));
//2.数据的读入写出
char[]cbuf=new char[1024];
int len=0;
while((len=bufferedReader.read(cbuf))!=-1){
bufferedWriter.write(cbuf,0,len);
}
//3.关流
bufferedReader.close();
bufferedWriter.close();
}
InputStreamReader:将一个字节的输入流转换为字符的输入流
解码:字节、字节数组—>字符数组、字符串
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
编码:字符数组、字符串—>字节、字节数组
编码决定解码!!!
提供字节流的字符流之间的转换
将字节输入流转换为字符输出流
呃,其实就多加了一步将字节输入流的对象放到转换流的构造器中而已
//转换输入流
@Test
public void test1() throws IOException {
FileInputStream fileInputStream = new FileInputStream("e:\\news1.txt");
//参数“UTF-8”指明了字符集(根据文件初始存的时候决定)
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"UTF-8");
char[]cbuf=new char[1];
int len;
while((len=inputStreamReader.read(cbuf))!=-1){
String str=new String(cbuf,0,len);
System.out.print(str);
}
inputStreamReader.close();
}
将字符输出流转换为字节输出流
写了这么多了,学了节点流,缓冲流,转换流,那现在咱们把这三个流都用到一起,综合升华一下,嘿嘿
这段代码的作用是实现文本的复制,就是将news1.txt中的文本信息复制到nesw3.txt中
快结束了,再来综合复习一下吧兄弟们
①造文件
②造节点流
③造缓冲流,提高运行效率
④造转换流,分别将输入流和输出流放入两个转换流中
⑤数组+循环 实现读入和写出
⑥关闭流
//转换输出流
@Test
public void test2() throws IOException{
File file1 = new File("e:\\news1.txt");
File file2 = new File("e:\\news3.txt");
FileInputStream fileInputStream = new FileInputStream(file1);
FileOutputStream fileOutputStream = new FileOutputStream(file2);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"gbk");
char[]cubf=new char[1];
int len;
while((len=inputStreamReader.read(cubf))!=-1){
outputStreamWriter.write(cubf,0,len);
}
inputStreamReader.close();
outputStreamWriter.close();
}
System.in:标准的输入流,默认从键盘输入 例如:new Scanner(System.in)
System.out:标准的输出流,默认从控制台输出 例如:System.out.println()
PrintStream和PrintWriter
说明:提供了一系列重载的print()和println()方法,用于多种数据类型的输出
System.out返回的是PrintStream的实例
DataInputStream和DataOutputStream
作用:用于读取或写出基本数据类型的变量或字符串
上面的三个流作为了解即可,不用深入
ObjectInputStream和ObjectOutputStream
作用:
ObjectInputStream:**内存中的对象—>存储中的文件、通过网络传输出去 ** 序列化过程
ObjectOutputStream:存储中的文件、通过网络接受过来—>内存中的对象 反序列化过程
概念描述:
序列化过程:把内存中Java对象转换成平台无关的二进制流,从而把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
反序列化过程:当其它程序获取了这种二进制流之后,就可以恢复成原来的Java对象
序列化过程
在这里说一点就是,java可以对自定义类进行序列化转换,但这个自定义类需要满足两个条件,第一个就是需要实现Serializable接口,这个接口是一个标识接口,不需要重写任何方法。第二个就是在类中需要提供一个全局常量serialVersionUID,其中这个全局常量的值必须进行自定义修改,任何值都可以。
* @author 一只鱼zzz
* @version 1.0
* Person类需要满足一下的要求,方可序列化
* 1.需要实现接口:Serializable(标识接口,无需重写方法)
* 2.当前类需要提供一个全局常量:serialVersionUID
*/
public class Person implements Serializable {
public static final long serialVersionUID = 1231213123132L;
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/*
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
*/
@Test
public void test1(){
//1.造流造文件
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("e:\\news1.dat"));
//2.写
oos.writeObject(new String("我爱轻大"));
oos.flush();
//3.刷新
oos.writeObject(new Person("一只鱼zzzz",19));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
反序列化过程
@Test
public void test2(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("e:\\news1.dat"));
Object obj=ois.readObject();
String str=(String)obj;
Person p = (Person) ois.readObject();
System.out.println(p);
System.out.println(str);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(1)该类直接继承于Object类,实现了DataInput和DataOutput接口
(2)既可以作为一个输入流,又可以作为一个输出流
(3)如果作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。如果写出到的文件存在,则会对原文件内容进行覆盖。(默认情况下,从头覆盖)
(4)通过相关的操作,可以实现RandomAccessFile“插入数据”的功能
@Test
public void test1() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile(new File("hello1.txt"), "r");
RandomAccessFile raf2 = new RandomAccessFile(new File("hello3.txt"), "rw");
byte[]buffer = new byte[1024];
int len;
while((len=raf1.read(buffer))!=-1){
raf2.write(buffer,0,len);
}
raf2.close();
raf1.close();
}
@Test
public void test2() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("hello3.txt", "rw");
raf1.seek(3);//将指针调节到角标为3的位置
//保存指针3后面的所有数据到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File("hello3.txt").length());
byte[] buffer = new byte[20];
int len;
while((len=raf1.read(buffer))!=-1){
builder.append(new String(buffer,0,len));
}
//调回指针,写入“xyz”
raf1.seek(3);
raf1.write("xyz".getBytes());
//将StringBuilder中的数据写入到文件中
raf1.write(builder.toString().getBytes());
raf1.close();
}