写在前面
- 对于java中IO流这部分的内容,一眼望过去就一个字:多。而且这部分内容在web开发中可能不是经常使用,即使要用,可能都是使用各种工具类或者是框架了。但是你不会呢又感觉说不过去。哎,难受。 这意味着即使现在清楚了,以后肯定会淡忘啊,要不简单写个小总结,以后忘了但又需要的时候也可以查阅?嗯,就这么搞吧。
- 阅读须知:对IO的一个简单汇总,部分地方的详情请参照API。
一、File类
1.1 基础认识
- 类: java.io.File,是实体文件的一个抽象形式。
- 静态常量:
- static String pathSeparator -> 与系统有关的路径分隔符,eg: 分号。
- static String separator -> 与系统相关的默认分割符, eg: / \
- 构造方法:
- public File(String pathname): 直接文件路径(相对|绝对)
- public File(String parent , String child): 需要在哪里创建什么文件。
- public File(File parent , String child): 同上,只不过第一个参数类型不同。
1. 2 使用
- 注意点: 皆是对象思想,故使用前一定是创建File对象。
- 创建方法:
- public boolean createNewFile() : 该名称的文件不存在时,创建一个新的空文件
- public boolean mkdir() :创建单个目录
- public boolean mkdirs():创建由此File对象表示的目录,包括任何必需但不存在的父级目录
- 获取方法:
- public String getAbsolutePath(): 获取File表示的绝对路径
- public String getPath(): 将File对象转为路径名字的字符串,创建时是啥就是啥
- public String getName(): 获取指定文件或者文件夹的名字
- public long length(): 获取的是指定文件的长度(字节)
- public File getParentFile(): 获取指定文件或者目录的父路径,没有父路径则返回null
- 判断方法:
- public boolean exists() : 此File对象代表的文件是否存在
- public boolean isDirectory() : 判断是否为文件夹
- public boolean isFile(): 判断是否为文件
- 删除方法:
- public boolean delete(): 删除此对象表示的文件或者文件夹
- 操作需知: ① 删除文件夹的时候只能是空的文件夹
- ② 删除之后的文件或者文件夹是不走回收站的,小心使用
- public boolean delete(): 删除此对象表示的文件或者文件夹
- 遍历方法:
- public String[] list() : 获取指定文件夹路径下的文件夹及文件的名字组成的字符串数组
- public File[] listFiles(): 获取的是指定文件夹路径下的文件夹及文件组成的文件对象数组
1.3 统计文件夹大小的案例
private static long countFileSize(File file) {
File[] files = file.listFiles();
// 为空直接返回
if (files.length == 0 ){
return 0;
}
long size = 0;
for (File f : files) {
if (f.isDirectory()){
size+=countFileSize(f);
}else {
size+=f.length();
}
}
return size;
}
二、IO总述与文件流
2.1 序
-
分类:
- 以操作的数据单位来分: 操作8bit的是字节流,操作16bit的是字符流
- 以流向来分: 输入流,输出流(我们一般以运行内存为基准)
- 以流所扮演的角色来分:节点流,处理流
-
这里就简单汇总一部分:
分类 字节输入流 字节输出流 字符输入流 字符输出流 抽象基类 InputStream OutputStream Reader Writer 文件流 FileInputStream FileOutputStream FileReader FileWriter 缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 转换流 —————— —————— InputStreamReader OutputStreamWriter 序列化流 ObjectInputStream ObjectOutputStream —————— —————— -
接下来,就是具体详情。
2.2 字符流
- 对于非文本类,不能使用字符流去操作。因为你在读的时候是使用默认编码去以字符为单位读取,但是其中难免有不认识的字节(因为我是非文本),所以读的时候就会出现乱码了。
- 基类:Reader和Writer
- 常用方法:
- int read() : 读方法,按一个一个字符读取返回值为int
- int read(char[] cbuf) : 读方法, 一次读取一个字符数组,返回有效长度
- close() : 读|写 方法,关闭流,对于写操作来说,是先刷新,再关闭。
- void write(int c) : 写方法,一次写单个字符
- void write(char[] cbuf) :写方法 , 一次写一个数组
- void write(char[] cbuf, int off , int len): 写方法,写以某位置开始的有效长度为len的字符数组
- void write(String str) : 写方法, 写一个字符串
- void write(String str, int off , int len) : 写方法,写以某位置开始的有效长度的字符串内容
- void flush(): 刷新缓冲区,流对象可以继续使用
2.2.1 字符输入流
- 构造方法
- FileReader(File file)
- FileReader(String fileName)
2.2.2 字符输出流
- 注: 操作的时候带了个缓冲区,所写的内容在缓冲区,需要刷新
- 构造方法
- FileWriter(File file)
- FileWriter(String fileName)
- 注:可以有第二个参数,为boolean 类型,默认为false,若显示定义为true, 则可以续写。
2.2.3 小案例
-
这里是复制单个文件的小案例,复制文件及文件夹请移步:传送门
public class FileWandRTest { public static void main(String[] args) { // 需要复制的文件 File from = new File("D:\\up2\\eg.txt"); // 需要复制到哪里 File to = new File("D:leboy\\up"); try { copyFile_char(from,to ); } catch (IOException e) { e.printStackTrace(); } } private static void copyFile_char(File from , File to) throws IOException { // 给赋值后的文件命名 File copy = new File(to, "copy.txt"); // 创建流 FileReader readF = new FileReader(from); FileWriter writeF = new FileWriter(copy); // 定义字符数组 char[] chars = new char[1024]; // 定义有效长度 int len = 0; while ((len = readF.read(chars))!=-1){ // 若想观察 //String s = new String(chars, 0, len); //System.out.println(s); writeF.write(chars,0 ,len ); } // 注意: 字符流写的时候自带缓冲,flush刷新,close刷新后关闭 // 关闭流 writeF.close(); readF.close(); } }
2.3 字节流
- 号称万能流,啥都可以操作。但是对于文本类(比如中文.txt)的操作时,你得等人家操作完,若半路杀出个程咬金去拦路,那么可能会读取到乱码,因为可能会把某些汉字字节给截断。
- 基类:InputStream和OutputStream
- 常用方法:与字符流何其相似,这里不做赘述。
2.3.1 字节输入流
- 构造: 参数均为要读取文件的位置。
- FileInputStream(File file )
- FileInputStream(String name)
2.3.2 字节输出流
- 构造:
- FileOutputStream(File file)
- FileOutputStream(String name)
- 注:可以有第二个参数,为boolean 类型,默认为false,若显示定义为true, 则可以续写。
2.3.3 小案例
-
这里是复制视频的小案例。
public static void main(String[] args) throws IOException { File fileIn = new File("D:\\io\\test.avi"); File fileOut = new File("D:\\io\\myCopy.avi"); // 输入流 FileInputStream fis = new FileInputStream(fileIn); // 输出流 FileOutputStream fos = new FileOutputStream(fileOut); // 定义字节数组 byte[] glass = new byte[1024]; // 定义字节长度 int len = 0; // 开始 while ((len = fis.read(glass))!=-1){ // 只获取有效长度 fos.write(glass,0,len); } // 关闭流 fos.close(); fis.close(); }
三、IO之处理流
- 说明:
- 基本的读,写,关流(先开后关)等操作与文件流是相同的,以下将不再赘述。
3.1 高效流(缓冲流)
- 缓冲流之所以可以高效读写,是因为在创建流对象的时候,会创建一个默认大小的缓冲区数组,通过缓冲区读写,减少系统IO的次数,从而提高读写的效率。
3.1.1 字节缓冲流
- 构造:
- BufferedInputStream(InputStream in): 形参为抽象类,故实参应为其子类,一般我们使用FileInputStream
- BufferedOutputStream(OutputStream out): 同理 , 一般我们使用FileOutputStream
3.1.2 字符缓冲流
- 构造 :
- BufferedWriter(Writer out) : 其中形参为抽象类,则传递他的子类,一般传递FileWriter
- BufferedReader(Reader in) : 同理,传递的是Reader的子类,一般传递FileReader
- 对于字符缓冲流特有的方法:
- 写: void newLine() : 用于写的时候换行,字符缓冲流对象调用即可。
- 读:String readLine() :一次读取一行, 字符缓冲流对象调用后的返回值即是读取的那一行字符串。有个注意的点:读完的时候返回的可不是-1, 而是返回null,那么在循环读取的时候条件不等于-1换为不等于null即可。
3.1.3 简单小案例
-
需求:已知某文本文件如下内容如下:
D.低头思故乡 C.举头望明月 A.窗前明月光 B.疑是地上霜
请拷贝一份文件,但是拷贝的这份文件中顺序是排好的。
实现如下:
public class BufferedWandRTest { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new FileReader("D:\\eg.txt")); BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\egOut.txt")); // TreeSet 自动 是按字典顺序排 TreeSet<String> strSet = new TreeSet<>(); // 定义字符串用于读 String s = null; while ((s=br.readLine())!=null){ strSet.add(s); } // 遍历进行输出 for (String str : strSet) { bw.write(str); // 换行 bw.newLine(); } // 关流 bw.close(); br.close(); } }
3.2 转换流
3.2.1 序
- 关于字符编码,若深究,篇幅不比IO流少,这里不做过多探讨,简单说明一下。
- 计算机存储信息都是二进制,我们看到的数字,符号,汉字等等信息存入计算机叫编码,反之叫解码。此两个过程都是按照某种规则在进行,类似密码本,就是一套自然语言与二进制之间的对应规则。
- 关于字符集,也可以称之为编码表就是上述所表达的密码本。
3.2.2 字符输入转换流
-
InputStreamReader: 是字节流通向字符流的桥梁
-
构造
- InputStreamReader(InputStream in): 创建一个使用默认字符集的字符流。
- InputStreamReader(InputStream in , String charsetName): 创建一个指定字符集的字符流。
-
简单示例
- test_1.txt文件是保存的方式为GBK,则其编码为GBK,我们现在默认(utf-8)去读与指定去读:
public static void main(String[] args) throws IOException { InputStreamReader isr_1 = new InputStreamReader(new FileInputStream("D:\\IO\\test_1.txt")); int r_1 = isr_1.read(); System.out.println((char)r_1); // � isr_1.close(); InputStreamReader isr_2 = new InputStreamReader(new FileInputStream("D:\\IO\\test_1.txt"),"GBK"); int r_2 = isr_2.read(); System.out.println((char)r_2); //我 isr_2.close(); }
3.2.2 字符输出转换流
-
OutputStreamWriter : 字符流通向字节流的桥梁
-
构造
- OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流。
- OutputStreamWriter(OutputStream in ,String charsetName): 创建一个指定字符集的字符流。
-
简单示例
- 以不同的编码写入数据,默认utf-8:
OutputStreamWriter osw_1 = new OutputStreamWriter(new FileOutputStream("D:\\IO\\test_2.txt")); osw_1.write("在吗?"); osw_1.close(); OutputStreamWriter osw_2 = new OutputStreamWriter(new FileOutputStream("D:\\IO\\test_3.txt"),"GBK"); osw_2.write("在吗?"); osw_2.close(); }
3.3 序列化流
3.3.1 序
- java提供一种对象序列化机制,用一个自己序列表示一个对象,包含对象的数据、对象的类型等等,字节序列写到文件后可以持久保存对象的信息。
- 序列化:采用ObjectOutputStream将对象转换为字节
- 反序列化:采用ObjectInputStream将字节重构为对象
3.3.2 ObjectOutputStream类
- 构造:
- public ObjectOutputStream(OutputStream out):形参为OutputStream抽象类,则实参应为其子类
- 写方法:
- public final void writeObject (Object obj): 将指定对象写出
3.3.3 ObjectInputStream类
- 构造:
- public ObjectInputStream(InputStream in ): 形参为InputStream抽象类,则实参应为其子类
- 读方法:
- public final Object readObject() : 读取一个对象
3.3.4 值得注意的点
-
一个对象若想序列化,则必须实现java.io.Serializable接口。
-
一个对象若想序列化,还必须存在可序列化的属性,static修饰的属性不可被序列化,若想某个属性不被序列化,专门提供了关键字修饰: transient.
-
对象要被反序列化,有要求:jvm可以找到class文件的类
-
反序列化要成功,还要保证反序列化操作时与序列化时的class文件是相同的,反序列化操作失败的原因有:
-
该类的序列版本号与从流中读取的类描述符的版本号不匹配
- Serializeable接口提供一个版本号:serialVersionUID可以验证序列化的对象和对应类是否版本匹配,serialVersionUID是一个long型的静态常量。
-
该类包含未知数据类型
-
该类没有可访问的无参构造方法
-
3.3.5 简单案例
-
定义Student类,有姓名、性别、年龄,并实现Serializable接口后,实例化一个对象进行序列化和反序列化测试
- Student类的部分展示:
public class Student implements Serializable { // 序列版本号 private static final long serialVersionUID = 3141592653589L;-
序列化操作:
// 序列化 private static void serialized() throws Exception { Student stu = new Student("迪丽热巴", "女", 18); // 开流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\IO\\test3_1.txt")); // 写 oos.writeObject(stu); // 关流 oos.close(); } -
反序列化操作:
// 反序列化 private static void deserialized() throws Exception { // 开流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\IO\\test3_1.txt")); // 读 Object o = ois.readObject(); // 查看 System.out.println((Student)o); // 关流 }
3.4 打印流
- 平时控制台打印输出调用的print和println方法都是java.io.PrintStream类的方法,因为System.out就是PrintSream类型。
- 构造:
- public PrintStream(String fileName): 创建新的打印流到指定文件。
- 使用:
- System.setOut(PrintStream ps): 可以设置系统的打印流向
四、异常处理
-
在实际开发中,不建议采用甩锅式上抛异常,那就需要及时处理异常,这里以FileWriter向文件输出某字符串为例子说明异常的处理。
-
JDK7之前的处理方式为try...catch...finally代码块。
public static void main(String[] args) { FileWriter f = null; try { f = new FileWriter("out.txt"); f.write("this is test!!"); } catch (IOException e) { e.printStackTrace(); } finally { // 若为null , 则文件创建不成功,也就无关闭之说 if (f != null){ try { f.close(); } catch (IOException e) { e.printStackTrace(); } } } } -
JDK7及以后新增了 try-with-resource语句,该语句可以确保每个资源在语句结束时关闭。
public static void main(String[] args) { // 若有多个创建流对象,可以用分号隔开 try (FileWriter f = new FileWriter("out.txt")){ f.write("this is test!"); } catch (IOException e) { e.printStackTrace(); } }
五、其他类的使用
5.1 Properties类
- java.util.Properties类继承于Hashtable,是一个持久的属性集,一般用于保存配置文件。
- 构造方法: public Properties();
- 自身特点:
- 数据结构为哈希表,无序
- 线程为安全,也就意味着运行速度相对慢
- key和value均为String
- 不允许出现null,无论是值还是键
5.1.1 基本的使用
- 与其说properties是一个类似HashMap结构,不如说他更像JSON。
- 特有的方法:
- Object setProperty(String key,String value): 添加键值对。
- String getProperty(String key) : 获取键对应的值,若没有key,则返回null.
- Set stringPropertyNames() : 所有的键组成的集合。
5.1.2 配合IO流使用
- 方法:
- void load(输入流对象) : 输入流读取列表,用于读操作
- void store(输出流对象,String comments) : 输出流列表 , 用于写操作, 可选择写入有关信息
5.1.3 配合使用小案例
public class PropertiesTest {
public static void main(String[] args) throws IOException {
Properties pro = new Properties();
// 设置
pro.setProperty("username","faker" );
pro.put("password","sss" );
//修改
pro.setProperty("password","aaa" );
// 持久化
pro.store(new FileWriter("pro.properties"), "supper pass!");
// 获取
Properties pro_ = new Properties();
pro_.load(new FileReader("pro.properties"));
// 取所有键
Set<String> keys = pro_.stringPropertyNames();
// 遍历
for (String key : keys) {
System.out.println("集合中"+key+"对应的值是:"+pro_.getProperty(key));
}
}
}
5.2 ResourceBundle工具类
- 说明:这里不对本地化信息展开叙述,仅是简化使用Properties集合中load方法读取文件。
5.2.1 基本介绍
-
ResourceBundle类中提供了一个静态方法,用于实例化对象(他是抽象类,所以是个子类实例)
- static ResouceBundle getBundle(String baseName);
-
须知:
-
① .properties文件必须为当前模块的src的子文件。
-
② baseName需要去掉后缀.properties。
-
② .properties文件一般都不存储中文,所以不建议,key一定不能为中文。
5.2.2 使用案例
public class ResourceBundleTest {
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("test");
String username = bundle.getString("username");
String password = bundle.getString("password");
System.out.println("usernaem="+username+", password="+password);
}
}
六、commons-io工具包
6.1 序
- IO技术开发中,代码量很大,而且代码的重复率较高。于是乎,apache开源基金组织提供的一组IO操作的类库,即commons-io, 它是可以提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类:
| 包 | 功能描述 |
|---|---|
| org.apache.commons.io | 有关Streams、Readers、Writers、Files的工具类 |
| org.apache.commons.io.input | 输入流相关的实现类,包含Reader和InputStream |
| org.apache.commons.io.output | 输出流相关的实现类,包含Writer和OutputStream |
| org.apache.commons.io.serialization | 序列化相关的类 |
6.2 如何使用
- 下载commons-io相关jar包,请移步:传送门
- 把commons-io-2.6.jar包复制到指定的Module的lib目录中
- 将commons-io-2.6.jar加入到classpath中
- 接下来就是使用了,里面工具类非常多,这里不做赘述,简单示例几个。
- org.apache.commons.io.IOUtils:封装了大量IO读写操作的代码。比如:
- IOUtils.copy(InputStream in, OutputStream out):把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数 (适合文件大小为2GB以下,比如拷贝图片)。
- IOUtils.copyLarge(InputStream in, OutputStream out): 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)。
- IOUtils.closeQuietly (任意流对象) : 释放资源,自动处理close()方法抛出的异常。
- org.apache.commons.io.FileUtils:封装了一些对文件操作的方法。 比如:
- FileUtils.copyFileToDirectory(File srcFile, File destFile) : 复制文件到另外一个目录下。
- FileUtils.copyDirectoryToDirectory( from, to): 复制from目录到to位置。
- FileUtils.writeStringToFile(File file, String str) : 写字符串到文本中。
- FileUtils.readFileToString(File file) :读取文本文件,返回字符串。
- org.apache.commons.io.IOUtils:封装了大量IO读写操作的代码。比如:
6.3 使用案例
-
简单示例说明,具体使用的时候具体情况具体分析即可。
public static void main(String[] args) throws IOException { //实现文件拷贝到某文件夹 FileUtils.copyFileToDirectory(new File("D:\\io\\test.avi"), new File("D:\\io\\up")); // 实现文件夹的复制,简化了我们用递归去操作 FileUtils.copyDirectoryToDirectory(new File("D:\\io\\up"),new File("D:\\io\\up") ); // 实现图片的拷贝 IOUtils.copy(new FileInputStream("D:\\io\\test.jpeg"), new FileOutputStream("D:\\io\\copy.jpeg")); }