Re zero:马上就又没学上又没班上的悲惨生活
写在前面
出于许多造化弄人的原因,博主现在要工作保研两手抓了,并且主要精力放在找工作上。
经过了很长一段时间的消沉之后需要开始加把劲了,于是在这里记一下每天学了啥。
5.4
《JAVA与模式》之单例模式 - java_my_life - 博客园
- 作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。
- 特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
- 实现方式:
- 饿汉式单例类:空间换时间,类装载的时候就会创建类的实例;
- 懒汉式单例类:时间换空间,就是每次获取实例都会进行判断,看是否需要创建实例,需要实现线程同步,以保证不会创建多个实例破坏线程安全。
- 双重检查加锁:懒汉式单例类的一种实现方式,并不是每次进入
getInstance
方法都需要同步,而是先不同步,进入方法后,先检查实例是否存在,如果不存在才进行下面的同步块,这是第一重检查,进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。- “双重检查加锁”机制的实现会使用关键字volatile修饰单例实例,它的意思是:被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
- 可以实现线程安全地创建实例,而又不会对性能造成太大的影响。它只是第一次创建实例的时候同步,以后就不需要同步了,从而加快了运行速度。
- Lazy initialization holder class模式:懒汉式单例类的另一种实现方式,使用了Java的类级内部类和多线程缺省同步锁,使JVM隐含地执行同步,巧妙地实现了延迟加载和线程安全。
- 类级内部类?类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类;相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量;类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
- 多线程缺省同步锁的知识:某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己再来进行同步控制了。这些情况包括:
1. 由静态初始化器(在静态字段上或static{}块中的初始化器)初始化数据时
2. 访问final字段时
3. 在创建线程之前创建对象时
4. 线程可以看见它将要处理的对象时
- 采用类级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象实例,从而同时实现延迟加载和线程安全。getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
- 类级内部类?类级内部类指的是,有static修饰的成员式内部类。如果没有static修饰的成员式内部类被称为对象级内部类;相当于其外部类的static成分,它的对象与外部类对象间不存在依赖关系,因此可直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量;类级内部类相当于其外部类的成员,只有在第一次被使用的时候才被会装载。
- 双重检查加锁:懒汉式单例类的一种实现方式,并不是每次进入
public class Singleton {
private Singleton(){}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
* 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class SingletonHolder {
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}
- 单元素的枚举类型已经成为实现Singleton的最佳方法。用枚举来实现单例非常简单,只需要编写一个包含单个元素的枚举类型即可。使用枚举来实现单实例控制会更加简洁,而且无偿地提供了序列化机制,并由JVM从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式。
public enum Singleton {
/**
* 定义一个枚举的元素,它就代表了Singleton的一个实例。
*/
uniqueInstance;
/**
* 单例可以有自己的操作
*/
public void singletonOperation(){
//功能处理
}
}
5.3
背背八股喵。
唉唉为什么效率这么低。
记一次SQL隐式转换导致精度丢失问题的排查 → 不规范就踩坑 - 青石路 - 博客园
- SQL 运算发生隐式类型转换,并影响运算结果;
- 发生隐式转换时,因为数据类型不匹配导致索引结构无法直接匹配查询条件,导致索引失效。
- 索引的有序性被破坏:MySQL 的 B-Tree 索引基于字段的原始值排序。若查询条件触发隐式转换(如字符串转数字),数据库需逐行转换字段值后再比较,破坏了索引的有序性,导致无法通过索引树快速定位数据。
- 转换后数据不唯一:例如,varchar 字段存储了 "123a" 和 "123b",隐式转换为整数时均变为 123。此时 MySQL 无法通过索引唯一确定目标数据,只能全表扫描。
神奇的 SQL 之温柔的陷阱 → 三值逻辑 与 NULL ! - 青石路 - 博客园.html
- SQL 中实际上有三种逻辑值:
true,false,unknown
; - NULL 用于表示缺失的值或遗漏的未知数据,不是某种具体类型的值,不能对其使用谓词;
- 对 NULL 使用比较谓词后得到的结果总是 unknown;
- 我们应该把 IS NULL 看作是一个谓词,而不是:IS 是谓词,NULL 是值;类似的还有 IS TRUE、IS FALSE;
- IN 和 EXISTS 可以互相替换使用,而 NOT IN和 NOT EXISTS;
- 最佳方法应该是往表里添加 NOT NULL 约束来尽力排除 NULL。
- 呃呃为了看着顺眼之前一直全小写,好像不太行呃呃。
神奇的 SQL 之 MySQL 执行计划 → EXPLAIN,让我们了解 SQL 的执行过程! - 青石路 - 博客园
explain
指令可输出指令具体执行时的执行类型、操作对象、关联类型或者访问类型、是否使用索引、匹配对象、需要扫描的行数等等;- 便于检查语句是否按预期执行;
- 可用于判断优化是否生效等。
神奇的 SQL 之性能优化 → 让 SQL 飞起来 - 青石路 - 博客园
想起来妈的淘天笔试 Java 语法选择题选错了呃呃,太唐了。
Java开发——15.引用类型方法的参数和返回值-CSDN博客
深入理解Java函数的原理与应用-CSDN博客
- Java 是值传递!
- 传入参数传入、返回值返回的是基本类型的值,或引用类型的地址的副本;
public User resetUser(User user) {
user = new User(); // 方法内的引用副本指向新对象
return user;
}
User u1 = new User("Alice");
User u2 = resetUser(u1);
// u1 仍指向原对象,u2 指向新对象
- 对于
String
、Integer
等不可变类,虽然返回的是地址值副本,但修改操作会创建新对象,不影响原对象。例如:
public String getString() {
String s = "Hello";
return s;
}
String s = getString();
s = s + " World"; // 创建新对象,原 "Hello" 未改变
java final 使用讲解(讲解string)_final string有无final的区别-CSDN博客
- 修饰的类不可以被继承;
- 修饰的方法不可以被重写;
- 修饰的变量不可以被改变,不过被 final 修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。
- Java String类 为什么是 final 不可变的?
- 安全性:为了线程安全;
- 效率:为了实现字符串池。
- String的不可变性:
- 为什么需要保证String不可变:因为只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现,因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。
- 如果字符串是可变的,那么会引起很严重的安全问题:比如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。
- 因为字符串是不可变的,所以是多线程安全的:同一个字符串实例可以被多个线程共享,这样便不用因为线程安全问题而使用同步,字符串自己便是线程安全的。
- 因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算:这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象,这就是HashMap中的键往往都使用字符串
学习设计模式:
- 类的创建模式,又叫做静态工厂方法(Static Factory Method)模式。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例;
- 优点:
- 解耦客户端与具体类:客户端无需直接实例化具体类,只需通过工厂类获取对象,降低了代码的依赖关系。
- 集中管理对象创建逻辑:所有对象的创建逻辑集中在工厂类中,便于统一维护和修改。例如,若需要修改对象初始化方式,只需调整工厂类即可。
- 代码复用; - 简化客户端使用。
- 解耦客户端与具体类:客户端无需直接实例化具体类,只需通过工厂类获取对象,降低了代码的依赖关系。
- 缺点:
- 单个工厂类集中了所有的创建逻辑,复杂难以维护,违反开闭原则,新增类型需修改工厂类。
《JAVA与模式》之工厂方法模式 - java_my_life - 博客园
- 抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个类应当被实例化这种细节;
- 通过子类化实现扩展,符合开闭原则。
维度 | 简单工厂模式 | 工厂方法模式 |
---|---|---|
核心结构 | 一个工厂类,通过条件判断创建不同对象。 | 抽象工厂接口 + 多个具体工厂子类,每个子类负责创建一种产品。 |
扩展性 | 新增产品需修改工厂类的逻辑,违反开闭原则。 | 新增产品时只需添加新的工厂子类,符合开闭原则。 |
代码复杂度 | 结构简单,适合产品类型较少的场景。 | 需要更多子类,结构更复杂,但扩展性更强。 |
创建方式 | 通常使用静态方法集中创建对象。 | 通过实例化的工厂子类动态创建对象。 |
设计原则 | 集中控制,但牺牲了扩展灵活性。 | 通过多态和子类化实现高扩展性,强调单一职责原则。 |
- 简单工厂模式适合产品类型固定、变化较少的场景,优势在于快速实现和代码简洁。
- 工厂方法模式通过抽象化和多态,解决了扩展性问题,适合需要频繁新增产品的场景,但需要更多子类支持。
- 两者选择取决于具体需求:若需快速开发且无需频繁扩展,简单工厂是优选;若系统需要长期演进和高扩展性,工厂方法模式更为合适。
- 抽象工厂模式是对象的创建模式,提供一种接口来创建一系列相关或相互依赖的对象,而无需指定具体类。
- 抽象工厂关注的是一组相互关联或依赖的对象(例如:UI 组件中的按钮、文本框、弹窗等),这些对象需要协同工作,且需要保证风格的一致性(如 Windows 风格或 macOS 风格)。假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。
- 通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题,适用于需要保证一组对象兼容性的场景(如跨平台 UI 组件、游戏中的不同主题皮肤)。
- 优点:解决产品族的一致性问题;
- 缺点:新增产品类型需修改所有工厂接口:核心原因:抽象工厂模式的设计约束。抽象工厂接口声明了整个产品族的创建方法,每个具体工厂必须实现所有方法。新增产品类型意味着接口扩展,所有实现类必须同步更新;否则如果某个具体工厂不实现新增方法,会导致该工厂无法生产完整的产品族,破坏设计一致性,也会编译失败。
- 设计原则的权衡:抽象工厂模式优先保证产品族的兼容性,而不是扩展性。新增产品类型属于对现有接口的“横向扩展”,需要修改已有代码,这违反了开闭原则(OCP),但这是模式的固有代价。
维度 | 简单工厂模式 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|---|
核心目标 | 集中创建单一类型的对象 | 将对象创建延迟到子类,解决单一产品的扩展 | 创建一组相关或依赖的对象(产品族) |
结构复杂度 | 最简单(一个工厂类+条件判断) | 中等(抽象工厂接口+具体工厂子类) | 最复杂(抽象工厂+多个具体工厂+多类产品) |
扩展性 | 违反开闭原则(新增产品需修改工厂) | 支持扩展单一产品(新增工厂子类) | 支持扩展产品族(新增工厂子类+产品类) |
适用场景 | 对象类型少且固定 | 单一产品类型需要灵活扩展 | 需要保证一组对象的兼容性和一致性 |
代码示例 | Product p = Factory.create(type) |
Factory f = new SubFactory(); p = f.create() |
GUIFactory f = new WinFactory(); Button b = f.createButton() |
// 抽象产品族接口
public interface Button { void render(); }
public interface TextInput { void display(); }
// 具体产品(Windows 风格)
public class WinButton implements Button {
public void render() { System.out.println("Windows 按钮"); }
}
public class WinTextInput implements TextInput {
public void display() { System.out.println("Windows 输入框"); }
}
// 抽象工厂接口
public interface GUIFactory {
Button createButton();
TextInput createTextInput();
}
// 具体工厂(Windows 产品族)
public class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public TextInput createTextInput() { return new WinTextInput(); }
}
// 使用
GUIFactory factory = new WinFactory();
Button btn = factory.createButton();
TextInput input = factory.createTextInput();
5.1~5.2
淘天约面 5.7,唉唉假期要突击了。
继续修题解。
研究 luogu crayon 并尝试在 Linux 下造数据以规避 CRLF 的问题。
开摆一会儿!我草 Kingdom Rush 怎么这么好玩啊
圣地巡礼之湖师大附中。
4.19~4.30
哦吼怎么又十天过去了,怎么才能让自己不鱼鱼呢?
我觉得有必要降低一下写这个的标准以减轻心理压力,随便记录一下感觉就好了。
校赛换了两道题并制作了题解,不要尝试黑入我的电脑啊呃呃。
打了武汉邀请赛并守银。
研究阿里云 ECS 并部署 SpringBoot 项目,总结:「笔记」从校赛报不上名开始的服务器运维 - Luckyblock - SpringBoot 项目部署到阿里云服务器
研究阿里云 OSS,总结:阿里云 OSS
4.18
4.7~4.17
吼吼怎么又过去了十天。
这十天里:
- 验校赛;
- P 了几张海报和纪念品设计;
- 投了简历,并受到了若干份笔试;
- 大量看八股;
- 运动;
- 以及鱼鱼。
4.6
简历编完了!
看八股。
牛客周赛 Round 88 - Luckyblock - 博客园.html
4.3~4.5
给 rating 功能修 bug。
鱼鱼鱼鱼鱼鱼鱼鱼妈的别鱼鱼了。
3.31~4.2
八股。
编简历,哎呦我草写了啥啊给我整笑了。
因为校队 OJ 外网上引用的某个 cdn 加速的 css 挂掉了,尝试阅读祖传文档研究校队 OJ 服务器修 bug。
经过八十万年的努力终于成功了,总结:「笔记」从校赛报不上名开始的服务器运维 - Luckyblock - 博客园
初步恢复对 CSUOJ 的维护能力。
3.30
0Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题) done.
java UDP 通信:DatagramSocket
类:
Java TCP 通信:Socket
类:
Junit 单元测试:
- 定义测试类,编写测试方法;
- 测试方法上必须加
@Test
注解,必须无参无返回值; Assert
断言,判断测试方法中运行的样例是否符合期望值;- 当被测试方法抛出异常,或
Assert
不符合期望时测试失败; - 测试类中其他方法的其他注解:
@BeforeEach
,@AfterEach
,@BeforeAll
,@AfterAll
(Junit 5.x),用来初始化资源、释放资源; - 详解介绍JUnit单元测试框架(完整版)_junit安装步骤-CSDN博客。
Java 反射:
注解:
@
;- 让其他程序根据注解信息决定如何执行该程序;
- 自定义注解:
public @interface注解名称 {
public 属性类型 属性名() default 默认值;
}
- 本质是继承了
Annotation
的接口;使用注解时,本质是在创建接口的实现类对象; - 元注解:
- 修饰注解的注解;
@Target
:限定被注解修饰的位置;Retention
:声明注解的保留周期:RetentionPolicy.SOURCE/CLASS/RUNTIME
。
- 注解的解析:
- 先拿到被注解修饰的对象、方法、属性……(反射);
- 根据拿到的信息做操作。
- 结合反射技术,用来写框架。
代理:
- 对于某个对象,若功能过多,课通过代理来转移部分职责;
- 对象有什么方法想被代理,代理就一定要有对应的方法——通过接口实现;
- 提高代码复用性,将复用代码通过代理实现;
- 不需要修改被代理的对象。
java 静态代理:
- 通过继承、组合实现。
java.lang.reflect.Proxy
类:
- JDK 动态代理技术。
- 【Java】代理模式(Proxy模式)详解_java proxy-CSDN博客
- 优点:可以生成所有实现接口的代理对象;
- 缺点:
JDK
反射生成代理必须面向接口, 这是由Proxy
的内部实现决定的。生成代理的方法中必须指定实现类的接口,根据这个接口,来实现代理类生成的所实现的接口。
SpringAOP:
- Spring 动态代理技术,见下方。
in java:牛客周赛 Round 87 - Luckyblock - 博客园。
3.27~3.29
复健。
验题。
造数据。
鱼鱼。
3.26
0Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题) P170~P186.
XML:
- 可扩展标记语言;
- XML 语法 _ 菜鸟教程。
- 本质是一种数据格式,可以存储复杂的数据结构,和数据关系。
- 应用场景:经常用来做为系统的配置文件;或者作为一种特殊的数据结构,在网络中进行传输。
Dom4j
:解析XML 的第三方库:【Dom4j】Dom4j完整教程详解-CSDN博客- 约束文档:约束 XML 的书写格式,规范化解析方式。
- 分类:
DTD
文档(.dtd
):可以约束编写格式,但是不能约束具体类型; Schema
文档(.xsd
):可以约束编写格式和数据类型。
- 分类:
日志:
- 日志框架:JUL (java.util.loggiing)、Log4j、Logback……
- 日志接口/日志门面:JCL、SLF4J
- SLF4J的介绍与使用(有logback和log4j2的具体实现案例)-CSDN博客
- Logback 由 logback-core(必有)、logback-classic(必有)、logback-access 三个模块组成;
- 核心配置文件:
logback.xml
:- 输出位置、格式设置;
- 指定日志文件拆分和压缩规则;
- 开启、取消日志;
- 日志级别(优先级依次升高):
- 大于或等于核心配置文件配置的日志级别,才会被记录。
日志级别 | 说明 |
---|---|
trace | 追踪,指明程序运行轨迹 |
debug | 调试,实际应用中一般将其作为最低级别,而 trace 则很少使用 |
info | 输出重要的运行信息,数据连接、网络连接、IO操作等等,使用较多 |
warn | 警告信息,可能会发生问题,使用较多 |
error | 错误信息,使用较多 |
多线程:
- 概念;
java.lang.Thread
类的对象代表线程;- 创建(目的是实现
run
方法):- 继承
Thread
类,重写run
方法(代码简单,但是功能有限,无法多重继承,拓展性差); - 实现
Runnable
接口,重写run
方法,传入Thread
作为构造参数(还可以实现其他接口)(有函数类方法注解,可以匿名类 + Lambda 实现); - 实现
Callable
接口、FutureTask
类(可以返回线程执行完毕后的结果):- 创建任务对象:实现
Callable
接口,重写call
方法,封装要做的事情,和要返回的数据,再把Callable
类型的对象封装成FutureTask
(线程任务对象,实现类Runnable
接口)。 - 把线程任务对象交给
Thread
对象,启动。 - 执行完毕后,通过
FutureTask
对象的的get
方法去获取线程任务执行的结果(若调用时还未执行完毕,则等待)。
- 创建任务对象:实现
- 继承
- 启动,用
.start()
,不用.run
(退化成方法调用,是串行的);
线程安全问题:
- 原因:多线程,访问且修改共享资源(出现数据竞争、死锁或资源冲突),且未同步;
- 解决:线程同步。
线程同步:
- 协调多个线程对共享资源的访问,确保线程之间能够正确、有序地执行,避免出现数据竞争、死锁、资源冲突等问题。
- 实现:
- 互斥锁;
- 信号量;
- 条件变量;
- 屏障、自旋锁、原子操作……
java 实现线程同步:
synchronized
关键字:- 同步方法;
- 同步代码块:括号中的对象是锁对象。只有获取锁的线程可以执行同步代码块中的代码。
ReentrantLock
:- 手动创建锁对象;
- 其他同步工具类:
CountDownLatch
、CyclicBarrier
、Semaphore
……
线程通信:
- 当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。
Object
类提供的:wait()
:让当前线程等待并释放所占锁,直到另一个线程调用以下两个唤醒方法;notify()
;notigyAll()
。- 应当使用当前同步锁对象进行调用。
线程池:
- 目的:复用线程,避免每次发起请求都创建新线程,导致性能损失,或线程数量过多导致资源耗尽;
- 接口:
ExecutorService
; ThreadPoolExecutor
构造器:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)
• 参数一:corePoolSize
:指定线程池的核心线程的数量;
• 参数二:maximumPoolSize
:指定线程池的最大线程数量;
• 参数三:keepAliveTime
:指定临时线程的存活时间;
• 参数四:unit
:指定临时线程存活的时间单位(秒、分、时、天);
• 参数五:workQueue
:指定线程池的,还未运行任务的任务队列;
• 参数六:threadFactory
:指定线程池的线程工厂;
• 参数七:handler
:指定线程池的任务拒绝策略(线程都在忙,任务队列也满了的时候,新任务来了该怎么处理)。
ExecutorService
的常用方法
方法名称 | 说明 |
---|---|
void execute(Runnable command) |
执行 Runnable 任务 |
Future<T> submit(Callable<T> task) |
执行 Callable 任务,返回未来任务对象,用于获取线程返回的结果 |
void shutdown() |
等全部任务执行完毕后,再关闭线程池! |
List<Runnable> shutdownNow() |
立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务 |
- 任务拒绝策略:
策略 | 详解 |
---|---|
ThreadPoolExecutor.AbortPolicy |
丢弃任务并抛出RejectedExecutionException异常。是默认的策略 |
ThreadPoolExecutor.DiscardPolicy |
丢弃任务,但是不抛出异常 这是不推荐的做法 |
ThreadPoolExecutor.DiscardOldestPolicy |
抛弃队列中等待最久的任务 然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunsPolicy |
由主线程负责调用任务的run()方法从而绕过线程池直接执行 |
Executors
工具类- 一个线程池的工具类,提供了很多静态方法用于返回不同特点的线程池对象。
方法名称 | 说明 |
---|---|
public static ExecutorService newFixedThreadPool(int nThreads) |
创建固定线程数量的线程池,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。 |
public static ExecutorService newSingleThreadExecutor() |
创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。 |
public static ExecutorService newCachedThreadPool() |
线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了60s则会被回收掉。 |
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) |
创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 |
- 注意:这些方法的底层,都是通过线程池的实现类
ThreadPoolExecutor
创建的线程池对象。 - 注意:大型并发项目中应避免使用
Executors
创建线程池,可能会堆积大量请求和线程导致 OutOfMemoryError。应当用ThreadPoolExecutor
明确各个参数和处理策略,规避资源耗尽。
并发 - 并行。
java 线程的生命周期:
public class Thread {
...
public enum State {
NEW, //新建
RUNNABLE, //可运行
BLOCKED, //锁、阻塞
WAITING, //无限等待
TIMED_WAITING, //计时等待
TERMINATED; //被终止
}
...
}
悲观锁 - 乐观锁:乐观锁和悲观锁详解 _ JavaGuide;
通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量_重量级锁、读写锁、各种锁及其Java实现! - 知乎
IP、端口(16 位二进制)、域名、协议、DNS、C/S、B/S、公网/内网IP、localhost。
InetAddress
类:
- 用于表示和操作 IP 地址;
- 一些方法;
可靠连接:通信双方收发消息都正常无问题(全双工)。
3.24~3.25
速通:0Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题) P148~P170.
Stream 流:
- JDK8 新增的一套结合 Lambda 操作集合或数组数据的 API;
- 获取 Stream 流;
- Stream 流中间方法,支持链式编程;
- 终结方法。
- 哎我草这东西不就是 Linux 的管道/ javascript 的链式编程吗学的东西够多够杂就全能理解是吧哈哈。
获取 Stream 流:
Collections
类:.Stream
方法;- 数组:
Arrays.stream
或Stream.of
方法。
Stream 流中间方法,支持链式编程:
Stream提供的的常用中间方法 | 说明 |
---|---|
Stream<T> filter(Predicate<? super T> predicate) |
用于对流中的数据进行过滤。 |
Stream<T> sorted(Comparator<? super T> comparator) |
按照指定规则排序(无参数默认按照 T 的比较函数升序) |
Stream<T> limit(long maxSize) |
获取前几个元素 |
Stream<T> skip(long n) |
跳过前几个元素 |
Stream<T> distinct() |
去除流中重复的元素。 |
<R> Stream<R> map(Function<? super T, ? extends R> mapper) |
对元素进行加工,并返回对应的新流 |
static <T> Stream<T> concat(Stream a, Stream b) |
合并a和b两个流为一个流 |
终结方法:将 Stream 流操作后的结果再存回到集合或数组中。
R collect(Collector collector)
:把流处理后的结果,根据 collector 方法指定的返回值类型,收集到一个指定的集合中去;Object[] toArray()
:把流处理后的结果收集到一个数组中去,()
内可添加类型转化等。
File 类:
- 创建:通过
File
类构造函数传入路径; - 常用方法;
- 创建文件、目录;删除文件、目录;
- 遍历:
.list
、.listFiles
;
字符集;
字符编码解码:
String
提供的编码:byte[] getBytes(String charsetName)
;String
提供的解码:String(byte[] bytes, String charsetName)
。
IO 流体系:
- 各个流的抽象类:
- 字节输入流:InputStream
- 字节输出流:OutputStream
- 字符输入流:Reader
- 字符输出流:Writer
- 文件操作的 IO 流对应的实现类:前面加
File
。
FileInputStream
:
- 构造函数:传入 File 对象或路径;
- 使用:
read()
、read(byte[] buffer)
、public byte[] readAllBytes() throws IOException
;
FileOutputStream
:
- 构造函数:传入 File 对象或路径,指定
append
模式; - 使用:
write()
、write(byte[] buffer, int off, int len)
;
注意必须要关闭流,释放资源:
try-catch-finally
,再finally
里写close
;- JDK7开始:
try-with-resource
:
try (定义资源1;定义资源2;..) {
可能出现异常的代码;
} catch (异常类名 变量名) {
异常的处理代码;
}
- 是一种表达式,不是类似于
try-catch-finally
控制类语法; - try 后的
()
里面只能放资源; - Java里可以自动释放的不只是内存,只要是“资源”,都可以自动释放!轻松加愉快! - 知乎
- 从JDK7开始,对资源的定义可以简单的描述为:凡是实现了
AutoCloseable
接口,自定义了close
方法的类的实例都可以被看作是一种资源; try-with-resource
因此可以自动识别close
方法并释放资源;- 可能存在“异常压制”,会被压制的异常就是资源即
AutoCloseable
的close
方法抛出的异常,资源在创建以及try
代码块中抛出的异常不会被压制。因为close
方法不是手动调用的。
FileReader
:
- 构造同上;
- 用法同上;
- Java一个汉字占几个字节(详解与原理) - Genesisx - 博客园
- Java中关于Char存储中文到底是2个字节还是3个还是4个? - 子非鱼的回答 - 知乎
https://www.zhihu.com/question/279539793/answer/1830657398
FileWriter
:
- 构造同上;
- 用法同上;
- 刷新流
.flush()
或关闭流.close()
(包含了刷新) 时才会修改文件;
原始流/低级流 - 包装流/处理流
缓冲流 Buffered
:
- 用于提升原始流读写数据的性能;
- 建立缓冲区,减少系统调用操作外存的次数;
- 默认缓冲区:8K。
构造器 | 说明 |
---|---|
public BufferedInputStream(InputStream is) |
把低级的字节输入流包装成一个高级的缓冲字节输入流,从而提高读数据的性能 |
public BufferedOutputStream(OutputStream os) |
把低级的字节输出流包装成一个高级的缓冲字节输出流,从而提高写数据的性能 |
public BufferedReader(Reader r) |
把低级的字符输入流包装成字符缓冲输入流管道,从而提高字符输入流读字符数据的性能 |
public BufferedWriter(Writer r) |
把低级的字符输出流包装成一个高级的缓冲字符输出流管道,从而提高字符输出流写数据的性能 |
public String BufferedReader.readLine()
: 读一行;public void newLine()
:换行;
性能比较:主要和系统调用次数、缓冲区大小有关。
字符输入转换流:InputStreamReader
:
- 解决不同编码时,字符流读取文本内容乱码的问题;
- 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不乱码了;
- 可包装进
BufferedReader
。
构造器 | 说明 |
---|---|
public InputStreamReader(InputStream is) |
把原始的字节输入流,按照代码默认编码转换成字符输入流(与直接用FileReader的效果一样) |
public InputStreamReader(InputStream is, String charset) |
把原始的字节输入流,按照指定字符集编码转换成字符输入流(重点) |
字符输出转换流:OutputStreamWriter
:
- 作用同输入转换流;
- 构造同输入转换流;
- 可包装进
BufferedWriter
。
打印流:PrintStream/Printwriter
:
- 用于高效、便捷打印数据;
- 包装了缓冲流,性能高;
PrintStream
提供的打印数据的方案:- 构造器
构造器 | 说明 |
---|---|
public PrintStream(OutputStream/File/String) |
打印流直接通向字节输出流/文件/文件路径 |
public PrintStream(OutputStream out, boolean autoFlush, String encoding) |
可以指定实现自动刷新,并可指定字符的编码 |
- 方法
方法 | 说明 |
---|---|
public void println(Xxxx xx) |
打印任意类型的数据出去 |
public void write(int/byte[]/byte[]一部分) |
可以支持写字节数据出去 |
Printwriter
用法与PrintStream
类似,但是支持写字符;- 重定向打印流的输出位置(可重定向
System.out
);
数据输入/输出流:DataInputStream
、DataOutputStream
:
- 允许把数据和其类型一并写出去;
- 构造:
public DataOutputstream(Outputstream out)
、public DataInputStream(InputStream is)
; - 方法:有许多
.write[类型]
、.read[类型]()
;
序列化流:
- 用于进行序列化 - 反序列化;
ObjectInputStream
、ObjectOutputStream
;- 构造:
public ObjectOutputStream(OutputStream out)
、public ObjectInputStream(InputStreamis)
; - 方法:
public final void writeObject(Object o) throws IOException
、public final Object readObject()
; - 注意待序列化的类,要实现接口:
Serializable
; - 序列化/反序列化多个对象:操作
ArrayList
即可,已实现接口Serializable
。
框架。
IO 框架。
导入第三方库。
特殊文件:存储有关系的数据,可作为配置文件;以特殊格式进行信息传输。
日志技术:存程序运行信息,便于调试;
.properties
:
- 规范:
- 都只能是键值对;
- 键不能重复;
- 文件后缀一般是.properties结尾的。
- 构造:
public Properties()
; - 常用方法:
常用方法 | 说明 |
---|---|
public void load(InputStream is) |
通过字节输入流,读取属性文件里的键值对数据 |
public void load(Reader reader) |
通过字符输入流,读取属性文件里的键值对数据 |
public String getProperty(String key) |
根据键获取值(其实就是get方法的效果) |
public Set<String> stringPropertyNames() |
获取全部键的集合(其实就是keySet方法的效果) |
public Object setProperty(String key, String value) |
保存键值对数据到Properties对象中去。 |
public void store(OutputStream os, String comments) |
把键值对数据,通过字节输出流写出到属性文件里去 |
public void store(Writer w, String comments) |
把键值对数据,通过字符输出流写出到属性文件里去 |
校赛出题 done。
3.22~3.23
鱼鱼。
运动!运动!运动!
停止鱼鱼!停止鱼鱼!停止鱼鱼!
校赛出题。
打了打算法竞赛喵。
Codeforces Round 1011 (Div. 2) - Luckyblock。
3.21
速通:0Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题) P128~P148.
算法?算法!
异常 Exception;
集合框架:集合中存的是元素对象的地址;
Collection
:单列集合的祖先接口,有很多子接口:
List
系列集合:添加的元素是有序、可重复、有索引。ArrayList
、``:有序、可重复、有索引。
Set
系列集合:添加的元素是无序、不重复、无索引。HashSet
: 无序、不重复、无索引;LinkedList
: 有序、不重复、无索引。TreeSet
:按照大小默认升序排序、不重复、无索引。
Collection
集合的迭代器:Iterator
;
增强 for 循环 for (类型 变量名: 可迭代对象)
;
forEach:
forEach(Consumer <? super T> action)
;- 结合 Lambda:
forEach(x -> { })
; - 结合方法引用:
forEach( xx::yy )
。
ArrarList
和 LinkedList
区别:
- 前者基于数组,查询速度快,插入删除效率低;长度动态增加,扩容机制;
- 后者基于双链表,查询速度慢,插入删除效率高;适合当栈和队列;
HashSet
:
- 基于哈希链表;
- JDK8开始,当链表长度超过 8,且数组长度大于等于 64 时,自动将链表转成红黑树。
LinkedHashSet
:
- 基于哈希表;
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置。
TreeSet
:
- 基于红黑树;
- 自定义类型需要自定义比较器
compareTo
,或构造时设置参数Comparator
对象。 - 集合自带的比较器排序优先级更高。
可变参数:数据类型...参数名称
:
- 可以传任意数量参数/数组过去。
Collections
集合工具类。
Map
:双列集合/键值对集合(entry
对象):
- 键不能重复,值可以重复;
HashMap
(由键决定特点):无序、不重复、无索引,用的最多;LinkedHashMap
(由键决定特点):由键决定的特点:有序、不重复、无索引;TreeMap
(由键决定特点):按照大小默认升序排序,不重复、无索引。
遍历 Map
:
- 取得
keys
遍历,然后调用get
; - 遍历键值对
Map.Entry<, > entry: entries
; Lambda
+forEach
。
HashMap
:底层同 HashSet
。
LinkedHashMap
:底层同 LinkedHashSet
。
3.20
速通:0Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题) P1~P128;
代码块:
- 静态代码块:实现类的初始化;
- 实例代码块:实现对象的初始化。
单例类:一个类只有一个对象:
- 把类的构造器私有。
- 定义一个类变量记住类的一个对象。
- 定义一个类方法,返回对象。
- (可以懒惰创建对象);
枚举: enum
;
形参和实参——Java值传递详细说明_java 传参 简洁-CSDN博客
泛型 <E>
:
- 类似 cpp template。
- 可用于约束类、方法内元素类型的一致性;可扩大方法接收参数的类型范围;
- 泛型的本质:把具体的数据类型作为参数传给类型变量。
- 通配符:
?
- 泛型的上下限:
? extends xxx
、? super xxx
; - 泛型擦除:泛型仅在编译阶段约束,编译后消失,可用反射越过泛型检查;
- 仅接受引用数据类型。基本数据类型:
int -> Integer
;
Object 超类。
详解Java中的clone方法 - 何必等明天 - 博客园.html
包装类;
- 原生类型(
primitive
)对应的对象类型(object
); - 值转字符串;
- 字符串转数值;
- Java中为什么会有包装类?自动拆装箱必要吗?关于Wrapping Class这是重点! - 知乎
Objects
工具类;
字符串:
StringBuilder
:可修改,修改时效率更高;StringBuffer
:用法同StringBuilder
,但是线程安全的;StringJoiner
:用于拼接字符串的容器,可自定义分隔符与前后缀。
System
;
Runtime
:当前运行环境,单例。
BigDecimal
:用于解决浮点数计算失真的大浮点数类。
Date
、SimpleDateFormat
、Calendar
:已过时;
代替Calenclar
- LocalDate:年、月、日
- LocalTime:时、分、秒
- LocalDateTime:年、月、日时、分、秒
- ZoneId:时区
- ZonedDateTime:带时区的时间
代替Date
- Instant:时间戳/时间线
代替SimpleDateFormat
- DateTimeFormatter:用于时间的格式化和解析;
其他补充:
- Period:时间间隔(年,月,日)
- Duration:时间间隔(时、分、秒,纳秒)
Arrays
:操作数组的工具类;
Comparator
:比较器类;
Lambda 表达式:简化函数式接口的匿名内部类的写法。
() -> {}
;- 省略写法。
方法引用:类型::方法
;
构造器引用:类名::new
;
然后开始背八股补项目呃呃呃呃。
妈的光他妈应试学法感觉自己的付出纯纯被异化了啊好虚无啊,必须要干点实事出来!
试试给校队做个管理平台。
3.19
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) done.
SpringBoot 原理:
- 简化了 Spring Framework;
- 起步依赖:通过 Maven 依赖传递间接引入了一系列 Spring 类;
- 自动配置。
自动配置:
- 当spring容器启动后,一些配置类、bean对象就自动存入到了 IOC 容器中,可直接使用;
- 默认自动配置路径:同包内的;
- 指定扫描其他包,在启动类加注解:
@ComponentScan
:修改value
orbasePackage
属性 指定组件扫描的路径;@Import
注解:- 导入普通类;
- 配置类;
ImportSelector
接口实现类,实现方法selectImports
,返回要导入类的字符串;
- 最常用:第三方依赖提供的
@Enable
注解,封装了@Import
。
@SpringBootApplication
:该注解标识在SpringBoot工程引导类上,SpringBoot中最最最重要的注解。该注解由三个部分组成:
- @SpringBootConfiguration:该注解与@Configuration注解作用相同,用来声明当前也是一个配置类。
- @ComponentScan:组件扫描,默认扫描当前引导类所在包及其子包。
- @EnableAutoConfiguration:SpringBoot实现自动化配置的核心注解。其中封装了
@Import
类,将配置类交给 IOC 容器中;
与 @Bean
结合使用的条件装配:@Conditional
:
- 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到SpringIOc容器中。
- 位置:方法、类
- @Conditional本身是一个父注解,派生出大量的子注解:
- @ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器。
- @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOc容器。
- @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。
自定义 starter
:
- 自定义公共组件,完成自动配置,配置起步依赖;
SSM框架讲解(史上最详细的文章)_ssm架构-CSDN博客
3.18
校赛出题验题。
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) P164~180.
统一拦截:
- 过滤器 Filter;
- 拦截器 Interceptor
过滤器:
-
实现过滤器类:
- 注解
@WebFilter
,配置拦截资源的路径; - 实现方法:
init
;doFilter
;destroy
。
- 注解
-
引导类加上
@ServletComponentScan
开启 Servlet 组件支持; -
顺序:放行前逻辑、放行、放行后逻辑;
-
拦截路径:
拦截路径 urlPatterns值 含义 拦截具体路径 /login 只有访问 /login 路径时,才会被拦截 目录拦截 /emps/* 访问/emps下的所有资源,都会被拦截 拦截所有 /* 访问所有资源,都会被拦截 -
过滤器链。
拦截器(Interceptor):
- Spring 中提供的动态拦截方法调用的机制;
- 定义拦截器,实现
HandlerInterceptor
接口; - 重写方法:
preHandle
(返回值表示放不放行)、postHandle``afterCompletion
; - 配置类注册拦截器:
@Configuration
;- 注入拦截器的实例;
- 重写接口
WebMvcConfigurer
; - 在其中配置拦截路径,需要拦截资源和不需要拦截的资源;
- 拦截路径:
/*
:匹配一级路径;/**
:匹配任意级路径;
- 与 Filter 区别:
- 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口;
- 拦截范围不同:过滤器Filter会拦截所有的资源(在服务器层面实现),而Interceptor只会拦截Spring环境中的资源。
全局异常处理器:
- 注解:
RestControllerAdvice
,用于向客户端返回 JSON 数据; - 注解:
@ExceptionHandler()
,捕获对应类型异常并处理;
Spring 事务:
- 注解
@Transactional
; - 一般放在 Service 层的方法、类、接口上;
- 执行了多次数据访问的方法上;
- 开启事务开关:
logging:
level:
org.springframework.jdbc.support,dbcTransactionManager: debug
- 在注解
@Transactional
中配置rollbackFor
属性,控制进行回滚的异常类型(默认仅回滚 RuntimeException)。 - 事务的传播行为(事务嵌套时,被嵌套事务的控制方式):
属性值 | 含义 |
---|---|
REQUIRED | 【默认值】需要事务,有则加入,无则创建新事务 |
REQUIRES_NEW | 需要新事务,无论有无,总是创建新事务 |
SUPPORTS | 支持事务,有则加入,无则在无事务状态中运行 |
NOT_SUPPORTED | 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务 |
MANDATORY | 必须有事务,否则抛异常 |
NEVER | 必须没事务,否则抛异常 |
... | ... |
AOP(AspectOrientedProgramming(面向切面编程、面向方面编程),其实就是面向特定方法编程)
- 动态代理是面向切面编程最主流的实现。而SpringAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
- Java动态代理详细讲解-使用方式及应用场景_动态代理有哪些 事物用的是哪种-CSDN博客
@Component
注解交给 IOC 容器;@Aspect
AOP 类注解;- 通知类型:
@Around
、@Before
、@After
(详见下方)……注解,使用切入点表达式,限定操作的原始方法; - 参数
ProceedingJoinPoint
类型,传入原始方法; Transactional
基于 AOP 实现。
一些概念:
- 连接点:
JoinPoint
,可以被AOP控制的方法(暗含方法执行时的相关信息)。 - 通知:
Advice
,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法) - 切入点:
PointCut
,匹配连接点的条件,通知仅会在切入点方法执行时被应用(使用切入点表达式限定)。 - 切面:
Aspect
,描述通知与切入点的对应关系(通知+切入点); - 目标对象:
Target
,通知所应用的对象。
AOP 执行流程:
- 实际运行对象实际上是基于 AOP 生成的,添加了通知的代理对象;
- 类似 Python 的装饰器。
通知类型,与切入点在目标方法中的执行顺序:
序号 | 通知类型 | 描述 |
---|---|---|
1 | @Around | 环绕通知,此注解标注的通知方法在目标方法前、后都被执行 |
2 | @Before | 前置通知,此注解标注的通知方法在目标方法前被执行 |
3 | @After | 后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行 |
4 | @AfterReturning | 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行 |
5 | @AfterThrowing | 异常后通知,此注解标注的通知方法发生异常后执行 |
@Around
环绕通知需要自己调用ProceedingJoinPoint.proceed()
来让原始方法执行,其他通知不需要考虑目标方法执行;@Around
环绕通知方法的返回值,必须指定为Object
,来接收原始方法的返回值。@Pointcut
注解,抽取公共切入点表达式以复用。
不同切面类,在同一目标方法中的执行顺序:
- 默认:不同切面类中,默认按照切面类的类名字母排序:
- 目标方法前的通知方法:字母排名靠前的先执行;
- 目标方法后的通知方法:字母排名靠前的后执行;
- 用
@Order(数字)
加在切面类上来控制顺序- 目标方法前的通知方法:数字小的先执行;
- 目标方法后的通知方法:数字小的后执行;
切入点表达式:
- 作用:主要用来决定项目中的哪些方法需要加入通知;
- 常见形式:
execution(访问修饰符?返回值包名.类名.?方法名(方法参数) throws异常?)
:根据方法的签名来匹配(?可省略);- throws 的异常类型为声明时,不是实际抛出的;
- 通配符:
*
:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分:execution(* com.*.service.*.update*(*))
;- 可使用逻辑运算符进行组合;
··
:多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数:execution(* com.itheima..DeptService.*(..))
;
@annotation(……)
,用于匹配标识有特定注解的方法。- 可自定义注解;
- 传入注解的全限制名。
连接点:
- 可在通知方法中获得目标方法的属性;
- 类名、方法名、传入参数、放行、返回值……
@Around
中参数类型只能用ProceedingJoinPoint
,其他只能用JoinPoint
;
配置文件(按优先级排序):
yaml
<yml
<properties
;- Java 系统属性;
- 命令行参数;
Bean 对象:
-
主动获取 bean 对象
getBean()
- 可根据名称、类型、带类型转换获取;
- 根据名称获取时,返回值为 Object 类型,需要强制类型转换;
-
bean 的作用域:
- 使用
@Scpoe
注解配置作用域; - Spring支持五种作用域,后三种在web环境才生效:
作用域 说明 singleton 容器内同一名称的 bean 只有一个实例(单例)(默认) prototype 每次使用该 bean 时会创建新的实例(非单例) request 每个请求范围内会创建新的实例(web环境中,了解) session 每个会话范围内会创建新的实例(web环境中,了解) application 每个应用范围内会创建新的实例(web环境中,了解) - 使用
-
@Lazy
:延迟初始化,第一次使用时才创建;
第三方 Bean:
- 将第三方库中的类交给 IOC 容器管理;
- 创建方法返回该类的实例,并对该方法使用注解
@Bean
即可。 - 若要管理的第三方bean对象,建议对这些bean进行集中分类配置,可以通过
@Configuration
注解声明一个配置类。 - 通过
@Bean
注解的name/value
属性指定bean
名称,如果未指定,默认是方法名; - 若第三方 bean 需要依赖其他 bean,在 bean 定义方法中设置形参即可自动装配。
3.17
组题:CSU-ACM2025 春季训练赛-第二场 题解 - Luckyblock - 博客园。
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) P148~P163.
本地存储:
- 不安全;
- 维护服务器麻烦;
OSS:
- 注册充钱;
- 根据官方实例,添加 SDK 依赖,根据 demo 将 OSS 集成到项目中即可。
- 在工程中创建工具类,并交给 IOC 容器管理。
配置文件:
- 用于配置常量等信息;
- 不在 java 源代码中配置信息,更改配置时不需要重新编译;
- 在 application.xxx 中定义;
- 声明常量时使用
@Value("${}")
单个注入配置。 - 配置文件类型,注意格式不同:
.XML
;.properties
;.yml
/.ymal
。
- 自动向 Bean 对象批量注入配置:
@Component
;@ConfigurationProperties (prefix = "...")
设定;- 保证全限定名一致;
会话(Session)技术:
- 会话:当用户通过浏览器访问一个 Web 应用时,从用户打开网页开始,到用户关闭网页或离开该应用的这段时间内,用户与服务器之间发生的所有请求和响应的集合就构成了一个会话;
- 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。
- 客户端:Cookie;
- 浏览器通过请求头
Cookie
发送 Cookie、服务端通过响应头Set-Cookie
设置。 - 优点:HTTP 自带;
- 缺点:移动端无法用,用户可禁用,不能跨域;
- 浏览器通过请求头
- 服务端:Session;
- 在服务端存储 Session,向浏览器返回对应 Cookie;
- 缺点:服务器集群无法直接使用,Cookie 的所有缺点。
- 令牌技术;
- 生成后发送并存储在客户端,每次请求都携带令牌;
- 支持多端,可解决集群,存储不一定在 Cookie;
- 需要自行实现。
- 客户端:Cookie;
JWT(JSON Web Tokens)
- 依赖;
- 结构:头(base64)、有效载荷(base64)、签名;
- 生成后返回给浏览器;
- 浏览器每次请求都会携带 token;
- 令牌校验:
- 校验时的签名密钥和生成时的必须配套;
Educational Codeforces Round 176 (Rated for Div. 2) - Luckyblock - 博客园。
3.16
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) P113~P147.
索引:
- 大大增加查询效率;
- 占用储存,降低修改效率。
MySQL的索引——索引的介绍及其数据结构B+树 & 索引的类型 & 索引的使用及其失效场景 & 相关名词解释_mysql的索引结构-CSDN博客
索引结构:
- 默认结构为 B+ Tree。
唯一约束:添加唯一索引;
聚集索引(主键索引,聚簇索引)- 非聚集索引
- 主键子段:自动创建聚集索引。
- 聚集索引 - 唯一索引
- 回表:非聚集索引叶节点存的是索引字段的数据和主键的值,还要在根据查出的主键再去聚集索引中查表;
- 索引下推:在非主键索引上的优化,可以有效减少回表。
联合(组合)索引;在多个字段上创建索引,遵循最左匹配原则;
- 最左前缀原则:联合索引时,查询条件必须从索引的最左边的字段开始匹配,才能有效利用索引;
- 覆盖索引:指一个索引包含了查询中需要的所有字段,可以直接通过索引获取到查询结果,而无需回表。
索引失效场景:
- 不满足最左前缀,查询条件跳过最左边的字段(跳过了中间字段可以部分利用索引);
- 范围查询之后;
- 索引字段做运算;
- 隐式类型转换;
- select * 无法使用覆盖索引;
- or分割的条件;
- 以%开头的Like模糊查询:使用覆盖索引可解决;
- order by 非主键时,可能。
Mybatis:
-
持久层(dao 层)框架,简化 JDBC 开发。
-
@Mapper
注解实现类,自动生成代理对象,交给 IOC 容器管理; -
使用
@Select
、@Delete
、@Insert
、@Update
等注解为方法添加 SQL 语句; -
方法返回值为影响的数据条数;
-
参数占位符:
#{}
:执行SQL时,会将#{..}替换为?,生成预编译SQL,会自动设置参数值。使用时机:参数传递;${}
:拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题。使用时机:对表名、列表进行动态设置。
-
@Options
- 插入时主键返回
@Options(keyProperty = "id", useGeneratedKeys = true)
;
- 插入时主键返回
-
数据封装:实体类属性名与列名相同;
- 无法封装的解决方案:
- SQL 中子段起别名;
@Result
注解手动映射;- 推荐:开启mybatis的驼峰命名自动映射开关:
mybatis.configuration.map-underscore-to-camel-case=true
- 无法封装的解决方案:
-
指定mybatis输出日志的位置,输出控制台:
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
; -
XML 映射文件:
- 同包同名;
- namespace 属性与 Mapper 接口全限定名一致;
- sql 的 id 与 Mapper 接口方法名一致,返回类型一致。
-
动态 SQL:
<if>
:条件成立则拼接;<where>
:子元素有内容则插入where
子句,并动态自动去除或保留子句开头的 and 或 or。<set>
:用于 update 中,在行首插入set
,并动态自动去除或保留子句最后的,
。<foreach>
:用于将一个可迭代对象转换为 SQL 中的集合。collection:遍历的集合,iten:遍历出来的元素,separator:分隔符,open:遍历开始前拼接的sQL片段,close:遍历结束后拼按的sQL片段。<sql id=>
和<include refid=>
:定义和引用可重用的 SQL 片段。
-
妈的做课设的时候嗯写 JDBC 太傻比了早知道这么牛逼的东西就爽死了。
预编译 SQL:
- 性能更高;
- 防止 SQL 注入,更安全;
@Test
配置 SQL 方言和数据库连接,在 IDE 中建立自动补全。
JDBC:
- 官方提供操作关系型数据库的API;
- 数据库厂商提供对应数据库的驱动jar包;
- 麻烦。
数据库连接池:
- 负责分配、管理数据库连接的容器;
- 如果没有,则每次都要重新建立连接,麻烦;
- 资源重用,提升系统响应速度,避免数据库连接遗漏;
DataSource
。
Lombok:通过注解自动生成构造器的 Java 类库;
注解 | 作用 |
---|---|
@Getter/@Setter | 为所有的属性提供get/set方法 |
@ToString | 会给类自动生成易阅读的 toString 方法 |
@EqualsAndHashCode | 根据类所拥有的非静态字段自动重写 equals 方法和 hashCode 方法 |
@Data | 提供了更综合的生成代码功能(@Getter + @Setter + @ToString + @EqualsAndHashCode) |
@NoArgsConstructor | 为实体类生成无参的构造器方法 |
@AllArgsConstructor | 为实体类生成除了static修饰的字段之外带有各参数的构造器方法。 |
RESTful
- 表述性状态转换,它是一种软件架构风格;
- 一文搞懂什么是RESTful API - 知乎
slf4j
- SLF4J的介绍与使用(有logback和log4j2的具体实现案例)-CSDN博客
- springboot项目默认使用 SLF4J 作为日志门面;
@Slf4j
。
RequestMapping
- 提取公共路径前缀;
- 一个完整的请求路径,应该是类上的
@RequestMapping
的 value 属性+方法上的@RequestMapping
的 value 属性。
分页查询:
- 前端提供分页起点与每页数量;
@RequestParam(defaultValue="")
设置请求参数默认值。
PageHelper 分页插件。
文件上传时,:
- 前端表单:
method = "post"
;enctype = "multipart/form-data"
;type="file"
。
- 服务端:
MultipartFile xxx
接收。- 一些方法:获取文件信息、转存到本地;
- 配置上传文件大小限制:
- 配置单个文件最大上传大小:
spring.servlet.multipart.max-file-size=10MB
; - 配置单个请求最大上传大小(一次请求可以上传多个文件):
spring.servlet.multipart.max-request-size=100MB
;
- 配置单个文件最大上传大小:
3.14
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) P61~P112
复习 HTTP、Tomcat。
SpringBoot 内置 Tomcat 作为默认的嵌入式服务器。
学习 postman。
SpringBoot 的请求响应。
请求参数的写法:
- 简单参数;
- 实体对象参数:规则:请求参数名与形参对象属性名相同;
- 数组:请求参数名与形参中数组变量名根同,可以直接使用数组封装;
- 集合:请求参数名与形参中集合变量名相同,通过@RequestParam绑定参数关系;
- 日期:使用@DateTimeFormat注解完成日期参数格式转换;
- JSON:JSON数据键名与形参对象属性名相同,定义对应类型形参即可接收参数,需要使用@RequestBody标识,可将 JSON 类型封装到实体;
- 路径:@PathVariable 注解,使用形参接收传入路径参数。
响应参数:
- @ResponseBody:位置:注解在Controller类上/方法,作用:将方法返回值直接响应,若返回值类型是实体对象/集合,转JSON格式响应;
- 自定义 Result 类统一响应结果:Result (code、msg、data)
分层解耦。
三层架构。复用性强,便于维护:
- controller:控制层,接收前端发送的请求,对请求进行处理,并响应数据。
- service:业务逻辑层,处理具体的业务逻辑。
- dao:数据访问层(DataAccess Object)(持久层),负责数据访问操作,包括数据的增、删、改、查。
高内聚低耦合,好。
- 内聚:软件中各个功能模块内部的功能联系。
- 耦合:衡量软件中各个层/模块之间的依赖、关联的程度。
直接实现分层架构,相邻层耦合。
SpringBoot 使用控制反转与依赖注入实现分层解耦:
- 控制反转:InversionOfControl,简称IOc。对象的创建控制权由程序自身转移到外部(容器),这种思想称为控制反转。
- 依赖注入:DependencyInjection,简称DI。容器为应用程序提供运行时,所依赖的资源,称之为依赖注入。
- Bean对象:IOc容器中创建、管理的对象,称之为bean。
实现控制反转与依赖注入:
- Service层及Dao层的实现类,交给IOc容器管理,使用如下注解:
- @Componert:声明bean的基础注解,不属于以下三类时,用此注解
- @Controller;
- @Service;
- @Repository。
- 为Controller及Service注入运行时,依赖的对象,使用 @Autowired 注解
- @SpringBootApplication具有包扫描作用,默认扫描当前包及其子包。
- @Autowired注解,默认是按照类型进行自动装配,如果同类型的bean存在多个:
- @Primary
- @Autowired +@Qualifier("bean的名称")
- @Resource(name="bean的名称")
复习 MYSQL。
where与having区别:
- 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过
- 判断条件不同:where不能对聚合函数进行判断,而having可以。
注意事项: - 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义。
- 执行顺序: where >聚合函数>having。
物理外键 - 逻辑外键
表的设计:
- 一对多:在多的一方添加外键,关联另外一方的主键,保证数据一致性完整性;
- 一对一:单表拆分,提高效率;任意一方设置外键关联另一方主键;
- 多对多:中间表,其中设置外键关联各表的主键。
多表查询:
- 内连接
- 外连接
- 子查询:
- 标量
- 列:in
- 行
- 表:临时表
自定义事务:
- 开启事务:start transaction;/begin;
- 提交事务:commit;
- 回滚事务:rollback;
事务:
- 事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。
- 四大特性
- 原子性:事务是不可分割的最小单元,要么全部成功,要么全部失败;
- 一致性:事务完成时,必须使所有的数据都保持一致状态;
- 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响;
- 持久性:事务一旦事务一旦提交或回滚,库中的数据的改变就是永久的。
幸亏上学期课设的时候比较认真还学了不少东西所以现在不至于变成无头苍蝇,感慨了。
3.13
黑马程序员JavaWeb开发教程,实现javaweb企业开发全流程(涵盖Spring+MyBatis+SpringMVC+SpringBoot等) P1~P60
复习 html+css+js。
Vue
3.12
黑马程序员DeepSeek+Cursor+Devbox+Sealos带你零代码搞定实战项目开发部署视频教程,基于AI完成项目的设计、开发、测试、联调、部署全流程
被生产力工具震撼了!
3.10~3.11
速通:《王道考研 - 操作系统》
3.8~3.9
速通:《王道考研 - 计算机网络》