java程序员面试题

目录

Java

1、栈和堆有什么区别

2、Java的数据类型

3、为啥要有包装类

4、short s1 = 1; s1 = s1 + 1;有什么错? s1 += 1;有什么错?

5、对象转换

6、实际开发中使用的计算类型

7、全局变量和局部变量的区别

8、Java访问权限

9、对static关键字的理解

10、static修饰的类能不能被继承

11、static是否可以修饰外部类和内部类

12、JDK、JRE、JVM三者的关系

13、JVM包含哪几部分

14、本地方法栈的作用

15、没有程序计数器会怎么样

16、Java程序是怎么运行的

17、Java代码的编译过程

18、介绍一下类加载的过程

19、对象的实例化过程

20、Java的内存分布情况1

21、JVM的双亲委派模型

22、JDK1.8新特性

23、==和equals的区别

24、为什么要重写hashCode()和equals()

25、两个对象值相同,但却可有不同的hash code,这句话对不对?

26、重载和重写的区别,重载的方法是否可以改变返回值的类型?

27、abstract和interface的概念

28、abstract和interface的区别

29、简述final

30、为什么内部类只能访问final变量

31、final, finally, finalize的区别

32、<<、>>、>>>

33、值传递和引用传递

34、String s = new String("xyz");创建了几个String Object?   

35、String类是否可以被继承

36、break 和continue的区别

37、String常用方法

38、String、StringBuffer和StringBuilder的区别

39、StringBuffer常用方法

40、java中的常用包

41、Java的异常接口,error和exception的区别1

42、异常跟踪栈

43、return与finally的先后关系

44、如何处理异常

45、常见异常

46、Java有哪些容器(集合类)?通用方法?1

47、HashSet和TreeSet的区别

48、List、Set、Map三者区别

49、Hash Map和Hashtable的区别 

50、HashMap的实现原理

51、HashMap的扩容机制1

52、线程安全的HashMap1

53、ConcurrentHashMap是怎么实现的2

54、ArrayList,LinkedList,Vector的存储性能和特性1

55、ArrayList,LinkedList,Vector的扩容机制

56、Java容器中,线程安全和不安全的分别有哪些

57、对泛型的理解

58、泛型擦除和泛型转换

59、List和List有什么区别

60、对Java反射机制的理解1

61、Java反射在实际项目中的应用场景1

62、Java的四种引用方式

63、Servlet的生命周期

64、forward 和redirect的区别

65、GC是什么? 为什么要有GC?

66、垃圾回收的优点和原理,常用回收机制

67、分代回收机制

68、怎么定义垃圾

69、内存泄漏和内存溢出的区别,如何避免

70、多线程有几种实现方法2

71、线程同步有几种实现方法

72、线程冲突

73、线程死锁

74、乐观锁和悲观锁的区别

75、如何实现互斥锁1

76、Java中的锁升级

77、run()和start()的区别1

78、wait,sleep,notify,notifyAll的作用1

79、多线程之间的通信方式

80、线程的生命周期1

81、对数据库连接池的理解

82、介绍一下线程池

83、线程池的工作流程1

84、线程池的拒绝策略

85、线程池的任务队列大小怎么设置

86、Java线程池有哪些

87、线程和线程池的区别

88、线程池的参数1

89、Java中的IO流

90、怎么用流打开一个大文件

91、Java的序列化与反序列化

92、jsp九大内置对象

93、jsp和servlet的区别

Spring/SpringMVC

1、什么是Spring

2、Spring的优点

3、什么是IOC

5、什么是AOP

6、Spring常用的注入方式

7、Bean的作用域

8、Bean的生命周期2

9、Spring中的bean是否线程安全

10、对Spring容器的了解

11、Spring自动装配Bean有哪些方式

12、Spring是如何管理Bean的

13、@Autowired和@Resource的区别

14、Spring的事务实现原理

15、Spring的事务实现方式

16、@Transactional注解的失效场景2

16、Spring的事务隔离级别

17、事务隔离级别使用不当,可能会发生什么问题1

18、Spring的事务传播机制有哪些

19、Spring的事务如何配置,常用注解有哪些

20、什么是Spring MVC

21、MVC架构

22、Spring MVC 的优点

23、Spring MVC有哪些组件

24、Spring MVC的运行流程

25、Spring MVC的常用注解有哪些3

26、如何解决get和post乱码问题

27、如何配置Spring MVC的拦截器

28、拦截器与过滤器的区别

29、动态代理的两种方式和区别1

30、什么是XSS攻击,如何避免

31、什么是CSRF攻击,如何避免

Mybatis

1、什么是Mybatis

2、Mybaits的优点

3、MyBatis的缺点

4、#{} 和${}的区别是什么1

5、resultType和resultMap的区别

6、Mybatis如何处理>,<等字符

7、模糊查询like语句该怎么写

8、Dao层接口的工作原理,Dao中的方法是否可以重载

9、Mybatis是如何将sql执行结果封装为目标对象并返回的,都有哪些映射形式

10、MyBatis分页和自己写的分页哪个效率高

11、如何获取自动生成的(主)键值

12、在mapper中如何传递多个参数

13、MyBatis输入输出支持的类型有哪些

14、有那些动态 sql标签

15、MyBatis实现一对一有几种方式?具体怎么操作的?

16、MyBatis实现一对多有几种方式,怎么操作的?

17、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

18、Mybatis的一级、二级缓存

19、什么是MyBatis的接口绑定?有哪些实现方式?

20、使用MyBatis的mapper接口调用时有哪些要求

21、MyBatis批量操作

SpringBoot

1、什么是Spring Boot

2、Spring Boot的优点

3、Spring Boot的注解有那些1

4、运行Spring Boot有哪几种方式

5、如何理解Spring Boot中的Starters

6、SpringBoot常用的starter有哪些

7、Spring、Spring MVC和Spring Boot的区别

8、为什么需要spring-boot-maven-plugin

9、什么是YAML

10、SpringBoot自动配置的原理

11、RequestMapping和GetMapping的区别

12、spring-boot-starter-parent有什么作用

13、Spring Boot 2.X有哪些新特性?与1.X有什么区别?

14、什么是Swagger

15、开启Spring Boot特性有哪几种方式

16、Spring Boot支持哪些日志框架

17、保护SpringBoot应用有哪些方法

18、SpringBoot读取配置的注解有那些1

19、前后端分离,如何维护接口文档

20、什么是Spring Data

21、SpringBoot 打成jar和普通的jar的区别

22、如何重新加载Spring Boot上的更改,而无需重新启动服务器

23、如何自定义端口

24、如何使用Maven设置Spring Boot应用程序

25、SpringBoot的事务是在哪层处理的

26、SpringBoot跨平台调API

27、SpringBoot中的yml文件都干些什么

28、Spring Boot的启动流程

29、如何在SpringBoot中实现一个定时任务

Spring Cloud

2、Spring Cloud五大核心组件

3、Spring Cloud的优点

4、什么是Eureka

5、Eureka的作用

6、什么是Eureka的自我保护模式

7、Eureka和Zoo Keeper的区别

8、什么是CAP原则

9、什么是网关,网关的作用

10、Zuul与Nginx有什么区别

11、什么是Hystrix

12、服务雪崩效应

13、在微服务中,如何保护服务

14、服务熔断、服务降级的区别

15、负载平衡的意义是什么

16、对分布式事务的了解

17、Spring Boot和Spring Cloud的区别

18、怎么定义依赖jar包的版本号

19、dependencyManagement与dependencies区别

Redis

1、什么是Redis

2、使用Redis有哪些好处

3、Redis和关系型数据库的区别

4、Redis的数据类型

5、List的常用命令

6、hash底层的数据结构

7、zset底层的数据结构

8、为何Redis速度这么快

9、字符串类型能存储的最大容量是多少

10、Redis持久化机制

11、Redis持久化机制的优缺点1

12、Redis常见性能问题和解决方案

13、Redis过期键的删除策略1

14、Redis的回收与淘汰策略

15、Redis的同步机制了解么

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

17、Redis如何设置密码及验证密码

18、怎么理解Redis事务

19、Redis事务相关的命令有哪几个

20、Redis key的过期时间和永久有效分别怎么设置

21、如何设计Redis的过期时间

22、Redis中如何实现分布式锁

23、Redis如何做内存优化

24、Redis的内存用完了会发生什么

25、如何实现Redis的高可用

26、缓存雪崩

27、缓存穿透

28、缓存预热

29、缓存降级

30、Redis工具开发建议

31、Redis适合存储什么数据1

MySql

2、数据库结构优化-垂直分库分表1

3、数据库结构优化-水平分库分表1

4、常用函数1

5、where和having的区别

6、数据库三范式

7、数据库的左右内外连接

8、表与表的关联关系

9、Mysql分页查询

10、MySql查重

11、SQL行转列

12、SQL注入攻击

13、Mysql都有哪些索引

14、创建索引的三种方式

15、如何判断是否加索引1

16、主键、外键和索引的区别

聚簇索引和非聚簇索引

Mysql查看查询是否用索引

17、MySql和Redis的数据同步问题

18、事务的特性1

19、SQL的事务隔离级别1

20、MySQL事务如何回滚

21、SQL语言包括哪几部分?每部分都有哪些操作关键字?1

23、%和_的区别

24、数据库锁机制

25、数据库死锁的解决办法

26、redo log、undo log、binlog的作用

27、MVCC是什么

28、MySQL主从同步是如何实现的

Linux常用命令1

1、常用命令

2、系统操作

3、网络设置

4、运行一个SpringBoot程序并查看日志

网络

1、OSI 七层模型

2、TCP和UDP的区别

3、GET和POST的区别

4、错误码

5、IPV4和IPV6有什么区别

6、HTTP和HTTPS的区别

7、TCP/IP协议

8、应用层报文怎么传输到另一个应用层

9、TCP的三次握手

10、TCP的四次挥手

数据结构与算法

1、打印二叉树每层的节点

2、排序都有哪几种方法

设计模式

1、单例模式1

2、工厂模式

3、观察者模式

4、生产者与消费者模式

消息队列

1、消息队列(MQ)有什么用

2、消息队列如何保证顺序消费

消息队列如何保证消息不丢

RabbitMQ和Kafka有什么区别

场景

单点登录

单点注销


Java

1、有什么区别

  1. 栈中存放的是类和方法中定义的属性和变量及对象引用变量;堆中存放的是new出来的对象和数组;
  2. 栈的特点:先进后出,变量一使用完毕会立即释放,节约内存空间;堆中的实体不再被指向时,JVM启动垃圾回收机制,自动清除
  3. 栈内存中的数据,没有默认初始值,需手动赋值;堆内存中所有的实体都有内存地址值,数据都有默认初始值;
  4. 栈的优点是存取速度快,仅次于直接位于CPU中的寄存器;堆的优点是可以动态地分配内存大小,生存期不必事先告诉编译器,GC会自动收走这些不再使用的数据;
  5. 栈的缺点是存放于栈中的数据大小与生存期必须是确定的,缺乏灵活性;堆的缺点是由于要在运行时动态分配内存,存取速度较慢。

2、Java的数据类型

Java数据类型包括基本数据类型和引用数据类型两大类:

基本数据类型8个关键字,分4类,分别是整型、浮点型、字符型、布尔型。

byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1,默认0。

short:2字节(16位),数据范围是 -2^15 ~ 2^15-1,默认0。

int:4字节(32位),数据范围是 -2^31 ~ 2^31-1,默认0。

long:8字节(64位),数据范围是 -2^63 ~ 2^63-1,默认0L。

float:4字节(32位),数据范围大约是 ±3.4*10^38,默认值0.0F。

double:8字节(64位),数据范围大约是 ±1.8*10^308,默认值0.0。

char:2字节(16位),数据范围是 \u0000 ~ \uffff,默认值为’\u0000’。

boolean:只有两个值true和false,默认值false。

在这8个基本类型当中,除了布尔类型之外的其他7个类型相互之间可以进行类型转换。

引用类型就是对一个对象的引用,根据引用对象类型的不同,可以将引用类型分为数组、类、接口。

3、为啥要有包装类

java语言是面向对象的语言,其设计理念是“一切皆对象”,但8种基本数据类型却不具备面向对象的特征,也不是继承自Object类,所以java需要一个包装类来完善面向对象的特性。

包装类可以进行对象操作,可以自动拆装箱,通过new关键字创建后存放到堆中,基本类型是直接创建,存放到栈中;包装类的初始值为null,基本类型的的初始值视具体的类型而定,比如整型的初始值为0,boolean型为false;还有就是在集合类中只能使用包装类型。

什么时候该用包装类,什么时候用基本类型,看这个字段允不允许为null值,如果允许null值,则必然要用包装类,否则基本类型就可以了。

基本类型和包装类在做运算时,包装类会自动拆箱为基本类型。

4、short s1 = 1; s1 = s1 + 1;有什么错? s1 += 1;有什么错?

short s1 = 1; s1 = s1 + 1;

s1+1运算结果是int型,需要强制转换类型,这样子才可以正确的编译

short s1 = 1; s1 += 1;(可以正确编译)

+=、-=、*=、/= 运算符拥有一个内置转换器,当左右两边都是整数时,会自动将等号右边 转为等号左边的类型。

5、对象转换

1、基本类型转换为字符串有三种方法:

使用包装类的 toString() 方法

使用字符串的 valueOf() 方法

用一个空字符串加上基本类型。

2、将字符串转换成基本类型有两种方法:

调用包装类的 parseXxx 静态方法。

调用包装类的 valueOf() 方法。

6、实际开发中使用的计算类型

实际开发中用的BigDecimal类,它比基本类型和包装类更为精确,常用方法:

加法add()、减法subtract()、乘法multiply()、除法divide()、绝对值abs()、比较大小compareTo(),小于返回-1,大于返回1、等于返回0;方法中的参数必须是BigDecimal的对象。

保留两位小数,四舍五入:

qty = qty.divide(days), 2, BigDecimal.ROUND_HALF_DOWN);

在输出时,使用toPlainString()方法,它不会将结果转为科学计数法。

7、全局变量和局部变量的区别

Java中的变量分为成员变量和局部变量,它们的区别主要有几个方面:

首先成员变量是在类的范围里定义的变量,作用域范围是类;局部变量是在方法里定义的变量,作用域范围在方法中;

其次成员变量有默认初始值,局部变量没有默认初始值;

还有就是未被static修饰的成员变量也叫实例变量,它存储于对象所在的堆内存中,生命周期与对象相同,被static修饰的成员变量也叫类变量,它存储于方法区中,生命周期与当前类相同;局部变量存储于栈内存中,作用域范围结束,变量空间会自动释放。

Java中没有真正的全局变量,我们所说的全局变量只是相对类而言的全局变量,不是真正意义的全局变量, Java不支持全局变量,原因在于全局变量破坏了引用的透明性,容易引起命名空间冲突。

可以将想要全局有效的变量写在properties文件中,需要时直接从此properties文件中读取变量值就可以了,这个值在任何时候都可以更改。

8、Java访问权限

Java提供了四种访问权限,按照修饰成员变量/方法时的权限大小分别是:

类只有两种访问权限:default和public。

9、对static关键字的理解

static可以修饰java类的所有成员,包括属性、方法、初始化块、内部类(包括接口、枚举),static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不是单个对象。

对static关键字而言,静态成员不能访问非静态成员。因为静态成员是属于整个类的,作用域范围比非静态成员要大,如果允许访问很有可能出现静态成员已经初始化完成,但非静态成员还未曾初始化的情况,会引起大量错误。

10、static修饰的类能不能被继承

static修饰的类可以被继承。

11、static是否可以修饰外部类和内部类

外部类的上一级程序单元是包,不能用static修饰;而内部类的上一级程序单元是外部类,可以用static修饰;被static修饰的内部类称为静态内部类,属于外部类相关,而不是外部类实例相关。

静态内部类需满足如下规则:

静态内部类可以包含静态成员,也可以包含非静态成员;

静态内部类只能访问外部类的静态成员;

外部类的所有方法、初始化块都能访问其内部定义的静态内部类;

在外部类的外部,也可以实例化静态内部类,语法如下:

外部类.内部类 变量名 = new 外部类.内部类构造方法();

12、JDK、JRE、JVM三者的关系

JDK=JRE+开发工具集(例如java编译工具等),JRE=JVM+javaSE标准类库等

JDK是提供给开发人员使用的开发工具包,是整个Java的核心

JRE是Java运行环境,包括JVM虚拟机及Java核心类库,可以单独提供给用户在终端运行java程序,安装包中的bin目录下就是jvm,lib是类库

JVM是java虚拟机,是java实现跨平台的最核心的部分,能够运行以Java语言编写的软件,只要在不同的平台上安装对应的JVM,就能一次编写、到处运行。

13、JVM包含哪几部分

JVM 主要由四大部分组成:

类加载器:负责加载class字节码文件,但它只负责class文件的加载,至于是否可以运行,则由执行引擎决定。

运行时数据区:是存放数据的,分为五部分:虚拟机栈,堆,方法区,程序计数器,本地方法栈。几乎所有的关于 Java 内存方面的问题,都是集中在这块。

执行引擎:Class文件被加载后,会把指令和数据信息放入方法区中,执行引擎则负责把这些命令解释给操作系统,也就是将JVM 指令集翻译为操作系统指令集。

本地库接口:负责调用本地接口的。

14、本地方法栈的作用

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈是为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的本地方法服务。

15、没有程序计数器会怎么样

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程正在执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

如果没有程序计数器,Java程序中的流程控制将无法得到正确的控制,多线程也无法正确的轮换。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的字节码指令的地址;如果执行的是本地方法,这个计数器的值应该为空。

16、Java程序是怎么运行的

我们写好的java程序先经javac工具编译成class文件,然后java工具运行,class文件送入jvm中,jvm通过类加载器将class文件加载到运行时数据区的方法区,也叫静态区,然后再调用执行引擎将jvm指令集翻译成操作系统指令集交给CPU去执行,这个过程中会需要用到本地库接口调用一些不同语言为 Java 提供的接口,例如驱动等。

17、Java代码的编译过程

Java代码的编译过程大致可以分为1个准备过程和3个处理过程。

一个准备过程是初始化插入式注解处理器过程;三个处理过程分别是解析与填充符号表过程;插入式注解处理器的注解处理过程;分析与字节码生成过程;

上述3个处理过程里,如果在执行插入式注解时有新的符号产生,就必须回转到之前的解析与填充符号表过程重新处理这些新符号。

18、介绍一下类加载的过程

一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading)、连接(Linking)、初始化(Initialization)、使用(Using)和卸载(Unloading)五个阶段。

19、对象的实例化过程

对象实例化过程,就是执行类构造器对应在字节码文件中的<init>()方法;其执行顺序是:静态变量、静态代码块、普通变量、普通代码块、构造器。

子类的实例化顺序是:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类普通变量、父类普通代码块、父类构造器、子类普通变量、子类普通代码块、子类构造器。

20、Java的内存分布情况1

Java虚拟机在执行Java程序时会把它所管理的内存划分为若干个不同的数据区域,比如:

  1. 程序计数器:是线程私有的
  2. Java虚拟机栈:是线程私有的,用于存储局部变量、引用变量等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  3. 本地方法栈
  4. Java堆:线程共享
  5. 方法区:线程共享,它用于存储已被虚拟机加载的类信息、常量、静态变量、字节码指令等数据。
  6. 常量池:是方法区的一部分,用于存放编译期生成的各种字面量与符号引用。

21、JVM的双亲委派模型

自JDK1.2以来,Java一直保持着三层类加载器,分别是:启动类加载器、扩展类加载器、应用程序类加载器;

而双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器,通过使用组合来复用父类加载器的代码。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此,最终所有的加载请求都应该传送到最顶层的启动类加载器中,只有当父类加载器反馈自己的搜索范围中没有找到所需的类时,子加载器才会尝试自己去完成加载。

其好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。

22、JDK1.8新特性

  1. Lambda表达式:Lambda允许把函数作为一个方法的参数。
  2. 方法引用:可以直接引用已有Java类或对象的方法或构造器。与lambda联合使用可以使语言结构更紧凑简洁,减少冗余代码。
  3. 函数式编程风格。
  4. 新的日期处理类LocalDateTime。
  5. HashMap,1.8使用的是数组+链表+红黑树的结构,链表存储的数据个数大于等于8时,会转为红黑树存储,除了添加外,其余操作都比链表要快;1.7中是数组+链表的结构,不会变更。

23、==和equals的区别

==对比的是栈中的值,因为基本数据类型的值是和变量一起存放在栈空间中,所以比较的是变量值引用类型存放在栈空间中的是其指向的对象在堆空间中的内存地址,所以比较的是内存地址值

Object中的equals中默认也是采用==比较,通常会重写,比如String、包装类等把它变成了值比较,所以一般情况下 equals 比较的是具体内容是否相等。

equals重写:先判断传入的对象与当前对象是否内存地址相等,相等返回true;否则进一步判断传入的对象是否为当前类的实例,是的话将其转为当前类的对象,然后校验对象属性的值是否相等,相等返回true,否则返回false。

Instanceof:是 Java 中的一个双目运算符,是Java的关键字。用于判断一个对象是否为某个类或接口的实例。

24、为什么要重写hashCode()和equals()

Object类提供的equals()方法默认是用==来进行比较的。而实际业务中,是需要比较两个对象的内容是否相同,因此通常都会重写。

由于hashCode()与equals()具有联动关系,所以equals()方法重写时,通常也要将hashCode()进行重写,使得这两个方法始终满足相关约定。

25、两个对象值相同,但却可有不同的hash code,这句话对不对?

不对,有相同的hash code。

hashCode()用于获取哈希码(散列码),如果两个对象相等,则它们必须有相同的哈希码。如果两个对象哈希码相同,则它们未必相等。

hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,确定该对象在集合中存放的位置。如果这个位置上没有元素,它就可以直接存储,不用再进行任何比较;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals的次数就大大降低了,几乎只需要一两次。

比如HashSet、HashMap就是采用这种方式,当对象不相等时,会采用链式结构在同一位置保存多个对象,也就是将新的对象链接到原来的对象后面。之后,再有新添加对象也映射到这个位置时,就需要与这个位置中所有的对象进行equals()比较,若均不相等则将其链到最后一个对象之后。

26、重载和重写的区别,重载的方法是否可以改变返回值的类型?

重载:同一个类中,方法名相同,参数列表不同(参数个数,参数类型,参数顺序),重载与方法的返回值以及访问修饰符无关,即重载的方法不能根据返回类型进行区分。

重写:发生在父子类中,两者中的方法名相同,参数列表也同,子类返回值必须是父类返回值的子类或其本身,不能是父类返回值的父类,子类抛出的异常必须是父类抛出的异常的子类或其本身,不能是父类抛出异常的父类,访问修饰符则要大于等于父类方法。private修饰的方法不能重写。

方法的重写是父子类间的一种多态性的表现。重载的方法是可以改变返回值的类型。

27、abstract和interface的概念

含有abstract修饰符的类即为抽象类,抽象类不能实例化。含有abstract方法的类必须定义为抽象类,抽象类中可以含有非抽象方法。抽象类中定义的抽象方法必须在具体子类中实现,因此,不能有抽象构造方法和抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

28、abstract和interface的区别

从设计目的上来说,接口体现的是一种规范和实现分离的设计;抽象类体现的是一种模板式设计。

从使用上来说,在实现上,抽象类使用extends关键字来继承;接口使用implements来实现;实现数量:一个类可以实现多个接口;但只能继承一个抽象类;抽象类可以有构造器和main方法,接口不能有;抽象类里的构造器并不是用于创建对象,而是让子类完成抽象父类的初始化操作;接口中的方法必须是public访问修饰符;抽象类中的方法可以是任意级别的访问修饰符;接口里不能有普通方法和初始化块;抽象类可以有。

两者的共同点在于:

1、接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。

2、接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

29、简述final

final:最终的

它的作用是:

  1. 修饰类:表示类不可被继承
  2. 修饰方法:表示方法不可被子类重写(覆盖),但可以重载
  3. 修饰变量:表示变量一旦被赋值就不可更改它的值,是线程安全的;除此之外可以用枚举保持常量,它也是线程安全的。
  1. 如果final修饰的是静态属性,只能在静态初始化块中赋值或者在声明该属性时赋值
  2. 如果final修饰的是非静态属性,可以在声明该成员变量时赋值,可以在非静态初始化块中或构造器中赋值
  3. 如果修饰的是局部变量,可以在定义时赋值,也可以在后面的代码中赋值
  4. 如果修饰的是基本数据类型,则其数值一旦初始化之后不可更改
  5. 如果修饰的是引用类型,则在对其初始化之后便不能再让其指向另一对象,但是其引用的对象中的值可以变,这是因为final修饰的是引用变量指向的对象在堆中的内存地址值,它是连同引用变量本身一起存放在栈中的。

30、为什么内部类只能访问final变量

首先,内部类不会因为定义在方法中就会随着方法的执行完毕被销毁

这时,当外部类的方法结束时,局部变量就会被销毁了,但是内部类对象可能还存在(只有没人再引用它时,才会死亡),这里就出现了一个矛盾,内部类对象可能访问了一个不存在的变量,为了解决这个问题,就将局部变量复制了一份作为内部类的的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的副本,就好像延长了局部变量的生命周期。

为保证两个变量的一致性,就需要将局部变量设置为final,对它初始化后,值就不可更改。

31、final, finally, finalize的区别

final修饰的类,不能被继承;

final修饰的方法,不能被子类覆写。

final修饰的变量,不可再改变。

finally用于异常,表示无论是否发生异常,都会执行finally中的代码。而且一般情况,方法遇到return 就终止,但即使try...catch中有return 也仍然会执行finally中的代码。

finalize是Object类的一个方法,在垃圾收集器执行的时候会调用一次被回收对象的此方法,对象可以重写此方法,处理关闭流之类的工作。finalize()只会在对象内存回收前被调用一次;需要注意的是,finalize()方法何时调用、是否调用都是不确定的,开发过程中也不会主动调用finalize()方法。从JDK9开始,这个方法被标记为不推荐使用的方法。

32、<<、>>、>>>

<<表示左移,不分正负数,低位补0; 

>>表示右移,如果该数为正,则高位补0,若为负数,则高位补1;

>>>表示无符号右移,也叫逻辑右移,即无论正否,高位补0。

33值传递引用传递

值传递:

方法调用时,实参把它的值传递给对应的形参,形参拿到的是实参的值,此时栈中存在两个相等的基本类型,即实参和形参,后面方法中对形参的操作不会影响实参的值。

引用传递:

也称为传地址。方法调用时,实参将它的引用地址传给对应的形参,形参拿到的是对象的内存地址;此时,形参和实参内容相同,指向同一块内存地址,后续方法中对形参的修改也会影响到实参。

34、String s = new String("xyz");创建了几个String Object?   

两个或一个。

1个:如果执行此句之前,常量池里已经存在xyz,则只通过new创建一个新的String对象,指向常量池中的直接量;

2个:如果执行此句之前,常量池中不存在xyz,则JVM会先创建一个"xyz"存入常量池。然后在堆内存中创建一个新的String对象,指向常量池中的直接量。

35、String类是否可以被继承

String类是final修饰的,所以不可以继承。

36、break 和continue的区别

break:跳出循环,不再执行剩余部分。

continue:停止当次循环,进入下一次循环操作。continue之后的语句将不在执行。

37、String常用方法

  1. toLowerCase:字符串全部转小写
  2. toUpperCase:字符串全部转大写
  3. Trim:去掉字符两端的空格
  4. indexOf:查找后面的字符串在前面字符串的第一次出现的位置
  5. lastIndexOf:查找字符最后一次出现的位置
  6. equals:比较大小
  7. equalsIgnoreCase:忽略大小写比较大小
  8. charAt():输出指定位置的字符
  9. substring:从指定位置开始输出后续字符
  10. String[] split(String regex):以指定的规则将此字符串分割成数组

38、StringStringBufferStringBuilder的区别

String、StringBuffer和StringBuilder都是可以储存和操作字符串的,区别在于String类是不可变的,一但String对象创建后,对象中的值就不可更改;所谓的重新赋值,实际上是重新开辟了一个内存空间并赋值,而后让其指向新值。

StringBuffer、StringBuilder都代表可变字符串对象,它们都继承自 AbstractStringBuilder,并且两个类的构造方法和成员方法也基本相同。不同的是,StringBuffer是线程安全的,而StringBuilder是非线程安全的,所以StringBuilder性能略高。一般情况下,要创建一个可变字符串,建议优先考虑StringBuilder类。

String:适用于少量的字符串操作的情况

StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

39、StringBuffer常用方法

strbf.append(String s);//追加字符串。

strbf.reverse(); //反转字符串。

strbf.delete(int start, int end); //删除指定区间的字符串,左含右不含。

strbf.insert(int offset, String s); //将字符串插入指定下标字符前。

strbf.replace(int start, int end, String str); //替换指定下标区间的字符串。

40、java中的常用包

1、java.lang包:提供了Java的基础类和核心类,比如Object、字符串、各包装类等,它是默认导入的包。

2、java.util包:提供了包含集合、数组及相关工具类。

3、java.io包:提供了流和序列化相关的工具类。

4、java.time包:提供了LocalDate等日期处理类。

5、java.math包:提供了BigDecimal等计算工具类。

41、Java的异常接口error和exception区别1

Throwable是异常的顶层父类,代表所有的非正常情况。它有两个直接子类,分别是Error和Exception。

Error是错误,一般是指与虚拟机相关的问题,如系统崩溃、虚拟机错误等,会导致应用程序中断;通常应用程序无法恢复和捕获这些错误,因此应用程序无须去捕获或抛出Error对象。

Exception是异常,它被分为两大类,分别是编译时异常和运行时异常。Java认为编译时异常都是可以被处理或修复的,所以Java程序必须显式处理编译时异常,如果没有处理是无法通过编译的。运行时异常则更加灵活,运行时异常无须显式声明,需要的话,可以使用try...catch捕获或throws抛出。

42、异常跟踪栈

程序运行时,经常会发生一系列方法调用,从而形成方法调用栈。异常机制会导致异常在这些方法之间传播,而异常传播的顺序与方法的调用相反。异常从发生异常的方法向外传播,首先传给该方法的调用者,再传给上层调用者,以此类推。最终会传到main方法,若依然没有得到处理,则JVM会终止程序,并打印异常跟踪栈的信息。

43、return与finally的先后关系

当Java程序执行try...catch块时遇到了return,系统会先去寻找finally,如果没有finally,程序立即执行return,方法终止;

如果有finally,系统会先执行finally语句块。然后再执行return;但如果finally块里也使用了return,系统会立即结束,不会执行之前的return语句。

44、如何处理异常

异常处理分三步:

1、捕获异常

将业务代码包裹在try块中,当发生异常时,系统会为此异常创建一个异常对象,然后JVM会将异常对象交给catch块处理。

除此以外,当业务代码中判断某项异常的条件成立时,可以使用throw或throws关键字向外抛出全局异常处理器实例,包含异常代码和信息。

2、处理异常

在catch块中处理异常时,创建全局异常处理器实例,将异常代码和信息传入处理器;处理器会先记录日志,便于追溯。然后拼接异常信息传给父类Exception返回。

3、回收资源

执行finally代码块,关闭数据库连接、IO流等资源。

45、常见异常

1. java.lang.Nullpointerexception:空指针异常,调用了未经初始化的对象。

2. java.lang.NumberFormatException:字符串转数字异常,一般就是字符型数据中包含了非数字型的字符。

3. java.lang.arithmeticexception:数学运算异常,比如程序中出现了除以零的情况。

4. java.lang.arrayindexoutofboundsexception:数组下标越界,调用的下标超出了数组的范围。

5.java.sql.SQLSyntaxErrorException:sql语句错误,sql语句拼写或者符号错误。

6. java.lang.ClassCastException:类型转换异常,通常是类型强转时引起的。

46、Java有哪些容器(集合类)?通用方法?1

Collection

--List(有序、元素可重复)

--ArrayList,LinkedList,Vector

--Set(无序的,元素不可重复)

   --HashSet、TreeSet

--Queue(先进先出(FIFO)的队列)

Map(代表具有映射关系(key-value)的集合,其所有的key是一个Set集 合,也就是说key无序且不可重复)

--HashMap,HashTable,TreeMap

集合中的通用方法

void clear() 清空所有集合元素

boolean contains(Object o) 判断集合是否包含指定对象

boolean isEmpty() 判断集合的元素size是否为0

boolean remove() 删除集合中的某个元素

int size() 集合的元素个数

47、HashSet和TreeSet的区别

HashSet和TreeSet中的元素都是不能重复的,并且它们都是线程不安全的,两者的区别在于:

1、HashSet中的元素可以为null,但TreeSet中的元素不能为null;

2、HashSet是无序的,而TreeSet支持自然排序、自定义Comparator比较器排序;

3、HashSet底层采用哈希表实现,而TreeSet底层采用红黑树实现。

48、List、Set、Map三者区别

49、Hash Map和Hashtable的区别 

Hash Map是Hashtable的轻量级实现,他们都完成了Map接口。

Hash Map允许将null作为一个entry的key或者value,而HashTable不允许。

Hash Map没有contains方法,HashTable有。

最大的不同是,Hashtable直接在put、get等方法上面加synchronized关键字来实现线程安全,所以效率极差;而Hash Map没有,所以效率上高于HashTable。

50、HashMap的实现原理

HashMap是基于hash算法,通过put方法和get方法存储和获取对象。

存储对象时,我们将K/V传给put方法,它调用K的hashCode计算hash从而得到存储位置,进一步存储,HashMap会根据当前存储的占用情况自动调整容量(超过容量限制则resize为原来的2倍)。

获取对象时,我们将K传给get,它调用hashCode计算hash从而得到存储位置,并进一步调用equals()方法确定键值对。

51、HashMap的扩容机制1

HashMap采用懒加载模式,底层采用数组+链表+红黑树的结构存储,第一次调用put方法时才开始初始化并创建数组,数组的初始容量为16,而容量是以2的次方扩充的。

数组是否需要扩充是通过负载因子判断的,负载因子就是当前元素个数与数组容量的比重,默认负载因子为0.75时,就会扩充数组。

如果发生哈希冲突,也就是两个元素映射到了一个位置上,HashMap会通过链表将产生哈希冲突的元素组织起来。当链表长度超过8时,会将链表转换成红黑树提高性能,前提是当前数组长度超过64了,否则会放弃转换,先去扩充数组。而当链表长度小于8时,又会将红黑树转回链表。

红黑树之所以性能高,是因为通过红黑树节点的有序性保证查找和插入操作的时间复杂度是O(lgN),自然比链表的O(N) 高效很多;红黑树插入慢,查询快,链表插入快,查询慢。

52、线程安全的HashMap1

直接使用ConcurrentHashMap;或者使用Collections.synchronizedMap()将HashMap包装成线程安全的Map,HashTable采用给每个方法如put、get、remove等添加synchronized关键字来实现线程同步,所以效率极差。

53、ConcurrentHashMap是怎么实现的2

ConcurrentHashMap是在HashMap的基础上给哈希槽加Synchronized和CAS来进行并发控制。

54、ArrayList,LinkedList,Vector的存储性能和特性1

ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入操作,它们都支持直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized关键字,所以是线程安全的,但性能上比ArrayList差;

LinkedList使用链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入快而索引慢。

55、ArrayList,LinkedList,Vector的扩容机制

ArrayList:第一次调用add()时,底层创建10长度的数组,类似于单例的懒汉式,默认扩容为原来容量的1.5倍,原数组中的数据以System.arraycopy()复制到新的数组,因此开发中最好使用带参的构造器:ArrayList list = new ArrayList(int capacity),给出数组大小的预估值。

Vector:通过Vector()构造器创建对象时,底层创建10长度的数组,类似于单例的饿汉式。扩容时,默认扩容为原来的数组长度的2倍。

LinkedList:采用链表的自动扩容机制,初始默认10元素,超过扩容1.5倍。

56、Java容器中,线程安全和不安全的分别有哪些

java.util包下除了Vector、Hashtable外都是线程不安全的,但是它们的优点是性能好。如果需要线程安全的集合类,可以使用Collections工具类提供的synchronizedXxx()方法,将这些集合类包装成线程安全的集合类。

还有在java.util.concurrent包下提供了大量线程安全的集合类,它们分为两部分:以Concurrent开头的集合类代表了支持并发访问的线程安全的集合类,如ConcurrentHashMap;还有以CopyOnWrite开头的集合类采用复制底层数组的方式来实现线程安全,比如有CopyOnWriteArrayList、CopyOnWriteArraySet。

57、对泛型的理解

泛型是JDK5引入的,最初应用到集合中。

因为Java集合有个缺点就是把一个对象放入集合里之后,集合就会忽略这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型。这样以来集合就具有了很好的通用性。但却会带来两个问题:

1、集合对元素类型没有任何限制,这样可能出现说,程序将Integer类型的对象放入String类型的对象集合中,可能会引发异常。

2、由于把对象放入集合时,集合丢失了对象的类型信息,因此取出集合元素后通常还需要进行强制类型转换。但强转既增加了编程的复杂度,也可能引发ClassCastException异常。

因此从JDK5开始,Java引入了“参数化类型”的概念,允许程序在创建集合时指定集合元素的类型,Java的参数化类型就被称为泛型。

有了泛型以后,程序再也不会把其他对象放入集合中。而且程序更加简洁,集合自动记住所有集合元素的数据类型,从而无须对集合元素进行强制类型转换。

Java泛型的设计原则是,只要代码在编译时没有出现警告,就不会遇到运行时ClassCastException异常。

58、泛型擦除和泛型转换

泛型擦除是:当把一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。比如一个List<String>类型被转换为List,则该List对集合元素的类型检查变成了泛型参数的上限、即Object。

泛型转换是:虽然从逻辑上来看,List<String> 是List的子类,但对泛型而言,可以直接把一个List对象赋给一个 List<String> 对象,编译器仅仅提示“未经检查的转换”,不会引起编译时异常。

59、List<? super T>和List<? extends T>有什么区别

?是类型通配符,List<?> 可以表示各种List泛型的父类,意思是元素类型未知的List;

List<? super T> 用于设定类型通配符的下限,此处?代表一个未知的类型,但它必须是T的父类型;

List<? extends T> 用于设定类型通配符的上限,此处?代表一个未知的类型,但它必须是T的子类型。

60、对Java反射机制的理解1

Java程序中的对象在运行时可以表现为两种类型,即编译时类型和运行时类型。例如Number a = new Integer (),这行代码将会生成一个a变量,该变量的编译时类型为Number,运行时类型为Integer。

有时,程序在运行时接收到外部传入的一个对象,该对象的编译时类型是Object,但程序又需要调用该对象的运行时类型的方法,但由于编译时不知道该对象属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。

具体来说,通过反射机制,我们可以实现这么几个操作:

程序运行时,可以通过反射获得任意一个类的Class对象,并通过这个对象查看这个类的信息;

程序运行时,可以通过反射创建任意一个类的实例,并访问该实例的成员;

程序运行时,可以通过反射机制生成一个类的动态代理类或动态代理对象。

61、Java反射在实际项目中应用场景1

Java的反射机制在实际项目中常见的应用场景有:

1、使用Mybatis时,如果要创建数据库连接,则需要先通过反射机制加载数据库的驱动程序;

2、多数框架都支持注解/XML配置,从配置中解析出来的类是字符串,需要利用反射机制实例化;

3、面向切面编程(AOP),就是在程序运行时创建目标对象的代理类,这必须由反射机制来实现。

62、Java的四种引用方式

Java对象的四种引用方式分别是强引用、软引用、弱引用、虚引用;平常通过new关键字创建对象的方式就是强引用,虚引用主要用于跟踪对象被垃圾回收的状态。

63、Servlet的生命周期

servlet生命周期的定义,包括加载和实例化、初始化、处理请求以及服务结束,这个生命周期由javax.servlet.servlet接口中的init、service、destroy方法表达。

web容器加载servlet实例化,生命周期开始,通过调用servlet的init()方法进行servlet的初始化。

默认情况下,init方法会在第一次请求时被调用,但也可以通过配置<load-on-startup>1</load-on-startup>标签,提升其优先级,使得init在web服务器(tomtcat)启动时自动运行。

Servlet初始化后,请求到达时运行其service方法,service方法自动派遣与请求对应的doGet或doPost等方法处理请求,当服务器决定将实例销毁时调用其destroy方法。

64、forward 和redirect的区别

Forward:请求转发,一次请求一次响应。

redirect:重定向,两次请求一次响应。

forward的页面跳转发生在服务器端,地址栏不变,redirect的页面跳转发生在客户端,地址栏改变。

forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。

redirect就是服务器根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。

65、GC是什么? 为什么要有GC?

GC是垃圾收集的意思(Gabage Collection),也就是垃圾自动回收机制。主要是用来清理数据,节约内存空间。

因为内存处理是开发人员容易出现问题的地方,忘记或者错误的内存回收会导致程序的不稳定甚至崩溃;Java中使用GC来监视程序的运行,当对象不再使用时,就自动释放对象所使用的内存,Java语言没有提供释放已分配内存的显示操作方法,都是由GC自动运行的,GC的执行时间是不确定的。

一般情况下,无须显示地请求GC。也可以调用System类中的静态gc()方法运行GC,但这样并不能保证会立即回收指定对象。

在GC收集一个对象前,一般会调用对象的finalize()方法来释放资源。

66、垃圾回收的优点和原理常用回收机制

垃圾回收机制是Java的一个显著特点,它使其他语言最头疼的内存管理问题得到迎刃而解,让我们在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。

垃圾回收可以有效的防止内存泄露,有效的使用内存。

垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。

回收机制有分代收集垃圾回收、标记-整理垃圾回收、标记-清除垃圾回收、标记-复制垃圾回收。

67、分代回收机制

分代回收建立在三个分代假说之上:

弱分代假说:绝大多数对象都是朝生夕灭的。

强分代假说:熬过越多次垃圾收集过程的对象就越难以消亡。

跨代引用假说:跨代引用相对于同代引用来说仅占极少数。

基于这三个假说,GC将Java堆划分为新生代(YG)和老年代(OG)两个区域,然后老年代中又划分为若干个小块;然后在新生代中,每次垃圾收集时都会有大批对象死去,而每次回收后存活的少量对象,将会根据其年龄逐步晋升到老年代中存放,年龄也就是对象熬过垃圾收集过程的次数。

68、怎么定义垃圾

通过可达性分析算法来判定对象是否存活。这个算法的基本思路就是通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”,如果某个对象到GC Roots间没有任何引用链相连,也就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

69、内存泄漏和内存溢出区别,如何避免

内存泄漏是指程序运行过程中分配内存给临时变量,用完之后却没有被GC回收,始终占用着内存,既不能被使用也不能分配给其他程序,于是就发生了内存泄漏。

如何避免:尽早释放无用对象的引用;避免在循环中创建对象;使用字符串时尽量使用StringBuffer。

内存溢出是指程序运行过程中申请的内存大于系统能够提供的内存,于是就发生了内存溢出。

内存溢出的解决方案就是修改JVM启动参数,直接增加内存或者对代码进行走查和分析,找出可能发生内存溢出的位置。

70、多线程有几种实现方法2

多线程有三种实现方式,分别是:

1、继承Thread类来实现多线程,具体步骤:先定义Thread类的子类并重写线程执行体run()方法;然后创建Thread子类对象;最后调用对象的start()方法来启动线程。

2、实现Runnable接口来实现多线程,具体步骤:先定义Runnable接口的实现类,并实现线程执行体run()方法;然后创建Runnable实现类对象,并将其作为Thread类的target属性来创建并启动线程。

3、实现Callable接口来实现多线程,具体步骤:先创建Callable接口的实现类并实现线程执行体call()方法;然后创建Callable实现类对象;接着使用FutureTask类来封装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值;然后使用FutureTask对象作为Thread类的target属性创建并启动线程;最后调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。

一般推荐采用实现Runnable接口、Callable接口的方式来创建多线程。

71、线程同步有几种实现方法

所谓线程同步,就是指使用同步锁来防止在多线程情况下,同时操作一个可共享的资源时可能会出现的线程安全问题,实现线程同步的方法有:

1.synchronized关键字 1

synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。使用synchronized关键字修饰普通方法,锁是当前实例对象;修饰静态方法,锁是当前类的class对象;修饰非静态代码块,锁是括号里面的对象;synchronized不能修饰静态代码块。

2.使用特殊域变量(volatile)实现线程同步

volatile关键字为域变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此每次使用该域就要重新计算值,而不是使用寄存器中的值,它不能用来修饰final类型的变量,volatile在并发下是不安全的。

3.使用互斥锁ReetrantLock

ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized具有相同的基本行为和语义,并且扩展了其能力。调用lock()方法,同步执行体执行完毕之后,需要用unlock()释放锁。

关于synchronized和ReetrantLock的选择上:

  1. 如果synchronized能满足需求,就用synchronized,因为它代码简单
  2. 如果需要更高级的功能,就用ReentrantLock类,但要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

4.使用ThreadLocal管理局部变量

ThreadLocal是线程私有的局部变量存储容器,它用来存储线程私有变量,多线程间只能访问各自的局部变量,相当于在每个线程间建立了一个隔板。线程被终止后,它的所有实例将被回收。

72、线程冲突

线程冲突就是指多个线程访问共享的资源时,当一个线程对变量进行操作还未结束时,另一个线程也同时对这个变量进行操作,那么结果就是未知的,不准确的,此类问题统称为“线程冲突”。

73、线程死锁

线程死锁指的是两个线程互相持有对方依赖的共享资源,造成无限阻塞,导致死锁的根源在于不适当的运用“synchronized”关键字来管理线程对特点对象的访问

避免死锁的办法:

  1. 尽量不使用多线程
  2. 尽量不采用嵌套的同步语句。

74、乐观锁和悲观锁的区别

乐观锁:就是每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于多读少写的应用场景,这样可以提高吞吐量。Java中乐观锁是通过CAS非阻塞算法来实现的。

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到数据。Java中悲观锁是通过synchronized关键字或Lock接口来实现的。

75、如何实现互斥锁1

在Java中,最基本的互斥同步手段就是synchronized关键字,这是一种块结构(Block Structured)的同步语法。synchronized根据修饰的方法类型来决定是取代码所在的对象实例还是类型对应的Class对象来作为线程要持有的锁。

自JDK 5起,Java类库中新提供的Lock接口便成了Java的另一种互斥同步手段。基于Lock接口,用户能够以非块结构(Non-Block Structured)来实现互斥同步,从而摆脱了语言特性的束缚。

76、Java中的锁升级

自JDK 6后,JVM为了提高锁的获取与释放效率对synchronized进行了优化,引入了偏向锁和轻量级锁,从此锁的状态就有了四种:无锁、偏向锁、轻量级锁、重量级锁。并且四种状态会随着竞争的情况逐渐升级,且过程不可逆,即不可降级。

无锁是指没有对资源进行锁定,锁标志位0。

偏向锁是指当线程访问到synchronized代码块的时候,不存在锁竞争,锁对象变成偏向锁。

轻量级锁是指当有锁竞争时,由偏向锁升级到轻量级锁,锁标志位00。

重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态,会将轻量级锁升级为重量级锁,锁标志位01。

77、run()和start()区别1

run()方法被称为线程执行体,它的方法体代表了线程需要完成的任务,而start()方法是用来启动线程的。

调用start()方法启动线程时,系统会把该run()方法当成线程执行体来处理。但如果直接调用线程对象的run()方法,则run()方法就会立即执行,且在run()方法返回之前其他线程无法并发执行,也就没有达到多线程的目的。 

78、wait,sleep,notify,notifyAll的作用1

wait()是Object类的方法,作用是使一个线程进入阻塞队列,等待被唤醒,并且释放所持有的对象锁。只有针对此对象发出notify或notifyAll方法后本线程才进入就绪队列准备获得对象锁进入运行状态。

sleep()是线程类Thread的静态方法,作用是使一个正在运行的线程进入睡眠状态,让出执行机会给其他线程,但并没有释放对象锁,休眠时间一到,线程进入运行状态。

notify()是唤醒一个处于阻塞队列的线程,然后在当前线程释放锁后竞争锁,进而得到CPU的执行,需要注意的是在调用此方法时,并不能确切的唤醒某一个线程,而是由JVM来决定,而且不是按优先级。

notityAll()是唤醒所有处入阻塞队列的线程;然后在当前线程释放锁后竞争锁,进而得到CPU的执行。

79、多线程之间的通信方式

如果线程之间采用synchronized来保证线程安全,则可以利用wait()、notify()、notifyAll()来实现线程通信。

如果线程之间采用Lock来保证线程安全,则可以利用await()、signal()、signalAll()来实现线程通信。

80、线程的生命周期1

在线程的生命周期中,它要经过新建(New)、就绪(Ready)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。

1、当程序使用new关键字创建了一个线程之后,该线程就进入新建状态。

2、当调用线程的start()方法之后,该线程进入就绪状态。

3、当线程获得了CPU开始执行run()方法后,线程就处于运行状态。

4、当线程调用sleep()或wait()等方法后线程将进入阻塞状态,只有针对此线程发出notify()或notifyAll()方法后线程才会转回运行状态。

5、线程体执行完毕或抛出未捕获的异常后,线程就进入死亡状态。

81、对数据连接池的理解

J2EE服务器启动时建立足够的数据库连接,并将这些连接组成一个连接池,且一直维持不少于此数目的池连接。

创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠。

客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量由配置参数决定。

当使用的池连接调用完成后,池驱动程序会将其标记为闲,供其他请求调用。

82、介绍一下线程池

线程池是在系统启动时创建大量空闲的线程,程序将一个Runnable对象或Callable对象传给线程池,线程池就会启动一个空闲的线程来执行它们的方法体,当方法体执行结束后,该线程返回线程池中成为空闲状态,等待执行下一个线程对象。

从JDK5开始,新增了一个Executors工厂类来产生线程池,该工厂类包含如newCachedThreadPool()等静态工厂方法来创建线程池。

83、线程池的工作流程1

判断核心线程池是否已满,没满则创建一个新的工作线程来执行任务,满了就判断任务队列是否已满,没满则将新提交的任务添加在工作队列,满了就判断整个线程池是否已满,没满则创建一个新的工作线程来执行任务,已满则执行饱和(拒绝)策略。

84、线程池的拒绝策略

当线程池的任务队列已满且线程池中的线程数目达到maximumPoolSize要求,如果还有任务到来就会采取拒绝策略,常见策略有:

一是丢弃任务并抛出拒绝执行异常;二也是丢弃任务,但是不抛出异常。三丢弃队列最前面的任务,然后循环尝试重新执行任务。四是由调用线程处理该任务。

85、线程池的任务队列大小怎么设置

CPU密集型任务:一般为CPU核心数+1。

IO密集型任务:一般为CPU核心数的两倍。

混合型任务:可以将任务分成CPU密集型和IO密集型任务,然后分别用不同的线程池去处理。

86、Java线程池有哪些

Java通过Executors提供四种线程池

1、newSingleThreadExexcutor:单线程数的线程池

2、newFixedThreadPool:固定线程数的线程池

3、newCacheThreadPool:可缓存的线程池

4、newScheduledThreadPool:支持定时或周期任务的线程池

87、线程和线程池的区别

线程池可以重用存在的线程,减少对象创建、消亡的开销,性能比新创建的线程好。

88、线程池参数1

核心参数有:

corePoolSize:核心工作线程数,也称为最小工作线程数。

maximumPoolSize:最大线程数,线程池所允许的最大线程数量。

keepAliveTime:多余线程存活时间。

threadFactory:线程创建工厂,用于创建新线程。

handler:拒绝策略,当线程池和队列都满了,再加入线程会执行此策略。

89、Java中的IO流

IO(Input Output)用于实现对数据的输入与输出操作,按照数据流向,可以将流分为输入流和输出流;按照数据类型,可以将流分为字节流和字符流;按照处理功能,可以将流分为节点流和处理流。

    1. 节点流中常用类
      • 字节输入流:FileInputStream
      • 字节输出流:FileOutputStream
      • 字符输入流:FileReader
      • 字符输出流:FileWriter
    2. 处理流中常用类
      • 缓冲字节输入流:BufferedInputStream
      • 缓冲字节输出流:BufferedOutputStream
      • 缓冲字符输入流:BufferedReader
      • 缓冲字符输出流:BufferedWriter
      • 对象输入流:ObjectInputStream
      • 对象输出流:ObjectOutputStream
      • 数据输入流:DataInputStream
      • 数据输出流:DataOutputStream

90、怎么用流打开一个大文件

打开大文件,应避免直接将文件中的数据全部读取到内存中;可以使用一个缓冲流。缓冲流内部维护了一个缓冲区,通过与缓冲区的交互,减少与内存的交互次数。

91、Java的序列化与反序列化

序列化是指将一个Java对象写入IO流中,保存在磁盘上或在网络中传输,反序列化则是指从IO流中恢复Java对象。

若对象要支持序列化机制,则它的类需要实现Serializable序列化接口,该接口没有任何实现方法,只是标明该类是可以被序列化的。

若要实现序列化,则需要调用ObjectOutputStream对象的writeObject()方法,以输出对象序列。在反序列化时需要调用ObjectInputStream对象的readObject()方法,将对象序列恢复为对象。

92、jsp九大内置对象

    1. pageContext:4大作用域对象之一,存放当前页面能够访问的数据
    2. request:4大作用域对象之一,存放当前来自客户端的请求数据
    3. session:4大作用域对象之一,存放同一个浏览器的多个页面之间,共享的数据
    4. application:4大作用域对象之一,存放同一个网站,不同浏览器之间,共享数据
    5. response:封装了响应数据、操作的对象
    6. out:输出流,通过它可以把内容返回给客户端(浏览器)
    7. config:代表配置对象
    8. page:代表当前页面
    9. exception:异常处理对象

93、jsp和servlet区别

jsp经编译后就变成了Servlet,JSP的本质就是Servlet,JVM只能识别java类,不能识别JSP的代码,Web容器就将JSP的代码编译成JVM能够识别的java类。

jsp更擅于页面显示,servlet更擅于逻辑控制。

Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest、HttpServletResponse以及HttpServlet对象获取。

Jsp是Servlet的一种简化,使用Jsp只需要完成我们需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。

Spring/SpringMVC

1、什么是Spring

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的为Java应用程序提供基础性服务的容器框架目的是简化企业应用程序的开发,使开发者只需要关心业务需求。Spring已集成了20多个部分这些部分被分为:IOC、AOP、数据访问/集成,(数据库、事务管理)、Web和测试这五个模块。

2、Spring的优点

1、Spring属于低侵入式设计,代码污染极低;

2、Spring的IOC机制将对象间的依赖关系交由框架处理,降低了组件耦合性;

3、Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用;

4、方便程序的测试和集成各种优秀框架。

3、什么是IOC

IOC即控制反转。

软件系统在没有引入IOC容器之前,对象A依赖于对象B,那么对象A在需要对象B的时候,必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用已创建的实例,控制权都在自己手上。

在引入IOC容器之后,两个对象之间失去了直接联系,当对象A需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

也就是说对象A获得依赖对象B的过程,由主动变为了被动,控制权颠倒过来了,这就是“控制反转”这个名称的由来。

简单来说就是将IOC作为“第三方”,全部对象的控制权上交给“第三方”IOC容器使其成为整个系统的关键核心,使其他对象没有了耦合关系。它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用。

  1. IOC和DI的关系

IOC即控制反转,DI是依赖注入;IOC是目的,而为了达到这个目的需要DI这个技术手段。

5、什么是AOP

AOP即面向切面编程,也称面向方面,在AOP思想下,我们可以将解决共性需求的代码独立出来,比如权限检查、记录日志、事务管理等。然后通过配置的方式,声明这些代码在什么地方、什么时机调用。当满足调用条件时,AOP会将该业务代码织入到我们指定的位置,从而解决了问题,又不需要修改这一批组件的代码。

Spring AOP是通过动态代理实现的,默认采用JDK动态代理;AOP在遇到比如 final 修饰的、static 修饰的 、private 修饰的方法和构造方法时会失效。

AOP执行顺序:通过@order()注解配置,order越小越先执行,最先执行的最后结束。

6、Spring常用的注入方式

构造方法注入

setter注入

基于注解的注入

7、Bean的作用域

1、singleton:默认作用域,采用单例模式,每个容器中只有一个bean实例。

2、prototype:原型模式,会为每个bean请求创建一个bean实例。

3、request:为每一个request请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。

4、session:与request范围类似,同一个session会话共享一个实例,不同会话使用不同的实例。

5、global-session:全局作用域,所有会话共享一个实例。如果想要声明让所有会话共享存储变量的话,那么这个存储变量就需要存储在global-session中。

8、Bean的生命周期2

Spring容器管理Bean,涉及对Bean的创建、初始化、调用、销毁等一系列流程,这个流程就是Bean的生命周期。

这个过程是由Spring容器自动管理的,其中有两个环节我们可以进行干预。

1是可以自定义初始化方法,并在该方法前增加@PostConstruct注解,届时Spring容器将在调用SetBeanFactory方法之后调用该方法。

2是可以自定义销毁方法,并在该方法前增加@PreDestroy注解,届时Spring容器将在自身销毁前,调用该方法。

9、Spring中的bean是线程安全

Spring容器本身并没有提供Bean的线程安全策略。如果单例的Bean是一个无状态的,也就是线程中的操作不会对Bean的成员执行查询以外的操作,那么这个单例的Bean是线程安全的。比如,Controller、Service、DAO这样的组件,通常都是单例且线程安全的。如果单例的Bean是一个有状态的Bean,则可以采用ThreadLocal对状态数据做线程隔离,来保证线程安全。

10、对Spring容器的了解

Spring主要提供了两种类型的容器:BeanFactory和ApplicationContext。

Bean Factory是个Bean工厂,使用简单工厂模式,是Spring IOC容器顶级接口,可以理解为含有Bean集合的工厂类,作用是管理Bean,包括实例化、定位、配置对象及建立对象间的依赖。Bean Factory实例化后并不会立即自动实例化配置的Bean,只有当Bean被调用时才会实例化、装配依赖关系,属于延迟加载,适合非单例模式。

Application Context是Bean Factory的子接口,它扩展了Bean Factory的功能,包括对国际化的支持,统一的文件资源读取方式,事件传播及应用层的特别配置等。容器会在初始化时对配置的Bean进行预实例化,Bean的依赖注入在容器初始化时就已经完成,属于立即加载,适合单例模式,推荐使用。

11、Spring自动装配Bean有哪些方式

1、基于XML配置文件配置,bean所需的依赖项和服务在XML格式的配置文件中指定。它们通常以bean标签开头。

2、基于注解配置,可以通过在相关的类属性前使用@Autowired、@Resource注解配置bean。Spring容器默认不开启注解装配。需要时在Spring配置文件中启用它。标签为:<context:component-scan/>

3、基于Java API配置,通过使用@Configuration和@Bean来实现。@Configuration添加在类前,@Bean添加在方法前,当方法所在类被扫描时,底层会主动调该方法,该方法的返回值会作为上下文中的一个bean(默认单例)被其他类Autowired进去。

12、Spring是如何管理Bean的

现在的项目中大多时候会使用注解配置的方式来管理Bean,底层采用IOC。

常用的管理Bean的注解:

@MapperScan用于声明包的扫描范围;

@Scope用于声明Bean的作用域;

@PostConstruct、@PreDestroy用于声明Bean的生命周期。其中,被@PostConstruct修饰的方法将在Bean实例化之后被调用,@PreDestroy修饰的方法将在容器销毁前被调用。

13、@Autowired和@Resource的区别

@Autowired默认是按照类型装配注入的,可以结合@Qualifier,按照名称来装配。

@Resource默认是按照名称来装配注入的,如果找不到会按照类型来装配注入。

14、Spring事务实现原理

Spring事务的本质就是数据库对事务的支持,没有数据库的事务支持,Spring是无法提供事务功能的。Spring只提供了统一的事务管理接口,具体实现都是由各数据库自行完成,数据库事务的提交和回滚是通过redo log和undo log实现的。Spring会在事务开始时,根据当前环境中设置的隔离级别,调整数据库隔离级别,由此保持一致。

15、Spring事务实现方式

Spring支持编程式事务管理和声明式事务管理两种方式:

1、编程式事务管理的优点是可以作用到代码块级别;缺点是需要在业务逻辑代码中掺杂事务管理的代码。

2、声明式事务管理建立在AOP技术之上。本质是通过AOP技术,在目标方法开始之前启动一个事务,在方法执行完之后根据执行情况提交或者回滚事务。优点是只需在配置文件中做相关的事务规则声明或通过@Transactional注解即可,减少业务代码的污染;缺点是只能作用到方法级别。

16、@Transactional注解的失效场景2

1、访问权限问题 (只有public方法会生效)

2、方法用final修饰,不会生效,这是因为spring事务底层使用了aop,也就是通过jdk动态代理或者cglib帮我们生成了代理类,因此final修饰的方法是无法生效的

3、同一个类中的方法直接内部调用,会导致事务失效,可以新加一个Service方法,把@Transactional注解加到新Service方法上,把需要事务执行的代码移到新方法中即可

4、类本身未被spring管理,未添加@RestController、@Service等注解

5、多线程调用等。

16、Spring的事务隔离级别

事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度:

1、默认隔离级别,使用数据库的事务隔离级别。

2、读未提交,允许事务在执行过程中,读取其他事务未提交的数据。

3、读已提交,允许事务在执行过程中,读取其他事务已提交的数据。

4、可重复读,在同一个事务内,任意时刻的查询结果都是一致的。

5、串行化:所有事务逐个依次执行。

17、事务隔离级别使用不当,可能发生什么问题1

1、脏读:一个事务读到另一个事务未提交的更新数据。

2、幻读:例如第一个事务对表中的“全部数据行”进行了修改,第二个事务向表中插入了“一行新数据”。就会导致第一个事务的用户发现表中还存在未修改的数据行,就好象发生了幻觉一样。

3、不可重复读:例如在一个事务内对同一条select语句执行了两次,除此之外无任何操作,但先后得到的结果却不一致,这就是不可重复读。

18、Spring的事务传播机制有哪些

事务传播机制是指当出现业务方法嵌套调用的时候,如果这两个方法都是要保证事务的,那么需要保证当前事务可以传播到被嵌套调用的业务方法中。Spring规定了7种类型的事务传播机制:

19、Spring的事务如何配置,常用注解有哪些

对于声明式事务,可以使用@Transactional注解进行配置,它可以标注在类或者方法上。当它标注在类上时,代表这个类所有公共(public)非静态的方法都将启用事务功能。当它标注在方法上时,代表这个方法将启用事务功能。

另外,在@Transactional注解上,我们可以使用isolation属性声明事务的隔离级别,使用propagation属性声明事务的传播机制。

20、什么是Spring MVC

Spring MVC是一个基于MVC架构的用来简化web应用程序开发的spring的子框架。

21、MVC架构

MVC是Model-View-Controller的简写。

View:视图层,为用户提供使用界面,与用户进行直接交互。

Model:模型层,分为两部分,一是承载业务数据的数据库映射结果集Bean层:二是处理用户请求的服务层Service和与数据库交互的持久层Dao,也叫Mapper。

Controller:控制器,用于将用户请求转发给相应的 Model 进行处理,并根据Model的计算结果向用户提供相应响应。Controller默认是单例的,在@RestController之后增加@Scope("prototype")就可以改为多例模式。

通过这种设计模型把应用逻辑,处理过程和显示逻辑分成不同的组件实现。这些组件可以进行交互和重用。

22、Spring MVC 的优点

  1. Spring MVC是Spring的子框架,它拥有Spring的优点;
  2. 支持灵活的URL到页面控制器的映射;
  3. 支持与其他视图技术(JSP、Free Marker等)进行整合;
  4. 可以方便地进行数据验证、格式化,使用任意对象进行数据绑定操作;
  5. 支持RestFul风格。

23、Spring MVC有哪些组件

  1. Dispatcher Servlet:中央控制器,把请求给转发到具体的控制类
  2. Controller:处理具体请求的控制器
  3. Handler Mapping:映射处理器,负责映射中央处理器转发给Controller时的映射策略
  4. Model And View:服务层返回的数据和视图层的封装类
  5. View Resolver:视图解析器,解析具体的视图

24、Spring MVC的运行流程

  1. 用户发送请求至中央控制器Dispatcher Servlet;
  2. Dispatcher Servlet收到请求后,调用映射处理器Handler Mapping,请求获取处理器Handle;
  3. 映射处理器根据请求的url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给中央控制器Dispatcher Servlet;
  4. Dispatcher Servlet通过处理器适配器Handler Adapte调用处理器Handle;
  5. 执行处理器Handler(也就是后端控制器,需要程序员做处理);
  6. 处理器适配器HandlerAdapter将执行完成返回的ModelAndView返回给Dispatcher Servlet;
  7. Dispatcher Servlet将ModelAndView传给视图解析器View Resolver进行解析;
  8. View Reslover解析后返回具体View;
  9. Dispatcher Servlet对View进行渲染视图(即将模型数据填充至视图中)之后响应用户。

25、Spring MVC的常用注解哪些3

  1. @Controller:标识此类是控制器,单例
  2. @Service:标识此类是service实现类,单例
  3. @Repository:标识此类是dao实现类,单例
  4. @Component:标识此类是普通类,单例
  5. @RequestMapping:映射请求地址和方法之间的关联
  6. @ResponseBody:注解返回数据而不是返回页面
  7. @PathVariable:获取URL路径中的变量值
  8. @RequestParam:获取前端传入的参数
  9. @ModelAttribute:把前端参数,放入到同名属性对应的类中,并自动创建出一个bean对象,且默认放入request中。

26、如何解决get和post乱码问题

解决post请求乱码:在web.xml里边配置一个CharacterEncodingFilter过滤器并设置编码为 utf-8即可。

解决get请求乱码:修改tomcat配置文件添加编码与工程编码一致;对参数进行重新编码

27、如何配置Spring MVC的拦截器

Spring MVC拦截器的开发步骤:

实现handlerInterceptor接口,从preHandle()、postHandle()、afterCompletion()三个方法中选择合适的方法,实现拦截时要执行的具体业务逻辑。

然后定义配置类,并让它实现WebMvcConfigurer接口,在接口的addInterceptors方法中,注册拦截器,并定义该拦截器匹配哪些请求路径。

拦截器的执行流程是:

1、执行preHandle方法,它会返回一个布尔值。如果为false,则结束所有流程,如果为true,则执行下一步。

2、执行处理器逻辑,它包含控制器的功能。

3、执行postHandle方法。

4、执行视图解析和视图渲染。

5、执行afterCompletion方法。

28、拦截器与过滤器的区别

拦截器:在Spring MVC中依赖于Spring MVC框架,因此可以使用Spring的DI依赖注入进行一些业务操作。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,同时一个拦截器实例在一个controller生命周期之内可以多次调用。拦截器可以对静态资源的请求进行拦截处理。

过滤器:它依赖于servlet容器。它可以对几乎所有请求进行过滤,但缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据,或者在过滤器中修改字符编码,过滤低俗文字、危险字符等。

29、动态代理的两种方式和区别1

JDK动态代理:是Java提供的动态代理技术,可以在运行时创建接口的代理实例。

CGlib动态代理:采用底层的字节码技术,在运行时动态创建子类的方式生成代理对象,也因此它不能代理final修饰的类。

CGLib创建的代理对象在性能比JDK动态代理创建的代理对象高很多,但所花费的时间也比JDK动态代理多很多;所以,CGLib动态代理适合单例对象,JDK动态代理适合非单例对象。

30、什么是XSS攻击,如何避免

XSS攻击全称跨站脚本攻击,其原理是攻击者向有XSS漏洞的网站中输入恶意的HTML代码,当用户浏览该网站时,这段代码会自动执行,从而达到攻击的目的。XSS攻击类似于SQL注入攻击,SQL注入攻击中以SQL语句作为用户输入,从而达到查询/修改/删除数据的目的,而在XSS攻击中,通过插入恶意脚本,实现对用户游览器的控制,获取用户信息。XSS是Web程序中常见的漏洞,属于被动式且用于客户端的攻击方式。

XSS防范的总体思路是:对输入(和URL参数)进行过滤,对输出进行编码。

31、什么是CSRF攻击,如何避免

CSRF全称跨站请求伪造。一般来说,攻击者通过伪造用户的浏览器请求,向一个用户自己曾经认证访问过的网站发送出去,使目标网站接收并误以为是用户的真实操作而去执行命令。常用于盗号、转账、发送虚假消息等。攻击者利用网站对请求的验证漏洞而实现这样的攻击行为,网站能够确认请求来源于用户的浏览器,却不能验证请求是否源于用户的真实意愿下的操作行为。

如何避免:

1、使用验证码:关键操作页面加上验证码。但这种方法对用户不太友好。

2、在请求地址中添加token并验证

Mybatis

1、什么是Mybatis

Mybatis是一个半ORM(对象关系映射)框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

2、Mybaits的优点

1、基于SQL语句编程,相当灵活,不会对应用程序或者数据库的现有设计造成任何影响

2、SQL写在XML里,解除sql与程序代码的耦合,便于统一管理;

3、提供XML标签,支持编写动态SQL语句,并可重用。

4、MyBatis 使用JDBC来连接数据库,所以只要JDBC支持的数据库MyBatis都支持,减少了JDBC大量冗余的代码;

5、能够与Spring很好的集成;

3、MyBatis的缺点

1、SQL语句的编写工作量较大,尤其当字段多、关联表多时,对开发人员编写SQL语句的功底有一定要求。

2、SQL语句依赖于数据库,导致数据库移植性差,不能随意更换数据库。

4、#{} 和${}的区别是什么1

#{}是预编译处理,${}是字符串替换。Mybatis在处理#{}时,会将sql中的#{}替换为?号,处理$时,就是把${}替换成变量的值。使用#{}可以有效的防止SQL注入,提高系统安全性。

5、resultType和resultMap的区别

两个都是用来接收sql的返回值的,区别在于:

ResultType相对与ResultMap更简单一点。只要数据库表中的字段名和实体类中的属性完全一致,就能使用,否则会出现数据不显示的情况。

ResultMap更适合处理一对多、多对多的映射关系。

type属性是JavaBean对象的包名加类名,用来封装信息。

id标签是用来描述表中的主键的对应关系

result标签是用来描述表中的普通字段的对应关系

association标签用来实现一对一的关系

collection标签用来实现一对多的关系

6、Mybatis如何处理>,<等字符

有两种解决⽅法:⼀是使⽤<![CDATA[ ]]>标签来包裹sql语句。二是使⽤XML转义序列来表⽰这些字符。特殊字符转义序列

<

<

>

>

&

&

"

"

'

'

7、模糊查询like语句该怎么写

(1)使用#{...}

注意:因为#{...}解析成sql语句时候,会在变量外侧自动加单引号' ',所以这里 % 需要使用双引号" ",不能使用单引号 ' ',不然会查不到任何结果。

(2)使用CONCAT()函数连接参数形式

8、Dao接口工作原理,Dao中的方法是否可以重载

Dao接口,也即Mapper接口,接口的路径名,就是映射文件中<mapper> 根标签的namespace的值,接口的方法名,就是mapper文件中sql标签的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口路径名+方法名拼接字符串作为key值,可唯一定位一个sql标签

Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理对象,代理对象会拦截接口方法,转而执行sql标签中的sql语句,然后将sql执行结果返回。

Dao接口里的方法,是不能重载的,因为是路径名+方法名的保存和寻找策略。

9、Mybatis是如何将sql执行结果封装为目标对象并返回的都有哪些映射形式

第一种是使用标签,逐一定义列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列别名书写为对象属性名,Mybatis会忽略列名大小写,智能找到与之对应对象属性名。有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

10、MyBatis分页和自己写的分页哪个效率高

MyBatis的分页插件是通过拦截查询SQL,在这个SQL基础上自动为其添加limit分页条件。它会提高开发效率,但是无法对分页语句做出有针对性的优化,而自己写的分页SQL是可以针对性的开发,因此自己写的分页效率会更好。

11、如何获取自动生成的(主)键值

insert方法总是返回一个int的插入的行数值。如果采用自增长策略,自动生成的键值在insert方法执行完后可以被设置到传入的参数对象中。在insert标签中有一个usegeneratedkeys属性,将其设置为true,键值就会赋值到对象的对应属性中。

12、在mapper中如何传递多个参数

如果传递的是自定义的类属性值,可以将sql语句的参数类型定义为类的全限名,然后将参数封装到类中,而后将类的实例传给sql语句。

如果传递的是基本类型或字符串之类的属性,可以将其封装到集合中,然后将sql语句的参数类型定义为集合,而后将集合传给sql语句。

13、MyBatis输入输出支持的类型有哪些

MyBatis支持多种输入输出类型,包括基本数据类型、字符串、集合类和自定义的JavaBean。

其中,基本数据类型和字符串的数值直接映射到参数上。对于Map或JavaBean则将其属性值按照名称映射到参数上。

14有那些动态 sql标签

Mybatis提供了9种动态sql标签 :trim | where | set | foreach | if | choose| when | otherwise | bind。

15、MyBatis实现一对一有几种方式?具体怎么操作的?

有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过resultMap里面的association节点配置一对一的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,再去另一个表里面查询数据,也是通过association配置,但另外一个表的查询通过select属性配置。

16、MyBatis实现一对多有几种方式,怎么操作的?

有联合查询和嵌套查询。联合查询是几个表联合查询,只查询一次,通过resultMap 里面的collection节点配置一对多的类就可以完成;嵌套查询是先查一个表,根据这个表里面的结果的外键id,再去另一个表里面查询数据,也是通过collection配置,但另外一个表的查询通过select属性配置。

17、Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false。

它的原理就是先创建目标对象的代理对象,当调用目标方法属性时,进入拦截器方法invoke(),若目标方法属性是null 值,那么就会单独发送事先保存好的查询关联对象的sql,然后把结果放入目标方法属性内,于是目标方法属性就有值了,接着完成目标方法属性的调用。这就是延迟加载的基本原理。

18、Mybatis的一级、二级缓存

一级缓存:基于HashMap本地缓存,其生命周期为SqlSession,默认打开

二级缓存与一级缓存其机制类似,默认也是采用HashMap存储,不同在于其生命周期为SqlSessionFactory,在MyBatis 的全局配置settings 中有一个参数cacheEnabled,这个参数是二级缓存的全局开关,默认值是true ,初始状态为启用状态。

19、什么是MyBatis的接口绑定?有哪些实现方式?

接口绑定,就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定,我们直接调用接口方法就可以。

接口绑定有两种实现方式,一种是通过注解绑定,就是在接口方法上面加上@Select、@Update等注解,里面包含Sql语句来绑定;另外一种就是通过xml里面写SQL来绑定,在这种情况下,要指定xml 映射文件里面的namespace必须为接口的全路径名,一般用xml绑定。

20、使用MyBatis的mapper接口调用时有哪些要求

1、Mapper接口方法名和mapper.xml中定义的每个sql的id相同;

2、Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;

3、Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同;

4、Mapper.xml文件中的namespace即是mapper接口的类路径。

21、MyBatis批量操作

可以使用foreach循环迭代List集合的形式批量操作;迭代的是要插入或查询的数据,而不是sql语句。


SpringBoot

1、什么是Spring Boot

从本质上来说,Spring Boot就是Spring。因为现有的Spring比较复杂。如果要启动一个新的Spring项目,我们必须添加Maven依赖关系,配置应用程序服务器,添加Spring配置,非常繁琐。

Spring Boot就是为了解决这个问题。Spring Boot建立在现有Spring框架之上。使用Spring启动,避免了之前需要做的很多配置。因此,Spring Boot可以最少的工作量, 更加健壮地使用现有的Spring功能。

2、Spring Boot的优点

一、独立运行

Spring Boot内嵌了各种Servlet容器,Tomcat等,只需打成一个可执行的jar包就能独立运行,所有的依赖包都在一个jar包内。

二、简化配置

spring-boot-starter-web启动器自动依赖其他组件,简少了maven的配置。

三、自动配置

Spring Boot能根据当前类路径下的类、jar包来自动配置bean。

四、无代码生成和XML配置

Spring Boot配置过程中无代码生成,也无需XML配置文件就能完成所有配置工作。

3、Spring Boot的注解有那些1

一个是SpringBoot的核心注解:@SpringBootApplication,主要包含以下3个注解:@SpringBootConfiguration:组合了@Configuration注解,实现配置文件的功能;@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,SpringBoot默认的包扫描路径是启动类所在的package及其子包;@ComponentScan:Spring组件扫描。

二是异步注解:在启动类中加上@EnableAsync ,在异步类方法上使用@Async。Spring 底层会用Aop去检测到有@Async 注解的方法,然后创建一个代理方法进行异步操作, 如果方法中有错误, 程序不再执行但不会抛出异常,所以最好是加上try...catch代码块, 在catch中将异常输出。@Async 需配合配置异步线程池executor使用。

4、运行Spring Boot有哪几种方式

1)打包用命令或者放到容器中运行

2)用Maven插件运行

3)直接执行main方法运行

5、如何理解Spring Boot中的Starters

Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,可以一站式集成Spring及其他技术,可以通过Spring Boot Starter导入包。

Starters命名:

Spring Boot官方的启动器都是以spring-boot-starter-命名的,代表了一个特定的应用类型。第三方的启动器一般以第三方的名称来开头,像mybatis的mybatis-spring-boot-starter。

Starters分类:

一是SpringBoot应用类启动器、二是SpringBoot生产启动器、三是其他第三方启动器

6SpringBoot常用的starter有哪些

spring-boot-starter-web嵌入tomcat和web开发需要servlet与jsp支持

spring-boot-starter-test Spring对测试的支持启动器

spring-boot-starter-data-jpa数据库支持

spring-boot-starter-data-redis redis数据库支持

mybatis-spring-boot-starter第三方的mybatis集成starter

7、Spring、Spring MVC和Spring Boot区别

Spring

Spring最重要的特征是依赖注入。所有Spring Modules不是依赖注入就是IOC控制反转。当我们恰当的使用DI或者是IOC的时候,可以开发松耦合应用。

Spring MVC

Spring MVC提供了一种分离式的方法来开发Web应用。通过运用像DispatcherServelet,MoudlAndView 和 ViewResolver等一些核心组件,开发 Web 应用会变的非常简单。

SpringBoot

Spring和Spring MVC的问题在于需要配置大量的参数。

SpringBoot通过一个自动配置和启动项来解决这个问题。

8、为什么需要spring-boot-maven-plugin

spring-boot-maven-plugin提供了一些像jar一样打包或运行应用程序的命令:

run 运行SpringBoot应用程序;

repackage 重新打包jar包或war包使其可执行

start和stop管理Spring Boot应用程序的生命周期

build-info生成执行器可以使用的构造信息

9、什么是YAML

YAML是一种人类可读的数据序列化语言。通常用于配置文件。

与属性文件相比,如果我们要在配置文件中添加复杂的属性,YAML文件就更加结构化,具有分层配置数据的优点。

10、SpringBoot自动配置的原理

Spring Boot通过@EnableAutoConfiguration注解开启自动配置,加载spring.factories文件中注册的各种AutoConfiguration类,当某个AutoConfiguration类满足生效条件时,实例化该AutoConfiguration类中定义的Bean,并注入Spring容器,就可以完成依赖框架的自动配置。

11、RequestMapping和GetMapping的区别

RequestMapping具有类属性的,可以进行GET、POST、PUT或者其它的注释中具有的请求方法。

GetMapping是GET 请求方法中的一个特例。它是ResquestMapping的一个延伸,只接受get请求。

12、spring-boot-starter-parent有什么作用

新建SpringBoot项目时,都必须导入spring-boot-starter-parent,它的作用有:

  1. 定义了Java编译版本为1.8
  2. 使用UTF-8格式编码
  3. 定义了依赖的版本,让我们在写依赖时不需要写版本号
  4. 执行打包操作的配置
  5. 自动化的资源过滤和插件配置
  6. 针对各配置文件的资源过滤。

13、Spring Boot 2.X有哪些新特性?与1.X有什么区别?

配置变更、JDK版本升级、第三方类库升级、响应式Spring编程支持、HTTP/2支持、配置属性绑定、更多改进与加强

14、什么是Swagger

Swagger广泛用于可视化API,Swagger是用于生成Restful Web服务的可视化表示的工具、规范和完整框架实现。它使文档能够以与服务器相同的速度更新。当通过Swagger正确定义时,消费者可以最少量的实现逻辑来理解远程服务并与其进行交互。因此,Swagger消除了调用服务时的猜测。

15、开启Spring Boot特性有哪几种方式

继承spring-boot-starter-parent项目

导入spring-boot-dependencies项目依赖

16、Spring Boot支持哪些日志框架

Spring Boot支持Java Util Logging、 Log4j2, Lockback作为日志框架,如果使用Starters启动器,Spring Boot默认将使用Lockback日志框架。

17、保护SpringBoot应用有哪些方法

1、在生产中使用HTTPS

2、使用Synk检查依赖关系

3、升级到最新版本

4、启用CSRF保护

5、使用内容安全策略防止XSS攻击

18、SpringBoot读取配置的注解有那些1

@PropertySource、

@Environment、

@ConfigurationProperties、

@Value:

@Value的失效场景:

1.不能作用于静态变量(static)

2.不能作用于常量(final)

3.不能在非注册的类中使用,如未被@Service等注解修饰

4.使用这个类时,只能通过依赖注入的方式,用new的方式是不会自动注入这些配置的。

19、前后端分离,如何维护接口文档

在Spring Boot中,可以使用Swagger,它可以快速生成一个接口文档网站,接口一旦发生变化,文档自动更新,所有人访问这个在线网站就可以获取到最新的接口文档,非常方便。2.9版网址:localhost:8080/swagger-ui.html;3.0版网址:localhost:8080/swagger-ui/index.html。

20、什么是Spring Data

Spring Data是Spring的一个子项目。支持关系型和非关系型数据库。主要目标是使数据库访问变得方便快捷。其次是致力于减少数据访问层 (DAO) 的开发量. 我们只需声明持久层的接口,Spring Data JPA会根据符合规范的名字来确定方法需要实现什么样的逻辑。

21、SpringBoot 打成jar和普通的jar区别

Spring Boot项目最终打成的jar是可执行jar ,这种jar可以直接通过java -jar xxx.jar命令来运行,这种jar不能作为普通的jar被其他项目依赖。

原因在于它和普通jar的结构不同。普通的jar包,解压后直接就是包名,包里就是我们的代码,而Spring Boot打成的可执行jar解压后,在\BOOT-INF\classes目录下才是我们的代码,因此无法被直接引用。如果非要引用,可以在pom.xml文件中增加配置,将Spring Boot项目打包成两个jar ,一个可执行,一个可引用。

22、如何重新加载Spring Boot上的更改,而无需重新启动服务器

可以使用DEV工具来实现。(其实就是热部署之一)

23、如何自定义端口

可以在application中指定端口:server port=自定义端口号

24、如何使用Maven设置Spring Boot应用程序

最好的方法是继承spring-boot-starter-parent 项目并声明依赖于Spring Boot启动器。这样做可以让项目重用Spring Boot的默认设置。

25、SpringBoot的事务是在哪层处理的

在服务层service的接口中加@Transactional注解,dao层不需要写。因为业务逻辑处理在service层,它可能会调用多个dao层接口。

26、SpringBoot跨平台调API

使用Feign进行消费

1、在maven中添加依赖

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-openfeign</artifactId>

    <version>2.2.2</version>

</dependency>

2、启动类上加上@EnableFeignClients(basePackages = {"com.aaa.aurora"})

3、编写service接口

实现类上加入:@FeignClient(url = "${pangu.url}",name = "panguUrl")

其中:pangu.url是配置在application.properties中的ip及端口

  1. 代码中调用

27、SpringBoot中的yml文件都干些什么

配置数据源、端口、MyBatis的xml文件,微服务的话还会配置eureka,ip和端口号以及网关的ip和端口号等自动化配置。

28、Spring Boot的启动流程

在main方法中,通过SpringApplication的静态方法,即run方法进行SpringApplication类的实例化操作,然后再针对实例化对象调用另外一个run方法来完成整个项目的初始化和启动。SpringApplication在run方法中的核心操作:

获取监听器和参数配置;打印Banner信息;创建并初始化容器;监听器发送通知。除了这些,run方法运行过程中还涉及启动时长统计、异常报告、启动日志、异常处理等辅助操作。

29、如何在SpringBoot中实现一个定时任务

Spring给我们提供了可以执行定时任务的线程池ThreadPoolTaskScheduler,该线程池提供了多个可以执行定时任务的方法schedule()、scheduleAtFixedRate()和scheduleWithFixedDelay(),只需要在配置类中启用线程池注解,就可以直接使用这个线程池了。

  1. Tomcat的安装部署
  • Windows下:
    1. 官网下载压缩包,解压后放入安装目录下;
    2. 配置Tomcat环境变量;
    3. 在Tomcat安装路径下的bin目录里找到startup.bat,双击运行,保持窗口打开状态;
    4. 打开浏览器,在地址栏输入http://localhost:8080/,能看到Tomcat首页即启动成功。
  • Linux下:
    1. 下载tomcat安装包,以.gz后缀;
    2. 上传tomcat安装包并解压,解压命令:tar -xzvf xxxgz;
    3. 启动tomcat:在tomcat的bin目录下输入sh startup.sh命令启动tomcat;
    4. 打开浏览器,在地址栏输入虚拟机IP:8080/,能看到Tomcat首页即启动成功。
    5. 上传并运行项目:将war包上传到tomcat的webapps包下,war包不用解压,启动tomcat后会自动为我们解压成文件夹。

Spring Cloud

  1. 什么是Spring Cloud

Spring Cloud是微服务系统架构的一站式解决方案,它为我们提供了一套简易的编程模型,使我们能在Spring Boot的基础上轻松地构建微服务项目。

2、Spring Cloud五大核心组件

Spring Cloud Eureka:服务注册与发现

Spring Cloud Zuul:服务网关

Spring Cloud Ribbon:负载均衡

Spring Cloud Hystrix:断路器

Spring Cloud Config:配置中心

3、Spring Cloud的优点

1、耦合度比较低,可以并行开发;每个人只需要关注自己的业务,不会影响其它模块。

2、配置比较简单,基本用注解就能实现,没有过多的配置文件。

3、因为微服务是跨平台的,所以每个微服务可以有自己的独立的数据库也可以用公共的数据库。

4、什么是Eureka

Eureka是Spring Cloud的服务注册中心,系统中的其他服务使用Eureka客户端将其连接到Eureka Service中,并且保持心跳,这样可以通过Eureka Service来监控各个微服务是否运行正常。

5Eureka的作用

当我们开始一个项目时,通常会在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,某些位置会发生变化。手动更改属性可能会产生问题。

Eureka服务注册与发现可以在这种情况下提供帮助。由于所有服务都在Eureka服务器上注册并通过调用Eureka服务器完成查找,因此无需对服务地点做任何处理和更改。

6、什么是Eureka的自我保护模式

默认情况下,如果Eureka server在一定时间内没有收到某个微服务实例的心跳,那么服务器就会注销该实例,默认为90s。但这种行为是有可能出现误判的。此时就需要Eureka的自我保护模式来解决这个问题。

当Eureka server节点在短时间内丢失过多的客户端时,这个节点就会进入自我保护模式,Eureka server会保护服务注册表中的数据,不再删除这些信息,当网络故障恢复,server会自动退出自我保护模式。

7、Eureka和Zoo Keeper的区别

1. Zoo Keeper中的节点服务挂了就要选举,在选举期间注册服务瘫痪,选举就是该微服务做了集群,必须有一台主其他的都是从

2. Eureka各个节点是平等关系,服务器挂了没关系,只要有一台Eureka就可以保证服务可用,数据都是最新的。如果查询到的数据不是最新的,就是因为Eureka的自我保护模式导致的。

3. Eureka本质上是一个工程,而Zoo Keeper只是一个进程。

4. Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,不会像Zoo Keeper一样整个注册系统瘫痪。

5. Zoo Keeper保证的是CP,Eureka保证的是AP(自我保护模式下保证可用性)

CAP:C:一致性;A:可用性;P:分区容错性

8、什么是CAP原则

CAP原则:

C(一致性):是指更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。

A(可用性):是指服务一直可用,而且是正常响应时间。

P(分区容错性):是指分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。

而在一个分布式系统中,最多只能同时保证三个特性中的两个,三者不可兼得。

9、什么是网关,网关的作用

网关相当于一个网络服务架构的入口,所有网络请求必须通过网关转发到具体的业务。

网关的作用主要就是统一管理微服务请求,权限控制、负载均衡、路由转发、监控、安全控制黑名单和白名单等。

10、Zuul与Nginx有什么区别

Zuul是java语言实现的,主要为java服务提供网关服务,尤其在微服务架构中可以更加灵活的对网关进行操作。

Nginx是C语言实现,性能高于Zuul,但是实现自定义操作需要熟悉lua语言,对程序员要求较高,可以使用Nginx做Zuul集群。

11、什么是Hystrix

Hystrix是一种断路器,主要作用是在微服务治理中保护服务,当出现不可避免的故障时,停止级联故障。主要功能:

1、服务降级:当接口调用失败,自动执行一个空的方法。避免线程阻塞

2、服务熔断:当接口调用失败,返回预先准备好的响应结果,告知服务不可用。

3、服务隔离:隔离服务间的相互影响

4、服务监控:将服务调用的每秒请求数和每秒成功请求数记录下来。

12、服务雪崩效应

雪崩效应是在大型项目中,当某个服务发生宕机时,调用这个服务的其他服务也会发生宕机,这样就会将服务的不可用逐步扩大到其他各个服务中,从而使整个项目的服务宕机崩溃。

原因在于Tomcat默认情况下只有一个线程池来维护客户端发送的所有请求,这时候某一接口在某一时刻被大量访问就会占据tomcat线程池中的所有线程,其他请求处于等待状态,无法连接到服务接口。

13、在微服务中,如何保护服务

一般使用Hystrix,一是实现服务隔离来避免出现服务雪崩,从而达到保护服务的效果。

二是使用服务降级既能有效地为不同的服务分配资源,同时一旦服务不可用则返回友好提示,不占用其他服务资源,从而避免服务雪崩。

14、服务熔断、服务降级的区别

服务熔断和服务降级,在技术实现上没有本质区别。他们的区别在业务逻辑上:

  1. 服务熔断:原服务down掉(掉线、未启动),此时发生熔断,给前端返回的是预先准备好的响应结果,告知服务不可用。一般发生在服务提供者(Provider)侧。
  2. 服务降级:服务提供者不能提供原有高质量服务了(比如服务器过忙,请求人数过多),由其他微服务提供次一等级的服务。一般发生在服务消费者(Consumer)侧。

15、负载平衡的意义什么

负载平衡可以改善跨计算机,计算机集群,网络链接或磁盘驱动器等多种计算资源的工作负载分布。

负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。

16、对分布式事务的了解

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。

17、Spring Boot和Spring Cloud的区别

Spring Boot专注于快速方便的开发单体微服务,可以离开Spring Cloud独立开发项目。

但Spring Cloud离不开Spring Boot,属于依赖的关系。Spring Cloud关注全局的服务治理框架。它将Spring Boot开发的单体微服务整合并管理起来,为各个微服务之间提供配置管理、服务发现等集成服务。

18、怎么定义依赖jar包的版本号

使用properties标签,定义java版本:java.version为1.8,spring-cloud.version为cloud版本号。

19、dependencyManagement与dependencies区别

dependencyManagement可以理解为多继承,在parent标签中继承boot项目后,可以继承cloud项目,同时声明依赖版本号,这样所有的cloud依赖jar包都不需要再显示声明版本。Maven会使用顶层父项目中的dependencyManagement元素中指定的版本号。dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显式的声明需要用的依赖。

顶层父项目中的dependencies中声明的依赖jar包会默认被子项目继承。

Redis

1、什么是Redis

Redis是一个完全开源免费的,高性能的key-value非关系型数据库;常用来做缓存。

2、使用Redis有哪些好处

  1. 速度快,因为数据存放于内存中,类似于Hash Map,查找和操作的时间复杂度都是 O(1)(无穷小)
  2. 支持事务,操作都是原子性,所谓的原子性就是对数据的更新要么全部执行,要么全部不执行
  3. 丰富的数据类型
  4. 丰富的特性:可用于缓存,消息;按key设置过期时间,过期后将自动删除

3、Redis和关系型数据库的区别

Redis是一种基于键值对的非关系性数据库,而键值对的值是由多种数据结构和算法组成的。Redis的数据都存储于内存中,因此它的速度远超关系型数据库。

关系型数据库是基于二维数据表来存储数据的,它的数据格式更为严谨,并支持关系查询。关系型数据库的数据存储于磁盘上,可以存放海量的数据,但性能远不如Redis。

4、Redis的数据类型

Redis主要有5种数据类型

  1. String:用于存储字符串、整数或浮点数;主要用于简单的键值对缓存
  2. Hash:包含键值对的无序散列表;主要应用于结构化的数据,比如一个对象
  3. List:列表;用于存储线性有序的数据结构
  4. Set:无序、不可重复的集合;除了支持对元素的增删改查之外,还支持对多 个集合取交集、并集、差集
  5. Zset:有序、不可重复的集合;主要应用于去重,也可以排序

5、List的常用命令

List列表用于存储线性有序的数据结构,它内部的元素是可以重复的,并且一个列表最多能存储2^32-1个元素,常用命令:

  1. lpush/rpush:从列表的左侧/右侧添加数据;
  2. lrange:指定索引范围,并返回这个范围内的数据;
  3. lindex:返回指定索引处的数据;
  4. lpop/rpop:从列表的左侧/右侧弹出一个数据;
  5. blpop/brpop:从列表的左侧/右侧弹出一个数据,若列表为空则进入阻塞状态。

6、hash底层的数据结构

哈希对象有两种编码方案,当哈希对象保存的键值对数量小于512个,且保存的所有键值对中的键和值,其字符串长度都小于64字节时,哈希对象采用ziplist编码,否则采用hashtable编码:

ziplist编码采用压缩列表作为底层实现,而hashtable编码采用散列表作为底层实现。

压缩列表:是Redis为了节约内存而设计的一种线性数据结构,它是由一系列具有特殊编码的连续内存块构成的。

7、zset底层的数据结构

Zest有序集合对象有2种编码方案,当有序集合保存的元素数量不超过128个,且保存的所有元素的成员长度都小于64字节时,zset采用ziplist编码,否则采用skiplist编码。

ziplist编码采用压缩列表作为底层实现,skiplist编码采用zset结构作为底层实现。

zset是一个复合结构,它的内部采用散列表和跳跃表来实现,跳跃表是在链表的基础上,通过增加索引来提高查找效率。压缩列表是Redis为了节约内存而设计的一种线性数据结构,它是由一系列具有特殊编码的连续内存块构成。

8、为Redis速度这么快

因为Redis是单进程单线程的,避免了线程切换和竞争所产生的消耗,数据存放于内存上,加上采用了IO多路复用机制,使其在网络IO操作中能并发处理大量的客户端请求,这是它能这么快的原因。

9、字符串类型能存储最大容量是多少

512M。

10、Redis持久化机制

Redis提供两种持久化机制RDB和AOF。通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。当Redis重启后通过把硬盘文件重新加载到内存,就能达到恢复数据的目的。

  1. RDB:是Redis默认的持久化方式。按照一定的时间周期策略把内存中的数据以快照的形式保存到硬盘的二进制文件。即快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。(快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
  2. AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。

当两种方式同时开启时,Redis会优先选择AOF恢复。

11、Redis持久化机制的优缺点1

  1. RDB机制
    1. 优点:
      • 只有一个数据文件dump.rdb,方便持久化。
      • 容灾性好,一个文件可以保存到安全的磁盘。
      • 性能最大化,使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了 redis的高性能。
      • 数据集过大时,比AOF的启动效率更高。
    2. 缺点:数据安全性低。RDB是按时间周期策略进行持久化,如果持久化之间redis发生故障,会发生数据丢失。所以RDB更适合数据要求不严谨的时候
  2. AOF机制
    1. 优点:
      • 数据安全性高,每进行一次命令操作就记录到AOF文件中一次。
      • 通过append模式写文件,即使中途服务器宕机,可以通过redis-check-aof工具解决数据一致性问题。
      • AOF文件没对命令进行合重写前,可以删除其中的某些命令
    2. 缺点:
      • AOF文件比RDB文件大,且恢复速度慢。
      • 数据集过大时,比RDB启动效率低。

12、Redis常见性能问题和解决方案

  1. 主节点最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  2. 如果数据比较重要,某个从节点开启AOF备份数据,策略要设置为每秒同步一次
  3. 为了主从复制的速度和连接的稳定性,主节点和从节点最好在同一个局域网内
  4. 尽量避免在压力很大的主库上增加从库
  5. 主从复制不要用图状结构,用单向链表结构更为稳定,即:主节点<- 从节点1<- 从节点2<-从节点3

13、Redis过期键的删除策略1

  1. 定时删除:在设置键的过期时间的同时,创建一个定时器timer。让定时器在键过期时,立即执行对键的删除操作。
  2. 定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。
  3. 惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,若过期,就删除该键;若没有过期,就返回该键。

14、Redis的回收淘汰策略

  1. volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集中挑选任意数据淘汰
  4. allkeys-lru:从数据集中挑选最近最少使用的数据淘汰
  5. allkeys-random:从数据集中挑选任意数据淘汰
  6. no-enviction:永不回收的策略

策略使用规则:

  1. 如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru
  2. 如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

15、Redis的同步机制了解么

Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将rdb文件全量同步到复制节点,复制节点接受完成后将rdb镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。

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

采用先更新数据库、再删除缓存的同步策略。

17、Redis如何设置密码及验证密码

设置密码:config set requirepass 123456

授权密码:auth 123456

18、怎么理解Redis事务

事务是一个原子性的单独的隔离操作;事务中的所有命令都会序列化,按顺序执行且要么全部执行,要么全部不执行。事务在执行的过程中,不会被其他客户端发送的请求所打断。

19、Redis事务相关的命令有哪几个

1、multi:用于开启一个事务。multi执行之后,会将命令放到队列中,等待exec命令调用执行。

2、exec命令:负责触发并执行事务中的所有命令:

3、discard命令:放弃事务,清空事务队列,客户端从事务状态中退出。

4、watch命令:是一个乐观锁的机制,监控键是否被改动过。但凡有一个键在exec执行前被修改了,那么整个事务都会被取消,exec返回空值表示事务已经失败。

5、unwatch命令:取消watch命令对所有key的监视。

20、Redis key的过期时间和永久有效分别怎么设置

1、设置过期时间:expire key名 时间

2、设置永久有效:使用persist命令清除超时即可

21、如何设计Redis的过期时间

热点数据不设置过期时间,使其达到“物理”上的永不过期,可以避免缓存击穿问题;

在设置过期时间时,可以附加一个随机数,避免大量的key同时过期,导致缓存雪崩。

22、Redis中如何实现分布式锁

采用Redis实现分布式锁,就是在Redis里存一份代表锁的数据,通常用字符串即可,命令是:

set key value nx expire 过期时间 

将加锁、过期命令排到一起,它们就成原子操作了,可以避免死锁。解锁就是删除代表锁的那份数据:

del key

23、Redis如何做内存优化

尽可能的将数据模型抽象到一个hash散列表里面,这是因为散列表占用内存非常小。比如web系统中有一个用户对象,不用为这个用户的姓名,性别等设置单独的key,而是把这个用户的所有信息存储到一张散列表里面。

24、Redis的内存用完了会发生什么

如果达到设置的上限,Redis的写命令会返回错误信息,但读命令还可以正常返回。如果配置淘汰机制,当达到内存上限时会冲刷掉旧的内容。

25、如何实现Redis的高可用

实现Redis的高可用,主要有哨兵和集群两种方式。

以集群为例:Redis集群采用虚拟槽分区来实现数据分片,它把所有的键根据哈希函数映射到0-16383个整数槽内,每一个节点负责维护一部分槽以及槽所映射的键值数据。

26、缓存雪崩

由于原有缓存失效,新缓存未到期间(比如们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),所有原本应该访问缓存的请求都去查询数据库了,致使对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。导致整个系统崩溃。

解决办法:

一是设置过期时间时,附加一个随机数,避免大量的key同时过期。

二是启用降级和熔断措施:在发生雪崩时,若应用访问的不是核心数据,则直接返回预定义信息。或者在发生雪崩时,对于访问缓存接口的请求,直接返回。

27、缓存穿透

缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有,然后返回空。这样就导致用户查询时相当于进行了两次无用的查询。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

解决办法:缓存空对象:数据库未找到时,仍然将空值存入缓存中,客户端再次访问时,缓存中会直接返回空值。

28、缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求时,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决思路:

1、直接写个缓存刷新页面,上线时手工操作下;

2、数据量不大,可以在项目启动的时候自行加载;

3、定时刷新缓存

29、缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据预案进行自动降级,也可以配置开关实现人工降级。不过有些服务是无法降级的(如加入购物车、结算)。

参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  3. 错误:比如服务成功率低于90%,或者数据库连接池被打爆了,或者访问量突增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

30、Redis工具开发建议

1、冷热数据分离,不要将所有数据全部都放到Redis中

2、不同的业务数据要分开存储

3、存储的Key一定要设置超时时间

4、对于必须要存储的大文本数据一定要压缩后存储

5、禁止1MB以上的String字符串。

31、Redis适合存储什么数据1

1、字典表、配置类的数据:这类数据更新频率极低

2、热点数据:失效时间要定的短些。

MySql

  1. Sql优化2
  1. sql语句尽量避免使用or来连接条件查询数据,or可能会导致全表扫描,效率降低。可以将条件分为多个select语句,用union或union all连接语句。union和union all的区别在于前者会对结果排序和去重,后者不会但效率会高。
  2. 在连续数值的查询中,尽量使用between,而不使用in。in和not in可能会导致全表查询。
  3. sql语句的where查询条件,对字段进行表达式或函数操作,会导致全表扫描。
  4. 多表查询,使用inner join,left/right join来代替子查询。因为子查询需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。
  5. 使用like语句进行模糊查询时,尽量把%放后面,或者不使用%。
  6. 尽量把字段设置为not null,这样在将来执行查询的时候,数据库不用去比较null值。

2、数据库结构优化-垂直分库分表1

  1. 垂直分库:就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。按业务逻辑进行独立划分,每个业务使用单独的一个数据库。
  2. 垂直分表:是基于数据库中的"列"进行,比如某个表字段较多,可以将不经常用或长度比较大的字段拆分出去。在字段很多的情况下,通过"大表拆小表",更便于开发与维护。

3、数据库结构优化-水平分库分表1

当一个应用再难以细粒度的垂直切分,或切分后数据量行数很大,存在单库读写、存储性能瓶颈时可以进行水平切分。常用的"冷热数据分离模式",就是将使用较少的历史数据迁移到其他库中,业务功能上只提供热点数据的查询。

4、常用函数1

模糊查询

like

剔重

distinct

伪列

rownum

排序,默认升序

order by

分组

group by

升序

asc

降序

desc

常用的聚合函数

计数

count

平均值

avg

sum

最大值

max

最小值

min

5、where和having的区别

where是一个约束声明,where是在结果返回之前起作用的,where中不能使用聚合函数。

having是一个过滤声明,having是在结果返回之后起作用的,having中只能使用聚合函数和分组字段,但建议分组字段在where中使用。

6、数据库范式

  1. 第一范式: 属性不可再分
  2. 第二范式:在第一范式的基础上,要求表中的每一行必须有一个惟一属性可以区分,这个惟一属性列被称为主键。
  3. 第三范式:在第二范式的基础上, 要求一个数据库表中不包含已在其它表中已包含的非主关键字信息。
  4. 所以第三范式具有如下特征:
    1. 每一列只有一个值
    2. 每一行都能区分
    3. 每一个表都不包含其它表已经包含的非主关键字信息

7、数据库的左右内外连接

    1. 内连接:当进行内连接时,系统会自动忽略两个表中对应不起来的数据,结果中将只包含两个表中关联起来的数据,关键字:inner join using/on。
    2. 左外连接:左表全部行+右表匹配的行,左表有的右表没有,右表以空来补充,关键字:left join on。
    3. 右外连接:右表全部行+左表匹配的行,右表有的左表没有,左表以空来补充,关键字:right join on。
    4. 全外连接:显示所有数据,对应不起来的以空来补充,关键字:full outer join,MySQL不支持。

8、关联关系

常见的关联关系有:一对一、一对多和多对多的关联。

一对一关联就是自己与自己相关联。

一对多关联就是两张表具有主从关系,并且以主表的主键关联从表的外键来实现这种关联关系。

多对多关联就是两张表具有多对多的关系,它们之间需要有一张中间表来作为衔接,以实现这种关联关系。这个中间表要设计两列,分别存储那两张表的主键。

9、Mysql分页查询

分页查询是通过limit关键字查询的,它有两个参数,第一个参数是从哪一行开始,第二个参数是返回多少行。

SELECT prod_name FROM products LIMIT 5,5;
若数据量过多,可以结合当前页最后一条数据的索引进行查询:

SELECT * FROM T WHERE id > #{ID} LIMIT #{LIMIT}

这种情况下索引必须是有序的。

10、MySql查重

用group by 分组,统计count大于1的即可。

select 列1, count(列1) as count from 表名group by 列1 having count > 1;

11、SQL行转列

首先,假设我们有一张分数表(tb_score),表中的数据如下图:

然后,我们再来看一下转换之后需要得到的结果,如下图:

可以看出,这里行转列是将原来的subject字段的多行内容选出来,作为结果集中的不同列,并根据userid进行分组显示对应的score。通常,我们可以使用 CASE...WHEN...THEN 语句实现行转列,参考如下代码:

SELECT

userid,

SUM( CASE 'subject' WHEN '语文' THEN score ELSE 0 END ) AS '语文',

SUM( CASE 'subject' WHEN '数学' THEN score ELSE 0 END ) AS '数学',

SUM( CASE 'subject' WHEN '英语' THEN score ELSE 0 END ) AS '英语',

SUM( CASE 'subject' WHEN '政治' THEN score ELSE 0 END ) AS '政治'

FROM

tb_score

GROUP BY

userid;

12、SQL注入攻击

SQL注入的原理是将SQL语句作为用户输入填充到参数中,传递到服务器解析并执行的一种攻击手法,如果程序对用户输入数据的合法性没有判断和处理,就会导致执行一些预期之外的操作,如何避免:

  1. 过滤输入内容,校验字符串
  2. 对SQL进行预编译:在对参数进行编译并填充到相应的占位符的过程中,既做了转义操作,又防止SQL注入攻击。
  3. 避免使用动态SQL。

13、Mysql都有哪些索引

1、唯一索引:唯一索引要求索引列的值必须唯一,但允许有空值。

2、主键索引:主键索引是一种特殊的唯一索引,不允许有空值。

3、联合索引:联合索引是指多个字段联合创建的索引,只有在查询条件中使用了联合索引的左边字段时,索引才会被使用。

4、普通索引:普通索引是MySQL中的基本索引类型,允许重复值和空值。

14、创建索引的三种方式

1、在执行create table建表语句时可以创建索引

2、alter table用来创建index(普通)索引、unique(唯一)索引或primary key(主键)索引,语法:

ALTER TABLE 表名 ADD INDEX 索引名 (列名,多列时用逗号分隔)

  1. 索引名若不设置,MySQL将自动生成一个。

3、create index可对表增加index(普通)索引或unique(唯一)索引,语法:

CREATE UNIQUE INDEX 索引名称 ON 表名 (列名)

15、如何判断是否加索引1

索引设计原则:

  1. 避免对经常更新的表添加过多的索引,并且索引中的列要尽可能少。
  2. 经常用于查询的字段要创建索引。
  3. 数据量少的表最好不要使用索引。
  4. 在条件表达式中经常用到的不同值较多的列上建立索引,但诸如“性别”字段的就无须建立索引。
  5. 当唯一性是某种数据本身的特征时,指定唯一索引。
  6. 在频繁进行排序或分组的列上建立索引,如果待排序的列有多个,可以建立联合索引。

16、主键、外键和索引的区别

主键:是用来唯一标识一条记录的,不能重复,不可为空;

外键:是用来和其他表建立联系的,一个表的外键是另一表的主键, 外键可以重复, 可以为空;

索引:是提高查询速度的,不可重复,可以为空

一个表只能有一个主键,可以有多个外键唯一索引。

聚簇索引和非聚簇索引

聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据;

非聚簇索引:将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置;

聚簇索引是物理有序的;非聚簇索引是逻辑有序,物理无序,在mysql中数据存储顺序就是聚簇索引的顺序,所以一个表只有一个聚簇索引,其他索引都是非聚簇的。

Mysql查看查询是否用索引

在查询语句的前面加上explain关键字,结果中的possible_keys即为SQ查询时用到的索引,无则为NULL,

17、MySql和Redis的数据同步问题

读操作,先读Redis,没有再去MySql取,取出了再放Redis。

写操作,先写Redis,异步存MySql,然后再有个监控机制(alibaba开源的cacal ),监控到MySql插入了数据,把这条数据回写到Redis中。写Redis时,给key加上一个过期时间,在写操作过程中,如果MySql挂了,那么会丢失一条数据,这个没办法。如果Redis挂了,当Redis重启后,监控机制会继续把数据写进Redis,保证同步。

18、事务的特性1

事务特性ACID:

A、原子性:即不可分割性,事务要么全部被执行, 要么就全部不被执行。

C、一致性或可串性:事务的执行使得数据库从一种正确状态转换成另一种正确状态

I、隔离性:在事务正确提交之前,不允许把该事务对数据的任何改变提供给任何其他事务

D、持久性:事务正确提交后,其结果将永久保存在数据库中,即使在事务提交后有了其他故障,事务的处理结果也会得到保存。

19、SQL的事务隔离级别1

SQL 标准定义了四种隔离级别,分别是:

读未提交:存在脏读问题;

读已提交:存在不可重复读问题;

可重复读:解决了不可重复读和幻读的问题。

串行化:所有事务逐个依次执行。

InnoDB存储引擎默认采取的是可重复读。

20、MySQL事务如何回滚

在MySQL默认的配置下,事务都是自动提交和回滚的。当显示地开启一个事务时,可以使用ROLLBACK语句进行回滚。

21、SQL语言包括哪几部分?每部分都有哪些操作关键字?1

SQL语言包括数据定义(DDL)、数据操纵(DML),数据控制(DCL)和数据查询( DQL)四个部分。

数据定义:Create Table,Alter Table,Drop Table, Craete/Drop Index

数据操纵:Select,insert,update,delete,

数据控制:grant,revoke

数据查询:select

23、%和_的区别

%匹配0个或多个字符,_匹配一个字符。

24、数据库锁机制

MySQL使用了三种级别的锁机制:表级锁,行级锁和页级锁。

表级锁:优点在于开销小,加锁快;不会出现死锁;缺点就是锁定粒度大,容易发生锁冲突,而且并发度最低;

行级锁:锁定粒度最小,发生锁冲突的概率最低,并发度也最高;缺点就是开销大,加锁慢,会出现死锁;

页面锁:锁定粒度,获取锁所需要的资源开销,以及所能提供的并发处理能力都介于行级锁定与表级锁之间。

25、数据库死锁的解决办法

解决死锁问题最简单的一种方法是超时,当两个事务互相等待超过一定时间后,其中一个事务进行回滚,另一个等待的事务就能继续进行。

除了超时机制,当前数据库还都普遍采用等待图的方式来进行死锁检测。这是一种更为主动的死锁检测方式。MySql默认也采用的这种方式。

26、redo log、undo log、binlog的作用

binlog:二进制日志文件,binlog记录了所有修改数据库的操作。

redo log:重做日志,用来实现事务的持久性,即事务ACID中的D。

undo log:它是用来帮助事务回滚及MVCC的功能。

27、MVCC是什么

MVCC全称多版本并发控制协议。它最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB默认的隔离级别是可重复读,可重复读使用的就是MVCC。

28、MySQL主从同步是如何实现的

总体来说,主从同步的工作原理分3步:

先由主服务器(master)把数据更改记录到binlog中。

然后从服务器(slave)把主服务器的二进制日志复制到自己的中继日志(relay log)中。

最后从服务器重做中继日志中的日志,把更改应用到自己的数据库上,以达到数据的最终一致性。

Linux常用命令1

1、常用命令

1、ll 列出当前或指定路径下的文件和目录以及大小等信息。

2、mkdir 创建一个文件夹。

3、rmdir 删除一个文件夹。

4、cd 更换当前目录。

5、cp 复制文件或目录。

6、rm 删除文件或目录。

7、mv 移动文件或目录。

8、pwd 查看当前的路径。

9、find 在当前目录下查找某个文件或目录。

10、grep 管道,在前面的命令的结果基础之上,通过管道搜索指定的内容。

2、系统操作

1、clear 清屏。

2、man 查看帮助手册。如man clear。

3、exit 退出当前操作界面,回到上一级。

4、reboot 重启系统

5、shutdown 关机

6、ps 列出程序运行情况。

7、kill 删除某个进程。

3、网络设置

1、ifconfig 查看网络设备的ip地址、掩码等。

2、ping 测试网络是否通畅。

3、netstat 查看当前网络连接情况,包括有多少连接,跟谁连接等。

4、linux通过端口查看进程:netstat -nap | grep 端口号

4、运行一个SpringBoot程序并查看日志

1、以后台方式运行jar包并将日志写入到xxx.log中

nohup java -jar xxx.jar > xxx.log 2>&1 &。

2、查看日志:tail -f xxx.log,动态查看日志,日志文件有变化,会实时显示出来。

3、动态清空日志:cat /dev/null > xxx.log,会把“空”内容覆盖日志。

4、关闭一个程序:ps -ef |grep xxx.jar,查看进程

比如,我们会看到:

root 3780 3262 10 15:58 pts/000:00:22 java -jar ServiceOne-1.0-SNAPSHOT.jar

表示,名为ServiceOne-1.0-SNAPSHOT.jar的java包,在运行,然后第一组数字,是当前程序的PID(进程ID)。本例为:3780。

而后,我们可以通过命令强行结束它:kill -9 3780


网络

1、OSI 七层模型

应用层:网络服务与最终用户的一个接口。

表示层:数据的表示,还负责安全和压缩。

会话层:建立、管理、终止会话。

传输层:定义传输数据的协议端口号,以及数据传输和流控。

网络层:进行逻辑地址寻址,实现不同网络之间的路径选择。

链路层:建立逻辑连接、进行硬件地址寻址、差错校验等功能。

物理层:建立、维护、断开物理连接。

五层网络体系结构没有表示层和会话层。

2、TCPUDP的区别

1、TCP面向连接的;UDP是无连接的,即发送数据之前不需要建立连接。

2、TCP是点到点的连接;UDP支持一对一,一对多,多对一和多对多的交互通信。

3、TCP提供可靠的数据传输服务;UDP尽最大努力交付,但不保证可靠交付。

4、UDP具有较好的实时性,工作效率比TCP高,适用于对高速传输和实时性有较高要求的广播通信等。

5、TCP对系统资源要求比UDP要多。

3、GET和POST的区别

POST在浏览器回退时会再次提交请求,GET不会。

GET请求参数通过URL传递,且有长度限制,POST参数放在Request body中,没有长度限制。

GET请求参数会被完整保留在浏览器历史记录里,POST参数不会被保留。

GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

get请求方式用户端和服务端只产生一次交互,post请求方式用户端会和服务端产生两次交互,POST会先问下服务端是否正常,然后才发送数据。

4、错误

400 1、语义有误。 2、请求参数有误。

404 请求失败,请求所希望得到的资源未在服务器上发现。

405(方法禁用) 禁用请求中所指定的方法。

408 请求超时。

500 服务器无法完成对请求的处理。

5、IPV4和IPV6有什么区别

    IPv4和IPv6是是目前使用的两种Internet协议版本,它俩最大的区别是协议地址的区别:

1、地址长度:IPv4协议具有32位地址长度;IPv6协议具有128位地址长度。

2、地址的表示方法:IPv4地址是以小数表示的二进制数;IPv6地址是以十六进制表示的二进制数。

3、地址配置:IPv4协议的地址可以通过手动或DHCP配置的;IPv6协议需要使用无状态地址自动配置(SLAAC)。

6、HTTP和HTTPS的区别

HTTP:基于TCP的超文本传输协议,以明文的形式来传输数据,是无状态的;默认端口号80。

HTTPS:超文本传输安全协议,经由HTTP进行通信,但利用SSL/TLS来加密数据包。目的是为了提高网站服务器的安全性,保护交换数据的隐私与完整性;需要到CA申请证书,默认端口号443。

7、TCP/IP协议

TCP/IP全称传输控制协议/网际协议:是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议是由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇。

8、应用层报文怎么传输到另一个应用层

应用层数据(报文)向外发送时,数据是由最上面的应用层向下经过一层层封装后发送给物理层;而接收数据时,数据是由物理层向上经过一层层解封后发给应用层。

9、TCP的三次握手

第一次握手:建立连接时,客户端发送syn包到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号。

第二次握手:服务器收到syn包,必须确认客户的SYN,同时自己也发送一个SYN包,即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入连接成功状态,完成三次握手。

10、TCP的四次挥手

1、客户端向服务器发送FIN控制报文段;

2、服务端收到FIN,回复ACK。服务器进入关闭等待状态,发送FIN;

3、客户端收到FIN,给服务器回复ACK,客户端进入等待状态,以确保服务器收到ACK真正关闭连接;

4、服务端收到ACK,链接关闭。

数据结构与算法

1、打印二叉树每层的节点

创建一个deque队列实例,属性是二叉树类,用deque.offer()方法将二叉树实例入队,而后用deque.pop()方法层次遍历二叉树,将值放入list集合中,左右节点若不为空,则将节点放入队列继续层次遍历取值,最后输出。

2、排序都有哪几种方法

1、冒泡排序

2、选择排序

3、插入排序

4、快速排序

快排:从数组中选择一个元素作为支点,把余下的元素分割为两段,使得左边中的元素都小于等于支点,而右边中的元素都大于等于支点,然后让左边的下标一直往右走,右边的下标一直往左走,直到两边都走到支点下标位置。

然后交换左右两边下标,重复上面的过程,直到左边下标 > 右边下标。交换右边下标和支点下标,完成一次快速排序。

设计模式

1、单例模式1

饿汉式单例模式:

public class Singleton {  
    private static Singleton instance = new Singleton();    
    // 私有构造方法,保证外界无法直接实例化。    
    private Singleton() {}  
    // 通过公有的静态方法获取对象实例    
    public static Singleton getInstace() {    
        return instance;
    }
}

线程同步懒汉式单例模式:

public class Singleton {    
    private static Singleton instance = null;    
    // 私有构造方法,保证外界无法直接实例化。  
    private Singleton() {}    
    // 通过公有的静态方法获取对象实例  
    synchronized public static Singleton getInstace() {    
        if (instance == null) {        
            instance = new Singleton();    
        }    
        return instance;  
    }
}

对获取对象实例的公有静态方法进行同步,以确保多线程环境下只创建一个实例。

2、工厂模式

工厂模式的用意是定义一个创建产品对象的工厂接口,将实际创建性工作推迟到子类中。工厂模式可分为简单工厂、工厂方法模式和抽象工厂模式。

以简单工厂为例:

简单工厂的适用场景是:1、需要创建的对象较少。2、客户端不关心对象的创建过程。

代码思路:首先定义一个接口或者抽象类,作为公共父类,并在其中声明一个公共方法:然后编写具体的类,每个类都实现公共父类接口:最后就是工厂类的具体实现:为工厂类传入不同的参数可以new不同的对象,返回结果为公共父类型,这个就是简单工厂核心的地方了。

3、观察者模式

观察者模式(Observer Pattern)也称发布订阅模式,它的目的是定义对象间一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。观察者模式具有4个角色:

1、抽象主题角色:该角色又称为“被观察者”,可以增加和删除观察者对象。

2、抽象观察者角色:它为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。

3、具体主题角色:又称为“具体被观察者”,它将有关状态存入具体观察者对象,在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

4、具体观察者角色:该角色实现抽象观察者所要求的更新接口,以便使自身的状态与主题的状态相协调。

观察者模式的应用场景:

关联行为场景。

事件多级触发场景。

跨系统的消息交换场景,如消息队列的处理机制。

4、生产者与消费者模式

所谓生产者-消费者模式,实际上主要是包含了两类线程。一种是生产者线程用于生产数据,另一种是消费者线程用于消费数据,为了解耦生产者和消费者的关系,通常会采用共享数据区,就像是一个仓库。生产者生产数据之后直接放置在共享数据区中,并不关心消费者的行为。而消费者只需要从共享数据区中去获取数据,不需要关心生产者的行为。如果共享数据区已满的话,阻塞生产者继续生产数据放置入内;如果共享数据区为空的话,阻塞消费者继续消费数据。

在Java语言中,实现生产者消费者模式,可以使用Object的wait/notify或Lock的await/signal的消息通知机制。

消息队列

1、消息队列(MQ)有什么用

消息队列常用于解耦、异步和削峰。

解耦:就是将各模块所需的共享数据放在消息队列中,各业务模块直接从消息队列中订阅该类消息,对原有系统和业务没有任何影响。

异步:是指消息队列提供的异步处理机制,很多时候应用不想也不需要立即处理的消息就可以先放入消息队列中,在之后时间里慢慢处理。

削峰:是在访问量骤增的场景下,使用消息队列将消息积压起来,能够使关键组件支撑突发访问压力,不会因为突发的超负荷请求而完全崩溃。

2、消息队列如何保证顺序消费

在RabbitMQ中,导致顺序错乱的原因通常是不同的消费者消费到了同一个订单的不同的消息。解决这个问题,我们可以给RabbitMQ创建多个queue,每个消费者固定消费一个queue的消息,生产者发送消息的时候,同一个订单号的消息发送到同一个queue中,由于同一个queue的消息一定是有序的,那么同一个订单号的消息就只会被一个消费者顺序消费,从而保证了消息的顺序性。

消息队列如何保证消息不丢

在RabbitMQ中,根据不同的情况有不同的处理方式:

1、生产者丢消息:可以开启confirm模式,它是异步的,发送消息之后可以接着发送下一个消息,然后RabbitMQ回调成功会回传一个ack消息,告诉你这个消息发送OK了。如果RabbitMQ没能处理这个消息,会回调你一个nack接口,告诉你这个消息失败了,你可以进行重试。

2、RabbitMQ自己丢消息:设置消息持久化到磁盘,设置持久化有两个步骤:

创建queue的时候将其设置为持久化的,这样就可以保证RabbitMQ持久化queue的元数据,但是不会持久化queue里面的数据。

发送消息的时候将消息的deliveryMode设置为2,这样消息就会被设为持久化方式,此时RabbitMQ就会将消息持久化到磁盘上。

3、消费端丢消息:使用RabbitMQ提供的ack机制,首先关闭RabbitMQ的自动ack,然后每次在确保处理完这个消息之后,在代码里手动调用ack。这样就可以避免消息还没有处理完就ack。

RabbitMQ和Kafka有什么区别

在实际生产应用中,通常会使用Kafka作为消息传输的数据管道,RabbitMQ作为交易数据的数据传输管道,主要的取舍因素则是是否存在丢数据的可能。RabbitMQ在金融场景中经常使用,具有较高的严谨性,数据丢失的可能性更小,同事具备更高的实时性。而Kafka优势主要体现在吞吐量上,虽然可以通过策略实现数据不丢失,但从严谨性角度来讲,大不如RabbitMQ。而且由于Kafka保证每条消息最少送达一次,有较小的概率会出现数据重复发送的情况。

场景

单点登录

用户访问系统1的受保护资源,系统1发现用户未登录,跳转至认证中心,并将自己的地址作为参数;

认证中心发现用户未登录,将用户引导至登录页面;

用户输入用户名密码提交登录申请;

认证中心校验用户信息,创建用户与认证中心之间的会话,称为全局会话,同时创建授权令牌;

认证中心带着令牌跳转会最初的请求地址(系统1);

系统1拿到令牌,去认证中心校验令牌是否有效

认证中心校验令牌,返回有效,注册系统1;

系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源;

用户访问系统2的受保护资源;

系统2发现用户未登录,跳转至认证中心,并将自己的地址作为参数;

认证中心发现用户已登录,跳转回系统2的地址,并附上令牌;

系统2拿到令牌,去认证中心校验令牌是否有效;

认证中心校验令牌,返回有效,注册系统2;

系统2使用该令牌创建与用户的局部会话,返回受保护资源。

全局会话与局部会话有如下约束关系:

局部会话存在,全局会话一定存在;

全局会话存在,局部会话不一定存在;

全局会话销毁,局部会话必须销毁。

单点注销

用户向系统1发起注销请求;

系统1根据用户与系统1建立的会话id拿到令牌,向认证中心发起注销请求;

认证中心校验令牌有效,销毁全局会话,同时取出所有用此令牌注册的系统地址;

认证中心向所有注册系统发起注销请求;

各注册系统接收认证中心的注销请求,销毁局部会话;

认证中心引导用户至登录页面。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值