java IO文件流
java IO文件流
- File类
- 静态成员
- 构造器
- 文件操作
- boolean createNewFile() 创建文件
- boolean mkdir() 创建文件夹
- boolean mkdirs() 创建多级文件夹
- boolean delete() 删除文件夹
- getName() 获取文件名
- getPath() 获取路径名
- long length()
- String getAbsolutePath()
- File getAbsoluteFile()
- String getParent()
- File getParentFile()
- boolean exists() 判断文件存在
- isDirectory() 判断是否文件夹
- isFile() 判断是否文件
- 文件遍历
- IO 字节流
- 转换流
- 缓冲流
- 字节输出缓冲流 BufferedOutputStream
- 字节输入缓冲流 BufferedInputStream
- 字符输出缓冲流 BufferedWriter
- 字符输入缓冲流 BufferedReader
- properties 类
- 序列化流和反序列化流
- 打印流
- 文件第三方工具类 commons-io
File类
java.io.File
提供操作文件的各种方法,与系统无关类
静态成员
pathSeparator 路径分隔符
是一个String类型
环境变量设置时候,不同路径之间的分隔符
在windows是分号,在unix是冒号
separator 名称分隔符
windows是反斜杠,unix下是正斜线
构造器
File(String path)
path可以写到文件夹,也可以写到文件,封装成File对象
File(String parent, String child)
父路径和子路径
File(“c:”, “windows”);
File(File parent, String child)
文件操作
boolean createNewFile() 创建文件
存在返回false,不存在才创建
boolean mkdir() 创建文件夹
存在返回false,不存在才创建
boolean mkdirs() 创建多级文件夹
推荐用该方法创建
boolean delete() 删除文件夹
删除失败返回false 比如文件不存在,或者文件被打开
该删除方法,不走回车站,直接硬盘中删除
删除有风险,运行需谨慎
getName() 获取文件名
getPath() 获取路径名
和toString() 一样
long length()
返回路径表示的字节数,注意,只限于文件
String getAbsolutePath()
获取绝对路径
File getAbsoluteFile()
获取绝对路径下的文件
String getParent()
获取父路径
File getParentFile()
获取父路径下的文件
boolean exists() 判断文件存在
isDirectory() 判断是否文件夹
isFile() 判断是否文件
文件遍历
String[] list() 遍历一个目录
返回的只是文件名
File[] listFiles() 遍历一个目录
File[] File.listRoots() 所有根目录
C D E 盘
File[] listFiles(FileFilter filter) 文件过滤器
需要自定义文件过滤实现类,重写accept
accept方法参数表示遍历得到的文件名,返回值代表是否接收
public class myFilter implements FileFilter {
public boolean accept(File filename) {
return filename.filename.getName().endsWith(".java");
}
}
递归遍历
public static void getAllDir(File dir) {
System.out.println(dir);
File[] = fileArr = dir.listFiles();
for(File f: fileArr) {
if(f.isDirectory()) {
getAllDir(f);
}else{
System.out.println(f);
}
}
}
带过滤器的递归遍历
public class myFilter implements FileFilter {
public boolean accept(File filename) {
if (pathname.isDirectory()) {
return true;
} else {
return pathname.getName().toLowerCase().endsWith(this.suffix);
}
}
}
public static void getAllDir(File dir) {
File[] = fileArr = dir.listFiles(new myFilter());
for(File f: fileArr) {
if(f.isDirectory()) {
getAllDir(f);
}else{
System.out.println(f);
}
}
}
将过滤的文件以数组的形式保存
private static File[] fileTranverse(File file) {
File[] fileList = file.listFiles();
for (File f : fileList) {
if (f.isDirectory()) {
fileList = array_merge(fileList, fileTranverse(f));
}
}
return fileList;
}
这里要用到将两个普通数组合并的方法,这个方法数组工具类中没有,需要靠集合来实现
/**
* 两个数组的的合并
*/
public static File[] array_merge(File[]... strs) {
List<File> list = new LinkedList<File>();
for (File[] str : strs) {
list.addAll(Arrays.asList(str));
}
return list.toArray(new File[0]);
}
IO 字节流
输入流: 从文件到程序中数据(读)
输出流: 从程序中的数据到文件(写)
字节输出流 OutputStream
java.io.OutputStream
这样的流每次只操作文件中的1个字节,可以操作任意文件
这个流中都是写入的方法,这是一个抽象类
write(int b); 写一个字节
write(byte[] b);
write(byte[] b, int offset, int length);
close()
flush()
它的已知子类 FileOutputStream 专用于写文件
FileOutputStream
该类的所有构造器的目的都是为了绑定数据输出目的,输出目的是一个文件,则会创建一个文件,即使原有文件,也会被覆盖(修改创建模式)
构造器会抛出文件找不到异常 FileNotFoundException
写入方法write会抛出个IOException异常,这两个异常之间有继承关系
FileOutputStream fos = new FileOutputStream("c:\\a.txt");
fos.write(100); // 实际写的结果是字母d ASCII表
fos.close();
FileOutputStream(File, boolean append)
第二个参数,表示是否追加
换行的实现,需要写入 \r\n
异常处理
文件操作,一般不抛异常,哪里操作处理哪里
处理异常时,释放资源的代码必须在finally块
需要注意的问题:
- 注意变量的作用域,需要提升
- catch处理方式
2.1 输出异常信息
2.2 抛出运行时异常 - close方法也会抛出异常,即使在finally块中,也要加上try…catch
- 如果流对象创建失败,则不需要关闭资源,但是考虑到finally代码块必须执行的问题,在释放之前要先判空
FileOutputStream fos = null;
try {
fos = new FileOutputStream("c:\\a.txt");
fos.write(100);
} catch(IOException ex) {
System.out.println(ex);
throw new RuntimeException("文件写入失败");
} finally {
try {
if(fos != null) {
fos.close();
}
} catch(IOException ex) {
throw new RuntimeException("文件关闭失败");
}
}
字节输入流 InputStream
int read() // 读取一个字节
存在文件指针记录,返回值是读取到的字节,如果到达数据结尾则返回-1
int read(byte[] b) // 读取字节数组
字节数组的作用,起到缓冲的作用,提高效率
每次读取的最大长度为定义的b长度,返回的是实际读取的长度,如果读取到最后的结束标记,则会返回-1
FileInputStream
构造器 绑定数据源
int read()
FileInputStream fis = new FileInputStream("c:\\a.txt");
int len = 0;
while((len = fis.read()) != -1) {
System.out.print(len);
}
int read(byte[] b)
FileInputStream fis = new FileInputStream("c:\\a.txt");
int len = 0;
byte[] b = new byte[1024];
while((len = fis.read(b)) != -1) {
System.out.print(new String(b, 0, len));
}
字符输出流 Writer
字符输出流,仅用于文本文件
write(int c)
write(char[] c)
write(char[] c, int, int)
write(String s)
FileWriter
用于指定输出目的地
输出时必须要先刷新流,也就只有输出字符流要刷新,其他流就不要刷新
关闭流也会自动刷新流
字符输入流 FileReader
专门用于读取文本文件
int read()
int read(char[] c)
构造方法绑定数据源
如果要读一个文本文件并看到结果,最好用字符流,因为它可以忽略中英文字节差异
转换流
字符字节转换流 OutputStreamWriter
字符流转换字节流
通常用于编码表之间的转换 如 GBK UTF8
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out, String charset)
转换流的目的只是纯粹编码表转换,最终还是字节流写进文件的
因此操作和字节流一致,写入还是需要刷新缓冲,关闭转换流自动关闭字节流
字节字符转换流 InputStreamReader
InputStreamReader(InputStream in)
InputStreamReader(InputStream in, String charset)
缓冲流
缓冲流的提出目的,就是提高读写效率
字节输出缓冲流 BufferedOutputStream
继承自OutputStream 和字节输出流一致
因此关注的是其构造器
构造器传递的就是字节输出流对象,提高该流的效率
字节输入缓冲流 BufferedInputStream
继承自InputStream 标准字节输入流
构造方法传递的参数就是任意的字节输入流,纯粹为了提高效率
字符输出缓冲流 BufferedWriter
BufferedWriter(Writer w)
能传的比较多,包括FileWriter 和 OutputStreamWriter 转换流
也是纯粹为了提高效率
注意字符输出流,都别忘了走刷新
void newLine(); 每次写入都会换行
可以省去\r\n,该方法可以避免操作系统的差异
bfw.write("asg");
bfw.newLine();
bfw.flush();
字符输入缓冲流 BufferedReader
BufferedReader(Reader r)
传递任意的字符输入流 如 FileReader 和 InputStreamReader 转换流
也是纯粹为了提高效率
String readLine()
读取一行数据,不包含行终止符,这也是该类特有的方法
如果读到文件结束符,返回null
BufferReader bfr = new BufferReader(new FileReader("c:\\a.txt"));
String line = null;
while((line = bfr.readLine()) != null) {
System.out.println(line);
}
LineNumberReader
BufferedReader 的一个子类,可以根据行号读取数据
其实所谓的行号,也就是一个计数器
properties 类
Hashtable的子类,实现Map接口,没有泛型,结合IO对象,用于持久化存储属性集
setProperty(String, String) 存储键值对
不在用map方法put,但实际上调用的就是put,但不建议我们直接用put
getProperty(String)
stringPropertyName()
类似于map接口中的方法keySet, 拿到的就是String集合
load(InputStream in)
load(Reader in)
持久化数据转化为临时数据
传递任意(字符或字节)输入流获取文件中的键值对,这个文件后缀名一般用.properties 格式按照
键值=值
的格式,每个键值对需要换行,中间不能有其他符号,比如分号或空格
键值对可以有注释,以#开头
Properties pro = new Properties();
FileReader fr = new FileReader("c:\\pro.properties");
pro.load(fr);
fr.close();
System.out.println(pro);
store
store(OutputStream out, String comment);
store(Writer out, String comment);
第一个参数为任意的输出流,第二个参数是写注释,声明为什么要改动文件,注意不要用中文
FileWriter fw = new FileWriter("src\\com\\scnu\\FileDemo2\\test.properties");
Properties pro = new Properties();
pro.setProperty("name", "大姨妈");
pro.setProperty("username", "autom");
pro.setProperty("password", "07568963");
pro.store(fw, "modifify by autom");
fw.close();
序列化流和反序列化流
问题:如何将对象以流的形式保存到文件中持久化存储?
Serializable 对象序列化接口
类都通过实现java.io.Serializable 接口启用其序列化功能,未实现该接口的无法使其序列化和反序列化
这个接口没有任何方法可重写,这是典型的标记接口之一
ObjectOutputStream
对象写入文件中,实现序列化
- 创建字节输出流对象,封装文件
- 创建对象序列化流对象,构造函数传递字节输出流
- 调用WriteObject方法写入对象
- 关闭序列化流,释放资源
FileOutputStream fos = new FileOutputStream("src\\com\\scnu\\FileDemo2\\object.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p1);
oos.writeObject(p2);
oos.close();
ObjectInputStream
反序列化
- 创建字节输入流对象,封装已序列化的文件
- 创建反序列化流对象,构造函数传递字节输入流
- 调用ReadObject方法写入对象
- 关闭序列化流,释放资源
注意: ReadObject 会抛出一个类找不到异常,这个异常通常是原来可序列化的类文件不存在了
FileInputStream fis = new FileInputStream("src\\com\\scnu\\FileDemo2\\object.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Object p1 = ois.readObject(); // ClassNotFoundException
Object p2 = ois.readObject();
Object p3 = ois.readObject(); // EOFException
ois.close();
读不到对象,将会发出文件终止符异常
不可序列化修饰
static 静态关键字
transient 瞬态关键字
***
***冲突现象
如果在反序列化前修改了源代码,那么反序列化后结果会抛出这么个异常:
java.io.InvalidClassException: com.scnu.FileDemo2.Player; local class incompatible: stream classdesc serialVersionUID = -7820994692885960704, local class serialVersionUID = -8596169815882869075
这就是***冲突现象,javac对有可序列化对象编译时,会根据类成员变量来生成***,修改源代码后,由于每次生成的***不同,那么保存在文件里的***和编译后的***就不匹配,这样就造成无法反序列化的状况
***ID
为了避免每次修改源代码发生的***冲突问题,一种方式是给可序列化对象指定唯一的***ID
private static final long serialVersionUID = -7820994692885960704L;
打印流
打印流包含两个类 PrintStream PrintWriter
- 打印流只负责数据源,不负责数据目的
- 打印流不会抛出IOException,可能会抛出其他异常
构造器用于绑定数据目的端,这是两者唯一的区别,PrintWriter更强大,更常用一些
PrintStream 支持File类型,字符串文件名,字节输出流
PrintWriter 支持File类型,字符串文件名,字节输出流 字符输出流
File 类型 和 String 类型的目的,构造器中可以通过第二个参数指定编码表的名字
流对象的数据目的,构造器中第二个参数可开启自动刷新功能
打印流的方法,和常用的输出方法,是完全一致的!
print()
println()
自动刷新
打印流可开启自动刷新功能,但有两个条件:
- 输出数据目的必须是流对象(文件,字符串都不行)
- 必须调用println, printf, format三个方法中的一个才可以
自动刷新功能是构造器中开启的
PrintWriter(OutputStream out, boolean autoFlush)
文件第三方工具类 commons-io
引入第三方类库
一般开源类库都包含 class javadoc source tests的jar包
分别为编译后的class文件,javadoc文档,源码和测试代码
我们需要引入的是class文件
引入方式要注意,最好在IDE上创建文件夹lib(而不是资源管理器)再把类库中的class文件拷贝过来
拷贝过来后,需要编译到项目目录中去,在eclipse中,右键选择bulidPath Add to Build Path
这时候工程环境就会多一个引用类库,这时就算引入完成了
在编辑器中,输入其中一个类名,如 FilenameUtils
自动导包,就能发现org.apache.commons.io.FilenameUtils
但是这里看不到源码,有时候需要看到源码,因此这里还需要关联源码
Attach Source -> External location
这个工具类库的方法全是静态的,因此直接类名直接调用