java
2024-04-27 23:10:01 12 举报
AI智能生成
java
作者其他创作
大纲/内容
基础
八种数据类型
byte
1字节
short
2字节
int
4字节
缓冲区 127 ~ -128
Integer a = 127;
Integer b = 127;
System.out.println(a == b); true
Integer b = 127;
System.out.println(a == b); true
Integer a = 128;
Integer b = 128;
System.out.println(a == b); false
Integer b = 128;
System.out.println(a == b); false
long
8字节
boolean
1字节
char
2字节
float
4字节
double
8字节
基本数据类型和引用类型的区别
存储地址不同 基本数据类型的值存在栈上 引用类型存在堆上
内存占用不同 基本数据类型都是固定的 应用类型要根据内部属性而定
默认值不同 基本数据都有默认值 int 为0 引用数据类型为null
传递方式不同:基本数据类型的传递方式是按值传递,即传递的是值的拷贝,而引用类型传递的是引用的拷贝,即传递的是地址的拷贝,所以在方法内部修改引用类型的值,会影响到外部的对象。
操作方式不同:基本数据类型是直接操作它们的值,而引用类型则需要通过引用来访问和修改它们所引用的对象的值。
内存占用不同 基本数据类型都是固定的 应用类型要根据内部属性而定
默认值不同 基本数据都有默认值 int 为0 引用数据类型为null
传递方式不同:基本数据类型的传递方式是按值传递,即传递的是值的拷贝,而引用类型传递的是引用的拷贝,即传递的是地址的拷贝,所以在方法内部修改引用类型的值,会影响到外部的对象。
操作方式不同:基本数据类型是直接操作它们的值,而引用类型则需要通过引用来访问和修改它们所引用的对象的值。
注解
注解在Java中是以@符号开头的特殊标记,可以用于类、方法、变量、参数等元素的注释。注解可以包含元素,这些元素可以在注解使用时提供值。
反射
三种方式
Class.forName
Class clz = String.class;
String str = new String("Hello");
Class clz = str.getClass();
invoke
执行方法
泛型
核心概念
参数化类型:通过在类名后面添加尖括号,将类型参数化,使得类可以适用于多种类型。
泛型类:使用参数化类型的类。
泛型接口:使用参数化类型的接口。
泛型方法:在方法中使用参数化类型的方法。
类型通配符:使用 ? 表示未知类型的通配符。
泛型类:使用参数化类型的类。
泛型接口:使用参数化类型的接口。
泛型方法:在方法中使用参数化类型的方法。
类型通配符:使用 ? 表示未知类型的通配符。
继承
继承实现了 IS-A 关系,例如 Cat 和 Animal 就是一种 IS-A 关系,因此 Cat 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法
Animal animal = new Cat();
Animal animal = new Cat();
抽象类和接口
抽象类
抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。
抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
接口
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
集合
map
HashMap
HashMap 是最常用的 Map 实现类之一。它基于哈希表实现,可以快速的插入和查找数据。HashMap 允许 null 值和 null 键,但不保证元素的顺序。HashMap 是非线程安全的
HashMap 是最常用的 Map 实现类之一。它基于哈希表实现,可以快速的插入和查找数据。HashMap 允许 null 值和 null 键,但不保证元素的顺序。HashMap 是非线程安全的
TreeMap
TreeMap 是基于红黑树实现的有序 Map,它会按照键的自然排序或指定的比较器进行排序。TreeMap 不允许 null 键,但允许 null 值。TreeMap 是非线程安全的
TreeMap 是基于红黑树实现的有序 Map,它会按照键的自然排序或指定的比较器进行排序。TreeMap 不允许 null 键,但允许 null 值。TreeMap 是非线程安全的
LinkedHashMap
LinkedHashMap 是 HashMap 的一个子类,它保留了元素插入的顺序.LinkedHashMap 通过维护一个双向链表来实现顺序性。LinkedHashMap 允许 null 值和null 键。LinkedHashMap 是非线程安全的
LinkedHashMap 是 HashMap 的一个子类,它保留了元素插入的顺序.LinkedHashMap 通过维护一个双向链表来实现顺序性。LinkedHashMap 允许 null 值和null 键。LinkedHashMap 是非线程安全的
Hashtable
Hashtable 是早期的 Map 实现类,它基于哈希表实现,提供了与 HashMap 类似的操作,但是它是线程安全的。Hashtable 不允许 null 值和 null 键。它被认为是过时的,并且在新代码中不建议使用
Hashtable 是早期的 Map 实现类,它基于哈希表实现,提供了与 HashMap 类似的操作,但是它是线程安全的。Hashtable 不允许 null 值和 null 键。它被认为是过时的,并且在新代码中不建议使用
ConcurrentHashMap
ConcurrentHashMap 是 Java 5 引入的 Map 实现类,它是 HashMap 的线程安全版本。ConcurrentHashMap 采用分段锁技术,将 Map 分成多个段,每个段都有一个独立的锁,因此多个线程可以同时访问不同的段。ConcurrentHashMap 允许 null 值和 null摩
ConcurrentHashMap 是 Java 5 引入的 Map 实现类,它是 HashMap 的线程安全版本。ConcurrentHashMap 采用分段锁技术,将 Map 分成多个段,每个段都有一个独立的锁,因此多个线程可以同时访问不同的段。ConcurrentHashMap 允许 null 值和 null摩
list
ArrayList
ArrayList 是最常用的 List 实现类之一,它是基于数组实现的。ArrayList 允许快速的随机访问,但是在插入和删除元素时,性能较差。ArrayList 不是线程安全的。
ArrayList 是最常用的 List 实现类之一,它是基于数组实现的。ArrayList 允许快速的随机访问,但是在插入和删除元素时,性能较差。ArrayList 不是线程安全的。
LinkedList
LinkedList 是基于链表实现的 List,它在插入和删除元素时比 ArrayList 更快,但是随机访问性能较差。LinkedList 不是线程安全的。
Vector
Vector 是Java 最早的 List 实现类之一,它是线程安全的。Vector 和 ArrayList 类似,但是性能较差,应尽可能避免使用。
Stack
Stack 是基于 Vector 实现的栈,它提供了 push0 和 pop0 方法。Stack 继承自 Vector.
因此也是线程安全的。但是在 Java 5 中,推荐使用 Deque 来代替 Stack。
是否保证线程安全?
ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全。
底层数据结构区别?
Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构。
插入和删除是否受元素位置的影响?
ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
是否支持快速随机访问?
LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
内存空间占用?
ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全。
底层数据结构区别?
Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构。
插入和删除是否受元素位置的影响?
ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
是否支持快速随机访问?
LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
内存空间占用?
ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
set
HashSet
HashSet 是最常用的 Set 实现类之一,它基于哈希表实现。HashSet 不保证元素的顺序,允许 null 值。HashSet 不是线程安全的
HashSet 是最常用的 Set 实现类之一,它基于哈希表实现。HashSet 不保证元素的顺序,允许 null 值。HashSet 不是线程安全的
LinkedHashSet
LinkedHashSet 是 HashSet 的子类,它通过维护一个双向链表来保留插入顺序LinkedHashSet 不是线程安全的
TreeSet
TreeSet 是基于红黑树实现的有序 Set,它会按照元素的自然排序或指定的比较器进行排序。TreeSet 不允许 null 值
EnumSet
EnumSet 是专门用于枚举类型元素的 Set 实现类。EnumSet 内部使用一个位向量实现它的性能非常高。EnumSet 是非线程安全的。
ConcurrentSkipListSetConcurrentSkipListSet 是Java 6 引入的 Set 实现类,它是线程安全的ConcurrentSkipListSet 基于跳表实现,它提供了高效的并发访问
CopyOnWriteArraySet
CopyOnWriteArraySet 是 Java 5 引入的 Set 实现类,它是线程安全的。CopyOnWriteArraySet 基于 CopyOnWriteArrayList 实现,它支持并发访问,并且在遍历时不需要加锁。
面试题
Java 中 List、Set 和 Map 有什么区别?
List:List 是一个有序的元素集合,每个元素可以通过索引访问。List 中的元素可以重复,可以在 List 中的任何位置插入或删除元素。
Set:Set 是一个元素集合,不允许重复。与 List 不同,Set 不维护元素的顺序。
Map:Map 是一个键值对的集合,每个键是唯一的,用于检索相应的值。与 List 或 Set 不同,Map 不维护元素的顺序。
List:List 是一个有序的元素集合,每个元素可以通过索引访问。List 中的元素可以重复,可以在 List 中的任何位置插入或删除元素。
Set:Set 是一个元素集合,不允许重复。与 List 不同,Set 不维护元素的顺序。
Map:Map 是一个键值对的集合,每个键是唯一的,用于检索相应的值。与 List 或 Set 不同,Map 不维护元素的顺序。
Java 中 List、Set 和 Map 的一些实现有哪些?
List:ArrayList、LinkedList、Vector
Set:HashSet、TreeSet、LinkedHashSet
Map:HashMap、TreeMap、LinkedHashMap
List:ArrayList、LinkedList、Vector
Set:HashSet、TreeSet、LinkedHashSet
Map:HashMap、TreeMap、LinkedHashMap
如何遍历 Map 中的键值对?
可以使用 Map 的 keySet() 方法获取所有的键,然后通过迭代器或 for-each 循环遍历键,再根据键获取对应的值。也可以使用 entrySet() 方法获取键值对的集合,然后通过迭代器或 for-each 循环遍历键值对。
HashMap 和 TreeMap 有什么区别?
HashMap 和 TreeMap 都是 Map 接口的实现类。它们的主要区别在于存储和遍历顺序不同。HashMap 使用哈希表实现,它的元素是无序的,因此无法保证顺序。而 TreeMap 使用红黑树实现,它的元素是有序的,根据键进行排序。
如果要保证 Map 的键是按照插入顺序排序的,应该使用哪个实现类?
LinkedHashMap 可以保证键值对的插入顺序。它内部使用了一个双向链表来维护键值对的插入顺序。
如果要保证 Map 的键是按照自然顺序排序的,应该使用哪个实现类?
TreeMap 可以保证键值对按照自然顺序排序。它内部使用红黑树来维护键值对的顺序。如果要使用自定义排序,则可以使用 TreeMap 的构造方法,传入一个自定义的 Comparator 对象。
HashMap 的默认初始容量是多少?为什么要这样设计?
HashMap 的默认初始容量是 16。这样设计是为了在哈希表的桶数和链表长度之间找到一个平衡点,以提高查询和插入的效率。如果初始容量过小,哈希表中的链表会变得很长,影响查询效率。如果初始容量过大,会浪费内存空间。
如果要对 Map 中的所有值进行操作,应该使用哪个方法?
可以使用 values() 方法获取 Map 中所有的值,返回一个 Collection 对象。然后通过迭代器或 for-each 循环遍历集合,对每个值进行操作。
List和Set的常见实现类有哪些?
答:List的常见实现类有ArrayList、LinkedList和Vector,而Set的常见实现类有HashSet、TreeSet和LinkedHashSet。
IO流
两种类型
字节流(Byte Stream):以字节为单位进行操作,如InputStream和OutputStream。
字符流(Character Stream):以字符为单位进行操作,如Reader和Writer。
面试题
Java I/O流有哪些常见的类和接口?
常见的类和接口包括InputStream、OutputStream、Reader、Writer、File、FileInputStream、FileOutputStream、FileReader、FileWriter、BufferedInputStream、BufferedOutputStream、BufferedReader和BufferedWriter等。
Java I/O流中的字节流和字符流有什么区别?
字节流是以字节为单位进行操作的,可以读取和写入字节,适合于处理二进制数据;字符流是以字符为单位进行操作的,可以读取和写入字符,适合于处理文本数据。
Java I/O流中的BufferedInputStream和BufferedOutputStream有什么作用?
BufferedInputStream和BufferedOutputStream是字节流的缓冲区实现,它们可以提高读写效率,避免频繁读写硬盘。
lambda
Lambda表达式的语法:Lambda表达式由参数列表、箭头符号和方法体组成,形如:(parameters) -> expression或(parameters) -> { statements; }。
Lambda表达式的类型:Lambda表达式是函数式接口的实例。函数式接口是只包含一个抽象方法的接口,可以使用@FunctionalInterface注解进行标识。
Lambda表达式的作用:Lambda表达式可以用于简化代码,提高可读性和可维护性。Lambda表达式可以使代码更加紧凑,并且可以将代码作为数据进行传递。
Lambda表达式的特点:Lambda表达式是匿名的,可以捕获局部变量,但必须是final或等效的,不支持多重继承,不能使用显式的构造函数。
Lambda表达式的用途:Lambda表达式通常用于函数式编程,如在集合中进行筛选、排序、转换等操作。Lambda表达式也可以用于事件驱动程序设计和并发编程。
Lambda表达式的类型:Lambda表达式是函数式接口的实例。函数式接口是只包含一个抽象方法的接口,可以使用@FunctionalInterface注解进行标识。
Lambda表达式的作用:Lambda表达式可以用于简化代码,提高可读性和可维护性。Lambda表达式可以使代码更加紧凑,并且可以将代码作为数据进行传递。
Lambda表达式的特点:Lambda表达式是匿名的,可以捕获局部变量,但必须是final或等效的,不支持多重继承,不能使用显式的构造函数。
Lambda表达式的用途:Lambda表达式通常用于函数式编程,如在集合中进行筛选、排序、转换等操作。Lambda表达式也可以用于事件驱动程序设计和并发编程。
stream
过滤操作(Filter)
该接口用于判断某个元素是否符合条件
映射操作(Map)
.map(n -> n * n)
排序操作(Sort)
stream().sorted()
聚合操作(Reduce)
它接受一个BinaryOperator函数式接口作为参数,该接口用于将集合中的元素逐个进行累积计算 .reduce(0, (a, b) -> a + b);
并行操作(Parallel)
可以使用parallelStream方法来将集合转换为并行流,从而实现并行处理
收集操作(Collect)
.collect(Collectors.toList());
计数操作(Count)
numbers.stream().count();
匹配操作(Match)
allMatch、anyMatch、noneMatch 判断是否所有、任意、或者没有一个元素满足某个条件
查找操作(Find)
findFirst、findAny等。这些方法可以用于查找集合中的第一个或者任意一个元素
线程
线程池
new ThreadPoolExecutor
int corePoolSize, 核心数
int maximumPoolSize, 最大数
long keepAliveTime,线程空闲存活时间
TimeUnit unit, 时间单位
BlockingQueue<Runnable> workQueue 任务列表缓存区 设置大小
int maximumPoolSize, 最大数
long keepAliveTime,线程空闲存活时间
TimeUnit unit, 时间单位
BlockingQueue<Runnable> workQueue 任务列表缓存区 设置大小
threadLocal
每个线程都是相互隔离的
一个线程可以使用多线程。这被称为多线程内部并发。
在一个单独的线程内,你可以创建和管理多个子线程,这些子线程可以并行执行不同的任务。这对于需要同时处理多个任务的情况非常有用。在这种情况下,主线程可以创建子线程,并协调它们的工作,以完成更复杂的任务。
然而,需要谨慎处理多线程内部并发,因为它可能导致线程安全性问题(例如竞态条件和死锁),需要适当的同步和管理。
在一个单独的线程内,你可以创建和管理多个子线程,这些子线程可以并行执行不同的任务。这对于需要同时处理多个任务的情况非常有用。在这种情况下,主线程可以创建子线程,并协调它们的工作,以完成更复杂的任务。
然而,需要谨慎处理多线程内部并发,因为它可能导致线程安全性问题(例如竞态条件和死锁),需要适当的同步和管理。
ThreadLocal 能够存对象 是因为 Thread 类中有threadLocals 属性 通过set 赋值 线程又是有隔离性的 所以保证了数据不紊乱
线程状态
线程只有运行中状态占用cpu资源
无锁 没有加锁
轻量级锁 无锁状态加syncz 之后就是
偏向锁 在程序启动一定时间后 (4s~) 再加锁就是
重量级锁 只要竞争同一个资源就是
轻量级锁 无锁状态加syncz 之后就是
偏向锁 在程序启动一定时间后 (4s~) 再加锁就是
重量级锁 只要竞争同一个资源就是
https://github.com/sunwu51/notebook/blob/master/20.06/A.java
关键字
fianl
1. 数据声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。对于基本类型,final 使数值不变;对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
2. 方法声明方法不能被子类重写。private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。
3. 类
声明类不允许被继承。
声明类不允许被继承。
static
初始化顺序
public static String staticField = "静态变量";
static {
System.out.println("静态语句块");
}
public String field = "实例变量";
{
System.out.println("普通语句块");
}
public InitialOrderTest() {
System.out.println("构造函数");
}
异常
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: Error 和 Exception。其中 Error 用来表示 JVM 无法处理的错误,Exception 分为两种:受检异常 : 需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;非受检异常 : 是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。
中间件
redis
string
hash
list
set
zset
rabbitmq
direct
fanout
tocpic
es
index
sharding-jdbc
spring
aop
前置通知(Before advice):在目标方法执行前执行。
后置通知(After advice):在目标方法执行后执行,无论方法是否发生异常。
返回通知(After returning advice):在目标方法成功执行并返回结果后执行。
异常通知(After throwing advice):在目标方法抛出异常后执行。
环绕通知(Around advice):围绕目标方法的执行进行包装,可以在方法执行前后自定义操作。
后置通知(After advice):在目标方法执行后执行,无论方法是否发生异常。
返回通知(After returning advice):在目标方法成功执行并返回结果后执行。
异常通知(After throwing advice):在目标方法抛出异常后执行。
环绕通知(Around advice):围绕目标方法的执行进行包装,可以在方法执行前后自定义操作。
di
DI的核心思想是通过构造函数、Setter方法或注解来实现对象之间的依赖注入。在DI中,对象不再负责创建或查找依赖的对象,而是通过外部容器提供依赖的实例
ioc
IOC的核心思想是将对象的创建和依赖注入从应用程序代码中解耦出来,通过配置文件或注解的方式描述对象的创建和依赖关系。这样,应用程序代码只需要关注自身的业务逻辑,而不需要关心对象的具体创建和依赖注入过程。
bean 生命周期
实例化(Instantiation):在这个阶段,Spring容器根据配置信息或注解创建Bean的实例。这通常是通过调用Bean的构造函数来实现的。
属性赋值(Population):在实例化之后,Spring容器会根据配置信息或注解为Bean的属性注入相应的值,包括基本类型、引用类型和集合类型等。
初始化(Initialization):在属性赋值之后,Spring容器会调用Bean的初始化方法进行一些初始化操作。可以通过配置文件中的init-method属性或@PostConstruct注解来指定初始化方法。
使用(In Use):在初始化完成后,Bean可以被应用程序使用,执行相应的业务逻辑。
销毁(Destruction):当Bean不再被使用或容器关闭时,Spring容器会调用Bean的销毁方法进行资源释放或清理操作。可以通过配置文件中的destroy-method属性或@PreDestroy注解来指定销毁方法。
属性赋值(Population):在实例化之后,Spring容器会根据配置信息或注解为Bean的属性注入相应的值,包括基本类型、引用类型和集合类型等。
初始化(Initialization):在属性赋值之后,Spring容器会调用Bean的初始化方法进行一些初始化操作。可以通过配置文件中的init-method属性或@PostConstruct注解来指定初始化方法。
使用(In Use):在初始化完成后,Bean可以被应用程序使用,执行相应的业务逻辑。
销毁(Destruction):当Bean不再被使用或容器关闭时,Spring容器会调用Bean的销毁方法进行资源释放或清理操作。可以通过配置文件中的destroy-method属性或@PreDestroy注解来指定销毁方法。
Spring容器的启动流程
Spring框架的启动流程可以概括为以下几个主要步骤:
1. 加载配置:Spring框架会加载配置文件,例如XML配置文件(如applicationContext.xml)或基于注解的配置类(如@Configuration注解的类),来获取应用程序的配置信息。
2. 创建IOC容器:根据配置信息,Spring框架会创建IOC(Inversion of Control,控制反转)容器,即ApplicationContext。IOC容器负责管理Bean的创建、依赖注入和生命周期等。
3. 扫描组件:Spring框架会扫描配置文件或注解中指定的组件,例如通过@ComponentScan注解来指定扫描的包路径,以获取需要被管理的Bean。
4. 创建Bean实例:根据扫描到的组件信息,IOC容器会创建相应的Bean实例。这通常是通过调用构造函数来实现的,也可以使用工厂方法或其他方式创建Bean实例。
5. 属性注入:在Bean实例化后,IOC容器会自动解析Bean之间的依赖关系,并进行属性注入。属性注入可以通过构造函数、Setter方法或其他方式来实现。
6. 初始化Bean:在属性注入完成后,IOC容器会调用Bean的初始化方法进行一些初始化操作。可以通过配置文件中的`init-method`属性或`@PostConstruct`注解来指定初始化方法。
7. 应用程序使用:在初始化完成后,Bean可以被应用程序使用,执行相应的业务逻辑。
8. 销毁Bean:当应用程序关闭或IOC容器销毁时,IOC容器会调用Bean的销毁方法进行资源释放或清理操作。可以通过配置文件中的`destroy-method`属性或`@PreDestroy`注解来指定销毁方法。
需要注意的是,Spring框架的启动流程可以根据具体的使用方式和配置进行调整和定制。例如,可以使用Java配置类替代XML配置文件,或使用更细粒度的注解来配置Bean的创建和依赖注入。
通过理解Spring框架的启动流程,可以更好地掌握Spring的核心原理和功能,以便正确配置和使用Spring框架。
1. 加载配置:Spring框架会加载配置文件,例如XML配置文件(如applicationContext.xml)或基于注解的配置类(如@Configuration注解的类),来获取应用程序的配置信息。
2. 创建IOC容器:根据配置信息,Spring框架会创建IOC(Inversion of Control,控制反转)容器,即ApplicationContext。IOC容器负责管理Bean的创建、依赖注入和生命周期等。
3. 扫描组件:Spring框架会扫描配置文件或注解中指定的组件,例如通过@ComponentScan注解来指定扫描的包路径,以获取需要被管理的Bean。
4. 创建Bean实例:根据扫描到的组件信息,IOC容器会创建相应的Bean实例。这通常是通过调用构造函数来实现的,也可以使用工厂方法或其他方式创建Bean实例。
5. 属性注入:在Bean实例化后,IOC容器会自动解析Bean之间的依赖关系,并进行属性注入。属性注入可以通过构造函数、Setter方法或其他方式来实现。
6. 初始化Bean:在属性注入完成后,IOC容器会调用Bean的初始化方法进行一些初始化操作。可以通过配置文件中的`init-method`属性或`@PostConstruct`注解来指定初始化方法。
7. 应用程序使用:在初始化完成后,Bean可以被应用程序使用,执行相应的业务逻辑。
8. 销毁Bean:当应用程序关闭或IOC容器销毁时,IOC容器会调用Bean的销毁方法进行资源释放或清理操作。可以通过配置文件中的`destroy-method`属性或`@PreDestroy`注解来指定销毁方法。
需要注意的是,Spring框架的启动流程可以根据具体的使用方式和配置进行调整和定制。例如,可以使用Java配置类替代XML配置文件,或使用更细粒度的注解来配置Bean的创建和依赖注入。
通过理解Spring框架的启动流程,可以更好地掌握Spring的核心原理和功能,以便正确配置和使用Spring框架。
mysql
MVCC
MVCC(Multi-Version Concurrency Control,多版本并发控制)是MySQL中用于实现并发事务控制的一种机制,用于解决多个事务同时对同一数据进行读写操作的并发问题。在MVCC中,每个事务都可以看到一个历史版本的数据,这个历史版本是在该事务开始之前已经存在的。因此,每个事务都可以读取并操作自己的数据版本,而不会与其他事务发生冲突,从而避免了锁的使用,提高了数据库的并发性能。
具体来说,MVCC通过在每个数据行上维护一个版本号来实现,每次有事务对该行数据进行更新时,会将该行数据的当前版本号加1,同时将旧版本的数据存储到一个回滚段中,供其他事务查询。当有事务读取该数据行时,如果该事务的启动时间早于该数据行的更新时间,则该事务将读取到旧版本的数据,否则读取当前版本的数据。
在MVCC中,每个事务都有一个唯一的事务ID(transaction ID),该事务ID可以用来识别该事务创建的版本号。事务ID是在事务开始时由MySQL自动分配的,当事务提交时,该事务创建的所有版本都会被标记为“已提交”。如果事务回滚,则所有版本都被标记为“已回滚”,并在回滚段中被清除。
MVCC在MySQL中的实现方式是通过InnoDB存储引擎来实现的,因此只有使用InnoDB存储引擎的表才支持MVCC机制。同时,在使用MVCC时,应注意避免长事务和大事务的产生,以免回滚段占用过多的空间。
具体来说,MVCC通过在每个数据行上维护一个版本号来实现,每次有事务对该行数据进行更新时,会将该行数据的当前版本号加1,同时将旧版本的数据存储到一个回滚段中,供其他事务查询。当有事务读取该数据行时,如果该事务的启动时间早于该数据行的更新时间,则该事务将读取到旧版本的数据,否则读取当前版本的数据。
在MVCC中,每个事务都有一个唯一的事务ID(transaction ID),该事务ID可以用来识别该事务创建的版本号。事务ID是在事务开始时由MySQL自动分配的,当事务提交时,该事务创建的所有版本都会被标记为“已提交”。如果事务回滚,则所有版本都被标记为“已回滚”,并在回滚段中被清除。
MVCC在MySQL中的实现方式是通过InnoDB存储引擎来实现的,因此只有使用InnoDB存储引擎的表才支持MVCC机制。同时,在使用MVCC时,应注意避免长事务和大事务的产生,以免回滚段占用过多的空间。
事务隔离
读未提交
在该隔离级别下,事务可以读取其他事务尚未提交的数据,存在“脏读”的可能性,可能会读取到其他事务还未提交的不合法数据。该级别下并发性能最高,但同时也是最不安全的。
可以读取到其他事务还没有提交事务的数据 这种现象属于脏读
读已提交
在该隔离级别下,事务只能读取其他事务已经提交的数据,避免了脏读的发生。但是,由于其他事务可能正在对该数据进行修改,因此该级别下可能会出现“不可重复读”的问题。
读取其他事务已经提交了的数据 但是本次事务最开始读取的数据和再一次读取数据后发现不一致(因为其他事务改变了数据) 这种现象属于不可重复读
可重复读
在该隔离级别下,事务在读取数据时会创建一个快照,保证在该事务执行期间,读取的数据都是一致的,即使其他事务对该数据进行了修改。因此,该级别下不存在“不可重复读”的问题。但是,其他事务对该数据的修改操作在该事务提交后才会生效,可能导致“幻读”的问题。
在事务开始时 读数据生成了快照 其他事务不影响本次事务的数据 但是其他事务创建或删除了数据 再次读取数据时和快照的条目数不一致 属于幻读
串行化
在该隔离级别下,事务是串行执行的,每个事务都必须等待前一个事务执行完毕才能执行,可以避免脏读、不可重复读和幻读的问题,但是并发性能最差。
读取数据时 对数据进行锁定 不让其他事务修改 避免了数据不一致性 但性能消耗高
锁
表锁
语法:LOCK TABLES table_name [AS alias_name] lock_type
其中,table_name表示要加锁的表名,alias_name表示表的别名(可选),lock_type表示要加的锁的类型(共有三种类型:读锁(SHARED)、写锁(EXCLUSIVE)、自动增量锁(AUTO-INC))。
例如:
加读锁:LOCK TABLES table_name READ;
加写锁:LOCK TABLES table_name WRITE;
在加锁之后,需要执行UNLOCK TABLES来释放锁定。
其中,table_name表示要加锁的表名,alias_name表示表的别名(可选),lock_type表示要加的锁的类型(共有三种类型:读锁(SHARED)、写锁(EXCLUSIVE)、自动增量锁(AUTO-INC))。
例如:
加读锁:LOCK TABLES table_name READ;
加写锁:LOCK TABLES table_name WRITE;
在加锁之后,需要执行UNLOCK TABLES来释放锁定。
行锁
FOR UPDATE
当您使用FOR UPDATE子句时,您正在执行一个排他锁定,这意味着其他事务无法在此时读取或写入此行数据。只有在当前事务提交或回滚之后,其他事务才能读取或写入此行数据。这确保了在当前事务中对该行数据进行更改时,其他事务不会干扰或更改该数据。因此,其他事务无法读取该行数据,直到当前事务释放锁定并完成其更改。
LOCK IN SHARE MODE
当您使用 LOCK IN SHARE MODE 语句时,您正在执行一个共享锁,这意味着其他事务可以读取该行数据,但不能修改该行数据。当前事务可以读取该行数据,并且在没有其他事务对其进行更改时,可以修改该行数据。
但是需要注意的是,如果在当前事务获取共享锁之后,另一个事务获取了排他锁并尝试修改该行数据,那么当前事务会被阻塞,直到排他锁被释放为止。因此,如果您想要修改数据,最好使用 FOR UPDATE 语句,以避免其他事务干扰您的操作。
但是需要注意的是,如果在当前事务获取共享锁之后,另一个事务获取了排他锁并尝试修改该行数据,那么当前事务会被阻塞,直到排他锁被释放为止。因此,如果您想要修改数据,最好使用 FOR UPDATE 语句,以避免其他事务干扰您的操作。
库级锁
MySQL也支持对整个库进行锁定,以保证数据的完整性和一致性。库级锁使用LOCK TABLES语句进行加锁和UNLOCK TABLES语句进行释放。
语法:FLUSH TABLES WITH READ LOCK;
该语句将整个库加上共享锁(SHARED),只允许当前事务读取数据,其他事务不能修改数据,直到当前事务提交或回滚。
总的来说,MySQL通过表锁、行锁和库级锁来实现对数据的并发控制和并发访问的控制,开发人员需要根据实际情况选择适当的锁机制来保证数据的一致性和可靠性。
可以用于演示系统 不允许写操作 但不建议 可以使用restful 接口风格进行判断是否是查询操作
连接池
资源重用
避免了频繁创建、释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
更快的系统响应速度
在初始化过程中,就已经创建好若干数据库连接。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
在初始化过程中,就已经创建好若干数据库连接。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
新的资源分配手段
对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接的配置,实现数据库连接池技术,几年钱也许还是个新鲜话题,对于目前的业务系统而言,如果设计中还没有考虑到连接池的应用,那么…….快在设计文档中加上这部分的内容吧。某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
统一的连接管理,避免数据库连接泄漏
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄漏。
jvm
g1
分区 region
动态分 区域清理完成之后 可以根据需求来变更分区内容
G1 垃圾回收机制
根据cms 的bug 衍生出来的g1 三色标记算法
cms
Serial
stw Stop the world
ps po
edan s0 s1 | old
三种算法
标记算法 标记需要删除的 但是删除之后 内存呈碎片
拷贝算法 把内存分为两块 只使用一块 标记完之后 把需要留下来的放到另一块当中 循环往复 缺点很明显 内存浪费
两种结合 标记清除后 内存碎片整理
https://pdai.tech/md/java/basic/java-basic-x-generic.html
设计模式
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一实例的情况,如日志记录器、数据库连接等。
工厂模式(Factory Pattern):将对象的实例化交给子类来决定,通过一个共同的接口创建对象。它提供了一种解耦的方式,使得客户端代码与具体实现类解耦,可以更灵活地切换和扩展对象的创建。
观察者模式(Observer Pattern):定义对象之间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会收到通知并自动更新。它用于解耦触发者和观察者之间的关系,实现对象间的松耦合。
策略模式(Strategy Pattern):定义一系列算法,并将每个算法封装起来,使得它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端,从而提高了代码的灵活性和可维护性。
装饰器模式(Decorator Pattern):动态地给一个对象添加额外的职责,同时又不改变其接口。装饰器模式通过组合和委托的方式,使得对象的功能可以动态地扩展,而无需修改原始对象的代码。
适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以协同工作。
工厂模式(Factory Pattern):将对象的实例化交给子类来决定,通过一个共同的接口创建对象。它提供了一种解耦的方式,使得客户端代码与具体实现类解耦,可以更灵活地切换和扩展对象的创建。
观察者模式(Observer Pattern):定义对象之间的一对多依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会收到通知并自动更新。它用于解耦触发者和观察者之间的关系,实现对象间的松耦合。
策略模式(Strategy Pattern):定义一系列算法,并将每个算法封装起来,使得它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端,从而提高了代码的灵活性和可维护性。
装饰器模式(Decorator Pattern):动态地给一个对象添加额外的职责,同时又不改变其接口。装饰器模式通过组合和委托的方式,使得对象的功能可以动态地扩展,而无需修改原始对象的代码。
适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以协同工作。
工厂模式
观察者模式
策略模式
iot
mqtt
服务端 客户端 EMQ MQTT.FX
订阅话题 0 1 2
node-red
node install -g node-red
node-red
localhost:1880
http mqtt redis
布隆过滤器
是一种概率型数据结构 用于快速判断一个元素是否可能存在于一个集合中
布隆过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中的不同位置,每个位置用一个位来表示,初始时所有位都设为0。当一个元素被插入到布隆过滤器中时,通过多个哈希函数计算得到的位置对应的位都被置为1。在查询时,如果一个元素经过哈希函数计算得到的位置对应的位有任何一个为0,那么可以确定该元素一定不存在于集合中;如果所有位都为1,那么该元素可能存在于集合中。
布隆过滤器基于哈希函数和位数组实现。它的核心思想是使用多个哈希函数将元素映射到位数组中的不同位置,每个位置用一个位来表示,初始时所有位都设为0。当一个元素被插入到布隆过滤器中时,通过多个哈希函数计算得到的位置对应的位都被置为1。在查询时,如果一个元素经过哈希函数计算得到的位置对应的位有任何一个为0,那么可以确定该元素一定不存在于集合中;如果所有位都为1,那么该元素可能存在于集合中。
- 类加载器
bootstarp 启动类加载器
java bin/lib
ext 扩展类加载器
java bin/lib/ext
application 系统类加载器
项目业务代码
如果包名重复 在bootStarp 不会重新生成实例
Bootstrap类加载器位于类加载器层次结构的最顶层,它没有父类加载器。因此,当类名和包名与核心类库中的类相同或冲突时,Bootstrap类加载器无法重新生成实例。这意味着无法通过自定义类加载器来绕过这个问题,因为自定义类加载器的父类加载器是Bootstrap类加载器
双亲委派
双亲委派模型的基本原则是:当一个类加载器收到加载类的请求时,它首先将加载请求委派给其父类加载器,如果父类加载器可以加载该类,则直接返回已加载的类;如果父类加载器无法加载该类,则由当前类加载器尝试加载。
arthas
执行spring bean 方法
vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("environment").getProperty("second.jdbc.url")
vmtool --action getInstances --className org.springframework.context.ApplicationContext --express 'instances[0].getBean("environment").getProperty("second.jdbc.url")
0 条评论
下一页