当前位置: 博客首页>> 学习笔记 >> 阅读正文

JAVA基础整理

作者: 分类: 学习笔记 发布于: 2024-06-14 16:28:07 浏览:2,472 评论(0)


基础整理

JAVA

JAVA 中的几种基本数据类型是什么,各自占用多少字节。

Java中有八种基本数据类型

  1. byte----->1字节
  2. short---->2字节
  3. int------>4字节
  4. long----->8字节
  5. char----->2字节
  6. float---->4字节
  7. double--->8字节
  8. boolean--->1bit

java中4种引用类型

  1. 强: User user = new User(); 不会被系统回收, 在方法内会随着方法栈弹出
  2. 软:SoftReference; 系统内存不足时才回收
  3. 弱:只要垃圾回收机制触发, 都会被回收, 不管内存是否足。如ThreadLocal
  4. 虚:必须配合引用队列联合使用,跟踪对象被垃圾回收的状态。永远返回null,提供一个方法isEnQueued判断对象是否已销毁,可用于监控对象的创建和销毁

java中线程实现方式

  1. 继承Thread类, 实现run方法

  2. 实现Runnable接口, 重写run方法 (底层都是使用这个)

  3. 实现Callable重写call方法, 配合FutureTask可以拿到执行结果

  4. 基于线程池构建线程

equals== 的区别

==: 比较基本数据类型时比较的是数据的值,而比较引用类型时比较的是两个对象的地址值。 如:int a=10 与 long b=10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。 equals: 用来比较的是两个对象的内容是否相等。

String、StringBuffer、 和 StringBuilder 的区别是什么?

String是只读字符串,每次修改都会生成一个新的对象

StringBuffer 同一内存空间, 线程安全

StringBuilder 同一内存空间, 无线程锁,多线程不安全

ArrayList 和 LinkedList的区别

ArrayList:连续的内存空间。读取指定位置的数据很快,直接通过索引访问。 频繁读取的列表时使用

LinkedList: 内存分散。 双向链表实现。插入与删除快(通过指针), 读慢(需要从头遍历)。频繁插入与删除的列表时使用

HashMapHashTable的区别

HashTable: 线程安全、不允许使用空键或空值、继承自Dictionary类、提供elements(), keys(), values()等方法

HashMap: 不是线程安全的、允许使用一个空键,空值允许、继承自AbstractMap类。 建议使用ConcurrentHashMap类

sleep 和 wait 的区别

  1. sleep 属于 Thread 类中的静态方法, wait是Object类的方法
  2. sleep 属于TIME_WAITTING, 自动被唤醒, wait 属于WAITTING, 手动唤醒
  3. sleep持有锁时, 不会释放资源,wait在执行后, 会释放锁资源
  4. sleep 在持有锁或未持有锁时都可执行, wait必须在持有锁的时候才能执行

CAS是什么

Compare and swap, 在替换内存中的值时, 先判断是否与预期的值一致, 一致就执行, 保证原子性操作。

ABA问题: 1.加版本号, 2.加锁

线程安全

多个线程在访问同一对象的时候, 要保证对象的属性不会被破坏,保证每个线程在执行的过程中不会出现竞争的情况

实现:

  1. 加 synchronized 关键字, 当一个线程进入时, 其它线程无法进入。 互斥.

  2. 使用Atomic类来实现, 如AtomicInteger、AtomicLong等, 提供了原子方法如:incrementAndGet()、getAndIncrement()等。

  3. 使用ThreadLocal类来实现, 可创建线程局部变量。

java中锁的类型

  1. 可重入锁与不可重入锁

    1. 可重入锁:如 synchronized、ReentrantLock、ReentrantReadWriteLock, 当前线程获取到A锁, 在获取到后再次尝试获取锁也可获取到
    2. 不可重入锁:当前线程获取到A锁, 再次尝试获取无法获取到, 需要释放后
  2. 乐观锁与悲观锁

    1. synchronized、ReentrantLock、ReentrantReadWriteLock都是悲观锁。先加锁的都是悲观锁,线程会挂起
    2. Java中提供的CAS就是乐观锁的一种实现。 Atomic原子类中, 就是乐观锁实现的。 获取不到锁可重新尝试
  3. 公平锁与非公平锁

    synchronized是非公平锁,ReentrantLock、ReentrantReadWriteLock可公平可不公平

    1. 公平锁:排队的方式,A获取锁, B没获得锁,B等待, C线程过来, 锁被A持有, 排在B的后面
    2. 非公平锁:。C线程过来先尝试获取锁, 获得后就成功, 未获取后再排到B后面
  4. 互斥锁和共享锁

    跟mysql的读锁和写锁差不多

synchronized 实现原理

synchronized是基于对象实现的, 对象中记录了锁在不同状态下的参数值

synchronized与ReentrantLock的区别

synchronized 是一个关键字, 可作用在方法或代码块中, 锁自动释放

ReentrantLock 是一个类, new 出来后调用 lock 与 unlock 方法,需要手动释放锁

ThreadLocal内存泄漏问题

ThreadLocal内部维护了一个ThreadLocalMap 的map对象

造成内存泄漏主要包括两个方向, 一个是map的key, 采用弱引用解决了。

另一个是value的内存泄漏, 主要出现在连接池当中, 因为线程一直存在, 所以值就一直存在, 可在线程回收时执行remove方法去掉

线程池参数

  1. 核心线程数
  2. 最大线程数
  3. 等待时间
  4. 等待时间单位
  5. 等待队列
  6. 队列生成工厂
  7. 拒绝策略

泛型

Java中的泛型是一种编程语言的一种类型参数化的形式。简单来说,它允许你在定义类、接口和方法时使用类型参数,以便在使用时可以接受不同的实际类型。

泛型的主要目的是提高代码的可重用性和可读性。在没有泛型的情况下,我们可能需要编写大量重复的代码来处理不同的数据类型。而有了泛型,我们可以用一个通用的代码来处理所有类型的数据。

Java17对比Java8新特性

  1. 封闭类

    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 这三个子类继承。

  2. 文本块

    String html = """
        <html>
            <body>
                <h1>Hello, world!</h1>
            </body>
        </html>
        """;
    
  3. Switch 表达式

    int num = 5;
    int square = switch (num) {
        case 1, 2, 3 -> num;
        default -> num * num;
    };
    System.out.println(square);
    
  4. 垃圾回收器

    JDK 17 中对 ZGC(可伸缩的低延迟垃圾回收器)进行了一些改进,其中包括了针对 MappedByteBuffer 和堆外内存的改进,使得它们可以更快地进行垃圾回收,提高了应用程序的性能。

Mybatis

工作原理

  1. 系统加载全局配置文件,以及映射文件, 放到Configuration中
  2. 创建SqlSessionFactory工厂(绑定Configuration,只需要创建一次, 单例)
  3. 从SqlSessionFactory获取SqlSession对象
  4. 通过SqlSession提供的api方法来操作数据库
  5. 处理完关闭会话

设计模式

  1. 缓存模块:装饰器模式
  2. 日志模块:适配器模式、代理模式
  3. 反射模块:工厂模式、装饰器模式
  4. Mapping:代理模式
  5. SqlSessionFactory:SqlSessionFactoryBuilder 建造者模式

分页

  1. 逻辑分页: RowBounds, 全查出来, 然后逻辑分页
  2. 物理分页: 通过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的核心注解主要包括:

  1. @SpringBootApplication:这是Spring Boot最核心的注解,它由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan三个注解组合而成。这个注解主要用来开启Spring Boot的各项能力,并自动配置文件。
  2. @Configuration:该注解表示当前类是一个配置类,Spring Boot在启动时会扫描该类,并将其作为配置文件。
  3. @SpringBootConfiguration:这个注解表示当前类是一个Spring Boot的配置类,它实现了配置文件的功能。
  4. @EnableAutoConfiguration:这个注解打开自动配置功能,让Spring Boot根据项目中的依赖关系自动配置Bean。
  5. @ComponentScan:这个注解用来代替配置文件中的component-scan配置,开启组件扫描,自动扫描包路径下的@Component注解进行注册bean实例放到context(容器)中。

以上就是SpringBoot的核心注解及其包含的注解。

Springboot2 和 Springboot3的区别

  1. 最低运行环境的区别

    Springboot2最低运行环境为java8, 同时支持java9。 SpringBoot3最低运行环境为java17, 并支持java19

    Spring Boot2基于Spring Framework5开发;而SpringBoot3构建基于Spring Framework6之上,需要使用Spring Framework6。

  2. GraalVM支持的区别

    相比SpringBoot2,SpringBoot3的Spring Native也是升级的一个重大特性,支持使用GraalVM将Spring的应用程序编译成本地可执行的镜像文件,可以显著提升启动速度、峰值性能以及减少内存使用。

  3. 依赖项的区别

    删除了一些附加的依赖项,包括Apache ActiveMQ、Atomikos、EhCache2和HazelCast3。

AOP面向切面编程

  1. 如何理解AOP

    以日志为例,在很多管理系统,比如订单系统、推送系统等等都需要把日志记录下来。如果每个业务逻辑里面都写日志的相关代码,那就重复太多了。

    把日志的相关逻辑代码,统一封装起来。然后在需要的地方嵌入即可。AOP 也主要就是做嵌入这件事的。

  2. 为什么要引入aop

    主要是为在解决在业务开发中的一些痛点

    1. 在开发当中,相互之间都是模块化开发,使用 AOP 可以有效的实现模块化的思路。
    2. 将辅助逻辑(日志、安全、监控等)从业务主体逻辑中进行剥离,同步进行开发。
  3. 对aop的看法

    任何新技术的出现都是为了解决目前开发中存在的某些痛点。对于 aop 来说,其主要是把一些功能代码进行抽象封装,和主业务逻辑代码进行剥离。在需要的地方进行值入即可。

Spring Bean的生命周期

  1. 实例化

    当Spring容器加载配置文件时,它将实例化所有的Bean。在这个阶段,Spring会使用Java反射机制创建一个对象实例。

  2. 属性赋值

    在实例化Bean之后,Spring将使用依赖注入或属性设置方法来设置Bean的属性值。

  3. 初始化

    BeanPostProcessor前置处理;检查是否是InitializingBean已决定调用afterPropertiesSet方法;检查是否配置自定义init-method;BeanPostProcessor后置处理。

  4. 销毁

    当应用程序关闭时,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注解在使用的时候指定用哪个名称的Bean

Resource:是jdk提供的注解, Spring支持了这个。 可使用byName与byType两种方式去找到使用的Bean。 如都未配置, 优先使用名称来找, 名称找不到的话再使用类型来找。 如找到多个类型相同的Bean, 则报运行时错误。

Mysql

常用存储引擎以及其区别

  1. Innodb:默认存储引擎,支持事务处理和行级锁定。
  2. MyISAM:支持表级锁定和全文本搜索,不支持事务,并发性能差

聚簇索引和非聚簇索引的区别

聚簇索引:索引的叶子节点存的整行数据, 查询速率快

非聚簇索引:索引和数据分开存储, 索引的叶子节点指向数据对应的位置

索引有哪几种类型

  1. 普通索引
  2. 唯一索引
  3. 主键索引
  4. 联合索引:最左前缀原则,首先会对联合索引最大左边的字段进行排序,在第一个字段的的基础之上,再对第二个字段进行排序

为什么模糊搜索%在左边会使索引失效

由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个特性以及说明

  1. 原子性:事务必须是一个不可分割的操作序列单元,要么全部成功,要么全部失败。
  2. 一致性:事务执行前后,数据库都必须处于一致性状态,不能破坏数据的完整性和一致性。
  3. 隔离性:事务之间是隔离开的,一个事务看不到另一个事务正在操作的数据。
  4. 持久性:一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使数据库崩溃也能通过某种机制将数据恢复到提交后的状态。

Redis

使用场景、分类

  1. String:计数器、分布式锁
  2. Hash:分部式Session、存储对象
  3. List:消息队列、分页文章(有序的)
  4. Set:存储列表不需要重复且不需要顺序时使用
  5. ZSet:排行榜

为什么Redis6.0之前不用多线程

  1. 使用redis, cpu不是瓶颈、受制于内存和网络
  2. Pipline批量处理, 每秒100万次请求
  3. 单线程,内部维护比较低
  4. 多线程的话, 需要线程切换,加解锁, 可能导致死锁

为什么Redis6.0要引入多线程

大公司需要更高的QPS。多线程其实是IO的多线程,内部执行命令还是单线程

Redis对比Memcached

Redis:丰富的数据结构、单线程、支持事务、支持持久化、支持集群

Memcached:仅支持Key->value结构、多线程、不支持持久化

过期策略与内存淘汰机制

  1. 过期策略:

    定时扫描redis中过期的key

    惰性删除:客户端访问这个key时, 如果这个key已经过期, 就删除, 如果过期后未访问, 就会一直存在

  2. 淘汰机制(内存不足时,删除设置了过期时间的key)

    LRU:删除最早未使用的key(链接)

    TTL:过期时间越小, 越先淘汰

    RONDOM:随机

缓存穿透与缓存雪崩

  1. 穿透:伪造不存在的key, 缓存中不存在, 就会查数据库。 过滤器,如无, 直接返回
  2. 雪崩:
    1. 大量缓存在同一时间失效。1.业务端设置缓存过期不要在同一时间。 2.加锁, 过期后只允许一个线程去刷新缓存
    2. redis故障,引入集群

持久化方式

  1. RDB : 通过几行配置, 多少秒更新多少次就触发
  2. AOF:将修改的每一条指令追加写进文件 appendonly.aof 中,每隔多少秒触发一次

如何保证缓存与数据库双写数据一致性

一般采用更新数据库后删除缓存的策略。 另一种是延时双删策略: 删除缓存->更新数据库->延时1秒后再次删除缓存

队列

常见消息队列比较

  1. ActiveMQ: 并发 6000/单机、持久化 支持、多语言支持、缺乏大规模使用(老)
  2. RabbitMQ:并发12000/单机、支持持久化、多语言支持、集群不支持动态扩展
  3. RocketMQ:并发100000/单机、支持持久化、只支持java、阿里大规模使用
  4. Kafka: 并发100w/单机、支持持久化、多语言支持、大数据、天生分部式、动态易扩展、运维难度大,带宽要求高、流处理

RabbitMQ里面的queue是否有限制?

默认无限制, 可通过x-max-length配置最多消息条数, 也可通过x-max-length-bytes对单条消息的大小进行限制

RabbitMQ 交换器4种类型

  1. Direct:直连,需要routeKey,一个消息由一个队列收到
  2. Fanout:广播, 不需要routeKey, 多个队列都能收到消息
  3. Topic:主题, routeKey正则通配, 匹配上的队列能收到消息
  4. Headers:跟Direct差不多, 被替换

如何解决消息重复消费

消费端幂等性处理:

  1. MVCC多版本控制(生产的时候带上数据版本号): update inventory set stock = stock - 1, version = version + 1 where id = :id and version = :version, 消息生产的时候带version这个值

  2. 根据业务, 可利用数据库唯一索引的特性

       

转载时请注明出处及相应链接。

本文永久链接: https://blog.baigei.com/articles/java-base