IO流概述
- 什么是IO流?
IO流指的是:程序中数据的流动。数据可以从内存流动到硬盘,也可以从硬盘流动到内存。
Java中IO流最基本的作用是:完成文件的读和写。- IO流的分类?
根据数据流向分为:输入和输出是相对于内存而言的。
① 输入流:从硬盘到内存。(输入又叫做读:read、input)
② 输出流:从内存到硬盘。(输出又叫做写:write、output)
根据读写数据形式分为:
① 字节流:一次读取一个字节。适合读取非文本数据。例如图片、声音、视频等文件。(当然字节流是万能的。什么都可以读和写。)
②字符流:一次读取一个字符。只适合读取普通文本。不适合读取二进制文件。因为字符流统一使用Unicode编码,可以避免出现编码混乱的问题。
根据流在IO操作中的作用和实现方式来分类:
① 节点流:节点流负责数据源和数据目的地的连接,是IO中最基本的组成部分。
② 处理流:处理流对节点流进行装饰/包装,提供更多高级处理操作,方便用户进行数据处理。
Java中已经将IO流实现了,在java.io包下,可以直接使用。
- 注意:Java的所有IO流中凡是以Stream结尾的都是字节流。凡是以Reader和Writer结尾的都是字符流。
IO流体系结构
所有的流都实现了Closeable接口,都有close()方法,流用完要关闭。
所有的输出流都实现了Flushable接口,都有flush()方法,flush方法的作用是,将缓存清空,全部写出。养成好习惯,以防数据丢失。
- 四个头领:都是抽象类
Inputstream
Outputstream
Reader
Writer - File相关的:
FileInputstream
FileOutputstream
FileReader
Filewriter - Buffered相关的:
BufferedInputstream
Bufferedoutputstream
BufferedReader
BufferedWriter - 转换流:
InputStreamReader
OutputstreamWriter - 标准输出流:
Printstream
PrintWriter - Data相关的:
DataInputStream
Data0utputstream - object相关的:
ObjectInputstream
Objectoutputstream - 线程相关的:
PipedInputstream
Pipedoutputstream - 压缩和解压缩相关的:
GZIPInputstream
GZIPOutputstream - 字节数组相关的:
ByteArrayInputstream
ByteArrayoutputstream
FileInputStream 字节输入流
1. 称为文件字节输入流。负责读。
2. 是一个万能流,任何文件都能读。但还是建议读二进制文件。例如:图片,声音,视频等。
3.但是FileInputstream肯定也是可以读普通文本的。只不过一次读取一个字节。容易出现乱码问题。
4.FileInputstream的常用构造方法:
FileInputStream(string name)通过文件路径构建一个文件字节输入流对象。
5.FileInputStream的常用方法:
int read(); 调用一次read()方法则读取一个字节,返回读到的字节本身。如果读不到任何数据则返回 -1
int read(byte[]b); 一次最多可以读到b.length个字节(只要文件内容足够多)。返回植是读取到的字节数量。如果这一次没有读取到任何数据,则返回 -1
int read(byte[l b,int off,int len); 一次读取len个字节。将读到的数据从byte数组的off位置开始放。
void close()关闭流
Long skip(long n);跳过个字节。
int available();获取流中剩余的估计字节数。
// 示例
FileInputStream fileInputStream = new FileInputStream("a.txt");
// try-with-resource,流自动关闭
try (fileInputStream) {
// 一次读取字节数
byte[] bytes = new byte[1024];
// 每次读取的数量
int len;
while ((len = fileInputStream.read(bytes)) > 0) {
System.out.println(new String(bytes, 0, len));
}
}
FileOutputStream 文件字节输出流
1. 文件字节输出流,负责写。
2. 常用构造方法:
File0utputStream(string name)
创建一个文件字节输出流对象,这个流在使用的时候,最开始会将原文件内容全部清空,然后写入。
File0utputStream(String name,boolean append)
创建一个文件字节输出流对象,当append是true的时候,不会清空原文件的内容,在原文件的末尾追加写入。
创建一个文件字节输出流对象,当append是false的时候,会清空原文件的内容,然后写入。
3.
常用方法:
void close();
void flush();
void write(int b);写一个字节
void write(byte[]b);将整个byte字节数组写出
void write(byte[]b,int off,int len) 将byte字节数组的一部分写出。
// 示例 文件复制
// 输入流
FileInputStream fileInputStream = new FileInputStream("a.txt");
// 输出流
FileOutputStream fileOutputStream = new FileOutputStream("b.txt");
// try-with-resource,流自动关闭
try (fileInputStream; fileOutputStream) {
// 一次读取字节数
byte[] bytes = new byte[1024];
// 每次读取的数量
int len;
while ((len = fileInputStream.read(bytes)) > 0) {
// 把每次读取的字节数写入到输出流中,大小为读取的字节数
fileOutputStream.write(bytes, 0, len);
}
}
FileReader 文件字符输入流
常用的构造方法:
FileReader (String fileName)
常用的方法:
int read ()
int read(char[] cbuf);
int read (char[] cbuf, int off, int len)
long skip(long n):
void close()
FileWrite
文件字符输出流
常用的构造方法
FileWriter (String fileName)
FileWriter(String fileName, boolean append)
常用的方法:
void write(char[] cbuf)
void write(char[] cbuf, int off, int len);
void write(String str);
void write(String str, int off, int len):
void flush();
void close();
Writer append(CharSequence csq, int start, int end)
使用FileReader和FileWriter拷贝普通文本文件
// 示例
FileReader fileReader = new FileReader("a.txt");
FileWriter fileWriter = new FileWriter("b.txt");
// try-with-resource,流自动关闭
try (fileReader; fileWriter) {
// 一次读取字符数
char[] buf = new char[1024];
// 每次读取的字符数
int len;
while ((len = fileReader.read(buf)) != -1) {
fileWriter.write(buf, 0, len);
}
}
缓冲流 BufferedReader/BufferedInputstream
- 输入缓冲流
1.java.io.BufferedInputstream的用法和FileInputstream用法相同。
他们的不同点是:
2.FileInputstream是节点流。
BufferedInputstream是缓冲流(包装流|处理流)。这个流的效率高。自带缓冲区。并且自己维护这个缓冲区。读大文件的时候建议采用这个缓冲流来读取。
3.BufferedInputStream对 FileInputStream 进行了功能增强。增加了一个缓冲区的功能。
4.创建一个BufferedInputstream对象,构造方法:BufferedInputStream(InputStream in)
- 输出缓冲流
1.java.io.Bufferedoutputstream也是一个缓冲流。属于输出流
2.创建Bufferedoutputstream对象:Buffered0utputStream(0utputstream out)
3.File0utputStream是节点流。Buffered0utputstream是包装流。
- 文件拷贝 缓冲字节流
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("b.txt"));
try (bufferedInputStream; bufferedOutputStream) {
byte[] bytes = new byte[1024];
int len;
while ((len = bufferedInputStream.read(bytes)) > 0) {
bufferedOutputStream.write(bytes, 0, len);
}
}
- 文件拷贝 缓冲字符流
BufferedReader bufferedReader = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("b.txt"));
try (bufferedReader; bufferedWriter) {
String s = null;
while ((s = bufferedReader.readLine()) != null) {
bufferedWriter.write(s);
}
}
BufferedReader/BufferedInputstream的两个方法:
1.mark方法:在当前的位置上打标记
2.reset方法:回到上一次打标记的位置
这两个方法的调用顺序是:
先调用mark,再调用reset
这两个方法组合起来完成的任务是:某段内容重复读取。
转换流 InputStreamReader/OutputStreamWriter
- InputStreamReader
使用InputStreamReader时,可以指定解码的字符集。用来解决读过程中的乱码问题
InputstreamReader是一个字符流。是一个转换流。
InputstreamReader是一个输入的过程。
InputstreamReader是一个解码的过程
InputStreamReader常用的构造方法:
InputStreamReader(InputStream in)采用平台默认的字符集进行解码。
InputStreamReader(Inputstream in,String charsetName) 采用指定的字符集进行解码。
FileReader实际上是InputStreamReader的子类
FileReader也是一个包装流,不是节点流。
- OutputStreamWriter
0utputstreamWriter也是一个字符流。也是一个转换流。
0utputStreamWriter是一个编码的过程。
如果0utputStreamWriter在编码的过程中使用的字符集和文件的字符集不一致时会出现乱码。
FileWriter是outputstreamWriter的子类
FileWriter的出现简化了java代码。
FileWriter是一个包装流,不是节点流。
- 指定字符集进行文件复制
// 指定UTF-8字符集进行解码
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("a.txt"), StandardCharsets.UTF_8);
// 写入文件指定GBK字符集进行编码
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("b.txt"), Charset.forName("GBK"));
try (inputStreamReader; outputStreamWriter) {
char[] buffer = new char[1024];
int read;
while ((read = inputStreamReader.read(buffer)) != -1) {
outputStreamWriter.write(buffer, 0, read);
}
}
数据流
java.io.Data0utputstream:数据流(数据字节输出流)
作用:将java程序中的数据直接写入到文件,写到文件中就是二进制。
Data0utputStream写的效率很高,原因是:写的过程不需要转码。
Data0utputStream写到文件中的数据,只能由DataInputStream来读取。
java.io.DataInputStream:数据流(数据字节输入流)作用:专门用来读取使用Data0utputstream流写入的文件。
注意:读取的顺序要和写入的顺序一致。(要不然无法恢复原样。)
对象流(序列化&反序列化)
重点:
1.凡是参与序列化和反序列化的对象必须实现java.io.Serializable 可序列化的接口。
2.这个接口是一个标志接口,没有任何方法。只是起到一个标记的作用。
3.当java程序中类实现了Serializable接口,编译器会自动给该类添加一个“序列化版本号”序列化版本号:serialVersionuID:
@Serial
private static final long serialVersionUID = 随机数
4.在Java语言中是如何区分class版本的?首先通过类的名字,然后再通过序列化版本号进行区分的。
5.transient 关键字修饰的属性不参与序列化
java.io.0bjectoutputStream:对象流(对象字节输出流)
1.它的作用是完成对象的序列化过程。
2.它可以将JVM当中的Java对象序列化到文件中/网络中。
3.序列化:将Java对象转换为字节序列的过程。(字节序列可以在网络中传输。)
4.序列化:serial
java.io.0bjectInputstream:对象流(对象字节输入流)
1.专门完成反序列化的。(将字节序列转换成JVM当中的iava对象。)
// 序列化
Date date = new Date();
Date date2 = new Date();
Date date3 = new Date();
ArrayList<Date> dates = new ArrayList<>();
dates.add(date);
dates.add(date2);
dates.add(date3);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("data.txt"));
objectOutputStream.writeObject(dates);
objectOutputStream.flush();
objectOutputStream.close();
// 反序列化
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("data.txt"));
List<Date> dates = (List<Date>) objectInputStream.readObject();
System.out.println(dates);
objectInputStream.close();
打印流
PrintStream 打印流(字节形式)
主要用在打印方面,提供便捷的打印方法和格式化输出。主要打印内容到文件或控制台。
常用方法: print(Type x) printIn(Type x)
便捷在哪里?
1.直接输出各种数据类型
2.自动刷新和自动换行(println方法)
3.支持字符串转义
4.自动编码(自动根据环境选择合适的编码方式)
格式化输出?调用printf方法。
1.%s 表示字符串
2.%d 表示整数
3.%f 表示小数(%.2f 这个格式就代表保留两位小数的数字。)
4.%c 表示字符
PrintWrite
1.打印流(字符形式)注意Printwriter使用时需要手动调用flush()方法进行刷新。
2.比PrintStream多一个构造方法,PrintStream参数只能是0utputStream类型,但PrintWriter参数可以是0utputStream,也可以是Writer。
3.常用方法: print(Type x) printIn(Type x)
4.同样,也可以支持格式化输出,调用printf方法。
PrintStream printStream = new PrintStream(new FileOutputStream("a.txt"));
printStream.flush();
printStream.close();
printStream.println("Hello World");
// 方式一 传write
PrintWriter printWriter = new PrintWriter(new FileWriter("b.txt"));
// 方式二 传stream
PrintWriter printWriter = new PrintWriter(new FileOutputStream("b.txt"));
printWriter.println("Hello World2");
printWriter.flush();
printWriter.close();
标准输入流&标准输出流
标准输入流
1.System.in获取到的InputStream就是一个标准输入流。
2.标准输入流是用来接收用户在控制台上的输入的。(普通的输入流,是获得文件或网络中的数据)
3.标准输入流不需要关闭。(它是一个系统级的全局的流,JVM负责最后的关闭。)
4.也可以使用BufferedReader对标准输入流进行包装。这样可以方便的接收用户在控制台上的输入。
(这种方式太麻烦了,因此JDK中提供了更好用的Scanner。)
5.当然,你也可以修改输入流的方向(System.setIn())。让其指向文件。
InputStream in = System.in;
byte[] buf = new byte[1024];
int read = in.read(buf);
String s = new String(buf, 0, read);
System.out.println(s);
标准输出流:System.out
1.标准输出流怎么获取? System.out
2.标准输出流是向哪里输出呢? 控制台。
3.普通输出流是向哪里输出呢? 文件或者网络或者其他.....
4.标准输出流是一个全局的输出流,不需要手动关闭。JVM退出的时候,JVM会负责关闭这个流。
File
File file = new File("dir");
// 获取文件夹列表
File[] files = file.listFiles();
for (File f : files) {
System.out.println(f.getName());
}
file.isFile();
file.isDirectory();
file.isHidden();
...
读取属性配置文件
- Properties
使用Properties集合类+I0流来读取性置文件
将属性配置文件中的配置信息加载到内存中。
配置文件 config.properties
name=xx
sex=nn
// 获取配置文件内容
Properties properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties"));
// 获取配置文件某个key
System.out.println(properties.get("name"));
- ResourceBundle 资源绑定
// 只能获取当前项目下的文件,不带后缀.properties
ResourceBundle bundle = ResourceBundle.getBundle("config");
System.out.println(bundle.getString("name"));
压缩和解压流
- 压缩
FileInputStream fileInputStream = new FileInputStream("a.txxt");
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(new FileOutputStream("z.gz"));
byte[] buffer = new byte[1024];
int length;
while ((length = fileInputStream.read(buffer)) > 0) {
gzipOutputStream.write(buffer, 0, length);
}
fileInputStream.close();
gzipOutputStream.close();
- 解压
GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream("b.gz"));
FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
byte[] buffer = new byte[1024];
int len;
while ((len = gzipInputStream.read(buffer)) > 0) {
fileOutputStream.write(buffer, 0, len);
}
gzipInputStream.close();
fileOutputStream.close();
字节数组流
1.ByteArrayInputstream和ByteArray0utputStream都是内存操作流,不需要打开和关闭文件等操作。这些流是非常常用的,可以将它们看作开发中的常用工具,能够方便地读写字节数组、图像数据等内存中的数据。
2.ByteArrayInputStream和ByteArray0utputStream都是节点流。
3.ByteArray0utputStream,将数据写入到内存中的字节数组当中。
4.ByteArraylnputStream,读取内存中某个字节数组中的数据。
对象克隆
对象的深克隆
1.除了深克隆是重写clone()方法,使用字节数组流也可以完成对象的深克隆。
2.原理是:将要克隆的java对象写到内存中的字节数组中,再从内存中的字节数组中读取对象,读取到的对象就是一个深克隆。
3.目前为止,对象拷贝方式:
- 调用0bject的clone方法,默认是浅克隆,需要深克隆的话,就需要重写clone方法。
- 可以通过序列化和反序列化完成对象的克隆。
同也可以通过ByteArrayInputStream和ByteArray0utputStream完成深克隆。
Address addr = new Address("北京");
User user = new User("张三", addr);
// 将对象写入到字节数组
// 获取一个字节输出流,相当于创建了一个数组充当输出流源
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(user);
// 从字节数组中读取一个对象 完成了深克隆
// 从刚才的字节数组中读取数据流,充当输入数据源
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
User user2 = (User) objectInputStream.readObject();