Skip to content

Commit

Permalink
wang, feat: redis
Browse files Browse the repository at this point in the history
  • Loading branch information
lbwanga committed Sep 18, 2024
1 parent db00023 commit 9fe2195
Show file tree
Hide file tree
Showing 69 changed files with 3,556 additions and 64 deletions.
145 changes: 88 additions & 57 deletions docs/Java/Java.md
Original file line number Diff line number Diff line change
Expand Up @@ -408,17 +408,7 @@ finalize

**final数据**

许多程序设计语言都有自己的办法告诉编译器某个数据是“常数”。常数主要应用于下述两个方面:

(1) 编译期常数,它永远不会改变

(2) 在运行期初始化的一个值,我们不希望它发生变化

对于编译期的常数,编译器(程序)可将常数值“封装”到需要的计算过程里。也就是说,计算可在编译期间提前执行,从而节省运行时的一些开销。在 Java 中,这些形式的常数必须属于基本数据类型(Primitives),而且要用 final 关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值。

无论static 还是 final 字段,都只能存储一个数据,而且不得改变。

对于基本数据类型,final 会将值变成一个常数;但对于对象句柄,final 会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java 对此未提供任何手段,可将一个对象直接变成一个常数(但是,我们可自己编写一个类,使其中的对象具有 “常数”效果)。这一限制也适用于数组,它也属于对象。
对于基本数据类型,final 会将值变成一个常数;但对于对象句柄,final 会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。Java 对此未提供任何手段,可将一个对象直接变成一个常数。这一限制也适用于数组,它也属于对象。

final数据必须赋初值,以后不能修改,初始化位置:

Expand All @@ -442,7 +432,7 @@ final数据必须赋初值,以后不能修改,初始化位置:

**final类**

将类定义成 final 后,结果只是禁止进行继承——没有更多的限制。然 而,由于它禁止了继承,所以一个 final 类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final 一样,编译器此时有相同的效率选择。
将类定义成 final 后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个 final 类中的所有方法都默认为final。因为此时再也无法覆盖它们。所以与我们将一个方法明确声明为final 一样,编译器此时有相同的效率选择。



Expand Down Expand Up @@ -593,25 +583,33 @@ enum Season

![](./Java/异常.jpeg)

执行过程中发生的异常分为两大类
Exception 和 Error 都是继承了 Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。

Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。

Exception 又分为**可检查**(checked)异常和**不检查**(unchecked)异常。

可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。如果受检查异常没有被 `catch`或者`throws` 关键字处理的话,就没办法通过编译。

1. Error(错误):Java虚拟机无法解决的严重问题,如`JVM`系统内部错误、资源耗尽等。比如`StackOverflowError``OOM(out of memory)`
不检查异常就是所谓的运行时异常,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

2. Exception:其他因编程或偶然的外在因素导致的一般问题,可以使用针对性的代码处理,分为两大类:运行时异常和编译时异常。编译异常程序中必须处理,运行时异常程序中没有处理默认是`throws`
1. 运行时异常:
1. 空指针异常 NullPointerException
2. 数学运算异常 ArithmeticException
3. 数组下标越界异常 ArrayIndexOutOfBoundsException
4. 类型转换异常 ClassCastException
5. 数字格式不正确异常 NumberFormatException
编译时异常(可检查异常):

2. 编译时异常:
1. 数据库操作异常 SQLException
2. 文件操作异常 IOException
3. 文件不存在 FileNotFoundException
4. 类不存在 ClassNotFoundException
5. 文件末尾发生异常 EOPException
6. 参数异常 IllegalArguementException
1. 数据库操作异常 SQLException
2. 文件操作异常 IOException
3. 文件不存在 FileNotFoundException
4. 类不存在 ClassNotFoundException
5. 文件末尾发生异常 EOPException
6. 参数异常 IllegalArguementException

运行时异常(不检查异常):
1. 空指针异常 NullPointerException
2. 数学运算异常 ArithmeticException
3. 数组下标越界异常 ArrayIndexOutOfBoundsException
4. 类型转换异常 ClassCastException
5. 数字格式不正确异常 NumberFormatException



Expand Down Expand Up @@ -643,7 +641,7 @@ finally子句中包含return语句时肯产生意想不到的结果,finally中

编译异常程序中必须处理,运行时异常程序中没有处理默认是`throws`

子类重写父类的方法时,对抛出异常的规定:子类重写的方法抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出异常类型的子类型
子类重写父类的方法时,对抛出异常的规定:子类重写的方法抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出异常类型的子类型

`throws`过程中,如果有`try-catch`,相当于处理异常,就可以不必`throws`

Expand Down Expand Up @@ -699,7 +697,7 @@ try (Resource res = ...) { // 可以指定多个资源, ';'间隔

`String` 真正不可变有下面几点原因:

1. 保存字符串的数组被 `final` 修饰且为私有的,并且`String` 类没有提供/暴露修改这个字符串的方法。
1. 保存字符串的数组被 `final` 修饰且为私有的,并且`String` 类没有提供/暴露修改这个字符串的方法。拼接、裁剪字符串等动作,都会产生新的 String 对象。
2. `String` 类被 `final` 修饰导致其不能被继承,进而避免了子类破坏 `String` 不可变。

`String` 中的 `equals` 方法是被重写过的,比较的是 String 字符串的值是否相等。 `Object``equals` 方法是比较的对象的内存地址。
Expand All @@ -714,7 +712,7 @@ try (Resource res = ...) { // 可以指定多个资源, ';'间隔

和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。

`StringBuilder``StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中使用char数组保存字符串(JDK9以后是byte数组),不过没有使用 `final``private` 关键字修饰,是**可变的**
`StringBuilder``StringBuffer` 都继承自 `AbstractStringBuilder` 类,在 `AbstractStringBuilder` 中使用char数组保存字符串(JDK9以后是byte数组),不过没有使用 `final``private` 关键字修饰,是**可变的**构建时初始字符串长度加 16。



Expand Down Expand Up @@ -982,8 +980,6 @@ Java序列化机制只会保存对象的实例变量的状态,而不会保存

反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息,并能操作对象的属性及方法。

加载完类之后,在堆中产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含类的完整结构信息。

反射相关的主要类:

```java
Expand Down Expand Up @@ -1117,23 +1113,22 @@ getType:以Class形式返回类型



## 代理模式

### 静态代理

需要对每个目标类都单独写一个代理类,不灵活且麻烦

### 动态代理
## 动态代理

动态代理是在运行时动态生成类字节码,并加载到 JVM 中

#### JDK动态代理
### JDK动态代理

基于接口的,代理类一定是有定义的接口,在 Java 动态代理机制中 `InvocationHandler` 接口和 `Proxy` 类是核心。

`Proxy` 类中使用频率最高的方法是:`newProxyInstance()` ,这个方法主要用来生成一个代理对象。

```java
/**
* loader :类加载器,用于加载代理对象。
* interfaces : 被代理类实现的一些接口;
* h : 实现了 `InvocationHandler` 接口的对象;
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
Expand All @@ -1143,30 +1138,23 @@ public static Object newProxyInstance(ClassLoader loader,
}
```

这个方法一共有 3 个参数:

1. **loader** :类加载器,用于加载代理对象。
2. **interfaces** : 被代理类实现的一些接口;
3. **h** : 实现了 `InvocationHandler` 接口的对象;

要实现动态代理的话,还必须需要实现`InvocationHandler` 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现`InvocationHandler` 接口类的 `invoke` 方法来调用。

```java
public interface InvocationHandler {

/**
* 当使用代理对象调用方法的时候实际会调用到这个方法
* proxy :动态生成的代理类
* method : 与代理类对象调用的方法相对应
* args : 当前 method 方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
```

1. **proxy** :动态生成的代理类
2. **method** : 与代理类对象调用的方法相对应
3. **args** : 当前 method 方法的参数

**通过`Proxy` 类的 `newProxyInstance()` 创建的代理对象在调用方法的时候,实际会调用到实现`InvocationHandler` 接口的类的 `invoke()`方法。** 可以在 `invoke()` 方法中自定义处理逻辑,比如在方法执行前后做什么事情。
通过`Proxy` 类的 `newProxyInstance()` 创建的代理对象在调用方法的时候,实际会调用到实现`InvocationHandler` 接口的类的 `invoke()`方法。 可以在 `invoke()` 方法中自定义处理逻辑,比如在方法执行前后做什么事情。



Expand All @@ -1176,9 +1164,43 @@ public interface InvocationHandler {
2. 自定义代理类实现 `InvocationHandler`接口并重写`invoke`方法,在 `invoke` 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
3. 通过 `Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)` 方法创建代理对象;

```java
interface Hello {
void sayHello();
}
class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello World");
}
}
class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Invoking sayHello");
Object result = method.invoke(target, args);
return result;
}
}
public class MyDynamicProxy {
public static void main (String[] args) {
HelloImpl hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
// 构造代码实例
Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(), HelloImpl.class.getInterfaces(), handler);
// 调用代理方法
proxyHello.sayHello();
}
}
```


#### CGLIB 动态代理

### CGLIB 动态代理

CGLIB基于ASM字节码生成工具,它通过继承的方式实现代理类,所以不需要接口,可以代理普通类,但需要注意 final 方法(不可继承)。

Expand All @@ -1187,11 +1209,20 @@ CGLIB基于ASM字节码生成工具,它通过继承的方式实现代理类,
你需要自定义 `MethodInterceptor` 并重写 `intercept` 方法,`intercept` 用于拦截增强被代理类的方法。

```java
public class ServiceMethodInterceptor implements MethodInterceptor{
// 拦截被代理类中的方法
public class ServiceMethodInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable {

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在方法调用之前执行
System.out.println("Before invoking method: " + method.getName());

// 调用原始目标类的方法
Object result = proxy.invokeSuper(obj, args);

// 在方法调用之后执行
System.out.println("After invoking method: " + method.getName());

return result;
}
}
```
Expand Down
18 changes: 11 additions & 7 deletions docs/Java/Java集合.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Collection接口没有直接的实现子类,是通过它的子接口Set、List



和Array区别
和数组区别

1. 大小和自动扩容
2. 支持泛型
Expand Down Expand Up @@ -203,7 +203,7 @@ BlockingQueue的实现类:

线程不安全,保证线程安全就选用 `ConcurrentHashMap`

`HashMap` 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个
`HashMap` 可以存储 null 的 key 和 value,通常情况下,HashMap 进行 put 或者 get 操作,可以达到常数时间的性能,所以它是绝大部分利用键值对存取场景的首选。

JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

Expand Down Expand Up @@ -263,7 +263,9 @@ JDK1.8 之前 `HashMap` 由数组+链表组成的,数组是 `HashMap` 的主

### ConcurrentHashMap

Java 7 中 `ConcurrnetHashMap` 由很多个 `Segment` 组合,而每一个 `Segment` 是一个类似于 `HashMap` 的结构,所以每一个 `HashMap` 的内部可以进行扩容。但是 `Segment` 的个数一旦**初始化就不能改变**,默认 `Segment` 的个数是 16 个,你也可以认为 `ConcurrentHashMap` 默认支持最多 16 个线程并发。
Java 7 中 `ConcurrnetHashMap` 由很多个 `Segment` 组合,而每一个 `Segment` 是一个类似于 `HashMap` 的结构,所以每一个 `HashMap` 的内部可以进行扩容。但是 `Segment` 的个数一旦**初始化就不能改变**,默认 `Segment` 的个数是 16 个,可以认为 `ConcurrentHashMap` 默认支持最多 16 个线程并发。



Java 8 中 不再是之前的 **Segment 数组 + HashEntry 数组 + 链表**,而是 **Node 数组 + 链表 / 红黑树**。当冲突链表达到一定长度时,链表会转换成红黑树。

Expand Down Expand Up @@ -322,9 +324,11 @@ for (int i = 1; i <= 5; i++) {

### Hashtable

* 键和值都不能为空
* 使用方法基本和HashMap一样
* Hashtable是线程安全的,通过在每个⽅法上添加同步关键字来实现的,但这也可能 导致性能下降。
键和值都不能为null

使用方法基本和HashMap一样

Hashtable是线程安全的,通过在每个⽅法上添加同步关键字来实现的,但这也可能导致性能下降。

```
底层数组Hashtable$Entry[] 初始化大小 11
Expand All @@ -346,7 +350,7 @@ for (int i = 1; i <= 5; i++) {

### TreeMap

基于红黑树数据结构的实现的
TreeMap 则是基于红黑树的一种提供顺序访问的 Map,和 HashMap 不同,它的 get、put、remove 之类操作都是 O(log(n))的时间复杂度。

实现 `NavigableMap` 接口让 `TreeMap` 有了对集合内元素的搜索的能力。

Expand Down
Binary file added docs/Java/Java集合/concurentHashMap-Java7.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 9fe2195

Please sign in to comment.