文章目录
IO
IO的分类
按流向分(以内存为参照物)
- 输出流: 内存—> 磁盘
- 输入流: 磁盘—> 内存
按照数据类型分
- 字节流: 逻辑单位是字节,(1B = 8bit 0000 0000)
- 字符流: 逻辑单位是字符(理解为一种文化符号,abc , “你”, “の”)
4个抽象基类
-
字节输出流: OutputStream
-
字节输入流: InputStream
-
字符输出流: Writer
-
字符输入流: Reader
一般来讲, 纯文本文件用字符流 .txt .java .cpp
其他情况用字节流(字节流是万能的) .word .ppt .mp4 .mp3 .jpg . png .exe
字节流
字节输出流
抽象基类OutputStream
抽象基类OutputStream的定义:此抽象类是表示输出字节流的所有类的父类
继承关系:
成员方法:
FileOutputStream文件字节输出流
用于将数据写入 File
构造方法:
FileOutputStream(File file) // 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append)
// 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(String fileName) // 创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append)
// 创建一个向具有指定 name 的文件中写入数据的输出文件流 append
// - 如果为 true,则将字节写入文件末尾处,而不是写入文件开始处
eg:
// 1.
File f = new File("D:\\Java_test\\a.text");
FileOutputStream out1 = new FileOutputStream(f);
// 2.
FileOutputStream out2 = new FileOutputStream(new File("a.txt"));
// 3.
FileOutputStream out3 = new FileOutputStream("a.txt");
成员方法:
- 写数据的步骤
-
- 创建输出流对象
-
- write
-
- 释放资源 close
-
eg:
// 1. 创建输出流对象
FileOutputStream out = new FileOutputStream("a.txt");
// 2.1 write(int b) 写单个字节
out.write(97);
// 2.2 write(byte[] b) 批量写
String s = "hello world";
byte[] bytes = s.getBytes();
out.write(bytes);
// 2.3 write(byte [] b , int off , int len) 写字节数组的部分
out.write(bytes,0,5);
// 3. 释放资源 close
out.close();
注意事项:
- 当我们创建一个输出流对象的时候,发生了什么?
- jvm向操作系统中看这个文件是否存在
- 如果文件不存在, 帮我们创建
- 文件已经存在, 覆盖重新开始写
实现文件追加功能:
借助于带append参数的构造方法
eg:
// 使用带append的构造方法
// 创建输出流对象
FileOutputStream out = new FileOutputStream("a.txt", true);
// write
out.write(100);
// close
out.close();
实现换行功能:
借助于换行符
- “\r\n”
System.lineSeparator()
eg:
// 创建输出流对象
FileOutputStream out = new FileOutputStream("a.txt");
out.write("abcd".getBytes());
// write 写换行符
// \r\n
out.write("\r\n".getBytes());
out.write(97);
// 使用系统默认换行符
out.write(System.lineSeparator().getBytes());
out.write(97);
// close
out.close();
异常处理:
- 采用
try-catch-finally
eg:
FileOutputStream out = null;
try {
out = new FileOutputStream("a.txt");
out.write("hello world".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
- 采用
try-with-resources
语法:
try(资源,只要实现了AutoCloseable接口的类){
//可能出现异常的代码
// 当出了try代码块的时候 close方法会自动执行 资源会被自动释放
}catch(){
}
eg:
try(FileOutputStream out = new FileOutputStream("a.txt")) {
// write数据
out.write("abc".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
--------------------------------------------------------------------
/*
验证close方法自动执行
*/
public class Demo {
public static void main(String[] args) {
try (A a = new A()){
// 调用func 不调用close
a.func();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class A implements AutoCloseable{
@Override
public void close() throws Exception {
System.out.println("close执行了");
}
public void func(){
System.out.println("func方法执行了");
}
}
为什么要close
?
- jvm使用了不属于jvm的资源, 不能通过GC回收, 只能功能close显式的释放资源.
BufferedOutputStream缓冲字节输出流
该类实现缓冲的输出流。
通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
构造方法:
(装饰器设计模式)
BufferedOutputStream(OutputStream out)
// 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。默认缓冲区大小是8KB
BufferedOutputStream(OutputStream out, int size)
// 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。指定缓冲区size大小
成员方法:
字节缓冲输出流的步骤:
-
- 创建输出流对象
-
- write写数据
-
- flush
-
- close
eg:
// 1. 创建输出流对象
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(
new FileOutputStream("a.txt"));
// 2. write
// write(b)
bufferedOutputStream.write(97);
bufferedOutputStream.write(System.lineSeparator().getBytes());
// write(byte[] b)
bufferedOutputStream.write("yyds".getBytes());
bufferedOutputStream.write(System.lineSeparator().getBytes());
// write(byte[] b, int off, int len)
bufferedOutputStream.write("hello world".getBytes(),0,5);
bufferedOutputStream.write(System.lineSeparator().getBytes());
// 3. flush
bufferedOutputStream.flush();
// 4. close
bufferedOutputStream.close();
字节输入流
抽象基类InputStream
此抽象类是表示字节输入流的所有类的父类。
继承关系:
成员方法:
注意:
读入缓冲区的总字节数;如果因为已经到达流末尾而不再有数据可用,则返回 -1。返回值表示读取的字节的个数 readCount
FileInputStream文件字节输入流
构造方法:
FileInputStream(File file)
// 通过打开一个到实际文件的连接来创建一个 FileInputStream,
// 该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(String fileName)
// 通过打开一个到实际文件的连接来创建一个 FileInputStream,
// 该文件通过文件系统中的路径名 name 指定。
成员方法:
读取数据的步骤:
-
- 创建输入流对象
-
- read
-
- close释放资源
eg:
// 1. 创建输入流对象
FileInputStream in = new FileInputStream("a.txt");
// 2.1 read() 读取单字节,返回值是读取到的字节值
int readData = in.read();
System.out.println("readData = " + ((char)readData));
// 2.2 read(byte[] b)
byte[] bytes = new byte[1024];
int readCount = in.read(bytes);
System.out.println("readCount = " + readCount);// 读取到字节的个数
// byte[] ---> String
System.out.println("new String() = " + new String(bytes));
// 2.3 read(byte[],int off,int len)
byte[] bytes1 = new byte[1024];
int readCount1 = in.read(bytes1,2,3);
System.out.println("readCount = " + readCount1);// 读取到字节的个数
// 3. close
in.close();
BufferedInputStream缓冲字节输入流
构造方法:
(装饰器设计模式)
BufferedInputStream(InputStream in)
// 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。默认缓冲区8KB
BufferedInputStream(InputStream in, int size)
// 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
// 使用指定的缓冲区
成员方法:
读取数据的步骤:
-
- 创建缓存的输入流对象
-
- read
-
- close释放资源
eg:
// 1. 创建输入流对象
BufferedInputStream in =
new BufferedInputStream(new FileInputStream("a.txt"));
// 2. read
// 2.1 单个读取
int readData = in.read();
System.out.println(readData);
// 2.2 批量
byte[] bytes = new byte[1024];
int readCount = in.read(bytes);
System.out.println(new String(bytes,0,readCount));
// 3. close
in.close();
文件复制功能
思路:
- 读取源文件, 把数据读取到内存里
- 把内存的数据写到新文件
eg:
/*
文件的复制
*/
// 1. 创建输入流对象
FileInputStream in = new FileInputStream("a.txt");
// 2. 创建输出流对象
FileOutputStream out = new FileOutputStream("a_Copy.txt");
// 3. 边读边写
// 3.1 单字节复制
long timeStart = System.currentTimeMillis();
int readData;
while ((readData = in.read()) != -1) {
// 写入
out.write(readData);
}
// 3.2 字节数组方式复制
int readCount;
byte[] bytes = new byte[1024];
while((readCount = in.read(bytes)) != -1){
out.write(bytes,0,readCount);
}
long timeEnd = System.currentTimeMillis();
System.out.println(timeEnd - timeStart + "ms");
// 4. 释放资源
in.close();
out.close();
单字节复制还是字节数组方式效率高?
- 字节数组批量的方式效率高
为什么?
- 批量的方式会减少跟操作系统的交互
构建文件复制的工具类:
import java.io.*;
public class FileUtils {
public static void copy1(InputStream in, OutputStream out) throws IOException{
int readData;
while((readData = in.read()) != -1){
out.write(readData);
}
in.close();
out.close();
}
public static void copy2(InputStream in, OutputStream out) throws IOException{
int readCount;
byte[] bytes = new byte[1024];
while((readCount = in.read(bytes)) != -1){
out.write(bytes,0,readCount);
}
in.close();
out.close();
}
}
使用缓冲的输入流复制文件:
BufferedInputStream in =
new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream out =
new BufferedOutputStream(new FileOutputStream("a_Copy.txt"));
// 利用工具类进行复制
long start = System.currentTimeMillis();
FileUtils.copy1(in,out);
FileUtils.copy2(in,out);
long end = System.currentTimeMillis();
System.out.println(end - start + "ms");
使用字节流复制
文本文件: 正常复制
图片文件: 正常复制
视频文件: 正常复制