学习笔记2:JavaSE & API(流处理)
1、File:java.io.File
(1)定义:File类的每一个实例可以表示硬盘(文件系统)中的一个文件或目录(实际上表示的是一个抽象路径)
(2)功能:
- 访问其表示的文件或目录的属性信息,例如:名字,大小,修改时间等等
- 创建和删除文件或目录
- 访问一个目录中的子项
(3)构造器:
- File(String pathname)
- File(File parent,String name)可参考文档了解
(4)常用方法:
- length():返回一个long值,表示占用的磁盘空间,单位为字节。
- canRead():File表示的文件或目录是否可读
- canWrite():File表示的文件或目录是否可写
- isHidden():File表示的文件或目录是否为隐藏的
- createNewFile():创建一个新文件,如果指定的文件所在的目录不存在会抛出异常java.io.FileNotFoundException
- mkdir:创建一个目录
- mkdirs:创建一个目录,并且会将所有不存在的父目录一同创建出来,推荐使用。
- delete():删除当前文件或目录,如果目录不是空的则删除失败。
- exists():判断File表示的文件或目录是否真实存在。true:存在 false:不存在
- isFile():判断当前File表示的是否为一个文件。
- isDirectory():判断当前File表示的是否为一个目录
- listFiles():获取File表示的目录中的所有子项
- listFiles(FileFilter filter):获取File表示的目录中满足filter过滤器要求的所有子项
(5)示例:
/**************************** 获取一个文件 ****************************/ // 读取一个文件,目录符号"./" 表示当前目录,对于IDEA而言,该目录是当前程序所在的项目目录。 File file = new File("./demo.txt"); // 获取文件或目录的名字 String name = file.getName(); // 获取大小,就是文件在硬盘上实际占用的空间(单位是字节) long length = file.length(); // 可写、可读、隐藏 boolean cw = file.canWrite(); boolean cr = file.canRead(); boolean ih = file.isHidden(); /**************************** 新建一个文件 ****************************/ // 创建文件的前提是该文件所在的目录必须存在,不然会抛出异常:java.io.IOException: 系统找不到指定的路径。 File newFile = new File("./newDemo.txt"); if(newFile.exists()){ System.out.println("该文件已存在!"); }else{ newFile.createNewFile(); } /*************************** 删除一个文件 ****************************/ if(file.exists()){ file.delete();//删除目录时只有空目录可以被删除! }else{ System.out.println("该文件不存在!"); } /*************************** 新建一个目录 ****************************/ File dir = new File("demo");// ./可以忽略不写 if(dir.exists()){ System.out.println("该目录已存在!"); }else{ dir.mkdir();//创建目录时要求所在的目录必须存在,否则创建失败 dir.mkdirs();//会一同将不存在的父目录全部创建出来(推荐使用该方法) } /*************************** 删除一个目录 ****************************/ if(dir.exists()){ dir.delete();//删除目录时只有空目录可以被删除! }else{ System.out.println("该目录不存在!"); } /*************************** 查看当前目录下的所有子项 ******************/ if(dir.isDirectory()){ //返回当前目录中的所有子项,每个子项都包含在返回的数组中作为一个元素。 File[] subs = dir.listFiles(); for(int i=0;i<subs.length;i++){ File sub = subs[i]; System.out.println(sub.getName()); } } /*************************** 筛选当前目录下的子项 *********************/ if(dir.isDirectory()){ // 过滤器:匿名内部类,lambda表达式方式 FileFilter filter = subFile -> { //多种写法 //String regex = ".*o.*";//正则表达式写法 //boolean match = name.matches(regex); //name.indexOf("o")>=0;// String的方法 return subFile.getName().contains("o"); }; File[] subs = dir.listFiles(filter); for(int i=0;i<subs.length;i++){ File sub = subs[i]; System.out.println(sub.getName()); } }
2、lambda表达式
(1)定义:JDK8之后,java支持了lambda表达式这个特性,可以用更精简的代码创建匿名内部类。
(2)特点:匿名内部类所实现的接口,只能有一个抽象方法。
(3)写法:
- 创建匿名内部类的对象:对象 = (参数)->{方法体}
- 简写:参数可以不写类型 ; 如果只有一个参数,参数的括号可以不写;如果方法体只有一句话,可省略大括号,包括return ;
(4)示例:
// 标准写法 FileFilter filter1 = (File file)->{ return file.getName().contains("o"); }; // 参数类型可以忽略不写,如果只有一个参数时,参数列表的"()"可以忽略不写 FileFilter filter2 = file->{ return file.getName().contains("o"); }; // 当方法体中只含有一句话时,方法体的"{}"可以忽略,并且如果含有return时要一同忽略 FileFilter filter3 = file->file.getName().contains("o");
3、流的分类
(1)分类一:按照功能分为节点流和处理流。也叫做低级流、高级流
- 节点流、低级流:真实连接程序与另一端。
- 处理流、高级流、过滤流。必须连接在其他流上,加工经过它的数据。
(2)分类二:java将流按照读写的数据单位划分为字节流与字符流
(3)两个字节流的超类(抽象类)
- java.io.InputStream:所有字节输入流的超类,其中定义了读取数据的方法。
- java.io.OutputStream:所有字节输出流的超类,其中定义了写出数据的方法。
(4)两个字符流的超类(抽象类)
- java.io.Reader:所有字符输入流的超类,定义了读取字符的相关方法。
- java.io.Writer:所有字符输出流的超类,定义了写出字符的相关方法。
- 最小读写单位:char 字符
(5)流连接:
- 实际应用:通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接。
- 关闭:close只需要在高级流上关闭,他会自动关闭和他连接的低级流。
4、文件流/字节流(字节、低级)
(1)定义:低级流,字节流,用于读写文件的流。
(2)FileOutputStream:
- 构造方法:文件不存在会自动创建,如果存在,默认会抹掉原来内容(覆盖模式)。使用构造方法append,会使用追加模式,不再覆盖。
- write(int):一次写一个字节,将int的低八位写进去。
- write(byte[]):写块,塞进去一个字节数组。
- close():关闭。
(3)FileInputStream:
- read(): 读一个字节并以int返回。int的低八位为读取到的值,前面补0。返-1为到了末尾。
- read(byte[]):读块
- close():关闭
(4)文件复制
- 单字节读写(随机读写模式)
FileInputStream fis = new FileInputStream("image.jpg"); FileOutputStream fos = new FileOutputStream("image_cp.jpg"); int d;//保存每次读取到的字节 long start = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间) while((d = fis.read()) != -1) { fos.write(d); } long end = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间) System.out.println("复制完毕!耗时:"+(end-start)+"ms"); fis.close(); fos.close();
- 块读写
FileInputStream fis = new FileInputStream("abc.exe"); FileOutputStream fos = new FileOutputStream("abc_cp.exe"); int len;//记录每次实际读取的字节量 byte[] data = new byte[1024*10];//10KB的字节数组 long start = System.currentTimeMillis(); while((len = fis.read(data))!=-1){ fos.write(data,0,len);//读取多少就写多少,offset是数组起始下标 } long end = System.currentTimeMillis(); System.out.println("复制完毕!耗时:"+(end-start)+"ms"); fis.close(); fos.close();
(5)文本的读写
- 字符集与字符编码:GBK、UTF-8等
- 字符串与编码二进制的转换:String的getByte方法,返回字节数组,放到write里。
/************************************ 写文本 ***************************************/ FileOutputStream fos = new FileOutputStream("fos.txt",true);//开启了追加模式 String lineOut = "我是一串字符串"; // String提供的方法可以将该字符串按照指定的字符集转换为对应的一组字节 byte[] dataOut = lineOut.getBytes(StandardCharsets.UTF_8); fos.write(dataOut); System.out.println("写出完毕!"); fos.close(); /************************************ 读文本 ***************************************/ FileInputStream fis = new FileInputStream("fos.txt"); byte[] dataIn = new byte[1024*10]; int len = fis.read(dataIn); System.out.println("实际读取的字节数:"+len); String lineIn = new String(dataIn,0,len, StandardCharsets.UTF_8); System.out.println(lineIn); fis.close();
5、缓冲流(字节、高级)
(1)定义:高级流,字节流,作用是提高读写数据的效率。缓冲流内部默认缓冲区为8KB,缓冲流总是块读写数据来提高读写效率。
(2)BufferedOutputStream:
- 构造方法:BufferedOutputStream(OutputStream out),BufferedOutputStream(OutputStream out,int size) 可以指定缓冲区
- flush():强制将缓冲区中缓存的数据一次性写出。注意:此方法是基类定义,其他流实现该方法的目的,只是为了传递flush动作给缓冲流。
(3)BufferedInputStream:
(4)示例:
/************************************ flush缓冲输出 ***************************************/ FileOutputStream fos = new FileOutputStream("bos.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); String lineOut = "我是字符串"; byte[] data = lineOut.getBytes(StandardCharsets.UTF_8); bos.write(data);//这里不会直接输出,需要缓冲区达到8KB System.out.println("写出完毕!"); bos.flush();//强制输出 bos.close(); /************************************ 复制文件 ***************************************/ //复制abc.txt FileInputStream fis = new FileInputStream("abc.txt"); BufferedInputStream bis = new BufferedInputStream(fis); FileOutputStream fos = new FileOutputStream("abc_cp.txt"); BufferedOutputStream bos = new BufferedOutputStream(fos); int d; long start = System.currentTimeMillis(); while((d = bis.read())!=-1){//使用缓冲流读取字节 bos.write(d);//使用缓冲流写出字节 } long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start)+"ms"); bis.close();//关闭流时只需要关闭高级流即可,它会自动关闭它连接的流 bos.close();
6、对象流(字节、高级)
(1)定义:高级流,字节流,在流连接中的作用是进行对象的序列化与反序列化。
(2)对象序列化:将一个java对象按照其结构转换为一组字节的过程。
- 类必须实现接口: java.io.Serializable ,Serializable是签名接口,里面没有任何抽象方法。
-
serialVersionUID:序列化的时候自动生成的版本ID,只要类的结构不变,这个UID不变;同时反序列化的时候也会校验。这个ID可以自己固定,避免类结构变化,导致反序列化时校验不通过:public static final long serialVersionUID = 1L ,建议这样做。
- transient关键字:可以修饰属性,用于在进行对象序列化时忽略不必要的属性,达到对象瘦身的目的。
- writeObject(对象):序列化方法,写入文件
(3)对象反序列化:将一组字节还原为java对象,注意:前提是这组字节是一个对象序列化得到的字节。
- readObject:反序列化
(4)示例:
/************************************ 序列化 ***************************************/ String name = "张三"; int age = 18; String gender = "男"; String[] otherInfo = {"公务员","江苏","爱好读书"}; Person p = new Person(name,age,gender,otherInfo); FileOutputStream fos = new FileOutputStream("person.obj"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(p); System.out.println("写出完毕!"); oos.close(); /************************************ 反序列化 ***************************************/ FileInputStream fis = new FileInputStream("person.obj"); ObjectInputStream ois = new ObjectInputStream(fis); Person person = (Person)ois.readObject(); System.out.println(person); ois.close();
7、字符流/转换流(字符、高级)
(1)定义:继承自Reader和Writer,是常见的字符流实现类,高级流。实际开发中我们不会直接使用它们,但是在流连接中它们是重要的一环。
(2)OutputStreamWriter:
(3)InputStreamReader:
- read():读取一个字符,返回的int值实际上表示的是一个char(低16位有效)。如果返回的int值表示的是-1则说明EOF。
(4)字符流最小读写单位为字符(char),但是底层实际还是读写字节,只是字符与字节的转换工作由字符流完成。
(5)示例:
/************************************ 使用转换流输出 ***************************************/ //向文件osw.txt中写入文本数据 FileOutputStream fos = new FileOutputStream("osw.txt"); //创建转换流时,可以通过第二个参数来指定字符集 OutputStreamWriter osw = new OutputStreamWriter(fos,StandardCharsets.UTF_8); String line = "我是字符串"; //byte[] data = line.getBytes(StandardCharsets.UTF_8);//这是使用文件流,将字符转为字节输出的写法 //fos.write(data); //字符流的write方法可以直接写出字符串,无需再手动转换为字节 osw.write(line); System.out.println("写出完毕!"); osw.close(); /************************************ 使用转换流读入 ***************************************/ //将osw.txt文件中的所有文字读取回来. FileInputStream fis = new FileInputStream("osw.txt"); InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8); int d; while((d = isr.read()) != -1){ System.out.print((char)d); } isr.close();
(6)意义:实际开发中我们还有功能更好用的字符高级流。但是其他的字符高级流都有一个共通点:不能直接连接在字节流上。而实际操作设备的流都是低级流,同时也都是字节流,因此不能直接在流连接中串联起来。转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上,在流连接中起到"转换器"的作用(负责字符与字节的实际转换)。
(7)连接图示:
8、缓冲字符流(字符、高级)
(1)定义:字符流,高级流,作用是提高读写文本数据的效率,并且
可以按行读写字符串。
(2)BufferedWriter
(3)BufferedReader
- readLine():读一行,该方法会连续读取若干字符,当遇到换行符停止,然后将换行符之前的内容以一个字符串形式返回。注意:这是内存操作,因为第一次调用readLine时,缓冲流会将数据先一次性读取到内部的char数组中(8K的字符),然后返回内部的一行字符串,如果流读取到了末尾,会返回null。
(4)示例:
/************************************ 缓冲字符输入流 ***************************************/ FileInputStream fis = new FileInputStream("demo");//文件流 InputStreamReader isr = new InputStreamReader(fis);//转换流 BufferedReader br = new BufferedReader(isr);//缓冲字符流 String line; while((line = br.readLine())!=null) { System.out.println(line); } br.close();
9、自动行刷新功能的缓冲字符输出流 PrintWriter(字符、高级)
(1)定义:java.io.PrintWriter 具有自动行刷新的缓冲字符输出流,实际开发中更常用。
(2)特点:
- 自动连接:它内部总是会自动连接BufferedWriter作为块写加速使用。
- println():直接换行写入字符串。
- 自动行刷新:构造函数加参数autoFlush,println方法就会写一行输出一行。本质上是调用了flush。
(3)示例:
/****************************** 完整的流链接过程:输出文字 ***************************************/ //1、文件字节输出流(是一个低级流),向文件中写入字节数据 FileOutputStream fos = new FileOutputStream("abc.txt",true); //2、转换输出流(是一个高级流,且是一个字符流)。1:衔接字符与字节流 2:将写出的字符转换为字节 OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8); //3、缓冲输出流(是一个高级流,且是一个字符流)。块写文本数据加速 BufferedWriter bw = new BufferedWriter(osw); //4、具有自动行刷新的缓冲字符输出流,此处打开了"自动行刷新",写一行输出一行 PrintWriter pw = new PrintWriter(bw,true); //5、控制台输入的每行字符串都按行写入文件。单独输入exit时退出。 Scanner scanner = new Scanner(System.in); while(true){ String line = scanner.nextLine(); if("exit".equalsIgnoreCase(line)){//忽略大小写 break; } pw.println(line); } pw.close(); /****************************** 简写:输出文字 ***************************************/ //直接向文件中写入字符串 PrintWriter pw2 = new PrintWriter("pw.txt", "UTF-8");//字符集是以字符串形式提供 pw2.println("第一行内容"); pw2.println("第二行内容"); System.out.println("写出完毕!"); pw2.close();
(4)连接示意图:
App-PrintWriter-BufferedWriter(缓冲字符流)-OutputStreamWriter(转换流)-FileOutputStream(字节流)-文件
10、输入输出流总结
11、补充:字节数组输入输出流(字节、低级)
(1)定义:java.io.ByteArrayOutputStream和ByteArrayInputStream 内部有一个字节数组,写入的内容暂存在内部数组中。
(2)特点:
- 内部数组:内部有一个用于缓冲的字节数组。
-
ByteArrayOutputStream通过该流写出的数据都会保存在内部维护的字节数组中。
-
toByteArray:获取内部数组,注意,返回的数组是copy的内部字节数组。
-
size():获取内部数组缓冲的字节,注意,是写了多少内容,并非数组的length。
(3)示例:
public class BAOSDemo { public static void main(String[] args){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(baos, StandardCharsets.UTF_8); BufferedWriter bw = new BufferedWriter(osw); PrintWriter pw = new PrintWriter(bw,true); /* ByteArrayOutputStream内部提供的方法: Byte[] toByteArray() 该方法返回的字节数组包含这目前通过这个流写出的所有字节 int size() 返回的数字表示已经通过当前流写出了多少字节(在流自行维护的字节数组中实际 保存了多少字节) */ byte[] data = baos.toByteArray(); System.out.println("内部数组长度:"+data.length); System.out.println("内部数组内容:"+ Arrays.toString(data)); System.out.println("内部缓冲大小:"+baos.size()); pw.println("helloworld"); data = baos.toByteArray(); System.out.println("内部数组长度:"+data.length); System.out.println("内部数组内容:"+ Arrays.toString(data)); System.out.println("内部缓冲大小:"+baos.size()); pw.println("think in java"); data = baos.toByteArray(); System.out.println("内部数组内容:"+ Arrays.toString(data)); System.out.println("内部缓冲大小:"+baos.size()); pw.close(); } }