IO框架
一、流的概念
内存与存储设备之间传输数据的通道叫做流。
注意:流创建后,还需要进行数据的传递,传递结束应该关闭通道(流)。关闭的代码应该写在finally中。
二、流的分类
2.1 按方向
参照物是内存。
输入流:从存储设备将数据输入到内存。读,Input
输出流:从内存中将数据输出到存储设备。写,Output
IO流是指输入输出流的简称。
2.2 按单位
字节流:能够操作所有的文件类型,以字节为单位传输。
字符流:只能操作文本,而且受到字符集限制(如果字符集不对会出现乱码),以字符为单位。
2.3 按功能
节点流:一般的传输数据的读写功能。
过滤流:在普通的节点流上做的增强功能。
三、字节流
3.1 抽象父类
InputStream:主要作用是读取(read)。
OutputStream:主要作用是写入(write)。
3.2 字节节点流
3.2.1 FileInputStream
FileInputStream:操作文件读取
public class TestMain {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
int n;
while((n = fis.read()) != -1) {
System.out.print((char)n);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
try (
// 定义变量,此处定义的变量下面的区域均有访问作用域
// 在此处定义的变量必须具备有关闭的方法,会自动关闭
FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
// int n = 5; // 此代码不能在此处定义,因为没有实现自动关闭的接口
){
int n;
while((n = fis.read()) != -1) {
System.out.print((char)n);
}
} catch (Exception e) {
e.printStackTrace();
}
3.2.2 FileOutputStream
FileOutputStream:以字节的方式写入数据。
public class TestMain1 {
public static void main(String[] args) {
try (
FileOutputStream fos = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
){
String str = "Hello, world\n"; // 如果需要写入的时候换行,可以使用\n
byte [] bytes = str.getBytes(); // 将字符串转成byte数组
fos.write(bytes);
fos.write(bytes);
fos.flush(); // 刷新,清空系统缓冲区,目的将内存中的数据直接写到硬盘
} catch (Exception e) {
e.printStackTrace();
}
}
}
1、FileOutputStream可以处理中文
2、FileOutputStream写入文件时,并非追加内容,而是覆盖。
3.2.3 复制文件
复制文件,指的是将一个文件先读取,然后写入到另一个文件中。
public class TestMain2 {
// 复制内容
public static void main(String[] args) {
try (
// 需要读取的文件
FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\14.png");
// 需要写入的文件,如果文件不存在会自动创建文件,但是不会创建中途的文件夹
FileOutputStream fos = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\2.txt");
){
byte [] buffer = new byte[1024]; // 创建一个1kb大小的空间,每次读写都操作1kb
int len; // 每次读取的真正长度
// 每次读取1024个字节,len记录每次能够读取到的字节数
while((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len); // 将读取的内容从0开始写,写入真正的内容长度
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3 字节过滤流
3.3.1 缓冲流
减少磁盘IO操作,提高性能。
缓冲区中的内容写入文件:
- flush方法
- 关闭流
- 缓冲区满时会自动写入
public class TestMain3 {
public static void main(String[] args) {
try (
FileInputStream fis = new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
){
int n;
bis.skip(3); // 跳过部分,进行读取
while((n = bis.read()) != -1) {
System.out.print((char)n);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TestMain4 {
public static void main(String[] args) {
try (
FileOutputStream fis = new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt");
BufferedOutputStream bis = new BufferedOutputStream(fis);
){
String str = "hello, world\n";
bis.write(str.getBytes());
bis.write(str.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3.2 对象流
主要作用是将对象写入文件,以及将文件中保存的对象读取到内存。
注意:要将对象写入文件或者从文件中读取,需要对象所对应的类实现序列化接口。
将对象的属性应该如何解析告知操作的流,叫做对象序列化。
在Java中对象序列化比较简单,只需要实现序列化接口即可。无需实现任何方法。
public class Student implements java.io.Serializable{
private int id;
private String name;
private String sex;
private int age;
// 省略getter和setter
}
对象流写入文件:
public class TestMain5 {
public static void main(String[] args) {
try (
ObjectOutputStream fis = new ObjectOutputStream(new FileOutputStream("C:\\Users\\wangliang\\Desktop\\1.txt"));
){
Student stu = new Student(1, "张三", "男", 20);
fis.writeObject(stu);
fis.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
对象流读取文件:
public class TestMain6 {
public static void main(String[] args) {
try (
ObjectInputStream fis = new ObjectInputStream(new FileInputStream("C:\\Users\\wangliang\\Desktop\\1.txt"));
){
Student stu = (Student)fis.readObject();
System.out.println(stu);
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.3.3 序列化UID
标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。
序列化即告诉对象流如何写入一个对象。
反序列化即告诉对象流如何读取一个对象。
public class Student implements java.io.Serializable{
private static final long serialVersionUID = -840791652073023431L;
private int id;
private String name;
private String sex;
private int age;
// 省略getter和setter
}
如果写入的时候版本号为:-840791652073023430L, 但是读取时版本号已经改为-840791652073023431L,那么读取时会出现异常:java.io.InvalidClassException: com.qf.day21_IO.Student; local class incompatible: stream classdesc serialVersionUID = -840791652073023430, local class serialVersionUID = -840791652073023431
3.3.4 transient关键字
用来修饰属性。表示在序列化和反序列化时忽略该属性。
public class Student implements java.io.Serializable{
private static final long serialVersionUID = -840791652073023431L;
private int id;
private String name;
private transient String sex; // 忽略属性,不进行序列化和反序列化
private int age;
// 省略getter和setter
}
四、字符流
4.1 抽象父类
Reader:可以操作char型数组
Writer:可以操作char型数组,可以直接操作字符串。
4.2 字符节点流
FileReader:文本文件读取
public class TestMain7 {
public static void main(String[] args) {
try (
FileReader reader = new FileReader("C:\\Users\\wangliang\\Desktop\\1.txt");
){
char [] buffer = new char[1024];
int len;
while((len = reader.read(buffer))!=-1) {
String str = new String(buffer, 0, len);
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
FileWriter:文本文件写入
public class TestMain8 {
public static void main(String[] args) {
try (
FileWriter writer = new FileWriter("C:\\Users\\wangliang\\Desktop\\1.txt");
){
String str = "标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。\n序列化即告诉对象流如何写入一个对象。";
writer.write(str);
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.3 字符过滤流
4.3.1 缓冲流
字符缓冲流是文本文档操作使用最多的流。能够直接操作一行,读取一行,写入一行,支持换行符等。
BufferedReader:读取文本文件,支持按行读取
public class TestMain9 {
public static void main(String[] args) {
try (
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\wangliang\\Desktop\\1.txt"));
){
String str;
// 读取一行字符串
while((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
BufferedWriter:写入文本文件,支持按行写入,支持换行。
public class TestMain10 {
public static void main(String[] args) {
try (
BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\Users\\wangliang\\Desktop\\1.txt"));
){
String str = "标识当前类唯一的版本号。作用在于不同版本的相同类型也不能进行序列化和反序列化。";
writer.write(str);
writer.newLine(); // 写入换行
writer.write("序列化即告诉对象流如何写入一个对象。");
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.3.2 PrintWriter
封装了print()和println()这些打印的方法,支持原样打印。
五、转换流
作用是将字节流转换字符流。
public class TestMain11 {
public static void main(String[] args) {
try (
// 将字节流转换成字符流
FileInputStream fis = new FileInputStream(""); // 创建一个字节流
InputStreamReader isr = new InputStreamReader(fis, "utf-8"); // 创建一个转换流
BufferedReader reader = new BufferedReader(isr); // 创建一字符流
){
String str;
while((str = reader.readLine()) != null) {
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class TestMain12 {
public static void main(String[] args) {
try (
// 将字节流转换成字符流
FileOutputStream fos = new FileOutputStream(""); // 创建一个字节流
OutputStreamWriter osr = new OutputStreamWriter(fos, "utf-8"); // 创建一个转换流
BufferedWriter writer = new BufferedWriter(osr); // 创建一字符流
){
writer.write("床前明月光");
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:在项目中,可以需要定义一些通用方法,此时,为了通用,会将所有文件操作定义成字节流,但是如果操作的文件在明知是文本文件的情况下,需要将得到字节流转换成字符流,此时就需要使用转换流。
六、File
6.1 File类
File类代表磁盘上的一个文件或文件夹。
常见方法:
public class TestMain13 {
// 文件操作
public static void main(String[] args) {
try {
File file = new File("C:\\Users\\wangliang\\Desktop\\1.txt");
file.createNewFile(); // 创建一个文件
File file1 = new File("C:\\Users\\wangliang\\Desktop\\1\\2\\3");
file1.mkdir(); // 创建一个文件夹,只能在上层文件夹存在情况下创建,否则无法创建
file1.mkdirs();// 创建一个文件夹,但是如果路径中有上层文件夹不存在,会连上层一起创建,可以创建多层文件夹
file1.delete(); // 删除文件或空文件夹
System.out.println(file.exists()); // 判断一个文件或文件夹是否存在
System.out.println(file1.getAbsolutePath()); // 得到文件或文件夹的绝对路径
System.out.println(file1.getName()); // 得到当前文件或文件夹的名称
System.out.println(file.getParent()); // 得到上层文件夹的路径名称
file.getParentFile(); // 得到上层文件夹File对象
System.out.println(file.isDirectory()); // 判断file是否文件夹
System.out.println(file.isFile()); // 判断file是否文件
System.out.println(file.length()); // 得到文件的大小
file.list(); // 得到文件夹中的所有内容的路径
file.listFiles();// 得到文件夹中所有的File对象
file.renameTo(new File("C:\\Users\\wangliang\\Desktop\\2.txt")); // 给file改名
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.2 案例:复制文件夹中的所有内容
public class TestMain14 {
// 文件夹复制
public static void main(String[] args) {
String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
copy(srcPath, destPath);
}
/**
* 复制文件夹
* @param srcPath 源路径
* @param destPath 目标路径
*/
public static void copy(String srcPath, String destPath) {
try {
File srcFile = new File(srcPath); // 源路径文件
// 如果源路径存在
if(srcFile.exists()) {
File destFile = new File(destPath); // 目标路径文件
if(!destFile.exists()) {
destFile.mkdirs();
}
copyFile(srcFile, destFile);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyFile(File src, File dest) {
if(src.isDirectory()) {
dest.mkdir(); // 创建该文件夹
// 循环遍历
File[] files = src.listFiles();
for (File file : files) {
// 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
// Java中提供路径常量File.separator
copyFile(file, new File(dest + File.separator + file.getName()));
}
}else {
try (
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte [] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
七、FileFilter接口
过滤文件,例如,在复制文件夹时,部分文件不复制。
public class TestMain15 {
// 文件夹复制
public static void main(String[] args) {
String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
copy(srcPath, destPath);
}
/**
* 复制文件夹
* @param srcPath 源路径
* @param destPath 目标路径
*/
public static void copy(String srcPath, String destPath) {
try {
File srcFile = new File(srcPath); // 源路径文件
// 如果源路径存在
if(srcFile.exists()) {
File destFile = new File(destPath); // 目标路径文件
if(!destFile.exists()) {
destFile.mkdirs();
}
copyFile(srcFile, destFile);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyFile(File src, File dest) {
if(src.isDirectory()) {
dest.mkdir(); // 创建该文件夹
// 循环遍历
// 文件名称过滤器
File[] files = src.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
// 得到文件
String path = dir.getAbsolutePath() + File.separator + name;
// 判断文件是否路径
if(new File(path).isDirectory()) {
return true;
}
if(name.endsWith(".jpg") || name.endsWith(".png")) {
return true;
}
return false;
}
});
for (File file : files) {
// 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
// Java中提供路径常量File.separator
copyFile(file, new File(dest + File.separator + file.getName()));
}
}else {
try (
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte [] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class TestMain16 {
// 文件夹复制
public static void main(String[] args) {
String srcPath = "C:\\Users\\wangliang\\Desktop\\img";
String destPath = "C:\\Users\\wangliang\\Desktop\\11\\22";
copy(srcPath, destPath);
}
/**
* 复制文件夹
* @param srcPath 源路径
* @param destPath 目标路径
*/
public static void copy(String srcPath, String destPath) {
try {
File srcFile = new File(srcPath); // 源路径文件
// 如果源路径存在
if(srcFile.exists()) {
File destFile = new File(destPath); // 目标路径文件
if(!destFile.exists()) {
destFile.mkdirs();
}
copyFile(srcFile, destFile);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyFile(File src, File dest) {
if(src.isDirectory()) {
dest.mkdir(); // 创建该文件夹
// 循环遍历
// 文件名称过滤器
File[] files = src.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
// 判断文件是否文件夹
if(file.isDirectory()) {
return true;
}
if(file.getName().endsWith(".jpg") || file.getName().endsWith(".png")) {
return true;
}
return false;
}
});
for (File file : files) {
// 路径在不同的系统中不一样,win系统\\,Linux、macos都是/,win也可以使用/
// Java中提供路径常量File.separator
copyFile(file, new File(dest + File.separator + file.getName()));
}
}else {
try (
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte [] buffer = new byte[1024];
int len;
while((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}