本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为“云同桌”。
每月预计保持更新数量三章起,每章都会从整体框架入手,介绍章节所涉及的重要知识点及相关练习题,并会设置推荐学习时间,每篇博客涉及到的点都会在开篇目录进行总览。(博客中所有高亮部分表示是面试题进阶考点)
学习时间:4天 学习建议:IO流常用于文件操作,建议了解各类的主要使用情况并熟练使用
java.util.File
介绍:文件的抽象表示,可以进行文件删除,文件位置更改,新建文件/文件夹
注意:此类对象对文件操作时,会直接对源文件进行操作,包括删除,注意操作的文件的数据备份
静态变量:对于不同的操作系统,文件路径中使用的路径分隔符和名称分隔符是不一样的,为实现跨平台性,此类中提供了自动检测不同系统分隔符的静态成员变量,使用时,可以将其连接至路径中需要分隔符的位置
//pathSeparator,字符串形式的该系统相关目录路径分隔符(Windows里是;) //separator,字符串形式的该系统相关名称分隔符(Windows里是\) //实例: File file1 = new File("C:" +File.pathSeparator+ "Users" +File.pathSeparator+ "42419" +File.pathSeparator+ "Desktop" +File.pathSeparator+ "a.txt"); System.out.println(File.pathSeparator);//;构造方法:
File(String pathname),直接指定文件路径,获取到操作该文件的file对象 File(File parent, String child),指定要操作文件的父文件夹的file对象,获取到该file对象目录下文件的file对象 File(String parent, String child),指定要操作文件的父文件夹的路径,获取到该父文件+目录下文件的file对象 File file1 = new File("C:\\Users\\42419\\Desktop\\a.txt"); File file2 = new File(new File("C:\\Users\\42419\\Desktop"),"a.txt"); File file3 = new File("C:\\Users\\42419\\Desktop","a.txt");常用方法:
createNewFile(),若该file对象指向的文件不存在,则在该路径创建一个新的空文件 mkdir(),若该file对象指向的文件路径不存在,则在该路径创建一个新的空文件夹 delete(),在本地上删除该file指向的文件或文件夹 getAbsolutePath(),获取到该file对象指向的文件的绝对路径,以字符串形式返回 length(),获取到该file对象指向的文件的文件大小,单位是字节数实例:
new File("C:\\Users\\42419\\Desktop\\a.txt").createNewFile(); System.out.println(file1.length());//0 System.out.println(file1.getAbsolutePath());//C:\\Users\\42419\\Desktop\\a.txt isDirectory(),判断该file对象指向的是文件夹还是其他,如果是文件夹,则返回true isFile(),判断该file对象指向的是文件还是其他,如果是文件,则返回true getName(),获取到该file对象指向的文件或文件夹的名称,以字符串形式返回文件遍历练习:
要求,遍历出C盘桌面及其子路径下的所有.txt文件(注意只是遍历打印路径,不要删除)
public class Test { //被检索文件数量 static int count = 0; public static void main(String[] args) throws IOException { //创建C盘桌面路径的file对象 File file = new File("C:\\Users\\42419\\Desktop\\"); //获取得到该路径下的第一层所有文件的file对象 File[] files = file.listFiles(); //遍历搜索文件并输出绝对路径 searchFile(files); } public static void searchFile(File[] files) { if(files != null && files.length > 0){ //遍历每一个非空文件 for (File temp : files){ if(temp.isDirectory()){ //目录的话,进入下一层 //获取得到该路径下的第一层所有文件的file对象 File[] tempFiles = temp.listFiles(); //递归遍历搜索文件并输出绝对路径 searchFile(tempFiles); }else if(temp.isFile()){ //文件,对其进行检索 if(temp.getName().endsWith(".txt")){ //如果后缀是.txt,则输出其绝对路径 System.out.println("找到第" + count++ + "个.txt文件,路径为:" +temp.getAbsolutePath()); } } } }else{ System.out.println("检索文件路径为空,检索结束"); } } } /* 输出: 找到第0个.txt文件,路径为:C:\Users\42419\Desktop\a.txt 找到第1个.txt文件,路径为:C:\Users\42419\Desktop\build.gradle.txt 找到第2个.txt文件,路径为:C:\Users\42419\Desktop\Tomcat\Tomcat应用指南.txt 找到第3个.txt文件,路径为:C:\Users\42419\Desktop\测试题目0926\测试题目0926\3-叶子节点数目\sample-data\in-order.txt 找到第4个.txt文件,路径为:C:\Users\42419\Desktop\测试题目0926\测试题目0926\3-叶子节点数目\sample-data\num.txt 找到第5个.txt文件,路径为:C:\Users\42419\Desktop\测试题目0926\测试题目0926\3-叶子节点数目\sample-data\pre-order.txt 找到第6个.txt文件,路径为:C:\Users\42419\Desktop\测试题目0926\测试题目0926\4-中文分词\sample-data\test.txt 找到第7个.txt文件,路径为:C:\Users\42419\Desktop\测试题目0926\测试题目0926\4-中文分词\sample-data\train.txt */在开始之前,我们先对IO流做一个总的概述。在一些数据传输操作中,我们可以将数据的传输看做一种数据的流动,按照方向可以分为输入流和输出流。在JAVA中,IO操作主要是对java.IO包下一些常用类的使用。
IO流按照流动的数据类型,可以分为字节流(其他类型数据)和字符流(传输文字类型数据)
IO流的学习中的整体框架如图:
介绍:此抽象类是所有字节输出流的父类,里面定义了输出流最基本的关闭和写入的抽象方法
常用方法:
close()关闭字节输出流,在操作完输出流后,必须关闭流,否则会一直占用流的数据文件等等,造成错乱 write(byte[] b)向输出流中写入一个byte数组 write(byte[] b, int off, int len)向输出流中写入一个byte数组,从off下标开始,最后共写入len个byte数据 write(int b)写入一个单独的字节数据, 数据内容为传入的int数据的低8位数据介绍:字节流中最常用的输出流的类,对抽象父类OutputStream进行了方法的实现,常用方法也都是OutputStream的方法,可以把代码中的数据写入文件中
构造方法:
FileOutputStream(File file, boolean append)FileOutputStream(String name)FileOutputStream(String name, boolean append)可以通过file对象或者路径创建文件输出流对象,参数append的意思是追加,在流关闭之前在文件的末尾进行追加,如果不传append参数,默认是false
实例:
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\42419\\Desktop\\a.txt",true);//追加在a.txt文件末尾 byte[] bytes = "ABCDE".getBytes();//将字符串转为二进制放入比特数组 fileOutputStream.write(bytes); fileOutputStream.close();介绍:此类是字节流输入流的抽象父类,里面定义了文件读取和关闭流的常用方法
常用方法:
close()关闭输入流 read()从文件中读取一个字节的数据 read(byte[] b)从文件中读取一个字节数组b.length个字节的数据,如果文件中剩余数量不足该字节数组的长度,则将剩余字节有多少读多少,放到流的前面的部分 read(byte[] b, int off, int len)从文件中读取一个字节数组数据,从下标off开始,往后len个字节数据对于上述的read方法的重载,都需要注意,都有一个返回的int值,代表读取到流中的有效字节数,如果文件没有数据可读,则返回-1
介绍:对抽象父类InputStream进行了方法的实现,常用方法也都是InputStream的方法,可以把文件中的数据读取到代码中
构造方法:
FileInputStream(File file)FileInputStream(String name)通过文件对象或路径方式创建输入流对象
实例:读取一个不知道有多少内容的字节内容文件数据,并打印
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\42419\\Desktop\\a.txt"); byte[] bytes = new byte[10]; while(true){ //获取返回到的读入输入的有效字节个数 int len = fileInputStream.read(bytes); if(len == -1) break; String s = new String(bytes,0,len); System.out.print(s); } fileInputStream.close(); //ABCDEABCDEDFGDFGAGDMALGMAKMSVDKL原理:利用异或的特性,a == a ^b ^b ,即同一数据对同一个数经过两次异或后仍是原数据
适用性:不单单适用于txt文件,还可适用于其他uncoide编码类型的文件,支持中文加密
完整代码及截图可以查看我的另一篇博客java实现文件超简易加解密操作
核心代码:
while (true){ int data = fileInputStream.read(); if(data == -1){ //数据读完 break; } //加解密。同一数据经过对同一数字异或后还是原数据 fileOutputStream.write(data^99); }由于各国使用的语言不通,基础的ASCII编码解决不了此问题,后来又引入了Uncoide编码解决传统编码字符方案的局限性,后来目前开发中常用的UTF-8编码则是在Uncoide编码上发展而来的一种可变长度字符编码。
但在使用汉字时,常出现的请况是,因为每个汉字的UTF-8编码所占的字节数是不确定的,当读取一个汉字文档到定长字节数组时,经常出现读取“半个汉字”的情况,导致其显示为乱码。于是java为实现语言互通并解决乱码显示问题,开发了字符流处理的相关类,字符流不同与字节流,它会以一整个字符为单位进行输出,来负责解决非英文字符的数据显示问题。
介绍:此类是所有字符输出流的抽象父类,提供了基本的写入,追加等方法
常用方法:
append(char c):将指定的字符追加到此字符输出流后面write(int c):将对应的字符写入文件对象中write(String str):将该字符串写入文件对象中介绍:此类是字符输出流的实现类
构造方法:(基本和前面相同,可以通过file对象构建或通过路径直接构建,只需要注意多了一个编码格式参数)
FileWriter(File file, Charset charset, boolean append),新建一个对象,该对象是file对象的字符输出流,使用charset编码,输出方式为追加,后两个参数不写则为默认编码和不追加 FileWriter(String fileName, Charset charset, boolean append),新建一个对象,该对象的文件名称路径为fileName,使用charset编码,输出方式为追加,后两个参数不写则为默认编码和不追加常用方法:(基本都是其父类Writer的方法)
append(char c):将指定的字符追加到此字符输出流后面,返回自己本身对象,可以连续调用,参加实例使用实例:
//字符流 FileWriter fileWriter = new FileWriter("C:\\Users\\42419\\Desktop\\c.txt"); fileWriter.write("这是一个测试1"); fileWriter.append("这是一个测试2").append("这是一个测试3"); fileWriter.close();c.txt文件内容为:
这是一个测试1这是一个测试2这是一个测试3
介绍:此类是字符输入流的抽象父类
常用方法:(基本等同于字节输入流的常用方法)
close()read()read(byte[] b)read(byte[] b, int off, int len)对于上述的read方法的重载,都需要注意,都有一个返回的int值,代表读取到流中的有效字节数,如果文件没有数据可读,则返回-1
介绍:此类是字符输入流的实现类
构造方法:基本等同,可使用file对象或路径构造
FileReader(File file, Charset charset)FileReader(String fileName, Charset charset)常用方法:(无子类独有方法,基本都是父类Reader的方法)
c.txt文件内容为:
这是一个测试1这是一个测试2这是一个测试3
使用实例(无多余空格输出):
//字符输入流 FileReader fileReader = new FileReader("C:\\Users\\42419\\Desktop\\c.txt"); char[] c = new char[100]; int length = fileReader.read(c); System.out.println(new String(c,0,length)); fileReader.close();//这是一个测试这是一个测试2这是一个测试3额外补充一点,不论字节流还是字符流,输出流都是先将流对象的数据放入缓冲区,只有执行flush()后才会将缓冲区中的数据输出,close()也会调用刷新,不刷新或关闭的话,文件中不会写入数据
在java的底层,字符流其实也是使用的字节流然后添加了一些额外条件,同样,java也提供了程序员字节流转换为字符流的类,解决从其他来源获取到的字节流数据转换为字符流显示或存储的问题
介绍:可以将字节输入流转换为字符输入流
构造方法:
InputStreamReader(InputStream in),传递待转换的字节输入流即可,得到的对象可以等同于字符输入流进行操作常用方法:基本等同于字符输入流的操作
介绍:可以将字节输出流转换为字符输出流
构造方法:
OutputStreamWriter(OutputStream out),传递待转换的字节输出流即可,得到的对象可以等同于字符输出流进行操作常用方法:基本等同于字符输出流的操作
介绍:Writer的子类,字符打印输出流,里面有一些进行格式控制的方法,并提供了方便的传入字节流输出对象的构造方法,较为常用
构造方法:其余方法基本和其他流构造方法相同
PrintWriter(OutputStream out),传入一个字节输出流对象,可以通过此构造方法建立一个没有自动刷新的PrintWriter对象,这样就很简便的得到了一个字符输出流对象常用方法:由输出流的常用方法和print常用的方法组成,可以实现打印到某个文件中(此类本质上是输出流)
在java中,常将一些配置信息存储在properties文件中,常使用properties类进行数据收集然后输出到此类型文件中,此类型文件中,会以“键 = 值”的形式进行存储,以“#”作为注释的开头符号
介绍:此类是Map的子类,相当于集合与IO流的交互的类,可以从流中获取数据,并写入properties文件中
特点:
每个键和值都已string类型存储 线程安全常用方法:
load(InputStream inStream) load(Reader reader) 从输入流中加载得到数据至代码中
store(OutputStream out, String comments store(Writer writer, String comments) 将代码中存入对象的集合数据存储到输出流指向的properties文件中
getProperty(String key) 在对象数据中返回键名为key的对应的数据
存入与取出实例:
//存入properties文件 Properties properties1 = new Properties(); properties1.put("name","《冷场》"); properties1.put("author","李诞"); //字符输出流指向文件 FileWriter fw = new FileWriter("C:\\Users\\42419\\Desktop\\example.properties"); properties1.store(fw,"这是我最近看的一本新书"); fw.close(); //加载properties文件 //字符输入流指向文件 FileReader fr = new FileReader("C:\\Users\\42419\\Desktop\\example.properties"); Properties properties2 = new Properties(); properties2.load(fr); System.out.println(properties2.getProperty("name"));//《冷场》 System.out.println(properties2.getProperty("author"));//李诞 fr.close();example.properties文件内容为
#\u8FD9\u662F\u6211\u6700\u8FD1\u770B\u7684\u4E00\u672C\u65B0\u4E66 #Mon Oct 05 16:55:21 CST 2020 author=李诞 name=《冷场》
正常来说,我们在往文件里存储时,存储的是对象中的数据,然后下次从文件中取出数据,再放入对象中。而序列化将对象看做一个容器,整体进行操作。
序列化:将对象整个存储进文件中 反序列化:将序列化文件中的对象取出
注意: * 进行序列化的对象的类必须继承标记类Serializable * 2018年java官方通告序列化技术存在bug,望尽量不要使用。
介绍:实现序列化技术的字节输出流,将对象写入文件
常用方法:
writeObject(Object obj),将指定对象写入输出流中介绍:实现反序列化技术的字节输入流,读取序列化的文件中的对象
常用方法:
readObject(),从输入流中读取得到一个对象我们知道,如果在try块中使用了IO流,但是发生了异常,很有可能会跳过关闭流的代码,导致流没有被关闭,而因为作用域原因流关闭的代码也无法写在finally块中,于是,在JDK9中更新了一种新结构
可以在try块上创建IO流,然后将对象传递给try块,该IO流对象会在try-catch执行完后自动关闭,例如:
FileReader fr = new FileReader("C:\\Users\\42419\\Desktop\\example.properties"); try(fr) { }catch (IOException e){ }都学习到这里了,不妨点击一个关注吧~
