1. 流的概念
- 内存与存储设备之间传输数据的通道
- 数据借助流传输
2. 流的分类
按方向(重点)
- 输入流:将 存储设备 中的内容读到 内存 中;
- 输出流:将 内存 中的内容写到 存储设备 中;
按单位
- 字节流:以字节为单位,可以读写 所有数据;
- 字符流:以字符为单位,只能读写 文本数据;
按功能
- 节点流:具有 实际传输数据 的 读写功能;
- 过滤流:在节点流的基础之上 增强功能;
3. 字节流
- 字节流的父类(抽象类)
// InputStream 字节输入流 (读取)
public int read(){}
public int read(byte[] b){}
public int read(byte[] b, int off, int len){}
// OutputStream 字节输出流 (写入)
public void write(int n){}
public void write(byte[] b){}
public void write(byte[] b, int off, int len){}
文件字节流
- 文件字节输入流:
FileInputStream
public static void main(String[] args) throws Exception{
// 1 创建FileInputStream 并指定文件路径
FileInputStream fis = new FileInputStream("d:\\abc.txt");
// 2 读取文件
// fis.read();
// 2.1单字节读取
int data = 0;
while((data = fis.read()) != -1){
System.out.println((char)data);
}
// 2.2 一次读取多个字节
byte[] buf = new byte[3]; // 大小为3的缓存区
int count = fis.read(buf); // 一次读3个
System.out.println(new String(buf));
System.out.println(count);
int count2 = fis.read(buf); // 再读3个
System.out.println(new String(buf));
System.out.println(count2);
// 上述优化后
int count = 0;
while((count = fis.read(buf)) != -1){
System.out.println(new String(buf, 0, count));
}
// 3 关闭
fis.close();
}
- 文件字节输出流:
FileOutputStream
public static void main(String[] args) throws Exception{
// 1 创建文件字节输出流
// true 表示不覆盖 接着写
FileOutputStream fos = new FileOutputStream("路径", true);
// 2 写入文件
fos.write(97);
fos.write('a');
// String string = "hello world";
fos.write(string.getBytes());
// 3 关闭
fos.close();
}
图片复制案例
// 1 创建流
// 1.1 文件字节输入流
FileInputStream fis = new FileInputStream("路径");
// 1.2 文件字节输出流
FileInputStream fos = new FileOutpuStream("路径");
// 2 边读边写
byte[] buf = new byte[1024];
int count = 0; // 计数器
while((count = fis.read(buf)) != -1){
fos.write(buf, 0, count);
}
// 3 关闭
fis.close();
fos.close();
字节缓冲流(过滤流):提高 IO 效率
- 缓冲流:
BufferedInputStream/ BufferedOutputStream
- 提高 IO 效率,减少访问磁盘次数;
- 数据存储在缓冲区中(8K),flush 是将缓冲区的内容写入文件中,也可以直接 close。
-
BufferedInputStream
:读取文件
// 使用字节缓冲流 读取 文件
public static void main(String[] args) throws Exception{ // 抛出异常
// 1 创建BufferedInputStream
FileInputStream fis = new FileInputStream("路径");
BufferedInputStream bis = new BufferedInputStream(fis);
// 2 读取 BufferedInputStream内部自定义了8K缓冲区
int data = 0;
while((data = bis.read()) != -1){
System.out.print((char)data);
}
// 用自己创建的缓冲流
byte[] buf = new byte[1024];
int count = 0;
while((count = bis.read(buf)) != -1){
System.out.println(new String(buf, 0, count));
}
// 3 关闭,同时也关闭了 fis
bis.close();
}
-
BufferedOutputStream
:写入文件
// 使用字节缓冲流 写入 文件
public static void main(String[] args) throws Exception{
// 1. 创建BufferedInputStream
FileOutputStream fos = new FileOutputStream("路径");
BufferedOutputStream bis = new BufferedOutputStream(fos);
// 2. 写入文件
for(int i = 0; i < 10; i ++){
bos.write("hello".getBytes());// 写入内部 8k 缓冲区
bos.flush(); // 刷新缓冲区,写入到硬盘
}
// 3. 关闭(内部调用flush())
bos.close();
}
4. 对象流:ObjectOutputStream / ObjectInputStream
- 增强了缓冲区功能
- 增强了读写 8 种基本数据类型和字符串的功能
- 增强了读写对象的功能
-
readObject();
:从流中读取一个对象(反序列化) -
writeObject(Object obj);
:向流中写入一个对象(序列化)
-
- 使用流传输对象的过程,称为序列化、反序列化
5. 序列化与反序列化
序列化
- 实例:创建 Student 类实现 Serializable 接口
package com.base.demo06;
import java.io.Serializable;
// 要求:序列化类,必须要实现 Serializable 接口
public class Student implements Serializable {
// serialVersionUID 序列化版本号,保证序列化、反序列化为同一个类
private static final long serialVersionUID = -3115151823789216775L;
private String name;
// transient(内存中,瞬时的) 不被序列化
private transient int age;
// 静态属性,不被序列化
private static String country = "China";
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 序列化
package com.base.demo06;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class Demo06 {
public static void main(String[] args) throws Exception {
/*
1. 使用 ObjectOutputStream 实现对象的序列化(写入)
2. 要求:序列化类,必须要实现 Serializable 接口
*/
// 1. 创建对象流
FileOutputStream fos = new FileOutputStream("d:\\stu.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 2. 序列化(写入操作)
Student s1 = new Student("Liu", 20);
// 创建多个对象
Student s2 = new Student("Chen", 21);
// 集合方式
ArrayList list = new ArrayList<>();
list.add(s1);
list.add(s2);
/*
普通方式,写入多个对象
oos.writeObject(s1);
oos.writeObject(s2);
*/
// 集合写入
oos.writeObject(list);
// 3. 关闭
oos.close();
System.out.println("序列化完华");
}
}
反序列化
- 实例:
package com.base.demo06;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Demo07 {
public static void main(String[] args) throws Exception {
// 使用 ObjectInputStream 实现反序列化(读取重构成对象)
// 1.创建对象流
FileInputStream fis = new FileInputStream("d:\\stu.bin");
ObjectInputStream ois = new ObjectInputStream(fis);
// 2.读取文件(反序列化)
// Student s = (Student) ois.readObject(); // 强制转换成 Student 类型
// Student s1 = (Student) ois.readObject();
// 集合方式读取
ArrayList list = (ArrayList) ois.readObject(); // 强制转换
// 3.关闭
ois.close();
System.out.println("反序列化,执行完华");
// System.out.println(s.toString());
// System.out.println(s1.toString());
System.out.println(list.toString());
}
}
- 运行结果:
-
readObject()
,读取完后,再次读取时,会出现异常
注意事项:
- 类要想序列化,必须实现
Serializable
接口; - 序列化类中,对象属性要求实现
Serializable
接口; - 序列化版本号 ID(
serialVersionUID
),保证序列化的类和反序列化的类是同一个类; - 使用
transient
(内存中,瞬时的)修饰属性,这个属性就不能被序列化; - 静态属性(
static
)不能被序列化; - 序列化多个对象,可以借助集合来实现。
6. 编码方式
- UTF-8 等
编码方式要一致:当编码方式和解码方式不一致时,会出现乱码。
7. 字符流
// 传统字节流读取
public static void main(String[] args){
// 1. 创建FileInputStream 对象
FileInputSteam fis = new FileInputStream("路径");
// 2. 读取
int data = 0;
// 3. 字节流按一个字节为单位来读取
while((data = fis.read()) != -1){
System.out.print((char)data);
}
// 3. 关闭
fis.close();
}
- 字节流读取汉字时乱码:(一般一个汉字 2 个字节),字节流按一个字节为单位读取
字符流的父类(抽象类)
-
reader
字符输入流public int read(){}
public int read(char[] c){}
public int read(char[] b, int off, int len){}
-
writer
字符输出流public void write(int n){}
public void write(String str){}
public void write(char[] c){}
FileReader
文件字符输入流:(读取)
// 1. 创建FileReader 文件字符输入流
FileReader fr = new FileReader("路径");
// 2. 读取
// 2.1 单个字符读取
int data = 0;
while((data = fr.read()) != -1){
System.out.print((char)data);// 读取一个字符
}
// 2.2 字符缓冲区读取
char[] buf = new char[2];
int count = 0; // 计数器
while((count = fr.read(buf) != -1)){
System.out.println(new String(buf, 0, count));
}
// 3. 关闭
fr.close();
FileWriter
文件字符输出流:(写入)
// 1. 创建FileWriter对象
FileWriter fw = new FileWriter("路径");
// 2. 写入
for(int i = 0; i < 10; i ++){
fw.write("写入的内容\r\n"); // \r\n 换行
fw.flush(); // 刷新,写入
}
// 3. 关闭
fw.close();
System.out.println("执行完毕");
案例:文本文件复制
- 字符流:不能复制图片或二进制文件;字节流:可以复制任意文件
字符流读取文件时,会将文件转化成字符
public static void main(String[] args) throws Exception{
// 1. 创建流
FileReader fr = new FileReader("路径");
FileWriter fw = new FileWriter("路径");
// 2. 读写
int data = 0;
while((data = fr.read()) != -1){
fw.write(data);
// 刷新,写入
fw.flush();
}
// 3. 关闭
fw.close();
fr.close();
}
字符缓冲流:BufferedReader / BufferedWriter
- 高效读写;
- 支持输入换行符;
- 可一次写一行读一行。
BufferedReader:读取缓冲流
public static void main(String[] args) throws Exception{
// 创建缓冲流
FileReader fr = new FileReader("路径");
BufferedReader br = new BufferedReader(fr);
// 读取
// 1. 第一种方式
char[] buf = new char[1024]; // 自定义缓冲区
int count = 0;
while((count = br.read(buf)) != -1){
System.out.print(new String(buf, 0, count));
}
// 2. 第二种方式:readLine() 一行一行读取
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
// 3.关闭
br.close();
}
BufferedWriter:写入缓冲流
public static void main(String[] args) throws Exception{
// 1. 创建BufferedWriter对象
FileWriter fw = new FileWriter("路径");
BufferedWriter bw = new BufferedWriter(fw);
// 2. 写入
for(int i = 0; i < 10; i ++){
bw.write("写入的内容");
bw.newLine(); // 写入一个换行符,根据操作系统,自动调整
bw.flush();
}
// 3. 关闭
bw.close(); // 此时会自动关闭fw
}
PrintWrite:打印流
- 封装了
print() / println()
方法,支持写入后换行 - 支持数据原样打印
public static void main(String[] args) throws Exception{
// 1 创建打印流
PrintWriter pw = new PrintWriter("路径");
// 2 打印
pw.println(97); // 97 原样打印,非字母 a
pw.println(true);
pw.println(3.14);
pw.println('a');
// 3 关闭
pw.close();
}
转换流
- 又称桥转换流:
InputStreamReader / OutputStreamWriter
- 可将字节流转换为字符流
- 可设置字符的编码方式
InputStreamReader:读取
public static void main(String[] args) throws Exception{
// 1 创建InputStreamReader对象
FileInputStream fis = new FisInputStream("路径");
// 指定读取编码方式,要与文件编码方式统一,否则乱码
InputStreamReader isr = new InputStreamReader(fis, "utf-8");
// 2 读取文件
int data = 0;
while((data = isr.read()) != -1){
System.out.print((char)data);
}
// 3 关闭
isr.close();
}
OutputStreamWriter:写入
public static void main(String[] args) throws Exception{
// 1 创建OutputStreamReader对象
FileOutputStream fos = new FisOutputStream("路径");
OutputStreamWriter osw = new OutputStreamReader(fos,"utf-8");
// 2 写入
for(int i = 0; i < 10; i ++){
osw.write("写入内容\r\n");
osw.flush();
}
// 3 关闭
osw.close();
}
8. File 类
概念:代表物理盘符中的一个 文件 或者 文件夹
方法名 | 说明 |
---|---|
createNewFile() | 创建一个新文件,需要声明异常 |
mkdir() | 创建一个新目录 |
delete() | 删除文件或空目录 |
exists() | 判断文件或目录是否存在 |
getAbsolutePath() | 获取文件的绝对路径 |
getName() | 获取文件或目录的名称 |
getParent() | 获取文件或目录所在的目录名称 |
isDirectory() | 判断是否是一个目录 |
isFile() | 判断是否是文件 |
length() | 获取文件的长度 |
ListFiles() | 列出目录中的所有内容 |
renameTo() | 修改文件名称 |
- 分隔符、文件操作、文件夹操作
/*
File类的使用
1. 分隔符
2. 文件操作
3. 文件夹操作
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
// 1.分隔符
// separator();
// 2.文件操作
fileOpe();
// 3.文件夹操作
// directoryOpe();
}
// 1、分隔符
public static void separator(){
System.out.println("路径分隔符" + File.pathSeparator);
System.out.println("名称分隔符" + File.separator);
}
// 2、文件操作
public static void fileOpe() throws Exception{
// 1. 创建文件
if(!file.exists()){ // 是否存在
File file = new File("路径");
boolean b = file.creatNewFile();
System.out.println("创建结果:" + b);
}
// 2. 删除文件
// 2.1 直接删除
file.delete(); // 成功 true
// 2.2 使用 jvm 退出时删除
file.deleteOnExit();
// 3. 获取文件信息
System.out.println("获取绝对路径" + file.getAbsolutePaht());
System.out.println("获取路径" + file.getPath());
System.out.println("获取文件名称" + file.getName());
System.out.println("获取父目录" + file.getParent());
System.out.println("获取文件长度" + file.length());
System.out.println("文件创建时间" + new Date(file.lashModified()).toLocalString());
// 4. 判断
System.out.println("是否可写:" + file.canWrite());
System.out.println("是否是文件:" + file.isFile());
System.out.println("是否隐藏:" + file.isHidden());
}
// 3、文件夹操作
public static void directoryOpe() throws Exception{
// 1. 创建文件夹
File dir = new File("路径");
System.out.println(dir.toString());
if(!dir.exists()){
//dir.mkdir(); // 只能创建单级目录
dir.mkdirs(); // 创建多级目录
}
// 2. 删除文件夹
// 2.1 直接删除
dir.delete(); // 只能删除最底层空目录
// 2.2 使用 jvm 删除
dir.deleteOnExit();
// 3. 获取文件夹信息
System.out.println("获取绝对路径" + dir.getAbsolutePaht());
System.out.println("获取路径" + dir.getPath());
System.out.println("获取文件名称" + dir.getName());
System.out.println("获取夫目录" + dir.getParent());
System.out.println("获取文件长度" + dir.length());
System.out.println("文件夹创建时间" + new Date(dir.lashModified()).toLocalString());
// 4. 判断
System.out.println("是否是文件夹" + dir.isFile());
System.out.println("是否隐藏" + dir.isHidden());
// 5. 遍历文件夹
File dir2 = new File("路径");
String[] files = dir2.list();
for(String string : files){
System.out.println(string);
}
// FileFilter 接口的使用
File[] files2 = dir2.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname){
if(pathname.getName().endsWith(".jpg")){
return true;
}
return false;
}
});
for(File file : files2){
System.out.println(file.getName());
}
}
}
FileFilter 接口:文件过滤器
- 用来过滤文件(File对象);
- public interface FileFilter
- boolean accept(File pathname)
- 当调用 File 类中的 listFiles() 方法时,支持传入 FileFilter 接口实现类,对获取文件进行过滤,只有满足条件的文件才可以出现在 listFiles() 的返回值中。
File[] files2 = dir2.listFiles(new FileFilter(){
@Override
public boolean accept(File pathname){
if(pathname.getName().endsWith(".jpg")){
return true;
}
return false;
}
});
递归遍历文件夹
public static void main(String[] args){
listDir(new File("d:\\myfiles"));
}
public static void listDir(File dir){
File[] files = dir.listFiles();
// 打印文件夹下文件
System.out.println(dir.getAbsolutePath());
// 判断文件夹是否为空
if(files != null && files.length > 0){
for(File file : files){
// 判断是否是文件夹
if(file.isDirectory()){
// 如果是文件夹,递归调用listDir,遍历文件
listDir(file); // 递归
}else {
// 打印子文件夹内文件
System.out.println(file.getAbsolutePath());
}
}
}
}
递归删除文件夹
public static void deleteDir(File dir){
File[] files = dir.listFiles();
// 判断是否为空
if(files != null && files.length > 0){
for(File file : files){
// 判断是否是文件夹
if(file.idDirectory()){
deleteDir(file); // 递归
}else{
// 删除子文件夹文件
System.out.println(file.getAbsolutePath() + " 删除:" + file.delete());
}
}
}
// 删除文件
System.out.println(dir.getAbsolutePath() + " 删除:" + dir.delete());
}
补充:Properties
- Properties:属性集合
- 特点:
- 存储属性名和属性值
- 属性名和属性值都是字符串类型
- 没有泛型
- 和流有关
package com.base.demo08;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.Properties;
import java.util.Set;
/*
* Properties 集合的使用
* */
public class Demo03 {
public static void main(String[] args) throws Exception {
// 1.创建集合
Properties properties = new Properties();
// 2.添加数据
properties.setProperty("username", "Liu");
properties.setProperty("age", "20");
System.out.println(properties.toString());
// 3.遍历
// 3.1--------------keySet---------------
// 3.2--------------entrySet---------------
// 3.3--------------stringPropertyNames()---------------
Set pronames = properties.stringPropertyNames();
for (String pro : pronames) {
// 打印键、值
System.out.println(pro + "=========" + properties.getProperty(pro));
}
// 4.和流有关的方法
// ---------1.list方法----------
// 创建打印流
PrintWriter pw = new PrintWriter("d:\\print.txt");
properties.list(pw);
pw.close();
// ---------2.store方法(保存)----------
// 创建流
FileOutputStream fos = new FileOutputStream("d:\\store.properties");
properties.store(fos, "注释");
fos.close();
// ---------3.load方法(加载)----------
// 创建对象
Properties properties2 = new Properties();
// 创建流
FileInputStream fis = new FileInputStream("d:\\store.properties");
properties2.load(fis);
fis.close();
System.out.println(properties2.toString());
}
}