第十周笔记
十二、Java 8新增的Predicate操作Collection集合
使用 Predicate 可以充分简化集合的运算,假设依然有上面程序所示的 objs 集合,如果程序有如下三个统计需求: 1、统计集合中出现“C语言中文网”字符串的数量。 2、统计集合中出现“Java”字符串的数量。 3、统计集合中出现字符串长度大于 12 的数量。
十三、Java 8新增的Stream操作Collection集合
独立使用 Stream 的步骤如下: 1、使用 Stream 或 XxxStream 的 builder() 类方法创建该 Stream 对应的 Builder。 2、重复调用 Builder 的 add() 方法向该流中添加多个元素。 3、调用 Builder 的 build() 方法获取对应的 Stream。 4、调用 Stream 的聚集方法。
Stream 提供了大量的方法进行聚集操作,这些方法既可以是“中间的”(intermediate),也可以是 "末端的"(terminal)。 中间方法:中间操作允许流保持打开状态,并允许直接调用后续方法。上面程序中的 map() 方法就是中间方法。中间方法的返 回值是另外一个流。 末端方法:末端方法是对流的最终操作。当对某个 Stream 执行末端方法后,该流将会被“消耗”且不再可用。上面程序中的 sum()、count()、average() 等方法都是末端方法。
除此之外,关于流的方法还有如下两个特征。 有状态的方法:这种方法会给流增加一些新的属性,比如元素的唯一性、元素的最大数量、保证元素以排序的方式被处理等。有 状态的方法往往需要更大的性能开销。 短路方法:短路方法可以尽早结束对流的操作,不必检查所有的元素。
十四、泛型
前面我们提到 Java 集合有个缺点,就是把一个对象“丢进”集合里之后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了 Object 类型(其运行时类型没变)。
Java 集合之所以被设计成这样,是因为集合的设计者不知道我们会用集合来保存什么类型的对象,所以他们把集合设计成能保存任何类型的对象,只要求具有很好的通用性,但这样做带来如下两个问题: 1、集合对元素类型没有任何限制,这样可能引发一些问题。例如,想创建一个只能保存 Dog 对象的集合,但程序也可以轻易地 将 Cat 对象“丢”进去,所以可能引发异常。 2、由于把对象“丢进”集合时,集合丢失了对象的状态信息,集合只知道它盛装的是 Object,因此取出集合元素后通常还需要进 行强制类型转换。这种强制类型转换既增加了编程的复杂度,也可能引发 ClassCastException 异常。
I/O 流
一、流的概念
在 Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。
1、什么是输入/输出流
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。
数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。 1、按照流的方向主要分为输入流和输出流两大类。 2、数据流按照数据单位的不同分为字节流和字符流。 3、按照功能可以划分为节点流和处理流。
2、输入流
Java 流相关的类都封装在 java.io 包中,而且每个数据流都是一个对象。所有输入流类都是 InputStream 抽象类(字节输入流)和 Reader 抽象类(字符输入流)的子类。其中 InputStream 类是字节输入流的抽象类,是所有字节输入流的父类,
3、输出流
在 Java 中所有输出流类都是 OutputStream 抽象类(字节输出流)和 Writer 抽象类(字符输出流)的子类。其中 OutputStream 类是字节输出流的抽象类,是所有字节输出流的父类,
二、系统流
System.in:标准输入流,默认设备是键盘。 System.out:标准输出流,默认设备是控制台。 System.err:标准错误流,默认设备是控制台。
四、File类(文件操作类)
1、File类简介
File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。
File 类提供了如下三种形式构造方法。 File(String path):如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文 件。 File(String path, String name):path 是路径名,name 是文件名。 File(File dir, String name):dir 是路径对象,name 是文件名。
File 类中有以下两个常用常量: public static final String pathSeparator:指的是分隔连续多个路径字符串的分隔符,Windows 下指;。例如 java -cp test.jar;abc.jar HelloWorld。 public static final String separator:用来分隔同一个路径字符串中的目录的,Windows 下指/。例如 C:/Program Files/Common Files。
5、遍历目录
通过遍历目录可以在指定的目录中查找文件,或者显示所有的文件列表。File 类的 list() 方法提供了遍历目录功能,该方法有如下两种重载形式。
1. String[] list()
该方法表示返回由 File 对象表示目录中所有文件和子目录名称组成的字符串数组,如果调用的 File 对象不是目录,则返回 null。
提示:list() 方法返回的数组中仅包含文件名称,而不包含路径。但不保证所得数组中的相同字符串将以特定顺序出现,特别是不保证它们按字母顺序出现。
2. String[] list(FilenameFilter filter)
该方法的作用与 list() 方法相同,不同的是返回数组中仅包含符合 filter 过滤器的文件和目录,如果 filter 为 null,则接受所有名称。
五、RandomAccessFile类
1、动态读取文件内容
所谓动态读取是指从文件的任意位置开始访问文件,而不是必须从文件开始位置读取到文件末尾。动态读取需要用到 Java 中的 RandomAccessFile 类。
RandomAccessFile 类的构造方法有如下两种重载形式。
1、RandomAccessFile(File file, String mode):访问参数 file 指定的文件,访问形式由参数 mode 指定,mode 参数有两个常用的可选值 r 和 rw,其中 r 表示只读,rw 表示读写。
2、RandomAccessFile(String name, String mode):访问参数 name 指定的文件,mode 参数的含义同上。
注意:如果使用 rw 方式声明 RandomAccessFile 对象时,要写入的文件不存在,系统将自动进行创建。
RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,
它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。
由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,
使用 RandomAccessFile 类是一个很好的选择
RandomAccessFile类提供了两个构造方法:
RandomAccessFile(File file, String mode)
其中file表示文件对象,mode表示文档的属性(常用属性有r:只读模式,w:只写模式, rw:可读可写模式)
RandomAccessFile(String path, String mode)其中path表示文件所在路径,mode表示文档的属性(常用属性有r:只读模式,w:只写模式,rw:可读可写模式)
注意:当指定路径下文件不存在,则会创建改文件对象
六、字节流的使用
InputStream 是 Java 所有字节输入流类的父类,OutputStream 是 Java 所有字节输出流类的父类,它们都是一个抽象类,因此继承它们的子类要重新定义父类中的抽象方法。
下面首先介绍上述两个父类提供的常用方法,然后介绍如何使用它们的子类输入和输出字节流,包括 ByteArrayInputStream 类、ByteArrayOutputStream 类、FileInputStream 类和 FileOutputStream 类。
1、字节输入流
InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。 ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。 FileInputStream 类:从文件中读取数据。 PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。 SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。 ObjectInputStream 类:将对象反序列化。
2、字节输出流
OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。 ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。 FileOutputStream 类:向文件中写数据。 PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。 ObjectOutputStream 类:将对象序列化。
3、字节数组输入流
ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。
1、ByteArrayInputStream(byte[] buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。
2、ByteArrayInputStream(byte[] buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数组类型的数据源,offset 指定在数组中开始读取数据的起始下标位置,length 指定读取的元素个数。
4、字节数组输出流
ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。 1、ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。 2、ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。
ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。 1、intsize():返回缓冲区中的当前字节数。 2、byte[] toByteArray():以字节数组的形式返回输出流中的当前内容。
5、文件输入流
FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。
在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。
FileInputStream 常用的构造方法主要有如下两种重载形式。 FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 FileInputStream(String name):通过打开一个到实际文件的链接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。
FileInputStream: 文件字节输入流
FileInputStream从文件系统中的文件获取输入字节。 什么文件可用取决于主机环境。
FileInputStream用于读取诸如图像数据的原始字节流。 要阅读字符流,请考虑使用FileReader 。
构造方法
FileInputStream(File file)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(FileDescriptor fdObj)
通过使用文件描述符 fdObj创建 FileInputStream ,该文件描述符表示与文件系统中的实际文件的现有连接。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
6、文件输出流
FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。
FileOutputStream 类的构造方法主要有如下 4 种重载形式。 FileOutputStream(File file):创建一个文件输出流,参数 file 指定目标文件。 FileOutputStream(File file,boolean append):创建一个文件输出流,参数 file 指定目标文件,append 指定是否将数据添加到目标文件的内容末尾,如果为 true,则在末尾添加;如果为 false,则覆盖原有内容;其默认值为 false。 FileOutputStream(String name):创建一个文件输出流,参数 name 指定目标文件的文件路径信息。 FileOutputStream(String name,boolean append):创建一个文件输出流,参数 name 和 append 的含义同上。
注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。
对文件输出流有如下四点说明: 1、在 FileOutputStream 类的构造方法中指定目标文件时,目标文件可以不存在。 2、目标文件的名称可以是任意的,例如 D:\abc、D:\abc.de 和 D:\abc.de.fg 等都可以,可以使用记事本等工具打开并浏览这些文件中的内容。 3、目标文件所在目录必须存在,否则会拋出 java.io.FileNotFoundException 异常。 4、目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹,那么就不能使用 Java 作为文件名,即不能使用 D:\Java,否则抛出 java.io.FileNotFoundException 异常。
文件字节输出流 FileOutputStream
文件输出流是用于将数据写入File或FileDescriptor的输出流。
文件是否可用或可能被创建取决于底层平台。 特别是某些平台允许一次只能打开一个文件来写入一个
FileOutputStream (或其他文件写入对象)。 在这种情况下,如果所涉及的文件已经打开,
则此类中的构造函数将失败。
FileOutputStream用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter 。
构造函数:
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(FileDescriptor fdObj)
创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
FileOutputStream(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件。
参数
file - 要打开的文件进行写入。
append - 如果是 true ,则字节将写入文件的末尾而不是开头
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean append)
创建文件输出流以指定的名称写入文件。
参数
file - 要打开的文件进行写入。
append - 如果是 true ,则字节将写入文件的末尾而不是开头
七、字符流的使用
尽管 Java 中字节流的功能十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。
1、字符输入流
Reader 类的常用子类如下。
CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。
StringReader 类:将字符串转换为字符输入流,从中读取字符。
BufferedReader 类:为其他字符输入流提供读缓冲区。
PipedReader 类:连接到一个 PipedWriter。
InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。
2、字符输出流
与 Reader 类相反Writer 类是所有字符输出流的父类,该类中有许多方法,这些方法对继承该类的所有子类都是有效的。
Writer 类的常用子类如下。
CharArrayWriter 类:向内存缓冲区的字符数组写数据。
StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。
BufferedWriter 类:为其他字符输出流提供写缓冲区。
PipedWriter 类:连接到一个 PipedReader。
OutputStreamReader 类:将字节输出流转换为字符输出流,可以指定字符编码。
3、字符文件输入流
为了读取方便,Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。
1、FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。
2、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。
在用该类的构造方法创建 FileReader 读取对象时,默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FilelnputStream 上构造一个 InputStreamReader。
注意:在创建 FileReader 对象时可能会引发一个 FileNotFoundException 异常,因此需要使用 try catch 语句捕获该异常。
字符流和字节流的操作步骤相同,都是首先创建输入流或输出流对象,即建立连接管道,建立完成后进行读或写操作,最后关闭输入/输出流通道。
4、字符文件输出流
Java 提供了写入字符文件的便捷类——FileWriter,该类的构造方法有如下 4 种重载形式。
1、FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。其中,file 表示要写入数据的 File 对象。
2、FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。
3、FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。
4、FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处。
在创建 FileWriter 对象时,默认字符编码和默认字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter 对象。
FileWriter 类的创建不依赖于文件存在与否,如果关联文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件,将引发一个 IOException 异常。
注意:在创建 FileWriter 对象时可能会引发 IOException 或 SecurityException 异常,因此需要使用 try catch 语句捕获该异常。
5、字符缓冲区输入流
BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,这样就可以提高数据的读取效率。
BufferedReader 类的构造方法有如下两种重载形式。
1、BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流。
2、BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。
除了可以为字符输入流提供缓冲区以外,BufferedReader 还提供了 `readLine()` 方法,该方法返回包含该行内容的字符串,但该字符串中不包含任何终止符,如果已到达流末尾,则返回 null。readLine() 方法表示每次读取一行文本内容,当遇到换行(\n)、回车(\r)或回车后直接跟着换行标记符即可认为某行已终止。
6、字符缓冲区输出流
BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。
BufferedWriter 类的构造方法有如下两种重载形式。
1、BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流。
2、BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。
该类除了可以给字符输出流提供缓冲区之外,还提供了一个新的方法 newLine(),该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行(\n)符。
提示:BufferedWriter 类的使用与 FileWriter 类相同。
八、转换流
正常情况下,字节流可以对所有的数据进行操作,但是有些时候在处理一些文本时我们要用到字符流,比如,查看文本的中文时就是需要采用字符流更为方便。所以 Java IO 流中提供了两种用于将字节流转换为字符流的转换流。
InputStreamReader 用于将字节输入流转换为字符输入流,其中 OutputStreamWriter 用于将字节输出流转换为字符输出流。使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。
字符流(高级流)/转换流
作用:专用于与文本的读写
对应的构造方法:
OutputStreamWriter(OutputStream out) 该构造方法会使用相同默认的字符编码 OutputStreamWriter(OutputStream out, String charsetName) 该构造方法可以指定字符集
OutputStreamWriter是从字符流到字节流的桥梁:使用指定的charset将写入的字符编码为字节。
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每次调用write()方法都会使编码转换器在给定的字符上被调用。 所得到的字节在写入底层输出流之前累
积在缓冲区中。 请注意,传递给write()方法的字符不会缓冲。
为了最大的效率,请考虑在BufferedWriter中包装一个OutputStreamWriter,以避免频繁的转换器调用。
高级流(字符流)\转换流
InputStreamReader是从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。
为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。
构造方法 Constructor 描述
InputStreamReader(InputStream in)
创建一个使用默认字符集的InputStreamReader。
InputStreamReader(InputStream in, String charsetName)
创建一个使用命名字符集的InputStreamReader。
InputStreamReader(InputStream in, Charset cs)
创建一个使用给定字符集的InputStreamReader。
InputStreamReader(InputStream in, CharsetDecoder dec)
创建一个使用给定字符集解码器的InputStreamReader。
为了最大的效率,请考虑在BufferedReader中包装一个InputStreamReader。
通过字节缓冲流的方式将给定的内容写入到文件当中去
public class BufferedOutputStream extends FilterOutputStream
该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,
而不必为写入的每个字节导致底层系统的调用。
构造方法摘要
构造方法 Constructor 描述
BufferedOutputStream(OutputStream out)
创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
BufferedOutputStream(OutputStream out, int size)
创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
public class BufferedInputStream extends FilterInputStreamA
BufferedInputStream为另一个输入流添加了功能,即缓冲输入并支持mark和reset方法的功能。
创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区
将根据需要从所包含的输入流中重新填充,一次很多字节。 mark操作会记住输入流中的一个点,并且
reset操作会导致从最近的mark操作读取的所有字节在从包含的输入流中取出新字节之前重新读取。
构造方法 Constructor 描述
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream并保存其参数,输入流 in供以后使用。
BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream ,并保存其参数,输入流 in供以后使用。