I\O流
一、流的概念
流是指内存和存储设备之间建立的数据传输的通道。
数据通过该通道进行传输,所以也可以说数据通过流传输。
二、分类
2.1 按版本分类
按照JDK的版本不同,把流分为三个阶段的内容:
- BIO:基本的二进制流(JDK1.0)同步阻塞
- NIO:新的流
- AIO:异步流
2.2 按方向分类
按方向:基础分类,针对于内存。
- 输入(Input)流:把数据从存储设备传输到内存。读
- 输出(Output)流:把数据从内存传输到存储设备。写
2.3 按单位分类
按单位:
- 字节流:以字节为单位,适合所有的文件类型。
- 字符流:以字符为单位,适合于文本文件。封装了一些适合于文本的操作方法。
2.4 按功能分类
按功能:
- 节点流:普通的数据传输
- 过滤流:在节点流的基础上增强型数据传输
三、字节流的使用
字节流的基本父类:(抽象类)
- InputStream:read()
- OutputStream:write()
节点流:
- FileInputStream:
- FileOutputStream:
public class Test1 {
public static void main(String[] args) throws IOException {
// 读取文件内容
// 1、建立输入通道
FileInputStream fis =
new FileInputStream("C:\Users\Desktop\1.txt");
// 2、读取文件
// 不合理,不知道长度,应该使用while判断是否-1
// for (int i = 0; i < 10; i++) {
// int b = fis.read(); // 读取一个字节
// System.out.println(b);
// }
// 当读取到最后时,再读取会得到-1
// read不仅会读取一个字节,而且还会向前移动一位
// 注意:一般情况下,流的操作会从第一个字节开始读取,且仅向前
int b;
while((b = fis.read()) != -1) {
System.out.print((char)b);
}
// 3、关闭通道
fis.close();// 正确写法,应该写在finally中
}
}
public class Test2 {
public static void main(String[] args) throws IOException {
// 写入文件
// 1、创建通道
FileOutputStream fos =
new FileOutputStream("C:\Users\Desktop\2.txt");
// 2、准备要写入的内容
String content = "java";
// 3、写入内容
fos.write(content.getBytes());
// 清空缓冲区
fos.flush();
// 4、关闭通道
fos.close();
}
}
public class Test3 {
public static void main(String[] args) throws IOException{
// 使用字节流复制图片
// 1、创建读写的通道
FileInputStream fis =
new FileInputStream("C:\Users\Desktop\img\8.jpg");
FileOutputStream fos =
new FileOutputStream("C:\Users\Desktop\8.jpg");
// 2、读写内容
// 每次读写一个字节,性能不好
// long s = System.currentTimeMillis();
// int b;
// while((b = fis.read()) != -1) {
// fos.write(b);
// }
// long e = System.currentTimeMillis();
// System.out.println(e-s);
// 读取多个字节再写入
long s = System.currentTimeMillis();
byte [] buffer = new byte[1024]; // 创建一个字节数组,1kb大小
int len; // 每次读取有效长度
while((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len); // 写入有效内容
}
long e = System.currentTimeMillis();
System.out.println(e-s);
// 3、关闭通道
fis.close();
fos.close();
}
}
四、异常在流中的处理方式
当需要在流中处理异常,并在finally中关闭流时,可以使用以下方式:
public class Test2 {
public static void main(String[] args) {
// 写入文件
// 1、创建通道
FileOutputStream fos = null;
try {
fos = new FileOutputStream("C:\Users\Desktop\2.txt");
// 2、准备要写入的内容
String content = "Java";
// 3、写入内容
fos.write(content.getBytes());
// 清空缓冲区
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
// 4、关闭通道
if(fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
上面的代码比较麻烦,在JDK的新版本(JDK1.7)有提供自动关闭的方式,如下:
public class Test2 {
public static void main(String[] args){
try (
// 1、创建通道
FileOutputStream fos =
new FileOutputStream("C:\Users\Desktop\2.txt");
){
// 写入文件
// 2、准备要写入的内容
String content = "Java";
// 3、写入内容
fos.write(content.getBytes());
// 清空缓冲区
fos.flush();
// 会自动关闭通道
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:在try的小括号中,不能随意定义变量,需要变量的类型实现了自动关闭接口,以便在代码执行完毕后自动关闭。
五、字节过滤流
5.1 缓冲流
BufferedInputStream:自带8kb的缓冲
BufferedOutputStream:自带8kb的缓冲
缓冲流是属于过滤流,不能独立完成IO操作,需要在创建对象时传入字节节点流,只是在该节点流操作文件的过程中进行增强,此处就是添加了缓冲的功能,减少频繁的IO操作。
public class Test4 {
public static void main(String[] args) {
try (
// 1、创建读写的通道
FileInputStream fis =
new FileInputStream("C:\Users\Desktop\img\8.jpg");
FileOutputStream fos =
new FileOutputStream("C:\Users\Desktop\8.jpg");
// 创建一个带缓冲的字节输入流(增强)
BufferedInputStream bis = new BufferedInputStream(fis);
// 创建一个带缓冲的字节输出流(增强)
BufferedOutputStream bos = new BufferedOutputStream(fos);
){
// 使用字节流复制图片
// 2、读写内容
long s = System.currentTimeMillis();
int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
long e = System.currentTimeMillis();
System.out.println(e-s);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
5.2 对象流
5.2.1 基本使用
ObjectInputStream和ObjectOutputStream,作用是将对象直接写入到文件中,以及在文件中直接读取对象,称为序列化和反序列化。
public class Student implements Serializable{
/**
* 序列化编号
*/
private static final long serialVersionUID = -7252210178194837068L;
private String id;
private String name;
private String sex;
private String phone;
public Student(String id, String name, String sex, String phone) {
super();
this.id = id;
this.name = name;
this.sex = sex;
this.phone = phone;
}
public Student() {
super();
}
public String getId() {
return id;
}
public void setId(String 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 String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", phone=" + phone + "]";
}
}
public class Test5 {
public static void main(String[] args) {
// 序列化,将对象写入文件
// 1、创建对象写入的通道
try (
ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("C:\Users\Desktop\bbb.txt"));
){
// 2、创建需要写入的对象
Student stu1 = new Student("1001", "zhangsan", "male", "18866554433");
oos.writeObject(stu1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test6 {
public static void main(String[] args) {
// 反序列化,将文件读取到对象中
// 1、创建对象读取的通道
try (
ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("C:\Users\Desktop\bbb.txt"));
){
// 2、将文件读取到对象中
Student stu1 = (Student) ois.readObject();
System.out.println(stu1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
解惑:序列化接口中为什么没有方法?
如果需要方法,方法的作用应该是将属性和值进行对应,然后在文件中操作。但是Java中的反射机制能够自动扫描类中的属性名称,并读取和写入其中的值,所以Java中序列化操作可以由反射实现,不需要人工添加代码。
private static final long serialVersionUID = -7252210178194837068L;
序列化和反序列化时,要求版本号(序列号)应该一致,避免出现版本问题。如果不一致,会报错。
5.2.2 transient关键字用法
当该关键字修饰属性时,表示该属性不参与序列化和反序列化。
六、字符流
字符流的父类:(抽象类)
- Reader:读,read(char [] c)
- Writer:写,write(String str)
节点流:
- FileReader
- FileWriter
public class Test7 {
public static void main(String[] args) {
try (
// 写入文本
// 1、创建字符流
FileWriter fw = new FileWriter("C:\Users\Desktop\1.txt");
){
fw.write("千锋我最强,千锋我最棒");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test8 {
public static void main(String[] args) {
try (
// 读取文本
// 1、创建字符流
FileReader fw = new FileReader("C:\Users\Desktop\1.txt");
){
char [] buffer = new char[1024];
int len;
while((len = fw.read(buffer)) != -1) {
String str = new String(buffer, 0, len);
System.out.println(str + "!!!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
七、字符过滤流
缓冲流:
- BufferedReader:添加了读取一行的方法
- BufferedWriter:添加了换行的方法
public class Test9 {
public static void main(String[] args) {
// 使用字符缓冲流写入文件
try (
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\Users\Desktop\1.txt"));
){
bw.write("java我最强,");
bw.newLine(); // 在文件中换行
bw.write("java我最棒");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test10 {
public static void main(String[] args) {
// 使用字符缓冲流读取文件
try (
BufferedReader br = new BufferedReader(new FileReader("C:\Users\Desktop\1.txt"));
){
String str;
// 每次读取一行
while((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
八、转换流
将字节流转换成字符流。
InputStreamReader:
OutputStreamWriter:
public class Test11 {
public static void main(String[] args) {
try (
// 模拟接收一个字节流
FileInputStream fis = new FileInputStream("C:\Users\Desktop\1.txt");
// 将字节流通过转换流转换成字符流
InputStreamReader reader = new InputStreamReader(fis, "gb2312");
// 将字符流封装成字符过滤流
BufferedReader br = new BufferedReader(reader);
){
String str;
// 每次读取一行
while((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
九、文件操作
操作文件夹或者文件本身,对文件夹或者文件进行遍历、创建、删除、重命名等操作。
File类,代表一个文件夹或者文件。
- createNewFile()//创建一个新文件
- mkdir()//创建一个新目录,要求路径中包含的前置目录必须存在 make directory
- mkdirs()//创建一个新目录,会同时创建前置目录中要求的所有文件夹
- delete()//删除文件或空目录 ,如果删除的是文件夹,必须要先删除该文件夹中的所有内容,才能删除该空文件夹。
- exists()//判断File对象所对象所代表的对象是否存在
- getAbsolutePath()//获取文件的绝对路径 ,包含盘符的。
- getName()//取得名字
- getParent()//获取文件/目录所在的目录
- isDirectory()//是否是目录
- isFile()//是否是文件
- length()//获得文件的长度 ,单位:字节
- listFiles()//列出当前目录中的所有内容
- renameTo()//修改文件名为
案例:复制一个文件夹中及其中的所有内容。
public class Test12 {
public static String srcDir = "C:\Users\Desktop\img"; // 源文件夹
public static String destDir = "D:\mypic\img";
public static void main(String[] args) {
// 复制一个文件夹中的所有内容
try {
// 创建一个文件对象
File file = new File(srcDir);
// 创建一个目标文件夹对象
File destFile = new File(destDir);
// 如果目标文件夹不存在
if(!destFile.exists()) {
// 创建文件夹
destFile.mkdirs();
}
// 复制文件夹
copyDirectory(file);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyDirectory(File file) {
// 判断是否文件夹
if(file.isDirectory()) {
// 得到其中的所有文件
File[] listFiles = file.listFiles();
for (File file2 : listFiles) {
if(file2.isFile()) {
String newFilePath = destDir + "\" + file2.getName(); // 获得文件路径
// 复制文件
copyFile(file2, newFilePath);
}
}
}
}
private static void copyFile(File srcFile, String newFilePath) {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFilePath));
){
int len;
while((len = bis.read()) != -1) {
bos.write(len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
递归复制:
public class Test12 {
public static String srcDir = "C:\Users\Desktop\img"; // 源文件夹
public static String destDir = "D:\mypic\img";
public static void main(String[] args) {
// 复制一个文件夹中的所有内容
try {
// 创建一个文件对象
File file = new File(srcDir);
// 创建一个目标文件夹对象
File destFile = new File(destDir);
// 如果目标文件夹不存在
if(!destFile.exists()) {
// 创建文件夹
destFile.mkdirs();
}
// 复制文件夹
copyDirectory(file, destFile);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void copyDirectory(File file, File destFile) {
// 判断是否文件夹
if(file.isDirectory()) {
// 得到其中的所有文件
File[] listFiles = file.listFiles();
for (File file2 : listFiles) {
if(file2.isFile()) {
String newFilePath = destFile.getPath() + "\" + file2.getName(); // 获得文件路径
// 复制文件
copyFile(file2, newFilePath);
}else {
// 如果目标文件夹不存在
String newDir = destFile.getPath() + "\" + file2.getName();
File newFile = new File(newDir);
if(!newFile.exists()) {
// 创建文件夹
newFile.mkdirs();
}
// 复制文件夹
copyDirectory(file2, newFile);
}
}
}
}
private static void copyFile(File srcFile, String newFilePath) {
try (
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFilePath));
){
int len;
while((len = bis.read()) != -1) {
bos.write(len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:Java中的路径符号处理问题:
1、windows路径使用
\,需要转义,但是其他系统使用/。2、在windows系统中路径也可以使用
/。3、在Java代码中应该使用File.separator来表示路径符号,而不应该直接硬编码。
文件递归复制:
public class Test13 {
// 将a文件夹中的所有内容,复制到a1文件夹中
public static final String srcPath = "C:/Users/Desktop/a";
public static final String destPath = "C:/Users/Desktop/a1";
public static void main(String[] args) {
copy(srcPath, destPath);
}
/**
* 复制文件夹
* @param src
* @param dest
*/
private static void copy(String src, String dest) {
// 源文件夹
File srcDir = new File(src);
File destDir = new File(dest);
// 判断目标文件夹是否存在,如果不存在,则创建文件夹
if(!destDir.exists()) {
destDir.mkdirs();
}
if(srcDir.exists()) {
// 打开文件夹,获取该文件夹里面的所有内容
File[] files = srcDir.listFiles();
for (File file : files) {
if(file.isFile()) {
copyFile(file, dest + File.separator + file.getName());
}else {
copy(file.getPath(), dest + File.separator + file.getName());
}
}
}
}
private static void copyFile(File file, String string) {
try (
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(string));
){
int len;
while((len = bis.read()) != -1) {
bos.write(len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
案例:创建文件,获得文件大小,然后删除该文件。
public class Test14 {
public static void main(String[] args) {
// 创建文件,写入内容,获得文件大小,重命名该文件,然后删除该文件。
File file = new File("C:\Users\Desktop\1.txt");
if(!file.exists()) {
// 创建文件
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
try (
BufferedWriter writer = new BufferedWriter(
new FileWriter(file));
){
writer.write("我是亚索,号称风一样的男子。");
} catch (Exception e) {
e.printStackTrace();
}
// 得到文件大小
System.out.println(file.length());
// 删除文件
file.delete();
}
}
案例:删除文件夹(文件)。
public class Test15 {
// 删除文件(文件夹)
public static final String path = "C:/Users/Desktop/a1";
public static void main(String[] args) {
// 根据路径创建文件
File destFile = new File(path);
delete(destFile);
}
/**
* 复制文件夹
* @param src
* @param dest
*/
private static void delete(File destFile) {
if(destFile.exists()) {
if(destFile.isDirectory()) {
// 打开文件夹,获取该文件夹里面的所有内容
File[] files = destFile.listFiles();
for (File file : files) {
delete(file);
}
}
destFile.delete();
}
}
}
十、文件过滤
在遍历文件夹内容时,可以根据规则过滤其中的内容,将内容处理。
public class Test16 {
// 删除文件(文件夹)
public static final String path = "C:/Users/Desktop/img";
public static void main(String[] args) {
File file = new File(path);
// 遍历文件夹中所有文件的名称
String[] list = file.list();
for (String string : list) {
System.out.println(string);
}
// 通过文件名过滤
String[] list1 = file.list(new FilenameFilter() {
// dir表示上级目录,name表示当前文件名称
@Override
public boolean accept(File dir, String name) {
// 当文件名称为jpg结尾,就返回true
if(name.endsWith(".jpg")) {
return true;
}
return false;
}
});
for (String string : list1) {
System.out.println(string);
}
// 通过文件过滤
File[] files = file.listFiles(new FileFilter() {
// 当File是文件时,打印输出,如果是文件夹,则不打印
@Override
public boolean accept(File pathname) {
if(pathname.isFile()) {
return true;
}
return false;
}
});
for (File file2 : files) {
System.out.println(file2.getName());
}
}
}
案例:复制所有的笔记md文件。
public class Test17 {
// 将a文件夹中的所有内容,复制到a1文件夹中
public static final String srcPath = "D:\mark";
public static final String destPath = "C:/Users/Desktop/a1";
public static void main(String[] args) {
copy(srcPath, destPath);
}
/**
* 复制文件夹
* @param src
* @param dest
*/
private static void copy(String src, String dest) {
// 源文件夹
File srcDir = new File(src);
File destDir = new File(dest);
// 判断目标文件夹是否存在,如果不存在,则创建文件夹
if(!destDir.exists()) {
destDir.mkdirs();
}
if(srcDir.exists()) {
// 打开文件夹,获取该文件夹里面的所有内容
File[] files = srcDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
// 所有的文件夹,需要复制,返回true
if(pathname.isDirectory()) {
return true;
}
// 所有的文件,如果是md文件,也需要复制,返回true
if(pathname.getName().endsWith(".md")) {
// if(pathname.getParentFile().getName().equals("预习笔记")) {
// return false;
// }
// if(pathname.getParent().endsWith("预习笔记")) {
// return false;
// }
return true;
}
return false;
}
});
for (File file : files) {
if(file.isFile()) {
copyFile(file, dest + File.separator + file.getName());
}else {
copy(file.getPath(), dest + File.separator + file.getName());
}
}
}
}
private static void copyFile(File file, String string) {
try (
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(string));
){
int len;
while((len = bis.read()) != -1) {
bos.write(len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}