概述
一、JavaSE 一些面试知识点
1、自增变量
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
最后一步
执行结果
i=4
j=1
k=11
小结:
2、单例模式
什么是Singleton?
Singleton:在Java中即指单例设计模式,探视软件开发最常用是设计模式之一
单:唯一
例:实例
单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
例如:代表JVM运行环境的Runtime类
要点:
一是某个类只能有一个实例
构造器私有化
二是他必须自行创建实例
含有一个该类的静态变量来保存这个唯一的实例
三是它必须自行向整个系统提供这个实例
对外提供获取该类实例对象的方式
- 直接暴露
- 用静态变量的get方法获取
几种常见形式
饿汉式:直接创建对象,不存在线程安全问题
直接实例化饿汉式(简洁直观)
枚举式 (最简洁)
静态代码块饿汉式(适合复杂实例化)
懒汉式:延迟创建对象
线程不安全(使用于单线程)
线程安全(使用于多线程)
静态内部类模式(适用于多线程)
饿汉式
直接实例化饿汉式(简洁直观)
public class Singleton1{
/**
* 1、构造器私有化
* 2、自行创建,并且用静态变量保存
* 3、向外提供实例
* 4、强调这是一个单例,我们可以用final修改
*/
private Singleton1() {
}
public static final Singleton1 INSTANCE = new Singleton1();
}
public enum Singleton2 {
/**
* 枚举类型:表示该类型是有限的几个
*/
INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
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;
}
}
懒汉式
线程不安全
public class Singleton4 {
/**
* 1、构造器私有化
* 2、用一个静态变量保存这个唯一的实例
* 3、提供一个静态方法,获取这个实例对象
*/
static Singleton4 instance;
private Singleton4() {}
public static Singleton4 getInstance() {
if (instance == null) {
instance = new Singleton4();
}
return instance;
}
}
线程安全(使用于多线程)
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;
}
}
静态内部类模式(适用于多线程)
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;
}
}
3、类初始化实例初始化
类初始化
一个类要创建实例,需要先加载并初始化该类
main方法所在的类需要先加载和初始化
一个子类初始化就是执行clinit方法
clinit方法由静态类变量显示赋值代码和静态代码块组成
类变量显示赋值代码和静态代码块代码从上到下执行
clinit方法只调用一次
实例初始化过程
实例初始化就是执行 init() 方法
init () 方法可能重载有多个,有几个构造器就有几个 init() 方法
init() 方法由非静态实例变量显示赋值代码和非静态代码块,对应构造器代码组成
非静态实例变量显示赋值代码和非静态代码块从上到下顺序执行,而对应构造器的代码最后执行
每次创建实例对象,调用对应构造器,执行的就是对应的 ini方法
init 方法的首行是super()和super(实参列表) ,即对应父类的 init 方法
Father.java
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()方法
*/
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
package com.atguigu.classLoader;
/**
* 子类的初始化<clinit>
* 1、j = method()
* 2、子类的静态代码块
*
* 先初始化父类 (5)(1)
* 初始化子类 (10) (6)
*
* 子类实例化方法:
* 1、super()(最前
* 2、i = test() (9)
* 3、子类的非静态代码块 (8)
* 4、子类的无参构造(最后)(7)
*/
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();
}
}
执行结果
(5)
(1)
(10)
(6)
(9)
(3)
(2)
(9)
(8)
(7)
(9)
(3)
(2)
(9)
(8)
(7)
4、方法参数传递机制
代码如下:
package com.xingchen.methodParam;
import java.util.Array
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
输出结果
i= 1
str= hello
num= 200
arr= [2, 2, 3, 4, 5]
my.a= 11
5、递归与迭代
首先看一道编程题如下:
有 n 步台阶,一次只能上 1 步或者 2 步,共有多少种走法?
1、递归fen
分析如图,当n等于1或者2时,走法就等于n,从第三层台阶开始,每一层台阶为前两层台阶走法之和。
2、迭代
用 one、two 这两个变量来存储 n 的最后走一步和最后走两步,从第三层开始走,用 sum 来保存前两次的走法的次数,sum = two + one; 然后 two 移到 one,one 移到 sum 循环迭代。
代码如下:
/**
* 编程题:有 n 步台阶,一次只能上 1 步或者 2 步,共有多少种走法
*/
public class Code_05_StepProblem {
@Test
public void test() {
// 时间复杂度 ...
// long start = System.currentTimeMillis();
// System.out.println(recursion(40)); // 165580141
// long end = System.currentTimeMillis(); // 537
// System.out.println(end - start);
// 时间复杂度 O(n)
long start = System.currentTimeMillis();
System.out.println(iteration(40)); // 165580141
long end = System.currentTimeMillis(); // 0
System.out.println(end - start);
}
// 递归实现
public int recursion(int n) {
if(n < 1) {
return 0;
}
if(n == 1 || n == 2) {
return n;
}
return recursion(n - 2) + recursion( n - 1);
}
// 迭代实现
public int iteration(int n) {
if(n < 1) {
return 0;
}
if(n == 1 || n == 2) {
return n;
}
int two = 1; // 一层台阶,有 1 走法, n 的前两层台阶的走法
int one = 2; // 二层台阶,有 2 走法, n 的前一层台阶的走法
int sum = 0; // 记录一共有多少中走法
for(int i = 3; i <= n; i++) {
sum = two + one;
two = one;
one = sum;
}
return sum;
}
}
总结:
1)方法调用自身称为递归,利用变量的原值推出新值称为迭代。
2)递归
优点:大问题转为小问题,可以减少代码量,同时代码精简,可读性好;
缺点:递归调用浪费了空间,而且递归太深容易造成堆栈的溢出。
3)迭代
优点:代码运行效率好,因为时间复杂度为0(n),而且没有额外空间的开销;
6、成员变量和局部变量
首先看一道题目如下:
考点?
1)就近原则
2)变量的分类
-
成员变量: 类变量、实例变量
-
局部变量
3)非静态代码块的执行:每次创建实例对象都会执行
4)方法的调用规则:调用一次执行一次
分析如图:
局部变量与成员变量区别:
1、声明的位置
局部变量:方法体{}中,形参,代码块{}中
成员变量:类方法外
类变量: 有static修饰
实例变量:没有static修饰
2、修饰符
局部变量:final
成员变量: public , private , protected, final , static, volatile ,transient
3、值存储位置
局部变量: 栈
实例变量: 堆
类变量: 方法区
堆(Heap) ,此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
通常所说的栈(Stack) ,是指虚拟机栈。虚拟机栈用于存储局部变量表等。局部变量
表存放了编译期可知长度的各种基本数据类型(boolean、byte、 char、short、 int、 float、long、double) 、对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。方法执行完, 自动释放。
方法区(Method Area)用于存储已被虛拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
4、作用域:
局部变量从声明处开始,到所属的}号结束
实例变量:在当前类中this 有时this. 可以省略 在其他类中通过 对象名. 访问
类变量: 在当前类中 类名 有时类名. 可以省略 在其他类中通过域名.或者对象名.访问
5、声明周期
局部变量:每一个线程,每一次调用执行都是新的生命周期
实例变量:随着对象的创建而初始化,随着对象的被回收而消亡,每一个对象的实例变量都是独立的
类变量:随着类的初始化而初始化,随着类的卸载而消亡,该类的所有对象的类变量是共享的
当局部变量与X变量重名时,如何区分:
1、局部变量与实例变量重名
- 在成员变量前面加“this.”
2、局部变量与类变量重名
- 在类变量前面加“类名.”
演示代码如下:
/**
* 成员变量和局部变量
*/
public class Code_06_LocalAndMemberVariable {
public static int s;
int i;
int j;
{
int i = 1;
i++;
j++;
s++;
}
public void test(int j) {
j++;
i++;
s++;
}
public static void main(String[] args) {
Code_06_LocalAndMemberVariable obj1 = new Code_06_LocalAndMemberVariable();
Code_06_LocalAndMemberVariable obj2 = new Code_06_LocalAndMemberVariable();
obj1.test(10);
obj1.test(20);
obj2.test(30);
System.out.println(obj1.i + "," + obj1.j + "," + obj1.s); // 2 1 5
System.out.println(obj2.i + "," + obj2.j + "," + obj2.s); // 1 1 5
}
}
二、SSM 一些面试知识点
1、Spring Bean 的作用域之间有什么区别?
在Spring的配置文件中,给bean加上scope属性来指定bean的作用域如下:
-
singleton:唯一 bean 实例,Spring 中的 bean 默认都是单例的。
-
prototype:每次请求都会创建一个新的 bean 实例。
-
request: 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。
-
session:每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。
-
global-session:全局session作用域,仅仅在基于portlet的web应用中才有意义,Spring5已经没有了。Portlet是能够生成语义代码(例如:HTML)片段的小型Java Web插件。它们基于portlet容器,可以像servlet一样处理HTTP请求。但是,与 servlet 不同,每个 portlet 都有不同的会话。
2、Spring 支持的常用数据库事务传播行为和事务的隔离级别?
1 事务的传播行为
1.1简介
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,例如方法可能继续在现有事务中运行,也可能开启一个新事物,并在自己的事务中运行,事务传播的行为有传播属性指定,Spring定义了7种类传播行为
传播属性 | 描述 |
---|---|
REQUIRED | 如果有事务在运行,当前的方法就在这个事务内运行,否则就启动一个新的事务,并在自己的事务内运行 |
REQUIRED_NEW | 当前方法必须启动事务,并在它自己的事务内运行,如果有事务正在运行,应该将他挂起 |
SUPPORTS | 如果有事务在运行,当前的方法就在这个事务内运行,否则他可以不运行在事务中 |
NOT_SUPPORTE | 当前的方法不应该运行在事务中,如果有运行的事务,将他挂起 |
MANDATORY | 当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常 |
NEVER | 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常 |
NESTED | 如果有事务在运行,当前的方法就应该在这个事物的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行 |
事务传播属性可以在@Transactional注解的propagation属性中定义
2 事务隔离级别
2.1数据库事务并发问题
假如现在有两个事务:Transaction01和Transaction2并发执行
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表时,多出了一些行。
2.2 隔离级别
数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。
- 读未提交:READ UNCOMMITTED
允许Transaction01读取Transaction02未提交的修改。
- 读已提交:READ COMMITTED
要求Transaction01只能读取Transaction02已提交的修改。
- 可重复读:REPEATABLE READ
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。
- 串行化:SERIALIZABLE
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
- 各个隔离级别解决并发问题的能力见下表
脏读 | 不可重复读 | 幻读 | |
---|---|---|---|
READ | 有 | 有 | 有 |
READ COMMITTED | 无 | 有 | 有 |
REPEATABLE READ | 无 | 无 | 有 |
SERIALIZABLE | 无 | 无 | 无 |
- 各种数据库产品对事务隔离级别的支持程度
Oracle | MySQL | |
---|---|---|
READ UNCOMMITTED | × | ✔ |
READ COMMITTED | ✔(默认) | ✔ |
REPEATABLE READ | × | ✔(默认) |
SERIALIZABLE | ✔ | ✔ |
总结
//1.请简单介绍Spring支持的常用数据库事务传播属性和事务隔离级别?
/**
- 事务的属性:
- 1.★propagation:用来设置事务的传播行为
事务的传播行为:一个方法运行在了一个开启了事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务
-Propagation.REQUIRED:默认值,使用原来的事务
-Propagation.REQUIRES_NEW:将原来的事务挂起,开启一个新的事务
- 2.★isolation:用来设置事务的隔离级别
-Isolation.REPEATABLE_READ:可重复读,MySQL默认的隔离级别
-Isolation.READ_COMMITTED:读已提交,Oracle默认的隔离级别,开发时通常使用的隔离级别
*/
3、Spring MVC 如何解决 POST 请求中文乱码问题
1、解决 POST请求中文乱码问题
修改项目中web.xml文件
<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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2、请求Get请求中文乱码问题
修改tomcat中server.xml文件
<Connector URIEncoding="UTF-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
4、Spring MVC 的工作流程?
整体流程
SpringMVC框架是一个基于请求驱动的Web框架,并且使用了‘前端控制器’模型来进行设计,再根据‘请求映射规则’分发给相应的页面控制器进行处理。
具体步骤:
第一步:发起请求到前端控制器(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…)
5、Mybatis 中当实体类中的属性名和表中的字段不一样,怎么解决?
解决方案有如下三种:
1、写SQL语句的时候写别名
2、在MyBatis的全局配置文件中开启驼峰命名规则,前提是符合驼峰命名规则
<!-- 开启驼峰命名规则,可以将数据库中下划线映射为驼峰命名
列如 last_name 可以映射为 lastName
-->
<setting name="mapUnderscoreToCameLCase" value="true" />
3、在Mapper映射文件中使用resultMap 自定义映射
<!--
自定义映射
-->
<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高级
1、Linux常用服务器类相关命令?
常用基本命令 -进程类,centos6和centos7及以上命令有些不同
1、centos6
service 服务名 start
service 服务名 stop
service 服务名 restart
service 服务名 reload
service 服务名 status
#查看服务的方法 /etc/init.d/ 服务名
#通过 chkconfig 命令设置自启动
#查看服务 chkconfig -list l grep XXX
chkconfig -level 5 服务名 on/off
2、运行级别(centos6)
inux 系统有 7 种运行级别 (runlevel) : 常用的是级别 3 和 5 。
- 运行级别0: 系统停机状态,系统默认运行级别不能设为0,否则不能正常启动
- 运行级别1: 单用户工作状态,root权限,用于系统维护,禁止远程登陆
- 运行级别2: 多用户状态(没有NFS),不支持网络
- 运行级别3: 完全的多用户状态(有NFS),登陆后进入控制台命令行模式
- 运行级别4: 系统未使用,保留
- 运行级别5: X11控制台,登陆后进入图形GUI模式
- 运行级别6: 系统正常关闭并重启,默认运行级别不能设为6,否则不能正常启动.
3、centos7
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
2、Git分支相关命令?
1、创建分支
git branch <分支名>
git branch - v 查看分支
2、切换分支
git checkout<分支名>
一步完成:git checkout - b <分支名>
3、合并分支
先切换到主干 git checkout master
git merge <分支名>
4、删除分支
先切换到主干 git checkout master
git branch <分支名>
5、工作介绍
简单来说就是 master 分支上线,如果 master 出问题,会创建一个 hotfix 分支进行解决 bug ,解决完后合并到 master 分支和 develop 分支,保持一个同步,有新的分支开发完成就会和 develop 分支合并,然后创建一个 release 分支进行测试,完成后在合并到 master 和 develp ,保持一致。
3、redis 持久化有几种类型,它们的区别是?
Redis提供了两种不同形式的持久化的方式
**1、RDB(Redis DataBase)
1)什么是RDB呢?
指定时间间隔从内存中的数据快照写入磁盘,也就是行话讲的Snapshot快照,它的恢复是将快照文件读取到内存中。
2)RDB备份是如何执行的?
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。在整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高校。RDB的缺点是最后一次持久化的数据可能丢失。
3)什么是 fork ?
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术”,**一般情况父进程和子进程会共用同一段物理内存,**只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
4)RDB 保存的文件
在 redis.conf 的配置文件中,默认保存文件的名称叫 dump.rdb
rdb文件的保存路径,也可以修改。默认为Redis启动时命令行所在的目录下
5)RDB 保存的策略
15 分钟 1 次添加 key 的操作,5 分钟 10 次添加 key 的操作,1 分钟 10000 次添加 key 的操作都会触发保存策略。
6)RDB 的备份
先通过 config get dir 查询 rdb文件的目录
将 *.rdb 的文件拷贝到别的地方
7)RDB 的恢复
关闭 Redis
先把备份文件拷贝到拷贝到工作目录下
启动 Redis,备份数据会直接加载
8)RDB 的优点
- 节省磁盘空间
- 恢复速度快
9)RDB 的缺点
虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。
在备份周期在一定间隔时间做一次备份, 所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
10)何时重写?
重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。
11)AOF 优点
- 备份机制更稳健,丢失数据概率更低。
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
12)AOF缺点
- 比起RDB占用更多的磁盘空间
- 恢复备份速度要慢
- 存在个别BUG,造成恢复不能。
4、MySQL什么时候适合创建索引,什么时候不适合创建索引?
1、什么适合适合创建索引
1)主键自动建立唯 一 索引
2)频繁作为查询条件的字段应该创建索引
3)查询中与其它表关联的字段,外键关系建立索引
4)频繁更新的字段不适合创建索引,因为每次更新不单是更新了记录还会更新索引
5)单键组索引的选择问题,who? 在高并发下领向创建组合索引
6)意询中排序的字段,排序字段若通过索引法访问将大大提高排序速度
7)查询中统计或者分组字段
2、什么适合不适合创建索引
1)表记录太少
2)经常增删改的表
因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数据列建立索引。
3) 注意,如果某个数据列包含许多重复的内容,为它建立索弓|就没有太大的实际效果。
5、JVM 垃圾回收机制,GC 发生在 JVM 哪部分,有几种 GC,他们的算法是什么?
参考如下文章:
jvm虚拟机(二)自动垃圾回收机制(GC)
JVM垃圾回收机制
四、项目面试知识点
1、redis在项目中的使用场景
数据类型 | 使用场景 |
---|---|
String | 比如:我想知道什么时候封锁一个IP(某一个IP地址在某一段时间内访问的特别频繁,那有可能这个IP可能存在风险,所以对它进行封锁),使用Incrby命令记录当前IP访问 存储用户信息【id,name,age】 存储方式:set(userkey,用户信息字符串) |
Hash | 存储用户信息【id,name,age】 存储方式:Hset(key,filed,value) hset(userKey,id,101) hset(userKey,name,“admin”) hset(userKey,age,23) 这样存储的好处:当修改用户信息某一项属性值时,直接通过filed取值,并修改值 -----修改案例------ hget(userKey,id) hset(userKey,id,102) ------------------------------ 为什么不使用String存储? 获取方式:get(userKey) 会把参数为userKey对应的用户信息字符串全部进行反序列号,而用户信息字符串包含了用户所有的信息 如果我只修改用户的ID,那反序列化的其他信息其实是没有任何意义的 序列化与反序列化是由IO进行操作的,使用String类型存储增加了IO的使用次数,降低了程序的性能 对值为某类信息时不建议使用String类型存储 |
List | 实现最新消息的排行,还可以利用List的push命令,将任务存在list集合中,同时使用另一个pop命令将任务从集合中取出 Redis——list数据类型来模拟消息队列。 比如:电商中的秒杀就可以采用这种方式来完成一个秒杀活动 |
set | 特殊之处:可以自动排重。 比如微博中将每个人的好友存在集合(Set)中 如果求两个人的共同好友的操作,我们只需要求交集即可。(交/并集命令) |
Zset | 有序集合(sorted set),以某一个条件为权重,进行排序。 比如:京东看商品详情时,都会有一个综合排名,还有可以安装价格、销量进行排名 |
2、Elasticsearch 与 solr的区别?
- 背景
他们都是基于Lucene搜索服务器基础上开发,一款优秀的,高性能的企业级别搜索服务器,【是因为他们都是基于分词技术创建的倒排索引的方式进行查询】
2)开发语言
Java
3)诞生时间
Solr:2004年诞生
ES:2010年诞生
4)主要区别
- 当实时建立索引的时候,solr会产生io阻塞,而es不会,es查询性能要高于solr
- 在不断动态添加数据的时候,solr的检索效率会变得低下,而es没有什么变化
- Solr利用zookeeper进行分布式管理,eres自带有分布式系统的管理功能,Solr一般都要部署到web服务器上,比如tomcat,启动tomcat的时候需要配置tomcat和solr的关联【Solr的本质,是一个动态的web项目】
- Solr支持更多格式的数据【xml、json、csv】等,而es仅仅支持json文件格式
- Solr是传统搜索应用的有利解决方案,但是es更加适用于新兴的是搜索应用
- Solr官网提供的功能更多,而es本身更加注重于核心功能,高级功能都有第三方插件完成
3、单点登录
单点登录: 一处登录多处使用!
前提:单点登录多使用在分布式系统中
一处登录,处处运行
Demo:
参观动物园流程
检票员=认证中心模块
1、我直接带着大家进动物园,则会被检票员拦住【看我们是否有票】,没有【售票处买票】
登录=买票
2、我去买票【带着票,带着大家一起准备进入动物园】 检票员check【有票】
Token = piao
3、我们手中有票就可以任意观赏动物园的每处景点
京东:单点登录,是将 token 放入到 cookie 中
案例:将浏览器的 cookie 禁用,则在登录京东则失效,无论如何登录不了
4、购物车实现过程
5、消息队列在项目中的使用
背景: 在分布式系统中如何处理高并发的
由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞,比如说,大量的 insert,update 之类的请求同时到达数据库 MySQL, 直接导致无数的行锁表锁,甚至会导致请求堆积过多,从而触发 too many connections ( 链接数太多 ) 错误,使用消息队列可以解决 【异步通信】
1)异步
2)并行
3)排队
4)消息队列在电商中的使用场景
5)消息队列的弊端
消息的不确定性: 延迟队列 和 轮询技术来解决问题即可!
最后
以上就是无心蛋挞为你收集整理的经典Java面试知识点总结-1一、JavaSE 一些面试知识点二、SSM 一些面试知识点三、 JAVA高级四、项目面试知识点的全部内容,希望文章能够帮你解决经典Java面试知识点总结-1一、JavaSE 一些面试知识点二、SSM 一些面试知识点三、 JAVA高级四、项目面试知识点所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复