用来进行输入输出操作的流就称为IO流。 换句话说,IO流就是以流的方式进行输入输出 。
根据流的流动方法(数据的传输方向):输入流、输出流
根据流的数据格式:字节流、字符流
根据流的主要作用:节点流、装饰流(过滤流)
装饰流:又被称为过滤流,这种流不能直接使用,主要的作用就是用来装饰节点流(各种可以直接使用流)。
装饰流,本质就是一种装饰者设计模式的体现。对原有对象的功能进一步的装饰增强。
转换字节和字符流动的特殊流:转换流
文件和目录是可以通过File封装成对象的
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的。
File类:
注:windows系统的路径分隔符\要用转义为\
常用字段:
// 路径结束的标志
static String pathSeparator
// 目录与目录之间的分隔符
static String separator
常用构造:
// 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例
File(String pathname)
// 从父路径名字符串和子路径名字符串创建新的File实例
File(String parent, String child)
// 从父抽象路径名和子路径名字符串创建新的File实例
File(File parent, String child)
常用方法:
// 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件
boolean createNewFile()
// 创建由此抽象路径名命名的目录
boolean mkdir()
// 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录
boolean mkdirs()
// 删除由此抽象路径名表示的文件或目录
// 如果一个目录中有内容,需要先删除目录中的内容,最后才能删除目录
boolean delete()
// 测试此抽象路径名表示的文件或目录是否存在
boolean exists()
// 测试此抽象路径名表示的文件是否为目录
boolean isDirectory()
// 测试此抽象路径名表示的文件是否为普通文件
boolean isFile()
// 返回文件的长度(单位B)
long length()
// 返回此抽象路径名的绝对路径名形式
File getAbsoluteFile()
// 返回此抽象路径名的绝对路径名字符串
String getAbsolutePath()
// 将此抽象路径名转换为路径名字符串
String getPath()
// 获取此文件或目录名称
String getName()
// 返回上一级目录;没有则返回null
File getParentFile()
// 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录
String[] list()
// 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件
File[] listFiles()
// 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录
File[] listFiles(FileFilter filter)
@Test
void testFile02() throws Exception {
File file = new File("a.txt");
// 是否存在执行权限
System.out.println(file.canExecute());
// 是否有写的权限
System.out.println(file.canWrite());
// 是否有读的权限
System.out.println(file.canRead());
// System.out.println(file.compareTo(new File("a.txt")));
// 创建文件
file.createNewFile();
// 删除文件
// System.out.println(file.delete());
// 在JVM退出是删除文件
// file.deleteOnExit();
// 判断文件是否存在
// file.exists();
// 将文件对象由相对路径转换为绝对路径
File absoluteFile = file.getAbsoluteFile();
// System.out.println(absoluteFile);
// 获取文件的真实路径
// String path = file.getAbsolutePath();
// System.out.println(path);
// 空闲空间(磁盘空间)
System.out.println((file.getFreeSpace() / 1024 / 1024) +"M");
// 可有空间
System.out.println((file.getUsableSpace() / 1024 / 1024 / 1024) +"G");
// 总空间
System.out.println((file.getTotalSpace() / 1024 / 1024 / 1024) +"G");
System.out.println(file.length());
System.out.println(absoluteFile.getName());
// 获取父级的路径等操作,必须使用绝对路径对象
System.out.println(absoluteFile.getParent());
System.out.println(absoluteFile.getParentFile());
System.out.println(file.getPath());
// 以is开头的几个方法,都是判断是否符合某种规则
// System.out.println(file.isXxx);
}
递归遍历目录:
// 递归遍历目录
public static void tree(File dir) {
// 获取给定的File目录下所有的文件或目录的File数组
File[] files = dir.listFiles();
// 遍历该File数组,得到每一个File对象
if (files != null) {
for (File file : files) {
// 判断该File对象是否是目录
if (file.isDirectory()) {
// 是目录:递归调用
System.out.println(file.getAbsolutePath() + ":");
tree(file);
} else {
// 不是目录:获取绝对路径输出在控制台
System.out.println(file.getAbsolutePath());
}
}
}
}
创建目录:
@Test
void test03() {
// File file = new File("./lib");
// 只能创建一层目录
// if (file.mkdir()) {
// System.out.println("目录创建成功");
// } else {
// System.out.println("创建失败");
// }
File file = new File("config/resources");
// 使用递归的方式,完成多层目录的创建
if (file.mkdirs()) {
System.out.println("目录创建成功");
} else {
System.out.println("创建失败");
}
}
字节流抽象基类:
子类名称都是以其父类名作为子类名的后缀(如: FileOutputStream)
构造方法:
new FileIntputStream(File file)
new FileInputStream(String name)
read方法:
read()
读取一个后自动指向下一个,返回值是int类型
读到末尾返回-1
原本的汉字可能是两个字节,现在每读取一个字节就转换为char输出,会出现乱码,好比汉字被拆成两半。
read(byte[] b)
返回读取字节长度,如果返回-1,说明已经读到文件末尾
输出打印时,String构造方法:把字节数组转字符串
@Test
void test01() {
InputStream is = null;
try {
// 1、创建一个输入流对象(xxx数据流)
is = new FileInputStream("a.txt");
// 创建字节数组,通过字节数组读取数据
byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) != -1) {
System.out.write(buf, 0, len);
// 借助字符串对象,将字节数组构造为字符串对象
//String str = new String(buf, 0, len);
//System.out.println(str);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 必须关闭流
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
构造方法:
new FileOutputStream(File file)
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
new FileOutputStream(File file, boolean append) : 支持追加写入,每次运行程序,同个代码实现追加写入,而不是覆盖
write方法:
void writer(byte[] b) : 将指定的字节写入此文件输出流 一次写一个字节数据
void write(byte[] b, int off, int len) : 从指定的字节数组写入此文件输出流 一次写一个数组
void write(int b) : 从偏移量off开始写入此文件输出流, 指定长度len 一次写一个数组的部分数据
@Test
void test03() {
String msg = "Hello world!";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File("b.txt"));
fos.write(msg.getBytes());
System.out.println("写入数据成功!!!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
例题:复制一个文件
@Test
void test04() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\第6天笔记.txt");
fos = new FileOutputStream(new File("c:\\Users\\Administrator\\Desktop\\a.txt"));
// 一定要通过字节数组进行读取和写入
// 防止内存过大,导致出现OOM
byte[] buf = new byte[1024];
int len = 0;
while ((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
System.out.println("写入数据成功!!!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节缓冲流介绍
构造方法:
例:通过字节缓冲流来复制一个文件
@Test
void test05() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("C:\\Users\\Administrator\\Desktop\\第6天笔记.txt"));
bos = new BufferedOutputStream(new FileOutputStream(new File("c:\\Users\\Administrator\\Desktop\\b.txt")));
byte[] buf = new byte[1024 * 8];
int len = 0;
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
System.out.println("写入数据成功!!!");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
数据流的使用:
IO流如果要保存数字,只要将数字转换为字符串,也就是说以字符流的形式保存数据,这样有时候并不是我们需要的,我们有时候就是需要以字节保存数据,因此就可以使用数据流来包装文件流完成。
@Test
void test07() throws Exception {
FileOutputStream fis = new FileOutputStream("a.dat");
int msg = 100;
// String str = Integer.valueOf(msg).toString();
// fis.write(str.getBytes());
fis.write((msg + "").getBytes());
fis.close();
}
@Test
void test08() throws Exception {
int msg = 1000000;
DataOutputStream dos = new DataOutputStream(new FileOutputStream("a.dat"));
dos.writeInt(msg);
dos.writeInt(132);
dos.writeInt(46);
dos.writeInt(78);
dos.close();
}
@Test
void test09() throws Exception {
int msg = 1000000;
DataInputStream dis = new DataInputStream(new FileInputStream("a.dat"));
System.out.println(dis.readInt());
System.out.println(dis.readInt());
dis.close();
}
计算机底层使用的二进制数据(字节数据)。
字符流是为了加快流的操作,而设计的专门用来操作字符串的一种流。
注意:字符串存在编码问题,需要保证在读取和写入时,保持一致!!!
字节流的弊端:
字节流玩中文不是特别方便,读一个打印一个:中文必定乱码,一次读一个数组,打印一个字符串:中文有可能乱码,具体看末尾字节是不是半个。于是,引入转换流:字节流+编码表
@Test
void test01() {
Reader reader = null;
try {
reader = new FileReader(new File("I:\\java\\java_se\\2022\\day23\\src\\com\\openlab\\day23\\TestIO.java"));
char[] buf = new char[1024];
int len = 0;
while((len = reader.read(buf)) != -1) {
System.out.println(String.valueOf(buf, 0, len));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
例:拷贝文件
@Test
void test02() {
Reader reader = null;
Writer writer = null;
try {
reader = new FileReader(new File("I:\\java\\java_se\\2022\\day23\\src\\com\\openlab\\day23\\TestIO.java"));
writer = new FileWriter("c:\\a.java");
char[] buf = new char[1024];
int len = 0;
while((len = reader.read(buf)) != -1) {
writer.write(buf, 0, len);
}
System.out.println("拷贝文件成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符缓存流完成字符的操作
通过行来读取和写入字符串
字符串输入缓存流 BufferedReader
字符串输出缓存流 BufferedWriter
不太推荐大家使用BufferedWriter,因为需要手动添加换行符
推荐使用打印输出流(节点流)PrintWriter
@Test
void test04() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("I:\\java\\java_se\\2022\\day23\\src\\com\\openlab\\day23\\TestIO.java")));
bw = new BufferedWriter(new FileWriter("d:\\a.java"));
String msg = null;
// 字符输入流,可以使用装饰流,按行读取
while ((msg = br.readLine()) != null) {
// bw.write(msg);
// 我们可以手动添加换行符
bw.write(msg + "\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
void test05() {
BufferedReader br = null;
PrintWriter out = null;
try {
br = new BufferedReader(new FileReader(new File("I:\\java\\java_se\\2022\\day23\\src\\com\\openlab\\day23\\TestIO.java")));
out = new PrintWriter("d:\\a.java");
String msg = null;
// 字符输入流,可以使用装饰流,按行读取
while ((msg = br.readLine()) != null) {
out.println(msg);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
}
}
将字节流转换为字符流操作
@Test
void test01() throws IOException {
BufferedReader br = null;
// InputStreamReader 转换流
// 可以将字节输入流转换为字符输入流
br = new BufferedReader(new InputStreamReader(System.in));
String str = null;
while ((str = br.readLine()) != null) {
if (str.equalsIgnoreCase("exit"))
break;
System.out.println(str.replace("吗?", ""));
}
br.close();
}
@Test
void test02() throws IOException {
BufferedReader br = null;
PrintWriter out = null;
br = new BufferedReader(new InputStreamReader(System.in));
out = new PrintWriter(new OutputStreamWriter(new FileOutputStream("d:\\a.html")));
String str = null;
while ((str = br.readLine()) != null) {
if (str.equalsIgnoreCase("exit"))
break;
out.println(str);
}
br.close();
out.close();
}
对象本质:它是一个抽象概念,是JVM中的一种虚拟出来的抽象概念
new 关键字,class类,构造一个java对象
对象流就是java提供一种,可以将java对象这种虚拟的概念转换为一种物理可以存储或者传输的真实数据。
将虚拟的JVM中的对象转换为字节数据
对象持久化:将数据永久保存(IO、数据库)
java官方提供的序列化,是将java对象转换为字节数据。
注意:java的对象要实现序列化和反序列化,必须实现Serializable(这个标记接口)。
transient关键字:被这个关键字修饰的属性,无法被持久化。
@Test
void test01() {
String msg = "Hello World!";
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("c:\\info.dat"));
oos.writeObject(msg);
System.out.println("对象保存成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
void test02() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("c:\\info.dat"));
// Object msg = ois.readObject();
String msg = (String) ois.readObject();
System.out.println(msg);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}