目录
3.1 对象序列化——对象字节输出流ObjectOutputStream
3.2 对象反序列化——对象字节输入流ObjectInputStream
不可变集合、Stream、异常
1 不可变集合(了解)
- 不可变集合,就是不可被修改的集合(静态),这个集合创建后不能添加,不能删除,不能修改
- 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变,否则报错
- 在List、Set、Map接口中,都存在of方法(JDK9以后),可以创建一个不可变的集合:
方法名称 | 说明 |
static List of(E…elements) | 创建一个具有指定元素的List集合对象 |
static Set of(E…elements) | 创建一个具有指定元素的Set集合对象 |
static <K , V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象 |
List<Double> lists = List.of(569.5, 700.5, 523.0, 570.5);//例子
2 Stream流
2.1 Stream流概念
- 目的:结合Lambda表达式,用于简化集合和数组操作的API
- Stream流思想:
- 先得到集合或者数组的Stream流(就是一根传送带)支持链式编程
- 把元素放上去
- 然后就用这个Stream流简化的API来方便的操作元素
//案例:从names集合中筛选出姓张,名称为三个字的人的姓名
names.stream().filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(s -> System.out.println(s));
- Stream流的三类方法:
- 获取Stream流:创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法(常用API):流水线上的操作,一次操作完毕之后,还可以继续进行其他操作
- 终结方法:一个Stream流只能有一个终结方法,是流水线上的最后一个操作
2.2 Stream流的获取方法
- 集合获取Stream流的方式:Collection集合使用Collection接口中的默认方法stream()生成流
default Stream<E> stream() 获取当前集合对象的Stream流
Map集合
Map<String, Integer> maps = new HashMap<>();
// 键流
Stream<String> keyStream = maps.keySet().stream();
// 值流
Stream<Integer> valueStream = maps.values().stream();
// 键值对流(拿整体)
Stream<Map.Entry<String,Integer>> keyAndValueStream = maps.entrySet().stream();
- 数组获取Stream流的方式
public static <T> Stream<T> stream(T[] array) 获取当前数组的Stream流
public static<T> Stream<T> of(T... values) 获取当前数组/可变数据的Stream流
2.3 Stream流中间方法(常用API)
- 非终结的中间方法会返回新的流,支持链式编程
名称 | 说明 |
Stream filter(Predicate<? super T> predicate) | 用于对流中的数据进行过滤。 |
Stream limit(long maxSize) | 获取前几个元素 |
Stream skip(long n) | 跳过前几个元素 |
Stream distinct() | 去除流中重复的元素。依赖(hashCode和equals方法) |
static Stream concat(Stream a, Stream b) | 合并a和b两个流为一个流 |
2.4 Stream流终结方法
- 终结操作方法,调用完成后流就无法继续使用了,原因是不会返回Stream了
名称 | 说明 |
void forEach(Consumer action) | 对此流的每个元素执行遍历操作 |
long count() | 返回此流中的元素数 |
2.5 收集Stream流
- 收集Stream流的含义:就是把Stream流操作后的结果数据转回到集合或者数组中去
- Stream流:方便操作集合/数组的手段
- 集合/数组:才是开发中的目的
- 收集方法:Collect方法的参数为Collectors工具类提供的具体收集方式
R collect (Collector collector) 开始收集Stream流,指定收集器
名称 | 说明 |
public static Collector toList() | 把元素收集到List集合中 |
public static Collector toSet() | 把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper , Function valueMapper) | 把元素收集到Map集合中 |
//举例
Stream<String> s1 = list.stream().filter(s -> s.startsWith("张"));
List<String> zhangList = s1.collect(Collectors.toList());
// Collectors.toList()方法将Stream流收集为List集合
注意:流收集完毕后就终结了,不能继续使用该流!!
3 异常
- 异常是程序在“编译”或者“执行”的过程中可能出现的问题,比如:数组索引越界、空指针异常、 日期格式化异常等
- 注意:语法错误不算在异常体系中
- 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止。研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性
3.1 异常体系与分类
- Error:系统级别问题、JVM退出等,代码无法控制。
- Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
- RuntimeException及其子类:运行时异常,编译阶段不会报错。(空指针异常,数组索引越界异常)
- 除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译 (日期格式化异常)
3.1.1 运行时异常(面试常考)
- 直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误,一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误
- 常见运行时异常:
- 数组索引越界异常: ArrayIndexOutOfBoundsException
- 空指针异常 : NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错
- 数学操作异常:ArithmeticException
- 类型转换异常:ClassCastException
- 数字转换异常: NumberFormatException
3.1.2 编译时异常
● 不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过
● 作用:担心程序员的技术不行,在编译阶段就爆出一个错误,目的在于善意的提醒不要出错,编译时异常是可遇不可求
//日期格式化异常实例
String date = "2015-01-12 10:23:21";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);//日期解析异常:ParseException
System.out.println(d);
3.2 异常处理
3.2.1 默认异常处理机制
- 默认会在出现异常的代码那里自动的创建一个异常对象
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机
- 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
- 直接从当前执行的异常点杀死当前程序
- 程序已经死亡,后续代码没有机会执行了,所以该处理机制并不好
3.2.2 编译时异常处理机制(重要)
3.3 自定义异常
- 可以使用异常的机制管理业务问题,如提醒程序员注意,同时一旦出现bug,可以用异常的形式清晰的指出出错的地方
3.2.1 自定义编译时异常
- 定义一个异常类继承Exception
- 重写构造器
- 在出现异常的地方用throw new 自定义对象抛出
3.2.2 自定义运行时异常
- 定义一个异常类继承RuntimeException
- 重写构造器
- 在出现异常的地方用throw new 自定义对象抛出
File、IO流
1 File
- 在内存中存储的数据是用来处理、修改、运算的,不能长久保存
- 磁盘可以永久存储数据,磁盘中数据的形式就是文件,文件是数据的载体
- File类可以定位文件:进行删除、获取文本本身信息等操作,但是不能读写文件内容!!
1.1 File类概述
- File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)
- File类提供了诸如:定位文件,获取文件本身的信息、删除文件、创建文件(文件夹)等功能
- File类创建对象的方法:
- File对象可以创建文件或文件夹
- File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的
方法名称 | 说明 |
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
方法名称 | 说明 |
public File(String pathname) | 根据文件路径创建文件对象 |
public File(String parent, String child) | 从父路径名字符串和子路径名字符串创建文件对象 |
public File(File parent, String child) | 根据父路径对应文件对象和子路径名字符串创建文件对象 |
- 绝对路径和相对路径:(重要)
File file1 = new File(“D:\\itheima\\a.txt”); //绝对路径:从盘符开始
- 动态获取分隔符(跨系统文件操作)
File f = new File("D:" + File.separator+"resources"+ File.separator +"xueshan.jpeg");
1.2 File常用API
1.2.1 判断文件类型、获取文件信息
方法名称 | 说明 |
public boolean isDirectory() | 测试此抽象路径名表示的File是否为文件夹 |
public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
public boolean exists() | 测试此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转换为路径名字符串 |
public String getName() | 返回由此抽象路径名表示的文件或文件夹的名称 |
public long lastModified() | 返回文件最后修改的时间毫秒值 |
1.2.2 创建文件、删除文件功能
方法名称 | 说明 |
public boolean createNewFile() | 创建一个新的空的文件(几乎不用) |
public boolean mkdir() | 只能创建一级文件夹 |
public boolean mkdirs() | 可以创建多级文件夹 |
public boolean delete() | 删除由此抽象路径名表示的文件或空文件夹 |
- delete方法默认只能删除文件和空文件夹
- delete方法直接删除不走回收站
1.2.3 遍历功能
方法名称 | 说明 |
public String[] list() | 获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。 |
public File[] listFiles()(常用) | 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点) |
listFiles方法注意事项:
- 当调用者不存在时,返回null
- 当调用者是一个文件时,返回null
- 当调用者是一个空文件夹时,返回一个长度为0的数组
- 当调用者是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回
- 当调用者是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏内容
- 当调用者是一个需要权限才能进入的文件夹时,返回null
- 递归解决File文件搜索
2 IO流
2.1 字符集
- 计算机底层不可以直接存储字符的。计算机中底层只能存储二进制(0、1)
- 二进制是可以转换成十进制的,计算机底层可以表示十进制编号。计算机可以给人类字符进行编号存储,这套编号规则就是字符集
2.1.1 ASCII字符集
- ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号
- ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于英文,数字来说是够用的
2.1.2 GBK
- window系统默认的码表。兼容ASCII码表,也包含了几万个汉字,并支持繁体汉字以及部分日韩文字
- 注意:GBK是中国的码表,一个中文以两个字节的形式存储,但不包含世界上所有国家的文字
2.1.3 Unicode码表
- unicode(又称统一码、万国码、单一码)是计算机科学领域里的一项业界字符编码标准,容纳世界上大多数国家的常见文字和符号
- 由于Unicode会先通过UTF-8,UTF-16,以及 UTF-32的编码成二进制后再存储到计算机,其中最为常见的就是UTF-8
- Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储,也要兼容ASCII编码表
- 技术人员都应该使用UTF-8的字符集编码,编码前和编码后的字符集需要一致,否则会出现中文乱码
2.1.4 汉字存储和展示过程解析
2.1.5 字符集的编码、解码操作
- String编码:
方法名称 | 说明 |
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 |
- String解码:
构造器 | 说明 |
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来构造新的 String |
2.2 IO流概述
- IO流也称为输入、输出流,就是用来读写数据的
- I表示intput,是数据从硬盘文件读入到内存的过程,称之输入,负责读
- O表示output,是内存程序的数据从内存到写出到硬盘文件的过程,称之输出,负责写
2.3 IO流体系与分类
- 流的四大类:
- 字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流
- 字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流
- 字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流
- 字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流
- IO流体系:
2.4 字节流
2.4.1 字节输入流FileInputStream
- 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去
- 构造器:
构造器 | 说明 |
public FileInputStream(File file) | 创建字节输入流管道与源文件对象接通 |
public FileInputStream(String pathname) | 创建字节输入流管道与源文件路径接通 |
- 方法
方法名称 | 说明 |
public int read() | 每次读取一个字节返回,如果字节已经没有可读的返回-1 |
public int read(byte[] buffer) | 每次读取一个字节数组返回,如果字节已经没有可读的返回-1 |
- 每次读取一个字节:性能较慢,读取中文字符输出无法避免乱码问题
- 每次读取一个字节数组:性能得到了提升,但读取中文字符输出仍无法避免乱码问题
- 读取优化——一次读完全部字节,避免中文乱码,JDK9之后才出现
方法名称 | 说明 |
public byte[] readAllBytes() throws IOException | 直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回 |
- 直接自定义一个数组一次读完全部字节——可能会出现溢出
2.4.2 字节输出流FileOutputStream
- 作用:以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流
- 构造器:
构造器 | 说明 |
public FileOutputStream(File file) | 创建字节输出流管道与源文件对象接通,清空原数据 |
public FileOutputStream(File file,boolean append) | 创建字节输出流管道与源文件对象接通,可追加数据 |
public FileOutputStream(String filepath) | 创建字节输出流管道与源文件路径接通,清空原数据 |
public FileOutputStream(String filepath,boolean append) | 创建字节输出流管道与源文件路径接通,可追加数据 |
- 方法:
方法名称 | 说明 |
public void write(int a) | 写一个字节出去 |
public void write(byte[] buffer) | 写一个字节数组出去 |
public void write(byte[] buffer , int pos , int len) | 写一个字节数组的一部分出去 |
flush() | 刷新流,还可以继续写数据,刷新后数据生效 |
close() | 关闭流,释放资源,但在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
tips: 输出流实现写数据换行:os.write(“\r\n”.getBytes())
2.4.3 资源释放优化
- 代码中间出现问题时,可能会导致代码最后的close()方法不被执行,资源无法释放
- try-catch-finally释放资源:
- finally:在异常处理时提供finally块来执行所有清除操作,比如说IO流中的释放资源
- 特点:被finally控制的语句最终一定会执行,除非JVM退出
- 异常处理标准格式:try….catch…finally
try {
FileOutputStream fos = new FileOutputStream("a.txt");
fos.write(97);
} catch (IOException e) {
e.printStackTrace();
} finally{
}
- try-catch-resource释放资源:
- 自动释放资源、代码简洁
- 格式:JDK7中的
try(定义流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
} //资源用完最终自动释放
2.5 字符流
- 字节流读取中文输出会乱码或者内存溢出,字符流不会出现中文乱码,最小单位是按照单个字符读取的
2.5.1 字符输入流FileReader
- 作用:以内存为基准,把磁盘文件中的数据以字符的形式读取到内存中去
- 构造器:
构造器 | 说明 |
public FileReader(File file) | 创建字符输入流管道与源文件对象接通 |
public FileReader(String pathname) | 创建字符输入流管道与源文件路径接通 |
- 方法:
方法名称 | 说明 |
public int read() | 每次读取一个字符返回,如果字符已经没有可读的返回-1 |
public int read(char[] buffer) | 每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1 |
2.5.2 字符输出流FileWriter
- 作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流
- 构造器:
构造器 | 说明 |
public FileWriter(File file) | 创建字符输出流管道与源文件对象接通 |
public FileWriter(File file,boolean append) | 创建字符输出流管道与源文件对象接通,可追加数据 |
public FileWriter(String filepath) | 创建字符输出流管道与源文件路径接通 |
public FileWriter(String filepath,boolean append) | 创建字符输出流管道与源文件路径接通,可追加数据 |
- 方法:
方法名称 | 说明 |
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
void write(int c) | 写一个字符 |
flush() | 刷新流,还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
2.6 字节流与字符流的使用场景
- 字节流适合做一切文件数据的拷贝(音视频,文本)
- 字节流不适合读取中文内容输出
- 字符流适合做文本文件的操作(读,写)
IO流进阶
1 缓冲流
1.1 概述
1.2 字节缓冲流
1.2.1 字节缓冲流性能优化原理
- 字节缓冲输入流自带了8KB缓冲池,以后我们直接从缓冲池读取数据,所以性能较好
- 字节缓冲输出流自带了8KB缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了
- 建议使用字节缓冲输入流、字节缓冲输出流,结合字节数组的方式,目前来看是性能最优的组合
1.2.2 字节缓冲流的使用
- 继承自字节流,可以使用字节流所有方法
- 字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能,读写功能上并无变化
- 字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能,读写功能上并无变化
- 构造器:
构造器 | 说明 |
public BufferedInputStream(InputStream is) | 可以把低级的字节输入流包装成一个高级的缓冲字节输入流管道,从而提高字节输入流读数据的性能 |
public BufferedOutputStream(OutputStream os) | 可以把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
1.3 字符缓冲流
1.3.1 字符缓冲输入流BufferedReader
- 作用:提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能
- 构造器:
构造器 | 说明 |
public BufferedReader(Reader r) | 可以把低级的字符输入流包装成一个高级的缓冲字符输入流管道,从而提高字符输入流读数据的性能 |
- 新增方法:
方法 | 说明 |
public String readLine() | 读取一行数据返回,如果读取没有完毕,无行可读返回null |
1.3.2 字符缓冲输出流BufferedWriter
- 作用:提高字符输出流写取数据的性能,除此之外多了换行功能
- 构造器:
构造器 | 说明 |
public BufferedWriter(Writer w) | 可以把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
- 新增方法:
方法 | 说明 |
public void newLine() | 换行操作 |
2 字符转换流
- 使用字符流读取中文没有乱码,因为代码编码和文件编码都是UTF-8,如果代码编码和文件编码不一致仍然会乱码
- 字符转换流可以解决字符流读取不同编码乱码的问题
2.1 字符输入转换流InputStreamReader
- 可以把原始的字节流按照指定编码转换成字符输入流 (CSDN二面)
- 构造器:
构造器 | 说明 |
public InputStreamReader(InputStream is) | 可以把原始的字节流按照代码默认编码转换成字符输入流。几乎不用,与默认的FileReader一样。 |
public InputStreamReader(InputStream is ,String charset) | 可以把原始的字节流按照指定编码转换成字符输入流,这样字符流中的字符就不乱码了(重点) |
2.2 字符输入转换流OutputStreamWriter
- 可以把字节输出流按照指定编码转换成字符输出流
- 构造器:
构造器 | 说明 |
public OutputStreamWriter(OutputStream os) | 可以把原始的字节输出流按照代码默认编码转换成字符输出流。几乎不用。 |
public OutputStreamWriter(OutputStream os,String charset) | 可以把原始的字节输出流按照指定编码转换成字符输出流(重点) |
3 对象字节流(序列化对象)
3.1 对象序列化——对象字节输出流ObjectOutputStream
- 作用:以内存为基准,把内存中的对象存储到磁盘文件中去,称为对象序列化
- 序列化对象的要求:对象的实体类必须implements Serializable序列化接口
- 构造器:
构造器 | 说明 |
public ObjectOutputStream(OutputStream out) | 把低级字节输出流包装成高级的对象字节输出流 |
- 方法:
方法名称 | 说明 |
public final void writeObject(Object obj) | 把对象写出去到对象序列化流的文件中去 |
3.2 对象反序列化——对象字节输入流ObjectInputStream
- 作用:以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象,称为对象反序列化
- 构造器:
构造器 | 说明 |
public ObjectInputStream(InputStream out) | 把低级字节输如流包装成高级的对象字节输入流 |
- 方法:
方法名称 | 说明 |
public Object readObject() | 把存储到磁盘文件中去的对象数据恢复成内存中的对象返回 |
4 打印流
- 作用:打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类,写数据时最终选择使用打印流
- 可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true
4.1 PrintStream和PrintWriter
4.1.1 PrintStream(属于字节输出流)
- 构造器:
构造器 | 说明 |
public PrintStream(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintStream(File f) | 打印流直接通向文件对象 |
public PrintStream(String filepath) | 打印流直接通向文件路径 |
- 方法:
方法 | 说明 |
public void print(Xxx xx) | 打印任意类型的数据出去 |
4.1.2 PrintWriter(属于字符输出流)
- 构造器:
构造器 | 说明 |
public PrintWriter(OutputStream os) | 打印流直接通向字节输出流管道 |
public PrintWriter (Writer w) | 打印流直接通向字符输出流管道 |
public PrintWriter (File f) | 打印流直接通向文件对象 |
public PrintWriter (String filepath) | 打印流直接通向文件路径 |
- 方法:
方法 | 说明 |
public void print(Xxx xx) | 打印任意类型的数据出去 |
4.2 应用:输出语句的重定向
- 属于打印流的一种应用,可以把输出语句的打印位置改到文件
PrintStream ps = new PrintStream("文件地址")
System.setOut(ps);
5 commons-io框架
- 右键lib中的jar包Add as Library,或用maven自动导入依赖
- commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率。
- commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
- 主要方法:
方法名 | 说明 |
String readFileToString(File file, String encoding) | 读取文件中的数据, 返回字符串 |
void copyFile(File srcFile, File destFile) | 复制文件 |
void copyDirectoryToDirectory(File srcDir, File destDir) | 复制文件夹 |