面试题:
1.Springboot 将原有的 xml 配置,简化为 java 注解
2.使用 IDEA 可以很方便的搭建一个 springboot 项目,选择对应的 maven 依赖,简化Spring应用的初始搭建以及开发过程
3.springboot 有内置的 tomcat 服务器,可以快速部署发布 web 服务
核心容器(Spring Core)
核心容器提供Spring框架的基本功能。Spring以bean的方式组织和管理Java应用中的各个组件及其关系。Spring使用BeanFactory来产生和管理Bean,它是工厂模式的实现。BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开。
应用上下文(Spring Context)
Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring面向切面编程(Spring AOP)
通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring框架中。所以,可以很容易地使 Spring框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
JDBC和DAO模块(Spring DAO)
JDBC、DAO的抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理,和不同数据库供应商所抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
对象实体映射(Spring ORM)
Spring框架插入了若干个ORM框架,从而提供了ORM对象的关系工具,其中包括了Hibernate、JDO和 IBatis SQL Map等,所有这些都遵从Spring的通用事物和DAO异常层次结构。
Web模块(Spring Web)
Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供了上下文。所以Spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
MVC模块(Spring Web MVC)
MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变成为高度可配置的。MVC容纳了大量视图技术,其中包括JSP、POI等,模型来有JavaBean来构成,存放于m当中,而视图是一个街口,负责实现模型,控制器表示逻辑代码,由c的事情。Spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境。Spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问的对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用。
回答思路:1.先介绍IOC和AOP的概念---------》2.各自的实现原理-----------》3.自己的项目中如何使用
1.IOC(Inverse of Control):控制反转,也可以称为依赖倒置。
许多应用都是通过彼此间的相互合作来实现业务逻辑的,如类A要调用类B的方法,以前我们都是在类A中,通过自身new一个类B,然后在调用类B的方法,现在我们把new类B的事情交给spring来做,在我们调用的时候,容器会为我们实例化。
(依赖:就是A要调用B的方法,那么A就依赖B。反转:A要调用B的时候,并不需要主动获取B,而是由其它人主动将B送上门)
IOC的实现原理:
- 先获取工厂类对象(Spring中封装了工厂类);BeanFactory
- 然后加载配置文件;
- 通过工厂类对象获取配置的bean对象(工厂类中的getBean()方法)
- 对bean对象进行各种操作(调用bean对象所属类的方法什么的)
2.AOP(Aspect Oriented Programming)面向切面编程。为了解耦而生。(*)
面向切面编程,就是让开发者把诸多业务流程中的通用功能抽取出来,单独编写功能功能代码,形成独立的模块,这些模块也被称为切面。在业务流程执行过程中,Spring框架会根据业务流程要求,自动把切面切入到流程的合适位置。
AOP的实现原理:
- 首先编写需要切入业务流程的独立模块(切面)和切入点(模块中的方法);
- 然后在Spring配置文件中配置AOP,添加切入面、切入点以及需要切入的目标Bean;
- 最后编写测试代码。
代码:注解方式实现
1、引入Jar 包,向pom中引入spring-context、aspectj
2、创建Spring核心配置文件,导入AOP的约束
3、配置Spring创建容器时要扫描的包和spring开启注解AOP的支持
4、配置AccountServiceImpl的注解
/**
* 账户的业务层实现类
*/
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
public void saveAccount(){
System.out.println("执行了保存");
// int i =1/0;
}
public void updateAccount(int i){
System.out.println("执行了更新");
}
public int deleteAccount(){
System.out.println("执行了删除");
return 0;
}
}
5、配置Logger的注解
/**
* 用于记录日志的工具类,它里面提供了公共的代码
*/
@Component("logger")
public class Logger {
//省略
}
6、配置切入点以及通知类型(配置切面,把增强用到方法上)
@Component("logger")
@Aspect//表示当前类是一个切面类
public class Logger {
//切入点
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1(){}
/**
* 前置通知
*/
@Before("pt1()")
public void beforePrintLog(){
System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
}
/**
* 后置通知
*/
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
}
/**
* 异常通知
*/
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
}
/**
* 最终通知
*/
@After("pt1()")
public void afterPrintLog(){
System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
}
}
7、编写测试类
/**
* 测试AOP的配置
*/
public class AOPTest {
public static void main(String[] args) {
//1.获取容器
ApplicationContext ac= new ClassPathXmlApplicationContext("bean.xml");
//2.获取对象
IAccountService as= ac.getBean("accountService",IAccountService.class);
//3.执行方法
as.saveAccount();
}
}
附:
使用表达式配置切入点
1、切入点:实际增强的方法
2、常用的表达式
execution(<访问修饰符>?<返回值类型><方法名>(参数)<异常>)
(1)execution(* com.bjxb.aop.Book.add(..))——星号代表匹配所有的访问修饰符(注意第一个星号后需要加空格),add(..)中的点代表可以有参数
(2)execution(* com.bjxb.aop.Book.*(..))——第二个星号代表Book类中所有的方法
(4)execution(* com.bjxb.aop. . *(..))——第二个星号前面的两个点代表该包、及其子包下所有的类,一个点不包含其子包
(5)execution(* *.*(..))——第二个星号代表所有的类
(6)execution(* save*(..))——匹配所有的save开头的方法
在Spring配置文件定义Bean时,通过声明scope配置项,可以灵活定义Bean的作用范围。
Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,作用域包括singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)。
scope配置项有5个属性,用于描述不同的作用域。
① singleton(默认)
使用该属性定义Bean时,IOC容器仅创建一个Bean实例,IOC容器每次返回的是同一个Bean实例。
② prototype
使用该属性定义Bean时,IOC容器可以创建多个Bean实例,每次返回的都是一个新的实例。
③ request
该属性仅对HTTP请求产生作用,使用该属性定义Bean时,每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境。
④ session
该属性仅用于HTTP Session,同一个Session共享一个Bean实例。不同Session使用不同的实例。
⑤ global-session
该属性仅用于HTTP Session,同session作用域不同的是,所有的Session共享一个Bean实例。
singleton和prototype模式,这两个模式的作用域在Spring框架中是经常用到的。对于singleton作用域的Bean,IOC容器每次都返回同一个实例,而prototype作用域的Bean,IOC容器每次产生一个新的实例。
1.前置通知
在目标方法执行之前执行执行的通知。
前置通知方法,可以没有参数,也可以额外接收一个JoinPoint,Spring会自动将该对象传入,代表当前的连接点,通过该对象可以获取目标对象 和 目标方法相关的信息。
注意,如果接收JoinPoint,必须保证其为方法的第一个参数,否则报错。
2.环绕通知
在目标方法执行之前和之后都可以执行额外代码的通知。
3.后置通知
在目标方法执行之后执行的通知。
4.异常通知
在目标方法抛出异常时执行的通知
5.最终通知
是在目标方法执行之后执行的通知。
和后置通知不同之处在于,后置通知是在方法正常返回后执行的通知,如果方法没有正常返-例如抛出异常,则后置通知不会执行。
而最终通知无论如何都会在目标方法调用过后执行,即使目标方法没有正常的执行完成。
回答思路:1.Seesion跟Cookie怎么来的---------》2.各自的概念-----------》3.各自的工作原理-----------》4.总结区别
HTTP 是一种无状态的连接,客户端每次读取 web页面时,服务器都会认为这是一次新的会话。但有时候我们又需要持久保持某些信息,比如登录时的用户名、密码,用户上一次连接时的信息等。这些信息就由 Cookie 和 Session 保存。
Cookie
cookie实际上是一小段文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个cookie,客户端浏览器会把cookie保存起来,当浏览器再次请求访问该网站时,浏览器把请求的网站连同该cookie一同提交给服务器,服务器检查该cookie,以此来辨认用户状态。
简单来说,cookie的工作原理可总结如下:client连接server
client生成cookie(有效期),再次访问时携带cookie
server根据cookie的信息识别用户身份
Session
Session是服务器端使用的一种记录客户端状态的机制,使用上比Cookie简单一些。同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 name为 JSESIONID 的一个 Cookie。Session依据这个id来识别是否为同一用户(只认ID不认人)。
区别:
1、cookie数据存放在客户端,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上,当访问增多,会比较占用你服务器的性能,考虑性能应当使用cookie。
4、可以考虑将登陆信息等重要信息存放为session,不重要的信息可以放在cookie中。
区别:
抽象类可以有构造函数,接口不可以有构造函数
抽象类中可以有普通成员变量,接口中没有普通成员变量,只能有常量
抽象类中的方法可以被static修饰,接口中的方法不可以被static修饰
抽象类中可以有普通方法和抽象方法,接口中的方法全是抽象方法
一个类只能继承一个抽象类,接口可以被多实现,即一个类只能继承一个类,可以实现多个接口
何时使用
接口主要用于模块与模块之间的调用。主要用接口来实现多继承,因为java不支持类的多继承,只能用接口
抽象类主要用于当做基础类使用,即基类。如果想拥有一些方法,并且这些方法有默认实现,那么使用抽象类
1 简单简介
1.1 Hibernate 框架
Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,建立对象与数据库表的映射。是一个全自动的、完全面向对象的持久层框架。
1.2 Mybatis框架
Mybatis是一个开源对象关系映射框架,原名:ibatis,2010年由谷歌接管以后更名。是一个半自动化的持久层框架。
2 两者区别
2.1 开发方面
在项目开发过程当中,就速度而言:
hibernate开发中,sql语句已经被封装,直接可以使用,加快系统开发;
Mybatis 属于半自动化,sql需要手工完成,稍微繁琐;
但是,凡事都不是绝对的,如果对于庞大复杂的系统项目来说,复杂语句较多,选择hibernate 就不是一个好方案。
2.2 sql优化方面
Hibernate 自动生成sql,有些语句较为繁琐,会多消耗一些性能;
Mybatis 手动编写sql,可以避免不需要的查询,提高系统性能;
Mybatis优势
MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
MyBatis容易掌握,而Hibernate门槛较高。
一句话总结
Mybatis:小巧、方便、高效、简单、直接、半自动化
Hibernate:强大、方便、高效、复杂、间接、全自动化
考点1、熟悉线程安全的集合对象:记忆线程安全的集合,其他都是非线程安全的
线程安全(Thread-safe)的集合对象:
非线程安全的集合对象:
考点2、arraylist和linkedlist的区别(多)
考察点:数据结构功底,理解及深入程度
考点3、HashMap跟Hashtable的区别(多)
1、安全性:HashMap是非线程安全的,在多线程并发的环境下,可能会产生死锁等问题。但是它的效率会比Hashtable要好很多。这样设计是合理的。在我们的日常使用当中,大部分时间是单线程操作的。HashMap把这部分操作解放出来了。当需要多线程操作的时候可以使用线程安全的ConcurrentHashMap。ConcurrentHashMap虽然也是线程安全的,但是它的效率比Hashtable要高好多倍。因为ConcurrentHashMap使用了分段锁,并不对整个数据进行锁定。
Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。在多线程并发的环境下,可以直接使用Hashtable,不需要自己为它的方法实现同步
2、产生时间:Hashtable不遵循驼峰式命名标准,Hashtable是java发布时就提供的键值映射的数据结构,而HashMap产生于JDK1.2
3、继承关系不同,HashMap继承自AbstractMap,而Hashtable继承自Dictionary。不过它们都同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。Dictionary类是一个已经被废弃的类(见其源码中的注释)。父类都被废弃,自然而然也没人用它的子类Hashtable了。
4、对外提供的接口不同,Hashtable比HashMap多提供了elments() 和contains() 两个方法。
elments() 方法继承自Hashtable的父类Dictionnary。elements() 方法用于返回此Hashtable中的value的枚举。
contains()方法判断该Hashtable是否包含传入的value。它的作用与containsValue()一致。事实上,contansValue() 就只是调用了一下contains() 方法。
public boolean containsValue(Object value){
return contains(value);
}
5、对Null key 和Null value的支持不同
Hashtable既不支持Null key也不支持Null value。当key为Null时,调用put() 方法,会抛出空指针异常。
HashMap中,null可以作为键,这样的键只有一个;HashMap中,null可以作为键,这样的键只有一个;当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
6、初始容量大小和每次扩充容量大小的不同
Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
创建时,如果给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。也就是说Hashtable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。
之所以会有这样的不同,是因为Hashtable和HashMap设计时的侧重点不同。Hashtable的侧重点是哈希的结果更加均匀,使得哈希冲突减少。当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。而HashMap则更加关注hash的计算效率问题。在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。这从而导致了Hashtable和HashMap的计算hash值的方法不同。
考点4、
Map实现原理、底层实现
Hash表的结构,是什么
HashMap怎么实现线程安全
线性表跟链表的区别
StringBuilder和StringBuffer的关系详解
1)问题:数据库中最常见的慢查询优化方式是什么?
回答:加索引
2)问题:为什么加索引能优化慢查询?
回答:因为索引是一种优化查询的数据结构,比如MySQL中的索引是B+树实现的,而B+树就是一种数据结构,可以优化查询速度,可以利用索引快速查找数据,所以能优化查询!
3)你知道哪些数据结构可以提高查询速度?
回答:哈希表、完全平衡二叉搜索树、B树、B+树等等;
4)那这些数据结构既然都能优化查询速度,那MySQL为何选择使用B+树?
总结出来,Mysql选用B+树这种数据结构作为索引,可以提高查询索引时的磁盘IO效率,并且可以提高范围查询的效率,并且B+树里的元素也是有序的。
1-当使用where范围查找时,哈希表的特点可以快速的精确查询,但是不支持范围查询!
2-完全平衡二叉搜索树是有序的,所以支持范围查找。
3-B树表示的要比完全平衡二叉搜索树要 "矮",原因在于B树中的一个节点可以存储多个元素!
4-一个节点可以存储多个元素,相对于完全平衡二叉树所以整棵树的高度就降低了,磁盘IO效率提高了。而B+树是B树的升级版,只是把非叶子节点冗余一下,这么做的好处是 为了提高范围查找的效率。
1、MySQL开源
2、单引号跟自动增长的数据类型处理
Mysql里可以用双引号包起字符串,Oracle里只可以用单引号包起字符串。Mysql有一个自动增长的数据类型,插入数据的时候,不需要管理,它自己会自动增长,Oracle不支持自动增长的数据类型,通过建立一个自动增长的序列号来完成自动增长。
3、sql语句的扩展性
Mysql对sql语句有很多非常实用而方便的扩展,比如limit功能,insert可以一次插入多行数据,插入的方式不同。
4、事物提交方式
oracle默认不自动提交,需要用户手动提交。Mysql默认是自动提交。不支持事物。
Mysql默认自动提交,也就是你提交一个query,他就直接执行,我们可以通过
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
5、数据库与用户、表之间的区别
6、分页的方式
使用的特殊关键字不同,mysql 使用limit;oracle使用rownum;
Mysql的分页是比较容易的,只需要调用limit关键字加上当前位置和之后多少条数据,就可以的到指定范围内的数据 了,但是在Oracle中,却不能这么做,虽然Oracle中有伪列的概念,但它只能使用rownum<50,不能使用rownum>50的写法,如果想对Oracle分页随心所欲的进行操作的话,需要使用嵌套查询,先将Oracle的伪列(RowNum)变为实列,再写指定的位置。
例子:
mysql的分页sql格式是:select * from table limit (start-1)*limit,limit; 其中start是页码,limit是每页显示的条数。
limit m,n 第一个参数表示从该参数的下一条数据开始,第二个参数表示每次返回的数据条数
select * from test limit 3,5;--查询test从3开始,5条数据
oracle:
select rownum,rowid,tt.* from t_user5 tt where rownum<=3 and sex=1 and rownum>=2
使用rownum使其重新排列,从1开始
Java序列化是为了保存各种对象在内存中的状态,并且可以把保存的对象状态再读出来。
以下情况需要使用Java序列化:
当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
考题1、JSP和servlet有什么区别?
1-JSP是Servlet技术的扩展,本质上就是Servlet的简易方式。
2-JSP侧重于视图,servlet主要用于控制逻辑。
3-JSP有九大内置对象、四大作用域,servlet有三大作用域;(挖坑)
4-Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件。
考题2、JSP有哪些内置对象?作用分别是什么?
JSP有9大内置对象:
request:封装客户端请求,其中包含来自get或post请求的参数;
response:封装服务器对客户端的响应;
pageContext:通过该对象可以获取其他对象;
session:封装用户会话的对象;
application:封装服务器运行环境的对象
out:输出服务器响应的输出流对象;
config:Web应用配置对象;
page:JSP页面本身(相当于Java程序中的this);
exception:封装页面抛出异常的对象。
考题3、说一下JSP的4大作用域?
1、page指当前页面。在一个jsp页面里有效 。
2、request 指从http请求到服务器处理结束,返回响应的整个过程。在这个过程中使用forward方式跳转多个jsp。在这些页面里你都可以使用这个变量。
3、Session 有效范围当前会话,从浏览器打开到浏览器关闭这个过程。
4、application它的有效范围是整个应用(包括多个页面、请求和会话的一个全局作用域)。 application里的变量,它的存活时间是最长的,如果不进行手工删除,它们就一直可以使用 。重启tomcat,否则它会一直变大。
考题4、Servlet的3大作用域是什么?
1、request(HttpServletRequest)
2、session(HttpSession):
3、application(ServletContext):tomcat启动时创建,tomcat关闭时销毁,整个web的生命周期只有一个
考题5、get跟post请求的区别
1、get是从服务器上获取数据,post是向服务器传送数据。
2、get传送的数据量较小,因为受URL限制,不能大于2KB,但是效率高。post传送的数据量较大,一般被默认为不受限制,所以上传文件时只能用post。
3、get方式的安全性较Post方式要差些,包含机密信息的话,建议用Post数据提交方式;
4、在做数据查询时,建议用Get方式;而在做数据添加、修改或删除时,建议用Post方式。
考题6、举例Http常见的状态码
200:请求被正常处理
204:请求被受理但没有资源可以返回
301:永久性重定向
302:临时重定向
303:与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上
307:临时重定向,与302类似,只是强制要求使用POST方法
400:请求报文语法有误,服务器无法识别
401:请求需要认证
403:请求的对应资源禁止被访问
404:服务器无法找到对应资源
500:服务器内部错误
503:服务器正忙
考点1、spring自动装配bean有哪些方式?
1-注解方式:
通过组件扫描(ComponentScan):自动发现应用上下文中所创建的bean。@ComponentScan作用是扫描带有@Component注解的类,并为其创建bean。
自动装配(Autowired):自动满足bean之间的依赖。
2-XML方式:通过 "ref" 属性来手动设置 bean。
考点2、说一下乐观锁和悲观锁?
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型。
优缺点:像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。
使用场景:版本号机制
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
举一个简单的例子:
假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。当需要对账户信息表进行更新的时候,需要首先读取version字段。
操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
操作员 A 完成了修改工作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,一致的话,就会将数据版本号加1( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
操作员 B 完成了操作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,而自己读取到的版本号为1 ,不满足 “ 当前最后更新的version与操作员第一次读取的版本号相等 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。
考点3、说一下Spring常见注解?
1、声明bean的注解
@Component 组件,没有明确的角色
@Service 在业务逻辑层使用(service层)
@Repository 在数据访问层使用(dao层)
@Controller 在展现层使用,控制器的声明
2、注入bean的注解
@Autowired:由Spring提供
@Resource:由JSR-250提供,JDK自带的标准,不是spring
@Qualifier:限定符
3、java配置类相关注解
@Configuration 声明当前类为配置类,相当于xml形式的Spring配置(类上)
@Bean 注解在方法上,声明当前方法的返回值为一个bean,替代xml中的方式(方法上)
@ComponentScan 用于对Component进行扫描,相当于xml中的(类上)
4、切面(AOP)相关注解
Spring支持AspectJ的注解式切面编程。
@Aspect 声明一个切面(类上)
使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。
@After 在方法执行之后执行(方法上)
@Before 在方法执行之前执行(方法上)
@Around 在方法执行之前与之后执行(方法上)
@PointCut 声明切点
在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)
5、@Bean的属性支持
@Scope 设置Spring容器如何新建Bean实例(方法上,得有@Bean)
其设置类型包括:
Singleton (单例,一个Spring容器中只有一个bean实例,默认模式),
Protetype (每次调用新建一个bean),
Request (web项目中,给每个http request新建一个bean),
Session (web项目中,给每个http session新建一个bean),
GlobalSession(给每一个 global http session新建一个Bean实例)
6、@Value注解
@Value 为属性注入值(属性上)
7、测试相关注解
@RunWith 运行器,Spring中通常用于对JUnit的支持
@ContextConfiguration 用来加载配置ApplicationContext,其中classes属性用来加载配置类
考点4、 说一下Spring|Spring MVC相关注解?
@EnableWebMvc 在配置类中开启Web MVC的配置支持,如一些ViewResolver或者MessageConverter等,若无此句,重写WebMvcConfigurerAdapter方法(用于对SpringMVC的配置)。
@Controller 声明该类为SpringMVC中的Controller
@RequestMapping 用于映射Web请求,包括访问路径和参数(类或方法上)
@ResponseBody 支持将返回值放在response内,而不是一个页面,通常用户返回json数据(返回值旁或方法上)
@RequestBody 允许request的参数在request体中,而不是在直接连接在地址后面。(放在参数前)
@PathVariable 用于接收路径参数,比如@RequestMapping(“/hello/{name}”)申明的路径,将注解放在参数中前,即可获取该值,通常作为Restful的接口实现方法。
@RestController 该注解为一个组合注解,相当于@Controller和@ResponseBody的组合,注解在类上,意味着,该Controller的所有方法都默认加上了@ResponseBody。
@ControllerAdvice 通过该注解,我们可以将对于控制器的全局配置放置在同一个位置,注解了@Controller的类的方法可使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上,这对所有注解了 @RequestMapping的控制器内的方法有效。
@ExceptionHandler 用于全局处理控制器里的异常
@InitBinder 用来设置WebDataBinder,WebDataBinder用来自动绑定前台请求参数到Model中。
@ModelAttribute 本来的作用是绑定键值对到Model里,在@ControllerAdvice中是让全局的@RequestMapping都能获得在此处设置的键值对。
考点5、SpringMVC的执行流程?
面试可以如下回答:
1、用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获(捕获)
2、DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;(查找handler)
3、DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller), Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象(执行handler)
4、DispatcherServlet 根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver) (选择ViewResolver)
5、通过ViewResolver 结合Model和View,来渲染视图,DispatcherServlet 将渲染结果返回给客户端。(渲染返回)
快速记忆技巧:核心控制器捕获请求、查找Handler、执行Handler、选择ViewResolver,通过ViewResolver渲染视图并返回
异常是指程序在运行过程中发生的一些不正常事件。(如:除0溢出,数组下标越界,所读取的文件不存在
考点1、java 异常有哪几种,特点是什么?
异常是发生在程序执行过程中阻碍程序正常执行的错误操作,只要在 Java 语句执行中产生异常则一个异常对象就会被创建。Throwable 是所有异常的父类,它有两个直接子类 Error 和 Exception,其中 Exception 又被继续划分为被检查的异常(checked exception)和运行时的异常(runtime exception,即不受检查的异常);Error 表示系统错误,通常不能预期和恢复(譬如 JVM 崩溃、内存不足等);被检查的异常(Checked exception)在程序中能预期且要尝试修复(如我们必须捕获 FileNotFoundException 异常并为用户提供有用信息和合适日志来进行调试,Exception 是所有被检查的异常的父类);运行时异常(Runtime Exception)又称为不受检查异常,譬如我们检索数组元素之前必须确认数组的长度,否则就可能会抛出 ArrayIndexOutOfBoundException 运行时异常,RuntimeException 是所有运行时异常的父类。
考点2、java 中 Error 和 Exception 有什么区别?
Error 表示系统级的错误,是 java 运行环境内部错误或者硬件问题,不能指望程序来处理这样的问题,除了退出运行外别无选择,它是 java 虚拟机抛出的。Exception 表示程序需要捕捉、需要处理的异常,是由与程序设计的不完善而出现的问题,程序可以处理的问题。
考点3、try-catch-finally-return执行顺序
考点4、列出常见的几种RunException
NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常
ArithmeticException - 算数运算异常,如除数为零
IndexOutOfBoundsException - 下标越界异常
FileNotFoundException - 文件未找到异常
SQLException - 操作数据库异常
IOException - 输入输出异常
java.lang.AbstractMethodError - 抽象方法错误。当应用试图调用抽象方法时抛出
java.lang.Error - 错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
考点5、Java中的检查型异常和非检查型异常有什么区别?(常)
被检查的异常应该用 try-catch 块代码处理或用 throws 关键字抛出,不受检查的异常在程序中不要求被处理或用 throws 抛出;
Exception 是所有被检查异常的基类,而 RuntimeException(是 Exception 的子类) 是所有不受检查异常的基类;被检查的异常适用于那些不是因程序引起的错误情况(如 FileNotFoundException),而不被检查的异常通常都是由于糟糕的编程引起(如 NullPointerException)。
考点6、java 中 throw 与 throws 的区别是什么?
考点7、java 中什么是异常链?
异常链是指在进行一个异常处理时抛出了另外一个异常,由此产生了一个异常链条,大多用于将受检查异常(checked exception)封装成为非受检查异常(unchecked exception)或者 RuntimeException。特别注意如果你因为一个异常而决定抛出另一个新的异常时一定要包含原有的异常,这样处理程序才可以通过 getCause() 和 initCause() 方法来访问异常最终的根源。
拓展:
下面的Java异常代码有什么错误:
public static void start(){
System.out.println(“Java Exception interivew question Answers for Programmers”);
}
public static void main(String args[]) {
try{
start();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
上面的Java异常例子代码中,编译器将在处理IOException时报错,因为IOException是受检查异常,而start
方法并没有抛出IOException,所以编译器将抛出“异常, java.io.IOException 不会在try语句体中抛出”,但
是如果你将IOException改为Exception,编译器报错将消失,因为Exception可以用来捕捉所有运行时异常,这样
就不需要声明抛出语句。
考点1、什么是事务?
事务,就是一组操作数据库的动作集合。
事务是现代数据库理论中的核心概念之一。如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务。当所有的步骤像一个操作一样被完整地执行,我们称该事务被提交。由于其中的一部分或多步执行失败,导致没有步骤被提交,则事务必须回滚到最初的系统状态。
事务必须服从ISO/IEC所制定的ACID原则
考点2、说一下ACID是什么?(次数1*)
ACID 一般是指数据库事务的ACID。
一个事务一般是指多个操作的集合,比如插入数据库分两段插入,第二次插入错误,第一次插入操作也需要回退。
1.Atomicity 原子性:指的是整个事务是一个独立的单元,要么操作成功,要么操作不成功;
2.Consistency 一致性:在事务开始在之前和事务结束之后,必须要保持和系统处于一致的状态(如果不一致会导致系统其它的方出现bug);
3.Isolation 隔离性:事务是并发控制机制,他们的交错也需要一致性,隔离隐藏,一般通过悲观或者乐观锁实现;
4.Durability 持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
考点3、J2EE服务器支持哪些类型?
支持三种类型的事务管理。即:JDBC事务,JTA事务,容器管理事务。
最好不要在程序中同时使用上述三种事务类型;并且,事务要在尽可能短的时间内完成,不要在不同方法中实现事务的使用。下面举两个例子说明JDBC及JTA事务,容器管理事务是在特定的框架中实现的(如:Spring的事务管理)
JDBC事务
public String delete(String id) {
String ID = id;
db = new getConnection();
Connection con = db.getConnection();
try {
con.setAutoCommit(false);
db.executeUpdate("delete from helloworld where ID=" + ID); //更新操作1
db.executeUpdate("delete from helloworld _book where ID=" + ID); //更新操作2
db.executeUpdate("delete from helloworld_user where ID=" + ID); //更新操作3
con.commit();//提交JDBC事务
con.setAutoCommit(true);
db.close();
return “success”;
}
catch (Exception e) {
con.rollBack();//回滚JDBC事务
e.printStackTrace();
db.close();
return “fail”;
}
}
如上例:更新操作1,2,3只有当三步操作都成功完成才进行提交,否则回滚已经进行的操作。这样,保证了数据的完整性,不会因为突然断电等特殊情况导致的数据错误。
JTA事务
JTA是J2EE事务服务的解决方案、描述了J2EE模型事务接口。JTA具有三个主要的接口:UserTransaction、TransactionManager、Transaction接口。这些接口共享公共的事务操作,如:commit()、rollback()。同时各自也有自己的操作。举例说明:
public String delete(String id) {
String ID = id;
db = new getConnection();
db.getConnection();
UserTransaction transaction = sessionContext.getUserTransaction();//获得JTA事务
try {
transaction.begin(); //开始JTA事务
db.executeUpdate("delete from helloworld where ID=" + ID);
db.executeUpdate("delete from helloworld _book where ID=" + ID);
db.executeUpdate("delete from helloworld _user where ID=" + ID);
transaction.commit(); //提交JTA事务
db.close();
return”success”;
}
catch (Exception e) {
try {
transaction.rollback();//事务回滚
}
catch (Exception e) {
e.printStackTrace();
}
exc.printStackTrace();
db.close();
return “fail”;
}
}
容器事务管理
在Spring、Hibernate等框架中都有各自的事务管理功能。虽然表现形式有些差别,但都是在JAVA事务管理的基础上实现的。
考点4、spring的事务管理有几种方式实现?如何实现?
实现方式共有两种:编码方式;声明式事务管理方式。
基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。
声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中。
考点5、说一下spring的事务隔离?
有5种事务隔离级别
1、Defalt 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.
2、Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
3、Repeatable read (可重复读):可避免脏读、不可重复读的发生。
4、Read committed (读已提交):可避免脏读的发生。
5、Read uncommitted (读未提交):最低级别,任何情况都无法保证。
MySQL数据库中,支持下面四种隔离级别,默认的为Repeatable read (可重复读);而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
考点6、如果不考虑隔离性可能引发安全性问题有哪些?
脏读 :一个事务读到了另一个事务的未提交的数据 。例如:用户A向用户B转账100元,对应SQL命令如下
update account set money=money+100 where name=’B’; (此时A通知B)
update account set money=money - 100 where name=’A’;
当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。
不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
虚幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。
幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
记住:设置数据库的隔离级别一定要是在开启事务之前!
如果是使用JDBC对数据库的事务设置隔离级别的话,也应该是在调用Connection对象的setAutoCommit(false)方法之前。
后记:隔离级别的设置只对当前链接有效。对于使用MySQL命令窗口而言,一个窗口就相当于一个链接,当前窗口设置的隔离级别只对当前窗口中的事务有效;对于JDBC操作数据库来说,一个Connection对象相当于一个链接,而对于Connection对象设置的隔离级别只对该Connection对象有效,与其他链接Connection对象无关。
考点7、Spring事务的传播行为有哪些?
事务传播属性:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。(一个方法运行在了一个开启事务的方法中时,当前方法是使用原来的事务还是开启一个新的事务)例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。事务的传播行为可以由传播属性指定,Spring 定义了 7 种传播行为。
事务传播属性可以在 @Transactional 注解的 propagation 属性中定义。
(*)
初始化顺序:
先初始化父类的静态代码--->初始化子类的静态代码-->初始化父类的非静态代码--->初始化父类构造函数--->初始化子类非静态代码--->初始化子类构造函数
优先级层次(相同优先级的属性按程序先后顺序初始化):
第一级:静态属性
1. 父类: 静态成员变量=静态代码块
2. 子类: 静态成员变量=静态代码块
第二级:非静态属性
1. 父类:
1.1 成员变量 = 代码块
1.2 构造函数
2. 子类:
2.1 成员变量 = 代码块
2.2 构造函数
总结:
静态块代码作用于类级别,类加载时被装载,且只被加载一次,用于进行属性的初始化,而构造方法和构造代码段作用于对象级别,当对象需要实例化时才被调用。构造代码段在每实例化一个对象时进行一次调用,并优先于构造函数,用于初始化实例环境和不同对象共性的实例化内容。构造函数每实例化一个对象时都会执行一次,用以初始化对象属性。
参考大神博客:https://blog.csdn.net/qq_36522306/article/details/80584595
例:String str1 = "world"、String str2 = new String("world")、String str3 = "wor" + "ld" 使用==、equals、hashcode之间的关系
str1 == str2 返回 false
str1 == str3 返回 true
str2 == str3 返回 false
str1.equals(str2) 返回 true
str1.equals(str3) 返回 true
str2.equals(str3) 返回 true
结论:
1、基本数据类型(byte,short,char,int,long,float,double,boolean)他们之间的比较,应用双等号(==),比较的是他们的值。复合数据类型(类) == 比较两个对象地址是否相同,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。不可重写
2、.equals()比较两个对象是否相同,如果两个对象所有字段都相等,返回true,否则返回false。程序员可重写方法自定义比较
3、hashCode() 生成对象hash值,一般在.equals()前执行,为了节约比较地址带来的系统开销。
拓展1:
在集合操作的时候有如下规则:
将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
例子:HashSet类就是这么实现的
如果两个对象根据equals()方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
如果两个对象根据equals()方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定要产生相同的整数结果
拓展2:
java中每个类都继承与object类,Object类中就有equals方法。
如果不复写equals()方法的话,下面是默认Object类中的equals方法。
public boolean equals(Object obj) {
return (this == obj);
}
可以看到默认情况下比较两个对象是否相同其实就是比较对象地址是否相同
当然也会在特定的业务场景的场景下,存在复写equals方法的情况。比如String的equals()的方法就是重写了的
String equals方法
public boolean equals(Object anObject) {
if (this == anObject) {//1
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {//2
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//3
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
1.如果比较的对象和当前对象是同一个对象,返回true
2.比较的String长度,如果与当前长度不相同则不同
3.因为String的数据结构使用的是char的数组。循环遍历char数组,当遇到相同位置char不同就返回false,全部相同则返回true。
String的equals()比较的是两个String的字面量是否相同。
我们都知道比较两个对象的地址对系统消耗太大,所以我们不希望每次比较对象是否相同都比较地址。
这时候就会用到对象的的hash(散列)值,每当jvm创建一个对象都会为他创建一个独一无二的hash值。
如果两个对象的hash值不同代表他们肯定不是同一个对象
如果两个对象的hash值相同,他们仍然可能不是同一个对象(概率很低),这个时候在进行地址比较判断是否是相同对象。
String hashCode方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
因此:
重写其中一个方法.equals(),hashCode() 都需要重写另一个方法
他们是搭档起来就是判断对象是否相同(hashCode()在前,.equals()在后)
参考大神博客:https://www.cnblogs.com/Gang-Bryant/p/10853772.html
String与常量池(JDK1.8):https://www.cnblogs.com/liangyueyuan/p/9796992.html
软件设计模式,目前主要有23种
1、创建型
创建对象时,不再由我们直接实例化对象;而是根据特定场景,由程序来确定创建对象的方式,从而保证更大的性能、更好的架构优势。创建型模式主要有简单工厂模式(并不是23种设计模式之一)、工厂方法、抽象工厂模式、单例模式、生成器模式和原型模式。
2、结构型
用于帮助将多个对象组织成更大的结构。结构型模式主要有适配器模式adapter、桥接模式bridge、组合器模式component、装饰器模式decorator、门面模式、亨元模式flyweight和代理模式proxy。
3、行为型
用于帮助系统间各对象的通信,以及如何控制复杂系统中流程。行为型模式主要有命令模式command、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式state、策略模式、模板模式和访问者模式。
拓展:
1、单例模式(singleton),有些时候,允许自由创建某个类的实例没有意义,还可能造成系统性能下降。如果一个类始终只能创建一个实例,则这个类被称为单例类,这种模式就被称为单例模式。
一般建议单例模式的方法命名为:getInstance(),这个方法的返回类型肯定是单例类的类型了。getInstance方法可以有参数,这些参数可能是创建类实例所需要的参数,当然,大多数情况下是不需要的
public class Singleton {
public static void main(String[] args)
{
//创建Singleton对象不能通过构造器,只能通过getInstance方法
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//将输出true
System.out.println(s1 == s2);
}
//使用一个变量来缓存曾经创建的实例
private static Singleton instance;
//将构造器使用private修饰,隐藏该构造器
private Singleton(){
System.out.println("Singleton被构造!");
}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义的控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
//如果instance为null,表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法
if (instance == null)
{
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
单例模式主要有如下两个优势:
1) 减少创建Java实例所带来的系统开销。
2) 便于系统跟踪单个Java实例的生命周期、实例状态等。
2、简单工厂(StaticFactory Method)
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。
A实例调用B实例的方法,称为A依赖于B。如果使用new关键字来创建一个B实例(硬编码耦合),然后调用B实例的方法。一旦系统需要重构:需要使用C类来代替B类时,程序不得不改写A类代码。而用工厂模式则不需要关心B对象的实现、创建过程。
代理模式的几种方式:静态代理、动态代理(JDK动态代理-通过实现接口、cglib动态代理-通过继承类)
1、创建多线程的方式
继承Thread类、实现Runable接口、实现Callable接口【重写call()方法】
JAVA中的线程是通过java.lang.Thread类来控制的,一个Thread类的对象代表一个线程,且只能代表一个线程,通过Thread类和它定义的对象,可以获取当前线程对象、获取某一线程的名称,可以实现控制线程暂停一段时间等功能。
第一种创建线程的方法:继承Thread类
方法步骤总结:
(1)定义一个类(如PrimeThread)继承Thread;
(2)重写Thread类中的run方法,将需要被多线程执行的代码存储到该run方法当中。
(3)建立Thread类的子类创建线程对象。
(4)直接调用子类从Thread类继承的start方法,开启一个线程(调用该线程的run方法)。
第二种创建线程的方法:实现Runable接口
Thread类有一个Thread(Runnable target)构造方法,在Runable接口类中只有一个run()方法。当使用Thread(Runnable target)方法创建线程对象时,需要为该方法传递一个实现 Runnable接口的对象,这样创建的线程将调用那个实现了Runnable接口类对象中的run()方法作为其运行代码,而不再是调用Thread类中的run方法了。
(1)定义一个类(如PrimeRun)实现Runnable接口,覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中;
(2)通过Thread类建立线程对象,将Runnable接口的子类实例对象作为实际参数传递给Thread类的构造方法。
两种方式区别:
(1)继承Thread: 线程代码存放Thread子类run方法中,且该run方法被调用。
(2)实现Runnable:线程代码存在实现了Runnable类接口的对象的run方法中,且该run方法被调用。
2、Runnable相比较于Thread的优势
1)可以避免java中的单继承的限制
2)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
3、多线程买多车票问题(使用同步synchronized)
public class ThreadDemo implements Runnable{
private static int ticket = 10;
public void run() {
while(true) {
synchronized(this) {
if(ticket > 0) {
try {
Thread.sleep(1000L);
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() +" is saling ticket "+ ticket--);
}else {
break;
}
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
}
}
4、多线程买多车票问题(使用Lock锁)
从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDemo implements Runnable{
private static int ticket = 10;
//可重入锁
private final ReentrantLock lock = new ReentrantLock();
public void run() {
while(true) {
try {
lock.lock(); //加锁
if(ticket > 0) {
try {
Thread.sleep(10L);
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println(Thread.currentThread().getName() +" is saling ticket "+ ticket--);
}else {
break;
}
} finally {
lock.unlock(); //解锁
}
}
}
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
}
}
5、volatile和synchronized区别
1)volatile本质是在告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取,synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住.
2)volatile仅能使用在变量级别,synchronized则可以使用在变量,方法.
3)volatile仅能实现变量的修改可见性,而synchronized则可以保证变量的修改可见性和原子性.
4)volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞.
5)使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。
6、synchronized和lock区别
1)Lock是一个接口,是Java的一个类,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。
多线程锁:https://blog.csdn.net/suchahaerkang/article/details/80456085
待补充:
object类有哪些常用的方法
HTTP有哪三部分组成(*)
MySQL索引有哪些、适用场景、优缺点(*)
Java工具库有哪些,有哪些好的特性(*)
maven的优点
负载均衡
安全权限认证
消息队列
JAVA类加载器有哪些
算法:
2)匹配(())是否符合规范,栈
JAVA为什么不支持多继承:https://www.cnblogs.com/zqqdbk/p/7117713.html