输入输出流(IO流)_字符流、字节流、节点流、处理流、缓冲流、序列化与反序列化
# 输入输出流(IO流)
1.File类
java.io.File,使用该类的构造函数就可以创建文件对象,将硬盘中一个具体的文件以Java对象的形式来表示。
File类常用方法
方法名 | 描述 |
---|---|
public File(String pathName) | 根据路径创建对象 |
public String getName() | 获取文件名 |
public String getParent() | 获取文件所在目录 |
public String getParentFile() | 获取文件所在目录对应的File对象 |
public String getPath() | 获取文件路径 |
public boolean exists() | 判断文件是否存在 |
public boolean isDirectory() | 判断对象是否为目录 |
public boolean isFile() | 判断对象是否为文件 |
public long length() | 获取文件的大小 |
public boolean createNewFile() | 根据当前对象创建新文件 |
public boolean delete() | 删除对象 |
public boolean mkdir() | 根据当前对象创建目录 |
public boolean renameTo(File file) | 重命名 |
//构造方法创建File对象
File file = new File("D://File .txt");
System.out.println(file);
//判断文件是否存在
System.out.println(file.exists());
//创建文件
File file1=new File("D://File2.txt");
//方法定义的异常如果直接继承自Exception,实际调用的时候需要手动处理(捕获异常\丢给虚拟机去处理)
try {
//输出创建是否成功
System.out.println(file1.createNewFile());
} catch (IOException e) {
e.printStackTrace();
}
//重命名文件
file1.compareTo(new File("D://File1.txt"));
System.out.println(file1);
//删除文件
file1.delete();
System.out.println(file1.exists());
注意:创建文件和重命名文件
2.IO流
IO流的分类
按照方向分,可以分为输入流和输出流。
按照单位分,可以分为字节流和字符流。
按照功能分,可以分为节点流和处理流。
3.字节流
按照方向可以分为输入字节流(InputStream)和输出字节流(OutputtStream)
1.输入字节流(InputStream)
public abstract class InputStream implements Closeable {
......
}
可以看出InputStream是个抽象类,不能直接使用构造方法实例化,只能实例化它的非抽象子类;还可以看出实现了Closeable接口。
2.输入字节流(InputStream)的常见方法
InputStream是抽象类,不能直接实例化,可以实例化它的非抽象子类FileInputStream
方法 | 描述 |
---|---|
int read() | 以字节为单位读取数据 |
int read(byte b[]) | 将数据存入byte类型数组并返回数组长度 |
int read(byte b[],int off,int len) | 将数据存入byte数组的指定区间内,返回数组长度 |
byte[] readAllBytes() | 将所有数据存入byte类型数组并返回 |
int available() | 返回当前数组流未读取的数据个数 |
void close() | 关闭数据流 |
package iotest;
import java.io.*;
public class InputStreamDemo {
//InputStream继承自Exception
public static void main(String[] args) throws Exception{
//File对象是用来映射D://File.txt文件的,将文件映射成对象才能进行操作
File file =new File("D://File.txt");
if(file.exists()){
//多态,父类引用指向子类对象(InputStream是抽象类,不能直接实例化)
InputStream inputStream=new FileInputStream(file);
/* long length=file.length();
for(int i=0;i<length;i++){
//输出文中第i个字母的ASCII值
System.out.println(inputStream.read());
}*/
/*int temp=0;
//当inputStream.read()=-1时代表数据读完了
while((temp=inputStream.read())!=-1){
System.out.println(temp+"剩余数据"+inputStream.available()+"个");
}*/
// byte[] byteArray=inputStream.readAllBytes();
byte[] byteArray=new byte[(int)file.length()];
byteArray=inputStream.readAllBytes();
for(int i=0;i<byteArray.length;i++){
//将ASCII进行强制类型转换成char类型
char c=(char)byteArray[i];
System.out.print(c);
}
//关闭流
inputStream.close();
}
}
}
首先,我们要区分几个概念:文件、文件中的字符、File对象、InputStream对象。Java是面向对象的语言,而且是一门高级语言,所以无法直接对文件进行操作,所以需要File类通过底层将文件映射成File对象才能对文件进行操作。我们可以把File对象看成一个箱子,文件中的字符就是箱子中的球,InputStream对象可以看成球外出的管道。当你使用read()
或者readAllBytes()
以字节为单位读取了全部或者部分File对象中的字符之后,File对象中会不剩下字符或者只会剩下未读取的部分。
当我们new一个java流对象之后,不仅在计算机内存中创建了一个相应类的实例对象。而且,还占用了相应的系统资源,比如:文件句柄、端口、数据库连接等。在内存中的实例对象,当没有引用指向的时候,java垃圾收集器会按照相应的策略自动回收,但是却无法对系统资源进行释放。所以,我们需要主动调用close()方法释放java流对象。在java7之后,可以使用try-with-resources语句来释放java流对象,从而避免了try-catch-finally语句的繁琐,尤其是在finally子句中,close()方法也会抛出异常。
3.输出字符流(OutputStream)
public abstract class OutputStream implements Closeable, Flushable {
......
}
可以看出OutputStream是一个抽象类,所以不能直接实例化,只能实例化它的非抽象子类。还可以看出OutputStream实现了Closeable和 Flushable接口。补充:Flushable接口作用(输出的时候可以使用缓冲流,速度更快,效率更高)。
4.输出字节流(OutputStream)的常见方法
OutputStream是抽象类,不能直接实例化,可以实例化它的非抽象子类FileOutputStream
方法 | 描述 |
---|---|
void write(int b) | 以字节为单位输出数据 |
void write(byte b[]) | 将byte数组中的数据输出 |
void write(byte b[],int off,int len) | 将byte数组中指定区间的数据输出 |
void close() | 关闭数据流 |
void flush() | 将缓冲流中的数据同步到输出流中 |
package iotest;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamDemo {
public static void main(String[] args) throws IOException {
//输出流自动创建文件,会自动覆盖上次创建的同名文件
OutputStream outputStream=new FileOutputStream("D://File1.txt");
byte[] byteArray={'j','a','v','a'};
outputStream.write(byteArray);
//清空缓冲区存入文件
outputStream.flush();
outputStream.close();
}
}
2.字符流
字节流是单位时间内处理(输入+输出)一个字节数据的IO流。
字符流是单位时间内处理(输入+输出)一个字符数据的IO流。
字符流同样也可以分为输入字符流(Reader)和输出字符流(Writer)。
1.输入字符流(Reader)
public abstract class Reader implements Readable, Closeable {
private static final int TRANSFER_BUFFER_SIZE = 8192;
......
}
可以看出Reader是一个抽象类,不能直接实例化,只能实例化它的非抽象子类。可以看出Reader类实现了Readable和Closeable接口。
Readable接口是把数据以字符的形式读入缓冲区
2.常用方法
Reader是一个抽象类,不能直接实例化,我们可以实例化它的非抽象子类FileReader。需要注意的是FileReader并不是Reader的直接子类,它的父类继承自Reader的直接子类InputStreamReader。
InputStreamReader的功能是将字节输入流转化为字符输入流。我们知道按照功能分IO流可以分为节点流和处理流。InputStreamReader是一个处理流,他处理的节点流失InputStream。
示例1:
package iotest;
import java.io.*;
public class ReaderDemo {
public static void main(String[] args) throws IOException {
Reader reader=new FileReader("D://File.txt");
int temp;
System.out.println("字符流读取:");
while((temp=reader.read())!=-1){
System.out.println(temp);
}
reader.close();
InputStream inputStream=new FileInputStream("D://File.txt");
int temp1;
System.out.println("字节流读取:");
while((temp1=inputStream.read())!=-1){
System.out.println(temp1);
}
reader.close();
}
}
在UTF-8编码下,英文和英文符号一个字符就是一个字节,汉字一个字符是三个字节。例:读取文件中是“你好”,结果如下:
示例2:
package iotest;
import java.io.*;
public class ReaderDemo {
public static void main(String[] args) throws IOException {
File file=new File("D://File.txt");
Reader reader=new FileReader("D://File.txt");
char[] charArray=new char[(int)file.length()];
//以字符位单位读取文件,并将字符存在字符数组中
reader.read(charArray);
System.out.println("字符数组:"+charArray.length);
for(char charN:charArray){
System.out.print(charN);
}
reader.close();
}
}
读取的文件中存储的是“你好Java”,可以看到字符数组长度为6,两个汉字字符和四个英文字符。而且,read()
方法返回的是int,直接将字符转化为字节返回。read(char[] charArray)
返回的是字符数组,不会转成字节。
3.输出字符流(Writer)
public abstract class Writer implements Appendable, Closeable, Flushable {
......
}
可以看出Writer是一个抽象类,不能直接实例化,只能实例化它的非抽象子类。可以看出Reader类实现了Appendable接口、Closeable接口和Flushable接口。
Appendable接口的作用是将char类型的数据读入到数据缓冲区中。
4.常用方法
Writer是一个抽象类,不能直接实例化,我们可以实例化它的非抽象子类FileWriter。需要注意的是FileWriter并不是Reader的直接子类,它的父类继承自Writer的直接子类OutputStreamWriter。
OutPutStream同样是一个处理流,将输出字节流转成输出字符流。
package iotest;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class WriterDemo {
public static void main(String[] args) throws IOException {
Writer writer=new FileWriter("D://File1.txt");
writer.write("你好");
char[] charArrays={'你','好','世','界'};
writer.write(charArrays);
String str="HelloWord!你好世界!";
writer.write(str,10,5);
writer.flush();
writer.close();
}
}
5.InputStreamReader
package iotest;
import java.io.*;
public class InputStreamReaderDemo {
public static void main(String[] args) throws IOException {
File file=new File("D://File.txt");
//基础管道,输入字节流,InputStream
InputStream inputStream =new FileInputStream(file);
//处理流,InputStreamReader
InputStreamReader inputStreamReader=new InputStreamReader(inputStream);
char[] charArray=new char[(int)file.length()];
//读取文件,以字符为单位存储到数组
inputStreamReader.read(charArray);
for(char charN:charArray){
System.out.print(charN);
}
String str=new String(charArray);
System.out.println(str);
//关闭流
inputStreamReader.close();
inputStream.close();
}
}
6.OutStreamWriter
package iotest;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
public class OutStreamWriterDemo {
public static void main(String[] args) throws IOException {
String str="你好!HelloWord!";
//节点流,输出字节流,OutputStream
OutputStream outputStream=new FileOutputStream("D://File1.txt");
//处理流,OutputStreamWriter
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(outputStream);
outputStreamWriter.write(str);
outputStream.flush();
outputStreamWriter.flush();
outputStream.close();
outputStreamWriter.close();
}
}
注意:无论是InputStreamReader还是OutStreamWriter,都是处理流,都需要对对应的节点流进行操作。
4.缓冲流
无论是字节流还是字符流,使用的时候都会频繁访问硬盘,对硬盘是一种损伤,同时效率不高。
可以使用缓冲流,缓冲流自带缓冲区,可以一次性从硬盘中读取部分数据存入缓冲区,再写入内存,这样就可以有效减少对硬盘的直接访问,从而减轻对硬盘的损伤。
1.节点流和处理流
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流。
节点流:可以从或者向一个特定的地方(节点)读写数据。例如InputStream、OutputStream、Reader、Writer。
处理流:是对一个已经存在的流的连接和封装,通过所封装的流的功能的调用实现数据的读写。处理流的构造方法总是要带一个其他的流对象做参数。例如:
节点流可以直接对接到文件对象File,而处理流不能直接对接到文件对象File。
2.缓冲流分类
缓冲流是处理流。可以分为:
根据方向分为输入缓冲流和输出缓冲流。
根据单位分为字符缓冲流和字节缓冲流。
3.字节输入缓冲流(BufferedInputStream)
public
class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
......
}
package iotest;
import java.io.*;
public class BufferedInputStreamDemo {
public static void main(String[] args) throws IOException {
//文件对象
File file=new File("D://java.txt");
//节点流
InputStream inputStream=new FileInputStream(file);
//缓冲流,处理流
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
int temp;
while((temp=bufferedInputStream.read())!=-1){
System.out.print(temp+" ");
}
//关闭流
inputStream.close();
bufferedInputStream.close();
}
}
4.字符输入缓冲流(BufferedWirter)
public class BufferedReader extends Reader {
......
}
package iotest;
import java.io.*;
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
//文件
File file=new File("D://java.txt");
//节点流,字符流
Reader reader=new FileReader(file);
//处理流,缓冲流
BufferedReader bufferedReader=new BufferedReader(reader);
char[] charArrays=new char[(int)file.length()];
//读取文件,存入数组
bufferedReader.read(charArrays);
for (char charArray : charArrays) {
System.out.print(charArray);
}
//按行读取文件
String str=null;
while((str=bufferedReader.readLine())!=null)
{
System.out.println(str);
}
//关闭流
reader.close();
bufferedReader.close();
}
}
5.字节输出缓冲流(BufferedOutputStreamWriter)
public class BufferedOutputStream extends FilterOutputStream {
......
}
package iotest;
import java.io.*;
public class BufferedOutputStreamDemo {
public static void main(String[] args) throws IOException {
String str="Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1] 。\n" +
"Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3] 。";
//文件
File file=new File("D://java1.txt");
//节点流,输出字节流
OutputStream outputStream=new FileOutputStream(file);
//处理流,输出字节缓冲流
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
//将字符串转为byte类型数组
byte[] byteArrays=str.getBytes();
bufferedOutputStream.write(byteArrays);
//清空缓冲区
bufferedOutputStream.flush();
//关闭流
outputStream.close();
bufferedOutputStream.close();
}
}
6.字符输出缓冲流
public class BufferedWriter extends Writer {
......
}
package iotest;
import java.io.*;
public class BufferedWirterDemo {
public static void main(String[] args) throws IOException {
String str="Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1] 。\n" +
"Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3] 。";
//文件
File file=new File("D://java1.txt");
//节点流,输出字符流
Writer writer=new FileWriter(file);
//处理流,字符输出缓冲流
BufferedWriter bufferedWriter=new BufferedWriter(writer);
bufferedWriter.write(str);
//清空缓冲区
bufferedWriter.flush();
//关闭流
bufferedWriter.close();
writer.close();
}
}
7.注意事项
1.只要是缓冲流就是处理流,不能直接与文件对接,需要对字节流进行操作。
2.记得输出流写flush()
方法和关闭流。
3.注意字节流的基本单位是字节,UTF-8中一个英文是1个字节,一个汉字是3个字节;字符流的基本单位是字符,UTF-8中一个英文是1个字符,一个汉字是1个字符。
5.序列化与反序列化
序列化就是指将Java对象转化为字节序列的过程。
反序列化就是指将字节序列恢复为Java对象的过程。
1.序列化
1.实体类实现序列化接口(Serializable接口)
package iotest;
import java.io.Serializable;
public class User implements Serializable {
int id;
String name;
String sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public User(int id, String name, String sex) {
this.id = id;
this.name = name;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
2.对实例化对象进行序列化处理,通过数据流(ObjectOutputStream)写入文件中。需要注意的是ObjectOutputStream是一个处理流。
package iotest;
import java.io.*;
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//对象
User user=new User(1,"张三","男");
//文件
File file=new File("D://test.txt");
//节点流,输出字节流
OutputStream outputStream=new FileOutputStream(file);
//处理流,对象输出字节流
ObjectOutputStream objectOutputStream=new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(user);
//关闭流
outputStream.close();
objectOutputStream.close();
}
}
2.反序列化
将字节序列恢复为Java对象,通过数据流(ObjectInputStream)读入Java程序,同样的ObjectInputStream是个处理流。
package iotest;
import java.io.*;
import java.util.function.DoubleToIntFunction;
public class ObjctInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//文件
File file=new File("D://test.txt");
//节点流,输入字节流
InputStream inputStream=new FileInputStream(file);
//处理流
ObjectInputStream objectInputStream=new ObjectInputStream(inputStream);
User user=(User)objectInputStream.readObject();
System.out.println(user.toString());
//关闭流
objectInputStream.close();
inputStream.close();
}
}
6.IO流的应用
IO流就是完成文件传输(文件上传、文件下载、发朋友圈等)
1.复制文件
package iotest;
import java.io.*;
import java.util.Scanner;
public class IODemo {
public static void main(String[] args) throws IOException {
System.out.println("欢迎使用文件复制系统!");
System.out.println("文件复制请按1,退出请按0");
Scanner scanner =new Scanner(System.in);
int i;
i=scanner.nextInt();
while(i==1){
System.out.println("请输入文件原目录:");
String str1=scanner.next();
System.out.println("请输入想复制到的文件地址:");
String str2=scanner.next();
//文件复制,返回结果
String str=copyFile(str1,str2);
System.out.println(str);
System.out.println("欢迎使用文件复制系统!");
System.out.println("文件复制请按1,退出请按0");
i=scanner.nextInt();
}
System.out.println("欢迎下次使用!");
}
public static String copyFile(String str1,String str2) throws IOException {
//文件a
File file1=new File(str1);
//文件b
File file2=new File(str2);
//节点流,输入流,输入到Java程序,输入字节流
InputStream inputStream=new FileInputStream(file1);
//处理流,缓冲流,字节输入缓冲流
BufferedInputStream bufferedInputStream=new BufferedInputStream(inputStream);
byte[] byteArrays=new byte[(int)file1.length()];
//读取文件,存入数组
bufferedInputStream.read(byteArrays);
//节点流,输出流,输出到硬盘,输出字节流
OutputStream outputStream=new FileOutputStream(file2);
//处理流,缓冲流,字节输出缓冲流
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream);
//写入文件
bufferedOutputStream.write(byteArrays);
//清空缓冲区
bufferedOutputStream.flush();
//关闭流
inputStream.close();
outputStream.close();
bufferedInputStream.close();
bufferedOutputStream.close();
//判断复制后的文件是否存在
if(file2.exists()){
return "复制成功!";
}else{
return "复制失败!";
}
}
}
我们也可以修改方法中的一段代码使他变成边读边写的形式。
//边度边写
int temp;
while((temp=bufferedInputStream.read())!=-1){
bufferedOutputStream.write(temp);
}
使用字节流是为了能传输更多类型的数据:文本、音频、视频、照片等。
字符流只能去读取文本类型的数据(txt,word等),使用字符流去读非文本类型数据,能读取,但是吧把其中的结构(字节的顺序)打散了。
7.总结
1,注意区分节点流和处理流,节点流是最基础的流,所以节点流类能直接接触文件对象;处理流是对节点流进行操作的,所以处理流类不能直接接触文件对象,接触节点流类对象。
2.字符流只能用于文件传输文本类型的数据,而字节流能处理文本类型和非文本类型的数据。
3.使用完IO流之后一定要记得关闭,JDK1.7以上支持自动关闭IO流
参考博客与感谢
感谢:
B站@楠哥教你学Java
参考博客: