IO流
异常
简介
-
概述:java中把程序出现的所有不正常的情况 统称为 异常
-
异常的体系图:
-
顶层类:Throwable类
-
Error:
- 概述: 表示错误,一般指的是非代码级别的问题,大多数问题你也处理不了.
- 举例:服务器宕机,数据库崩溃。
-
Exception:
-
概述:这个才是我们常说的异常,一般都是代码级别的问题,需要我们(程序员)处理
-
举例:
-
NullPointerException, StringIndexOutOfBoundsException, ConcurrentModificationException, ClassCastException, ArithmeticException, ParseException, .....
-
-
分类:
- 编译期异常:
- 指的是在代码编译期间就会发生的异常,非RuntimeException及其子类都是 编译期异常
- 运行时异常:
- 指的是在代码运行期间才会发生的异常,编译期间没问题,RuntimeException及其子类都是 运行时异常
- 编译期异常:
-
-
-
package com.ithiema.api.demo;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo01 {
public static void main(String[] args) {
System.out.println(10 / 0); //运行期异常
System.out.println("-----------");
SimpleDateFormat sdf = new SimpleDateFormat("yyy/MM/dd");
Date date = sdf.parse("2021/05/21"); //编译期异常
}
}
JVM默认处理异常的方式
- 把异常的类型,异常出现的原因以及异常出现的位置打印到控制台上.
- 并终止程序的执行.
自己处理异常
方式一:捕获异常
格式:
try{
//可能出问题的代码
} catch(Exception e) { //异常类型 对象名
//走这里, 说明程序出现异常了, 所以这里写的基本上都是解决方案.
e.printStackTrace(); //该方法会将异常的类型, 描述信息及位置打印到控制台上.
} finally {
//这里的代码正常情况下永远都会执行, 一般是用来释放资源的.
}
特点:处理完之后, 程序会继续运行.
package com.itheima.demo01_exception;
//案例: 演示异常的两种处理方式.
/*
方式一: try.catch.finally语句
格式:
try{
//可能出问题的代码
} catch(Exception e) {
//走这里, 说明程序出现异常了, 所以这里写的基本上都是解决方案.
e.printStackTrace(); //该方法会将异常的类型, 描述信息及位置打印到控制台上.
} finally {
//这里的代码正常情况下永远都会执行, 一般是用来释放资源的.
}
特点:
处理完之后, 代码会继续往下执行.
*/
public class Demo02 {
public static void main(String[] args) {
try{
int a = 10;
int b = 2;
System.out.println(a / b);
} catch (Exception e) {
e.printStackTrace();//该方法会将异常的类型, 描述信息及位置打印到控制台上.
}
System.out.println("看看我执行了吗?");
}
}
方式二:声明抛出异常
throws,声明抛出异常,把异常交给调用者 处理
格式:
在定义方法的小括号后 throws 异常类型1,异常类型2...
特点:处理完之后,程序会终止执行.
package com.ithiema.api.demo;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo02 {
public static void main(String[] args) /*throws Exception*/{
try {
show();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("看看我执行了吗");
}
public static void show() throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat();
Date date = sdf.parse("2021/2/3/"); //编译期异常
}
}
File类
简介
-
概述:
- 它是java.io包下的类,用之前需要先导包.
- 它表示文件或者文件夹(目录),以 路径的形式来描述.
-
路径分类:
- 绝对路径:
- 以盘符开头,固定的,写"死"的路径.
- 例如: d:/abc/1.txt
- 相对路径:
- 指的是相对于某个路劲来讲,在java中,默认都是以 当前的项目路径来做参考路径的.
- 例如: 你写了 1.txt, 其实是: 当前项目路径/1.txt
- 举例:
- 1.txt -> D:\workspace\bigdata_24_sz\JavaSE\1.txt
- 绝对路径:
构造方法:
-
public File(String path); 创建File对象, 封装: 字符串形式的路径.
-
public File(String parent, String child); 创建File对象, 封装: 字符串形式的父目录 和 子文件/子目录
-
public File(File parent, String child); 创建File对象, 封装: 父目录对象 和 字符串形式的子文件/子目录
细节:
- 上述三种形式创建的文件(夹), 结果都是一样的.
- 之所以提供这么多的创建File对象的形式, 就是为了满足用户灵活多变的需求.
- 盘符不区分大小写, 写大写或者写小写都可以.
- 因为 \ 在Java中表示转移符, 所以如果写 右斜线需要写两个, 即: \\ 。左斜线写一个 / 就行了。
案例: File类入门
package com.itheima.demo02_file;
import java.io.File;
//案例: File类入门.
public class Demo01 {
public static void main(String[] args) {
//需求: 描述 D:\\abc\\1.txt
//1. 演示 public File(String pathName); 根据传入的路径, 获取对应的File对象.
File file1 = new File("d:\\abc\\1.txt");
File file2 = new File("d:/abc/1.txt");
System.out.println("file1: " + file1);
System.out.println("file2: " + file2);
System.out.println("-----------------");
//2. 演示 public File(String parent, String child); 根据传入的父目录和子文件名获取其对应的File对象.
File file3 = new File("d:/abc/","1.txt");
System.out.println("file3: " + file3);
System.out.println("-----------------");
//3. 演示 public File(File parent, String child); 根据传入的父目录对象和子文件名获取其对应的File对象.
File file4 = new File("d:/abc/");
File file5 = new File(file4, "1.txt");
System.out.println("file5: " + file5);
}
}
成员方法之-创建功能
-
public boolean createNewFile(); 创建文件,文件不存在就创建返回true,否则返回false.
-
public boolean mkdir(); 创建目录, 只能创建单级目录. 不存在就创建返回true, 否则返回false.
-
public boolean mkdirs(); 创建目录, 可以创建单级目录, 也可以创建多级. 不存在就创建返回true, 否则返回false.
/(正)斜线,\ 反斜线
案例: 演示File类的创建功能.
package com.itheima.demo02_file;
import java.io.File;
import java.io.IOException;
//案例: 演示File类的创建功能.
public class Demo02 {
public static void main(String[] args) throws IOException {
//需求1: 在D:\ceshi文件夹下创建1.txt文件.
File file1 = new File("d:/ceshi/1.txt");
System.out.println(file1.createNewFile());
System.out.println("-----------------------");
//需求2: 在D:\ceshi文件夹下创建aa\bb文件夹
File file2 = new File("d:/ceshi/aa/bb");
//System.out.println(file2.mkdir()); 结果为false
System.out.println(file2.mkdirs()); //结果为true
System.out.println("-----------------------");
}
}
成员方法之-判断功能
- public boolean isDirectory() 是否是目录, 存在的且是目录路径就返回true, 否则:false
- public boolean isFile() 是否是文件, 存在的且是文件路径就返回true, 否则:false
- public boolean exists() 测试此抽象路径名表示的File是否存在
案例: 演示File类的判断功能
package com.itheima.demo02_file;
import java.io.File;
//案例: 演示File类的判断
public class Demo03 {
public static void main(String[] args) {
//1. 创建File对象.
//演示 public boolean exists(); //判断指定的文件(夹)是否存在.
File file1 = new File("d:/ceshi/1.txt");
System.out.println(file1.exists());
System.out.println("-------------------");
//2. 演示 public boolean isFile(); //判断给定的路径是否是文件.
File file2 = new File("d:/ceshi/1.txt");
System.out.println(file1.isFile());
System.out.println("-------------------");
//3. 演示public boolean isDirectory(); //判断给定的路径是否是文件夹.
File file3 = new File("d:/ceshi");
//System.out.println(file1.isDirectory()); //false
System.out.println(file3.isDirectory()); //true
}
}
成员方法之-获取功能
- public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串
- public String getPath() 将此抽象路径名转换为路径名字符串
- public String getName() 返回由此抽象路径名表示的文件或文件夹的名称
- public String[] list() 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
- public File[] listFiles() 返回此抽象路径名表示的目录中的文件和目录的File对象数组
案例: 演示File类的获取功能
package com.itheima.demo02_file;
import java.io.File;
//案例: 演示File类的获取功能
public class Demo04 {
public static void main(String[] args) {
//1. 获取getAbsolutePath(); //获取绝对路径.
File file1 = new File("day12/data/1.txt"); //相对路径.
System.out.println(file1.getAbsolutePath()); //绝对路径.
System.out.println(file1.getPath()); //相对路径, 一般是相对于当前项目的路径来讲的.
System.out.println("-------------------");
//2. public String getName(); //获取文件(夹)的名字.
System.out.println(file1.getName());
System.out.println("-------------------");
//3. public String[] list(); //获取指定的目录下所有的 文件(夹) 的字符串数组形式.
//理解即可.
File file2 = new File("d:/ceshi");
String[] fileNames = file2.list();
for (String fileName : fileNames) {
System.out.println(fileName); //1.txt aa
}
System.out.println("-------------------");
//4. public File[] listFiles(); //获取指定的目录下所有的 文件(夹) 的File对象数组形式.
File[] listFiles = file2.listFiles();
for (File listFile : listFiles) {
System.out.println(listFile); //d:\ceshi\1.txt d:\ceshi\aa
}
}
}
成员方法之-删除功能
- public boolean delete(); 成功true,false:失败
细节:
1.java的删除不走回收站.
2.要删除的文件夹必须是空文件夹.
案例: 演示File类的删除功能
package com.itheima.demo02_file;
import java.io.File;
//案例: 演示File类的删除功能
public class Demo05 {
public static void main(String[] args) {
//需求1: 删除 d:/ceshi/aa 文件夹
File file1 = new File("d:/ceshi/aa");
System.out.println(file1.delete());
//需求2: 删除d:/ceshi/1.txt文件
File file1 = new File("d:/ceshi/1.txt");
System.out.println(file1.delete());
//需求3: 删除d:/ceshi文件夹
File file1 = new File("d:/ceshi");
System.out.println(file1.delete());
}
}
IO流简介
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j7Avm0ey-1669232987297)(F:\BigData\java\图片\day12图片\02. IO流解释.png)]
简介:
-
概述:
- IO流也叫输入/输出流,主要是用来做数据传输的
- I:input(输入),O:output(输出)
-
作用:
- 文件复制.
- 文件上传.
- 文件下载.
分类:
-
按照流向分:
- 输入流: 读取数据
- 输出流: 写数据
-
按照操作分:
- 字节流:
- 特点:也叫万能流,能拷贝任意类型的文件,即:以字节为单位操作数据
- 分类:
- 字节输入流:
- 特点:以字节为单位读取数据, 顶层抽象类是: InputStream
- 常用子类:
- FileInputStream: 普通的字节输入流
- BufferedInputStream: 高效的字节输入流
- 字节输出流:
- 特点:以字节为单位写数据, 顶层抽象类是: OutputStream
- 常用子类:
- FileOutputStream: 普通的字节输出流
- BufferedOutputStream: 高效的字节输出流
- 字节输入流:
- 字符流:
- 特点:以字符为单位操作数据, 只能操作 纯文本文件, 操作其他文件(例如图片, 音频, 视频)可能会导致乱码.
- 分类:
- 字符输入流:
- 特点:以字符为单位读取数据, 顶层抽象类是: Reader
- 常用子类:
- FileReader: 普通的字符输入流
- BufferedReader: 高效的字符输入流
- 字符输出流:
- 特点:以字符为单位写数据, 顶层抽象类是: Writer
- 常用子类:
- FileWriter: 普通的字符输出流
- BufferedWriter: 高效的字符输出流
- 字符输入流:
- 字节流:
-
细节:
- 电脑的工作原理简单理解就是: 从磁盘(外存)加载数据到内存中, 处理之后在把数据写回到磁盘(外存)中
- 能用字符流优先考虑使用字符流, 字符流解决不了再考虑使用字节流
- IO流涉及到的单词: InputStream, OutputStream, Reader, Writer, File, Buffered
字节输出流
-
概述:
- 它的顶层抽象类是 OutputStream, 以字节为单位往文件中写数据,它不能new, 所以我们常用它的子类.
-
常用子类:
- FileOutputStream:普通的字节输出流,
- BufferedOutputStream:高效的字节输出流.
FileInputStream简介:
- 概述:
- 它是基本的字节输出流, 以字节为单位往数据中 写数据, 该流没有默认的缓冲区.
构造方法:
- public FileOutputStream(String pathName); 创建基本的字节输出流, 关联: 目的地文件(字符串形式的路径)
- public FileOutputStream(File pathName); 创建基本的字节输出流, 关联: 目的地文件(File对象形式)
- public FileOutputStream(String pathName, boolean append); true:追加, false: 覆盖(默认的)
- public FileOutputStream(File pathName, boolean append); true:追加, false: 覆盖(默认的)
成员方法:
- public void write(int c); 一次写一个字节.
- public void write(byte[] bys); 一次写一个字节数组.
- public void write(byte[] bys, int start, int len); 一次写一个字节数组的一部分, start:起始索引, len: 个数
- public void close(); 释放资源.
细节:
- 默认是覆盖文件中的数据, 即:如果重新往文件中写数据, 则会覆盖文件中之前的数据.
- 目的地文件如果不存在程序会自动创建, 前提: 该文件的父目录必须存在.
案例: 演示字节输出流入门
package com.itheima.demo03_outputstream;
import java.io.FileOutputStream;
import java.io.IOException;
//案例: 演示字节输出流入门.
public class Demo01 {
public static void main(String[] args) throws IOException {
//1.创建FileOutputStream对象, 关联指定的目的地文件.
//解释: 往哪个文件中写数据, 哪个文件就叫: 目的地文件.
//FileOutputStream fos = new FileOutputStream("day12/data/1.txt"); 默认是覆盖
FileOutputStream fos = new FileOutputStream("day12/data/1.txt", true); //传true表示追加
//FileOutputStream fos = new FileOutputStream(new File("day12/data/1.txt"));
//2.往文件中写入字符'a', 'b', 'c'.
//方式一: 一次写一个字节
fos.write(97);
fos.write(98);
fos.write(99);
//方式二: 一次写一个字节数组
byte[] bys = {65, 66, 67};
fos.write(bys);
//方式三: 一次写一个字节数组的一部分
byte[] bys = {65, 66, 67};
fos.write(bys,1, 1);
//3. 释放资源.
fos.close();
}
}
案例: 演示字节输出流写数据的两个小问题
细节:
- 如何往文件追加数据.
public FileOutputStream(String pathName, boolean append); true:追加, false: 覆盖(默认的)
public FileOutputStream(File pathName, boolean append); true:追加, false: 覆盖(默认的) - 如何换行.
转义符之 \r\n, 注意: 不同操作系统回车换行符不一样. windows: \r\n, Linux: \n, Mac: \r
- String类中有一个方法, 可以把 字符串转成其对应的 字节数组形式.
- public byte[] getBytes();
字符串转成其对应的 字节数组形式, 采用: 默认编码. - public byte[] getBytes(String charsetName);
字符串转成其对应的 字节数组形式, 采用: 指定编码.
- public byte[] getBytes();
package com.itheima.demo03_outputstream;
import java.io.FileOutputStream;
import java.io.IOException;
//案例: 演示字节输出流的两个小问题
/*
问题1: 换行符, \r\n
问题2: 追加数据, FIleOutputStream的构造方法, 传入true.
*/
public class Demo02 {
public static void main(String[] args) throws IOException {
//1.创建FileOutputStream对象, 关联指定的目的地文件.
FileOutputStream fos = new FileOutputStream("day12/data/2.txt", true); //目的地文件不存在, JVM会自动创建.
//2.往文件中写入10次hello, 每个hello占一行.
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
//换行符
fos.write("\r\n".getBytes()); //window操作系统: \r\n, linux: \n mac: \r
}
//3.往文件中追加一句话: 键盘敲烂, 月薪过万!
//字符串转字节数组 -> getBytes()
fos.write("键盘敲烂, 月薪过万!\r\n".getBytes());
//4. 关闭流, 释放资源
fos.close();
}
}
案例: 通过try.catch.finally语句解决IO流的异常问题
package com.itheima.demo03_outputstream;
import java.io.FileOutputStream;
import java.io.IOException;
//案例: 通过try.catch.finally语句解决IO流的异常问题.
public class Demo03 {
public static void main(String[] args) {
//1.创建FileOutputStream对象, 关联指定的目的地文件.
FileOutputStream fos = null;
try{
System.out.println(1/0);
//可能会出现问题的代码
fos = new FileOutputStream("day12/data/2.txt");
//2.往文件中写入10次hello, 每个hello占一行.
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
//换行符
fos.write("\r\n".getBytes());
}
}catch(Exception e) {
//出现问题的异常处理代码.
e.printStackTrace();
}finally{
//3. 关闭流, 释放资源
try {
if (fos != null) {
fos.close(); //快捷键: alt + enter
fos = null; //GC会优先回收null对象.
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节输入流
-
概述:它是一个抽象类, 用 InputStream表示, 可以以字节为单位读取文件中的数据.
-
常用子类:
- FileInputStream: 基本的字节输入流.
- BufferedInputStream: 高效的字节输入流.
FileInputStream类简介:
- 概述:它表示基本的字节输入流, 可以以字节为单位读取数据, 没有默认的缓冲区.
构造方法:
- public FileInputStream(String path); 创建字节输入流对象, 封装: 数据源文件(字符串形式的路径)
- public FileInputStream(File path); 创建字节输入流对象, 封装: 数据源文件(File形式的路径)
成员方法:
成员方法:
- public int read(); 一次读取一个字节, 并返回读取到的内容(即: 该字节的ASCII码值), 读不到返回-1
- public int read(byte[] bys);
一次读取一个字节数组, 并将读取到的内容存入到字节数组中, 返并回读取到的有效字节数, 读不到返回-1 - public void close(); 释放资源
IO流的核心操作步骤
1. 创建输入流对象, 关联: 数据源文件.
2. 创建输出流对象, 关联: 目的地文件.
3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
5. 将读取到的内容写入到目的地文件中.
6. 释放资源.
案例: 字节输入流入,一次读取一个字节
package com.itheima.demo04_inputstream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
//案例: 字节输入流入门.
public class Demo01 {
public static void main(String[] args) throws IOException {
//1. 创建字节输入流对象, 封装数据源文件. 数据源文件必须存在, 不然报错.
FileInputStream fis = new FileInputStream("day12/data/1.txt");
//2. 一次读一个字节
//2.1 定义变量, 记录读取到的内容.
int len = 0;
/*
(len = fis.read()) != -1 这行代码做了3件事:
1. 执行 fis.read(), 一次读取一个字节.
2. 执行 len = fis.read() 将读取到的字节的ASCII码值赋值给变量len
3. 执行 len != -1 如果len不等于-1, 说明读到内容了, 就进循环.
*/
while((len = fis.read()) != -1) {
System.out.println(len);
}
//3. 关流.
fis.close();
}
}
案例: 字节输入流,一次读取一个字节数组
package com.itheima.demo04_inputstream;
import java.io.FileInputStream;
import java.io.IOException;
//案例: 字节输入流 一次读取一个字节数组.
public class Demo02 {
public static void main(String[] args) throws IOException {
//1. 创建字节输入流对象, 封装数据源文件. 数据源文件必须存在, 不然报错.
FileInputStream fis = new FileInputStream("day12/data/1.txt");
//2. 一次读一个字节数组
//合并版
//2.1 定义字节数组, 记录读取到的数据.
byte[] bys = new byte[3]; //字节数组的长度: 一般是1024的整数倍. 初始值: 0, 0, 0
int len = 0; //记录读取到的有效字节数.
/*
(len = fis.read(bys)) != -1 这行代码做了3件事
1. 执行fis.read(bys), 一次读取一个字节数组, 并把读取到的内容存储到bys数组中.
2. 执行len = fis.read(bys) 将读取到的有效字节数赋值给变量len
3. 执行len != -1 如果len不等于-1, 说明读到内容了, 进循环.
*/
while((len = fis.read(bys)) != -1) {
System.out.println(new String(bys,0, len ));
}
//3. 关流.
fis.close();
//分解版
//第一次读取
int len1 = fis.read(bys); //bys: abc(97, 98, 99), len1 = 3
String s1 = new String(bys, 0, len1);
System.out.println(s1);
System.out.println(len1);
System.out.println("----------------------");
//第二次读取
int len2 = fis.read(bys); //bys: dbc(100, 98, 99), len2 = 1
String s2 = new String(bys, 0, len2);
System.out.println(s2);
System.out.println(len2);
}
}
案例: 普通字节流拷贝文件的两种方式
package com.ithiema.api.demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo13 {
public static void main(String[] args) throws IOException {
//拷贝文件方式一: 一次读写一个字节.
method01();
//拷贝文件方式二: 一次读写一个字节数组.
method02();
}
public static void method01() throws IOException {
//1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
//2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("javaSE/data/2.txt");
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = fis.read()) != -1) {
//5. 将读取到的内容写入到目的地文件中.
fos.write(len);
}
//6. 关流, 释放资源.
fis.close();
fos.close();
System.out.println("拷贝完成.");
}
public static void method02() throws IOException{
//1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
//2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("javaSE/data/2.txt");
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
byte[] bys = new byte[1024]; //一般是8KB
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = fis.read(bys)) != -1) {
//5. 将读取到的内容写入到目的地文件中.
fos.write(bys, 0, len);
}
//6. 关流, 释放资源.
fis.close();
fos.close();
System.out.println("拷贝完成.");
}
}
案例:图片加密
核心规则:
用位运算符之 位异或^, 它的规则是: 一个数字被同一个数字位异或两次, 值不变.
a ^ b ^ b = a
例如:
5 ^ 10 ^ 5 = 10
3 ^ 4 ^ 4 = 3
package com.itheima.demo05_exercise;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
//案例: 图片加密.
public class Demo02 {
public static void main(String[] args) throws IOException {
System.out.println(5 ^ 5 ^ 10); //10
System.out.println(2 ^ 3 ^ 3); //2
/*
需求:
1.对项目下的a.jpg图片进行加密, 获取到一个新的加密图片b.jpg.
2.对加密后的图片b.jpg进行解密, 获取解密后的图片c.jpg.
*/
// 1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("day12/data/b.jpg");
// 2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("day12/data/c.jpg");
// 3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
// 4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = fis.read()) != -1) {
// 5. 将读取到的内容写入到目的地文件中.
fos.write(len ^ 5); //这里的5就是密钥
}
// 6. 关流, 释放资源.
fis.close();
fos.close();
}
}
高效字节流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YHftTOad-1669232987299)(F:\BigData\java\图片\day12图片\01.字节流拷贝文件的图解.png)]
简介:
也叫: 字节高效流, 高效字节流, 缓冲字节流, 字节缓冲流.
- 概述:
- 高效的字节流指的是 BufferedInputStream 和 BufferedOutputStream, 它们自带有缓冲区.
- 单词解释:
- BufferedInputStream: 高效的字节输入流
- BufferedOutputStream: 高效的字节输出流
构造方法
- public BufferedInputStream(InputStream is);
- public BufferedOutputStream(OutputStream os);
问题: 为什么高效流需要封装基本流, 而不是普通的文件路径或者File对象呢?
答案: 因为高效流仅仅提供缓冲区的功能, 是对普通流的功能做扩展和延伸, 具体的读写操作还是由基本流完成的.
字节高效流入门详解
package com.ithiema.api.demo;
import java.io.*;
public class Demo15 {
public static void main(String[] args) throws IOException {
//1.通过字节缓冲输出流往文件中写一句话
//分解版
FileOutputStream fos = new FileOutputStream("javaSE/data/1.txt");
//高效版 基本流
BufferedOutputStream bos = new BufferedOutputStream(fos);
//合并版
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("javaSE/data/1.txt"));
bos.write("hello".getBytes());
bos.close();
//2.通过字节缓冲输入流读取文件中的内容
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("javaSE/data/1.txt"));
int len = 0;
while ((len = bis.read()) != -1){
System.out.println(len);
}
bis.close();
}
}
四种拷贝方式的效率问题
- 读写速度从高到低分别是:
- 高效的字节流一次读写一个 字节数组. 29 毫秒
- 基本的字节流一次读写一个 字节数组. 29 毫秒 //掌握
如果字节数组的长度为 8192(8KB), 理论上来讲, 这种拷贝方式效率最高. - 高效的字节流一次读写一个 字节. 633 毫秒 //掌握
- 基本的字节流一次读写一个 字节. 76675毫秒
- 小细节:
- 因为字节缓冲流提供的缓冲区的大小是: 8196个字节(8KB), 所以如果我们用 普通流一次读写一个字节数组的方式拷贝文件,且自定义的数组长度在 8196个字节, 那么可能会出现: 普通流一次读写一个字节数组的速度 > 高效流一次读写一个字节数组的速度。
package com.itheima.demo05_exercise;
import java.io.*;
//案例: 演示字节流拷贝文件的效率.
/*
读写速度从高到底分别是:
1. 高效流一次读写一个字节数组. 71毫秒
2. 普通流一次读写一个字节数组. 70毫秒 掌握
3. 高效流一次读写一个字节. 609毫秒 掌握
4. 普通流一次读写一个字节. 138550毫秒
小细节:
因为字节缓冲流提供的缓冲区的大小是: 8196个字节(8KB), 所以如果我们用 普通流一次读写一个字节数组的方式拷贝文件,
且自定义的数组长度在 8196个字节, 那么可能会出现: 普通流一次读写一个字节数组的速度 > 高效流一次读写一个字节数组的速度
*/
public class Demo03 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//1.分别通过四种方式拷贝视频文件, 并观察它们的效率.
//2.通过普通的字节流一次读写一个字节的形式实现.
method01(); 138550毫秒
//3.通过普通的字节流一次读写一个字节数组的形式实现.
method02(); //70毫秒
//4.通过高效的字节流一次读写一个字节的形式实现.
method03(); //609毫秒
//5.通过高效的字节流一次读写一个字节数组的形式实现.
method04(); 71毫秒
long end = System.currentTimeMillis();
System.out.println("拷贝时间为: " + (end - start));
}
public static void method04() throws IOException {
//1. 创建输入流对象, 关联数据源文件.
BufferedInputStream bis = new BufferedInputStream( new FileInputStream("day12/data/1.avi"));
//2. 创建输出流对象, 关联目的地文件.
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day12/data/5.avi"));
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
byte[] bys = new byte[8196];
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = bis.read(bys)) != -1) {
//5. 将读取到的内容写入到目的地文件中.
bos.write(bys, 0, len);
}
//6. 关流, 释放资源.
bis.close();
bos.close();
}
public static void method03() throws IOException {
//1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("day12/data/1.avi");
BufferedInputStream bis = new BufferedInputStream(fis);
//2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("day12/data/4.avi");
BufferedOutputStream bos = new BufferedOutputStream(fos);
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = bis.read()) != -1) {
//5. 将读取到的内容写入到目的地文件中.
bos.write(len);
}
//6. 关流, 释放资源.
bis.close();
bos.close();
}
public static void method02() throws IOException {
//1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("day12/data/1.avi");
//2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("day12/data/3.avi");
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
byte[] bys = new byte[8196];
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = fis.read(bys)) != -1) {
//5. 将读取到的内容写入到目的地文件中.
fos.write(bys, 0, len);
}
//6. 关流, 释放资源.
fis.close();
fos.close();
}
public static void method01() throws IOException {
//1. 创建输入流对象, 关联数据源文件.
FileInputStream fis = new FileInputStream("day12/data/1.avi");
//2. 创建输出流对象, 关联目的地文件.
FileOutputStream fos = new FileOutputStream("day12/data/2.avi");
//3. 定义变量, 记录读取到的内容或者有效字节(符)数.
int len = 0;
//4. 循环读取,只要条件满足就一直读, 并将读取到的内容赋值给变量.
while ((len = fis.read()) != -1) {
//5. 将读取到的内容写入到目的地文件中.
fos.write(len);
}
//6. 关流, 释放资源.
fis.close();
fos.close();
}
}
铺垫知识
为什么会出现字符流
-
问题1: 字节流也叫万能流, 能拷贝任意类型的数据, 那为什么要学习字符流?
- 因为中文在不同的码表中占用的字节数不一样, 所以如果是用字节流操作中文, 有可能出现中文乱码问题.
-
问题2: 那到底什么是字符流呢?
- 字符流 = 字节流 + 编码表, 即: 字节流按照指定的编码规则读取数据, 即为: 字符流.
看起来好像是我们在一个字符一个字符的读取, 其实是底层的字节流在根据码表规则读取.
- 字符流 = 字节流 + 编码表, 即: 字节流按照指定的编码规则读取数据, 即为: 字符流.
-
结论:
- 中文的第一个字节肯定是负数, 英文字母, 数字, 特殊符号的字节肯定是正数.
- 中文在UTF-8码表中占3个字节, 在GBK码表中占2个字节, 英文字母, 数字, 特殊符号不管在什么码表中都只占1个字节.
- "abc中国"在UTF-8码表下的内容是: 97, 98, 99, -28, -72, -83, -27, -101, -67
- 字符流 = 字节流 + 编码表
package com.ithiema.api.demo;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class Demo17 {
public static void main(String[] args) throws IOException {
/*
1.已知项目下的data文件夹下的1.txt文件中包含数据"abc中国".
2.请通过字节流一次读取一个字节的形式, 将上述文件中的内容读取出来.
3.将上述读取到的数据打印到控制台上.
解释:
1.字节流操作中文不是特别方便.
2.通过String#getBytes()方法, 可以查看中文在不同码表下占用的字节内容.
*/
//需求: 请通过字节流一次读取一个字节的形式, 将上述文件中的内容读取出来.
FileInputStream fis = new FileInputStream("javaSE/data/1.txt");
int len = 0;
while ((len = fis.read()) != -1) {
System.out.println((byte) len);
}
fis.close();
//演示不同码表下的中文占用的字节数.
//utf-8码表: 中国, -28,-72,-83,-27,-101,-67
String str = "中国";
byte[] bys = str.getBytes(); //默认码表: UTF-8
byte[] bys = str.getBytes("utf-8"); //-28,-72,-83,-27,-101,-67
byte[] bys = str.getBytes("gbk"); //-42, -48, -71, -6
System.out.println(Arrays.toString(bys));
}
}
字符串的编解码问题
名词解释:
-
编码:指的是把 字符串 按照指定的码表 转换成其对应的 整数形式.
- “abc中国” --> 按照utf-8, 转成: 97, 98, 99, -28, -72, -83, -27, -101, -67
-
解码:指的是把 整数 按照指定的码表 解析成其对应的 字符串形式.
- 97, 98, 99, -28, -72, -83, -27, -101, -67 -> 按照utf-8, 解析成: “abc中国”
-
字符集:
- 就使用来描述 char 和 int 之间的关系的, 各个国家都有自己的字符集.
拿咱们国内举例, 咱们用的是GB系列的字符集, 它记录了 国内的汉字, 数字, 特殊符号等 和 int之间的关系. - 例如: ASCII字符集规定 'a’ -> 97, ‘A’ -> 65, ‘0’ -> 48
- 就使用来描述 char 和 int 之间的关系的, 各个国家都有自己的字符集.
-
编码表:
-
可以把它理解为就是字符集的不同版本
-
例如:
- GB字符集分为 ->
- GB2313 (收录了7000左右个汉字)
- GBK (收录20000左右个汉字)
- GB18030 (收录了70000左右个汉字)
- GB字符集分为 ->
-
常用的编码表有:
- ASCII:美国信息标准代码, 记录的最基础的数据, 后续的所有的码表基本上都兼容它.
- ISO-8859-1:欧洲统一标准码.
- GBK:国内用的.
- UTF-8:万国码, 统一码.
-
-
-
演示 编解码动作:
- 编码: String -> byte[]
- public byte[] getBytes(); 按照默认码表.
- public byte[] getBytes(String charsetName); 按照指定码表.
- 解码: byte[] -> String
- public String(byte[] bys); 按照默认码表
- public String(byte[] bys, String charsetName); 按照指定码表
- 编码: String -> byte[]
-
结论:
- 中文在UTF-8码表中占3个字节, 在GBK码表中占2个字节, 英文字母, 数字, 特殊符号不管在什么码表中都只占1个字节.
- 中文的第一个字节肯定是负数, 英文字母, 数字, 特殊符号的字节肯定是正数.
- 只要出现乱码问题, 原因只有一个, 即: 编解码不一致.
package com.ithiema.api.demo;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Demo18 {
public static void main(String[] args) throws UnsupportedEncodingException {
//演示编码
String s1 = "abc中国";
System.out.println(Arrays.toString(s1.getBytes())); //utf-8
System.out.println(Arrays.toString(s1.getBytes("utf-8"))); //不区分大小写
System.out.println(Arrays.toString(s1.getBytes("gbk"))); //不区分大小写
System.out.println("----------------");
//演示解码
byte[] bys = {97, 98, 99, -42, -48, -71, -6};
byte[] bys = {97, 98, 99, -28, -72, -83, -27, -101, -67};
String s = new String(bys);
String s = new String(bys, "gbk"); //指定码表
String s = new String(bys, "utf-8"); //指定码表
System.out.println(s);
}
}
字符流
转换流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TFXom826-1669232987299)(F:\BigData\java\图片\day12图片\02.转换流解释.png)]
简介:
-
概述:
- 转换流指的是InputStreamReader 和 OutputStreamWriter, 它们都是属于: 字符流的.
-
作用:用来在 字节流 和 字符流 之间做转换的.
-
解释:
- InputStreamReader:
- 概述:它叫转换输入流, 是 字节流 通向 字符流的桥梁.
- 构造方法:
- public InputStreamReader(InputStream is);
- OutputStreamWriter:
- 概述:它叫转换输出流, 是 字符流 通向 字节流的桥梁
- 构造方法:
- public OutputStreamWriter(OutputStream os);
- InputStreamReader:
-
结论:
- 字符流 = 字节流 + 编码表, 这一点我们可以通过 转换流 实现.
- 区分一个流到底是字节流还是字符流的 小技巧:看该流以什么单词结尾.
如果是以 Stream结尾, 它就是: 字节流.
如果是以 Reader 或者 Writer 结尾, 它就是: 字符流.
演示转换流入门
package com.ithiema.api.demo;
import java.io.*;
public class Demo19 {
public static void main(String[] args) throws IOException {
//method01();
method02();
}
private static void method01() throws IOException {
//转换流一次读取一个字节
//1. 创建输入流对象, 关联: 数据源文件.
//字符流 = 字节流 编码表
InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
//2. 创建输出流对象, 关联: 目的地文件.
//字符流 = 字节流 编码表
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"), "utf-8");
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt")); 如果是默认码表可以这样写
//3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
int len;
//4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
while ((len = isr.read()) != -1) {
//5. 将读取到的内容写入到目的地文件中.
osw.write(len);
}
//6. 释放资源.
isr.close();
osw.close();
}
private static void method02() throws IOException {
//转换流一次读取一个字符数组
//1. 创建输入流对象, 关联: 数据源文件.
//字符流 = 字节流 编码表
InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
//2. 创建输出流对象, 关联: 目的地文件.
//字符流 = 字节流 编码表
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/3.txt"), "utf-8");
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt")); 如果是默认码表可以这样写
//3. 定义变量, 记录读取到的数据(字节, 有效字节数, 字符, 有效字符数,字符串)
int len;
char[] chs = new char[8192];
//4. 循环读取, 只要条件满足就一直读, 并将读取到的数据赋值给 变量
while ((len = isr.read(chs)) != -1) {
//5. 将读取到的内容写入到目的地文件中.
osw.write(chs, 0, len);
}
//6. 释放资源.
isr.close();
osw.close();
}
}
演示普通的字符流
即:FileReader 和 FileWriter
- 解读:
- 虽然我们通过 “字符流 = 字节流 + 编码表” 的规则,结合 转换流 实现了 获取字符流的操作,但这样写太麻烦了,我们可以直接创建字符流对象,通过 FileReader 和 FileWriter 直接实现。
- 即:FileReader 和 FileWriter 就是基本的字符流
- 虽然我们通过 “字符流 = 字节流 + 编码表” 的规则,结合 转换流 实现了 获取字符流的操作,但这样写太麻烦了,我们可以直接创建字符流对象,通过 FileReader 和 FileWriter 直接实现。
package com.ithiema.api.demo;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class Demo20 {
public static void main(String[] args) throws IOException {
method01();
method02();
}
public static void method02() throws IOException {
FileReader fr = new FileReader("javaSE/data/1.txt");
FileWriter fw = new FileWriter("javaSE/data/2.txt");
int len;
char[] chs = new char[1024];
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);
}
fr.close();
fw.close();
}
private static void method01() throws IOException {
FileReader fr = new FileReader("javaSE/data/1.txt");
FileWriter fw = new FileWriter("javaSE/data/2.txt");
int len;
while ((len = fr.read()) != -1) {
fw.write(len);
}
fr.close();
fw.close();
}
}
演示不同码表文件的拷贝
细节:
- 如果以后我们是拷贝 同码表的文件,尽量不用去使用 转换流,因为写法繁琐。
- 如果以后我们是拷贝 不同码表的文件,只能用:转换流来实现
package com.ithiema.api.demo;
import java.io.*;
public class Demo21 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("javaSE/data/1.txt"), "utf-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("javaSE/data/2.txt"), "GBK");
int len;
char[] chs = new char[1024];
while ((len = isr.read(chs)) != -1) {
osw.write(chs, 0, len);
}
isr.close();
osw.close();
}
}
字符高效流
简介:
-
概述:
- 它指的是 BufferedReader,BufferedWriter,
- 即: 可以以字符为单位操作文件, 它提供有缓冲区功能
-
类解释:
- BufferedReader:
- 概述:
- 字符高效输入流, 高效字符输入流, 字符缓冲输入流, 缓冲字符输入流.
- 构造方法:
- public BufferedReader(Reader r);
- 概述:
- BufferedWriter:
- 概述:
- 字符高效输出流, 高效字符输出流, 字符缓冲输出流, 缓冲字符输出流.
- 构造方法:
- public BufferedWriter(Writer w);
- 概述:
- BufferedReader:
-
问: 为什么字符高效流封装的不是文件的路径, 而是一个基本的字符流?
答: 因为高效流仅仅提供缓冲区的功能, 具体的读写还是由基本的流实现的.
字节高效流拷贝文件
package com.ithiema.api.demo;
import java.io.*;
public class Demo22 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("javaSE/data/1.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("javaSE/data/2.txt"));
int len;
char[] chs = new char[1024];
while ((len = br.read(chs)) != -1){
bw.write(chs,0,len);
}
bw.close();
br.close();
}
}
字符高效流的用法之 一次读写一行
成员方法
-
BufferedReader:
- public String readLine(); 一次读取一行的数据,并返回读取到的内容,读不到返回:null,它只接受行内容,不接收回车换行符。
-
BufferedWrite:
- public void newLine(); 根据当前操作系统,给出对应的换行符。
案例:字符高效流 一次读取一行
package com.ithiema.api.demo;
import java.io.*;
public class Demo23 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("javaSE/data/1.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("javaSE/data/2.txt"));
String line;
while ((line = br.readLine()) != null){
bw.write(line);
bw.newLine(); //细节:记得写换行符
}
br.close();
bw.close();
}
}
案例:随机点名器
package com.ithiema.api.demo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class Demo24 {
public static void main(String[] args) throws IOException {
//1.定义ArrayList<String>,用来存储电影名
ArrayList<String> list = new ArrayList<>();
//2.创建字符高效输入流,关联:数据源文件:
BufferedReader br = new BufferedReader(new FileReader("javaSE/data/name.txt"));
//3.定义变量,记录读取到的数据
String line;
//4.循环读取,只要条件满足就一直读,并将读取到的数据赋值给变量
while ((line = br.readLine()) != null) {
//5.将读取到的数据,存储到集合中
list.add(line);
}
//6.搞一个随机数,从集合中堆积获取 一个元素
int num = (int) (Math.random() * list.size());
String name = list.get(num);
System.out.println("随机点名,抽到的是:" + name);
//7.打印结果
//合并版
System.out.println(list.get((int) (Math.random() * list.size())));
}
}
序列化流
对象序列化流
-
概述:
- 对象序列化流: ObjectOutputStream, 它可以把对象序列化到文件中.
-
名词解释:
把 对象 写入到 文件中的动作就叫:序列化 -
ObjectOutputStream
-
构造方法:
- public ObjectOutputStream(OutputStream os);
- 问: 为什么序列化流要封装字节流?
答: 因为它只提供序列化操作, 具体的读写是由基本的字节流实现的.
-
成员方法:
- public void writeObject(Object obj); 序列化对象到文件中.
- public void close(); 释放资源.
-
-
细节:
- 一个类的对象要想实现序列化和反序列化操作, 则该类必须实现: Serializable接口.
- Serializable接口, 它是一个标记接口, 充当标记的作用, 里边没有任何的成员.
案例:演示对象的 序列化操作
JavaBean类,学生类
package com.ithiema.api.pojo;
import java.io.Serializable;
//实现Serializable接口
public class Student implements Serializable {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study(){
System.out.println("学习");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类,对象序列化操作
package com.ithiema.api.demo;
import com.ithiema.api.pojo.Student;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo25 {
public static void main(String[] args) throws IOException {
//创建 对象序列化流 关联 目的地文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("javaSE/data/2.txt"));
//创建JavaBean对象
Student s = new Student("李四", 23);
//把对象写入到文件中,即:序列化操作
oos.writeObject(s);
//释放资源
oos.close();
}
}
对象反序列化流
-
概述:
- 对象反序列化流: ObjectInputStream, 它可以从文件中 反序列化出对象.
-
名词解释:
- 从 文件 中加载出 对象 的操作就叫:反序列化
-
ObjectInputStream:
- 构造方法:
- public ObjectInputStream(InputStream is);
- 问: 为什么反序列化流要封装字节流?
答: 因为它只提供反序列化操作, 具体的读写是由基本的字节流实现的.
- 成员方法:
- public Object readObject(Object obj); 从文件中读取对象.
- public void close(); 释放资源.
- 构造方法:
案例:演示对象的 反序列化操作
package com.ithiema.api.demo;
import com.ithiema.api.pojo.Student;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class Demo26 {
public static void main(String[] args) throws Exception {
//创建对象反序列化流,关联:数据源文件
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javaSE/data/2.txt"));
//具体的反序列化操作,记得向下转型
Student s = (Student) ois.readObject();
System.out.println(s);
ois.close();
}
}
进行序列化和反序列化时候的三个小问题:
- 问: 当序列化之后, 我们修改了JavaBean类中的成员, 然后再进行反序列化, 是否会出问题?
答: 肯定会出问题, 因为 序列化版本号不一样, 如果一个类实现了Serializable接口, 没有手动给 序列化号,
程序会自动给一个, 且当JavaBean类修改的时候, 该序列化号也同步随机修改. - 如何解决上一步出现的问题?
方案1: 重新序列化一次, 然后再反序列化.
方案2: 我们手动给一个序列化版本号.
private static final long serialVersionUID = 1L; - 如果类中的某个属性不想实现序列化怎么办?
例如: Student s = new Student(“刘亦菲”, 33); 该对象的年龄不要序列化到文件中.
答案: 可以通过 transient 关键字实现, 它表示"瞬态"的意思, 被它标记的内容不能被实现序列化和反序列操作.
JavaBean类
package com.itheima.pojo;
import java.io.Serializable;
//JavaBean类, 叫: 学生类
public class Student implements Serializable {
//给类加一个 序列化版本号, 如果不加, 系统会默认生成一个.
private static final long serialVersionUID = 2L; //值根据需求自己加
private String name;
private transient int age; //transient: 学名叫 瞬态关键字.
private String email;
private double salary;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
package com.itheima.demo08_serializable;
import com.itheima.pojo.Student;
import java.io.*;
//案例: 演示serialVersionUID 和 transient关键字.
public class Demo03 {
public static void main(String[] args) throws Exception {
//需求1: 序列化操作.
//write();
//需求2: 反序列化操作.
read();
}
public static void read() throws IOException, ClassNotFoundException {
//1. 创建序列化流, 封装数据源文件.
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12/data/1.txt"));
//2. 进行反序列化操作.
Student s = (Student)(ois.readObject());
//3. 打印对象.
System.out.println(s);
//4. 关流.
ois.close();
}
public static void write() throws IOException {
//1. 创建序列化流, 封装目的地文件.
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12/data/1.txt"));
//2. 创建JavaBean对象.
Student s = new Student("刘亦菲", 33);
//3. 进行序列化操作.
oos.writeObject(s);
//4. 关流.
oos.close();
}
}
Lambda表达式
-
概述:
-
它是JDK1.8的特性, 体现的是面向函数式编程思想, 即: 强调做什么, 而不是怎么做. 需要哪些对象, 重写哪些方法, 由系统自己实现.
-
格式:
(形式参数) -> { 方法体; }
-
细节:
- Lambda表达式只针对于有且只能有一个抽象方法的 接口 有效.
- Lambda表达式和匿名内部类之间的区别是什么?
- A. 所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口 - B. 使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式 - C. 实现原理不同
匿名内部类:编译之后,产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
- A. 所需类型不同
Lambda表达式入门案例
package com.ithiema.api.demo;
//接口
public interface Animal {
//抽象方法
public abstract void eat();
}
package com.ithiema.api.demo;
public class Demo27 {
public static void main(String[] args) {
//方式1:匿名内部类,体现的是面向对象编程思想,需要我们创建对象,重写指定的当打,还需要我们指定要做的事(逻辑代码)
printAnimal(new Animal() {
@Override
public void eat() {
System.out.println("匿名内部类,动物会吃");
}
});
//方式2:Lambda表达式,体现的是面向函数式编程,即:只关心做什么,不关心怎么做,需要创建什么对象,重写什么方法由系统保证。
printAnimal(() -> {
System.out.println("Lambda表达式,动物会吃");
});
}
public static void printAnimal(Animal an) {
an.eat();
}
}
无参无返回值的方法
package com.ithiema.api.demo;
//Eatable 接口
public interface Eatable {
void eat();
}
package com.ithiema.api.demo;
public class Demo28 {
public static void main(String[] args) {
//方式1:匿名内部类
useEatable(new Eatable(){
@Override
public void eat() {
System.out.println("匿名内部类,吃饭");
}
});
//方式2:Lambda表达式
useEatable(() -> {
System.out.println("吃饭");
});
}
public static void useEatable(Eatable e){
e.eat();
}
}
有参无返回值的方法
package com.ithiema.api.demo;
public interface Flyable {
void fly(String s);
}
package com.ithiema.api.demo;
public class Demo29 {
public static void main(String[] args) {
//匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println("匿名内部类");
System.out.println(s);
}
});
System.out.println("--------------------");
//Lambda表达式
useFlyable((String s) -> {
System.out.println("Lambda表达式");
System.out.println(s);
});
//扩展:了解即可 ,调用:useFlyable(Flyable f,String s);
useFlyable((String s) -> {
System.out.println("Lambda表达式");
System.out.println(s);
}, "好好学习");
useFlyable("好好学习",
(String s) -> {
System.out.println("Lambda表达式");
System.out.println(s);
}
);
}
//入门版
public static void useFlyable(Flyable f) {
String s = "晴空万里";
f.fly(s);
}
//进阶版
public static void useFlyable(Flyable f, String s) {
f.fly(s);
}
//再进阶版
public static void useFlyable(String s, Flyable f) {
f.fly(s);
}
}
有参有返回值的方法
package com.ithiema.api.demo;
public interface Addable {
int getSum(int x, int y);
}
package com.ithiema.api.demo;
public class Demo30 {
public static void main(String[] args) {
//匿名内部类
useAddable(new Addable() {
@Override
public int getSum(int x, int y) {
return x + y;
}
});
//Lambda表达式
useAddable((int x, int y) -> {
System.out.println("Lambda表达式");
return x + y;
});
//进阶版
useAddable((int x, int y) -> {
System.out.println("Lambda表达式");
return x + y;
},
100,
200
);
}
public static void useAddable(Addable a) {
int x = 10, y = 20;
int sum = a.getSum(x, y);
System.out.println(sum);
}
//进阶版
public static void useAddable(Addable a, int x, int y) {
int sum = a.getSum(x, y);
System.out.println(sum);
}
}
Lambda表达式的省略模式
- 省略模式:
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
package com.ithiema.api.demo;
//接口
public interface Eatable {
void eat();
}
package com.ithiema.api.demo;
public interface Flyable {
void fly(String s);
}
package com.ithiema.api.demo;
public interface Addable {
//抽象方法 求和
int getSum(int x, int y);
}
package com.ithiema.api.demo;
public class Demo31 {
public static void main(String[] args) {
//1. 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
//不省略
useFlyable((String s) -> {
System.out.println(s);
});
useAddable((int x, int y) -> {
return x + y;
});
//省略
useFlyable((s) -> {
System.out.println(s);
});
useAddable((x, y) -> {
return x + y;
});
System.out.println("-------------------");
//2. 如果参数有且仅有一个,那么小括号可以省略
useFlyable(s -> {
System.out.println(s);
});
//3. 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
//不省略
useEatable(() -> {
System.out.println("吃饭");
});
useFlyable((String s) -> {
System.out.println(s);
});
useAddable((int x, int y) -> {
return x + y;
});
System.out.println("-------------------");
//省略写法,要求如下三种写法能看懂,且尽量能写出来
useEatable(() -> System.out.println("吃饭"));
useFlyable(s -> System.out.println(s));
useAddable((x, y) -> x + y);
}
public static void useEatable(Eatable e) {
e.eat();
}
public static void useFlyable(Flyable f) {
String s = "晴空万里";
f.fly(s);
}
public static void useAddable(Addable a) {
int x = 10, y = 20;
int sum = a.getSum(x, y);
System.out.println(sum);
}
}
么小括号可以省略
3. 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
package com.ithiema.api.demo;
//接口
public interface Eatable {
void eat();
}
package com.ithiema.api.demo;
public interface Flyable {
void fly(String s);
}
package com.ithiema.api.demo;
public interface Addable {
//抽象方法 求和
int getSum(int x, int y);
}
package com.ithiema.api.demo;
public class Demo31 {
public static void main(String[] args) {
//1. 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
//不省略
useFlyable((String s) -> {
System.out.println(s);
});
useAddable((int x, int y) -> {
return x + y;
});
//省略
useFlyable((s) -> {
System.out.println(s);
});
useAddable((x, y) -> {
return x + y;
});
System.out.println("-------------------");
//2. 如果参数有且仅有一个,那么小括号可以省略
useFlyable(s -> {
System.out.println(s);
});
//3. 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
//不省略
useEatable(() -> {
System.out.println("吃饭");
});
useFlyable((String s) -> {
System.out.println(s);
});
useAddable((int x, int y) -> {
return x + y;
});
System.out.println("-------------------");
//省略写法,要求如下三种写法能看懂,且尽量能写出来
useEatable(() -> System.out.println("吃饭"));
useFlyable(s -> System.out.println(s));
useAddable((x, y) -> x + y);
}
public static void useEatable(Eatable e) {
e.eat();
}
public static void useFlyable(Flyable f) {
String s = "晴空万里";
f.fly(s);
}
public static void useAddable(Addable a) {
int x = 10, y = 20;
int sum = a.getSum(x, y);
System.out.println(sum);
}
}