Commit on 2025/04/09 周三 17:23:21.27
This commit is contained in:
parent
426cec0a76
commit
7dcaebcd75
File diff suppressed because it is too large
Load Diff
540
自学/Java笔记本.md
540
自学/Java笔记本.md
@ -79,128 +79,90 @@ IDEA快捷键:
|
||||
|
||||
1. 二进制:0b 八进制:0 十六进制:0x
|
||||
|
||||
|
||||
|
||||
2. 在 `System.out.println()` 方法中,"ln" 代表 "line",表示换行。因此,`println` 实际上是 "print line" 的缩写。这个方法会在输出文本后自动换行.
|
||||
|
||||
```java
|
||||
System.out.println("nihao "+1.3331); #Java 会自动将数值转换为字符串
|
||||
```
|
||||
```java
|
||||
System.out.println("nihao "+1.3331); #Java 会自动将数值转换为字符串
|
||||
```
|
||||
|
||||
当直接打印一个没有重写 `toString()` 方法的对象时,Java 默认会调用 `Object` 类的 `toString()` 方法,其输出格式通常为:
|
||||
|
||||
```java
|
||||
java.lang.Object@15db9742
|
||||
```
|
||||
|
||||
|
||||
|
||||
当打印重写`toString()` 方法的对象时:
|
||||
|
||||
```java
|
||||
class Person {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public Person(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{name='" + name + "', age=" + age + "}";
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Person person = new Person("Alice", 30);
|
||||
System.out.println(person); //会自动调用对象的 toString() 方法
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
Person{name='Alice', age=30}
|
||||
```
|
||||
|
||||
|
||||
|
||||
3. 一维数组创建:
|
||||
|
||||
```java
|
||||
// 方式1:先声明,再指定长度(默认值为0、null等)
|
||||
int[] arr1 = new int[10]; // 创建一个长度为10的int数组
|
||||
|
||||
// 方式2:使用初始化列表直接创建数组
|
||||
int[] arr2 = {1, 2, 3, 4, 5}; // 创建并初始化一个包含5个元素的int数组
|
||||
|
||||
String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
|
||||
|
||||
// 方式3:结合new关键字和初始化列表创建数组(常用于明确指定类型时)
|
||||
int[] arr3 = new int[]{1, 2, 3, 4, 5}; // 与方式2效果相同
|
||||
```
|
||||
```java
|
||||
// 方式1:先声明,再指定长度(默认值为0、null等)
|
||||
int[] arr1 = new int[10]; // 创建一个长度为10的int数组
|
||||
|
||||
// 方式2:使用初始化列表直接创建数组
|
||||
int[] arr2 = {1, 2, 3, 4, 5}; // 创建并初始化一个包含5个元素的int数组
|
||||
|
||||
String[] strs = {"eat", "tea", "tan", "ate", "nat", "bat"};
|
||||
|
||||
// 方式3:结合new关键字和初始化列表创建数组(常用于明确指定类型时)
|
||||
int[] arr3 = new int[]{1, 2, 3, 4, 5}; // 与方式2效果相同
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
4. 字符串创建
|
||||
|
||||
```java
|
||||
String str = "Hello, World!"; //(1)直接赋值
|
||||
|
||||
String str = new String("Hello, World!"); //使用 new 关键字
|
||||
|
||||
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
|
||||
String str = new String(charArray); //通过字符数组创建
|
||||
```
|
||||
```java
|
||||
String str = "Hello, World!"; //(1)直接赋值
|
||||
|
||||
String str = new String("Hello, World!"); //使用 new 关键字
|
||||
|
||||
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
|
||||
String str = new String(charArray); //通过字符数组创建
|
||||
```
|
||||
|
||||
|
||||
|
||||
5. switch-case
|
||||
|
||||
```java
|
||||
public class SwitchCaseExample {
|
||||
public static void main(String[] args) {
|
||||
// 定义一个 int 类型变量,作为 switch 的表达式
|
||||
int day = 3;
|
||||
String dayName;
|
||||
|
||||
// 根据 day 的值执行相应的分支
|
||||
switch(day) {
|
||||
case 1:
|
||||
dayName = "Monday"; // 当 day 为 1 时
|
||||
break; // 结束当前 case
|
||||
case 2:
|
||||
dayName = "Tuesday"; // 当 day 为 2 时
|
||||
break;
|
||||
case 3:
|
||||
dayName = "Wednesday"; // 当 day 为 3 时
|
||||
break;
|
||||
case 4:
|
||||
dayName = "Thursday"; // 当 day 为 4 时
|
||||
break;
|
||||
case 5:
|
||||
dayName = "Friday"; // 当 day 为 5 时
|
||||
break;
|
||||
case 6:
|
||||
dayName = "Saturday"; // 当 day 为 6 时
|
||||
break;
|
||||
case 7:
|
||||
dayName = "Sunday"; // 当 day 为 7 时
|
||||
break;
|
||||
default:
|
||||
// 如果 day 不在 1 到 7 之间
|
||||
dayName = "Invalid day";
|
||||
}
|
||||
|
||||
// 输出最终结果
|
||||
System.out.println("The day is: " + dayName);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
```java
|
||||
public class SwitchCaseExample {
|
||||
public static void main(String[] args) {
|
||||
// 定义一个 int 类型变量,作为 switch 的表达式
|
||||
int day = 3;
|
||||
String dayName;
|
||||
|
||||
// 根据 day 的值执行相应的分支
|
||||
switch(day) {
|
||||
case 1:
|
||||
dayName = "Monday"; // 当 day 为 1 时
|
||||
break; // 结束当前 case
|
||||
case 2:
|
||||
dayName = "Tuesday"; // 当 day 为 2 时
|
||||
break;
|
||||
case 3:
|
||||
dayName = "Wednesday"; // 当 day 为 3 时
|
||||
break;
|
||||
case 4:
|
||||
dayName = "Thursday"; // 当 day 为 4 时
|
||||
break;
|
||||
case 5:
|
||||
dayName = "Friday"; // 当 day 为 5 时
|
||||
break;
|
||||
case 6:
|
||||
dayName = "Saturday"; // 当 day 为 6 时
|
||||
break;
|
||||
case 7:
|
||||
dayName = "Sunday"; // 当 day 为 7 时
|
||||
break;
|
||||
default:
|
||||
// 如果 day 不在 1 到 7 之间
|
||||
dayName = "Invalid day";
|
||||
}
|
||||
|
||||
// 输出最终结果
|
||||
System.out.println("The day is: " + dayName);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
#### Java传参方式
|
||||
|
||||
@ -897,6 +859,23 @@ protected static volatile int counter; #定义成员变量
|
||||
|
||||
|
||||
|
||||
#### 全限定名
|
||||
|
||||
全限定名(Fully Qualified Name,简称 FQN)指的是一个类或接口在 Java 中的完整名称,包括它所在的包名。例如:
|
||||
|
||||
- 对于类 `Integer`,其全限定名是 `java.lang.Integer`。
|
||||
- 对于自定义的类 `DeptServiceImpl`,如果它位于包 `edu.zju.zy123.service.impl` 中,那么它的全限定名就是 `edu.zju.zy123.service.impl.DeptServiceImpl`。
|
||||
|
||||
使用全限定名可以消除歧义,确保指定的类型在整个项目中唯一无误。
|
||||
|
||||
使用场景:
|
||||
|
||||
Spring AOP 的 Pointcut 表达式
|
||||
|
||||
MyBatis的XML映射文件的**namespace属性**
|
||||
|
||||
|
||||
|
||||
### JAVA面向对象
|
||||
|
||||
#### **JAVA**三大特性
|
||||
@ -1046,7 +1025,8 @@ class Dog extends Animal {
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#### 接口
|
||||
|
||||
@ -1291,13 +1271,15 @@ public class SomeException extends Exception {
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 好用的方法
|
||||
|
||||
### toString()
|
||||
|
||||
Arrays.toString()
|
||||
**Arrays.toString()**转一维数组
|
||||
|
||||
作用:方便地输出数组。
|
||||
**Arrays.deepToString()**转二维数组
|
||||
这个方法是是用来将数组转换成String类型输出的,入参可以是long,float,double,int,boolean,byte,object 型的数组。
|
||||
|
||||
```java
|
||||
@ -1319,6 +1301,45 @@ public class Main {
|
||||
System.out.println("二维数组输出: " + Arrays.deepToString(twoD));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**自定义对象的`toString()` 方法**
|
||||
|
||||
每个 Java 对象默认都有 `toString()` 方法(可以根据需要覆盖)
|
||||
|
||||
当直接打印一个没有重写 `toString()` 方法的对象时,其输出格式通常为:
|
||||
|
||||
```java
|
||||
java.lang.Object@15db9742
|
||||
```
|
||||
|
||||
当打印重写`toString()` 方法的对象时:
|
||||
|
||||
```java
|
||||
class Person {
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
public Person(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Person{name='" + name + "', age=" + age + "}";
|
||||
}
|
||||
}
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
Person person = new Person("Alice", 30);
|
||||
System.out.println(person); //会自动调用对象的 toString() 方法
|
||||
//Person{name='Alice', age=30}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@ -1330,7 +1351,7 @@ public class Main {
|
||||
|
||||
**类路径**是JVM在运行时用来查找类文件和资源文件的一组目录或JAR包。在许多项目(例如Maven或Gradle项目)中,`src/main/resources`目录下的内容在编译时会被复制到输出目录(如`target/classes`),`src/main/java` 下编译后的 class 文件也会放到这里。
|
||||
|
||||
```java
|
||||
```text
|
||||
MyProject/
|
||||
├── src/
|
||||
│ └── main/
|
||||
@ -1338,16 +1359,26 @@ MyProject/
|
||||
│ └── com/
|
||||
│ └── example/
|
||||
│ └── Main.java
|
||||
└── resources/
|
||||
├── emp.xml
|
||||
└── static/
|
||||
└── tt.img
|
||||
├── resources/
|
||||
│ ├── emp.xml
|
||||
│ └── static/
|
||||
│ └── tt.img
|
||||
└── target/
|
||||
└── classes/
|
||||
├── com/
|
||||
│ └── example/
|
||||
│ └── Main.class
|
||||
├── emp.xml
|
||||
└── static/
|
||||
└── tt.img
|
||||
|
||||
```
|
||||
|
||||
------------
|
||||
|
||||
```java
|
||||
// 获取 resources 根目录下的 emp.xml 文件路径
|
||||
String empFile = this.getClass().getClassLoader().getResource("emp.xml").getFile();
|
||||
String empFileUrl = this.getClass().getClassLoader().getResource("emp.xml").getFile();
|
||||
|
||||
// 获取 resources/static 目录下的 tt.img 文件路径
|
||||
URL resourceUrl = getClass().getClassLoader().getResource("static/tt.img");
|
||||
@ -1401,6 +1432,8 @@ public class Test1Class{
|
||||
|
||||
**2.获取类的构造器**
|
||||
|
||||
- 定义类
|
||||
|
||||
```java
|
||||
public class Cat{
|
||||
private String name;
|
||||
@ -1593,21 +1626,188 @@ public class FieldReflectionTest {
|
||||
|
||||
|
||||
|
||||
### Junit 单元测试
|
||||
### 注解
|
||||
|
||||

|
||||
在 Java 中,注解用于给程序元素(类、方法、字段等)**添加元数据**,这些元数据可被编译器、工具或运行时**反射读取**,以实现配置、检查、代码生成以及框架支持(如依赖注入、AOP 等)功能,而不直接影响代码的业务逻辑。
|
||||
|
||||
比如:Junit框架的@Test注解可以用在方法上,用来标记这个方法是测试方法,被`@Test`标记的方法能够被Junit框架执行。
|
||||
|
||||
再比如:@Override注解可以用在方法上,用来标记这个方法是重写方法,被`@Override`注解标记的方法能够被IDEA识别进行语法检查。
|
||||
|
||||
#### 使用注解
|
||||
|
||||
**定义注解**
|
||||
|
||||
使用 `@interface` 定义注解
|
||||
|
||||
```java
|
||||
@Test
|
||||
public void testListUser(){
|
||||
List<User>list=userMapper.list();
|
||||
for(User user:list){
|
||||
System.out.println(user);
|
||||
}
|
||||
}
|
||||
// 定义注解
|
||||
@Retention(RetentionPolicy.RUNTIME) // 定义注解的生命周期
|
||||
@Target(ElementType.METHOD) // 定义注解可以应用的Java元素类型
|
||||
public @interface MyAnnotation {
|
||||
// 定义注解的元素(属性)
|
||||
String description() default "This is a default description";
|
||||
int value() default 0;
|
||||
}
|
||||
```
|
||||
|
||||
写了@Test注解,那么该测试函数就可以直接运行!若一个测试类中写了多个测试方法,可以全部执行!
|
||||
**元注解**
|
||||
|
||||
**是修饰注解的注解**。
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME) //指定注解的生命周期,即在运行时有效,可用于反射等用途。
|
||||
|
||||
@Target(ElementType.METHOD) //方法上的注解
|
||||
|
||||
@Target(ElementType.TYPE) //类上的注解(包含类、接口、枚举等类型)
|
||||
|
||||
|
||||
|
||||
**简化使用**:如果注解中只有一个属性需要设置,而且该属性名为 `value`,则在使用时可以省略属性名
|
||||
|
||||
```java
|
||||
@MyAnnotation(5) // 等同于 @MyAnnotation(value = 5)
|
||||
public void someMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
当需要为注解的多个属性赋值时,传参必须指明属性名称:
|
||||
|
||||
```java
|
||||
@MyAnnotation(value = 5, description = "Specific description")
|
||||
public void anotherMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果所有属性都使用默认值,可以直接使用注解而不传入任何参数:
|
||||
|
||||
```java
|
||||
@MyAnnotation
|
||||
public void anotherMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### 解析注解
|
||||
|
||||
```java
|
||||
public class MyClass {
|
||||
@MyAnnotation(value = "specific value")
|
||||
public void myMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
```java
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class AnnotationReader {
|
||||
|
||||
public static void main(String[] args) throws NoSuchMethodException {
|
||||
// 获取MyClass的Class对象
|
||||
Class<MyClass> obj = MyClass.class;
|
||||
|
||||
// 获取myMethod方法的Method对象
|
||||
Method method = obj.getMethod("myMethod");
|
||||
|
||||
// 获取方法上的MyAnnotation注解实例
|
||||
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
|
||||
|
||||
if (annotation != null) {
|
||||
// 输出注解的value值
|
||||
System.out.println("注解的value: " + annotation.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
快速检查某个注解是否存在于`method` 上
|
||||
|
||||
```java
|
||||
if (method.isAnnotationPresent(MyAnnotation.class)) {
|
||||
// 如果存在MyAnnotation注解,则执行相应逻辑
|
||||
}
|
||||
```
|
||||
|
||||
检查方法 `method` 上是否存在 `MyAnnotation` 注解。如果存在,就返回该注解的实例,否则返回 `null`
|
||||
|
||||
```java
|
||||
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Junit 单元测试
|
||||
|
||||
**步骤**
|
||||
|
||||
**1.导入依赖**
|
||||
将 JUnit 框架的 jar 包添加到项目中(注意:IntelliJ IDEA 默认集成了 JUnit,无需手动导入)。
|
||||
|
||||
**2.编写测试类**
|
||||
|
||||
- 为待测业务方法创建对应的测试类。
|
||||
- 测试类中定义测试方法,要求方法必须为 `public` 且返回类型为 `void`。
|
||||
|
||||
**3.添加测试注解**
|
||||
在测试方法上添加 `@Test` 注解,确保 JUnit 能自动识别并执行该方法。
|
||||
|
||||
**4.运行测试**
|
||||
在测试方法上右键选择“JUnit运行”。
|
||||
|
||||
- 测试通过显示绿色标志;
|
||||
- 测试失败显示红色标志。
|
||||
|
||||
```java
|
||||
public class UserMapperTest {
|
||||
@Test
|
||||
public void testListUser() {
|
||||
UserMapper userMapper = new UserMapper();
|
||||
List<User> list = userMapper.list();
|
||||
Assert.assertNotNull("User list should not be null", list);
|
||||
list.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**注意**,如果需要使用**依赖注入**,需要在测试类上加`@SpringBootTest`注解
|
||||
|
||||
它会启动 Spring 应用程序上下文,并在测试期间模拟运行整个 Spring Boot 应用程序。这意味着你可以在集成测试中使用 Spring 的各种功能,例如**自动装配、依赖注入、配置加载**等
|
||||
|
||||
```java
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest
|
||||
public class UserMapperTest {
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Test
|
||||
public void testListUser() {
|
||||
List<User> list = userMapper.list();
|
||||
Assert.assertNotNull("User list should not be null", list);
|
||||
list.forEach(System.out::println);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
写了`@Test`注解,那么该测试函数就可以直接运行!若一个测试类中写了多个测试方法,可以全部执行!
|
||||
|
||||

|
||||
|
||||
@ -1654,98 +1854,6 @@ public class AnnotationTest4 {
|
||||
|
||||
|
||||
|
||||
### 注解
|
||||
|
||||
定义:
|
||||
|
||||
```java
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
// 定义注解
|
||||
@Retention(RetentionPolicy.RUNTIME) // 定义注解的生命周期
|
||||
@Target(ElementType.METHOD) // 定义注解可以应用的Java元素类型
|
||||
public @interface MyAnnotation {
|
||||
// 定义注解的元素(属性)
|
||||
String description() default "This is a default description";
|
||||
int value() default 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@Target(ElementType.METHOD) //方法上的注解
|
||||
|
||||
@Target(ElementType.CLASS) //类上的注解
|
||||
|
||||
|
||||
|
||||
**简化使用**:当注解只有一个元素需要设置时,且该元素的名字是`value`,在使用该注解时可以不用显式地指定元素名
|
||||
|
||||
```java
|
||||
@MyAnnotation(5) // 等同于 @MyAnnotation(value = 5)
|
||||
public void someMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
如果要同时设置`description`,则不能省略元素名:
|
||||
|
||||
```java
|
||||
@MyAnnotation(value = 5, description = "Specific description")
|
||||
public void anotherMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
**获得注解上的value:**反射
|
||||
|
||||
```java
|
||||
public class MyClass {
|
||||
|
||||
@MyAnnotation(value = "specific value")
|
||||
public void myMethod() {
|
||||
// 方法实现
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```java
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class AnnotationReader {
|
||||
|
||||
public static void main(String[] args) throws NoSuchMethodException {
|
||||
// 获取MyClass的Class对象
|
||||
Class<MyClass> obj = MyClass.class;
|
||||
|
||||
// 获取myMethod方法的Method对象
|
||||
Method method = obj.getMethod("myMethod");
|
||||
|
||||
// 获取方法上的MyAnnotation注解实例
|
||||
MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
|
||||
|
||||
if (annotation != null) {
|
||||
// 输出注解的value值
|
||||
System.out.println("注解的value: " + annotation.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 对象拷贝属性
|
||||
|
||||
```java
|
||||
|
@ -995,7 +995,7 @@ Joe主题:https://github.com/HaoOuBa/Joe
|
||||
|
||||
[Joe再续前缘主题 - 搭建本站同款网站 - 易航博客](https://blog.yihang.info/archives/18.html)
|
||||
|
||||
修改文章详情页的上方信息:
|
||||
自定义文章详情页的上方信息(如更新日期/文章字数第):
|
||||
`typecho/usr/themes/Joe/module/single/batten.php`
|
||||
|
||||
```php
|
||||
@ -1044,6 +1044,32 @@ if (!defined('__TYPECHO_ROOT_DIR__')) {
|
||||
</div>
|
||||
```
|
||||
|
||||
|
||||
|
||||
修改代码块背景色:
|
||||
|
||||
`typecho/usr/themes/Joe/assets/css/joe.global.css`
|
||||
|
||||
```css
|
||||
.joe_detail__article code:not([class]) {
|
||||
border-radius: var(--radius-inner, 4px); /* 可以设置一个默认值 */
|
||||
background: #f5f5f5; /* 稍微偏灰的背景色 */
|
||||
color: #000000; /* 黑色字体 */
|
||||
padding: 2px 6px; /* 内边距可以适当增大 */
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
word-break: break-word;
|
||||
font-weight: normal;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
white-space: pre-wrap; /* 保持代码换行 */
|
||||
font-size: 0.875em;
|
||||
margin-inline-start: 0.25em;
|
||||
margin-inline-end: 0.25em;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
大坑:{x}会显示为勾选框,无法正常进行latex公式解析,因为`typecho/usr/themes/Joe/public/short.php`中设置了短代码替换,**在文章输出前**对 `$content` 中的特定标记或短代码进行搜索和替换,从而实现一系列自定义功能。现已全部注释。
|
||||
|
||||
|
||||
|
@ -68,11 +68,21 @@ if (flag == false) { //更常用!
|
||||
|
||||
`Integer.toString(int i)`:将 `int` 转换为字符串。
|
||||
|
||||
`Integer.compare(int a,int b)` 比较a和b的大小,内部实现:
|
||||
|
||||
```
|
||||
public static int compare(int x, int y) {
|
||||
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
||||
}
|
||||
```
|
||||
|
||||
避免了 **整数溢出** 的风险,在排序中建议使用`Integer.compare(int a,int b)`代替 `a-b`
|
||||
|
||||
|
||||
|
||||
### 常用数据结构
|
||||
|
||||
#### `String`
|
||||
#### String
|
||||
|
||||
子串:字符串中**连续的一段字符**。
|
||||
|
||||
@ -107,7 +117,7 @@ String sortedStr = new String(charArray);
|
||||
|
||||
|
||||
|
||||
#### `StringBuffer`
|
||||
#### StringBuffer
|
||||
|
||||
`StringBuffer` 是 Java 中用于操作可变字符串的类
|
||||
|
||||
@ -162,7 +172,7 @@ sb.setLength(0);
|
||||
|
||||
|
||||
|
||||
#### `HashMap`
|
||||
#### HashMap
|
||||
|
||||
- 基于哈希表实现,查找、插入和删除的平均时间复杂度为 O(1)。
|
||||
- 不保证元素的顺序。
|
||||
@ -220,7 +230,7 @@ visited[i][j] = true;
|
||||
|
||||
|
||||
|
||||
#### `HashSet`
|
||||
#### HashSet
|
||||
|
||||
- 基于哈希表实现,查找、插入和删除的平均时间复杂度为 O(1)。
|
||||
|
||||
@ -263,7 +273,7 @@ visited[i][j] = true;
|
||||
|
||||
|
||||
|
||||
#### `PriorityQueue`
|
||||
#### PriorityQueue
|
||||
|
||||
- 基于优先堆(最小堆或最大堆)实现,元素按优先级排序。
|
||||
- **默认是最小堆**,即队首元素是最小的。 `new PriorityQueue<>(Comparator.reverseOrder());`定义最大堆
|
||||
@ -501,7 +511,7 @@ class MinHeap {
|
||||
|
||||
|
||||
|
||||
#### **`ArrayList`**
|
||||
#### **ArrayList**
|
||||
|
||||
- 基于数组实现,支持动态扩展。
|
||||
- 访问元素的时间复杂度为 O(1),在末尾插入和删除的时间复杂度为 O(1)。
|
||||
@ -597,7 +607,7 @@ for (int i = 0; i < list.size(); i++) {
|
||||
|
||||
|
||||
|
||||
#### **`数组(Array)`**
|
||||
#### **数组(Array)**
|
||||
|
||||
数组是一种固定长度的数据结构,用于存储相同类型的元素。数组的特点包括:
|
||||
|
||||
@ -688,7 +698,7 @@ Arrays.fill(memo, -1);
|
||||
|
||||
|
||||
|
||||
#### `二维数组`
|
||||
#### 二维数组
|
||||
|
||||
```java
|
||||
int rows = 3;
|
||||
@ -724,7 +734,7 @@ public void setZeroes(int[][] matrix) {
|
||||
|
||||
|
||||
|
||||
#### `Queue`
|
||||
#### Queue
|
||||
|
||||
队尾插入,队头取!
|
||||
|
||||
@ -761,7 +771,7 @@ public class QueueExample {
|
||||
```
|
||||
|
||||
|
||||
#### `Deque`(双端队列+栈)
|
||||
#### Deque(双端队列+栈)
|
||||
|
||||
支持在队列的两端(头和尾)进行元素的插入和删除。这使得 Deque 既能作为队列(FIFO)又能作为栈(LIFO)使用。
|
||||
|
||||
@ -847,7 +857,7 @@ public class DequeExample {
|
||||
|
||||
|
||||
|
||||
#### `Iterator`
|
||||
#### Iterator
|
||||
|
||||
- **`HashMap`、`HashSet`、`ArrayList` 和 `PriorityQueue`** 都实现了 `Iterable` 接口,支持 `iterator()` 方法。
|
||||
|
||||
@ -1135,9 +1145,9 @@ public class IntervalSort {
|
||||
// 自定义比较器,先比较第一个元素,如果相等再比较第二个元素
|
||||
Arrays.sort(intervals, (a, b) -> {
|
||||
if (a[0] != b[0]) {
|
||||
return a[0] - b[0];
|
||||
return Integer.compare(a[0], b[0]);
|
||||
} else {
|
||||
return a[1] - b[1];
|
||||
return Integer.compare(a[1], b[1]);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1123,8 +1123,6 @@ public class OrderTask {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Websocket
|
||||
|
||||
WebSocket 是基于 TCP 的一种新的**网络协议**。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建**持久性**的连接, 并进行**双向**数据传输。
|
||||
|
157
自学/草稿.md
157
自学/草稿.md
@ -1,53 +1,144 @@
|
||||
- 当然可以!下面是你可以直接记到笔记里的内容:
|
||||
下面从架构、扩展性、安全性、管理成本等几个维度,对 **Session** 和 **JWT** 进行对比,帮助你根据场景选择合适的方案。
|
||||
|
||||
------
|
||||
------
|
||||
|
||||
### 🧠 题型:**Top K 高频元素**(LeetCode 347)
|
||||
## 一、基本原理
|
||||
|
||||
**题目描述**:给定一个整数数组 `nums` 和一个整数 `k`,返回出现频率最高的前 `k` 个元素,返回顺序可以任意。
|
||||
| 特性 | Session | JWT(JSON Web Token) |
|
||||
| -------- | ------------------------------------ | ----------------------------------------------- |
|
||||
| 存储方式 | 服务端存储会话数据(如内存、Redis) | 客户端存储完整的令牌(通常在 Header 或 Cookie) |
|
||||
| 标识方式 | 客户端持有一个 Session ID | 客户端持有一个自包含的 Token |
|
||||
| 状态管理 | 有状态(Stateful),服务器要维护会话 | 无状态(Stateless),服务器不存会话 |
|
||||
|
||||
------
|
||||
------
|
||||
|
||||
### 📌 解法一:大根堆(最大堆)
|
||||
## 二、对比分析
|
||||
|
||||
**思路**:
|
||||
### 1. 架构与扩展性
|
||||
|
||||
1. 使用 `HashMap` 统计每个元素的出现频率。
|
||||
2. 构建一个**大根堆**(`PriorityQueue` + 自定义比较器),根据频率降序排列。
|
||||
3. 将所有元素加入堆中,**弹出前 `k` 个元素**即为答案。
|
||||
- **Session**
|
||||
- 单体应用:内存中维护 Map<sessionId, userData>,简单易用。
|
||||
- 分布式/集群:需要共享 Session(如 Redis、数据库、Sticky Session),增加运维成本。
|
||||
- **JWT**
|
||||
- 无状态:令牌自带用户信息及签名,服务器只需校验签名即可,无需存储。
|
||||
- 分布式友好:各节点只要共享签名密钥(或公钥),即可校验,无需集中存储。
|
||||
|
||||
**适合场景**:
|
||||
### 2. 性能
|
||||
|
||||
- 实现简单,适用于对全部元素排序后取前 `k` 个。
|
||||
- 时间复杂度:**O(n log n)**,因为需要将所有 `n` 个元素都加入堆。
|
||||
- **Session**
|
||||
- 每次请求都要从存储(内存/Redis/DB)读取会话数据,IO 成本或网络开销。
|
||||
- 并发高时,集中式 Session 存储可能成为瓶颈。
|
||||
- **JWT**
|
||||
- 校验签名(HMAC 或 RSA)为 CPU 操作,无网络开销,性能开销较小。
|
||||
- 但 Token 通常更大(包含多段 Base64),每次请求都要传输,带宽略增。
|
||||
|
||||
------
|
||||
### 3. 安全性
|
||||
|
||||
### 📌 解法二:小根堆(最小堆)
|
||||
- **Session**
|
||||
- 会话数据保存在服务器端,客户端只能拿到 Session ID,敏感数据不暴露。
|
||||
- 可在服务器端随时销毁或更新 Session(强制登出、权限变更即时生效)。
|
||||
- **JWT**
|
||||
- 令牌自包含所有声明(claims),如果存敏感数据需加密(JWE),否则仅签名(JWS)也可能泄露信息。
|
||||
- 无法主动撤销(除非做黑名单),需要控制有效期并结合“刷新令牌”机制。
|
||||
|
||||
**思路**:
|
||||
### 4. 可控性与管理
|
||||
|
||||
1. 使用 `HashMap` 统计频率。
|
||||
2. 构建一个**小根堆**,堆中仅保存前 `k` 个高频元素。
|
||||
3. 遍历每个元素:
|
||||
- 如果堆未满,直接加入。
|
||||
- 如果当前元素频率大于堆顶(最小频率),则弹出堆顶,加入当前元素。
|
||||
4. 最终堆中保存的就是前 `k` 个高频元素。
|
||||
- **Session**
|
||||
- **可控性强**:服务器可随时作废 Session,适合需要即时注销、权限动态调整的场景。
|
||||
- 过期策略灵活:可按用户、按应用统一配置。
|
||||
- **JWT**
|
||||
- **可控性弱**:Token 一旦签发,在到期前无法从服务器强制失效(除非额外维护黑名单)。
|
||||
- 需要设计“短生命周期 + 刷新令牌”模式,增加实现复杂度。
|
||||
|
||||
**适合场景**:
|
||||
### 5. 跨域与移动端
|
||||
|
||||
- 当 `k ≪ n` 时效率更高。
|
||||
- 时间复杂度:**O(n log k)**,因为堆中最多维护 `k` 个元素。
|
||||
- **Session**
|
||||
- 依赖 Cookie(同源策略),跨域或移动端(原生 App)使用受限。
|
||||
- 跨域时需配合 CORS + `withCredentials`,且浏览器必须支持并开启 Cookie。
|
||||
- **JWT**
|
||||
- 与 HTTP 协议无关,既可放在 Authorization 头,也可放在 URL、LocalStorage,移动端/第三方客户端更友好。
|
||||
- 只要客户端能发送 HTTP Header,就能携带 Token。
|
||||
|
||||
------
|
||||
### 6. 实现复杂度
|
||||
|
||||
### ✅ 总结对比:
|
||||
- **Session**
|
||||
- 框架通常开箱即用(如 Spring Session),开发者只需开启即可。
|
||||
- 自动处理过期、失效,管理简单。
|
||||
- **JWT**
|
||||
- 需要设计签名算法、密钥管理、过期策略、刷新机制、黑名单等。
|
||||
- 容易因配置不当造成安全漏洞(算法降级、密钥泄露、Token 劫持等)。
|
||||
|
||||
| 方法 | 适合场景 | 时间复杂度 | 空间复杂度 |
|
||||
| ------ | --------------- | ---------- | ---------- |
|
||||
| 大根堆 | k ≈ n,简单易写 | O(n log n) | O(n) |
|
||||
| 小根堆 | k ≪ n,更高效 | O(n log k) | O(n) |
|
||||
------
|
||||
|
||||
------
|
||||
## 三、何时选用
|
||||
|
||||
需要我再写成代码模板笔记也可以,随时说!
|
||||
| 场景类型 | 推荐方案 | 原因 |
|
||||
| --------------------------- | -------- | ---------------------------------------------------------- |
|
||||
| 单体 Web 应用、后台管理系统 | Session | 简单、可控、安全性高,框架支持完善。 |
|
||||
| 分布式微服务、无状态 API | JWT | 无需集中存储,易扩展;移动端/第三方客户端友好。 |
|
||||
| 高度安全、需即时失效场景 | Session | 可随时在服务器端销毁会话,确保强制登出或权限变更即时生效。 |
|
||||
| 跨域或多端(Web + App) | JWT | Token 可在多种客户端轻松传递,无需依赖浏览器 Cookie。 |
|
||||
|
||||
------
|
||||
|
||||
## 四、示例代码
|
||||
|
||||
### Session 示例(Spring Boot)
|
||||
|
||||
```java
|
||||
// 登录时创建 Session
|
||||
@PostMapping("/login")
|
||||
public String login(HttpServletRequest req, @RequestParam String user, @RequestParam String pass) {
|
||||
if (authService.verify(user, pass)) {
|
||||
req.getSession().setAttribute("userId", user);
|
||||
return "登录成功";
|
||||
}
|
||||
return "登录失败";
|
||||
}
|
||||
|
||||
// 受保护资源
|
||||
@GetMapping("/profile")
|
||||
public User profile(HttpServletRequest req) {
|
||||
String userId = (String) req.getSession().getAttribute("userId");
|
||||
return userService.findById(userId);
|
||||
}
|
||||
```
|
||||
|
||||
### JWT 示例(Spring Boot + jjwt)
|
||||
|
||||
```java
|
||||
// 生成 Token
|
||||
@PostMapping("/login")
|
||||
public String login(@RequestParam String user, @RequestParam String pass) {
|
||||
if (authService.verify(user, pass)) {
|
||||
return Jwts.builder()
|
||||
.setSubject(user)
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 3600_000))
|
||||
.signWith(SignatureAlgorithm.HS256, secretKey)
|
||||
.compact();
|
||||
}
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
|
||||
// 过滤器中校验
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) {
|
||||
String token = req.getHeader("Authorization");
|
||||
if (token != null && token.startsWith("Bearer ")) {
|
||||
String user = Jwts.parser().setSigningKey(secretKey)
|
||||
.parseClaimsJws(token.substring(7))
|
||||
.getBody().getSubject();
|
||||
// 将 user 存入 SecurityContext …
|
||||
}
|
||||
chain.doFilter(req, res);
|
||||
}
|
||||
```
|
||||
|
||||
------
|
||||
|
||||
## 五、结论
|
||||
|
||||
- **Session**:上手简单、安全可控,适合绝大多数传统 Web 应用。
|
||||
- **JWT**:更灵活、易扩展,适合分布式架构、多端场景,但需要更复杂的设计与安全防护。
|
||||
|
||||
根据你的项目架构、团队经验和安全需求,选择最合适的方案即可。
|
Loading…
x
Reference in New Issue
Block a user