1、File的概述和使用
File和IO流的概述
IO流可以将数据从本地文件中读取出来,将数据从内存保存到本地文件。File是文件和目录路径名的抽象表示,虚拟机通过读取文件地址,对文件/文件夹进行操作;File仅仅是一个路径名,可以是存在的也可以是不存在的。绝对路径是从盘符开始的,相对路径是相对当前模块性的路径。
File的构造
方法名 | 解释 |
---|---|
File(String pathname) | 通过字符串路径获取File实体类 |
File(String parent, String child) | 在指定父文件夹路径下,根据子文件路径创建File实体类 |
File(File parent, String child) | 从父路径名和子路径字符串创建File实体类 |
File的创建方法
方法名 | 解释 |
---|---|
public boolean createNewFile() | 创建一个文件 |
public boolean mkdir() | 创建一个单极文件夹 |
public boolean mkdirs() | 创建一个多级文件夹 |
File的其他方法
方法名 | 解释 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹(不走回收站) |
public boolean isDirectory() | 根据抽象路径名判断File是否为目录 |
public boolean isFile() | 根据抽象路径名判断是否是文件 |
public boolean exists() | 根据抽象路径名判断文件是否存在 |
public String getName() | 根据抽象路径名获取文件或目录的名称 |
public File[] listFile() | 进入文件夹,获取这个文件夹里的说有文件和文件夹的File对象(包括隐藏文件),并封装到一个数组中 |
代码练习 删除一个文件夹,
import java.io.File;
public class Demo {
public static void main(String[] args) {
File file = new File("D:\\develop\\apache-maven-3.6.1");
}
public static void deleteDir(File src) {
File[] files = src.listFiles();
for (File file : files) {
// 是文件则直接删除
if (file.isFile()) {
file.delete();
} else {
// 用递归知道src.文件和空文件夹全部删除完毕,才可以删除src
deleteDir(file);
}
}
src.delete();
}
}
代码练习 获取一个文件夹中所有文件类型的个数
import java.io.File;
import java.util.HashMap;
public class Demo {
public static void main(String[] args) {
// 选择File模块下的文件
File file = new File("File");
// 键代表文件类型,value代表出现次数
HashMap<String, Integer> hm = new HashMap<>();
getCount(hm,file);
}
private static void getCount(HashMap<String, Integer> hm, File file) {
File[] files = file.listFiles();
for (File f : files) {
if (f.isFile()) {
String fileName = f.getName();
String[] split = fileName.split("\\.");
String key = split[split.length - 1];
if (hm.containsKey(key)) {
// 如果键存在则进行value累加
Integer value = hm.get(key);
value ++;
hm.put(key,value);
} else {
// 如果不存在则put
hm.put(key,1);
}
} else {
getCount(hm,f);
}
}
}
}
2、IO流
IO流的概述
IO流的作用 为了实现数据的永久化存储,数据从内存到硬盘的过程叫读,数据从硬盘到内存的过程叫写。
IO流的分类 Input输入流读,Output输出流写;从数据类型分为:字节流,字符流(只能操作纯文本文件)。
纯文本文件 一般用记事本打开可以看到的非乱码文件被认为是纯文本文件。
2.1、字节流
字节流写的步骤
创建字节输出流对象、写数据、关闭流释放资源。
字节流的构造
方法名 | 解释 |
---|---|
FileOutputStream(File file) | 创建文件输出流由指定的 File对象表示文件 |
FileOutputStream(FileDescriptor fdObj) | 创建文件输出流写入指定的文件描述符,表示与文件系统中实际文件的现有连接 |
FileOutputStream(File file, boolean append) | 创建文件输出流由指定的 File对象表示文件,参数2表示是否开启续写开关 |
FileOutputStream(String name) | 创建文件输出流以指定的名称写入文件 |
FileOutputStream(String name, boolean append) | 创建文件输出流以指定的名称写入文件,参数2表示是否开启续写开关 |
字节流写数据的方法
方法名 | 解释 |
---|---|
void write(int b) | 一次写一个字节数据 |
void write(byte[] b) | 一次写一个字节数组数据 |
void write(byte[], int off, int len) | 一次写一个字节数组的部分数据 |
代码练习 字节流写数据
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo1 {
public static void main(String[] args) {
FileOutputStream fos = null;
// 创建字节流输入对象,操作形参中的文件
try {
fos = new FileOutputStream("E:\\a.txt", true);
// 在E盘中的a.txt文件中写数据
byte[] bytes = {78, 79, 80};
fos.write(bytes, 1, 2);
// fos.write("\r\n".getBytes()) // 表示换行
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
// 关闭流,释放资源
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意
文件不存在则,自动创建;文件存在,读写数据时会先把文件内容清空;字节流写数据时,写入的时码表中对应的字符;close()方法是最后一步。"\r\n".getBytes() 获取字符串换行的字节数据。
字节流读的步骤
创建字节输入流对象、读数据、关闭流释放资源。
构造方法
方法名 | 解释 |
---|---|
FileInputStream(File file) | 通过文件路径或文件对象创建一个FileInputStream对象 |
FileInputStream(FileDescriptor fdObj) | 通过文件的描述符fdObj创建一个FileInputStream对象 |
FileInputStream(String name) | 通过文件的路径名创建一个FileInputStream对象 |
代码练习 字节流读数据
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputDemo1 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
fis = new FileInputStream("IO\\a.txt");
int i;
while ((i = fis.read()) != -1) {
// 在读写的时候得到的都是编码中对应的字符
System.out.println((char)i);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字节流读写练习 文件复制
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 文件复制- 读出来写进入
*/
public class FileInputDemo2 {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("E:\\a.txt");
fos = new FileOutputStream("IO\\a.txt");
byte[] bytes = new byte[1024];
int len;
while ((len = fis.read(bytes)) != -1) {
// 一次写一个数组的部分
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null && fos != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.2、字节缓存流
字节缓冲流 相当于提供了一个缓冲区,读写数据时针对的是缓冲区的数据,用于提高效率。
构造方法
方法名 | 解释 |
---|---|
BufferedOutputStream(OutputStream out) | 字节缓存输出流 |
BufferedInputStream(InputStream in) | 字节缓冲输入流 |
文件复制 缓冲流
import java.io.*;
/**
* 文件复制- 读出来写进入
*/
public class FileInputDemo2 {
public static void main(String[] args) {
// 在底层创建了一个长度为8192的字节数组
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("E:\\a.jpg"));
bos = new BufferedOutputStream(new FileOutputStream("IO\\a.jpg"));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null && bos != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
缓冲流提高效率的原理
减少了硬盘与内存之间的传输次数,每次按照8192的字节数组进行传输。
2.3、字符流
字符流的意义 在于,它可以解决文本文件的中文乱码问题。
字符流 = 字节流 + 编码表 (一般使用GBK码表,两个字节表示一个汉字,或者UTF-8编码,三个字节表示一个汉字)。
编码
方法名 | 解释 |
---|---|
byte[] getBytes() | 使用默认字符集将该String编码为一系列字节,将结果存储到字节数组 |
byte[] getBytes(String charsetName) | 使用指定字符集将该String编码为一系列字节,将结果存储到字节数组 |
解码
方法名 | 解释 |
---|---|
String(byte[] bytes) | 使用默认字符集解码指定的字节数组来构造新的String |
String(byte[] bytes, String charsetName) | 使用指定字符集解码指定的字节数组来构造新的String |
字节流读取中文出现乱码的原因
因为字节流一次只读取一个字节,相当于读取了一个汉字的部分,因为单个汉字由两个或三个字节组成。
字符流写数据的方法
方法名 | 解释 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写出一个字符数组 |
void write(char[] cbuf, int off, int len) | 写一个字符数组的部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的部分 |
代码练习 字符流写数据
import java.io.FileWriter;
import java.io.IOException;
public class FileWriteDemo1 {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("IO\\a.txt", true);
String s = "冲哥yyds";
char[] chars = {19, 80, 81, 98};
// 如果写的是字符串则写入的是字符串本身
fw.write(s, 0, 2);
// 如果写入的是数字或其他数据类型,则写入的是码表中对应的字符
fw.write(chars);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
方法名 | 解释 |
---|---|
flush() | 刷新流,可以继续写入 |
代码练习 字符流读数据
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo1 {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("IO\\a.txt");
char[] chars = new char[1024];
int len;
while ((len = fr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2.4 字符缓冲流
字符缓冲流 读数据
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderDemo1 {
public static void main(String[] args) {
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("IO\\a.txt"));
char[] chars = new char[1024];
int len;
while ((len = br.read()) != -1) {
System.out.println(new String(chars, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字符缓冲流的特有方法
方法名 | 解释 |
---|---|
void newLine() | 写一行行分隔符,相当于写一个换行 |
public String readLine() | 读整行,读到结尾,为null |
字符缓冲流 写数据
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedWriterDemo1 {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter("IO\\a.txt", true));
char[] chars = {97, 98, 99, 90};
bw.write(chars, 0, 3);
bw.write("你好");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3、转换流
转换流的分类 InputStreamReader(输入流),OutputStreamWriter(输出流)。
InputStreamReader 是从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
InputStreamReader
方法名 | 解释 |
---|---|
InputStreamReader(InputStream in) | 创建一个使用默认字符集的InputStreamReader |
InputStreamReader(InputStream in, String charsetName) | 创建一个使用命名字符集的InputStreamReader |
InputStreamReader(InputStream in, Charset cs) | 创建一个使用给定字符集的InputStreamReader |
InputStreamReader(InputStream in, CharsetDecoder dec) | 创建一个使用给定字符集解码器的InputStreamReader |
OutputStreamWriter 是从字符流到字节流的桥梁:使用指定的charset将写入的字符编码为字节。它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
OutputStreamWriter
方法名 | 解释 |
---|---|
OutputStreamWriter(OutputStream out) | 创建一个使用默认字符编码的OutputStreamWriter |
OutputStreamWriter(OutputStream out, String charsetName) | 创建一个使用命名字符集的OutputStreamWriter |
OutputStreamWriter(OutputStream out, Charset sc) | 创建一个使用给定字符集的OutputStreamWriter |
OutputStreamWriter(OutputStream out, CharsetEncoder enc) | 创建一个使用给定字符编码器的OutputStreamWriter |
转换流 代码演示
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws Exception {
// 字节流转换为字符流, 给定字符集
InputStreamReader irs = new InputStreamReader(new FileInputStream("E:\\a.txt","gbk"));
int len;
while ((len = irs.read()) != -1) {
// 若不给定字符集则读出的是乱码
System.out.println((char) len);
}
irs.close();
// 字符流转换为字节流
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("E:\\a.txt"),"UTF-8");
// 若不给定字符集则写入的是乱码
osw.write("六月九号");
osw.close();
}
}
jdk11之后,字符流提供了可以指定编码表的构造,可以代替转换流解决乱码。
public static void main(String[] args) throws Exception {
FileReader fr = new FileReader("E:\\a.txt", forName("gbk"));
int ch;
while ((ch = fr.read()) != -1) {
System.out.println((char)ch);
}
fr.close();
}
4、对象操作流
对象操作流的 基本特点,把对象以字节的形式写入文件,当我们需要查看内容时再次读出来。
对象操作流 分类,ObjectInputStream(对象操作输入流),ObjectOutputStream(对象操作输出流)。
对象操作输出流(对象序列化流):将对象写到本地文件中,或者在网络中传输对象。对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象。
对象操作流 写数据
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
Student student = new Student("张三", 23);
oos.writeObject(student);
oos.close();
}
}
注意
被对象操作流操作的对象必须实现 Serializable 接口,如果想要这个类的对象被序列化,那么这个类必须实现 Serializable 接口。
对象操作流 读数据
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
Student stu = (Student) ois.readObject();
System.out.println(stu); // Student{name='张三', age=23}
ois.close();
}
}
问题
当我们进行序列化之后,再改动文件内容后,进行反序列化时,会出现异常,原因是没有自己设置 serialVersionID 序列号时,虚拟机会自动给我们一个 serialVersionID 序列号,但是我们修改文件前后,虚拟机对给出的 serialVersionID 序列号,进行了更变,因此会出现异常,解决方案是自己设置 serialVersionID 序列号。
对象操作流 代码练习
import java.io.*;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
Student student1 = new Student("张三", 23);
Student student2 = new Student("李四", 24);
Student student3 = new Student("王五", 25);
oos.writeObject(student1);
oos.writeObject(student2);
oos.writeObject(student3);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
while (true) {
try {
Object o = ois.readObject();
} catch (EOFException e) {
// 这个异常表示读到结束
break;
}
}
ois.close();
}
}
import java.io.*;
import java.util.ArrayList;
public class BufferedWriterDemo1 {
public static void main(String[] args) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
Student student1 = new Student("张三", 23);
Student student2 = new Student("李四", 24);
Student student3 = new Student("王五", 25);
ArrayList<Student> list = new ArrayList<>();
list.add(student1);
list.add(student2);
list.add(student3);
// 将序列化数据作为一个整体写入
oos.writeObject(list);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
// 反序列化时读集合
ArrayList<Student> list1 = (ArrayList<Student>) ois.readObject();
list1.forEach(s-> System.out.println(s));
ois.close();
}
}
5、Properties
properties 可以被看作是一个 Map集合,一般用于存储字符串;
Properties的特有方法
方法名 | 构造 |
---|---|
Object setProperty(String key, String value) | 相当于HashMap的put方法 |
String getProperty(String key) | 根据键获取值 |
Set stringPropertyNames() | 从属性列表中返回一个不可修改的键集 |
void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入到Properties文件中 |
void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入到Properties文件中 |
代码练习 加载
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
FileReader fr = new FileReader("prop.properties");
prop.load(fr);
fr.close();
System.out.println(prop);
}
}
代码练习 写入
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
prop.put("李四","24");
prop.put("王五","25");
FileWriter fw = new FileWriter("prop.properties",true);
prop.store(fw,"这里注释");
fw.close();
}
}