JAVA基础整理
作者: 分类: 学习笔记 发布于: 2024-06-14 16:28:07 浏览:2,472 评论(0)
基础整理
JAVA
JAVA 中的几种基本数据类型是什么,各自占用多少字节。
Java中有八种基本数据类型
- byte----->1字节
- short---->2字节
- int------>4字节
- long----->8字节
- char----->2字节
- float---->4字节
- double--->8字节
- boolean--->1bit
java中4种引用类型
- 强: User user = new User(); 不会被系统回收, 在方法内会随着方法栈弹出
- 软:SoftReference; 系统内存不足时才回收
- 弱:只要垃圾回收机制触发, 都会被回收, 不管内存是否足。如ThreadLocal
- 虚:必须配合引用队列联合使用,跟踪对象被垃圾回收的状态。永远返回null,提供一个方法
isEnQueued
判断对象是否已销毁,可用于监控对象的创建和销毁
java中线程实现方式
继承
Thread
类, 实现run
方法实现
Runnable
接口, 重写run
方法 (底层都是使用这个)实现
Callable
重写call
方法, 配合FutureTask
可以拿到执行结果基于线程池构建线程
equals 与 == 的区别
==: 比较基本数据类型时比较的是数据的值,而比较引用类型时比较的是两个对象的地址值。 如:int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。 equals: 用来比较的是两个对象的内容是否相等。
String、StringBuffer、 和 StringBuilder 的区别是什么?
String是只读字符串,每次修改都会生成一个新的对象
StringBuffer 同一内存空间, 线程安全
StringBuilder 同一内存空间, 无线程锁,多线程不安全
ArrayList 和 LinkedList的区别
ArrayList:连续的内存空间。读取指定位置的数据很快,直接通过索引访问。 频繁读取的列表时使用
LinkedList: 内存分散。 双向链表实现。插入与删除快(通过指针), 读慢(需要从头遍历)。频繁插入与删除的列表时使用
HashMap和HashTable的区别
HashTable: 线程安全、不允许使用空键或空值、继承自Dictionary类、提供elements(), keys(), values()等方法
HashMap: 不是线程安全的、允许使用一个空键,空值允许、继承自AbstractMap类。 建议使用ConcurrentHashMap类
sleep 和 wait 的区别
- sleep 属于 Thread 类中的静态方法, wait是Object类的方法
- sleep 属于TIME_WAITTING, 自动被唤醒, wait 属于WAITTING, 手动唤醒
- sleep持有锁时, 不会释放资源,wait在执行后, 会释放锁资源
- sleep 在持有锁或未持有锁时都可执行, wait必须在持有锁的时候才能执行
CAS是什么
Compare and swap, 在替换内存中的值时, 先判断是否与预期的值一致, 一致就执行, 保证原子性操作。
ABA问题: 1.加版本号, 2.加锁
线程安全
多个线程在访问同一对象的时候, 要保证对象的属性不会被破坏,保证每个线程在执行的过程中不会出现竞争的情况
实现:
加 synchronized 关键字, 当一个线程进入时, 其它线程无法进入。 互斥.
使用Atomic类来实现, 如AtomicInteger、AtomicLong等, 提供了原子方法如:incrementAndGet()、getAndIncrement()等。
使用ThreadLocal类来实现, 可创建线程局部变量。
java中锁的类型
可重入锁与不可重入锁
- 可重入锁:如 synchronized、ReentrantLock、ReentrantReadWriteLock, 当前线程获取到A锁, 在获取到后再次尝试获取锁也可获取到
- 不可重入锁:当前线程获取到A锁, 再次尝试获取无法获取到, 需要释放后
乐观锁与悲观锁
- synchronized、ReentrantLock、ReentrantReadWriteLock都是悲观锁。先加锁的都是悲观锁,线程会挂起
- Java中提供的CAS就是乐观锁的一种实现。 Atomic原子类中, 就是乐观锁实现的。 获取不到锁可重新尝试
公平锁与非公平锁
synchronized是非公平锁,ReentrantLock、ReentrantReadWriteLock可公平可不公平
- 公平锁:排队的方式,A获取锁, B没获得锁,B等待, C线程过来, 锁被A持有, 排在B的后面
- 非公平锁:。C线程过来先尝试获取锁, 获得后就成功, 未获取后再排到B后面
互斥锁和共享锁
跟mysql的读锁和写锁差不多
synchronized 实现原理
synchronized是基于对象实现的, 对象中记录了锁在不同状态下的参数值
synchronized与ReentrantLock的区别
synchronized 是一个关键字, 可作用在方法或代码块中, 锁自动释放
ReentrantLock 是一个类, new 出来后调用 lock 与 unlock 方法,需要手动释放锁
ThreadLocal内存泄漏问题
ThreadLocal内部维护了一个ThreadLocalMap 的map对象
造成内存泄漏主要包括两个方向, 一个是map的key, 采用弱引用解决了。
另一个是value的内存泄漏, 主要出现在连接池当中, 因为线程一直存在, 所以值就一直存在, 可在线程回收时执行remove方法去掉
线程池参数
- 核心线程数
- 最大线程数
- 等待时间
- 等待时间单位
- 等待队列
- 队列生成工厂
- 拒绝策略
泛型
Java中的泛型是一种编程语言的一种类型参数化的形式。简单来说,它允许你在定义类、接口和方法时使用类型参数,以便在使用时可以接受不同的实际类型。
泛型的主要目的是提高代码的可重用性和可读性。在没有泛型的情况下,我们可能需要编写大量重复的代码来处理不同的数据类型。而有了泛型,我们可以用一个通用的代码来处理所有类型的数据。
Java17对比Java8新特性
-
封闭类
public sealed class Shape permits Circle, Rectangle, Triangle { public abstract double area(); } public final class Circle extends Shape { private final double radius; public Circle(double radius) { this.radius = radius; } public double area() { return Math.PI * radius * radius; } } public final class Rectangle extends Shape { private final double width, height; public Rectangle(double width, double height) { this.width = width; this.height = height; } public double area() { return width * height; } } public final class Triangle extends Shape { private final double base, height; public Triangle(double base, double height) { this.base = base; this.height = height; } public double area() { return 0.5 * base * height; } } public class ShapeExample { public static void main(String[] args) { Shape shape = new Circle(2.0); System.out.println(shape.area()); } } // ...```
抽象的 Shape 类,使用 permits 关键字限制它只能被 Circle、Rectangle 和 Triangle 这三个子类继承。
-
文本块
String html = """ <html> <body> <h1>Hello, world!</h1> </body> </html> """;
-
Switch 表达式
int num = 5; int square = switch (num) { case 1, 2, 3 -> num; default -> num * num; }; System.out.println(square);
-
垃圾回收器
JDK 17 中对 ZGC(可伸缩的低延迟垃圾回收器)进行了一些改进,其中包括了针对 MappedByteBuffer 和堆外内存的改进,使得它们可以更快地进行垃圾回收,提高了应用程序的性能。
Mybatis
工作原理
- 系统加载全局配置文件,以及映射文件, 放到Configuration中
- 创建SqlSessionFactory工厂(绑定Configuration,只需要创建一次, 单例)
- 从SqlSessionFactory获取SqlSession对象
- 通过SqlSession提供的api方法来操作数据库
- 处理完关闭会话
设计模式
- 缓存模块:装饰器模式
- 日志模块:适配器模式、代理模式
- 反射模块:工厂模式、装饰器模式
- Mapping:代理模式
- SqlSessionFactory:SqlSessionFactoryBuilder 建造者模式
分页
- 逻辑分页: RowBounds, 全查出来, 然后逻辑分页
- 物理分页: 通过mybatis拦截器, 在查询的时候加上limit关键字
Spring中如何解决DefaultSqlSession数据安全问题的
DefaultSqlSession是一个非线程安全的, 不能放到成员变量中
Spring中提供了一个SqlSessionTemplate来实现SqlSession的定义, 它里面每一个方法都调用SqlSessionProxy来操作,这是一个动态代码的对象。 在代理对象中通过方法级别来创建SqlSession对象
如何获取自增主键的值
Mybatis自带的insert方法可以把自增主键插入到对象中
自定的的insert 语句需要在xml中加入两个属性, 把自增主键插入到对象中
<insert id="xxx" useGeneratedKeys="true" keyProperty="id">
Spring
什么是SpringBoot
Spring Boot是Spring家族的一个子项目,其设计初衷是为了简化Spring配置,从而可以轻松构建独立运行的程序,并极大提高开发效率。
SpringBoot的核心注解是什么?请列举其中包含的注解。
SpringBoot的核心注解主要包括:
- @SpringBootApplication:这是Spring Boot最核心的注解,它由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解组合而成。这个注解主要用来开启Spring Boot的各项能力,并自动配置文件。
- @Configuration:该注解表示当前类是一个配置类,Spring Boot在启动时会扫描该类,并将其作为配置文件。
- @SpringBootConfiguration:这个注解表示当前类是一个Spring Boot的配置类,它实现了配置文件的功能。
- @EnableAutoConfiguration:这个注解打开自动配置功能,让Spring Boot根据项目中的依赖关系自动配置Bean。
- @ComponentScan:这个注解用来代替配置文件中的component-scan配置,开启组件扫描,自动扫描包路径下的@Component注解进行注册bean实例放到context(容器)中。
以上就是SpringBoot的核心注解及其包含的注解。
Springboot2 和 Springboot3的区别
-
最低运行环境的区别
Springboot2最低运行环境为java8, 同时支持java9。 SpringBoot3最低运行环境为java17, 并支持java19
Spring Boot2基于Spring Framework5开发;而SpringBoot3构建基于Spring Framework6之上,需要使用Spring Framework6。
-
GraalVM支持的区别
相比SpringBoot2,SpringBoot3的Spring Native也是升级的一个重大特性,支持使用GraalVM将Spring的应用程序编译成本地可执行的镜像文件,可以显著提升启动速度、峰值性能以及减少内存使用。
-
依赖项的区别
删除了一些附加的依赖项,包括Apache ActiveMQ、Atomikos、EhCache2和HazelCast3。
AOP面向切面编程
-
如何理解AOP
以日志为例,在很多管理系统,比如订单系统、推送系统等等都需要把日志记录下来。如果每个业务逻辑里面都写日志的相关代码,那就重复太多了。
把日志的相关逻辑代码,统一封装起来。然后在需要的地方嵌入即可。AOP 也主要就是做嵌入这件事的。
-
为什么要引入aop
主要是为在解决在业务开发中的一些痛点
- 在开发当中,相互之间都是模块化开发,使用 AOP 可以有效的实现模块化的思路。
- 将辅助逻辑(日志、安全、监控等)从业务主体逻辑中进行剥离,同步进行开发。
-
对aop的看法
任何新技术的出现都是为了解决目前开发中存在的某些痛点。对于 aop 来说,其主要是把一些功能代码进行抽象封装,和主业务逻辑代码进行剥离。在需要的地方进行值入即可。
Spring Bean的生命周期
-
实例化
当Spring容器加载配置文件时,它将实例化所有的Bean。在这个阶段,Spring会使用Java反射机制创建一个对象实例。
-
属性赋值
在实例化Bean之后,Spring将使用依赖注入或属性设置方法来设置Bean的属性值。
-
初始化
BeanPostProcessor前置处理;检查是否是InitializingBean已决定调用afterPropertiesSet方法;检查是否配置自定义init-method;BeanPostProcessor后置处理。
-
销毁
当应用程序关闭时,Spring容器会销毁所有的Bean。在销毁Bean之前,Spring将调用Bean的销毁方法。如果Bean实现了DisposableBean接口,那么Spring将调用它的destroy()方法。
Autowired和Resource的区别
都是Spring生态里面实现Bean的依赖注入的
Autowired: 是spring提供的注解, 有个required属性, 如果为true, 则Spring启动的时候必须要包含这个bean。Autowired是根据类型来注入Bean的, 如果同时包含多个相同类型的Bean,则根据名称来注入。在使用时如找不到名称,启动时会报错, 可以使用
@Primary
指定默认使用哪个Bean。也可使用Qualifier
注解在使用的时候指定用哪个名称的BeanResource:是jdk提供的注解, Spring支持了这个。 可使用byName与byType两种方式去找到使用的Bean。 如都未配置, 优先使用名称来找, 名称找不到的话再使用类型来找。 如找到多个类型相同的Bean, 则报运行时错误。
Mysql
常用存储引擎以及其区别
- Innodb:默认存储引擎,支持事务处理和行级锁定。
- MyISAM:支持表级锁定和全文本搜索,不支持事务,并发性能差
聚簇索引和非聚簇索引的区别
聚簇索引:索引的叶子节点存的整行数据, 查询速率快
非聚簇索引:索引和数据分开存储, 索引的叶子节点指向数据对应的位置
索引有哪几种类型
- 普通索引
- 唯一索引
- 主键索引
- 联合索引:最左前缀原则,首先会对联合索引最大左边的字段进行排序,在第一个字段的的基础之上,再对第二个字段进行排序
为什么模糊搜索%在左边会使索引失效
由B+树索引顺序决定, 是按照首字母大小进入排序,%在左没办法匹配到首字母
InnoDB和MyISAM的区别
InnoDB:支持事务、行锁、外键、聚簇索引(索引和记录都在叶子节点)
MyISAM:不支持事务、只支持表锁,高并发不友好,有个值记录了表长度(select count(*) from xxx 很快),非聚簇索引(索引和记录分开)
B树和B+树的区别
B树:索引值和数据分部到整颗树的结构中, 会使树的高度变高
B+树:数据只存放到叶子节点中、 所有叶子节点之间还增加了指针
Mysql执行一条查询时内部流程
客户端执行语句->查询缓存(mysql5.7)->解析器->解析树->预处理器->新解析树->查询优化器->执行计划->引擎调用磁盘获取数据->返回数据
binlog、redolog、undolog作用
Binlog: 主从复制和数据恢复
Redolog: redo log是用来恢复数据的用于保障, 宕机时也可用此日志来恢复,已提交事务的持久化特性
Undolog: undo log是用来回滚数据的用于保障未提交事务的原子性
Relaylog:主从复制时从库拿到主库的binlog日志时首先放进此日志中, 然后再执行
事务的4个特性以及说明
- 原子性:事务必须是一个不可分割的操作序列单元,要么全部成功,要么全部失败。
- 一致性:事务执行前后,数据库都必须处于一致性状态,不能破坏数据的完整性和一致性。
- 隔离性:事务之间是隔离开的,一个事务看不到另一个事务正在操作的数据。
- 持久性:一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使数据库崩溃也能通过某种机制将数据恢复到提交后的状态。
Redis
使用场景、分类
- String:计数器、分布式锁
- Hash:分部式Session、存储对象
- List:消息队列、分页文章(有序的)
- Set:存储列表不需要重复且不需要顺序时使用
- ZSet:排行榜
为什么Redis6.0之前不用多线程
- 使用redis, cpu不是瓶颈、受制于内存和网络
- Pipline批量处理, 每秒100万次请求
- 单线程,内部维护比较低
- 多线程的话, 需要线程切换,加解锁, 可能导致死锁
为什么Redis6.0要引入多线程
大公司需要更高的QPS。多线程其实是IO的多线程,内部执行命令还是单线程
Redis对比Memcached
Redis:丰富的数据结构、单线程、支持事务、支持持久化、支持集群
Memcached:仅支持Key->value结构、多线程、不支持持久化
过期策略与内存淘汰机制
过期策略:
定时扫描redis中过期的key
惰性删除:客户端访问这个key时, 如果这个key已经过期, 就删除, 如果过期后未访问, 就会一直存在
淘汰机制(内存不足时,删除设置了过期时间的key)
LRU:删除最早未使用的key(链接)
TTL:过期时间越小, 越先淘汰
RONDOM:随机
缓存穿透与缓存雪崩
- 穿透:伪造不存在的key, 缓存中不存在, 就会查数据库。 过滤器,如无, 直接返回
- 雪崩:
- 大量缓存在同一时间失效。1.业务端设置缓存过期不要在同一时间。 2.加锁, 过期后只允许一个线程去刷新缓存
- redis故障,引入集群
持久化方式
- RDB : 通过几行配置, 多少秒更新多少次就触发
- AOF:将修改的每一条指令追加写进文件 appendonly.aof 中,每隔多少秒触发一次
如何保证缓存与数据库双写数据一致性
一般采用更新数据库后删除缓存的策略。 另一种是延时双删策略: 删除缓存->更新数据库->延时1秒后再次删除缓存
队列
常见消息队列比较
- ActiveMQ: 并发 6000/单机、持久化 支持、多语言支持、缺乏大规模使用(老)
- RabbitMQ:并发12000/单机、支持持久化、多语言支持、集群不支持动态扩展
- RocketMQ:并发100000/单机、支持持久化、只支持java、阿里大规模使用
- Kafka: 并发100w/单机、支持持久化、多语言支持、大数据、天生分部式、动态易扩展、运维难度大,带宽要求高、流处理
RabbitMQ里面的queue是否有限制?
默认无限制, 可通过x-max-length配置最多消息条数, 也可通过x-max-length-bytes对单条消息的大小进行限制
RabbitMQ 交换器4种类型
- Direct:直连,需要routeKey,一个消息由一个队列收到
- Fanout:广播, 不需要routeKey, 多个队列都能收到消息
- Topic:主题, routeKey正则通配, 匹配上的队列能收到消息
- Headers:跟Direct差不多, 被替换
如何解决消息重复消费
消费端幂等性处理:
MVCC多版本控制(生产的时候带上数据版本号): update inventory set stock = stock - 1, version = version + 1 where id = :id and version = :version, 消息生产的时候带version这个值
根据业务, 可利用数据库唯一索引的特性
转载时请注明出处及相应链接。
本文永久链接: https://blog.baigei.com/articles/java-base