JavaSE 面试题

自增变量

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}

在这里插入图片描述

执行 i = i++; 先将i变量压入操作数栈,然再对i变量进行自增,最后把计算结果赋值给 i , i 任然是1

i++ 计算后将值给到 j
在这里插入图片描述
最后一步
在这里插入图片描述
执行结果

1
2
3
i=4
j=1
k=11

单例模式

什么事Singleton?

Singleton:在Java中 即指单例设置模式,探视软件开发最常用的设置模式之一

单:唯一

例:实例

单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式

例如:代表JVM运行环境的Runtime类

要点:

一是某个类只能有一个实例

构造器私有化

二是他必须自行创建实例

含有一个该类的静态变量来保存这个唯一的实例

三是它必须自行向整个系统提供这个实例

对外提供获取该类实例对象的方式

  • 直接暴露
  • 用静态变量的get方法获取

几种常见形式

饿汉式:直接创建对象,不存在线程安全问题

直接实例化饿汉式(简洁直观)

枚举式 (最简洁)

静态代码块饿汉式(适合复杂实例化)

懒汉式;延迟创建对象

线程不安全(使用于单线程)

线程安全(使用于多线程)

静态内部类模式 (适用于多线程)

饿汉式

直接实例化饿汉式(简洁直观)
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton1 {
/**
* 1、构造器私有化
* 2、自行创建,并且用静态变量保存
* 3、向外提供实例
* 4、强调这是一个单例,我们可以用final修改
*/
private Singleton1() {

}
public static final Singleton1 INSTANCE = new Singleton1();

}
枚举式 (最简洁)
1
2
3
4
5
6
public enum  Singleton2 {
/**
* 枚举类型:表示该类型是有限的几个
*/
INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Singleton3 {
/**
* 静态代码块
*/
public static final Singleton3 INSTANCE;
private String info;

static {
try {
INSTANCE = new Singleton3("123");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private Singleton3(String info) {
this.info = info;
}
}

懒汉式

线程不安全(使用于单线程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton4 {
/**
* 1、构造器私有化
* 2、用一个静态变量保存这个唯一的实例
* 3、提供一个静态方法,获取这个实例对象
*/
static Singleton4 instance;
private Singleton4() {}

public static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;

}
}
线程安全(使用于多线程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Singleton5 {
/**
* 1、构造器私有化
* 2、用一个静态变量保存这个唯一的实例
* 3、提供一个静态方法,获取这个实例对象
*/
static Singleton5 instance;
private Singleton5() {}

public static Singleton5 getInstance() {
if (instance == null) {
synchronized (Singleton5.class) {
if (instance == null) {
instance = new Singleton5();
}
return instance;
}
}
return instance;
}
}
静态内部类模式 (适用于多线程)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton6 {
/**
* 1、内部类被加载和初始化时,才创建INSTANCE实例对象
* 2、静态内部类不会自动创建,随着外部类的加载初始化而初始化,他是要单独去加载和实例化的
* 3、因为是在内部类加载和初始化时,创建的,因此线程安全
*/
private Singleton6(){}

public static class Inner{
private static final Singleton6 INSTANCE = new Singleton6();
}
public static Singleton6 getInstance() {
return Inner.INSTANCE;
}
}

类初始化实例初始化

类初始化

一个类要创建实例需要先加载并初始化该类

main方法所在的类需要先加载和初始化

一个子类要初始化需要先初始化父类

一个类初始化就是执行 clinit 方法

clinit 方法由静态类变量显示赋值代码和静态代码块组成

类变量显示赋值代码和静态代码块代码从上到下执行

clinit 方法只调用一次

实例初始化过程

实例初始化就是执行 init() 方法

init () 方法可能重载有多个,有几个构造器就有几个 init() 方法

init() 方法由非静态实例变量显示赋值代码和非静态代码块,对应构造器代码组成

非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对应构造器的代码最后执行

每次创建实例对象,调用对应构造器,执行的就是对应的 ini方法

init 方法的首行是super()和super(实参列表) ,即对应父类的 init 方法

Father.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.atguigu.classLoader;

/**
* 父类初始化<clinit>
* 1、j = method()
* 2、 父类的静态代码块
*
* 父类实例化方法:
* 1、super()(最前)
* 2、i = test() (9)
* 3、子类的非静态代码块 (3)
* 4、子类的无参构造(最后)(2)
*
*
* 非静态方法前面其实有一个默认的对象this
* this在构造器或<init> 他表示的是正在创建的对象,因为咱们这里是正在创建Son对象,所以
* test()执行的就是子类重写的代码(面向对象多态)
*
* 这里i=test() 执行的就是子类重写的test()方法
* @author gcq
* @Create 2020-09-25
*/
public class Father {
private int i = test();
private static int j = method();

static{
System.out.println("(1)");
}
Father() {
System.out.println("(2)");
}
{
System.out.println("(3)");
}
public int test(){
System.out.println("(4)");
return 1;
}
public static int method() {
System.out.println("(5)");
return 1;
}
}
Son.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.atguigu.classLoader;

/**
* 子类的初始化<clinit>
* 1、j = method()
* 2、子类的静态代码块
*
* 先初始化父类 (5)(1)
* 初始化子类 (10) (6)
*
* 子类实例化方法:
* 1、super()(最前
* 2、i = test() (9)
* 3、子类的非静态代码块 (8)
* 4、子类的无参构造(最后)(7)
* @author gcq
* @Create 2020-09-25
*/
public class Son extends Father {
private int i = test();
private static int j = method();
static {
System.out.println("(6)");
}
Son() {
super();
System.out.println("(7)");
}
{
System.out.println("(8)");
}
public int test(){
System.out.println("(9)");
return 1;
}
public static int method() {
System.out.println("(10)");
return 1;
}

public static void main(String[] args) {
Son son = new Son();
System.out.println();
Son son1 = new Son();
}
}

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(5)
(1)
(10)
(6)
(9)
(3)
(2)
(9)
(8)
(7)

(9)
(3)
(2)
(9)
(8)
(7)

方法参数传递机制

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.atguigu.methodParam;

import java.util.Arrays;

/**
* @author gcq
* @Create 2020-09-28
*/
public class Exam4 {
public static void main(String[] args) {
int i = 1;
String str = "hello";
Integer num = 200;
int[] arr = {1,2,3,4,5};
MyData my = new MyData();

change(i,str,num,arr,my);

// arr my变了
System.out.println("i= " + i);
System.out.println("str= " + str);
System.out.println("num= " + num);
System.out.println("arr= " + Arrays.toString(arr));
System.out.println("my.a= " + my.a);

}
public static void change(int j, String s, Integer n, int[] a, MyData m) {
j += 1;
s += "world";
n += 1;
a[0] += 1;
m.a += 1;
}
}
class MyData {
int a = 10;

}

考点?

方法的参数传递机制

String、包装类等对象的不可变性

方法的参数传递机制

1、形参是基本数据类型

  • 传递数据值

2、实参是引用数据类型

  • 传递地址值

    特殊的类型:String、包装类等对象的不可变性

<<<<<<< Updated upstream

变量初始化 以及运算时 栈堆内存的变化
输出结果

1
2
3
4
5
i= 1
str= hello
num= 200
arr= [2, 2, 3, 4, 5]
my.a= 11

递归与迭代

成员变量和局部变量

考点?

就近原则

变量的分类

成员变量: 类变量、实例变量

局部变量

非静态代码块的执行:每次创建实例对象都会执行

方法的调用规则:调用一次执行一次

局部变量与成员变量区别:

声明的位置

局部变量:方法体{}中,形参,代码块{}中

成员变量:类方法外

类变量: 有static修饰

实例变量:没有static修饰

修饰符

局部变量:final

成员变量:public protected,private,final ,static volatile,transient

值存储位置

局部变量:栈

实例变量:堆

类变量:方法区

JVM 运行时数据区
堆(Heap) ,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

通常所说的栈(Stack) ,是指虚拟机栈。虚拟机栈用于存储局部变量表等。局部变量
表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char、short、 int、 float、long、double) 、对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完, 自动释放。
方法区(Method Area)用于存储已被虛拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

作用域:

局部变量从声明处开始,到所属的 } 好结束

实例变量:在当前类中 this 有时this. 可以省略,在其他类中 对象名. 访问

类变量:在当前类中 类名 有时类名. 可以省略,在其它类中类名.或对象名.访问

生命周期

局部变量:每一个线程,每一次调用执行都是新的生命周期

实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量都是独立的

类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的

当局部变量与XX变量重名时,如何区分:

1、局部变量与实例变量重名

  • 在成员便令前面加 this

2、局部变量与类变量重名

  • 在类变量前面加 类名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @author gcq
* @Create 2020-09-29
*/
public class Exam5 {
static int s;// 5
int i; // A-2 B-1
int j;//A-1 B-1
{
int i = 1;
i++; // 就近原则
j++;
s++;
}
public void test(int j) {
j++; // 就近原则 21
i++;
s++;
}
public static void main(String[] args){
Exam5 obj1 = new Exam5();
Exam5 obj2 = new Exam5();
obj1.test(10);
obj1.test(20);
obj2.test(30);
// 2 1 5
System.out.println(obj1.i + "," + obj1.j + "," + obj1.s);
// 1 1 5
System.out.println(obj2.i + "," + obj2.j + "," + obj2.s);
}
}

在这里插入图片描述

SSM面试题

Spring Bean的作用域

可通过scope属性来指定bean的作用域

  • singleton:默认值。当IOC容器一创建就会创建bean的实例,而且是单例的,每次得到的都是同一个。
  • prototype:原型的。当IOC容器一创建不再实例化该bean,每次调用getBean()方法时再实例化该bean,而且每次调用getBean()方法时会返回一个新的实例。
  • request:每次请求实例化一个bean
  • session:在一次会话中共享一个bean

Spring中支持的常用数据库事务传播属性和事务隔离级别

事务的传播行为

简介

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,列如方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行,事务传播的行为有传播属性指定,Spring定义了7中类传播行为

传播属性 描述
REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行
REQUIRED_NEW 当前方法必须启动事务,并在它自己的事务内运行,如果有事务正在运行,应该将他挂起
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则他可以不运行在事务中
NOT_SUPPORTE 当前的方法不应该运行在事务中,如果有运行的事务,将他挂起
MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常
NEVER 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事物的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行

事务传播属性可以在@Transactional注解的propagation属性中定义

事务隔离级别

数据库事务并发问题

假设现在有两个事务:Transaction01和Transaction02并发执行。

1) 脏读

①Transaction01将某条记录的AGE值从20修改为30。

②Transaction02读取了Transaction01更新后的值:30。

③Transaction01回滚,AGE值恢复到了20。

④Transaction02读取到的30就是一个无效的值。

2) 不可重复读

①Transaction01读取了AGE值为20。

②Transaction02将AGE值修改为30。

③Transaction01再次读取AGE值为30,和第一次读取不一致。

3) 幻读

①Transaction01读取了STUDENT表中的一部分数据。

②Transaction02向STUDENT表中插入了新的行。

③Transaction01读取了STUDENT表时,多出了一些行。

隔离级别

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。

  1. 读未提交:READ UNCOMMITTED

允许Transaction01读取Transaction02未提交的修改。

  1. 读已提交:READ COMMITTED

    要求Transaction01只能读取Transaction02已提交的修改。

  2. 可重复读:REPEATABLE READ

    确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。

  3. 串行化:SERIALIZABLE

    确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

  4. 各个隔离级别解决并发问题的能力见下表

在这里插入图片描述

请简单介绍Spring支持的常用数据库事务传播属性和事务隔离级别?

/**
 * 事务的属性:
 *     1.★propagation:用来设置事务的传播行为
 *         事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务
 *         -Propagation.REQUIRED:默认值,使用原来的事务
 *         -Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务
 *     2.★isolation:用来设置事务的隔离级别
 *         -Isolation.REPEATABLE_READ:可重复读,MySQL默认的隔离级别
 *         -Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别
 */

Spring MVC如何解决POST中文乱码问题

在web.xml中添加过滤器

1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Spring MVC 主要工作流程

image-20210121171610803

第一步:发起请求到中央控制器(DispatcherServlet)

第二步:中央控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)

第三步:处理器映射器HandlerMapping向中央控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略

第四步:中央控制器调用处理器适配器去执行Handler

第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler

第六步:Handler执行完成给适配器返回ModelAndView

第七步:处理器适配器向中央控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)

第八步:中央控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可

第九步:视图解析器向前端控制器返回View

第十步:中央控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)

第十一步:中央控制器向用户响应结果

1、 DispatcherServlet 在 web.xml 中的部署描述,从而拦截请求到 Spring Web MVC

2、 HandlerMapping 的配置,从而将请求映射到处理器

3、 HandlerAdapter 的配置,从而支持多种类型的处理器

注:处理器映射求和适配器使用纾解的话包含在了注解驱动中,不需要在单独配置

4、 ViewResolver 的配置,从而将逻辑视图名解析为具体视图技术

5、 处理器(页面控制器)的配置,从而进行功能处理

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

Mybatis中当实体类中的属性名和表中的字段不一样,怎么办

1、写 SQL 语句的时候 写别名

1
2
3
<select id=”selectorder” parametertype=”int” resultetype=”me.gacl.domain.order”>
select order_id id, order_no orderno ,order_price price form orders where order_id=#{id};
</select>

2、在MyBatis的全局配置文件中开启驼峰命名规则

1
2
3
4
5
<!-- 开启驼峰命名规则,可以将数据库中下划线映射为驼峰命名
列如 last_name 可以映射为 lastName
-->
<setting name="mapUnderscoreToCameLCase" value="true" />

要求 数据库字段中含有下划线

3、在Mapper映射文件中使用 resultMap 自定义映射

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 
自定义映射
-->
<resultMap type="com.atguigu.pojo.Employee" id="myMap">
<!-- 映射主键 -->
<id cloumn="id" property="id"/>
<!-- 映射其他列 -->
<result column="last_name" property="lastName" />
<result column="email" property="email" />
<result column="salary" property="salary" />
<result column="dept_id" property="deptId" />
</resultMap>

Java 高级面试题

Linux常用服务类相关命令

1
2
3
4
5
6
7
8
9
10
11
service服务名start
service服务名stop
service服务名restart
service服务名reload
service服务名status

#查看服务的方法 /etc/init.d/ 服务名
#通过 chkconfig 命令设置自启动
#查看服务 chkconfig -list l grepXXX

chkconfig -level 5 服务名on

运行级别 runlevel(centos6)

在这里插入图片描述

Linux系统有7种运行级别(runlevel): 常用的是级别3和5

运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动

运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆

运行级别2:多用户状态(没有NFS),不支持网络

运行级别3:完全的多用户状态(有NFS),登陆后进入控制台命令行模式

运行级别4:系统未使用,保留

运行级别5: X11控制台,登陆后进入图形GUI模式

运行级别6:系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动.

注册在系统中的标准化程序
有方便统一的管理方式(常用的方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
systemctl start 服务名(xxx.service)
systemct restart 服务名(xxxx.service)
systemctl stop 服务名(xxxx.service)
systemctl reload 服务名(xxxx.service)
systemctl status 服务名(xxxx.service)

#查看服务的方法 /usr/lib/systemd/system
#查看服务的命令

systemctl list-unit-files
systemctl --type service

#通过systemctl命令设置自启动

自启动systemctl enable service_ _name
不自启动systemctl disable service_ name

git分支相关命令

分支

创建分支

git branch <分支名>

git branch -v 查看分支

切换分支

git checkout <分支名>

一步完成: git checkout -b <分支名>

合并分支

先切换到主干 git checkout master

git merge <分支名>

删除分支

先切换到主干 git checkout master

git branch -D <分支名>

git 工作流

在这里插入图片描述

Redis持久化有几种类型及其区别

RDB ( Redis DataBase)

AOF (Append OF File)

RDB

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。

备份是如何执行的

Redis会单独创建(fork) -个子进程来进行持久化,先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,由于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

rdb 的保存文件

在 redis.conf 中配置文件名称 默认为 dump.rdb

rdb 的备份

先通过 config get dir 查询 rdb文件的目录

将 *.rdb 的文件拷贝到别的地方

rdb 的恢复

关闭 Redis

先把备份文件拷贝到拷贝到工作目录下

启动 Redis,备份数据会直接加载

rdb 的优点

节省磁盘空间

恢复速度快

在这里插入图片描述

rdb 的缺点

虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能

在备份周期在一定间隔时间做一-次备份, 所以如果Rediq意外down掉的话,就会丢失最后一-次快照后的所有修改

AOF

日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一-次以完成数据的恢复工作。

AOF 的优点

备份机制更稳健丢失数据概率更低
可读的日志文本,通过操作AOF稳健,可以处理误操作。

在这里插入图片描述

AOF的缺点

比起RDB占用更多的磁盘空间

恢复备份速度要

每次读写都同步的话,有一定的性能压力。

存在个别Bug,造成不能恢复。

Mysql什么时候建索引

索引(Index)是帮助Mysql高校获取数据的数据结构。

优势

  • 提高数据检索效率,降低数据库IO成本
  • 通过索引对数据进行排序,降低数据排序的成本,降低CPU消耗

劣势:

  • 降低更新表的速度。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件每次更新添加的索引列的字段,调整因为更新带来的键值变化后的索引信息。
  • 索引其实也是一张表,所以索引列也要占用空间

那些情况需要创建索引

  1. 主键自动建立唯 一 索引
  2. 频繁作为查询条件的字段应该创建索引
  3. 查询中与其它表关联的字段,外键关系建立索引
  4. 频繁更新的字段不适合创建索引,因为每次更新不单是更新了记录还会更新索引
  5. 单键组索引的选择问题,who? 在高并发下领向创建组合索引
  6. 意询中排序的字段,排序字段若通过索引法访问将大大提高排序速度
  7. 查询中统计或者分组字段

那些情况下不要建立索引

  1. 表记录太少
    • 原因:提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE.
  2. 经常增删改的表
    • 因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。
  3. where条件里用不到的字段不创建索引
  4. 注意,如果某个数据列包含许多重复的内容,为它建立索弓|就没有太大的实际效果。

JVM垃圾回收机制中,GC发生在JVM哪部分,有几种GC,他们的算法是什么?

GC 发生在JVM的堆里面

GC分为MinorGC(新生代)和FullGC(老年代)

在这里插入图片描述

引用计数法

不用

复制算法

它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。

发生在新生代

缺点:需要双倍空间

优点:

  • 没有标记和清除的过程,效率高
  • 没有内存碎片,可以利用bump-the-pointer实现快速内存分配

标记清除算法

老年代一般是由标记清楚或标记清除与标记整理的混合实现

该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:

  1. 效率问题(扫描一次,标记一次)
  2. 空间问题(标记清除后会产生大量不连续的碎片)

标记整理算法

老年代一般是由标记清楚或标记清除与标记整理的混合实现

根据老年代的特点提出的一种标记算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。

缺点:

  • 效率问题

有点:

  • 没有内存碎片

标记清除整理算法

结合

项目面试题

redis在项目中的使用场景

数据类型 使用场景
String 比如说,我想知道什么时候封锁一个 IP 地址 Incrby 命令
Hash 存储用户信息[ id, name , age] Hset( key ,field, value) Hset( key ,id, 101) Hset( key ,name, admin) Hset( key ,age, 23) 修改案例————- Hget(userKev,jd)+ Hset(userKey,id,102)。 为什么不使用String类型来存储 String拿到对象值之后需要反序列化,我们只需要更改id name, age 没有意义反序列化 Set(userKey;用信息的字符串) Get(userKey) 不建议使用String 类型。
List 实现最新消息的排行,还可以利用 List 的 push 命令,将任务存在list集合 中,同时使用另-个命令,将任务从集合中取出[ pop ]。 Redis - List 数据类型来模拟消息队列。[电商中的秒杀就可以采用这种方式 来完成一个秒杀活动]。
Set 特殊之处:可以自动排重。比如说微博中将每个人的好友存在集合( Set) 中,+ 这样求两个人的共通好友的操作。我们只需要求交集即可。
Zset 以某一个条件为权重,进行排序。 京东:商品详情的时候,都会有一个综合排名,还可以按照价格进行排名

ElasticSearch 和 splr的区别

背景:他们都是基于 Lucene 搜索服务器基础上开发,一款优秀的,高性能的企业级搜索服务器,【是因为他们都是基于分词技术构建的倒排索引的方式进行查询】

区别:

  1. 当实时建立索引的时候,solr 会产生 io 阻塞,而 es 不会,es 查询性能要高于 solr

  2. 在不断动态添加数据的时候,solr 的检索效率会变得低下,而 es 没有什么变化

  3. Solr 利用 zookeeper 进行分布式管理,而 es 自带有分布式系统的管理功能,Solr 一般都要部署到 web 服务器上,比如 tomcat,启动 tomcat 的时候需要配置 tomcat 和 solr 的 关联 【 Solr 的本质,是一个动态的 web项目】

  4. Solr支持更多格式的数据 【xml、json、csv 】等,而 es 仅仅支持 json 文件格式

  5. Solr 是传统搜索应用的有利解决方案,但是 es 更加适用于新兴的实时搜索应用

    • 单纯的对已有的数据进行检索, solr 效率更好,高于 es
  6. Solr 官网提供的功能更多哦,而 es 本身更加注重于核心功能,高级功能都有第三方插件完成

单点登录的实现过程

单点登录: 一处登录多处使用!

前提:单点登录多使用在分布式系统中

一处登录,处处运行

在这里插入图片描述

Demo:

参观动物园流程

检票员=认证中心模块

1、我直接带着大家进动物园,则会被检票员拦住【看我们是否有票】,没有【售票处买票】

登录=买票

2、我去买票【带着票,带着大家一起准备进入动物园】 检票员check【有票】

Token = piao

3、我们手中有票就可以任意观赏动物园的每处景点

京东:单点登录,是将 token 放入到 cookie 中

案例:将浏览器的 cookie 禁用,则在登录京东则失效,无论如何登录不了

:shopping_cart:购物车实现过程

购物车:

  1. 购物车跟用户的关系 ?
    • 一个用户必须对应一个购物车【一个用户不管买多少商品,都会存在属于自己的购物车中】
    • 单点登录一定要在购车前
  2. 和购物车相关的操作有哪些
    • 添加到购物车
      • 用户未登录状态时,添加到什么地方
        • Redis 京东
        • Cookie local storage
      • 用户登陆状态
        • Redis 缓存中 (为了读写速度快)
          • Hash : hset(key,filed,value)
            • Key:user:userId:cart
        • 存在数据库中
    • 展示购物车
      • 未登陆状态
        • 直接从cookie中取得数据展示即可
      • 登陆状态
        • 用户一旦登录,必须显示数据库或redis + cookie中的数据(合并)

消息队列在项目中的使用

背景: 在分布式系统中如何处理高并发

由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞,比如说,大量的 insert,update 之类的请求同时到达数据库 MySQL, 直接导致无数的行锁表锁,甚至会导致请求堆积过多,从而触发 too many connections ( 链接数太多 ) 错误,使用消息队列可以解决 【异步通信】

异步

在这里插入图片描述

并行

在这里插入图片描述

排队

在这里插入图片描述

消息队列在电商中的使用场景

在这里插入图片描述

弊端

消息的不确定性: 延迟队列 和 轮询技术来解决问题即可!