Java:File和IO流
18 File(18-19)
IO对硬盘的文件进行读写
File对(文件/文件夹)进行创建,删除等,表示要读写的(文件/文件夹)在哪
public static void main(String[] args) { // 将字符串转换为抽象路径名来创建新的File实例 String path = "D:\atest\a.txt"; // D:atesta.txt File file = ne File(path); System.out.println(file); // 拼接两个字符串为一个抽象路径名 String path1 = "D:\atest"; String path2 = "b.txt"; File file1 = ne File(path1,path2);//把两个路径拼接. System.out.println(file1);//D:atestb.txt // 拼接 file + 字符串为一个抽象路径名 File file2 = ne File("D:\atest"); String path3 = "c.txt"; File file3 = ne File(file2,path3); System.out.println(file3); //D:atestc.txt }18.2 绝对路径和相对路径
绝对路径从盘符开始
相对路径相对当前项目下的路径
public static void main(String[] args) { //这个路径固定不变了. File file = ne File("D:\itheima\a.txt"); //当前项目下的a.txt File file2 = ne File("a.txt"); //当前项目下 --- 指定模块下的 a.txt File file3 = ne File("filemodule\a.txt"); }18.3 File成员方法 创建文件/文件夹
public static void main(String[] args) thros IOException { File file = ne File("D:\atest\c.txt"); // createNeFile 注意 // 1.文件夹路径必需存在,如果不存在,例如atest没有提前创建,就会报错 // 2.a.txt不存在,可以创建成功,返回true。如果已存在,创建失败,返回false(不光判断文件,还是跟文件夹名判断) // 3.不管有没有后缀名,只能创建文件.不会创建文件夹。 // boolean res = file.createNeFile(); // System.out.println(res); // mkdirs 注意点 // 1.可以创建单级文件夹,也可以创建多级文件夹 // 2.不管调用者有没有后缀名,只能创建文件夹 // 3.如果文件名跟的路径名重复,也是会创建失败的,返回false。createNeFile也一样 boolean res2 = file.mkdirs(); System.out.println(res2); }删除文件/文件夹
public static void main(String[] args) { //删除文件 File file = ne File("D:\atest\a.txt"); boolean res = file.delete(); System.out.println(res); // 删除空白文件夹 File file2 = ne File("D:\atest\b"); boolean res2 = file2.delete(); System.out.println(res2); // 删除带文件的文件夹 File file3 = ne File("D:\atest\c"); boolean res3 = file3.delete(); // false System.out.println(res3); // 删除带空白文件夹的文件夹 File file4 = ne File("D:\atest\c"); boolean res4 = file4.delete(); // false System.out.println(res4); }判断和获取功能
listFiles 方法注意事项
- 当调用者不存在时,返回null
- 当调用者是一个文件时,返回null
- 当调用者是一个空文件夹时,返回一个长度为0的数组
- 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
- 当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
- 当调用者是一个需要权限才能进入的文件夹时,返回null
public static void main(String[] args) { File file = ne File("D:\atest"); File[] files = file.listFiles(); //返回值是一个File类型的数组 System.out.println(files.length); for (File path : files) { System.out.println(path); } }18.4 实用方法 删除带文件的文件夹
public class Test2 { public static void main(String[] args) { //练习二删除一个多级文件夹 //delete方法 //只能删除文件和空文件夹. //如果现在要删除一个有内容的文件夹? //先删掉这个文件夹里面所有的内容. //再删除这个文件夹 File src = ne File("C:\Users\apple\Desk\src"); deleteDir(src); } private static void deleteDir(File src) { //先删掉这个文件夹里面所有的内容. //1.进入 --- 得到src文件夹里面所有内容的File对象. File[] files = src.listFiles(); //2.遍历 --- 因为我想得到src文件夹里面每一个文件和文件夹的File对象. for (File file : files) { if(file.isFile()){ //3.判断 --- 如果遍历到的File对象是一个文件,那么直接删除 file.delete(); }else{ //4.判断 //递归 deleteDir(file);//参数一定要是src文件夹里面的文件夹File对象 } } //再删除这个文件夹 src.delete(); } }统计文件类型的个数
public class Test3 { public static void main(String[] args) { //统计一个文件夹中,每种文件出现的次数. //统计 --- 定义一个变量用来统计. ---- 弊端:只能统计一种文件 //利用map集合进行数据统计,键 --- 文件后缀名 值 ---- 次数 File file = ne File("filemodule"); // filemodule模块名 HashMap19 IO 19.1 概念hm = ne HashMap<>(); getCount(hm, file); System.out.println(hm); } private static void getCount(HashMap hm, File file) { File[] files = file.listFiles(); for (File f : files) { if(f.isFile()){ String fileName = f.getName(); String[] fileNameArr = fileName.split("\."); if(fileNameArr.length == 2){ String fileEndName = fileNameArr[1]; if(hm.containsKey(fileEndName)){ //已经存在 //将已经出现的次数获取出来 Integer count = hm.get(fileEndName); //这种文件又出现了一次. count++; //把已经出现的次数给覆盖掉. hm.put(fileEndName,count); }else{ //不存在 //表示当前文件是第一次出现 hm.put(fileEndName,1); } } }else{ getCount(hm,f); } } } }
I 表示intput,是数据从硬盘进内存的过程,称之为读。
O 表示output,是数据从内存到硬盘的过程。称之为写。
所谓的读写,都是以内存为参照物,内存在读,内存在写。
io流读/写数据
读数据内存读硬盘里的数据(输入流)
写数据内存把数据写到硬盘中(输出流)
io流分类
1.创建字节输出流对象
- 注意点
如果文件不存在,会帮我们自动创建出来.
如果文件存在,会把文件清空.2.写数据
- void rite(int b) 一次写一个字节数据
- void rite(byte[] b): 一次写一个字节数组数据
- void rite(byte[] b, int off, int len): 一次写一个字节数组的部分数据
3.释放资源
public static void main(String[] args) thros IOException { //1.创建字节输出流的对象 --- 告诉虚拟机我要往哪个文件中写数据了 // 第二个参数就是续写开关,如果没有传递,默认就是false, // 如果第二个参数为true,表示打开续写功能,那么创建对象的这行代码不会清空文件. FileOutputStream fos = ne FileOutputStream("D:\a.txt",true); //FileOutputStream fos = ne FileOutputStream(ne File("D:\a.txt")); // 两个效果一样 //2,写数据 // 这里的整数,实际写出的是整数在码表上对应的字母。 fos.rite(97); // 写入的是字节数据 fos.rite("rn".getBytes()); // 换行 byte [] bys = {97,98,99,100,101,102,103}; fos.rite(bys,1,2); // 写入多个字节,可以传byte数组,也可以写多个值 //3,释放资源 fos.close(); }
字节流写数据的异常处理(try…catch…finally)
public static void main(String[] args) { FileOutputStream fos = null; try { //System.out.println(2/0); fos = ne FileOutputStream("D:\a.txt"); fos.rite(97); }catch(IOException e){ e.printStackTrace(); }finally { //finally语句里面的代码,一定会被执行. if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }2. 读数据
1.创建字节输入流对象
- 注意点
如果文件不存在,会直接报错.2.读数据
- public int read(): 一次读取一个字节,返回值就是本次读到的那个字节数据.
- public int read(byte[] b)从输入流读取最多b.length个字节的数据
3.释放资源
基本使用
public static void main(String[] args) thros IOException { //如果文件存在,那么就不会报错. //如果文件不存在,那么就直接报错. FileInputStream fis = ne FileInputStream("bytestream\a.txt"); int read = fis.read(); //一次读取一个字节,返回值就是本次读到的那个字节数据. //也就是字符在码表中对应的那个数字. //如果我们想要看到的是字符数据,那么一定要强转成char System.out.println((char)read); //释放资源 fis.close(); }
读多个字节
public static void main(String[] args) thros IOException { FileInputStream fis = ne FileInputStream("bytestream\a.txt"); //1,文件中多个字节我怎么办? int b; hile ((b = fis.read())!=-1){ System.out.println((char) b); } fis.close(); }
复制功能
public static void main(String[] args) thros IOException { //创建了字节输入流,准备读数据. FileInputStream fis = ne FileInputStream("C:\itheima\a.avi"); //创建了字节输出流,准备写数据. FileOutputStream fos = ne FileOutputStream("bytestream\a.avi"); int b; hile((b = fis.read())!=-1){ fos.rite(b); } fis.close(); fos.close(); }
提升复制速度的解决方案
public static void main(String[] args) thros IOException { FileInputStream fis = ne FileInputStream("C:\itheima\a.avi"); FileOutputStream fos = ne FileOutputStream("bytestream\a.avi"); byte [] bytes = ne byte[1024]; int len; //本次读到的有效字节个数 -- 这次读了几个字节. hile((len = fis.read(bytes))!=-1){ fos.rite(bytes,0,len); } fis.close(); fos.close(); }19.3 字节缓冲流
主要是提升复制的效率
1. 复制数据(单个字节复制)public static void method1() thros IOException { BufferedInputStream bis = ne BufferedInputStream(ne FileInputStream("E:\itcast\字节流复制图片.avi")); BufferedOutputStream bos = ne BufferedOutputStream(ne FileOutputStream("myByteStream\字节流复制图片.avi")); int by; hile ((by=bis.read())!=-1) { bos.rite(by); } bos.close(); bis.close(); }2. 复制数据(字节数组复制)
public static void method2() thros IOException { BufferedInputStream bis = ne BufferedInputStream(ne FileInputStream("E:\itcast\字节流复制图片.avi")); BufferedOutputStream bos = ne BufferedOutputStream(ne FileOutputStream("myByteStream\字节流复制图片.avi")); byte[] bys = ne byte[1024]; int len; hile ((len=bis.read(bys))!=-1) { bos.rite(bys,0,len); } bos.close(); bis.close(); }19.4 字符流 1. 编码表
重点
indos默认使用码表为GBK,一个字符两个字节。
idea和以后工作默认使用Unicode的UTF-8编解码格式,一个中文三个字节。
编码
解码
字符流 = 字节流 + 编码表
不管是在哪张码表中,中文的第一个字节一定是负数。
1、想要进行拷贝,一律使用字节流或者字节缓冲流
2、想要把文件中的数据读到内存中打印或运算,请使用字符输入流。
想要把集合,数组,键盘录入等数据写到文件中,请使用字符输出流
3、GBK码表一个中文两个字节,UTF-8编码格式一个中文3个字节。
1.创建字符输出流对象
- 注意点
如果文件不存在,会帮我们自动创建出来.要保证父级路径存在。
如果文件存在,会把文件清空.2.写数据
- void rite(int c) 一次写一个字符数据
- void rite(char[] cbuf): 一次写一个字符数组数据
- void rite(char[] cbuf, int off, int len): 一次写一个字符数组的部分数据
- void rite(String str): 一次写一个字符串,可以写中文
- void rite(String str, int off, int len): 一次写一个字符串的部分数据,可以写中文
3.释放资源
- flush() 刷新流,还可以继续写数据。刷新流相当于把内存此时的内容保存到硬盘中。
- close(): 关闭流,释放资源,在关闭之前会先刷新流。一旦关闭,就不能再写数据
public static void main(String[] args) thros IOException { //创建字符输出流的对象 FileWriter f = ne FileWriter("charstream\a.txt"); f.rite(97); char [] chars = {98,99,100,101}; f.rite(chars); f.rite(chars,0,3); f.rite("黑马程序员abc"); //刷新流,把上面的内容先保存一份到硬盘中 f.flush(); f.rite("黑马程序员abc",0,2); //释放资源 f.close(); }6. 读数据
public static void main(String[] args) thros IOException { //创建字符输入流的对象 // FileReader fr = ne FileReader(ne File("charstream\a.txt")); FileReader fr = ne FileReader("charstream\a.txt"); //读取数据 //一次读取一个字符 int ch; hile((ch = fr.read()) != -1){ System.out.println((char) ch); } //创建一个数组 char [] chars = ne char[1024]; int len; //read方法还是读取,是一次读取多个字符 //他把读到的字符都存入到chars数组。 //返回值表示本次读到了多少个字符。 hile((len = fr.read(chars))!=-1){ System.out.println(ne String(chars,0,len)); } //释放资源 fr.close(); }19.5 字符缓冲流 1. 读数据
public static void main(String[] args) thros IOException { //字符缓冲输入流 BufferedReader br = ne BufferedReader(ne FileReader("charstream\a.txt")); //读取数据 char [] chars = ne char[1024]; int len; hile((len = br.read(chars)) != -1){ System.out.println(ne String(chars,0,len)); } br.close(); }2. 写数据
public static void main(String[] args) thros IOException { //字符缓冲输出流 BufferedWriter b = ne BufferedWriter(ne FileWriter("charstream\a.txt")); //写出数据 //实际写出的是97对应的字符a b.rite(97); b.rite("rn"); //实际写出的是97 - 101 对应的字符 abcde char [] chars = {97,98,99,100,101}; b.rite(chars); b.rite("rn"); //实际写的是abc b.rite(chars,0,3); b.rite("rn"); //会把字符串的内容原样写出 b.rite("黑马程序员"); b.rite("rn"); //会把字符串的一部分写出 abcde String line = "abcdefg"; b.rite(line,0,5); b.flush(); b.close(); }3. 字符缓冲流特有方法–neLine 跨平台的换行符
public static void main(String[] args) thros IOException { //字符缓冲流的特有功能 //字符缓冲输出流BufferedWrite neLine 跨平台的换行符 //创建对象 BufferedWriter b = ne BufferedWriter(ne FileWriter("charstream\a.txt")); //写出数据 b.rite("黑马程序员666"); //跨平台的回车换行 b.neLine(); b.rite("abcdef"); //跨平台的回车换行 b.neLine(); b.rite("-------------"); //刷新流 b.flush(); //释放资源 b.close(); }4. 字符缓冲流特有方法–readLine 读一整行
public static void main(String[] args) thros IOException { //字符缓冲流的特有功能 //字符缓冲输入流BufferedReader readLine 读一整行 //创建对象 BufferedReader br = ne BufferedReader(ne FileReader("charstream\a.txt")); //使用循环来进行改进 String line; //可以读取一整行数据。一直读,读到回车换行为止。 //他不会读取回车换行符。 // readLine如果读不到数据返回null hile((line = br.readLine()) != null){ System.out.println(line); } //释放资源 br.close(); }19.6 字节流和字符流小结 19.7 转换流
转换流就是来进行字节流和字符流之间转换的
- InputStreamReader是从字节流到字符流的桥梁
- OutputStreamWriter是从字符流到字节流的桥梁
public static void main(String[] args) thros IOException { //method1(); //method2(); //在JDK11之后,字符流新推出了一个构造,也可以指定编码表 FileReader fr = ne FileReader("C:\Users\apple\Desk\a.txt", Charset.forName("gbk")); int ch; hile ((ch = fr.read())!=-1){ System.out.println((char) ch); } fr.close(); } private static void method2() thros IOException { //如何解决乱码现象 //文件是什么码表,那么咱们就必须使用什么码表去读取. //我们就要指定使用GBK码表去读取文件. InputStreamReader isr = ne InputStreamReader(ne FileInputStream("C:\Users\apple\Desk\a.txt"),"gbk"); int ch; hile((ch = isr.read())!=-1){ System.out.println((char) ch); } isr.close(); OutputStreamWriter os = ne OutputStreamWriter(ne FileOutputStream("C:\Users\apple\Desk\b.txt"),"UTF-8"); os.rite("我爱学习,谁也别打扰我"); os.close(); } //这个方法直接读取会产生乱码 //因为文件是GBK码表 //而idea默认的是UTF-8编码格式. //所以两者不一致,导致乱码 private static void method1() thros IOException { FileReader fr = ne FileReader("C:\Users\apple\Desk\a.txt"); int ch; hile ((ch = fr.read())!=-1){ System.out.println((char) ch); } fr.close(); }19.8 对象操作流
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要用对象操作流读到内存中。
对象操作流分为两类对象操作输入流和对象操作输出流
- 对象操作输出流(对象序列化流)就是将对象写到本地文件中,或者在网络中传输对象
- 对象操作输入流(对象反序列化流)把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
public static void main(String[] args) thros IOException, ClassNotFoundException { ObjectInputStream ois = ne ObjectInputStream(ne FileInputStream("a.txt")); User o = (User) ois.readObject(); System.out.println(o); ois.close(); }2. 写数据
public static void main(String[] args) thros IOException { User user = ne User("zhangsan","qer"); ObjectOutputStream oos = ne ObjectOutputStream(ne FileOutputStream("a.txt")); oos.riteObject(user); oos.close(); } // 如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口.Serializable // User类 public class User implements Serializable {}3. 注意序列化对象后,修改了对象所属的JavaBean类
问题此时会出问题,会抛出InvalidClassException异常.
解决给对象所属的类加一个serialVersionUID
技巧打开ArrayList源码,它也实现了Serializable 接口
public class User implements Serializable { //serialVersionUID 序列号 //如果我们自己没有定义,那么虚拟机会根据类中的信息会自动的计算出一个序列号. //问题:如果我们修改了类中的信息.那么虚拟机会计算出一个序列号. //第一步:把User对象序列化到本地. --- -5824992206458892149 //第二步:修改了javabean类. 导致 --- 类中的序列号 4900133124572371851 //第三步:把文件中的对象读到内存. 本地中的序列号和类中的序列号不一致了. //解决? //不让虚拟机帮我们自动计算,我们自己手动给出.而且这个值不要变. private static final long serialVersionUID = 1L; // ...其他代码省略 }4. 注意成员不想序列化处理
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
public class User implements Serializable { private static final long serialVersionUID = 1L; private String username; private transient String passord; }5. 循环获取对象操作流
方式一
public static void main(String[] args) thros IOException, ClassNotFoundException { Student s1 = ne Student("杜子腾",16); Student s2 = ne Student("张三",23); Student s3 = ne Student("李四",24); ObjectOutputStream oos = ne ObjectOutputStream(ne FileOutputStream("a.txt")); oos.riteObject(s1); oos.riteObject(s2); oos.riteObject(s3); oos.close(); ObjectInputStream ois = ne ObjectInputStream(ne FileInputStream("a.txt")); Object obj; // 对象操作流,不是null,也不是-1,而是一个异常错误 hile(true){ try { Object o = ois.readObject(); System.out.println(o); } catch (EOFException e) { break; } } ois.close(); }
方式二
public static void main(String[] args) thros IOException, ClassNotFoundException { Student s1 = ne Student("杜子腾",16); Student s2 = ne Student("张三",23); Student s3 = ne Student("李四",24); ObjectOutputStream oos = ne ObjectOutputStream(ne FileOutputStream("a.txt")); ArrayList19.9 Properties 1. Properties概述list = ne ArrayList<>(); list.add(s1); list.add(s2); list.add(s3); //我们往本地文件中写的就是一个集合 oos.riteObject(list); oos.close(); ObjectInputStream ois = ne ObjectInputStream(ne FileInputStream("a.txt")); ArrayList list2 = (ArrayList ) ois.readObject(); for (Student student : list2) { System.out.println(student); } ois.close(); }
- 是一个Map体系的集合类
- Properties中有跟IO相关的方法
- 只存字符串
public static void main(String[] args) { Properties prop = ne Properties(); //增 prop.put("小龙女","尹志平"); prop.put("郭襄","杨过"); prop.put("黄蓉","欧阳克"); System.out.println(prop); //删 //prop.remove("郭襄"); //System.out.println(prop); //改 //put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖. prop.put("小龙女","杨过"); System.out.println(prop); //查 //Object value = prop.get("黄蓉"); //System.out.println(value); //遍历 Set3. Properties集合的特有方法
public static void main(String[] args) { //Object setProperty(String key, String value) --- put替代 //设置集合的键和值,都是String类型,底层调用 Hashtable方法 put Properties prop = ne Properties(); prop.setProperty("江苏","南京"); prop.setProperty("安徽","南京"); prop.setProperty("山东","济南"); System.out.println(prop); //String getProperty(String key) --- get替代 //使用此属性列表中指定的键搜索属性 //Set4. Properties集合与IO有关方法–loadstringPropertyNames() --- keySet替代 //从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 Set keys = prop.stringPropertyNames(); for (String key : keys) { String value = prop.getProperty(key); System.out.println(key + "=" + value); } }
public static void main(String[] args) thros IOException { //void load(Reader reader) 将本地文件中的键值对数据读取到集合中 //void store(Writer riter, String ments) 将集合中的数据以键值对形式保存在本地 //读取 Properties prop = ne Properties(); FileReader fr = ne FileReader("prop.properties"); //调用完了load方法之后,文件中的键值对数据已经在集合中了. prop.load(fr); fr.close(); System.out.println(prop); } }5. Properties集合与IO有关方法–store
public static void main(String[] args) thros IOException { //void load(Reader reader) 将本地文件中的键值对数据读取到集合中 //void store(Writer riter, String ments) 将集合中的数据以键值对形式保存在本地 Properties prop = ne Properties(); prop.put("zhangsan","123"); prop.put("lisi","456"); prop.put("angu","789"); FileWriter f = ne FileWriter("prop.properties"); prop.store(f,null); f.close(); }