一、文件
1.1 概念
文件在程序中是以流的形式来操作的
流:数据在数据源(文件)和程序(内存)之间经历的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(内存)到数据源(文件)的路径
1.2 常用操作
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
public class FileCreate {
public static void main(String[] args) {
}
//一、创建和删除文件
@Test
public void create01(){
//方式一:文件路径
String filePath = "d:\\file1.txt";
File file1 = new File(filePath);
//方式二:父目录文件+子路径
File parentFile = new File("d:\\");
String fileName = "file2.txt";
File file2 = new File(parentFile, fileName);
//方式三:父目录+子路径
String parentPath = "d:\\";
String fileName3 = "file3.txt";
File file3 = new File(parentPath, fileName3);
try {
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
System.out.println("文件创建成功!");
} catch (IOException e) {
e.printStackTrace();
}
//删除文件
file1.delete();
}
//二、文件的一些常用方法
@Test
public void methods(){
File file = new File("d:\\file1.txt");
//文件名 绝对目录 父目录 大小(字节) 是否存在、是文件、是目录
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());
System.out.println(file.getParent());
System.out.println(file.length());
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
File file1 = new File("d:\\e");
File file2 = new File("d:\\a\\b\\c");
//创建一级目录
file1.mkdir();
//创建多级目录
file2.mkdirs();
}
}
1.3 流的分类
- 按操作数据单位不同分为:字节流【(8 bit),二进制文件】,字符流【(按字符),文本文件】
- 按数据流的流向不同分为:输入流、输出流
- 按流的角色的不同分为:节点流、处理流(包装流)
1.4 IO流体系图
1.5 节点流和处理流
- 节点流:可以从一个特定的数据源读写数据,直接对数据操作;
- 处理流/包装流:“连接”在已存在的流(节点流或处理流)之上,为程序提供强大的读写功能。
例如:BufferReader封装了一个属性Reader,可以对Reader的子类进行操作,不会直接操作数据,而是操作文件、数组、管道等节点流。
二、文件流(节点流)
2.1 字节流:FileInputStream与FileOutputStream
- 文件输入流FileInputStream
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.*;
public class FileInputStream_ {
public static void main(String[] args) {
}
/**
* read()读取单个字节的读取
* 返回单个字节,若读取完毕,返回-1
* 效率比较低
*/
@Test
public void readFile() {
String filePath = "d:\\hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//1.创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream(filePath);
//2.从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
//如果返回-1,表示读取完毕
while((readData = fileInputStream.read()) != -1){
System.out.print((char)readData);//转成char显示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 使用read(byte[] b)读取文件,存到数组中
* 返回读取的字节数,若读取完毕,返回-1
* 提高效率
*/
@Test
public void readFile02() {
String filePath = "d:\\hello.txt";
//1.定义字节数组
byte[] buf = new byte[8];//一次读取8个字节
int readLen = 0;
FileInputStream fileInputStream = null;
try {
//2.创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream(filePath);
//3.从该输入流读取最多buf.length字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
//如果返回-1,表示读取完毕
//如果读取正常,返回实际读取的字节数
while((readLen = fileInputStream.read(buf)) != -1){
System.out.print(new String(buf, 0 ,readLen));//转成char显示
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 文件输出流FileOutputStream
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStream_ {
public static void main(String[] args) {
}
/**
* 演示使用 FileOutputStream 将数据写到文件中,
* 如果该文件不存在,则创建该文件
*/
@Test
public void writeFile() {
//创建FileOutputtream对象
String filePath = "d:\\hello.txt";
FileOutputStream fileOutputStream = null;
try {
//1.得到FileOutputStream对象
//说明
//1). new FileOutputStream(filePath)创建方式,覆盖原来内容
//2). new FileOutputStream(filePath, true)创建方式,追加到文件后面
fileOutputStream = new FileOutputStream(filePath,true);
//2.写入内容
//1).写入一个字节
//fileOutputStream.write('W'); //char可以自动转换成int
//2).写入一个字符串
String str = "hello,world!";
//fileOutputStream.write(str.getBytes()); //str.getBytes()这个方法可以把字符串转成字节数组
//3).写入一个字符串指定位置
/*
write(byte[] b, int off, int len)
将len字节从位于偏移量 off的指定字节数组写入此文件输出流。
*/
fileOutputStream.write(str.getBytes(), 0 , str.length());
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
- 文件拷贝
package FileExercise;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FIleCopy {
public static void main(String[] args) {
String srcfilePath = "d:\\hello1.txt";
String destfilePath = "d:\\hello2.txt";
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(srcfilePath);
fileOutputStream = new FileOutputStream(destfilePath);
byte[] buf = new byte[1024];
int readLen = 0;
while((readLen = fileInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fileInputStream != null){
fileInputStream.close();
}
if(fileOutputStream != null){
fileOutputStream.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
}
}
2.2 字符流:FileReader与FileWriter
- FIleReader相关方法
①new FIleReader(File/String)
②read:每次读取单个字符,返回该字符,如果到文件末尾返回-1
③read(char[] ):批量读取多个字符到数组,返回读取到的字符数,如果到文件末尾返回-1
相关API:
①new String(char[]):将char[]转换成String
②new String(char[],off,len):将char[]的指定部分转换成String
package FileExercise;
import org.junit.jupiter.api.Test;
import java.io.FileReader;
import java.io.IOException;
public class FileReader_ {
public static void main(String[] args) {
}
/**
* 单个字符读取文件
*/
@Test
public void readFile01() {
String filePath = "d:\\story.txt";
FileReader fileReader = null;
int data = 0;
//1.创建FileReader对象
try {
fileReader = new FileReader(filePath);
//2.循环读取,使用read,单个字符读取
while ((data = fileReader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 使用字符数组读取文件
*/
@Test
public void readFile02() {
String filePath = "d:\\story.txt";
FileReader fileReader = null;
int readLen = 0;
char[] buf = new char[8];
//1.创建FileReader对象
try {
fileReader = new FileReader(filePath);
//2.循环读取,使用read(buf),返回的是实际读取到的字符数
//如果返回-1,说明到文件结束
while ((readLen = fileReader.read(buf)) != -1) {
System.out.print(new String(buf, 0,readLen));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭
if (fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- FileWriter常用方法
①new FileWriter(File/String):覆盖模式,想当于流的指针在首段
②new FileWriter(File/String,true):追加模式,相当于流的指针在尾端
③write(int):写入单个字符
④write(char[]):写入指定数组
⑤write(char[],off,len):写入指定数组的指定部分
⑥write(string):写入整个字符串
⑦write(string,off,len):写入字符串的指定部分
注意:FileWriter使用后,必须要关闭(close)和刷新(flush),否则写入不到指定的文件!
三、缓冲流(处理流)
3.1 字节流:BufferedInputStream与BufferedOutputStream
package FileExercise;
import java.io.*;
/**
* 演示BufferedOutputStream 和 BufferedInputStream的使用
* 思考:字节流可以操作二进制文件,也可以操作文本文件吗?
* 答:可以,因为文本文件底层也是二进制
*/
public class BufferedStreamCopy {
public static void main(String[] args) {
String srcFilePath = "e:\\a1.java";
String destFilePath = "e:\\a2.java";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(srcFilePath));
bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
byte[] buff = new byte[1024];
int readLen = 0;
while ((readLen = bis.read(buff)) != -1) {
bos.write(buff, 0, readLen);
}
System.out.println("文件拷贝完毕");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (bis != null)
bis.close();
if (bos != null)
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
3.2 字符流:BufferedReader与BufferedWriter
package FileExercise;
import java.io.*;
public class BufferedCopy {
public static void main(String[] args) {
// BufferedReader和BufferedWriter是操作字符的
// 不要操作二进制文件
String srcFilePath = "e:\\a1.java";
String destFilePath = "e:\\a2.java";
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
String line;
try {
bufferedReader = new BufferedReader(new FileReader(srcFilePath));
bufferedWriter = new BufferedWriter(new FileWriter(destFilePath));
// 读取一行但是没有换行
while ((line = bufferedReader.readLine()) != null) {
bufferedWriter.write(line);
bufferedWriter.newLine();// 换行
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if (bufferedReader != null) {
bufferedReader.close();
}
if (bufferedWriter != null) {
bufferedWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
四、对象流(处理流)
4.1 使用场景
- 数据保存在文件中,也要保存其数据类型,并且可以恢复
- 一个Dog对象可以保存在文件中,并且可以从文件中恢复成为一个Dog对象
- 上述实质是序列化与反序列化
序列化和反序列化:
1)序列化就是在保存数据时,保存数据的值和数据类型;
2)反序列化就是在恢复数据时,恢复数据的值和数据类型;
3)需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable 和 Externalizable
4.2 字节流:ObjectInputStream与ObjectOutputStream
package FileExercise;
import java.io.*;
public class ObjectStream_ {
public static void main(String[] args) throws IOException, ClassNotFoundException {
new ObjectStream_().ObjectOutputStream_();
new ObjectStream_().ObjectInputStream_();
}
// 写文件
public void ObjectOutputStream_() throws IOException {
String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
// 基本类型已经继承了 Serializable 接口
oos.writeInt(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(9.5);
oos.writeUTF("hello world!");
// Dog要实现 Serializable 接口
oos.writeObject(new Dog("旺财", 10));
// 关闭流
oos.close();
}
// 读文件
public void ObjectInputStream_() throws IOException, ClassNotFoundException {
String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
// 读取(反序列化)的顺序需要和保存数据(序列化)的顺序一致
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
// 如果我们需要调用Dog的方法,需要向下转型
// 需要将Dog类的定义,拷贝到引用的位置
Dog dog = (Dog) ois.readObject();
System.out.println(dog.getClass());
dog.sound();
// 关闭流
ois.close();
}
}
class Dog implements Serializable {
private String name;
private int age;
// serialVersionUID 序列化的版本号,提高兼容性
// 修改Dog后,不会认为它是一个新的类,而是版本的升级
public Dog(String name,int age) {
this.name = name;
this.age = age;
}
public void sound(){
System.out.println("汪汪");
}
}
输出:
100
true
a
9.5
hello world!
class FileExercise.Dog
汪汪
4.3 字符流:ObjectReader与ObjectWriter
4.4 注意事项
- 读写顺序要一致;
- 要求序列化或反序列化对象 ,需要实现 Serializable 接口;
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性;
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员;
- 序列化对象时,要求里面属性的类型也需要实现序列化接口;
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化。
五、标准输入输出流(处理流)
字节流:System.in和System.out:
1)System.in的编译类型 InputStream,运行类型 BufferedInputStream,表示标准输入:键盘
2)System.out的编译类型 PrintStream,运行类型 PrintStream,表示标准输出:显示器
六、转换流(处理流)
InputStreamReader和OutputStreamWriter的目的将字节流转化成字符流
package FileExercise;
import java.io.*;
/**
* 系统默认情况下:使用utf-8
* 转换流:将字节流转换成字符流,用来解决文字乱码,可以指定编码方式
*/
public class Transformation {
public static void main(String[] args) throws IOException {
// 一、读取数据
String filePath = "C:\\Users\\DELL\\Desktop\\data.dat";
// 1.将 FileInputStream 转成 InputStreamReader
// 2.指定编码方式:gbk
InputStreamReader gbk = new InputStreamReader(new FileInputStream(filePath), "gbk");
// 3.将 InputStreamReader 传入 BufferedReader(目的:效率高)
// 开发者常常将1-3合在一起写
BufferedReader bufferedReader = new BufferedReader(gbk);
// 4.读取数据
String s = bufferedReader.readLine();
System.out.println(s);
bufferedReader.close();
// 二、写数据
// 1.将 FileOutputStream 转成 OutputStreamWriter
OutputStreamWriter gbk1 = new OutputStreamWriter(new FileOutputStream(filePath), "gbk");
// 2.写入数据
gbk1.write("你好 世界");
gbk1.close();
}
}
七、打印流(处理流)
打印流只有输出流
7.1 字节流:PrintStram
package FileExercise;
import java.io.IOException;
import java.io.PrintStream;
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream ps = System.out;
// 默认情况下,PrintStream输出数据位置是标准输出,即显示器
ps.println("hello world");
// println()底层是write
ps.write("hello world".getBytes());
ps.close();
// 修改输出位置
System.setOut(new PrintStream("C:\\Users\\DELL\\Desktop\\测试文档.txt"));
System.out.println("hello world");
}
}
7.2 字符流:PrintWriter
package FileExercise;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
PrintWriter printWriter = new PrintWriter(System.out);
printWriter.print("打印在显示器");
// 必须关闭,否则无法写入
printWriter.close();
PrintWriter printWriter1 = new PrintWriter(new FileWriter("C:\\Users\\DELL\\Desktop\\测试文档.txt"));
printWriter1.print("打印在文件中");
// 必须关闭,否则无法写入
printWriter1.close();
}
}
八、Properties类(配置文件)
8.1 基本介绍
- 专门用于读写配置文件的集合类,配置文件的格式:键=值
- 注意:键值对不需要空格,值不需要用引号,默认类型是String
8.2 使用
package FileExercise;
import java.io.*;
import java.util.Properties;
/**
* @author 神代言
* @version 1.0
*/
public class Properties_ {
public static void main(String[] args) throws IOException {
// 读取mysql.properties文件,得到ip,user,pwd
Properties_ properties_ = new Properties_();
properties_.traditionMethod();
properties_.propertiesMethod();
}
public void traditionMethod() throws IOException{
System.out.println("******传统方法******");
BufferedReader bufferedReader = new BufferedReader(new FileReader("src\\mysql.properties"));
String line = "";
while((line = bufferedReader.readLine())!=null){
String[] splite = line.split("=");
System.out.println(splite[0] + "的值为" +splite[1]);
}
bufferedReader.close();
}
public void propertiesMethod() throws IOException {
System.out.println("******Properties类******");
/* 读取mysql.properties文件,得到ip,user,pwd */
//1. 创建Properties对象
Properties properties = new Properties();
//2. 加载指定文件
properties.load(new FileReader("src\\mysql.properties"));
//3. 把键-值显示在控制台
properties.list(System.out);
//4. 根据Key获取Value
String user = properties.getProperty("user");
String pwd = properties.getProperty("pwd");
System.out.println("用户名为" + user);
System.out.println("密码是" + pwd);
/* 5.新建/修改一个配置文件 */
Properties properties2 = new Properties();
properties2.setProperty("charset","utf-8");
properties2.setProperty("user","汤姆");//中文在properties中以Unicode码存储
// 第二个参数表示注释
properties2.store(new FileOutputStream("src\\mysql2.properties")," This is comment");
}
}
输出:
******传统方法******
ip的值为192.168.100.100
user的值为root
pwd的值为12345
******Properties类******
-- listing properties --
user=root
pwd=12345
ip=192.168.100.100
用户名为root
密码是12345
mysql.properties
ip=192.168.100.100
user=root
pwd=12345
mysql2.properties
# This is comment
#Tue Sep 05 15:55:31 CST 2023
user=\u6C64\u59C6
charset=utf-8
特别说明
本文章是个人整理的学习笔记,参考b站韩顺平老师的课程(【零基础 快速学Java】韩顺平 零基础30天学会Java)。老师讲的非常好,有兴趣的可以去看一下。