Java教程

Java基础面试题

本文主要是介绍Java基础面试题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

java基础面试题

  • 一:面向对象的3个特征是什么?
  • 二:Java基础数据类型有哪些?
  • 三:Int和Integer有什么区别?
  • 四:String、StringBuffer 、StringBuilder 区别?
  • 五、Java创建对象有哪几种方式?
  • 六、Java中创建线程有哪几种方式?
  • 七、Java中创建线程池有哪几种方式?
  • 八、sleep()、yield()、join()有什么区别?
  • 九、线程的生命周期包含哪几个状态?
  • 十、Java中线程安全指的是什么?
  • 十一、常用的集合有哪几个?它们有哪几种遍历方式和删除指定?
  • 十二、数组(Array) 与 集合 (List)
  • 十三、Arraylist 与 LinkedList 区别?
  • 十四、http请求的get和post有什么区别?
  • 十五、session和cookie有什么区别?
  • 十六、cookie技术的实现原理
  • 十七、Java中常用的锁机制有哪几种?
  • 十八、多线程中死锁指的是什么?
  • 十九、并发编程中的乐观锁和悲观锁指的是什么?
  • 二十、并发编程中原子性、可见性、有序性指的是什么?
  • 二十一、如何进行JVM的性能调优?
  • 二十二、GC垃圾回收机制是如何实现的?
  • 二十三、什么是反射?它的原理是什么?它有哪些功能?它有哪些用途?
  • 二十四、什么是动态代理(cglib和jdk)?
  • 二十五、什么是分布式事务?它有哪些特性?常见的分布式事务解决方案有哪些?
  • 二十六、Spring中DI/IoC、AOP指的是什么?
  • 二十七、java 常见工厂模式
  • 二:工厂模式
  • 二十八、IO流面试题

一:面向对象的3个特征是什么?

封装
把客观事物封装抽象成类
继承
通过继承实现代码重用,让新定义的类拥有已有类的功能,并进行扩展
多态
通过父类的引用来进行子类方法的调用

二:Java基础数据类型有哪些?

数值型
整型:int、long、short、byte
浮点型:float、double
字符型
char、varchar
布尔型
boolean

三:Int和Integer有什么区别?

int是Java的一个基本数据类型
Integer是它的包装类,属于一个类,用于提供一些必要处理方法,比如Integer.toString(10)、Integer.parseInt(“12”)

四:String、StringBuffer 、StringBuilder 区别?

https://www.cnblogs.com/su-feng/p/6659064.html
即运行速度和线程安全的两个方面。
1、运行速度(String最慢)
运行速度快慢为:StringBuilder > StringBuffer > String
String最慢的原因:
String为字符串常量,即String对象一旦创建之后该对象是不可更改的,而StringBuilder和StringBuffer均为字符串变量,但后两者的对象是变量,是可以更改的。
2、再来说线程安全
在线程安全上,StringBuffer是线程安全的,而StringBuilder是线程不安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,
但是StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。
所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。

3、总结一下
String:String对象创建后对象不可更改,适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

五、Java创建对象有哪几种方式?

new
clone
通过反射
通过序列化

六、Java中创建线程有哪几种方式?

2种方式:
(1)继承Thread类,并重写线程执行体run()方法
(2)实现Runable接口,并实现线程执行体run()方法

七、Java中创建线程池有哪几种方式?

(1)Executors.newFixedThreadPool(int nThreads)
创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程
(2)Executors.newCachedThreadPool()
创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制
(3)Executors.newSingleThreadExecutor()
这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行
(4)Executors.newScheduledThreadPool(int corePoolSize)
创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

八、sleep()、yield()、join()有什么区别?

sleep():
让当前正在执行的线程休眠指定的毫秒数,让其它线程有机会执行,但不释放对象锁,即如果有synchronized同步代码块,其它线程仍然不能访问共享数据。
yield():
与sleep()方法类似,区别在于它没有参数,即yield()方法只是使当前线程重新回到可执行状态。
join():
让一个线程B加入到线程A的尾部,在A执行完毕之前,B不能工作。

九、线程的生命周期包含哪几个状态?

新建new、就绪runnable、运行running、阻塞blocked、死亡dead五种状态:
(1)新建new
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1=new Thread();
(2)就绪runnable
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。
例如:t1.start();
(3)运行running
线程获得CPU资源正在执行任务(即执行run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,否则线程将一直运行到结束。
(4)阻塞blocked
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
例如:调用sleeep()方法、wait()方法等;
正在睡眠:用sleep() 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
(5)死亡dead
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

十、Java中线程安全指的是什么?

线程安全是指要控制多个线程对某个资源的有序访问或修改,确保这些线程之间没有产生冲突。在Java中,通过synchronized关键词,可以实现多个Thread对同一个java实例的访问(read和modify)不会相互干扰。

十一、常用的集合有哪几个?它们有哪几种遍历方式和删除指定?

(1)常用集合有:List、Map、Set等
(2)遍历方式

**List遍历的三种方法:

// 准备数据
List<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println("未删除前list集合:" +  list);

//注意:将要删除的元素用list装起来,等遍历结束再remove掉,否则报错
List<String> removeStrs = new ArrayList<String>();
// 方法一:加强循环for遍历
for (String str : list) {
    //将元素放入需要删除的list集合
    if("ccc".equals(str)){
        removeStrs.add(str);
    }
}
System.out.println("----------------------------");

//删除元素
list.removeAll(removeStrs);
System.out.println("删除后list集合:" +  list);
for (String string : list) {
    System.out.println("删除后循环的list集合:" +  string);
}

// 方法二:普通循环for遍历
for(int i = 0 ; i < list.size() ; i++) {
    System.out.println(list.get(i));
}

// 方法三:iterator迭代器遍历
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}

**Map遍历的四种方式:

// 准备数据
Map<String, Object> map = new HashMap<>();
map.put("name","张三丰");
map.put("age",200);
map.put("sex","男");
map.put("job", "掌门人");

// 第一种方式:通过keySet()遍历
Set<String> keySet = map.keySet();
for (String key:keySet) {
    System.out.println("第一种方式:key="+key+",value="+map.get(key));
}

// 第二种方式:通过entrySet()+while遍历
Iterator<Map.Entry<String,Object>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry<String,Object> entry = iterator.next();
    System.out.println("第二种方式:key="+entry.getKey()+",value="+entry.getValue());
}

// 第三种方式:通过entrySet()+for加强循环遍历
for (Map.Entry<String,Object> entry : map.entrySet()) {
    System.out.println("第三种方式:key="+entry.getKey()+",value="+entry.getValue());
}

// 第四种方式:通过values()循环遍历
for (Object value : map.values()) {
    System.out.println("第四种方式:只能取值,不能取key,value="+value);
}

Set遍历的三种方式:

// 准备数据
Set<String> set = new HashSet<>();
set.add("张三");
set.add("李四");
set.add("王五");

// 第一种方式:加强循环for遍历
for (String value:set) {
    System.out.println("第一种方式:value="+value);
}

// 第二种方式:普通循环for遍历
for (Iterator<String> iterator=set.iterator(); iterator.hasNext();) {
    System.out.println("第二种方式:value="+iterator.next());
}

// 第三种方式:迭代器iterator遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
    System.out.println("第三种方式:value="+iterator.next());
}

关于Set、List、Map的区别:
区别:数组固定长度的,集合的长度是可以变化的。
List:继承Collection,可重复、有序的对象(保证顺序)
Set:继承Collection,不可重复、无序的对象(不保证顺序)
Map:键值对,提供key到value的映射。key无序、唯一;value无序,可重复

十二、数组(Array) 与 集合 (List)

https://www.cnblogs.com/tiandi/p/10641773.html
一:Array数组和List集合的介绍

  1. Array
    Array(数组)是基于索引(index)的数据结构,它使用索引在数组中搜索和读取数据是很快的。
    2.List

List—继承Collection,是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
List是一个接口,不可以实例化, 不能写成如下:List list = new List();//错误
List有两个重要的实现类:ArrayList和LinkedList

十三、Arraylist 与 LinkedList 区别?

https://www.cnblogs.com/otis/p/12846381.html#arraylist-与-linkedlist-区别
https://www.cnblogs.com/tiandi/p/10641773.html
一:ArrayList与LinkedList区别
1、ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于双链表的数据结构;
2、对于随机访问get和set,ArrayList要优于LinkedList,因为LinkedList要移动指针;
3、在数据量很大或者操作很频繁时,ArrayList查询快,增删慢,LinkedList增删快,查询慢

十四、http请求的get和post有什么区别?

get:参数以名值对形式拼接在url后面发送,数据长度受url最大2048个字符限制
post:参数放在请求体中发送

十五、session和cookie有什么区别?

session和cookie都是会话状态保持的一种解决方案,主要有如下两点区别:
存储位置不同:session存储在服务器端,cookie存储在客户端
安全性不同:cookie由于存放在客户端,安全性低于session

十六、cookie技术的实现原理

cookie的原理:
cookie技术是通过在HTTP请求和响应的报文信息中写入cookie信息来实现的
cookie的保存:
客户端根据服务端发送的报文中一个名为set-cookie的字段信息,确定保存cookie
cookie的发送:
客户端在下次发送请求时,会自动在请求报文中加入cookie信息
cookie的读取:
服务端在收到包含cookie的请求时,会识别来着哪个客户端,然后做相应处理

十七、Java中常用的锁机制有哪几种?

常见的锁机制有6种:
(1)独享锁/共享锁
(2)乐观锁/悲观锁
(3)公平锁/非公平锁
(4)可重入锁
(5)分段锁
(6)自旋锁
上述内容出处:https://blog.csdn.net/u010648018/article/details/79750608

十八、多线程中死锁指的是什么?

死锁
线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块。当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁。简单来说就是,你拥有我要的资源,我也拥有你要的资源,相互都不愿意放手,你得不到我的我也得不到你的;说白了就是互相伤害造成的。
例如:如果线程1锁住了A,然后尝试对B进行加锁,同时线程2已经锁住了B,接着尝试对A进行加锁,这时死锁就发生了。线程1永远得不到B,线程2也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A和B),它们将永远阻塞下去。这种情况就是一个死锁。
造成死锁的原因可以概括成三句话:
当前线程拥有其他线程需要的资源
当前线程等待其他线程已拥有的资源
都不放弃自己拥有的资源

四条件
死锁的发生必须具备以下四个必要条件:
1)互斥条件
指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件
指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件
指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件
指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

避免死锁
加锁顺序
加锁时限
死锁检测
说明:此处的死锁针对的是Java多线程领域的,与数据库中事务死锁、操作系统中进程死锁不是同一个概念。

十九、并发编程中的乐观锁和悲观锁指的是什么?

悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

两种锁的使用场景
从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

二十、并发编程中原子性、可见性、有序性指的是什么?

1、原子性(Atomicity)
原子性是指在一个操作中,CPU不可以在中途暂停然后再调度,即操作不被中断,要么执行完成,要不就不执行。
如果一个操作是原子性的,那么多线程并发的情况下,就不会出现变量被修改的情况;比如 :a=0这个操作是不可分割的,那么我们说这个操作时原子操作,而a++这个操作实际是由a = a + 1构成的,是可分割的,所以他不是一个原子操作。
非原子操作都会存在线程安全问题,需要我们使用同步技术(synchronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。
2、可见性(Visibility)
可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。
Java内存模型是通过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方法来实现可见性的,无论是普通变量还是volatile变量都是如此,普通变量与volatile变量的区别是volatile的特殊规则保证了新值能立即同步到主内存,以及每使用前立即从内存刷新。因为我们可以说volatile保证了线程操作时变量的可见性,而普通变量则不能保证这一点。
除了volatile之外,Java还有两个关键字能实现可见性,它们是synchronized、final。同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store和write操作)”这条规则获得的,而final关键字的可见性是指:被final修饰的字段是构造器一旦初始化完成,并且构造器没有把“this”引用传递出去,那么在其它线程中就能看见final字段的值。
3、有序性(Ordering)
即程序执行的顺序性。我们总是以为代码是从前往后依次执行的,在单线程情况下确实是这样;但是在并发程序中可能就会出现乱序,从而导致有序性问题。
有序性可以用一句话总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程中观察另一个线程,所有的操作都是无序的。

二十一、如何进行JVM的性能调优?

首先,在进行JVM性能调优前,需要先了解一下JVM的体系结构,JVM由类加载器、Java堆、Java栈、方法区、本地方法栈、程序计数器、执行引擎等核心部分构成,如下图所示:

JVM调优主要有2个方面:堆大小设置、收集器选择
(1)堆大小设置
JVM中堆大小的最大值收到三方面限制:系统可用物理内存限制、系统可用虚拟内存限制、系统数据模型(32位/64位)限制。在32位系统下,最大值范围一般限制在1.5~2G;在64位系统下,最大值一般不限制;典型的jvm堆大小设置有如下两种:
1)java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM初始内存为3550m;此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G;整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小,持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小,此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小;JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
2)java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4
设置年轻代(包括一个Eden区和两个Survivor区)与年老代的比值(除去持久代);设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4
设置年轻代中Eden区与Survivor区的大小比值;设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m
设置持久代大小为16m。
-XX:MaxTenuringThreshold=0
设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
(2)回收器选择
JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数;JDK5.0以后,JVM会根据当前系统配置进行判断。
并行收集器:吞吐量优先
并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等
并发收集器:响应时间优先
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间,适用于应用服务器、电信领域等
上述答案参考自:https://blog.csdn.net/wuzhilon88/article/details/49201891

二十二、GC垃圾回收机制是如何实现的?

Java垃圾回收机制主要对JVM中的Java堆内存进行回收,清理掉那些无引用的对象。
Java堆内存被划分为年轻代、年老代、永久代三个空间,年轻代和年老代存储动态产生的对象,永久代存放Java类信息,永久代空间几乎不参与垃圾回收。
年轻代分为一个Eden区和两个Survior区,新建的对象都放在Eden区,确保生命周期短的对象尽量留在年轻代中,当Eden区申请不到空间时,执行Minor GC,并把存活的对象拷贝到Survior;年老代主要存放生命周期比较长的对象,如缓存对象。
JVM垃圾回收机制的实现过程如下:
1、对象在Eden区完成内存分配
2、当Eden区满了,再创建对象就会申请不到空间,则触发minor GC,进行young(即回收eden区和survivor 1区的内存)
3、在minor GC时,Eden不能被回收的对象被放入到空的survior(即Eden肯定被清空),另一个survivor里不能被GC回收的对象也会被放入到这个survivor,始终保证一个survivor是空的
4、当完成第三步的时候,如果发现survivor满了,则这些对象被copy到old区,或者survivor并没有满,但有些对象已经足够old了,也被放入到old区,当old区被放满之后,进行full GC

二十三、什么是反射?它的原理是什么?它有哪些功能?它有哪些用途?

(1)什么是反射?
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性;对于任何一个对象,我们都能够对它的属性和方法进行调用。我们把这种动态获取类信息、调用对象方法的功能称之为反射机制。
(2)反射的原理
确定一个对象的类
取出类的修饰符modifiers、属性、方法、构造器、父类/接口
找出某个接口里定义的常量和方法信息
创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象)
取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到
在运行时刻调用动态对象的方法
创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值
(3)反射的功能
在运行时构造一个类的对象;获取方法、属性等类中的基本信息;调用类对象的方法;生成动态代理。
(4)反射的用途
反射最大的用途就是框架,比如:Spring中的Di/IoC、Hibernate中的find(Class clazz)、Jdbc中的classForName()等等,很多开发框架都用到了反射机制
注意:反射还有一个不得不说的问题,就是性能问题,大量使用反射会导致系统性能大打折扣

二十四、什么是动态代理(cglib和jdk)?

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

二十五、什么是分布式事务?它有哪些特性?常见的分布式事务解决方案有哪些?

参考:https://blog.csdn.net/mine_song/article/details/64118963
(1)什么是分布式事务?
分布式事务是指一系列操作分布在分布式系统中不同节点上,并且属于不同的应用,这些操作要么全部成功,要么全部失败,以保证不同数据库的数据一致性。
产生分布式事务的原因,一般是分库分表、应用SOA化/微服务化。
(2)它有哪些特性?
原子性(A):要么全部完成,要么全部失败
一致性(C):数据满足完整性约束,比如A向B转账,账户总额在转账后保持一致
隔离性(I):事务与事务之间不会相互影响
持久性(D):事务一旦执行完成,数据将被永久保存
(3)常见的分布式事务解决方案
基于消息事务+最终一致性:
基于消息中间件的两阶段提交往往用在高并发场景下,将一个分布式事务拆成一个消息事务(A系统的本地操作+发消息)+B系统的本地操作,其中B系统的操作由消息驱动,只要消息事务成功,那么A操作一定成功,消息也一定发出来了,这时候B会收到消息去执行本地操作,如果本地操作失败,消息会重投,直到B操作成功,这样就变相地实现了A与B的分布式事务。基于最终一致性来解决分布式事务存在一定的风险,因为在这种方式中,A和B并不是严格一致的。
基于XA协议的两阶段提交:
XA是一个分布式事务协议,由Tuxedo提出。XA中大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2这些商业数据库都实现了XA接口,而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。XA有致命的缺点,就是性能不理想。
TCC编程模式
所谓的TCC编程模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。

二十六、Spring中DI/IoC、AOP指的是什么?

DI/IoC:
DI即Dependency Injection,依赖注入;IoC即Inversion Of Control,控制反转;它们是同一个概念的不同描述,指的是容器将应用程序依赖的外部资源(对象)在运行期注入进来。
在Spring框架中,Spring Ioc容器(ApplicationContext)负责创建Bean,并通过容器将这些Bean注入到需要它们的消费者Bean中。
AOP:
AOP即Aspect Oriented Programming,面向切面编程;它指的是将那些与业务无关,但是却为业务模块所共同调用的逻辑封装起来,变成一个可重用模块,即切面(Aspect)。
在Spring框架中,通过动态代理技术实现了AOP,并由Spring Ioc容器(ApplicationContext)负责生成和管理AOP的代理。

二十七、java 常见工厂模式

https://www.cnblogs.com/dengshihuang/p/10413261.html
一:单例模式
基本概念:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
常见写法:饿汉式、懒汉式、双重线程检查模式

二:工厂模式

基本概念:为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
分为三类:
简单工厂模式Simple Factory:不利于产生系列产品;
工厂方法模式Factory Method:又称为多形性工厂;
抽象工厂模式Abstract Factory:又称为工具箱,产生产品族,但不利于产生新的产品;

二十八、IO流面试题

这篇关于Java基础面试题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!