File类、递归、IO流
一、File 类
-
概述
• java.io.File 是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找、修改、删除等操作。
• 构造方法
public static void main(String[] args) { //public String getAbsolutePath():返回此File的绝对路径名字符串 //相对路径 //idea中,java程序,相对路径是相对于项目路径存在的。 File f = new File("day10//a.txt"); System.out.println("相对路径的绝对路径:" + f.getAbsolutePath()); //绝对路径 File f2 = new File("F:\\study\\code\\Learning\\JavaSE_Plus\\day10\\a.txt"); System.out.println("绝对路径的绝对路径:" + f.getAbsolutePath()); }
• 常用方法
• “D:\a.txt” - 只是代表一个路径,不能说就是文件,因为还有可能是文件夹,到时候还要判断。
• idea中的默认相对路径是项目的路径。
• 获取当前路径得到的是当初new File对象用的路径。
• 只能获取文件的大小,不能获取文件夹的大小,因为计算机底层的文件夹本质上也是一个文件,只是在这个文件中标明了它所存放 的文件的信息,就好像一张清单一样,你去获取文件夹的大小并非真实大小。
• isDirectory() 与 isFile() ,这两个方法如果返回的是false,有两种情况 1 路径不是一个目录;2 路径压根不存在;delete()方法也一样,返回false,有可能是压根没有这个路径。
二、递归
-
指在当前方法内调用自己的这种现象,使用递归需要明确递归的出口(结束条件)和规律。
-
向下扩展,然后向上逐一返回,根据终止条件获得最后的值。
-
递归占用过多的栈内存,尽量不要用
-
案例 - 通过递归查找java 文件
public static void findJava(File file) { //2.判断不是目录,结束方法 if (!file.isDirectory()) { return; } //System.out.println(file.getAbsolutePath()); //3.是目录,获取目录下所有的File对象 File[] files = file.listFiles(); //4.遍历所有的File对象 for (File file1 : files) { //5.判断是否是目录 // 是目录 递归调用(规律) if (file1.isDirectory()) { findJava(file1); } else if (file1.isFile() && file1.getName().endsWith(".java")) { // 是文件 判断是否是java文件,打印文件名(出口) //System.out.println(file1.getName()); System.out.println(file1.getAbsolutePath()); } } }
三、IO流
-
IO 流概述
-
字节流
-
概述
-
字节输出流写出数据
-
字节输出流追加写入
-
字节输出流写出换行
-
字节输入流读取数据
-
为什么不能一次读取完在全部写出呢?因为内存有限,读取压力大,耗时,而且要读出来才能写出。
-
案例 - 将已存在的图片,从一个目录中,复制到另一个目录中。
public static void main(String[] args) throws IOException { //需求 将已存在的图片,从一个目录中,复制到另一个目录中。 //字节输入流对象 FileInputStream fis = new FileInputStream("day10\\image.jpg"); //字节输出流对象 FileOutputStream fos = new FileOutputStream("day10\\imageCopy.jpg"); //字节数组读写数据(复制) byte[] bys = new byte[8192]; //字符数组每次读取大小建议设为 8192 或者8192的整数倍,len表示实际读到的个数,bys表示读到的内容 int len = -1; while ((len = fis.read(bys)) != -1) { fos.write(bys, 0, len); } //关流 fis.close(); fos.close(); }
-
注意:路径不存在 创建字节输出流对象会报错
-
注意:创建一个流对象时,必须传入文件路径,且该路径下,如果没有该文件,会抛出文件不存在异常。
-
输入输出看的角度是内存(缓存),从硬盘读入到内存,从内存写出到内存
-
flush:刷新(将内存数据刷出到指定路径) 如果内存中有数据,就刷到路径中;close 底层会调用 flush ,但是有的时候还是要自己单独调用flush,因为close一调用就没法用了
-
读取的量太大,每读一部分都要flush 一下,确保每一段都正常写进去了
-
字节流输出流 - 写出换行,用字节输出流写出字符串,字符串需要调用getByte()方法转换成字节数组才能正常写出
-
两个 read 方法
-
~ 上一个int 返回的是ASCII码,比如字母a,读出来是97,需要转换才能看到a,-1表示没读到数据;第二个方法返回的int ,-1也表示没读到数据。
~ 读取要注意的问题
~ 这样写,每次只能读取2个数据,再读的话会把之前的覆盖,比如abc,第一次是ab,第二次会是cb,c把a覆盖了。
~ 正确用法
-
字符流
-
概述
-
字符输出流写出数据
-
字符输出流追加写入
-
字符输入流读取数据
-
用字节流复制文本还是没啥问题的,但是如果想把读到的字符比如汉字显示出来,用字节流会显示不全,因为汉字的字节是不固定的,有的2个,有的三个,所以要用到字符流。
-
字符流是对字节流的优化,本质上还是字节流
-
注意:字符输出流创建对象,路径对应的文件不存在,自动创建。
-
注意:使用字符输出流写数据,必须执行刷新操作。
-
注意:该文件或文件父路径不存在,会抛出文件不存在异常。
-
案例 - 复制文本
public static void main(String[] args) throws IOException { //注意:字符流只能复制字符(文本)数据 //输入流 FileReader fr = new FileReader("day10\\a.txt"); //输出流 FileWriter fw = new FileWriter("day10\\b.txt"); //字符读/写 //int ch = -1; //while ((ch = fr.read()) != -1) { // fw.write(ch); //} //字符数组读/写 char[] chs = new char[2]; int len = -1; while ((len = fr.read(chs)) != -1) { fw.write(chs, 0, len); } //关流 fr.close(); fw.close(); }
-
-
IO流扩展
-
JDK7版异常处理 - try with resouce
<1> 概述
<2> 可以关闭的对象才能使用
//try-with-resouce //把要关闭的对象放到try() 中 try(FileInputStream fis = new FileInputStream(""); FileOutputStream fos = new FileOutputStream("")){ //操作 }catch (IOException e){ e.printStackTrace(); }
-
属性集
<1> 概述
<2> 属性集IO相关操作
本质是个Map 集合,数据的存储形式是键值对,键和值一般都是String 用于存储配置信息
<3> 用属性集方法返回的是String 类型的
<4> 主要是结合IO流创建属性文件,用于写明配置信息
public static void main(String[] args) throws Exception { //字符流相关方法 //public void load(InputStream inStream): 从字节输入流中读取键值对。 //public void load(Reader reader):从字符输入流中读取键值对。 //创建属性集对象 Properties p = new Properties(); //取-读(字节,字符) //字节流 //FileInputStream fis = new FileInputStream("day11\\a.properties"); //p.load(fis); //fis.close(); //字符流 FileReader fis = new FileReader("day11\\a.properties"); p.load(fis); fis.close(); Set<String> keys = p.stringPropertyNames(); for (String key : keys) { String value = p.getProperty(key); System.out.println(key + "==" + value); } }
-
缓冲流读写数据
<1> 概述
<2> 缓冲流的使用
public static void main(String[] args) throws Exception { //字节缓冲流 //public BufferedInputStream(InputStream in):创建一个新的字节缓冲输入流。 //public BufferedOutputStream(OutputStream out):创建一个新的字节缓冲输出流。 //字符缓冲流 //public BufferedReader(Reader reader):创建一个新的字符缓冲输入流。 //public BufferedWriter(Writer writer):创建一个新的字符缓冲输出流。 //method1(); method2(); } private static void method2() throws IOException { FileReader fr = new FileReader("day11\\a.txt"); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter("day11\\b.txt"); BufferedWriter bw = new BufferedWriter(fw); //一次读一个字符 //int ch = -1; //while ((ch=br.read())!=-1){ // bw.write(ch); //} //使用字符数组读取数据 int len =-1; char[] chs = new char[8]; while ((len=br.read(chs))!=-1){ bw.write(chs,0,len); } //关流 关外面就行了(里面的流会自动关掉) br.close(); bw.close(); } private static void method1() throws IOException { //字节缓冲流 FileInputStream fis = new FileInputStream("day11\\a.txt"); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream("day11\\b.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); //一次读一个字节 //int by; //while ((by = bis.read()) != -1) { // //System.out.println(by); // bos.write(by); //} //使用字节数组读取数据 byte[] bys = new byte[8]; int len = -1; while ((len = bis.read(bys)) != -1) { System.out.println(new String(bys, 0, len)); bos.write(bys, 0, len); } //关流 关外面就行了(里面的流会自动关掉) bis.close(); bos.close(); }
<3> 缓冲流特有方法
BufferedReader类:public String readLine(): 读一行文字,读取不到内容,返回null。
BufferedWriter类:public void newLine(): 写一行行分隔符,由系统属性定义符号。
-
转换流
<1> 概述
<2> 转换流读写数据
<3> 转换流指定编码读写
~ 构造方法
InputStreamReader(InputStream in,String charsetName): 创建指定字符集的字符输入流。
OutputStreamWriter(OutputStream in,String charsetName): 创建指定字符集的字符输出流。
<4> 所有编码都包含ASCII 码。ASCII 码国际通用,对那些大家都用的字符都编好了,比如字母,逗号等。
<5> 转换流是字节流与字符流的桥梁,字节流就是通过转换流得到了字符流,没有转换流也就没有字符流,看它的名字就知道,肯定与字节流与字符流都有关系,原来是它们的桥梁。
<6> JDK11 可以在创建流的时候可以直接指定编码方式,JDK8 不支持。
<7> 乱码 - 一个严重的问题
读的时候我用UTF-8 去读GBK编码的文件,结果由于不同的编码格式,会出现一些utf-8 没法解码的内容,它就会把那些标识成无效字符,如果这个时候我把这些错误解码的数据写入文件,原本GBK能解码的内容,被这么一搞变成了无效字符,结果GBK也识别不了了,那数据就有问题了,如果源文件是一次性的,那后果很严重!
-
序列化概述
<1> 概述
<2> 序列化流读写数据
<3> 序列化注意事项
<4> 序列化操作 - 把类的对象这种运行时才有的数据存储到文件中,同时也支持读取出来,如果类中某个属性的值不想被序列化,可以用transient 关键字修饰成员变量,那在序列化的时候它的值就会显示成默认值。
<5> java文件编译后的class 文件不能改动,不然序列化也会失败。
<6> 序列版本号(class 文件的)要一致才能序列化,每次修改了类,都会生成新的版本号,这是系统给的;你也可以指定下面这句话,我们自己设置好版本号,修改了还是同样的版本号,才能正常序列化。
-
打印流
<1> 概述
<2> 注意事项
-
commons-io 工具包的使用
<1> 概述
<2> 工具包使用方法
先在模块文件夹中新建一个 lib 文件夹,然后把jar包复制过来,再右键 选择下方的add as library,idea会让你选择jar 包 的生效范围,比如是整个项目还是一个模块,目的是让它跟项目搭建关系,写代码才能使用到。
四、装饰者模式
-
概述
-
图解
-
今天的这些流其实用的就是装饰者模式,这些优化后的流的构造方法其实都需要传入一个基本流对象,本质上还是基本流在干活,这些流需要先创建一个字节流的对象,比如FileInputStream的 ,然后把字节流对象放进这些流的构造方法里,因为他们底层还是要用到基本的字节流