看的尚硅谷柴林燕老师的课程,整理的笔记
javaSE面试题
1.自增变量
题目
执行过程:
(1)i=i++;局部变量i=1,把i的值压入操作数栈,i局部变量自增1,把操作数栈中的值赋值给i,此时局部变量i=1
(2)int j=i++;局部变量i=1,把i的值压入操作数栈,i局部变量自增1,把操作数栈中的值赋值给j,此时局部变量j=1,局部变量i=2
(3)int k=i + ++i * i++;局部变量i=2,局部变量j=1,此时把i=2的值压入栈中;++i直接修改变量的值不压入栈中,此时i自增为3,压入栈中;把i的值3压入栈中,此时变量i自增,局部变量i=4。栈中元素计算2+3*3=11赋值给k,此时局部变量k=11
小结
(1)赋值=,最后计算
(2)=右边的从左到右加载值依次压入操作数栈
(3)实际先算哪个,看运算符优先级
(4)自增、自减操作都是直接修改变量的值,不经过操作数栈
(5)最后的赋值之前,临时结果也存储在操作数栈中
2.单例设计模式
编程题:写一个Singleton示例
解题思路:
什么是Singleton?
Singleton:在java中即指单例设计模式,他是软件开发中常用的设计模式之一。单:唯一;例:实例。
单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
例如:代表JVM运行环境的Runtime类
要点
(一)是某个类只能有一个实例
构造器私有化
(二)是它必须自行创建这个实例
含有一个该类的静态变量来保存这个唯一的实例
(三)是它必须向整个系统提供这个实例
对外提供获取该实例对象的方式:(1)直接暴露(2)用静态变量的get方法获取
几种常见形式
饿汉式:直接创建对象(不存在线程安全问题)
(1)直接实例化饿汉式(简洁直观)
/**
* 饿汉式:
* 直接创建实例对象,不管你是否需要这个对象都会创建
* (1)构造器私有化
* (2)自行创建,并且用静态变量保存
* (3)向外提供这个实例
* (4)强调这是一个单例,我们可以用final修改
*/
public class Singleton1 {
public static final Singleton1 INSTANCE = new Singleton1();
private Singleton1(){
}
}
public class TestSingleton1 {
public static void main(String[] args){
Singleton1 singleton1 = Singleton1.INSTANCE;
}
}
(2)枚举式(最简洁)
/**
* 枚举类型,表示该类型的对象是有限的几个
* 我们可以限定为一个,就成了单例
*/
public enum Singleton2 {
INSTANCE
}
(3)静态代码块饿汉式(适合复杂实例化)
public class Singleton3 {
public static final Singleton3 INSTANCE;
static {
//静态代码块
INSTANCE = new Singleton3();
}
private Singleton3(){
}
}
复杂情况:(静态代码块适合初始化时加载一堆配置)
public class Singleton3 {
public static final Singleton3 INSTANCE;
private String info;
static {
try {
Properties pro = new Properties();
//使用类加载器,文件singleton.properties必须在类路径src文件下
pro.load(Singleton3.class.getClassLoader().getResourceAsStream("singleton.properties"));
INSTANCE = new Singleton3(pro.getProperty("info"));
} catch (IOException e) {
e.printStackTrace();
}
}
private Singleton3(String info){
this.info = info;
}
public String getInfo(){
return info;
}
public void setInfo(String info){
this.info = info;
}
}
饿汉式:在类初始化时直接创建实例对象,不管你是否需要这个对象都会被创建
懒汉式:延迟创建对象
(1)线程不安全(适用于单线程)
/**
* 懒汉式
* 延迟创建这个实例对象
*
* (1)构造器私有化
* (2)用一个静态变量保存这个唯一的实例
* (3)提供一个静态方法,获取这个实例对象
*/
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){
}
public static Singleton4 getInstance(){
if (instance==null){
//Thread.sleep(100)
instance = new Singleton4();
}
return instance;
}
}
public class TestSingleton4 {
public static void main(String[] args){
Singleton4 singleton4 = Singleton4.getInstance();
}
}
可能产生线程安全问题:线程一个进来了,还没new实例,处于等待状态。另一个线程也进来了,当第一个线程创建完实例,第二个线程也创建实例。
(2)线程安全(适用于多线程)
加个锁synchronized,线程安全
public class Singleton4 {
private static Singleton4 instance;
private Singleton4(){
}
public static Singleton4 getInstance() throws InterruptedException {
if(instance==null){//判断不等于空,直接返回,大家不用去抢锁,增加性能问题
synchronized (Singleton4.class){
if (instance==null){
Thread.sleep(1000);
instance = new Singleton4();
}
}
}
return instance;
}
}
(3)静态内部类实现(适用于多线程)
/**
* 在内部类被加载和初始化时,才被创建INSTANCE实例对象
* 静态内部类不会自动个随着外部类的加载和初始化而初始化的
* 因为在内部类被加载和初始化时,创建的,因此是线程安全的
*/
public class Singleton5 {
private Singleton5(){
}
private static class Inner{
private static final Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance(){
return Inner.INSTANCE;
}
}
小结:
如果是饿汉式,枚举形式最简单。如果是懒汉式,静态内部类形式最简单。
3.类初始化和实例初始化等
题目:以下代码运行结果
/**
* 父类的初始化<clinit>
* (1)j=method()
* (2)父类的静态代码块
*
* 父类的实例化方法:
* (1)super()(最前)
* (2)i=test()
* (3)父类的非静态代码块
* (4)父类的无参构造(最后)
*/
public class Father {
private int i = test();
private static int j = method();
static{
System.out.print("(1)");
}
Father(){
System.out.print("(2)");
}
{
System.out.print("(3)");
}
public int test(){
System.out.print("(4)");
return 1;
}
public static int method(){
System.out.print("(5)");
return 1;
}
}
/**
* 子类的初始化<clinit>
* (1)j=method()
* (2)子类的静态代码块
*
* 子类的实例化方法:
* (1)super()(最前)->父类的实例化方法
* (2)i=test()
* (3)子类的非静态代码块
* (4)子类的无参构造(最后)
*/
public class Son extends Father {
private int i = test();
private static int j =method();
static {
System.out.print("(6)");
}
Son(){
//super()写或不写,子类构造器一定会调用父类构造器的方法
System.out.print("(7)");
}
{
System.out.print("(8)");
}
public int test(){
System.out.print("(9)");
return 1;
}
public static int method(){
System.out.print("(10)");
return 1;
}
public static void main(String[] args){
Son s1 = new Son();
System.out.println();
Son s2 = new Son();
}
}
答案
考点
一、类初始化过程
二、实例初始化过程
三、方法的重写
类初始化过程
(1)一个类要创建实例需要先加载并初始化该类
- main方法所在的类需要先加载和初始化
(2)一个子类要初始化需要先初始化父类
(3)一个类初始化就是执行< clinit >()方法 - < clinit >()方法由静态类变量显示赋值代码(声明的不算)和静态代码块组成
- 类变量显示赋值代码和静态代码块代码从上到喜爱顺序执行
- < clinit >()方法只执行一次
类初始化:5,1,10,6
实例初始化过程
实例初始化就是执行< init>()方法
- < init>()方法可能重载有多个,有几个构造器就有几个< init>方法
- < init>()方法由非静态实例变量显示赋值代码和非静态代码块、对应构造器代码组成
- 非静态实例变量显示赋值代码和非静态代码块代码从上到下顺序执行,而对应构造器的代码最后执行
- 每次创建实例对象,调用对应构造器,执行的就是对应的< init>方法
- < init>方法的首行是super()或super(实参列表),即对应父类的< init>方法
父类的实例化方法:
- (1)super()(最前)
- (2)i=test()(9)
- (3)父类的非静态代码块(3)
- (4)父类的无参构造(最后)(2)
注意:这里i=test()执行的是子类重写的test()方法
非静态方法前面其实有一个默认的对象this
this在构造器(或< init>)它表示的是正在创建的对象,因为这里是在创建Son对象,所以test()执行的是子类重写的代码(面向对象多态)
子类的实例化方法:
- (1)super()(9)(3)(2)
- (2)i=test()(9)
- (3)子类的非静态代码块(8)
- (4)子类的无参构造(最后)(7)
- 9、3、2、9、8、7
因为创建了两个Son对象,因此实例化方法< init>执行两次
方法的重写Override
(1)哪些方法不可以被重写
- final方法
- 静态方法
- private等子类中不可见方法
(2)对象的多态性
- 子类如果重写了父类的方法,通过子类对象调用的一定是子类重写过的代码
- 非静态方法默认的调用对象是this
- this对象在构造器活着说< init>方法中就是正在创建的对象
进阶要求
- Override和Overload的区别
- Override重写的要求?
- 方法名
- 形参列表
- 返回值类型
- 抛出的异常列表
- 修饰符
- 了解《JVM虚拟机规范》中关于< clinit>和< init>方法的说明、invokespecial指令
4.方法的参数传递机制
题目
public class Exem4 {
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);
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;
}
运行结果![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/76c8ce580ed21ad6245357c07e514882.png)
考点
- 方法的参数传递机制
- String、包装类等对象的不可变性
- JAVA中的栈和堆(https://www.cnblogs.com/ibelieve618/p/6380328.html)
相关资料
- 栈:由JVM分配区域,用于保存线程执行的动作和数据引用。栈是一个运行的单位,Java中一个线程就会相应有一个线程栈与之对应。
- 堆:由JVM分配的,用于存储对象等数据的区域。
- 常量池:在编译的阶段,在堆中分配出来的一块存储区域,用于存储显式的String,float或者integer.例如String str=“abc”; abc这个字符串是显式声明,所以存储在常量池。String str=new String(“abc”)就不是显示的。而Interfer-128-127在常量池,
过程:
栈中黄色代表主方法区变量,蓝色区表示静态方法区变量
以上步骤执行步骤
int i =1;
String str =“hello”;
Integer num = 200;
int[] arr = {1,2,3,4,5};
MyData my = new MyData();
- 第1步——执行int i =1;
- 第2步——执行String str =“hello”;
在栈内存中开辟一个空间,存放str变量(str变量是局部变量)。
同时,在堆内存中也开辟一个空间,存放“hello”,显式的String保存在常量池,str指向该内存地址。 - 第4步——执行 Integer num = 200;
同第二步,虽然 Integer是包装类,但是超出了-128-127范围,不在常量池。 - 第5步——执行 int[] arr = {1,2,3,4,5};
隐藏以下几分支
JVM执行main()函数,在栈内存中开辟一个空间,存放arr变量(arr变量是局部变量)。
同时,在堆内存中也开辟一个空间,存放{1,2,3,4,5}数组,堆内存会自动内存首地址值,如0x8989。
数组在栈内存中的地址值,会附给arr,这样arr也有地址值。所以,arr就指向(引用)了这个数组。此时,所有元素均未附值,但都有默认初始化值0。 - 第6步——MyData my = new MyData();
同上面的。
方法的参数传递机制
一、形参是基本数据类型
- 传递数据值
二、实参是引用数据类型
- 传递地址值
- 特殊类型:String、包装类对象不可变性
JVM执行change()函数,在栈内存中又开辟一个新的空间
实参给形参赋值,基本数据类型:数据值;引用数据类型:地址值。
change()执行完毕,变量立即释放,空间消失。但是main()函数空间仍存在,main中的变量仍然存在,不受影响。
6.递归与迭代
编程题:有n步台阶,一次只能上1步或2步,共有多少种走法?
一、递归![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/0da483738fc52d09debed676089529e9.png)
public class TestStep {
@Test
public void test(){
System.out.println(f(1));
}
//实现f(n):求n步台阶,一共有几种走法
public int f(int n){
if(n<1){
throw new IllegalArgumentException(n+"不能小于1");
}
if(n==1||n==2){
return n;
}
return f(n-1)+f(n-2);
}
}
二、循环迭代
public class TestStep2 {
@Test
public void test(){
System.out.println(loop(40));
}
public int loop(int n){
if(n<1){
throw new IllegalArgumentException(n+"不能小于1");
}
if(n==1||n==2){
return n;
}
int one = 1;//初始化为走到第一级台阶的走法
int two = 2;//初始化为走到第二级台阶的走法
int sum = 0;
for(int i=3;i<=n;i++){
//最后跨1步+最后跨2步的走法
sum = one + two;
one = two;
two = sum;
}
return sum;
}
}
小结
- 方法调用自身成为递归,利用变量的原值推断出新值成为迭代。
- 递归
- 优点:大问题转化为小问题,可以减少代码量,同时代码精简,可读性好;
- 缺点:递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。
- 迭代
- 优点:代码运行效率好,因为时间只因循环次数增加而增加,而且没有额外的空间开销;
- 缺点:代码不如递归简洁,可读性好
5.成员变量与局部变量
public class Exam5 {
static int s;//成员变量,类变量
int i ;//成员变量,实例变量
int j;//成员变量,实例变量
{
int i = 1;//非静态代码块中的局部变量i
i++;
j++;
s++;
}
public void test(int j){//形参,局部变量j
j++;
i++;
s++;
}
public static void main(String[] args){//形参,局部变量args
Exam5 obj1 = new Exam5();//局部变量obj1
Exam5 obj2 = new Exam5();//局部变量obj2
obj1.test(10);
obj1.test(20);
obj2.test(30);
System.out.println((obj1.i+","+obj1.j+","+obj1.s));
System.out.println((obj2.i+","+obj2.j+","+obj2.s));
}
}
运行结果
考点
- 就近原则:声明离他最近的,(前提考虑作用域)
- 变量的分类
- 成员变量:类变量、实例变量
- 局部变量
- 非静态代码块的执行:每次创建实例对象都会执行
- 方法的调用规则:调用一次执行一次
局部变量与成员变量的区别:
一、声明的位置
- 局部变量:方法体中{},形参,代码块{}中
- 成员变量:类中方法外
- 类变量:有static修饰
- 实例变量:没有static修饰
二、修饰符
- 局部变量:final
- 成员变量:public、protected、private、final、static、volatile、transient
三、值存储的位置
- 局部变量:栈
- 实例变量:堆
- 类变量:方法区
四、作用域
- 局部变量:从声明出开始,到所属的}结束
- 实例变量:在当前类中“this.”(有时this.可以缺省),在其他类中“类名.”或“对象名.”访问
- 类变量:在当前类中“类名.”(有时类名,可以省略),在其它类中“类名.”或“对象名.”访问
五、生命周期
- 局部变量:每一个线程,每一次调用执行都是新的生命周期
- 实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量都是独立的
另外:实例变量有默认值,实例化过程中,代码块也会执行。局部变量没有默认值
堆(Heap),此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
通常所说的栈(Stack),是指虚拟机栈,虚拟机栈用于存储局部变量表等。局部变量表存放了编译期可知长度的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完,自动释放。
**方法区(Method Area)**用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。
第一步:
执行 main函数,开辟一个栈空间。执行Exam5 obj1 = new Exam5();栈存储Exam5()的对象引用obj1,同时堆中存储实例变量int i和int j,默认值都为为0。成员变量、类变量s,在存储在方法区中,大家共享。实例化时会执行非静态代码块,代码块在栈中开辟一个新的空间,int i =1,i++就近原则,该区的局部变量i=1;
第二步:
执行Exam5 obj2 = new Exam5();原先obj1< init>方法执行完成释放,堆中存储Exam5()另外一个实例变量obj2,栈中存储对象的引用地址。后面的存储同obj1的方法。同时静态变量s是大家共享的,++1等于2;
第三步:
执行obj1.test(10);此时在栈中开辟一个obj1.test方法的空间,存储形参int j(没有默认值),实参会将初始值为10会传给j。
就近原则
i就近原则,obj1的实例对象局部变量参数i++;变为1。静态变量s++为3
第四步:
obj1.test(20);同第三步
第五步:
obj2.test(30);同前两步
当局部变量与xx变量重名时,如何区分:
- 局部变量与实例变量重名:在实例变量前面加“this.”
- 局部变量与类变量重名:在类变量前面加“类名.”
ssm面试题
7.Sringbean之间的作用域有什么区别?
在Spring中,可以在< bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例还是多实例的。
默认情况下,Spring只为每个在IOC容器里声明的bean创建唯一一个实例,整个IOC容器范围内都能共享该实例:所有后续的getBean()调用和bean引用都将返回这个唯一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。
类别 | 说明 |
---|---|
singleton | 在springIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在 |
prototype | 每次调用getBean()时都会返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTPSession共享一个Bean,不同的HttpSession使用不同的Bean。该作用域适用于WebApplicationContext环境 |
可以通过scope属性来指定bean的作用域
8.事务传播属性和事务传播行为
事务的四个特性(ACID):
- 原子性(Atomicity):
事务最基本的操作单元,要么全部成功,要么全部失败,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。 - 一致性(Consistency):
事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。 - 隔离性(Isolation):
指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。 - 持久性(Durability):
指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态
事务的传播行为:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。
事务的属性:
1.propagation:用来设置事务的传播行为
事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务
- Propagation.REQUIRED默认值,使用原来的事务
- Propagation.REQUIRES_NEW;将原来的事务挂起,开启一个新的事务
2.isolation:用来设置事务的隔离级别
- Isolation.REPEATABLE_READ:可重复读,MySQL默认的隔离级别
- Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别
事务的传播行为可以由传播属性。Spring定义了7种类传播行为。
举例:一个checkout调用类purchase方法,两个方法都有各自的事务
如果是默认的情况,purchase使用的事务是外面chekout的事务,两本书价格为110,余额为100,失败事务回滚,一本书也买不成功。
purchase开始一个新事务,原先的事务被挂起,可以买成功一本1001。余额变为40。库存减一。1002买不成功。
事务的隔离级别
数据库事务并发问题
假设现在有两个事务:Transaction01和Transaction02并发执行。
1)脏读
1.Transaction01将某条记录的AGE值从20修改为30.
2.Transaction02读取了Transaction01更新后的值:30
3.Transaction01回滚,AGE值恢复到了20
4.Transaction02读取到的30就是一个无效的值
2)不可重复读
1.Transaction01读取了AGE值为20
2.Transaction02将AGE值修改为30
3.Transaction01再次读取AGE值为30,和第一次读取不一致
3)幻读
1.Transaction01读取了STUDENT表中的一部分数据
2.Transaction02向STUDENT表中插入了新的行
3.Transaction01读取了STUDENT表时,多出了一些行
隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
1)读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
2)读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
3)可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
4)串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
各个隔离级别解决并发问题的能力见下表:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
READ UNCOMMITTED | 有 | 有 | 有 |
READ COMMITTED | 无 | 有 | 有 |
REPEATABLE READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
各种数据库产品对事务隔离级别的支持程度:
Oracle | MySQL | |
---|---|---|
READ UNCOMMITTED | x | √ |
READ COMMITTED | √(默认) | √ |
REPEATABLE READ | x | √(默认) |
SERIALIZABLE | √ | √ |
执行完第一个查询之后讲数据库价格改为600
mysql的默认级别可重复读,还是读取价格为60
隔离级别为读已提交,会读取到修改后的事务
9.SpringMVC中如何解决POST请求中文乱码问题,GET的又如何处理?
表单:
1.在web.xml添加过滤器过滤器:
上面是源码,下面是具体方法:
解决get请求乱码
1.在tomcat 配置文件serve.xml 文件中的第一个connector中添加URIEncoding=“UTF-8”
10.简单谈一下SpringMVC的工作流程
资料:https://blog.csdn.net/qq_30225725/article/details/88190399
成功页面:
testModelAndView
testMap
过程
第一步:用户发送请求至前端控制器DispatcherServlet。
第二步: DispatcherServlet收到请求调用HandlerMapping处理器映射器。
第三步:处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
找到了hander处理器。
第四步: DispatcherServlet调用HandlerAdapter处理器适配器。
如上图此时的mv还是null(还没调用之前),调用之后如下图
第五步: HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
来到了这里,调用testMap,返回添加后的模型数据
第六步:Controller执行完成返回ModelAndView
此时的mv(调用完之后)
第七步:HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
第八步: DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
第九步: ViewReslover解析后返回具体View。
得到视图解析器
第十步:DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)
渲染视图,得到视图view
第十一步:DispatcherServlet响应用户。
输出模型数据
暴露模型数据,放到request域中
获取转发器
进行请求转发
12.Mybatis中的当实体类的属性名与表中的字段名不一样,怎么办?
解决方案:
1.写sql语句时起别名
2.在MyBatis的全局配置文件中开启驼峰命名规则
3.在Mapper映射文件中使用resultMap来自定义映射规则
13.Linux常用服务类相关
13.git分支相关命令
1)创建分支
- git branch<分支名>
- git branch -查看分支
2)切换分支 - git checkout<分支名>
- 一步完成:git checkout -b <分支名>(创建加切换到该分支)
3)合并分支 - 先切换到主干 git checkout master
- git merge<分支名>
4)删除分支 - 先切换到主干 git checkout master
- git branch -D<分支名>
14.Redis持久化
Redis持久化方式有rdb和aof,当我们业务场景对数据一致性要求较高时,可以选择aof,较低的话可以选择rdb,当然可以都选择,如果都选择默认会用aof方式恢复数据。大数据时候,rdb恢复要比aof更快,相比aof,rdb一般保存频率较低,因此如果你对数据要求较高时,建议采取aof方式持久化。
Redis提供了2个不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
RDB全量替换,AOF增量操作
RDB(Redis DataBase)
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
备份是如何进行的
redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
RDB的优点
- 节省磁盘空间
- 恢复速度快
RDB的缺点
- 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
AOF(Append Of File)
以日志的形式来记录每个写操作,将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容,将写指令从前到后执行一次以完成数据的恢复工作。
AOF的优点
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
AOF的缺点 - 比起RDB占用更多的磁盘空间
- 恢复备份速度要慢
- 每次读写都要同步的话,有一定的性能压力
- 存在个别Bug,造成恢复不能
MySql什么时候建立索引
sql优化分析
- 性能下降SQL慢
- 执行时间长
- 等待时间长
索引是什么
mysql官方对索引的定义为:索引(Index)是帮助mysql高效获取数据的数据结构。可以得到索引的本质:索引是数据结构。
你可以简单理解为“排好序的快速查找数据结构”。
一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上
优点
检索查询快,排序快。
类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本。
通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
缺点
虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,Mysql不仅要保存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息
实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的
哪些情况需要创建索引
主键自动建立唯一索引
频繁作为查询条件的字段应该创建索引
查询中与其它表关联的字段,外键关系建立索引
单键/组合索引的选择问题,组合索引性价比更高
查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度
查询中统计或者分组字段
**哪些情况不要创建索引
表记录太少
经常增删改的表或者字段
where条件里用不到的字段不创建索引
过滤性不好的不适合建索引
16.JVM的垃圾回收机制
题目:JVM垃圾回收机制,GC发生在JVM哪部分,有几种GC,它们的算法是什么
GC发生在堆里(周阳的jvm)
GC4大算法
- 引用计数法
- 复制算法
- 标记清除
- 标记压缩
一、引用计数法
缺点:
- 每次对对象赋值时均要维护引用计数器,且计数器本身也有一定的消耗;
- 教难处理循环引用(a引用b,b引用a,互相引用没办法解决)
JVM的实现一般不采用这种歌方式
二、复制算法(Copying)
年轻代中使用的是MinorGC ,这种GC算法采用的是复制算法
原理
- 从根集合(GC Root)开始,通过Tracing从From中找到存活对象,拷贝到To中;
- From 、To交换身份,下次内存分配从To开始;
三、标记清除(Mark-Sweep)
老年代一般是由标记清除或者是标记清除与标记整理的混合实现
四、标记压缩(Mark-Compact)
老年代一般是由标记清除或者是标记清除与标记整理的混合实现
在整理压缩阶段,不再对标记的对象做回收,而是通过所有存活对象都向一端移动,然后直接清除边界以外的内存。
五、标记-清除-压缩(Mark-Sweep-Compact)
原理
- 1.Mark-Sweep和Mark-Compact的结合
- 2.和Mark-Sweep一致,当进行多次GC后才Compact
减少移动对象的成本(老年代)
项目面试题
redis在项目中的使用场景
五大数据类型![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2d94189c761e2d91b3637feb729d6388.png)
Hash中存储用户信息为什么不使用string类型来存储
get的时候反序列化出id,name,age,然而如果只修改id的信息,就会浪费性能。
18 ES与solr的区别
集群结构
单点登陆实现的过程
19
1.异步
2.并行
3.排队
4.电商中的使用场景