day20.IO 流
课前回顾:
1.HashMap:
a.特点:
无序,无索引,key唯一,value可重复,线程不安全
可以存null
b.数据结构:
哈希表
c.方法:
put get remove containsKey keySet entrySet
2.LinkedHashMap:
a.特点:
有序 无索引 key唯一,value可重复,线程不安全
可以存null
b.数据结构:哈希表+链表
3.TreeSet
a.特点:可以对元素进行排序,无索引,元素唯一,线程不安全
b.数据结构:红黑树
c.构造
TreeSet()
TreeSet(比较器)
4.TreeMap
a.特点:可以对key进行排序,无索引,key唯一,线程不安全
b.数据结构:红黑树
c.构造:
TreeMap()
TreeMap(比较器)
5.Hashtable:
a.特点:无序,无索引,key唯一,value可重复,线程安全,不可以存null
b.数据结构:哈希表
6.Properties:
a.特点:无序,无索引,key唯一,value可重复,线程安全,不可以存null,key和value都是String
b.数据结构:哈希表
c.特有方法:
setProperty getProperty stringPropertyNames()
load(字节输入流)
今日重点:
1.all
第一章.字节流
1.IO 流介绍以及输入输出以及流向的介绍
1.I:输入 -> Input
O:输出 -> Output
2.概述:将一个数据从一个设备上传输到另外一个设备上的技术
谁发数据谁就是输出 -> 写
谁收数据谁就是输入 -> 读
2.IO 流的流向_针对 se 阶段的 IO
1.针对于se阶段(内容和硬盘之间):
输出:将数据从内存中写到硬盘上
输入:从硬盘上将数据读到内存中

为啥要将数据写到硬盘上:
1.如果我们要保存一个数据,我可以用数组,集合,但是数组和集合都是临时存储,代码运行里面还有数据,但是代码结束了,数组和集合中的数据就没了
2.所以我们想能不能将数据永久保存,我们可以将数据保存到硬盘上,想使用的时候读回来使用即可 -> IO 流
3.IO 流分类
1.字节流:万物皆字节,所以字节流叫做"万能流"(侧重指的是复制)
OutputStream:字节输出流的父类 -> 抽象类
InputStream:字节输入流的父类 -> 抽象类
2.字符流:专门操作文本文档的
Writer:字符输出流的父类 -> 抽象类
Reader:字符输入流的父类 -> 抽象类
IO 流四大基类:
OutputStream
InputStream
Writer
Reader
4.OutputStream 中子类[FileOutputStream]的介绍以及方法的简单介绍
1.字节输出流:FileOutputStream extends OutputStream
2.作用:写数据
3.构造:
FileOutputStream(File file)
FileOutputStream(String path)
4.注意:
a.输出流如果指定的文件没有,会自动创建
b.默认情况下,每次执行,都会创建一个新的文件,覆盖老文件
5.方法:
a.void write(int data) 一个字节一个字节的写
b.void write(byte[] bytes) 一个字节数组一个字节数组的写
c.void write(byte[] bytes,int offset,int count) 一次写一个字节数组一部分
d.void close() 释放资源
public class Demo01FileOutputStream {
public static void main(String[] args) throws Exception{
//method01();
method02();
}
/**
* void write(int data) 一个字节一个字节的写
* @throws Exception
*/
private static void method01()throws Exception {
FileOutputStream fos = new FileOutputStream("day20_IO/output/1.txt");
fos.write(97);
fos.close();
}
/**
* void write(byte[] bytes) 一个字节数组一个字节数组的写
* void write(byte[] bytes,int offset,int count) 一次写一个字节数组一部分
*/
private static void method02()throws Exception {
FileOutputStream fos = new FileOutputStream("day20_IO/output/1.txt");
byte[] bytes = {97,98,99,100,101,102};
//fos.write(bytes,0,3);
//fos.write(bytes);
//byte[] bytes1 = "中国".getBytes();
//fos.write(bytes1);
fos.write("你好吗".getBytes());
fos.close();
}
}

续写追加:
FileOutputStream(String path,boolean flag) -> flag为true,就会实现续写追加
/**
* 续写追加
*/
private static void method03()throws Exception {
FileOutputStream fos = new FileOutputStream("day20_IO/output/1.txt",true);
fos.write("春种一粒粟\r\n".getBytes());
fos.write("秋收万颗子\r\n".getBytes());
fos.write("四海无闲田\r\n".getBytes());
fos.write("农夫犹饿死\r\n".getBytes());
fos.close();
}
换行符:
windows: \r\n
linux: \n
mac os: \r
5.InputStream 子类[FileInputStream]的介绍以及方法的使用
1.概述:字节输入流 -> FileInputStream extends InputStream
2.作用:读数据
3.构造:
FileInputStream(File file)
FileInputStream(String path)
4.方法:
a.int read() 一次读一个字节,返回的是读取的字节
b.int read(byte[] bytes) 一次读一个字节数组,返回的是读取的个数
c.int read(byte[] bytes,int offset,int count) 一次读一个字节数组一部分,返回的是读取的个数
d.void close()释放资源
6.一次读取一个字节
/**
* int read() 一次读一个字节,返回的是读取的字节
*/
private static void method01() throws Exception {
FileInputStream fis = new FileInputStream("day20_IO/output/2.txt");
//int data1 = fis.read();
//System.out.println(data1);
//int data2 = fis.read();
//System.out.println(data2);
//int data3 = fis.read();
//System.out.println(data3);
//int data4 = fis.read();
//System.out.println(data4);
//int data5 = fis.read();
//System.out.println(data5);
//定义一个变量,接收读取的字节
int len = 0;
while((len = fis.read())!=-1){
//System.out.println(len);
System.out.println((char)len);
}
fis.close();
}
1.流中的数据读完之后,就不能再继续读了,如果还想重新读,就再 new 一个对象
2.读取的过程中,不要连续写多个 read
3.流关闭之后,不能再次使用,否则会报错
javaException in thread "main" java.io.IOException: Stream Closed at java.base/java.io.FileInputStream.read0(Native Method) at java.base/java.io.FileInputStream.read(FileInputStream.java:228) at com.atguigu.b_input.Demo01FileInputStream.method01(Demo01FileInputStream.java:34) at com.atguigu.b_input.Demo01FileInputStream.main(Demo01FileInputStream.java:8)
7.读取-1 的问题
每个文件末尾都有一个"结束标记",这个"结束标记"我们看不见摸不到,但是当我们调用read方法,读到了"结束标记",就会固定返回-1

8.一次读取一个字节数组以及过程
/**
* int read(byte[] bytes) 一次读一个字节数组,返回的是读取的个数
* @throws Exception
*/
private static void method02() throws Exception{
FileInputStream fis = new FileInputStream("day20_IO/output/2.txt");
/*
数组长度定为多少,那么每次就会读取多少个数据
数据都会先保存到数组中,然后我们再从数组中获取
*/
byte[] bytes = new byte[2];
/*
int len1 = fis.read(bytes);
System.out.println(len1);
int len2 = fis.read(bytes);
System.out.println(len2);
int len3 = fis.read(bytes);
System.out.println(len3);*/
//定义一个变量,代表读取的个数
int len = 0;
while((len = fis.read(bytes))!=-1){
//System.out.println(new String(bytes));
System.out.println(new String(bytes,0,len));
}
fis.close();
}

一般情况我们把数组都会定为 1024 或者其倍数
9.字节流实现图片复制分析

10.字节流实现图片复制代码实现
public class Demo02Copy {
public static void main(String[] args) throws Exception {
//1.创建FileInputStream用于读取本地上的图片字节
FileInputStream fis = new FileInputStream("F:\\idea\\io\\2.png");
//2.创建FileOutputStream用于将读取到的字节写到指定的位置
FileOutputStream fos = new FileOutputStream("F:\\idea\\io\\2_copy.png");
//3.边读边写
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//4.关流 -> 先开后关
fos.close();
fis.close();
}
}
第二章.字符流
1.字节流读取中文的问题
1.注意:
a.英文字母:在GBK中,还是UTF-8中都是占一个字节
b.中文:一个汉字在GBK中占2个字节
一个汉字在UTF-8中占3个字节
2.字节流属于万能流(侧重于文件复制):但是不能边读边看(一边读,一边输出)
解决:我们在读取文本文档的时候,如果把内容看成是一个一个的字符去操作,就可以了
说明:即使用字符流去操作文本文档,那么前提也是编码和解码规则一致
如果编码解码规则不一致,字符流操作也会出现乱码情况
字符流操作文本文档,如果编码和解码一致,边读边看是不会乱的
字节流操作文本文档,即使编码和解码一致,边读边看也有可能出现乱码
2.FileReader 的介绍以及使用
1.概述:字符输入流-> FileReader extends Reader
2.作用:读字符
3.构造:
FileReader(File file)
FileReader(String path)
4.方法:
a.int read() 一次读取一个字符,返回的是读取的字符
b.int read(char[] chars)一次读取一个字符数组,返回的是读取的个数
c.void close() 关闭资源
/**
* int read() 一次读取一个字符,返回的是读取的字符
* @throws Exception
*/
private static void method02() throws Exception {
FileReader fr = new FileReader("day20_IO/output/3.txt");
/* int data1 = fr.read();
System.out.println((char) data1);
int data2 = fr.read();
System.out.println((char) data2);*/
int len = 0;
while((len = fr.read())!=-1){
System.out.println((char) len);
}
fr.close();
}
/**
* int read(char[] chars)一次读取一个字符数组,返回的是读取的个数
*/
private static void method03()throws Exception {
FileReader fr = new FileReader("day20_IO/output/3.txt");
char[] chars = new char[3];
//定义一个len,接收读取的个数
int len = 0;
while((len = fr.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
}
1.字符流读取文本文档中的内容,想要边读边输出,在编码一致的情况下,输出的不会是乱码
2.字节流读取文本文档中的内容,想要边读边输出,即使在编码一致的情况下,输出的也有可能是乱码,因为字节有可能读不全就是输出了
3.FileWriter 的介绍以及使用
1.概述:字符输出流 -> FileWriter extends Writer
2.作用:写字符
3.构造:
FileWriter(File file)
FileWriter(String path)
FileWriter(String path,boolean flag)-> flag如果是true,就会实现续写追加
4.方法:
void write(int i) -> 一次写一个字符
void write(char[] chars) -> 一次写一个字符数组
void write(char[] chars,int offset,int count) -> 一次写一个字符数组一部分
void write(String s) -> 一次写一个字符串
void flush() 刷新缓冲区
void close() 关闭资源
5.注意:FileWriter底层自带一个缓冲区,我们写的数据先在缓冲区中,我们需要将数据从缓冲区中刷到文件中
/**
* void write(String s) -> 一次写一个字符串
*/
private static void method01() throws IOException {
FileWriter fw = new FileWriter("day20_IO/output/4.txt");
fw.write("你好吗?小金莲111");
fw.close();
}
4.FileWriter 的刷新功能和关闭功能
1.flush():刷新缓冲区,这个方法刷完之后,流对象还能用
2.close():先刷新,后关闭,close之后,流对象就不能使用了
/**
* void write(String s) -> 一次写一个字符串
*/
private static void method01() throws IOException {
FileWriter fw = new FileWriter("day20_IO/output/4.txt",true);
fw.write("你好吗?小金莲111");
fw.flush();
fw.write("小金莲说:涛哥,你好吗?");
fw.flush();
fw.close();
//fw.write("涛哥说:小金莲,我嗑药了!你去和庆庆过吧");
}
5.IO 异常处理的方式
public class Demo02FileWriter {
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("day20_IO/output/4.txt");
fw.write("你好");
} catch (IOException e) {
e.printStackTrace();
} finally {
/*
如果fw不是null,证明new了,所以最后要close
如果是null,证明没new,这个时候就不用close了
*/
if (fw!=null){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
6.JDK7 之后 io 异常处理方式
1.格式:
try(IO流对象;IO流对象){
}catch(异常 对象名){
对象名.printStackTrace()
}
2.会自动关闭流对象
/**
* jdk7以后得IO流异常处理
*/
private static void method02() {
try (FileWriter fw = new FileWriter("day20_IO/output/4.txt")) {
fw.write("涛哥问:小金莲,你在干什么?");
} catch (IOException e) {
e.printStackTrace();
}
}
7.JDK9 之后的 IO 异常处理方式
之前我们讲过 JDK 1.7 引入了 trywith-resources 的新特性,可以实现资源的自动关闭,此时要求:
- 该资源必须实现 java.io.Closeable 接口
- 在 try 子句中声明并初始化资源对象
- 该资源对象必须是 final 的
try(IO流对象1声明和初始化;IO流对象2声明和初始化){
可能出现异常的代码
}catch(异常类型 对象名){
异常处理方案
}
JDK1.9 又对 trywith-resources 的语法升级了
- 该资源必须实现 java.io.Closeable 接口
- 在 try 子句中声明并初始化资源对象,也可以直接使用已初始化的资源对象
- 该资源对象必须是 final 的
IO流对象1声明和初始化;
IO流对象2声明和初始化;
try(IO流对象1;IO流对象2){
可能出现异常的代码
}catch(异常类型 对象名){
异常处理方案
}
/**
* 不用close
* 还减少了try的压力
* @throws IOException
*/
private static void method03() throws IOException {
FileWriter fw = new FileWriter("day20_IO/output/4.txt");
try (fw) {
fw.write("涛哥问:小金莲,你在干什么?");
} catch (IOException e) {
e.printStackTrace();
}
}
第三章.序列化流
一.序列化流和反序列化流介绍
1.有两个流对象:
a.序列化流:ObjectOutputStream
b.反序列化流:ObjectInputStream
2.作用:读写对象

二.序列化流_ObjectOutputStream
1.概述:ObjectOutputStream
2.作用:写对象
3.构造:
ObjectOutputStream(OutputStream os)
4.方法:
writeObject(Object o) 写对象
public class Person implements Serializable {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/**
* 序列化流
* 1.概述:ObjectOutputStream
* 2.作用:写对象
* 3.构造:
* ObjectOutputStream(OutputStream os)
* 4.方法:
* writeObject(Object o) 写对象
*/
private static void writer() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20_IO/output/person.txt"));
Person p1 = new Person("小金莲", 18);
oos.writeObject(p1);
oos.close();
}
三.反序列化_ObjectInputStream
1.概述:ObjectInputStream
2.作用:读对象
3.构造:
ObjectInputStream(InputStream is)
4.方法:
Object readObject()
/**
* 1.概述:ObjectInputStream
* 2.作用:读对象
* 3.构造:
* ObjectInputStream(InputStream is)
* 4.方法:
* Object readObject()
*/
private static void reader()throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20_IO/output/person.txt"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
一个对象想要在网络上进行传输,这个对象必须要实现序列化接口
四.不想被序列化操作(了解)
关键字: transient
五.反序列化时出现的问题以及分析以及解决
1.问题描述:
我们修改了源码,但是没有重新序列化,直接反序列化了,就会出现序列号冲突问题

解决:人为的将序列号定死
public class Person implements Serializable {
public static final long serialVersionUID = 42L;
private String name;
public Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
六.经验问题
1.如果我们序列化多个对象,我们反序列化时就需要循环读取,如果循环的次数和存储对象的个数不一样,就会出现EOFException(文件意外到达结尾异常)
public class Demo02ObjectWriteAndRead {
public static void main(String[] args)throws Exception {
//riter();
reader();
}
/**
* 1.概述:ObjectInputStream
* 2.作用:读对象
* 3.构造:
* ObjectInputStream(InputStream is)
* 4.方法:
* Object readObject()
*/
private static void reader()throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day20_IO/output/person.txt"));
//Person p1 = (Person)ois.readObject();
//Person p2 = (Person)ois.readObject();
//Person p3 = (Person)ois.readObject();
//Person p4 = (Person)ois.readObject();
//System.out.println(p1);
//System.out.println(p2);
//System.out.println(p3);
//System.out.println(p4);
ArrayList<Person> list = (ArrayList<Person>) ois.readObject();
for (Person person : list) {
System.out.println(person);
}
ois.close();
}
/**
* 序列化流
* 1.概述:ObjectOutputStream
* 2.作用:写对象
* 3.构造:
* ObjectOutputStream(OutputStream os)
* 4.方法:
* writeObject(Object o) 写对象
*/
private static void writer() throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day20_IO/output/person.txt"));
Person p1 = new Person("小金莲", 18);
Person p2 = new Person("小涛哥", 20);
Person p3 = new Person("小庆庆", 19);
ArrayList<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
//oos.writeObject(p1);
//oos.writeObject(p2);
//oos.writeObject(p3);
oos.writeObject(list);
oos.close();
}
}
第四章.打印流
1.PrintStream 打印流基本使用
1.概述:PrintStream extends OutputStream
2.作用:将数据打印到控制台上或者打印到指定文件中
3.构造:
PrintStream(String path)
4.方法:
println():原样输出,自带换行效果
print():原样输出,不带换行效果
public class Demo01PrintStream {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream("day20_IO/output/print.txt");
ps.println("一片两片三四片");
ps.println("五片六片七八片");
ps.println("九片十片十一片");
ps.println("香山红叶红满天");
ps.close();
}
}
1.System类有一个静态方法: setOut(PrintStream ps) -> 改变流向 -> 将输出语句在控制台上打印的结果转移到指定的文件中输出保存
public class Demo02PrintStream {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream("day20_IO/output/print.txt");
System.out.println("哈哈哈");
System.out.println("嘿嘿嘿");
System.setOut(ps);
System.out.println("此类中有一个异常");
System.out.println("叫做空指针异常");
System.out.println("原因是:ps对象为null");
System.out.println("在代码的第7行");
ps.close();
}
}
使用场景:
可以将输出的内容以及详细信息放到日志文件中,永久保存
以后我们希望将输出的内容永久保存,但是输出语句会将结果输出到控制台上,控制台是临时显示,如果有新的程序运行,新程序的运行结果会覆盖之前的结果,这样无法达到永久保存,到时候我们想看看之前的运行结果信息就看不到了,所以我们需要将输出的结果保存到日志文件中,就可以使用 setOut 改变流向
2.PrintStream 打印流完成续写
PrintStream(OutputStream out)
OutputStream抽象类,可以传递FileOutputStream,FileOutputStream中有一个追加续写的构造
public class Demo03PrintStream {
public static void main(String[] args) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("day21_net/3.txt",true));
ps.println("哈哈哈");
ps.close();
}
}
第五章.Properties 结合 IO 流使用方法
1.void load(InputStream inStream)-> 将文件中的数据加载到properties集合中
问题描述:
将来我们写代码的时候有很多重要的"硬数据",比如用户名,密码等,如果这些数据放到源代码中,后续要是修改,我们就要频繁的去源代码中修改,将来类和类之间有联系,频繁修改源代码,有可能导致代码出现问题,有可能牵连到其他的类
所以我们应该将这些"硬数据"放到文件中,用java代码动态解析此文件,动态获取文件中重要的数据
1.创建xxx.properties配置文件
2.创建方式: 在当前模块下,右键 -> new -> file -> 取名xxx.properties
3.做配置:
a.配置文件中数据格式:key=value形式
b.每一个键值对写完,需要换行写下一对
c.key和value都是String的,但是不要加""
d.不要写中文
username=root
password=1234
public class Demo01Properties {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
//创建FileInputStream
FileInputStream fis = new FileInputStream("day20_IO/output/pro.properties");
//调用load方法将流中的数据加载到properties集合中
properties.load(fis);
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(username+"..."+password);
}
}