I/O
流:字节序列的抽象概念
流的分类:
- 按数据流的流向(站在程序的角度看)
- 输入流
- 输出流
- 按操作数据的单位
- 字节流 一个字节一个字节的读取 1 byte
- 字符流 一个字符一个字符的读取 2 byte
IO流应用场景:
- 纯文本文件,优先使用字符流
- 图片、视频、音频等二进制文件,优先使用字节流
- 不确定文件类型,优先使用字节流,字节流是万能的流[推荐]
字节流
有2个抽象基类 InputStream 和 OutputStream,分别处理字节流的输入和输出。
InputStream 提供的方法
| 方法签名 | 描述 | 备注 |
|---|---|---|
int available() | 返回可从此输入流读取(或跳过)而不会因下次调用此输入流的方法而阻塞的字节数的估计值。 | 常用 |
void close() | 关闭此输入流并释放与该流关联的所有系统资源。 | 常用;读取流占用系统资源,使用完必须关闭 |
abstract int read() | 从输入流中读取下一个字节的数据。 | |
int read(byte[] b) | 从输入流中读取一定数量的字节,并将它们存储到缓冲区数组b中。 | 常用 |
int read(byte[] b, int off, int len) | 将最多len字节的数据从输入流读入字节数组。 |
OutputStream 提供的方法
| 方法签名 | 描述 | 备注 |
|---|---|---|
void close() | 关闭此输出流并释放与此流关联的所有系统资源。 | |
void flush() | 刷新此输出流并强制写出任何缓冲的输出字节。 | |
void write(byte[] b) | 将b.length字节从指定的字节数组写入此输出流。 | |
void write(byte[] b, int off, int len) | 将b.length字节从指定的字节数组写入此输出流。 |
InputStream 和 OutputStream 都是抽象类,要使用他们,必须用其子类。
FileInputStream 和 FileOutputStream
这2个类是最常用的了;
字节流读写文件标准写法:
public static void customBufferStream(){
// 因为在finally语句中将关闭流,所以将其定义在try之外
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
// 创建一个文件输入流
fileInputStream = new FileInputStream(new File("/Users/hlw/Desktop/111.png"));
// 创建一个文件输出流
fileOutputStream = new FileOutputStream(new File("/Users/hlw/Desktop/111_copy.png"));
// 读取的字节长度
int len;
// 定义缓冲区大小
byte[] buffer = new byte[1024];
long start = System.currentTimeMillis();
// 将输入流读取到 buffer 中,如果读到的长度不是-1,
while ((len = fileInputStream.read(buffer)) != -1){
// 将buffer 写入输出流
fileOutputStream.write(buffer, 0, len);
}
long end = System.currentTimeMillis();
System.out.println("use time [ " + (end - start) + " ] ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭2个流
if(fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ObjectInputStream 和 ObjectOutputStream
读取对象,作为字节流。例如,将内存中的对象转换为二进制数据流的形式输出,保存到硬盘,这叫作对象的序列化。通过将对象序列化,可以方便地实现对象的传输和保存。
static class Person implements Serializable{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// ... 省略name age getter setter 方法
}
public static void objectSerilized(){
// 对象序列化
Person p = new Person("hlw", 12);
System.out.println(p.hashCode());
try {
// 创建文件输出流
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/hlw/Desktop/person.dat"));
// 包装成对象输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
// 写对象
objectOutputStream.writeObject(p);
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try {
// 创建文件输入流
FileInputStream fileInputStream = new FileInputStream(new File("/Users/hlw/Desktop/person.dat"));
// 包装成对象输入流
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
// 读对象,并类型转换
p = (Person) objectInputStream.readObject();
System.out.println(p.hashCode() + "name:" + p.getName() + " age:" + p.getAge());
objectInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
输出如下
1996181658
1327763628name:hlw age:12
Process finished with exit code 0
- 序列化前和反序列化后,不是同一个对象。
- 被存储和被读取的对象都必须实现
java.io.Serializable接口,否则会报NotSerializableException异常。
DataInputStream和DataOutputStream
将对象中的一部分数据进行序列化和反序列化的类
public static void testDateStream(){
try {
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/hlw/Desktop/data.txt"));
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);
dataOutputStream.write(10);
dataOutputStream.writeChar('a');
dataOutputStream.writeInt(10);
dataOutputStream.writeBoolean(true);
dataOutputStream.writeUTF("你好");
FileInputStream fileInputStream = new FileInputStream(new File("/Users/hlw/Desktop/data.txt"));
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
System.out.println(dataInputStream.read());
System.out.println(dataInputStream.readChar());
System.out.println(dataInputStream.readInt());
System.out.println(dataInputStream.readBoolean());
System.out.println(dataInputStream.readUTF());
dataOutputStream.close();
dataInputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
读取的时候顺序一定要和写入一致,否则报错
PrintStream
打印流
虽然见的很少,但却一直在用它,常见的 System.out.println(); 这个 out 就是 PrintStream
public static void testPrintStream(){
try {
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/hlw/Desktop/111.txt"), true);
PrintStream out = new PrintStream(fileOutputStream);
out.println("printStream 你好呀");
out.println(1);
out.println('a');
out.println(1.2);
out.println(true);
out.println(1.20f);
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
字符流
也有两个抽象基类,分别是 Reader 和 Writer ,分别用来读取和写入字符
Reader 常用方法
| 方法签名 | 描述 | 备注 |
|---|---|---|
abstract void close() | 关闭流并释放与之关联的所有系统资源。 | |
int read() | 读取单个字符。 | |
int read(char[] cbuf) | 将字符读入数组。 | |
boolean ready() | 告诉该流是否准备好被读取。 |
Writer 常用方法
| 方法签名 | 描述 | 备注 |
|---|---|---|
abstract void close() | 关闭这个流,先flush | |
abstract void flush() | 冲洗该流,将缓冲区数据刷新到磁盘(个人理解,有误感谢指出) | |
void write(char[] cbuf) | 写入字符数组。 | |
void write(int c) | 写入单个字符 | |
void write(String str) | 写入一个字符串 |
子类实现
FileReader和FileWriter
FileReader 和 FileWriter 用来从文件中读取字符,写入字符
public static void testReader(){
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
fileReader = new FileReader(new File("/Users/hlw/Desktop/111.txt"));
fileWriter = new FileWriter(new File("/Users/hlw/Desktop/111_copy.txt"));
int zhar;
// 每次读取一个字符
while ((zhar = fileReader.read()) != -1){
System.out.println((char) zhar);
// 写入一个字符
fileWriter.write(zhar);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileReader != null){
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileWriter != null){
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedReader 和BufferedWriter
带缓冲区的字符流,提高读写效率
public static void testBufferedReader(){
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
FileReader fileReader = new FileReader(new File("/Users/hlw/Desktop/111.txt"));
bufferedReader = new BufferedReader(fileReader);
FileWriter fileWriter = new FileWriter(new File("/Users/hlw/Desktop/111_copy.txt"));
bufferedWriter = new BufferedWriter(fileWriter);
String line;
// 可以读一行字符
while ((line = bufferedReader.readLine()) != null){
System.out.println(line);
// 写入字符
bufferedWriter.write(line);
// 换行
bufferedWriter.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bufferedReader != null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufferedWriter != null){
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
LineNumberReader
可以跟踪行号的流
public static void testLineNumberReader(){
LineNumberReader lineNumReader = null;
BufferedWriter bufferedWriter = null;
try {
FileReader fileReader = new FileReader(new File("/Users/hlw/Desktop/111.txt"));
// 使用 LineNumberReader 包装流
lineNumReader = new LineNumberReader(fileReader);
FileWriter fileWriter = new FileWriter(new File("/Users/hlw/Desktop/111_copy.txt"));
bufferedWriter = new BufferedWriter(fileWriter);
String line;
// 可以读一行字符
while ((line = lineNumReader.readLine()) != null){
System.out.println("当前行:" + lineNumReader.getLineNumber() + " : " + line);
// 写入字符
bufferedWriter.write(line);
// 换行
bufferedWriter.newLine();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(lineNumReader != null){
try {
lineNumReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufferedWriter != null){
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
InputStreamReader 和 OutputStreamWriter
转换流: 实质就是输入对象类型是 InputStream ,前面的 fileReader ,输入对象类型是文件类型的。
在某种情况下,字节流需要与字符流互相转换,就用到这2个流
InputStreamReader:接收InputStream,读取到 字符流 Reader
OutputStreamWriter:接收OutputStream,输出到 字节流
FileInputStream fileInputStream = new FileInputStream(new File("/Users/hlw/Desktop/111.txt"));
// 也就是输入对象 是 InputStream
// 这里接收文件流
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
FileOutputStream fileOutputStream = new FileOutputStream(new File("/Users/hlw/Desktop/111_copy.txt"));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
int size;
char[] cahrs = new char[1024];
while ((size = inputStreamReader.read(cahrs)) != -1){
outputStreamWriter.write(cahrs);
}
inputStreamReader.close();
outputStreamWriter.close();
File
可代表文件,也可代表目录
file常见方法
| 方法签名 | 描述 | 备注 |
|---|---|---|
boolean delete() | 删除此抽象路径名表示的文件或目录。 | |
boolean exists() | 测试此抽象路径名表示的文件或目录是否存在。 | |
String getParent() | 返回此抽象路径名的父目录的路径名字符串,如果此路径名没有命名父目录,则返回null。 | |
File getParentFile() | 返回此抽象路径名的父目录的抽象路径名,如果此路径名没有命名父目录,则返回null。 | |
boolean isDirectory() | 测试此抽象路径名表示的文件是否为目录。 | |
long length() | 返回由抽象路径名表示的文件长度。 | |
String[] list() | 返回一个字符串数组,该字符串数组命名此抽象路径名所表示的目录中的文件和目录。 | |
File[] listFiles() | 返回一个抽象路径名数组,该数组表示由该抽象路径名表示的目录中的文件。 | |
boolean mkdir() | 创建按此抽象路径名命名的目录。 | |
boolean mkdirs() | 创建以此抽象路径名命名的目录,包括任何必要但不存在的父目录。 | |
boolean renameTo(File dest) | 重命名这个抽象路径名表示的文件。 | |
File[] listFiles(FilenameFilter filter) | 返回一个抽象路径名数组,表示此抽象路径名表示的目录中满足指定筛选器的文件和目录。 |
删除文件及目录
public static boolean delDirOrFile(@NotNull File file){
if(file.isDirectory()){
File[] files = file.listFiles();
if(files != null){
for(File subFile : files){
delDirOrFile(subFile);
}
}
}
return file.delete();
}
文件过滤
public static boolean acceptFile(File dir, String name){
String[] split = name.split("\\.");
if(split.length == 2 && "sh".equals(split[1])){
return true;
}
return false;
}
public static void testFileFilter(){
File file = new File("/Users/hlw/Desktop");
if(file.isDirectory()){
String[] list = file.list(BufferStreamTest::acceptFile);
for(String name : list){
System.out.println(name);
}
}
}