Java面试总结记录

List item

分布式事务的解决办法:

# 首先复习下本地数据库事务的四大特性以及隔离性

	* 数据库事务的几个特性:原子性(Atomicity )、一致性( Consistency )、隔离性或独立性( Isolation)和持久性(Durabilily),简称就是ACID。但是再往下比如问到隔离性指的是什么的时候可能就不知道了,或者是知道隔离性是什么但是再问到数据库实现隔离的都有哪些级别,或者是每个级别他们有什么区别的时候可能就不知道了。
	* 现在重点来说明下事务的隔离性,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
	* 1,脏读
		* 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。
		* 当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户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以后再次查看账户时就会发现钱其实并没有转。

	* 2,不可重复读
		* 不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。
		· 例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。
		·不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
		* 在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……

	*3,虚读(幻读)
		* 幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

		* 幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。
		
	* 现在来看看MySQL数据库为我们提供的四种隔离级别:

			* Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

			* Repeatable read (可重复读):可避免脏读、不可重复读的发生。

			* Read committed (读已提交):可避免脏读的发生。

			* Read uncommitted (读未提交):最低级别,任何情况都无法保证。
			
	* 以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
	
	* 在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);
	* 而在Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
	* 有一个知识点我们需要了解,就是假如数据库在提交事务的时候突然断电,那么它是怎么样恢复的呢? 为什么要提到这个知识点呢? 因为分布式系统的核心就是处理各种异常情况,这也是分布式系统复杂的地方,因为分布式的网络环境很复杂,这种“断电”故障要比单机多很多,所以我们在做分布式系统的时候,最先考虑的就是这种情况。这些异常可能有 机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失、其他异常等等...
	* 我们接着说本地事务数据库断电的这种情况,它是怎么保证数据一致性的呢?我们使用SQL Server来举例,我们知道我们在使用 SQL Server 数据库是由两个文件组成的,一个数据库文件和一个日志文件,通常情况下,日志文件都要比数据库文件大很多。数据库进行任何写入操作的时候都是要先写日志的,同样的道理,我们在执行事务的时候数据库首先会记录下这个事务的redo操作日志,然后才开始真正操作数据库,在操作之前首先会把日志文件写入磁盘,那么当突然断电的时候,即使操作没有完成,在重新启动数据库时候,数据库会根据当前数据的情况进行undo回滚或者是redo前滚,这样就保证了数据的强一致性。

现在我们开始介绍分布式事务的解决办法:

	本地消息表(异步确保):
		核心思想是将分布式事务拆分成本地事务进行处理,来源于ebay,它的基本思路如下:
消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。
这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET中 有现成的解决方案。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
			
		消息中间件MQ进行
	* 数据库优化之索引:
	* 数据库切片:
	* sorl:
	* JVM垃圾回收机制:

在这里插入图片描述

* MQ 事务消息

	* 有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
	* 以阿里的 RocketMQ 中间件为例,其思路大致为:
	* 第一阶段Prepared消息,会拿到消息的地址。
	* 第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
	* 也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
	* 遗憾的是,RocketMQ并没有 .NET 客户端。

		优点: 实现了最终一致性,不需要依赖本地数据库事务。

		缺点: 实现难度大,主流MQ不支持,没有.NET客户端,RocketMQ事务消息部分代码也未开源。

基础:

面向对象: 多态、继承、封装、抽象  
* 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类,得到继承信息的类被称为子类(派生类),继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段
* 封装:封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口,面向对象的本质是将现实世界描绘成完全自治、封闭的对象、我们编写一个类就是对数据和数据操作的封装。封装就是隐藏一切可隐藏的东西,只想外界提供最简单的编程接口;
* 多态:多态性是指允许不同类型的子类型对象对同一消息作出不同的响应(一个对象类型的多种实现)。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情,多态性分为编译时的多态性和运行时的多态性。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事: 1. 方法重写(子类继承父类并重写父类中已有的或抽象的方法); 2. 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
* 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。


1. 常见的设计模式有哪些:单例模式,工厂模式,装饰者模式等;
		* 单例模式
			* 饿汉式:
			* public class Singleton{
			* // 直接创建对象
			* public static Singleton instance = new Singleton();
			* // 私有化构造函数
			* private Singleton(){}
			* //返回对象实例
			public static Singleton getInstance(){
			return instance;}}

			* 懒汉式:
			* // 声明变量
			* // volatile :双重校验单例模型,需要在单例实例上加volatile,依此来保证第一次校验的时候不会暴露出一个不完整的单例对象,而导致系统崩溃
			* private static volatile Singleton singleton = null;
			* // 私有构造函数
			* private Singleton() {}
			* // 提供对外方法
			* public static Singleton getInstance(){
				if(singleton ==null){
				synchronized(Singleton.class){
				if(singlleton == null){
				singleton = new Singleton();
			* }}}
			* return singleton();

2.抽象类和接口区别:

* 抽象类:
		* 有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
		* 抽象类中可以包含静态方法
	*接口:接口中不能有静态方法
		* 一个类可以实现多个接口
		* 方法全部都是抽象方法
	总结:
		 * 抽象类里抽象方法必须被子类实现,抽象类可以继承多个抽象类,普通类只能单继承抽象类,不能有主方法,可以有普通方法,抽象方法默认被public abstract修饰
	
		 * 接口类里只能写抽象方法,属性默认被public static final修饰,多个接口可以被同一类实现

3.Java 8的新特性

 * 1.Java 8引入了函数式接口的概念。Lambda允许把函数作为一个方法的参数,或者把代码看成数据。

 * 2.接口的默认方法与静态方法,在接口中定义默认方法,使用default关键字,并提供默认的实现。所有实现这个接口的类都会接受默认方法的实现,除非子类提供的自己的实现,在接口中定义静态方法,使用static关键字,也可以提供实现

 * 3.方法引用,结合Lambda表达式联合使用

 	* 构造器引用。语法是Class::new 

 	* 静态方法引用。语法是Class::static_method

 	* 特定类的任意对象方法引用。它的语法是Class::method

 	* 特定对象的方法引用,它的语法是instance::method

 * 4.Java 8引入重复注解,相同的注解在同一地方可以声明多次。重复注解机制本身需要用@Repeatable注解。Java 8在编译器层做了优化,相同注解会以集合的方式保存,因此底层的原理并没有变化

 * 5.扩展注解的支持,java 8扩展了注解的上下文,几乎可以为任何东西添加注解,包括局部变量、泛型类、父类与接口的实现,连方法的异常也能添加注解

 * 6.引入Optional类,防止空指针异常,Optional类实际上是个容器:它可以保存类型T的值,或者保存null。使用Optional类我们就不用显式进行空指针检查了

 * 7.引入Stream API ,函数式编程风格,让代码变得连串支持连续、并行聚集操作,简单明了

 * 8.JavaScript引擎Nashorn,Nashorn允许在JVM上开发运行JavaScript应用,允许Java与JavaScript相互调用。

 * 9.Base64,Base64类提供了对URL、MIME友好的编码器与解码器

 * 10.Date/Time API (JSR 310),提供了新的java.time包,可以用来替代 java.util.Date和java.util.Calendar,一般会用到Clock、LocaleDate、LocalTime、LocaleDateTime、ZonedDateTime、Duration这些类,对于时间日期的改进还是非常不错的;

	* 用 lambda 表达式实现
	* 使用 Java 8 lambda 表达式进行事件处理

4.Java数组和链表的两种结构的操作效率

	* 数组效率高,数组底层是一个连续的内存空间,根据基地址和偏移量计算地址的;

	* 链表的数据是通过地址指向下一个数据地址找到的;

	* List、Set、Map是否继承自Collection接口?
		* List,Set是,Map不是
		* List、Map、Set三个接口存取元素时,各有什么特点? 

		*List允许数据重复,有序的,调用get(index i)来明确说明取第几个。

		*Set不允许重复数据,内部有排序,只能以Iterator接口取得所有的元素,再逐一遍历各个元素

		*Map是通过键值对存储数据,键唯一的,相同数据会覆盖,用get(Object key)方法根据key获得相应的value

5.线程同步以及线程调度相关的方法

	* wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁

	* sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常

	* notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,唤醒等待状态的线程不确定

	* notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
	
	5.1编写多线程程序有几种实现方式? 
		java5 以前第一种是继承Thread类(单继承不推荐)、第二种实现Runnable接口,重写run,java5以后可以实现Callable接口,重写call函数
		
	5.2简述synchronized 和java.util.concurrent.locks.Lock的异同? 

		相同点:Lock有synchronized所有功能

		不同点:Lock比synchronized性能更好,线程语义更精准,synchronized是自动释放锁,Lock必须要在finally手动释放

6.ArrayList和HashSet的区别,HashMap和Hashtable的区别?

	* ArrayList是实现List接口,HashSet是实现Set接口

	* ArrayList是数组存储。HashSet存储对象,具有HashMap的特性。

	* ArrayList是有序可重复,HashSet是无序不可重复判断重复的标准是 equals方法 /hashCode方法

	* HashSet和HashMap都是线程不安全的

	* HashMap实现Map接口,HashSet是实现Set接口

	* HashMap是键值对存储,键不能重复,值可以重复,而且查询速度比HashSet快,通过键查询值;

RESTful风格

可译为"表现层状态转化”。
REST最大的几个特点为:
资源、统一接口、URI和无状态。

资源:

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。资源总要通过某种载体反应其内容,文本可以用txt格式表现,也可以用HTML格式、XML格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现;JSON是现在最常用的资源表示格式。
结合我的开发实践,我对资源和数据理解如下:
充分利用 HTTP 协议本身语义。

浅谈总结:
无状态,这点非常重要。在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了复杂度。
HTTP 本身提供了丰富的内容协商手段,无论是缓存,还是资源修改的乐观并发控制,都可以以业务无关的中间件来实现。

    1. 轻量,直接基于http,不在需要任何别的诸如消息协议。get/post/put/delete为CRUD操作
    1. 面向资源,一目了然,具有自解释性。
    1. 数据描述简单,一般以xml,json做数据交换。
      总的来说就是一个适用于简单操作的接口规范而已,无规矩不成方圆,复杂操作并不适用,还是看业务发展需求的
      适合CRUD并且只适合CRUD,有的浏览器可能不支持POST、GET之外的提交方式,要特殊处理,API容易给让误解中能进行增、删、查、改等操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值