Java秋招面经(一)

1、mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。

2、链表适合遍历不适合随机访问。hash表不适合遍历,图强调数据之间的关系。链表插入删除快。数组随机查找快

3、数组在内存中的分配是连续的,对内存的使用要求高,需要一整块内存存放数据,无法动态扩容。

链表在内存中是非连续的,可以更好的利用内存碎片

4、B和B+树区别:

  • B+树内部有两种节点,一种是索引节点,一种是叶子节点
  • B+树的索引节点并不会保存记录,只用于索引,所有的数据都保存在B+树的叶子节点中。而B树则是所有节点都会保存数据。
  • B+树的叶子节点都会被练成一条链表。叶子本身按索引值的大小从小到大进行排序。即这条链表是从小到大的。多了条链表方便范围查找数据。
  • B树的所有索引值是不会重复的,而B+树非叶子节点的索引值最终一定会全部出现在叶子节点中。

MySQL底层使用B+树

5、红黑树:根节点为黑色,每个叶子节点都是为空的黑色节点,每个红色节点的两个子节点都是黑色,从根节点到叶子节点不能有两个连续的红色节点。

6、堆中某个节点的值总是不大于或不小于其父节点的值,堆是一颗完全二叉树。

7、二叉树k层最多2(k-1)个节点, 整棵树最多2k-1个节点。

8、每个节点有两个指针,共计2n个指针,除根结点外,每出现一个节点会占用其父节点的一个指针域,所有占用n-1个,空余n+1个指针域。

9、节点数=分叉数+1 N0 + N1 + N2 + …+Nm = N1+2N2 + …+ mNm

10、HashCode和equals方法重要性体现在哪:Java中的HashMap使用hashCode和equals来确定键值对应的索引,根据键获取值的时候也会用到这两个方法。如果没有正确的实现这两个方法,不同的键也会有相同的hash值。

11、判断有没有走索引:explain 你的sql语句(key、possible_key)

12、Java s1 = new String()指向堆 s1.intern()指向常量池

13、MySQL中delete、drop、truncate区别:

(1)delete仅删除表数据,会走事务,可以回滚。delete操作以后使用 optimize table 会立刻释放磁盘空间。不管是InnoDB还是MyISAM 。带条件的删除, 不管是InnoDB还是MyISAM都不会释放磁盘空间;删除表的全部数据,对于MyISAM 会立刻释放磁盘空间,InnoDB 不会释放磁盘空间。

(2)drop可以用于删除数据库、表及索引。

(3)truncate语句用于清除表中的所有数据。

drop> truncate > delete

14、Synchronized关键字,用来加锁。 Volatile只是保持变量的线程可见性。通常适用于一个线程写,多个线程读的场景。

​ Volatile关键字只能保证线程可见性, 不能保证原子性。不能保证线程安全性。

15、当 final 修饰类时,此类不允许被继承,表示此类设计的很完美,不需要被修改和扩展。

​ 当 final 修饰方法时,此方法不允许任何从此类继承的类来重写此方法,表示此方法提供的功能已经满足当前要求,不需要进行扩展。

​ 当 final 修饰变量时,表示该变量一旦被初始化便不可以被修改。

​ 当 final 修饰参数时,表示此参数在整个方法内不允许被修改。

16、String重写了equals方法,把引用比较变成了值比较。

17、守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 java 中垃圾回收线程就是特殊的守护线程。

18、CAS维护了三个变量值:当前内存值V,旧的预期值A、即将更新的值B。通过while循环不断的读取内存中的值V和A作比较,如果V=A,则将A更新为B

19、《码出高效》、《深入理解 Java 虚拟机》 、《Redis 深度历险》、《MySQL 45讲》

20、线程和进程有哪些状态:新建、就绪、执行、阻塞、死亡

21、①如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
②如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。

22、TCP和UDP

3a819be58c17309e0cd5ac309290d7f.png

22、红黑树:

  1. 节点不是黑色,就是红色(非黑即红)
  2. 根节点为黑色
  3. 叶节点为黑色(叶节点是指末梢的空节点 NilNull
  4. 一个节点为红色,则其两个子节点必须是黑色的(根到叶子的所有路径,不可能存在两个连续的红色节点)
  5. 每个节点到叶子节点的所有路径,都包含相同数目的黑色节点(相同的黑色高度)

23、为什么不直接采用红黑树:

时间: 因为Map中桶的元素初始化是链表保存的,其查找性能是O(n),而树结构能将查找性能提升到O(log(n))。当链表长度很小的时候,即使遍历,速度也非常快,但是当链表长度不断变长,肯定会对查询性能有一定的影响,所以才需要转成树。

空间:因为红黑树需要进行左旋,右旋操作, 而单链表不需要,TreeNodes占用空间是普通Nodes的两倍,所以只有当bin包含足够多的节点时才会转成TreeNodes,当bin中节点数变少时,又会转成普通的bin。并且我们查看源码的时候发现,链表长度达到8就转成红黑树,当长度降到6就转成普通bin。

(之所以选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。

而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题,我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少,所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。)

24、redis的淘汰策略:

前面说的过期删除策略,是删除已过期的 key,而当 Redis 的运行内存已经超过 Redis 设置的最大内存之后,则会使用内存淘汰策略删除符合条件的 key,以此来保障 Redis 高效的运行。

  • 不进行数据淘汰的策略

  • 进行数据淘汰的策略

在设置了过期时间的数据中进行淘汰:

  • volatile-random: 随机淘汰设置了过期时间的任意键值;

  • volatile-ttl: 优先淘汰更早过期的键值。

  • volatile-lru(Redis3.0 之前,默认的内存淘汰策略): 淘汰所有设置了过期时间的键值中,最久未使用的键值;

  • volatile-lfu(Redis 4.0 后新增的内存淘汰策略): 淘汰所有设置了过期时间的键值中,最少使用的键值;

在所有数据范围内进行淘汰:

  • allkeys-random: 随机淘汰任意键值;
  • allkeys-lru: 淘汰整个键值中最久未使用的键值;
  • allkeys-lfu(Redis 4.0 后新增的内存淘汰策略): 淘汰整个键值中最少使用的键值。

25、redis的过期策略:

  • 定时删除:在设置 key 的过期时间时,同时创建一个定时事件,当时间到达时,由事件处理器自动执行 key 的删除操作。
  • 惰性删除:不主动删除过期键,每次从数据库访问 key 时,都检测 key 是否过期,如果过期则删除该 key。
  • 定期删除:每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。

26、redis有一个过期字典:

typedef struct redisDb {
    dict *dict;    /* 数据库键空间,存放着所有的键值对 */
    dict *expires; /* 键的过期时间 */
    ....
} redisDb;

27、如何开启线程:继承Thread类重写run方法。实现Runable接口,实现run方法。实现Callable接口,实现call方法,通过FutureTask创建一个线程,获取线程执行的返回值。通过线程池开启线程。

28、保证线程安全:jvm提供的synchronized关键字、jdk提供的lock锁

29、volatile和synchronized:

synchronized用来加锁,volatile保持变量线程可见性,适用于一个线程写,多个线程读的情况。

volatile只能保证线程可见性,不能保证原子性,不能保证线程安全性。

在DCL场景下,volatile防止指令重排,在高并发情况下防止指令重排造成的线程安全问题。

30、Java的锁就是在markword中记录一个锁状态。无锁、偏向锁、轻量级锁、重量级锁对应不同的锁状态。

​ JAVA的锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程。

3、[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YOu2Avtf-1660623748395)(1615192248858.png)]

31、AOP理念:将分散在各个业务逻辑代码中的相同代码通过横向切割的方式抽取到独立模块中

​ 底层原理:动态代理 jdk动态代理(默认) cglib动态代理

常见注解

@Aspect : 声明这是一个切面类, 需要同 @Component 一起使用, 表明交由 Spring 管理

@Pointcut : 定义一个切点, 存在两种表达方式 :

  • execution() 匹配方法
  • annotation() 匹配注解

@Around : 增强处理, 表示环绕增强, 可以任意执行

@Before : 表示在切点方法前执行

@After : 表示在方法后执行

@AfterReturning : 与 @After 类似, 但可以捕获切点方法返回值进行增强处理

@AfterThrowing : 在方法抛出异常时执行

32、MySQL索引失效:

1、被索引字段,发生了隐式类型转换

2、被索引字段使用了表达式计算

3、被索引字段使用了函数

4、在like关键字后使用左模糊匹配’%##’

5、被使用的索引字段,不是联合索引的最左字段

33、 where、having、on区别

WHERE是在GROUP BY之前执行的,所以WHERE的后面是不能使用聚合函数来进行数据过滤的,只能使用FROM表里的字段来进行数据过滤;

HAVING是在GROUP BY之后执行的,那么这些数据就都已经分过组了的,可以使用聚合函数来进行数据的分组过滤。

img

查询人数不小于2的省份和人数我们先用WHERE来试验

SELECT 省份,COUNT(*) 人数
FROM Customers
WHERE COUNT(*)>1
GROUP BY 省份

报错:聚合函数不应出现在WHERE子句中 我们再使用HAVING来求解

SELECT 省份,COUNT(*) 人数
FROM Customers
GROUP BY 省份
HAVING COUNT(*)>1
img

功能区别:我们知道ON支持左连接和右连接,WHERE是不支持的,WHERE里面只支持内连接,这在功能上是一个较大的区别。

性能区别:我们知道所有的查询都回产生一个中间临时表,查询结果就是从返回临时报表中得到。ON和WHERE后面所跟限制条件的区别,主要与限制条件起作用的时机有关,ON根据限制条件对数据库记录进行过滤,然后生产临时表;而WHERE是在临时表生产之后,根据限制条件从临时表中筛选结果。因为ON限制条件发生时间较早,临时表的数据集要小,因此ON的性能要优于WHERE

34、DateTime类型没有时区信息(8字节),Timestamp类型字段的值会随着服务器时区的变化而变化(4字节)。

# 查看当前会话时区
SELECT @@session.time_zone;
# 设置当前会话时区
SET time_zone = 'Europe/Helsinki';
SET time_zone = "+00:00";
# 数据库全局时区设置
SELECT @@global.time_zone;
# 设置全局时区
SET GLOBAL time_zone = '+8:00';
SET GLOBAL time_zone = 'Europe/Helsinki';

35、并发情况下,读操作可能存在的三类问题:

  1. 脏读:当前事务(A)中可以读到其他事务(B)未提交的数据(脏数据),这种现象是脏读。
  2. 不可重复读:在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。
  3. 幻读:在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了。

36、MySQL索引的最左匹配原则 **会出现索引跳跃扫描(Index Skip Scan) ** Hash 索引不支持多列联合索引的最左匹配规则

create table test
(
    id       bigint auto_increment primary key,
    column_1 bigint null,
    column_2 bigint null,
    column_3 bigint null
);

create index test_column_1_column_2_column_3_index
    on test(column_1, column_2, column_3);

比如上面的test表,我们建立了联合索引index test_column_1_column_2_column_3_index on test (column_1, column_2, column_3);当我们进行查询的时候,按照最左前缀的原则,当查询(column_1)、(column_1,column_2)、(column_1,column_2,column_3)这三种组合是可以用到我们定义的联合索引的。如果我们查询(column_1,column_3)就只能用到column_1的索引了。

为什么会有最左前缀呢?

使用b+树作为索引的存储数据结构时,当我们创建联合索引的时候,比如(column_1, column_2, column_3),b+树建立索引是从左到右来建立搜索树的,比如当我们来查询的时候WHERE column_1 = 1 AND column_2 = 2 AND column_3 = 3。b+树会先通过最左边的(建立索引的字段的左边的字段)字段,也就是column_1来确定下一步的查找对象,然后找到column_2,再通过column_2的索引找到column_3。所以(column_2,column_3)这样的查询命中不到索引了。因为最左前缀,一定是从最左边的字段开始依次在b+树的子节点查询,然后确定下一个查找的子节点的数据。所以我们(column_1)、(column_1,column_2)、(column_1,column_2,column_3)这三种查询条件是可以使用到索引的。( 。)

会出现索引跳跃扫描 ?

CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(255) NOT NULL COMMENT '姓名',
  `gender` tinyint NOT NULL COMMENT '性别',
  PRIMARY KEY (`id`),
  KEY `idx_gender_name` (`gender`,`name`)
) ENGINE=InnoDB COMMENT='用户表';

在性别和姓名两个字段上(gender,name)建立联合索引,性别字段只有两个枚举值。

执行SQL查询验证一下:

explain select * from user where name='一灯';

虽然SQL查询条件只有name字段,但是从执行计划中看到依然是用了联合索引。

37、索引(mysql使用B树B-、B+和hash)优缺点 innodb采用B+

优点:

1.大大加快数据的查询速度

2.唯一索引可以保证数据库表每一行的唯一性

3.加速表连接时间

缺点:

1.创建、维护索引要耗费时间,所以,索引数量不能过多。

2.索引是一种数据结构,会占据磁盘空间。

3.对表进行更新操作时,索引也要动态维护,降低了维护速度

MySQL为什么最终要去选择B+Tree?

(1)B+Tree是B-Tree的变种,B-Tree能解决的问题,B+Tree也能够解决(降低树的高度,增大节点存储数据量)

(2)B+Tree扫库和扫表能力更强。如果我们要根据索引去进行数据表的扫描,对B-Tree进行扫描,需要把整棵树遍历一遍,而B+TREE只需要遍历他的所有叶子节点即可(叶子节点之间有引用)。

(3)B+Tree磁盘读写能力更强。他的根节点和支节点不保存数据区,所以根节点和支节点同样大小的情况下,保存的关键字要比B-Tree要多。所以,B+Tree读写一次磁盘加载的关键字比B-Tree更多。

(4)B+Tree排序能力更强。B+Tree天然具有排序功能。

(5)B+Tree查询性能稳定。B+Tree数据只保存在叶子节点,每次查询数据,查询IO次数一定是稳定的。当然这个每个人的理解都不同,因为在B-Tree如果根节点命中直接返回,确实效率更高。

38、什么时候发生自动装拆箱

① 赋值操作:在包装类型和基本数据类型之间进行赋值操作时,会发生自动拆装箱。

public static void main (String[] args) {
    // i1是Integer包装类型
    Integer i1 = 10;
    // i2是基本数据类型
    int i2 = 20;
    // 进行赋值时,会发生自动拆箱
    i1 = i2;
    System.out.println(i1);
}

②比较:在包装类型与基本数据类型进行比较时,会先将包装类进行拆箱成基本数据类型(Integer→int),然后再进行比较

public static void main (String[] args) {
    // i1是Integer包装类型
    Integer i1 = 10;
    // i2是基本数据类型
    int i2 = 20;
    // 进行比较时,会发生自动拆箱,将Integer转换成int
    if (i1 < i2)
        System.out.println("i1 小于 i2");
}

③运算符:操作符的只能对基本数据类型进行运算,所以在包装类和基本数据类型进行运算时,会发生自动拆箱,将包装类型转换成基本数据类型进行运算。

public static void main (String[] args) {
    // i1是Integer包装类型
    Integer i1 = 10;
    // i2是基本数据类型
    int i2 = 20;
    // 进行操作符运算时时,会发生自动拆箱
    int i3 = i1 + i2;
    System.out.println(i3);
}

④方法传参:有时候我们的方法形参是包装类型,传入基本数据类型时,会发生自动装箱;形参是基本数据类型,传入包装类型时,会发生自动装箱。

// 自动拆箱
public static int getNum1 (Integer a) {
    return a;
}
// 自动装箱
public static Integer getNum2 (int b) {
    return b;
}

39、Redis如何实现分布式ID?

其实redis扮演的角色就是一个计数器的作用。因为redis单线程,生成id自增。

40、Java中存储金额的数据类型,BigDecimal

	@Test
    public void testBigDecimal2(){
        BigDecimal x = new BigDecimal("0.05523545654897545616513");
        System.out.println("保留小数点后 2 位,之后的位数删除:" + x.setScale(2,BigDecimal.ROUND_DOWN));
        System.out.println("保留小数点后 2 位,之后的位数进位:" + x.setScale(2,BigDecimal.ROUND_UP));
        System.out.println("保留小数点后 2 位,之后的位数四舍五入:" + x.setScale(2,BigDecimal.ROUND_HALF_UP));
    }

保留小数点后 2 位,之后的位数删除:0.05
保留小数点后 2 位,之后的位数进位:0.06
保留小数点后 2 位,之后的位数四舍五入:0.06

注意new BigDemical,尽量在构造方法里使用String类型的数据,而不要为了省事使用 double类型的数据

	@Test
    public void testBigDecimal3(){
        System.out.println("double类型:" + new BigDecimal(0.99));
        System.out.println("String类型:" + new BigDecimal("0.99"));
        System.out.println("BigDecimal类型:" + BigDecimal.valueOf(0.99));
    }

double类型:0.9899999999999999911182158029987476766109466552734375
String类型:0.99
BigDecimal类型:0.99

41、Long类型可以用==比较吗?

Long.parseLong()返回long型。

包装类型超过128用equal比较,基础类型可以用==比较

42、线程池介绍下

(1)、线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

(2)、创建Java线程需要给线程分配堆栈内存以及初始化内存,还需要进行系统调用,频繁地创建和销毁线程会大大降低系统的运行效率,采用线程池来管理线程有以下好处:

  1. 提升性能:线程池能独立负责线程的创建、维护和分配
  2. 线程管理:每个Java线程池会保持一些基本的线程统计信息,对线程进行有效管理

43、信号量介绍下,用过吗(信号量是基于计数器的一种多线程同步机制,用来管理对资源的并发访问。)

信号量(Semaphore),也被称为信号灯,是在多线程环境下使用的一种设施,可以用来保证两个或多个关键代码段不被并发调用。在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。

信号量是一个非负整数,所有通过它的线程/进程都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。当一个线程调用 Wait 操作时,它要么得到资源然后将信号量减 1,要么一直等下去(指放入阻塞队列),直到信号量大于等于 1 时。Release(释放)实际上是在信号量上执行加操作。

44、wait和sleep的区别

1,所属的类不同:

(1)sleep方法是定义在Thread上
(2)wait方法是定义在Object上

2,使用语法不同:

(1)sleep可以使用在任何代码块

(2)wait必须在同步方法或同步代码块执行

3,唤醒的方式不同

sleep睡眠的时间到了之后,会自动唤醒

wait需要通过notify或notifyAll方法来唤醒

  • void notify()

Wakes up a single thread that is waiting on this object’s monitor.
译:唤醒在此对象监视器上等待的单个线程

  • void notifyAll()

Wakes up all threads that are waiting on this object’s monitor.
译:唤醒在此对象监视器上等待的所有线程

4,对于锁资源的处理方式不同

(1)sleep不会释放锁

(2)wait会释放锁

5,线程的状态不同(可回看之前讲的线程生命周期)

​ 1,当线程调用wait(),线程都会进入到waiting状态

​ 2,当线程调用sleep(time),或者wait(time)时,进入timed waiting状态

45、数据库优化

  1. 将字段很多的表分解成多个表
  2. 增加中间表
  3. 增加冗余字段
  4. 常用SQL查询语句优化方法
    • 不要使用select * from t,用具体的字段列表代替“*”,使用星号会降低查询效率,如果数据库字段改变,可能出现不可预知隐患。
    • 应尽量避免在where子句中使用!=或<>操作符,避免在where子句中字段进行null值判断,存储引擎将放弃使用索引而进行全表扫描。
    • 避免使用左模糊,左模糊查询将导致全表扫描。
    • IN语句查询时包含的值不应过多,否则将导致全表扫描。
    • 为经常作为查询条件的字段,经常需要排序、分组操作的字段建立索引。
    • 在使用联合索引字段作为条件时,应遵循最左前缀原则。
    • OR前后两个条件都要有索引,整个SQL才会使用索引,只要有一个条件没索引整个SQL就不会使用索引。
    • 尽量用union all代替union,union需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。

46、HTTP状态码

  • 200 - 请求成功
  • 301 - 资源(网页等)被永久转移到其它URL
  • 404 - 请求的资源(网页等)不存在
  • 500 - 内部服务器错误
分类分类错误
1**信息,服务器收到请求,需要请求者继续执行操作
2**成功,操作被成功接收并处理
3**重定向,需要进一步的操作以完成请求
4**客户端错误,请求包含语法错误或无法完成请求
5**服务器错误,服务器在处理请求的过程中发生了错误

46、垃圾回收算法

1、标记复制算法
会把内存分为相同的2个部分,每次回收,会把存活的对象移动到另一边,回收当前使用的空间。分配的内存被分成2份,实际使用空间变成正常的一半。但是不会出现垃圾碎片。

2、标记清除算法
标记存活的对象,把未标记的回收。回收后内存不是连续的,会产生大量的不连续的碎片,标记对象的时候效率低。

3、标记整理(压缩)算法
会把存活的对象移动到一起,清除边间外的垃圾对象,效率低

47、浅拷贝和深拷贝(基本类型、引用类型)String也是引用类型

浅拷贝:对基本类型数据进行值传递,对引用类型数据进行引用传递的拷贝

深拷贝:对基本类型数据进行值传递,对引用类型数据创建一个新的对象,并复制其内容

img

48、为什么要分为新生代和老年代

新生代:主要是用来存放新生的对象。一般占据堆的 1/3 空间

老生代:老年代的对象比较稳定,所以 MajorGC 不会频繁执行。

永生代(元空间):指内存的永久保存区域,主要存放 Class 和 Meta(元数据)的信息。

根据对象存活的时间来看,有的对象寿命长,有的对象寿命短。应该将寿命长的对象放在一个区,寿命短的对象放在一个区。不同的区采用不同的垃圾收集算法。寿命短的区清理频次高一点,寿命长的区清理频次低一点。提高效率。所以就有了新生代和老年代。

49、Java事务三种类型:

JDBC事务、JTA事务、容器事务。JDBC事务可以保证操作的完整性和一致性,JDBC事务由Connection发起、控制。

50、${}和#{}区别

${} 相当于直接拼接SQL,类似于 JDBC 的 Statement 用法,

String sql = "select * from table where xxx = " + xxx + " and yyy = " + yyy;

这种方式缺陷是有被 SQL 注入的风险,并且数据库每次都会重新编译此SQL,因为每次都认为是一个新的 SQL。

#{} 相当于预编译 SQL,对应 JDBC 的 PreparedStatement,就是 ? 占位符形式

String sql = "select * from table where xxx = ? and yyy = ?";
// 然后每次执行动态传入不同的参数即可

这样的好处是不会被 SQL 注入,因为数据库会将所有特殊的字符都当做参数处理,而且多次执行的话数据库也不会重新编译 SQL,因为每次数据库都认为是同一个 SQL,只是替换个参数而已。

另外,$符号的动态传参,可以适合应用在一些动态SQL场景中,比如动态传递表名、动态设置排序字段等。

51、可能抛出系统异常的方法是不需要申明异常的

52、存储过程:重复使用、提高性能、减少网络流量、安全性。

53、 线程中断方式:自定义中断标识符,停止线程、使用线程中断方法 interrupt 停止线程、使用 stop 停止线程。

自定义标识符:
class FlagThread extends Thread {
    // 自定义中断标识符
    public volatile boolean isInterrupt = false;
	@Override
    public void run() {
        // 如果为 true -> 中断执行
        while (!isInterrupt) {
            // 业务逻辑处理
        }
    }
}

interrupt
public static void main(String[] args) throws InterruptedException {
    // 创建可中断的线程实例
    Thread thread = new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println("thread 执行步骤1:线程即将进入休眠状态");
            try {
                // 休眠 1s
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("thread 线程接收到中断指令,执行中断操作");
                // 中断当前线程的任务执行
                break;
            }
            System.out.println("thread 执行步骤2:线程执行了任务");
        }
    });
    thread.start(); // 启动线程

    // 休眠 100ms,等待 thread 线程运行起来
    Thread.sleep(100);
    System.out.println("主线程:试图终止线程 thread");
    // 修改中断标识符,中断线程
    thread.interrupt();
}

stop方法
Thread.stop()

54、Java反射

在java中,反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。Java反射机制的主要功能:1、在运行时判断任意一个对象所属的类;2、在运行时构造任意一个类的对象;3、在运行时调用任意一个对象的方法等。

55、IP地址分为A、B、C、D等四大类。

每个IP地址有四个字节组成。IP地址的表示方法采用点分十进制表示。如果第一字节=0-127是A类,第一字节=128-191是B类,第一字节=192-223是C类,第一字节=224-239是D类。 任何一个A、B、C类的IP地址由网络号字段net-id和主机号字段host-id组成。

某部门申请一个C类IP地址,若要分成8个子网,其掩码应为 255.255.255.224                取主机号前三位出来做子网号,(11100000)=224

56、Service的启动方式

第一种启动方式:startService(Intent)

启动服务 生命周期方法流程:onCreate --- > onStartCommand 

关闭服务:stopService(Intent)
关闭服务 生命周期方法流程 :onDestory

第二种启动方式:bindService(Intent , ServiceConnection , flag)

启动服务 生命周期方法流程:onCreate --- > onBind

关闭服务:stopService(Intent)
关闭服务 生命周期方法流程 :onUnBind ----> onDestory

区别:

startService启动Service ,Service有独立的生命周期,不依赖该组建;
多次调用startService方法,会重复调用onStartCommand方法;
必须通过stopService或者stopSelf来停止服务(IntentService会自动调用stopSelf方法)

bindService启动Service,多次调用此方法,只会调用一次onBind方法;
bindService,Service 依赖于此组件,该组件销毁后,Service也会随之销毁。

57、高内聚低耦合

软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准,良好的系统设计模块之间的耦合度一定要低、模块内的内聚度一定要高,也就是高内聚低耦合。形象的说就是,写的代码要和电脑一样,主类就是电脑的主机箱,当程序需要实现什么功能的时候只需要把其他的类引入即可,就像主机箱需要内存条运行程序、硬盘存储数据、USB接口实现输入输出、CPU实现计算,主类加入这些功能类即可。例如,一个程序有50个函数,这个程序执行得非常好,然而一旦你修改其中一个函数,其他49个函数都需要做修改,这就是高耦合的后果。每个模块只完成系统要求的独立子功能,并且与其他模块的联系最少且接口简单。

58、Mybatis中mapper接口和xml文件的绑定方式

Mapper接口与xml文件绑定通过xml文件里mapper标签的namespace值绑定。

mapper接口中的方法名与xml中标签的id值绑定。

59、各个变量存放空间

Java中没有全局变量的概念,变量分为类的成员变量、静态成员变量和方法中的局部变量。

先说局部变量,基本类型的局部变量变量名和值都存放在虚拟机栈中,引用类型的局部变量变量名存放在栈中,而变量指向的对象存放在堆中。

再说类的成员变量,不论基本类型还是引用类型,变量名和值都随着类的实例(对象)存放在堆中。

最后说说静态变量,它比较特殊,是属于类的变量,在jdk7及之前的版本,随类存放在方法区中。在jdk8之后,由于虚拟机内存结构的变化,静态变量和常量池一起被迁移到了堆中
在这里插入图片描述

60、JVM五大区域:

java
方法区:又被称为元空间,用来存储类的信息,例如:方法,方法名,返回值,常量。当它无法满足内存分配需求时,方法区会抛出OutOfMemoryError。

:存放new出来的对象信息, 全局变量。

程序计数器:指向当前线程正在执行的行号,用来保证线程切换时回到程序调用的位置。(例如:在a方法里面掉用了b方法,代码从上往下执行,执行到掉用b方法的那行时,指针会记录下这个位置,然后执行b方法里面的逻辑,b方法正常执行完或异常退出,指针都会回到a方法里面。)

本地方法栈:和虚拟机栈类似,只是它描述的是为虚拟机是用到的Native方法出栈和入栈的过程(通常我们不需要了解这块,它底层是C语言实现的)。

虚拟机栈:描述的是线程进栈出栈的过程,线程结束内存自动释放。它用来存储当前线程运行方法所需要的数据、指令、返回地址。(即局部变量和正在调用的方法) 方法被调用时会在栈中开辟一块叫栈帧的空间,方法运行在栈帧空间中。栈帧出栈后,里面的局部变量直接就从内存里清理掉了。

61、MySQL锁类别

共享锁、排他锁、行锁、意向锁

锁级别:表级锁、行级锁、页面锁

62、ReentrantLock

  • 可中断
  • 可以设置超时时间
  • 可以设置为公平锁
  • 支持多个条件变量
  • 与 synchronized 一样,都支持可重入

63、三总线:I/O总线、主存总线、DMA总线

64、聚簇索引和非聚簇索引(InnoDB采用非聚簇索引,MyISAM默认非聚簇索引)

65、MySQL执行顺序:from > on > join > where > group by > 函数 > having > select > distinct > order by > limit

66、finalize()方法是Object类提供的方法,在GC(垃圾回收器)准备释放对象所占用的内存空间之前,它将首先调用finalize()方法。

67、-Xss:规定了每个线程虚拟机栈及堆栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。

​ -Xms:表示初始化JAVA堆的大小及该进程刚创建出来的时候,他的专属JAVA堆的大小,一旦对象容量超过了JAVA堆的初始容量,JAVA堆将会自动扩容到-Xmx大小。

​ -Xmx:表示java堆可以扩展到的最大值,在很多情况下,通常将-Xms和-Xmx设置成一样的,因为当堆不够用而发生扩容时,会发生内存抖动影响程序运行时的稳定性。

68、老年代和新生代,默认数据大小为2 :1,新生代又分为Eden区,s0区,s1区,默认数据大小为8 : 1 : 1。hotspot 默认eden和survivor为8:1

69、理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题

70、final和abstract不能同时存在。final可以修饰类、静态成员变量、普通成员变量、普通方法、局部变量。

71、hashmap key为null时,hash值为0,不为null时才会执行key.hashCode()。

所以hashmap key可以为null。hashmap扩容,loadFactor越小越容易扩容。(如loadFactor=0.5,则当存储达到一半时就会扩容)。负载因子决定了hashmap的密度,一般0.7-0.75此时检索长度接近常数。

1.调用HashMap的无参构造方法,对加载因子LoadFactor赋值0.75,table数组是null。
2.当第一次添加元素时,创建长度为16的数组,threshold=12
3.当链表长度大于8,并且数组长度大于等于64时,链表调整为红黑树
4.当红黑树的节点个数小于6时,调整为链表
5.当HashMap的存储量超出阈值时,数组扩容为原来的两倍。

72、JWT

1.Header(头) 作用:记录令牌类型、签名算法等 例如:{“alg":“HS256”,“type”,"JWT}

2.Payload(有效载荷)作用:携带一些用户信息 例如{“userId”:“1”,“username”:“mayikt”}

3.Signature(签名)作用:防止Token被篡改、确保安全性 例如 计算出来的签名,一个字符串

jwt存放在客户端、session存放在服务端,jwt不需要服务器存储。

73、高并发秒杀方案:熔断、限流、redis缓存处理、mq异步处理订单请求、网关层限制UID访问频率。

74、OSI七层模型:

在这里插入图片描述
TCP/IP
在这里插入图片描述

75、MyBatis执行流程:

SqlSessionFactoryBuilder ->SqlSessionFactoryBuilder根据传入的数据流(XML)生成Configuration对象 -> Configuration对象创建默认的SqlSessionFactory实例 sqlsessionFactory -> sqlsession(配置cofiguration、executor executor调用statementHandler访问数据库,将结果集放入缓存 ) resultSethandler处理结果集

每个StatementHandler都会包含一个ParameterHandler(参数处理器)及ResultSetHandler(结果集处理器)。

Executor发起sql执行任务
1、先调用​​statementHandler​​​中的​​prepare()​​进行SQL的编译
2、然后调用​​statementHandler​​​中的​​parameterize()​​​设置参数
3、这里其实真正设置参数的是​​​ParameterHandler​​​中的​​setparameters()​​​方法,该方法与​​typeHandler​​进行参数类型的转换。
4、然后执行​​query/update​​​方法,这里使用​​ResultSetHandler​​进行结果的组装工作

76、PathVariable注解使用

/*
     * 如何发送PUT和DELETE请求
     * 1.需要配置HiddenHttpMethodFilter
     * 2.需要发送POST请求
     * 3.需要发送POST请求时携带一个name="_method"的隐藏域,value值为DELETE或者PUT
     * 
     * 在springmvc框架中通过@PathVariable注解来获取id值
     * */
    //get请求
    @RequestMapping(value="/testRest/{id}",method=RequestMethod.GET)
    public String testRest(@PathVariable Integer id)
    {
        System.out.println("testRest Get"+id);
        return SUCCESS;
    }
 
    //post请求
    @RequestMapping(value="/testRest",method=RequestMethod.POST)
    public String testRest()
    {
        System.out.println("testRest POST");
        return SUCCESS;
    }
 
    //delete请求
    @RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
    public String testRestDelete(@PathVariable Integer id)
    {
        System.out.println("testRest DELETE"+id);
        return SUCCESS;
    }
 
    //delete请求
    @RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT)
    public String testRestPut(@PathVariable Integer id)
    {
        System.out.println("testRest PUT"+id);
        return SUCCESS;
    }

 

77 Feign底层

在这里插入图片描述
1、开启Feign支持(@EnableFeignClients、@FeignClient)
2、服务器启动时,通过FeignInvocationHandler类为每个远程接口创建JDK Proxy对象,并注入IOC容器
3、FeignInvocationHandler根据要调用的远程方法找到对应的MethodHandler方法处理器
4、MethodHandler通过RequestTemplate根据参数和url等信息封装Request对象,并调用Encoder进行编码
5、Client接口根据http请求框架发送http请求

78、 Java创建对象全过程

1、判断类有没有被加载
2、如果没有,就用ClassLoader加载类
3、初始化,给一些变量初始化
4、设置对象头
5、执行方法:对对象进行赋值和执行构造函数。

79、 Java对象包括:

对象头(markword、数组长度)、实例数据、对齐填充。32位8字节、64位16字节。

对齐填充并不是必然存在的,也没有特定的含义,仅仅起着占位符的作用。由于HotSpot虚拟机的自动内存管理系统要求对象的起始地址必须是8字
节的整数倍,也就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐的
时候,就需要通过对齐填充来补全。对象字节数/8的倍数,如果不足的话 实现填充。例如:一个对象21个字节,则需要补充3个字节。

80、 服务发现:

服务提供者、注册中心(服务注册表) 、服务消费者

  • 服务提供者:服务启动时将服务信息注册到注册中心,服务退出时将注册中心的信息删除。
  • 服务消费者:从服务注册中心获取服务提供者的最新网络位置等服务,维护与提供者之间的通信。
  • 注册中心:服务提供者和服务消费者的桥梁。

服务端发现模式(本质区别:客户端是否保存服务列表信息):

  • 客户端发现模式(nacos+ribbon)
  • 服务端发现模式

客户端:在客户端模式下,如果要进行微服务调用,首先要进行的是到服务注册中心获取服务列表,然后再根据调用端本地的负载均衡策略,进行服务调用
在这里插入图片描述
服务端:在服务端模式下,调用方直接向服务注册中心进行请求,服务注册中心再通过自身负载均衡策略,对微服务进行调用。这个模式下,调用方不需要在自身节点维护服务发现逻辑以及服务注册信息。
在这里插入图片描述

81、CompletableFature

Runnable+Thread虽然提供了多线程的能力但是没有返回值。
Callable+Thread的方法提供多线程和返回值的能力但是在获取返回值的时候会阻塞主线程。

82、 tcp三次握手、四次挥手

TCP三次握手
为了对每次发送的数据量进行跟踪与协商,确保数据段的发送和接收同步,根据所接收到的数据量而确认数据发送、接收完毕后何时撤消联系,并建立虚连接。

第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT(请求连接)状态,等待Server确认。

第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
TCP四次挥手(连接终止协议,性质为终止协议)
第一次挥手:TCP客户端发送一个FIN+ACK+SEQ,用来传输关闭客户端到服务端的数据。进入FIN_WAIT1状态。
第二次挥手:服务端收到FIN,被动发送一个ACK(SEQ+1),进入CLOSE_WAIT状态,客户端收到服务端发送的ACK,进入FIN_WAIT2状态。
第三次挥手:服务器关闭客户端连接,发送一个 FIN+ACK+SEQ 给客户端。进入 LAST_ACK 状态。
第四次挥手:客户端发送 ACK(ACK=SQE序号+1)报文确认,客户端进入 TIME_WAIT 状态,服务端收到 ACK 进入 CLOSE状态。
四次挥手
 为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值