学习日记-day24-6.8
完成内容:
知识点:
1.网络编程_TCP编程
### 编写客户端1.创建Socket对象,指明服务端的ip以及端口号
2.调用socket中的getOutputStream,往服务端发送请求
3.调用socket中的getInputStream,读取服务端响应回来的数据
4.关流public class Client {public static void main(String[] args)throws Exception {//1.创建Socket对象,指明服务端的ip以及端口号Socket socket = new Socket("127.0.0.1", 6666);//2.调用socket中的getOutputStream,往服务端发送请求OutputStream os = socket.getOutputStream();os.write("我想下载一个小电影".getBytes());//3.调用socket中的getInputStream,读取服务端响应回来的数据InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = is.read(bytes);System.out.println(new String(bytes,0,len));//4.关流is.close();os.close();socket.close();}
}===================================================================================
### 编写服务端1.创建ServerSocket对象,设置端口号
2.调用ServerSocket中的accept方法,等待客户端连接,返回Socket对象
3.调用socket中的getInputStream,用于读取客户端发送过来的数据
4.调用socket中的getOutputStream,用于给客户端响应数据
5.关闭资源public class Server {public static void main(String[] args)throws Exception {//1.创建ServerSocket对象,设置端口号ServerSocket ss = new ServerSocket(6666);//2.调用ServerSocket中的accept方法,等待客户端连接,返回Socket对象Socket socket = ss.accept();//3.调用socket中的getInputStream,用于读取客户端发送过来的数据InputStream is = socket.getInputStream();byte[] bytes = new byte[1024];int len = is.read(bytes);System.out.println(new String(bytes,0,len));//4.调用socket中的getOutputStream,用于给客户端响应数据OutputStream os = socket.getOutputStream();os.write("给你一个小电影".getBytes());//5.关闭资源os.close();is.close();socket.close();ss.close();}
}
知识点 | 核心内容 | 关键操作步骤 | 易混淆点 |
TCP交互过程 | 客户端主动连接服务端的五步流程 | 1. 客户端发送请求; 2. 服务端接收请求; 3. 服务端分析请求; 4. 服务端响应结果; 5. 客户端接收响应 | 连接方向:必须客户端主动连接服务端 |
Socket对象 | 客户端使用Socket类; 服务端使用ServerSocket类 | - 客户端创建需指定IP和端口; - 服务端只需设置端口 | 流对象获取:必须通过socket.getOutputStream()而非直接new FileOutputStream |
客户端编程 | 四步实现流程 | 1. 创建Socket对象; 2. 获取输出流发送请求; 3. 获取输入流读取响应; 4. 关闭资源 | 执行顺序:必须先启动服务端再启动客户端 |
服务端编程 | 五步实现流程 | 1. 创建ServerSocket; 2. accept()等待连接; 3. 获取输入流读取请求; 4. 获取输出流发送响应; 5. 关闭资源 | accept()方法:返回的是连接客户端的Socket对象 |
流操作对比 | 网络流 vs 本地文件流 | - 网络流:socket.getInputStream(); - 本地流:new FileInputStream() | 常见错误:混淆两种流的获取方式 |
三次握手 | TCP连接建立机制 | 必须服务端先运行,客户端才能成功连接 | 错误现象:直接运行客户端会报"拒绝连接"错误 |
2.网络编程_文件上传
### 文件上传客户端以及服务端实现public class Client {public static void main(String[] args)throws Exception {//1.创建Socket对象Socket socket = new Socket("127.0.0.1", 6666);//2.创建FileInputStream,用于读取本地上的图片FileInputStream fis = new FileInputStream("E:\\Idea\\io\\24.jpg");//3.调用getOutputStream,用于将读取过来的图片写给服务端OutputStream os = socket.getOutputStream();//4.边读边写byte[] bytes = new byte[1024];int len;while((len = fis.read(bytes))!=-1){os.write(bytes,0,len);}//给服务端写一个结束标记socket.shutdownOutput();System.out.println("======以下代码是读取响应的结果======");//5.调用getInputStream,读取响应结果InputStream is = socket.getInputStream();byte[] bytes1 = new byte[1024];int len1 = is.read(bytes1);System.out.println(new String(bytes1,0,len1));//6.关流is.close();os.close();fis.close();socket.close();}
}public class Server {public static void main(String[] args)throws Exception {//1.创建ServerSocket对象ServerSocket ss = new ServerSocket(6666);//2.调用accept方法等待客户端的连接Socket socket = ss.accept();//3.调用socket中的getInputStream,读取客户端发送过来的图片InputStream is = socket.getInputStream();/*UUID调用randomUUID(),再调用toString,将其转成String*/String s = UUID.randomUUID().toString();String name = s + System.currentTimeMillis();//4.创建FileOutputStream,将读取过来的图片写到硬盘上FileOutputStream fos = new FileOutputStream("E:\\Idea\\io\\upload\\"+name+".jpg");//5.边读边写byte[] bytes = new byte[1024];int len;while((len = is.read(bytes))!=-1){fos.write(bytes,0,len);}System.out.println("======以下代码是给客户端的响应结果======");//6.调用socket中的getOutputStream,给客户端响应结果OutputStream os = socket.getOutputStream();os.write("上传成功".getBytes());//7.关流os.close();fos.close();is.close();socket.close();ss.close();}
}public class Demo01UUID {public static void main(String[] args) {String string = UUID.randomUUID().toString();//生成一个十六进制的随机数System.out.println("string = " + string);}}======================================================================================
### 文件上传服务端实现(多线程)public class ServerThread {public static void main(String[] args) throws Exception {//1.创建ServerSocket对象ServerSocket ss = new ServerSocket(6666);while (true) {//2.调用accept方法等待客户端的连接Socket socket = ss.accept();new Thread(new Runnable() {@Overridepublic void run() {InputStream is = null;FileOutputStream fos = null;OutputStream os = null;try {//3.调用socket中的getInputStream,读取客户端发送过来的图片is = socket.getInputStream();/*UUID调用randomUUID(),再调用toString,将其转成String*/String s = UUID.randomUUID().toString();String name = s + System.currentTimeMillis();//4.创建FileOutputStream,将读取过来的图片写到硬盘上fos = new FileOutputStream("E:\\Idea\\io\\upload\\" + name + ".jpg");//5.边读边写byte[] bytes = new byte[1024];int len;while ((len = is.read(bytes)) != -1) {fos.write(bytes, 0, len);}System.out.println("======以下代码是给客户端的响应结果======");//6.调用socket中的getOutputStream,给客户端响应结果os = socket.getOutputStream();os.write("上传成功".getBytes());} catch (Exception e) {e.printStackTrace();}finally {//7.关流CloseUtils.closeQ(socket,fos,is,os);}}}).start();}}
}public class CloseUtils {private CloseUtils(){}public static void closeQ(Socket socket, FileOutputStream fos, InputStream is, OutputStream os){if (os!=null){try {os.close();} catch (IOException e) {throw new RuntimeException(e);}}if (fos!= null){try {fos.close();} catch (IOException e) {throw new RuntimeException(e);}}if (is!=null){try {is.close();} catch (IOException e) {throw new RuntimeException(e);}}if (socket!=null){try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}}}
}
知识点 | 核心内容 | 重点 |
TCP文件上传流程 | 客户端通过FileInputStream读取本地文件,经Socket传输至服务端,服务端通过FileOutputStream写入目标位置 | 结束标记未显式传递导致阻塞(需shutdownOutput解决) |
多线程服务端改造 | 使用Thread为每个客户端连接创建独立线程,处理文件上传任务 | 资源关闭需线程安全(工具类封装closeQuietly) |
UUID文件名防覆盖 | 通过UUID.randomUUID()生成唯一文件名,避免服务端存储冲突 | 拼接时间戳进一步降低重复概率 |
IO流选择原则 | 文件操作用FileInputStream/FileOutputStream,网络传输用Socket流 | 普通流与Socket流混用场景 |
阻塞问题分析 | 客户端未发送结束标记时,服务端read()会持续阻塞 | 需显式调用socket.shutdownOutput() |
3.正则表达式_介绍
## 1.正则表达式的概念及演示1.概述:正则表达式是一个具有特殊规则的字符串
2.作用:校验比如:校验手机号,身份证号,密码,用户名,邮箱等
3.String中有一个校验正则的方法:boolean matches(String regex) 校验字符串是否符合指定的regex的规则
4.比如:校验QQ号(不能以0开头,必须都是数字,必须是5-15位的) public class Demo01Regex {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String data = scanner.next();//boolean result01 = method01(data);//System.out.println("result01 = " + result01);boolean result02 = method02(data);System.out.println("result02 = " + result02);}private static boolean method02(String data) {boolean result = data.matches("[1-9][0-9]{4,14}");return result;}private static boolean method01(String data) {//不能是0开头的if (data.startsWith("0")) {return false;}//必须都是数字char[] chars = data.toCharArray();for (char aChar : chars) {if (aChar < '0' || aChar > '9') {return false;}}//必须是5-15位if (data.length()<5 || data.length()>15){return false;}return true;}
}
知识点 | 核心内容 | 重点 |
正则表达式概念 | 特殊规则的字符串,用于校验特定格式的数据 | 区分普通字符串与正则表达式的本质差异 |
正则表达式作用 | 数据校验(手机号/身份证/密码/邮箱等格式验证) | 理解正则表达式在表单验证中的实际应用场景 |
String.matches()方法 | 通过字符串.matches(正则表达式)进行格式校验 | 方法返回值是布尔类型,注意与Pattern类的区别 |
QQ号校验案例 | 非零开头(^[1-9]) + 纯数字([0-9]{4,14}) + 5-15位长度 | 边界值验证:首位数字限制与长度范围的组合判断 |
传统校验方式对比 | 通过charAt()、length()等基础方法逐条验证 | 代码复杂度:需要编写多重if-else嵌套结构 |
正则表达式优势 | 单行代码完成复杂校验(如[1-9][0-9]{4,14}) | 元字符含义理解([]范围限定符、{}次数限定符等) |
4.正则表达式_基本使用
## 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之间的任意一个字符 //字符类private static void method01() {//1.验证字符串是否以h开头,d结尾,中间是aeiou的某一个字符boolean result01 = "had".matches("[h][aeiou][d]");System.out.println("result01 = " + result01);//2.验证字符串是否以h开头,d结尾,中间不是aeiou的某个字符boolean result02 = "hyd".matches("[h][^aeiou][d]");System.out.println("result02 = " + result02);//3.验证字符串是否是开头a-z的任意一个小写字母,后面跟adboolean result03 = "had".matches("[a-z][a][d]");System.out.println("result03 = " + result03);}=======================================================================================
## 3.正则表达式-逻辑运算符正则表达式-逻辑运算符语法示例:1. &&:并且2. | :或者/*逻辑运算符*/private static void method02() {//1.要求字符串是小写字母并且字符不能以[aeiou]开头,后面跟adboolean result01 = "yad".matches("[[a-z]&&[^aeiou]][a][d]");System.out.println("result01 = " + result01);//2.要求字符是aeiou的某一个字符开头,后面跟adboolean result02 = "had".matches("[a|e|i|o|u][a][d]");System.out.println("result02 = " + result02);}=======================================================================================
## 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]//预定义字符private static void method03() {//1.验证字符串是否是三位数字//boolean result01 = "111".matches("[0-9][0-9][0-9]");boolean result01 = "111".matches("\\d\\d\\d");System.out.println("result01 = " + result01);//2.验证手机号: 1开头 第二位3 5 8 剩下的都是0-9的数字boolean result02 = "13838381438".matches("[1][358]\\d\\d\\d\\d\\d\\d\\d\\d\\d");System.out.println("result02 = " + result02);//3.验证字符串是否以h开头,d结尾,中间是任意一个字符boolean result03 = "had".matches("[h].[d]");System.out.println("result03 = " + 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//数量词private static void method04() {//1.验证字符串是否是三位数字boolean result01 = "111".matches("\\d{3}");System.out.println("result01 = " + result01);//2.验证手机号: 1开头 第二位3 5 8 剩下的都是0-9的数字boolean result02 = "13838381438".matches("[1][358]\\d{9}");System.out.println("result02 = " + result02);//3.验证qq号: 不能是0开头,都是数字,长度为5-15boolean result03 = "111111".matches("[1-9][0-9]{4,14}");System.out.println("result03 = " + result03);}=======================================================================================
## 6.正则表达式-分组括号( )正则表达式-分组括号( ) (abc)//分组括号private static void method05() {//校验abc可以出现任意次boolean result = "abcabc".matches("(abc)*");System.out.println("result = " + result);}=======================================================================================
## 7.String类中和正则表达式相关的方法String类中和正则表达式相关的方法boolean matches(String regex) 判断字符串是否匹配给定的正则表达式。String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符private static void method06() {//String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。String s1 = "abc hahah hehe hdhshsh";String[] arr1 = s1.split(" +");System.out.println(Arrays.toString(arr1));//String replaceAll(String regex, String replacement)把满足正则表达式的字符串,替换为新的字符String s2 = s1.replaceAll(" +", "z");System.out.println("s2 = " + s2);}
知识点 | 核心内容 | 重点 |
正则表达式基础 | 字符类使用(中括号表示范围,尖角号取反) | 中括号与圆括号的区别; 取反符号的两种含义(异或/取反) |
预定义字符 | 点号(任意字符)、\d(数字)、\s(空白符)等 | split方法处理点号需转义; 大小写字母的不同含义(如\d与\D) |
数量词 | ?(0或1次)、*(任意次)、+(1次以上)、{n,m}(范围次数) | {n,}表示至少n次; 手机号验证的9位数字处理 |
分组匹配 | 圆括号创建捕获组,要求元素必须连续出现 | 分组与字符类的本质区别; ABC*与(ABC)*的不同含义 |
字符串方法 | matches()/split()/replaceAll()的正则支持 | replaceAll处理连续空格; split多空格切割需用" +" |
实用技巧 | 在线正则生成工具使用(regulex等) | 现成正则表达式复用; 不同语言的正则语法差异 |
5.设计模式_模版方法&单例模式
# 第三章.设计模式设计模式(Design pattern),是一套被反复使用、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、保证代码可靠性、程序的重用性,稳定性。1995 年,GoF(Gang of Four,四人组)合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了 23 种设计模式。<大话设计模式>总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。-->创建对象结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。-->对功能进行增强行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。## 1.模版方法设计模式模板方法(Template Method)模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。明确了一部分功能,而另一部分功能不明确。需要延伸到子类中实现饭店中吃饭: 点菜,吃菜和买单三个步骤。点菜和买单基本上一致的,但是吃菜不同,吃法也不同。明确了一部分功能,而另一部分功能不明确。public abstract class Hotel {public void eat(){System.out.println("点菜");eatCai();System.out.println("买单");}public abstract void eatCai();
}public class QuanJuDe extends Hotel{@Overridepublic void eatCai() {System.out.println("薄饼");System.out.println("放鸭肉");System.out.println("酱");System.out.println("葱丝");System.out.println("黄瓜丝");System.out.println("卷着吃");}
}public class ZhangLiang extends Hotel{@Overridepublic void eatCai() {System.out.println("调麻酱");System.out.println("放辣椒油");System.out.println("倒到大碗中吃");}
}public class Test01 {public static void main(String[] args) {QuanJuDe quanJuDe = new QuanJuDe();quanJuDe.eat();System.out.println("================");ZhangLiang zhangLiang = new ZhangLiang();zhangLiang.eat();}
}========================================================================================
## 2.单例模式1.目的:单(一个) 例(实例,对象)让一个类只产生一个对象,供外界使用2.分类:a.饿汉式:我好饥渴呀,好饥饿呀,迫不及待要这个对象,所以和对象就需要赶紧new出来b.懒汉式:我好懒呀,不着急要对象,想啥时候使用,你啥时候new给我### 2.1.饿汉式:```properties
饿汉式:我好饥渴呀,好饥饿呀,迫不及待要这个对象,所以和对象就需要赶紧new出来
```public class Singleton {/*防止外界随意使用构造方法new对象,我们需要将构造私有化*/private Singleton(){}/*为了赶紧new对象,我们new对象的时候变成静态的,让其随着类的加载而加载为了不让外界随便使用类名调用此静态对象,我们将其变成private*/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.2.懒汉式:```properties
懒汉式:我好懒呀,不着急要对象,想啥时候使用,你啥时候new给我
```public class Singleton1 {/*防止外界随意使用构造方法new对象,我们需要将构造私有化*/private Singleton1() {}/*懒汉式,不着急new对象*/private static Singleton1 singleton1 = null;/*为了将内部new出来的对给外界定义一个方法,将内部new出来的对返回*/public static Singleton1 getSingleton1() {//如果singleton1不是null就没必要抢锁了,直接返回,是null再抢锁if (singleton1==null){synchronized (Singleton1.class){if (singleton1 == null) {singleton1 = new Singleton1();}}}return singleton1;}
}
知识点 | 核心内容 | 重点 |
设计模式概述 | 23种设计模式的分类与目的,强调代码可重用性、可靠性和稳定性 | 设计模式三大分类(创建型/结构型/行为型) |
模板方法模式 | 通过固定框架(如饭店三步流程)实现部分确定方法+子类实现抽象方法 | 抽象方法延伸实现 vs 普通继承 |
单例模式目的 | 确保类只产生一个对象供全局使用 | 私有构造方法+静态实例的核心实现原理 |
饿汉式实现 | 类加载时立即创建实例(private static final) | 线程安全性天然保证 |
懒汉式实现 | 延迟加载+双重检查锁定(if null→synchronized→if null) | 线程安全陷阱与解决方案 |
模式对比维度 | 初始化时机(饿汉立即/懒汉延迟) | 线程安全(饿汉天然/懒汉需同步) | 性能开销 | 面试高频手撕代码点 |
6.Lombok的使用
# Lombok1.作用:简化javabean开发
2.使用:a.下插件 -> 如果是idea2022不用下载了,自带b.导lombok的jar包c.修改设置 ## 1.lombok介绍Lombok通过增加一些“处理程序”,可以让javabean变得简洁、快速。Lombok能以注解形式来简化java代码,提高开发效率。开发中经常需要写的javabean,都需要花时间去添加相应的getter/setter,也许还要去写构造器、equals等方法,而且需要维护。Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。出现的神奇就是在源码中没有getter和setter方法,但是在编译生成的字节码文件中有getter和setter方法。这样就省去了手动重建这些代码的麻烦,使代码看起来更简洁些。======================================================================================## 2.lombok常用注解### @Getter和@Setter- 作用:生成成员变量的get和set方法。
- 写在成员变量上,指对当前成员变量有效。
- 写在类上,对所有成员变量有效。
- 注意:静态成员变量无效。### @ToString- 作用:生成toString()方法。
- 注解只能写在类上。### @NoArgsConstructor和@AllArgsConstructor- @NoArgsConstructor:无参数构造方法。
- @AllArgsConstructor:满参数构造方法。
- 注解只能写在类上。### @EqualsAndHashCode- 作用:生成hashCode()和equals()方法。
- 注解只能写在类上。### @Data- 作用:生成get/set,toString,hashCode,equals,无参构造方法
- 注解只能写在类上。@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private Integer age;
}public class Test01 {public static void main(String[] args) {Person person = new Person();person.setName("涛哥");person.setAge(10);System.out.println(person.getName()+"..."+person.getAge());System.out.println("================");Person p1 = new Person("张三", 28);System.out.println(p1.getName()+"..."+p1.getAge());}
}
知识点 | 核心内容 | 重点 |
Lombok工具介绍 | 简化JavaBean开发的注解工具,自动生成构造器/getter/setter等方法 | 区分手动编码与Lombok自动生成的区别 |
Lombok安装配置 | IDEA 2022+版本内置插件,旧版本需通过Plugins市场安装小辣椒图标插件 | 版本兼容性问题(2022前后版本差异) |
基础注解功能 | @Getter/@Setter自动生成方法; @Data包含toString/equals/hashCode/无参构造 | @Data不包含有参构造,需配合@AllArgsConstructor使用 |
环境配置要点 | 1. 启用Annotation Processors; 2. 开启Build project automatically | 注解失效的常见配置错误排查 |
实际应用案例 | java; @Data; public class Person{; private String name;; private int age;; } | 编译后字节码验证getter/setter的存在性 |
对比优势分析 | 传统开发需手动编写20+行代码 vs Lombok1行注解解决 | 属性修改时的维护成本对比 |
高级注解组合 | @NoArgsConstructor+@AllArgsConstructor实现全构造覆盖 | 有参构造导致无参构造消失的解决方案 |