java-文件输入输出处理
---------------------------------------------------
1、File类
File类是IO包中唯一代表磁盘文件本身的对象,File类定义了一些与平台无关的方法来操作文件。通过调用File类提供的各种方法,能够完成创建、删除文件、重命名文件、判断文件的读写权限权限是否存在、设置和查询文件的最近修改时间等操作。
File类没有无参构造方法,最常用的是使用下面的构造方法来生成File对象(注意分隔符可以使用"/"和"",但是使用""必须写"\",因为涉及转义的问题):
File(String pathName) pathName指的是文件的路径名;
File.separator
在Windows下的路径分隔符(\)和在Linux下的路径分隔符(/)是不一样的,当直接使用绝对路径时,跨平台会报No Such file or diretory异常。
File中还有几个与separator类似的静态常量,与系统有关,在编程中应尽量使用。
//File.separator 表示不同系统下的路径分割符
File file = new File("D:"+File.separator+"test"+File.separator+"test1");
//File file = new File("D:/test/test1.txt");
2、创建文件
创建文件对象 File(String pathname)
//pathname 文件路径
File file = new File("D:/test/test1.txt");
创建文件 createNewFile()
File file = new File("D:/test");
//判断文件是否存在,不存在,创建
if(!file.exists()){
try {
//判断文件是否创建成功
//createNewFile方法,会返回是否创建成功的结果,true成功,false失败
if(file.createNewFile()){
System.out.println("创建文件"+file.getName()+"成功");
}else{
System.out.println("创建文件"+file.getName()+"失败");
}
} catch (IOException e) {
e.printStackTrace();
}
3、创建目录(单级,多级)
mkdir() | 创建单级目录 |
---|---|
mkdirs() | 创建多级目录 |
创建单级目录 mkdir()
File dir = new File("D:/test/test1");//目前只有text文件夹
//判断目录是否存在,如果不存在则创建
if(!dir.exists()){
//mkdir方法,创建目录,返回是否创建成功的结果
//mkdirs方法,创建多级目录
if(dir.mkdir()){
System.out.println("创建目录成功"); //生成test1文件夹
}else{
System.out.println("删创建目录失败");
}
}else{
System.out.println("目录存在,不需要创建");
}
创建多级目录 mkdirs()
File dirs = new File("D:/test/test1/text1_1"); //目前只有text文件夹
//改变参数和方法即可
if(dirs.mkdirs()){
System.out.println("创建目录成功"); //生成test1文件夹及子文件夹text1_1
}else{
System.out.println("删创建目录失败");
}
4、删除文件或目录(只能删除单级空目录)
delete()
//存在则删除,不存在,提示
File file = new File("目录或文件路径");
if(file.exists()){
// delete方法,删除文件或者目录,并会返回是否删除成功的结果,true-成功,false-失败
//注意:删除目录,只能删除当前以及的目录,并且只能是空目录
if(file.delete()){
System.out.println("删除文件或目录成功");
}else{
System.out.println("删除文件或目录失败");
}
}else{
System.out.println("文件或目录不存在");
}
5、File类中常见方法
file.getName() | 文件名称 |
---|---|
file.length() | 文件大小 |
file.getPath() | 文件路径 |
file.getAbsolutePath() | 文件绝对路径 |
还有其他方法可以直接查看;
--------------------------------------------------
1、FileInputStream
1.1 初始化
FileInputStream(String name) |
---|
FileInputStream(File file) |
//直接通过文件地址初始化
FileInputStream fis = new ileInputStream("D:/test/test1.txt");
//通过File对象初始化
File file = new File("D:/test/test1.txt");
FileInputStream fis = new FileInputStream(file)
1.2 获取文件大小
available()
FileInputStream fis = new ileInputStream("D:/test/test1.txt");
fis.available(); //文件大小
1.3 读取文件内容
read() | 读取一个字节(返回对应字节的ascii码值) |
---|---|
read(byte b[]) | 根据字节缓冲数组的长度,进行读取(返回读取的字节数) |
read()
//文件 D:/test/test1.txt
内容
KH96abcdefghijk
FileInputStream fis = new ileInputStream("D:/test/test1.txt");
while (true){
//read() 方法:从输入流对象中,一次读取一个字节(返回的是对应字节的ascii码值,int类型)
int hasRead = fis.read();
//当读取到末尾,返回-1,代表文件读取结束
if(hasRead == -1){
break;
}
System.out.print((char) hasRead);
//打印文件中字符的ascii值
//转化为字符:KH96abcdefghijk
}
//最后一定要关闭资源
fis.close();
运行结果:
源文件的大小:15
KH96abcdefghijk
read(byte b[])
带缓冲字节数,读取文件内容,一次读取就不是一个字节,而是根据字节缓冲数组的长度,进行读取
错误案例
读取时通过read()来判断是否继续循环,读取到错误值
FileInputStream fis = new ileInputStream("D:/test/test1.txt");
//带缓冲字节数,根据字节缓冲数组的长度,进行读取
byte[] bytes = new byte[5];
//容易出错的判断方式:read()方式执行一次,就读取一个字节(没有保存,读完就扔,字节丢失),不可以作为判断条件
while(fis.read() != -1){
//循环读取内容
//带缓冲数组的读取,方法返回的是读取的字节数,不是读取的内容
//每次读取的数据,是由缓冲字节数组长度决定,每次都是从上一次读取的位置后开始继续读取,每次都是将读取的内容依次放入缓存数组
int hasRead = fis.read(bytes);
System.out.println("读取的字节数:"+hasRead);
System.out.println(new String(bytes));
System.out.println("读取文件成功");
}
fis.close();
运行结果:
源文件的大小:15
读取的字节数:5
H96ab //K丢失
读取文件成功
读取的字节数:5
defgh //c丢失
读取文件成功
读取的字节数:2
jkfgh //i丢失,并且还多出了上一次留下 dgh,这是因为没有读满缓冲字节数组,而造成的读取上一次的值
读取文件成功
正确案例
因为带字节缓冲数组返回的时读取到的长度,所以,用读取到的长度来判断是否要继续读取,和要写入多少个字节;
FileInputStream fis = new ileInputStream("D:/test/test1.txt");
//带缓冲字节数,根据字节缓冲数组的长度,进行读取
byte[] bytes = new byte[5];
//正确写法
int hasRead = 0;
while((hasRead = fis.read(bytes)) > 0){
//每次读取的内容
System.out.println(new String(bytes,0,hasRead));
}
fis.close();
运行结果:
源文件的大小:15
KH96a
bcdef
ghijk
// 没有丢失字节
1.4 资源关闭
close();
在使用流资源的时候一定要关闭资源,否则会造成资源浪费;
放在try( ) 里面
JDK1.7以后,只需将资源初始化放在try()里面就可以不用手动关闭流资源;
2、FileOutputStream
2.1初始化
FileOutputStream(File file) |
---|
FileOutputStream(File file, boolean append) |
FileOutputStream(String name) |
FileOutputStream(String name, boolean append) |
与FileInputStream类似,不过写入的文件不一定要存在,如果文件不存在,会自动创建一个空的文件;
2.2 写入方式 boolean append
boolean append 使用否以追加方式方式写入;
false(默认值,覆盖)
true(追加)
2.3 写入文件内容
write(byte b[]) |
---|
write(byte b[], int off, int len) |
String string = "KH96班,正在学习文件输出流,输出文件2";
//File file = new File("D:/test/test2.txt");
//JDK1.7以后,只需将资源初始化放在try()里面就可以不用手动关闭流资源,推荐使用;
try(FileOutputStream fos = new FileOutputStream("D:/test/test2.txt",true)){
//将字符串转成字节数组,写入目标文件
fos.write(string.getBytes());
//手动刷新缓冲区
fos.flush();
}catch (IOException e){
e.printStackTrace();
}
---------------------------------------------------
FileReader字符流读取文件,更适合用于读取文件,可以读取中文;
常用字符流类关系图
1、FileReader
1.1 初始化
FileReader(File file) |
---|
FileReader(String fileName) |
1.2 读取文件内容
read() | 按单个字符读取 |
---|---|
read(char cbuf[]) | 按字符数组长度读取 |
案例:按字符数组读取
//test1.txt文件内容:FileWriter测试内容
try(
//初始化字符读取流
FileReader frd = new FileReader("D:/test/test1.txt");
){
//定义一个可变字符串对象
StringBuilder sbd = new StringBuilder();
//定义缓冲字符数组
char[] chars = new char[5];
int hasRead = 0; //读取到的字符长度
while((hasRead = frd.read(chars))>0){
sbd.append(new String(chars,0,hasRead));
System.out.println("每次读取:"+sbd.toString());
}
//输出文件内容
System.out.println("文件全部内容:"+sbd.toString());
System.out.println("文件读取成功!");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
运行结果
每次读取:FileW
每次读取:FileWriter
每次读取:FileWriter测试内容
文件全部内容:FileWriter测试内容
文件读取成功!
2、FileWriter
2.1 初始化
FileReader(String fileName) |
---|
FileReader(File file) |
2.2 写入文件内容
write(String str) |
---|
write(String str, int off, int len) |
write(char cbuf[]) |
write(char cbuf[], int off, int len) |
案例:字符流直接写入字符串
//FileWriter 字符流写文件基本用法,可以直接写字符
try( FileWriter fwr= new FileWriter("D:/test/test2.txt")){
//定义写入文件
String string = "KH96,正在学习字符流写入文件";
//直接写入目标文件
fwr.write(string);
//刷新缓冲区
fwr.flush(); //一定要刷新缓冲区
System.out.println("字符流写入成功!!!");
}catch (Exception e){
e.printStackTrace();
}
运行结果
字符流写入成功!!!
-----------------------------------------------------
1、BufferedReader
BufferedReader高效字符流读取文件基本用法,自带缓冲区,读取文件效率高,支持逐行读取;
1.1 初始化
BufferedReader(Reader in) | 默认缓冲字符数组(大小8192) |
---|---|
BufferedReader(Reader in, int sz) | 自定义缓冲字符数组大小 |
1.2 读取文件内容
buffer1.txt文件内容
张三,23
李四,34
王五,34
逐行读取案例
try(BufferedReader bfrd = new BufferedReader(new FileReader("D:/test/buffer1.txt"))){
//使用逐行读取方式,读取文件
String readLinestr = bfrd.readLine();
//当读取内容为null的时候跳出循环
while(readLinestr != null){
System.out.println(readLinestr);
//继续读取下一行
readLinestr = bfrd.readLine();
}
System.out.println("逐行读取成功");
}catch (Exception e){
e.printStackTrace();
}
运行结果
张三,23
李四,34
王五,34
逐行读取成功
1.3 默认缓冲区
//默认缓冲区的大小为:8192个字符
源码
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize); //使用默认字符数组容量
}
private static int defaultCharBufferSize = 8192; //默认最大值为8192
2、BufferedWriter
BufferedWriter高效字符流写入文件基本用法,可以直接写整行,还可以换行(newLine());
2.1 初始化
BufferedWriter(Writer out) | 默认缓冲字符数组(大小8192) |
---|---|
BufferedWriter(Writer out, int sz) | 自定义缓冲字符数组大小 |
2.2写入文件内容
try(BufferedWriter bfwt = new BufferedWriter(new FileWriter("D:/test/buffer2.txt"))){
//写入内容
String string = "KH96,正在学习高效字符流写入";
//写入
bfwt.write(string);
//换行
bfwt.newLine();
bfwt.write(string+",新的一行");
//刷新缓冲区
bfwt.flush();
System.out.println("高效字符写入完成");
}catch (Exception e){
e.printStackTrace();
}
3、InputStreamReader
InputStreamReader(InputStream in) | 默认本地字符集 |
---|---|
InputStreamReader(InputStream in, String charsetName) | 自定义字符集 |
BufferedReader 通过InputStreamReader可以指定字符集读取文件的内容;
try(
//InputStreamReader提供了一个指定字符集的构造方法,创建输入字符对象,必须指定字符集跟文件字符集一致
BufferedReader bfrd = new BufferedReader(new InputStreamReader(new FileInputStream("D:/test/end1.txt"),"gbk"))
){
//使用逐行读取方式,读取文件
String readLinestr = bfrd.readLine();
//循环读取,读取到文件末尾,返回null
while(readLinestr != null){
System.out.println(readLinestr);
//继续读取下一行
readLinestr = bfrd.readLine();
}
System.out.println("逐行读取成功");
}catch (Exception e) {
e.printStackTrace();
}
4、 OutputStreamWriter
OutputStreamWriter(OutputStream out) | 默认本地字符集 |
---|---|
OutputStreamWriter(OutputStream out, String charsetName) | 自定义字符集 |
BufferedWriter 通过OutputStreamWriter可以指定字符集写入文件的内容;
try(
BufferedWriter bfrwt = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:/test/end1.txt"),"gbk"))
){
String str = "测试指定文件字符集为gbk写入";
bfrwt.write(str);
System.out.println("文件写入完成!!!");
}catch (Exception e) {
e.printStackTrace();
}
----------------------------------------------------
1、实例化
DataInputStream(InputStream in) | 参数是一个字节输入流 |
---|---|
DataOutputStream(OutputStream out) | 参数是一个字节输出流 |
演示
DataInputStream dis = new DataInputStream(new FileInputStream("D:/test/girl.jpg"));
DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:/test/girl2.jpg"));
2、举例
复制图片
try(
DataInputStream dis = new DataInputStream(new FileInputStream("D:/test/girl.jpg"));
DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:/test/girl2.jpg"));
){
//字符缓冲区
byte[] bytes = new byte[10];
//读取长度
int hasread = 0;
while((hasread = dis.read(bytes))>0){
dos.write(bytes,0,hasread);
}
//刷新缓冲区
dos.flush();
System.out.println("图片复制成功");
}catch (Exception e){
e.printStackTrace();
}
----------------------------------------------------
序列号
- 序列号是序列化和反序列化的唯一标识,是一个长整型数值;
- 如果类中不自己定义序列号,系统会自动生成一个序列号;
- 当一方实体类发生改变,而调用方的序列号是不会跟着改变的,不知道对象已修改,会导致两边序列号不一致,反序列化失败;
- 所以要求必须手动生成一个序列号;
- 手动生成序列号后,可以解决目标类发生改变,不影响接口调用,对象可以正确序列化,不过对象修改的属性返序列化后没有值;
序列化对象类
//如果要支持序列化操作必须实现序列化接口
//账户类
public class Account implements Serializable {
//手动生成序列号
private static final long serialVersionUID = 2116137267832764072L;
//账户名
private String aname;
//账户密码
private String apwd;
//set,get方法省略
@Override
public String toString() {
return "Account{" +
"aname='" + aname + '\'' +
", apwd='" + apwd + '\'' +
'}';
}
}
序列化
使用ObjectOutputStream 类的 writeObject(Object obj)方法
//序列化对象,写入文件
public static void xlhAccount() throws IOException {
Account account = new Account("KH96","12345");
//使用对象输出流,将内存中的对象写入到文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:/test/account.txt"));
//直接写入对象
oos.writeObject(account);
oos.close();
System.out.println("序列化对象写入成功");
}
序列化结果
反序列化
使用 ObjectInputStream 类的 readObject()方法
//反序列化目标对象读取写入序列化的文件,进行反序列化,变为写入的那个目标对象
public static void fxlhAccount() throws IOException, ClassNotFoundException {
//使用对象输入流,读入写入了序列化对象的文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/test/account.txt"));
Account account = (Account) ois.readObject();
ois.close();
//输出目标对象
System.out.println(account.toString());
System.out.println("读取序列化对象,反序列化成功");
}
反序列化结果
Account{aname='KH96', apwd='12345'}
读取序列化对象,反序列化成功
当类发生改变
改变后的序列化对象
//账户类
public class Account implements Serializable {
//手动生成序列号
private static final long serialVersionUID = 2116137267832764072L;
//账户名
private String aname;
//账户密码
private String apwd;
//添加手机
private String atel;
//set,get方法省略
@Override
public String toString() {
return "Account{" +
"aname='" + aname + '\'' +
", apwd='" + apwd + '\'' +
", atel='" + atel + '\'' +
'}';
}
}
反序列化结果
Account{aname='KH96', apwd='12345', atel='null'}
读取序列化对象,反序列化成功