day19.IO流
课前回顾:
字节流
FileInputStream:字节输入流(读)
字符流:
FileWriter:字符输出流(写)
FileReader:字符输入流(读)
字节缓冲流:
BufferedOutputStream:字节缓冲输出流
BufferedInptuStream:字节缓冲输入流
转换流:
OutputStreamWriter
InputStreamReader
今日内容:
1.会使用序列化流读写对象
2.会使用Properties集合结合IO流解析properties配置文件中的内容
3.会导入jar包
4.会使用Commons-io io工具类
5.能看懂正则表达式以及基本的写法
6.会手写单例模式(懒汉式 饿汉式)
7.会使用lombok插件
8.会定义一个简单的枚举类,会调用里面的成员
第一章.序列化流
一.序列化流和反序列化流介绍
1.作用: 读写对象的
2.对象:
序列化流:ObjectOutputStream(写对象)
反序列化流:ObjectInputStream(读对象)
二.序列化流_ObjectOutputStream
1.作用:写对象
2.构造:
ObjectOutputStream(OutputStream out)
OutputStream:抽象类,所以传递需要传递OutputStream的子类对象
3.方法:
void writeObject(Object obj) -> 写对象
4.注意:
如果想要实现序列化流,那么被操作的对象要实现Serializable接口
/*
序列化方法
构造:
ObjectOutputStream(OutputStream out)
OutputStream:抽象类,所以传递需要传递OutputStream的子类对象
方法:void writeObject(Object obj) -> 写对象
*/
private static void write01()throws Exception {
//创建对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day19\\io\\person.txt"));
//写对象
oos.writeObject(new Person("柳岩",36));
//关闭资源
oos.close();
}
public class Person implements Serializable {
private String name;
private int age;
public Person() {
}
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
三.反序列化_ObjectInputStream
1.作用:读对象
2.构造:
ObjectInputStream(InputStream in)
InputStream:抽象类,所以传递的是子类对象
3.方法:
Object readObject()->读对象
/*
反序列化:
构造:
ObjectInputStream(InputStream in)
InputStream:抽象类,所以传递的是子类对象
方法:
Object readObject()->读对象
*/
private static void read01()throws Exception {
//创建对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day19\\io\\person.txt"));
Object o = ois.readObject();
//向下转型
Person p = (Person)o;
System.out.println(p);
//关流
ois.close();
}
四.不想被序列化操作(了解)
方式1:将不想被序列化的成员变成[static]的
方式2:将不想被序列化的成员变成[transient]的-->推荐使用
private String name;
//private static int age;
private transient int age;
五.反序列化时出现的问题以及分析以及解决
出现的原因:
当一个对象中的代码被修改了,没有重新的序列化,就直接反序列化了
异常:InvalidClassException(序列化冲突)
当修改代码之后,重新生成了class文件,也会产生一个新的序列号,但是我们没有重新序列化一下
所以此时文件中还是保存的之前序列化的内容,版本号也是之前的1111
此时我们直接反序列化,用class文件中的2222版本号和文件中的1111做比对,发现对不上,就会出现版本号冲突问题
public class Person implements Serializable {
static final long serialVersionUID = 42L;
private String name;
public int age;
public Person() {
}
public Person(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 "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
六.扩展(经验值)
1.问题1:
如果想序列化多个对象到文件中,怎么反序列化? 循环
2.问题2:
如果我们反序列化的时候,不确定存储了多少个对象,那么循环反序列化次数就不确定,在不确定次数的前提下很容易出现:EOFException(文件意外到达结尾)
解决:
将多个对象放到一个集合中,将这一个集合序列化到文件中
当反序列化的时候,我们直接读取这一个集合对象,然后再遍历集合
public class Test01 {
public static void main(String[] args)throws Exception {
//write01();
read01();
}
/*
反序列化:
构造:
ObjectInputStream(InputStream in)
InputStream:抽象类,所以传递的是子类对象
方法:
Object readObject()->读对象
*/
private static void read01()throws Exception {
//创建对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day19\\io\\person3.txt"));
//Person person1 = (Person) ois.readObject();
//Person person2 = (Person) ois.readObject();
//Person person3 = (Person) ois.readObject();
//System.out.println(person1);
//System.out.println(person2);
//System.out.println(person3);
/*for (int i = 0; i < 4; i++) {
Person person = (Person) ois.readObject();
System.out.println(person);
}*/
ArrayList<Person> list = (ArrayList<Person>) ois.readObject();
for (Person person : list) {
System.out.println(person);
}
//关流
ois.close();
}
/*
序列化方法
构造:
ObjectOutputStream(OutputStream out)
OutputStream:抽象类,所以传递需要传递OutputStream的子类对象
方法:void writeObject(Object obj) -> 写对象
*/
private static void write01()throws Exception {
//创建对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day19\\io\\person3.txt"));
//写对象
//oos.writeObject(new Person("柳岩",36));
//oos.writeObject(new Person("杨幂",32));
//oos.writeObject(new Person("金莲",26));
//创建集合
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("柳岩",36));
list.add(new Person("杨幂",32));
list.add(new Person("金莲",26));
//序列化这存有多个对象的集合
oos.writeObject(list);
//关闭资源
oos.close();
}
}
第二章.打印流_PrintStream(了解)
1.PrintStream打印流基本使用
1.概述:PrintStream:打印流
2.方法:
println():原样输出,输出之后自动换行
print():原样输出,输出之后不会自动换行
3.构造:
PrintStream(String fileName)
fileName:指定输出的文件路径
public class Test02 {
public static void main(String[] args)throws Exception {
PrintStream ps = new PrintStream("day19/io/print.txt");
ps.println("哈哈哈哈");
ps.print("嘿嘿嘿嘿");
ps.close();
}
}
问题:
如果想将System.out.println输出语句打印到控制台上的内容,转移到指定的文件中,怎么做
解决:改变输出语句的流向
使用:System类中的方法
static void setOut(PrintStream out) ->将输出语句输出在控制台上的内容转移到文件中
public class Test03 {
public static void main(String[] args)throws Exception {
PrintStream ps = new PrintStream("day19/io/print.txt");
//改变流向
System.setOut(ps);
System.out.println("哈哈哈");
ps.close();
}
}
2.PrintStream打印流完成续写
构造:
PrintStream(OutputStream out)
续写:
PrintStream(new FileOutputStream("文件路径",true))
public class Test03 {
public static void main(String[] args)throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("day19/io/print.txt",true));
//改变流向
System.setOut(ps);
System.out.println("哈哈哈");
ps.close();
}
}
第三章.Properties集合结合IO使用
一.Properties集合IO流使用
回顾:
1.介绍:properties extends HashTable
2.特点:
a.key 和 value 默认都是String的
b.不能存null值 null键
c.线程安全,效率低
d.元素无序
3.方法:
String getProperty(String key) -> 根据key获取value
Object setProperty(String key, String value) ->存储key value
Set<String> stringPropertyNames() -> 获取所有的key放到set集合中
public class Test01 {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("涛哥","三上悠亚");
properties.setProperty("文章","马伊琍");
properties.setProperty("黄晓明","杨颖");
//遍历
Set<String> set = properties.stringPropertyNames();
for (String key : set) {
String value = properties.getProperty(key);
System.out.println(key+"...."+value);
}
}
}
1.Properties结合IO流使用方法
void load(InputStream inStream) -> 将流中的数据加载到properties集合中
作用:解析配置文件中的数据
1.创建一个配置文件(.properties结尾的文件)
2.在.properties文件中写数据
a.都是以key=value的形式编写
b.中间不要有空格
c.每一个键值对写完需要换行编写其他的键值对数据
d.在properties文件中的key,value默认都是String的,不要写上""
e.配置文件中最好不要出现中文
3.创建properties文件内容:
username=root
password=root
public class Test02 {
public static void main(String[] args)throws Exception {
Properties properties = new Properties();
/*
void load(InputStream inStream) -> 将流中的数据加载到properties集合中
*/
FileInputStream in = new FileInputStream("day19\\io\\pro.properties");
properties.load(in);
//遍历
Set<String> set = properties.stringPropertyNames();
for (String key : set) {
String value = properties.getProperty(key);
System.out.println(key+"..."+value);
}
}
}
第四章.Commons-io工具包
一.介绍
IO技术开发中,代码量很大,而且代码的重复率较高。如果我们要遍历目录,拷贝目录就需要使用方法的递归调用,也增大了程序的复杂度。
Apache软件基金会,开发了IO技术的工具类`commonsIO`,大大简化IO开发。
二.添加第三方jar包
Apahce软件基金会属于第三方,(Oracle公司第一方,我们自己第二方,其他都是第三方)我们要使用第三方开发好的工具,需要添加jar包。
jar包:就是Java自己的压缩包,包中是开发好的功能,全部以class文件形态出现,我们添加直接使用即可。
引入jar包:
1.在当前模块下创建一个Directory,取名为lib
2.将想使用的jar包粘贴到lib下
3.对着jar包,右键->选择 add as library
4.选择module library
5.点击ok
三.工具包的使用
IOUtils类
- 静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
- 静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
public class Demo01IOUtils {
public static void main(String[] args) throws Exception{
/*
IOUtils类
静态方法:IOUtils.copy(InputStream in,OutputStream out)传递字节流,实现文件复制。
静态方法:IOUtils.closeQuietly(任意流对象)悄悄的释放资源,自动处理close()方法抛出的异常。
*/
IOUtils.copy(new FileInputStream("E:\\Idea\\io\\1.jpg"),new FileOutputStream("E:\\Idea\\io\\a\\柳岩.jpg"));
}
}
FileUtils类
- 静态方法:FileUtils.copyDirectoryToDirectory(File src,File dest);
传递File类型的目录,进行整个目录的复制,自动进行递归遍历。
参数:
src:要复制的文件夹路径
dest:要将文件夹粘贴到哪里去
- 静态方法:writeStringToFile(File file,String str)写字符串到文本文件中。
- 静态方法:String readFileToString(File file)读取文本文件,返回字符串。
public class Demo02FileUtils {
public static void main(String[] args)throws Exception {
/*
FileUtils类
- 静态方法:FileUtils.copyDirectoryToDirectory(File src,File dest);
传递File类型的目录,进行整个目录的复制,自动进行递归遍历。
参数:
src:要复制的文件夹路径
dest:要将文件夹粘贴到哪里去
- 静态方法:writeStringToFile(File file,String str)写字符串到文本文件中。
- 静态方法:String readFileToString(File file)读取文本文件,返回字符串。
*/
//FileUtils.copyDirectoryToDirectory(new File("E:\\Idea\\io"),new File("E:\\"));
// FileUtils.writeStringToFile(new File("day19\\io\\commons.txt"),"我爱你柳岩");
String s = FileUtils.readFileToString(new File("day19\\io\\commons.txt"));
System.out.println(s);
}
}
第五章.正则表达式
1.正则表达式的概念及演示
1.概述:拥有特定规则的字符串
2.作用:主要是用于校验
我们可以利用正则表达式来规定我们填写的数据到底符不符合正则表达式的规定(密码,用户名,邮箱,手机号,身份证等)
3.需求:校验一个QQ号
a.长度5-15位
b.全是数字
c.第一位不能是0
4.正则表达式
[1-9][0-9]{4,14}
5.如何校验一个字符串是否符合我们的正则表达式?
String中有一个方法:
boolean matches(String regex) -> 判断一个字符串是否符合regex(参数)规定的规则
public class Demo01Regex {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请你输入QQ号:");
String qq = sc.next();
//boolean result = method(qq);
boolean result = method01(qq);
System.out.println(result);
}
private static boolean method01(String qq) {
boolean matches = qq.matches("[1-9][0-9]{4,14}");
return matches;
}
/*
a.长度5-15位
b.全是数字
c.第一位不能是0
*/
private static boolean method(String qq) {
//判断长度
if (qq.length()<5 || qq.length()>15){
return false;
}
//判断是否全是数字
char[] chars = qq.toCharArray();
for (char c : chars) {
if (c<'0' || c>'9'){
return false;
}
}
//判断第一位是否为0
if (qq.startsWith("0")){
return false;
}
return true;
}
}
2.正则表达式-字符类
java.util.regex.Pattern:正则表达式的编译表示形式。
正则表达式-字符类:[]表示一个区间,范围可以自己定义
语法示例:
1. [abc]:代表a或者b,或者c字符中的一个。
2. [^abc]:代表除a,b,c以外的任何字符。
3. [a-z]:代表a-z的所有小写字符中的一个。
4. [A-Z]:代表A-Z的所有大写字符中的一个。
5. [0-9]:代表0-9之间的某一个数字字符。
6. [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
7. [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符
public class Demo02Regex {
public static void main(String[] args) {
//1.验证字符串是否以h开头,d结尾,中间是aeiou的某一个字符
String regex = "[h][aeiou][d]";
boolean result01 = "had".matches(regex);
System.out.println(result01);
boolean result02 = "hyd".matches(regex);
System.out.println(result02);
System.out.println("==========================");
//2.验证字符串是否以h开头,d结尾,中间不是aeiou中的某一个字符
String regex1 = "[h][^aeiou][d]";
boolean result03 = "hyd".matches(regex1);
System.out.println(result03);
System.out.println("========================");
//3.验证字符串是否a-z任何一个小写字母开头,后面是ad
String regex2 = "[a-z][a][d]";
boolean result04 = "aad".matches(regex2);
System.out.println(result04);
}
}
3.正则表达式-逻辑运算符
正则表达式-逻辑运算符
语法示例:
1. &&:并且
2. | :或者
public class Demo03Regex {
public static void main(String[] args) {
//要求字符串是小写[a-z]并且字符[^aeiou]开头,后跟ad
String regex = "[[a-z]&&[^aeiou]][a][d]";
boolean result01 = "aad".matches(regex);
System.out.println(result01);
boolean result02 = "zad".matches(regex);
System.out.println(result02);
System.out.println("====================");
//2.要求字符串是aeiou中的某一个字母开头,后跟ad
String regex01 = "[a|e|i|o|u][a][d]";
//String regex01 = "[aeiou][a][d]";
boolean result03 = "aad".matches(regex01);
System.out.println(result03);
}
}
4.正则表达式-预定义字符
正则表达式-预定义字符
语法示例:
1. "." : 匹配任何字符。(重点)
2. "\\d":任何数字[0-9]的简写;(重点)
3. "\\D":任何非数字[^0-9]的简写;
4. "\\s": 空白字符:[ \t\n\x0B\f\r] 的简写
5. "\\S": 非空白字符:[^\s] 的简写
6. "\\w":单词字符:[a-zA-Z_0-9]的简写(重点)
7. "\\W":非单词字符:[^\w]
public class Demo04Regex {
public static void main(String[] args) {
//1.验证字符串是否是三位数字
//String regex = "[0-9][0-9][0-9]";
String regex = "\\d\\d\\d";
boolean result01 = "111".matches(regex);
System.out.println(result01);
System.out.println("======================");
//2.验证手机号:1开头;第二位3 5 8 整下的9位都是0-9的数字
//String regex01 = "[1][358][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]";
String regex01 = "[1][358]\\d\\d\\d\\d\\d\\d\\d\\d\\d";
boolean result02 = "13888888888".matches(regex01);
System.out.println(result02);
System.out.println("=========================");
//3.验证字符串是否以h开头,以d结尾,中间是任何一个字符
String regex02 = "[h].[d]";
boolean result03 = "h1d".matches(regex02);
System.out.println(result03);
}
}
5. 正则表达式-数量词
正则表达式-数量词
语法示例:x代表字符
1. X? : x出现的数量为 0次或1次
2. X* : x出现的数量为 0次到多次 任意次
3. X+ : x出现的数量为 1次或多次 X>=1次
4. X{n} : x出现的数量为 恰好n次 X=n次
5. X{n,} : x出现的数量为 至少n次 X>=n次 x{3,}
6. X{n,m}: x出现的数量为 n到m次(n和m都是包含的) n=<X<=m
public class Demo05Regex {
public static void main(String[] args) {
//1.验证字符串是否是3个数字
String regex = "[0-9]{3}";
boolean result01 = "111".matches(regex);
System.out.println(result01);
System.out.println("================");
//2.验证字符串是否为多个数字:1个以上
String regex02 = "[0-9]+";
boolean result02 = "1".matches(regex02);
System.out.println(result02);
System.out.println("====================");
//3.验证手机号:1开头;第二位3 5 8 整下的9位都是0-9的数字
String regex03 = "[1][358][0-9]{9}";
boolean result03 = "13838381438".matches(regex03);
System.out.println(result03);
System.out.println("=====================");
//4.验证QQ号:第一位不能为0 5-15位 全部都是数字
String regex04 = "[1-9][0-9]{4,14}";
boolean result04 = "111111".matches(regex04);
System.out.println(result04);
}
}
6.正则表达式-分组括号( )
正则表达式-分组括号( )
public class Demo06Regex {
public static void main(String[] args) {
//1.校验字符串abc可以出现任意次
String regex = "(abc)*";
boolean result = "abcabcabcabc".matches(regex);
System.out.println(result);
}
}
7.String类中和正则表达式相关的方法
String类中和正则表达式相关的方法
boolean matches(String regex) 判断字符串是否匹配给定的正则表达式。
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符
public class Demo07Regex {
public static void main(String[] args) {
// String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
String s = "abc haha hehe";
String[] split = s.split(" +");
System.out.println(Arrays.toString(split));
// String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符
String s1 = "abc haha hehe";
String z = s1.replaceAll(" +", "z");
System.out.println(z);
}
}
8.正则表达式生成网址:
https://www.sojson.com/regex/generate
第六章.设计模式
设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性。
1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象
结构型模式,共七种:[适配器模式]、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强
行为型模式,共十一种:策略模式、模板方法模式、[观察者模式]、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、[中介者模式]、解释器模式。
一.单例模式
目的:为了让一个类只产生一个对象共外界使用
1.饿汉式:
1.迫不及待地想要这个对象,就想让这个对象赶紧产生
public class Singleton {
/*
目的:让一个类只产生一个对象,供外界使用
所以:我们就不能让外界随意new
*/
private Singleton(){
}
/*
不能让外界new
需要在本类中自己new对象出来,而且还得保证对象随着类的加载而加载
*/
private static Singleton singleton = new Singleton();
/*
对外提供一个方法,返回本类内部new的对象
*/
public static Singleton getSingleton(){
return singleton;
}
}
public class Test01 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
}
}
}
2.懒汉式:
1.不着急要这个对象,什么时候使用,什么时候new对象给外界使用,但是还要保证是同一个对象
public class Singleton {
/*
目的:让一个类只产生一个对象,供外界使用
所以:我们就不能让外界随意new
*/
private Singleton(){
}
/*
不能让外界new
需要在本类中自己new对象出来,但是懒汉式不需要让对象随着类的加载而new
*/
private static Singleton singleton = null;
/*
对外提供一个方法,返回本类内部new的对象
在此方法中将对象new出来,供外界使用,但是要保证对象只有一个产生
-->双重检测锁
*/
public static Singleton getSingleton(){
if (singleton==null){
synchronized (Singleton.class){
if (singleton==null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class Test01 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Singleton singleton = Singleton.getSingleton();
System.out.println(singleton);
}
}
}
3.小结
1.单例模式:
a.将构造私有化
b.在内部创建对象,并且用private static修饰
c.对外提供静态方法,将内部new的对象返回给外部
第七章.Lombok使用
1 lombok介绍
Lombok通过增加一些“处理程序”,可以让java变得简洁、快速。
Lombok能以注解形式来简化java代码,提高开发效率。开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护。
Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。
5.2 lombok使用
- 添加lombok的jar包:lombok-1.18.8.jar。
- 为IDEA添加lombok插件(连接网络使用)
- 安装完毕后,重启IDEA。
5.3 lombok常用注解
@Getter和@Setter
- 作用:生成成员变量的get和set方法。
- 写在成员变量上,指对当前成员变量有效。
- 写在类上,对所有成员变量有效。
- 注意:静态成员变量无效。
- 作用:生成toString()方法。
- 注解只能写在类上。
@NoArgsConstructor和@AllArgsConstructor
- @NoArgsConstructor:无参数构造方法。
- @AllArgsConstructor:满参数构造方法。
- 注解只能写在类上。
@EqualsAndHashCode
- 作用:生成hashCode()和equals()方法。
- 注解只能写在类上。
@Data
- 作用:生成get/set,toString,hashCode,equals,无参构造方法
- 注解只能写在类上。