集合相关问题:
1、HashMap、LinkedHashMap、ConcurrentHashMap、ArrayList、LinkedList、TreeMap底层实现?
- HashMap:底层是数组与链表,当链表长度达到8,会转换成红黑树
- LinkedHashMap:继承HashMap,但是内部维持了一个双向链表,可以保持顺序
- ConcurrentHashMap:1.7之前为数组+分段锁,1.8后为数组+链表+红黑树的实现方式来设计,内部大量采用CAS操作
- ArrayList:底层数据结构是数组
- LinkedList:底层数据结构是链表
2、HashMap和HashTable的区别?
- HashMap线程不安全
- HashTable线程安全,实现方法里面都添加了synchronized关键字来确保线程同步。
3、ArrayList、LinkedList、Vector的区别?
- ArrayList 线程不安全、底层数据结构是数组、无序、不唯一。
- Vector 线程安全
- LinkedList 线程不安全
4、HashMap和ConcurrentHashMap的区别?
- HashMap线程不安全
- ConcurrentHashMap线程安全、采用分段锁
5、HashMap和LinkedHashMap的区别?
- HashMap Key和Value都可以为空
- LinkedHashMap Key和Value都不可以为空 能用于保证FIFO既有序的集合(先进先出)
6、HashMap是线程安全的吗?
- 线程不安全
7、ConcurrentHashMap是怎么实现线程安全的?
- 在Java 1.7之前使用的是分段锁技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一段数据的时候,其他段的数据也能被其他线程访问。Java1.8之后使用的是CAS + Synchronized来保证并发更新的安全,底层采用数组 + 链表 + 红黑树的存储结构
线程相关问题
1、创建线程的3种方式?
- 继承Thread
- 实现Ruuabled
- 通过Callable和Future创建线程
2、什么是线程安全?
- 是指当多个线程访问同一个变量时,该变量不会因为多线程访问产生意想不到的问题
3、Runnable接口和Callable接口的区别?
- Runnable 没有返回值;
- Callable 有返回值
4、wait方法和sleep方法的区别?
- wait 是等待,释放了锁,其他线程可以使用同步代码
- sleep 休眠,没有释放锁
5、synchronized、Lock、ReentrantLock、ReadWriteLock?
- synchronized是关键字属于JVM层面 底层是通过(monitor对象来完成)、synchronized不需要用户去手动释放锁,synchronized不可中断,除非异常或者运行完成。synchronized是非公平锁
- Lock 是一个锁的接口,Lock有个实现类ReentranLock
- ReentrantLock 重入锁 需要手动释放锁若没有主动释放锁,就有可能导致出现死锁现象。ReentrantLock默认非公平锁,构造方法传入boolean值,true为公平锁,false非公平锁
- ReadWriteLock 它也是个接口 读写锁 它可以实现读写锁,当读取的时候线程会获得read锁,其他线程也可以获得read锁同时并发的去读取,但是写程序运行获取到write锁的时候,其他线程是不能进行操作的,因为write是排它锁
6、介绍下CAS(无锁技术)?
- 比较并替换。思想很简单,三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false
- ABD问题 比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。
尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
可以通过AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。
7、什么是ThreadLocal?
- 通常情况下,我们创建的变量是可以被任何一个线程访问并修改的,而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改
8、创建线程池的4种方式?
- newFixedThreadPool 创建固定数目线程的线程池
- newCachedThreadPool 创建一个可缓存的线程池
- newSingleThreadExecutor 创建一个单线程化的Executor
- newScheduledThreadPool 创建一个支持定时及周期性的任务执行的线SpringMVC程池,多数情况下可用来替代Timer类
9、ThreadPoolExecutor的内部工作原理?
- 1、在创建了线程池后,等待提交过来的任务请求
- 2、当调用execute()方法添加一个请求任务时,线程会做出如下判断:
- 2.1、如果正在运行的线程数量小于,那么会马上创建线程运行这个任务
- 2.2、如果正在运行的线程数量大于,那么将这个任务放入队列中
- 2.3、如果这时候队列满了且正在运行的数量小于那么还是要创建非核心线程立刻运行这个任务
- 2.4、如果队列满了且正在运行的线程数量大于或等于,那么线程池会启动饱和拒绝策略来执行
- 3、当一个线程完成任务时,它从队列中取下一个任务来执行
- 4、当一个线程无事可做超过一定的时间(keepAliveTime)时,线程池会判断:
如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉
所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小
10、分布式环境下,怎么保证线程安全?
- 使用分布式锁:
1、Memcached分布式锁
利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下才能add成 功,也就意味着线程得到了锁2、Redis分布式锁
和Memcached的方式类似,利用Redis的setnx命令。此命令同样是原子性操作,只有在key不存在的情况下,才能set成功。(setnx命令并不完善)
11、Threadlocal怎么解决线程同步问题?
- ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
12、公平锁、非公平锁的区别
- 公平锁:是指多个线程按照申请锁的顺序来获取锁
- 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。
13、可重入锁
- 可重入锁也称递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法户自动获取锁。也即是说线程可以进入任何一个它已经拥有的锁所同步着的代码块
14、ReentrantLock的优点和缺点?
- 优点:
- 可以添加多个检控条件,如果使用synchronized,则只能使用一个,使用Reentrant Locks可以有多个 wait()/notify() 队列(多new 几个ReentrantLock就可以用多个 wait()/notify()了)
- 可以控制线程得到锁的顺序,也就是公平锁(按照进入顺序得到资源),也可以设置为非公平锁,也就是不按顺序执行,典型的非公平锁就是synchronized。
- 可以查看锁的状态,锁是否被锁上了
- 可以查看当前有多少线程再等待锁、
- 缺点:
- 需要使用import 引入相关的Class
- 不能忘记在finally 模块释放锁
- synchronized可以放在方法的定义里面, 而reentrantlock只能放在块里面. 比较起来, synchronized可以减少嵌套
15、我们面对ReentrantLock和synchronized改如何选择?
- 1.Synchronized相比Lock,为许多开发人员所熟悉,并且简洁紧凑,如果现有程序已经使用了内置锁,那么尽量保持代码风格统一,尽量不引入Lock,避免两种机制混用,容易令人困惑,也容易发生错误。
- 2.在Synchronized无法满足需求的情况下,Lock可以作为一种高级工具,这些功能包括“可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁”否则还是优先使用Synchronized。
- 3.最后,未来更可能提升Synchronized而不是Lock的性能,因为Synchronized是JVM的内置属性,他能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步,而如果基于类库的锁来实现这些功能,则可能性不大。
16、synchronized和java.util.concurrent.locks.Lock的异同?
- Lock 和 synchronized 有一点明显的区别 ——lock 必须在finally块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放。而synchronized JVM将确保锁会自动释放
- synchronized 代码块不能够保证进入访问等待的线程的先后顺序,而Lock可以
17、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
- 1、乐观锁,每次操作时不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止
- 2、悲观锁是会导致其他所有需要锁的线程挂起,等待持有锁的线程释放锁
- 3、乐观锁可以使用volatile + CAS原理实现,带参数版本来避免ABA问题,在读取和替换的时候进行判定版本是否一致
- 4、悲观锁可以使用synchronize的以及Lock
18、SynchronizedMap和ConcurrentHashMap(效率高)有什么区别
- SynchronizedMap这个类实现了Map接口,在调用方法时使用Synchronized来保证线程同步,当然了实际上操作的还是我们传入的HashMap实例,简单的说就是Collections.synchronizedMap()方法帮我们在操作HashMap时自动添加了synchronized来实现线程同步,类似的其它Collections.synchronizedXX方法也是类似原理)。
- ConcurrentHashMap 在1.7之前使用的是分段锁,1.8之后则是使用 CAS + synchronized,当然底层采用数组 + 链表 + 红黑树的存储结构。推荐使用
19、CopyOnWriteArrayList可以用于什么应用场景
- 多读少些的场景,读写并不是在同一个对象上。在写时会大面积复制数组,所以写的性能差,在写完成后将读的引用改为执行写的对象。CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException。在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。
JVM相关问题
1、介绍下垃圾收集机制(在什么时候,对什么,做了什么)?
- 什么时候:GC触发的条件有两种:①、程序调用System.gc时可以触发;②、系统自身判断GC的依据:根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程。
- 对什么:GC操作的对象分为:通过可达性分析法无法搜索到的对象和可以搜索到的对象,对于搜索不到的对象进行标记
- 做了什么:对于可以搜索到的对象进行复制操作,对于搜索不到的对象,调用finalize()方法进行释放。
2、垃圾收集有哪些算法,各自的特点?
- 引用计数:每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时即回收。此方法简单,无法解决对象互相引用的问题,且每次对象引用时要维护引用计数器,这样计数器本身也有一定的消耗。JVM的实现一般不采用这种方式
- 复制算法:复制算法将可用内存按照容量划分为大小相等的两块,每一次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另一块内存上去,然后再把已经使用过的内存空间一次清理掉
- 标记清理:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
- 标记整理:分为标记和整理两个阶段:首先标记出所有需要回收的对象,让所有存活的对象都向一端移动,然后直接清理掉边界意外的内存
-
分代收集算法:目前商用虚拟机都使用“分代收集算法”,所谓分代就是根据对象的生命周期把内存分为几块,一般把Java堆中分为新生代和老年代,这样就可以根据对象的“年龄”选择合适的垃圾回收算法。
- 新生代:“朝生夕死”,存活率低,使用复制算法。
- 老年代:存活率较高,使用“标记-清除”算法或者“标记-整理”算法。
3、类加载的过程。双亲委派模型?
- 类加载的过程:
- 加载:将Java源代码编译后的.class字节码文件以二进制的方式加载进内存
- 连接:
- 验证:验证加载进来的二进制流是否符合虚拟机的规范,不会危害虚拟机自身安全
- 准备:给类变量(静态变量)赋予初始值,基本数据/引用类型数据
- 解析:给字符串引用转换为直接引用
- 初始化:变量赋予初始值、执行静态语句块、执行构造函数等待
- 双亲委派模型:
- 双亲委派模型的工资流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类
- 1、当AppClassLoader加载一个Class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
- 2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader 去完成
- 3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载
- 4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException
- 双亲委派模型意义:
- 系统类防止内存中出现多份同样的字节码
- 保证Java程序安全稳定运行
- 双亲委派模型的工资流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类
4、有哪些类加载器?
- 启动类加载器(Bootstrap classLoader):又称为引导类加载器,由C++编写,无法通过程序得到。主要负责加载JAVA中的一些核心库类,主要是位于<JAVA_HOME>/lib/rt.jar中
- 扩展类加载器(Extension classLoader):主要加载JAVA中的一些扩展类,位于<JAVA_HOME>/lib/ext中,是启动类加载器的子类
- 应用类加载器(AppClassLoader classLoader):又称为系统加载器(System classLoader),主要用于加载CLASSPATH路径下我们自己写的类,是拓展类加载器的子类
5、新生代老年代分别使用什么算法?为什么
- 新生代使用复制算法 因为新生代的对象80%都是可以回收的
- 老年代使用标记整理算法 因为对象存活率高,没有额外空间对他进行分配担保。
6、什么是类的加载?
- 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.class 对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
7、JVM内存结构?
-
虚拟机栈(线程私有):Java线程执行方法的内存模型,一个线程对应一个栈,每个方法在执行的同时都会创建一个栈帧(用于存储局部变量表,操作数栈,动态链接,方法出口等信息)不存在垃圾回收问题,只要线程一结束该栈就是否,生命周期和线程一致
- 变量表:存储变量名
- 操作数栈:虚拟机把操作数栈作为工作空间,执行运算,最后把结果再次压入到操作数栈中。
- 方法出口:return 出的数据
- 动态链接:每一个栈帧内部都包含一个指向运行时常量池的引用来支持当前方法的代码实现动态链接。在 Class 文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用来表示的,动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接
- 栈帧:每当线程调用当前方法时,都会将,新栈压入,成为当前帧,JVM会使用它来存储形参,局部变量,中间运行结果等。(每个方法都有一个栈帧,里面放数据)
方法区(线程共享):类的所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池都存在方法区中,虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non - Heap(非堆),目的应该是与Java堆区分开来
堆(线程共享):虚拟机启动时创建,用于存放对象实例,几号所有的对象(包含常量池)都在堆上分配内存,当对象无法再该空间申请到内存时将抛出OutOfMemoryError异常。同时也是垃圾收集器管理的主要区域。可通过 -Xms -Xms 参数来分别指定最大堆和最小堆
本地方法栈(线程私有):登记native方法,在Execution Engine执行时加载本地方法库
程序计数器(线程私有):就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。
数据库相关问题
1、有没有SQL优化经验?
- (尽量避免全表搜索,多使用索引搜索的条件)
- 1、对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by涉及的列上建立索引
- 2、应尽量避免在 where 子句中使用 !=或<> 操作符,否则将引擎放弃使用索引而进行全表扫描
- 3、应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
- 4、in 和 not in 也要慎用,否则会导致全表扫描,对于部分业务可以使用between
- 5、应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描
- 6、避免 select * 语句 具体指出需要的字段
- 7、SQL大小写,数据库在解析sql时,先把SQL语句转换为大写,再进行解析操作,所以建议直接提供大写的SQL语句。
- 8、模糊查询时,like "%abc%",前模糊会导致索引失效,后模糊不会导致索引失效。
2、Mysql索引的数据结构?
- 1、顺序查找: 最基本的查询算法-复杂度O(n),大数据量此算法效率糟糕
- 2、二叉树查找(binary tree search): O(log2n),数据本身的组织结构不可能完全满足各种数据结构。
- 3、hash索引 无法满足范围查找。哈希索引基于哈希表实现,只有精确匹配索引所有列的查询才有效。
- 4、二叉树、红黑树 [复杂度O(h)]导致树高度非常高(平衡二叉树一个节点只能有左子树和右子树),逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,IO次数多查找慢,效率低。todo 逻辑上相邻节点没法直接通过顺序指针关联,可能需要迭代回到上层节点重复向下遍历找到对应节点,效率低
- B-Tree:
- B-TREE 每个节点都是一个二元数组: [key, data],所有节点都可以存储数据。key为索引key,data为除key之外的数据。
- 检索原理:首先从根节点进行二分查找,如果找到则返回对应节点的data,否则对相应区间的指针指向的节点递归进行查找,直到找到节点或未找到节点返回null指针。
- 缺点:1.插入删除新的数据记录会破坏B-Tree的性质,因此在插入删除时,需要对树进行一个分裂、合并、转移等操作以保持B-Tree性质。造成IO操作频繁。2.区间查找可能需要返回上层节点重复遍历,IO操作繁琐
- B+Tree: B-Tree的变种:
- 与B-Tree相比,B+Tree有以下不同点:非叶子节点不存储data,只存储索引key;只有叶子节点才存储data
- Mysql中B+Tree:在经典B+Tree的基础上进行了优化,增加了顺序访问指针。在B+Tree的每个叶子节点增加一个指向相邻叶子节点的指针,就形成了带有顺序访问指针的B+Tree。这样就提高了区间访问性能
3、Explain的Type列有哪几种值?
- 对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。(从上到下,性能从差到好)
- ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行
- index:Full Index Scan,index与ALL区别为index类型只遍历索引树
- range:只检索给定范围的行,使用一个索引来选择行
- ref:表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
- eq_ref:类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
- const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system
- NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成
4、SQL关键字的执行顺序?
- 1、from
- 2、on
- 3、join
- 4、where
- 5、group by
- 6、having
- 7、select
- 8、distinct(去重)
- 9、union
- 10、order by
5、有哪几种索引?
- 普通索引:仅加速查询
- 唯一索引:加速查询 + 列值唯一(可以有null)
- 全文索引:对文本的内容进行分词,进行搜索
- 主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
- 组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
6、什么时候该(不该)建索引?
- 1、唯一性太差的字段不适合做索引
- 2、更新太频繁地字段不适合做索引
- 3、不在where 条件后的字段不适合做索引
7、Explain包含哪些列?
- id:SELECT识别符。这是SELECT的查询序列号
- select_type:查询中每个select子句的类型
- table:显示这一步所访问数据库中表名称
- type:对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。
- possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)
- Key:key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中
- key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)
- ref:列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
- rows: 估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
- Extra:MySQL解决查询的详细信息
8、数据库事务中的四大特性ACID
- 原子性(Atomicity):原子性要求事务是一个不可分割的执行单元,事务中的所有操作要么全部执行,要么全部不执行。
- 一致性(Consistency):一致性要求,事务在开启前和结束后,数据库的完整性约束没有备破坏
- 隔离性(Isolation):事务的执行时相互独立的,它们不会互相干扰,一个事务不会看到另一个正在运行过程中的事务数据
- 持久性(Durability):持久性要求,一个事务完成之后,事务的执行结果必须是持久化保存的。即时数据库发生崩溃,在数据库恢复后事务提交的结果仍然不会丢失
框架相关问题
1、Hibernate和Mybatis的区别?
- 两个都是ORM 对象关系映射框架
- Hibernate 包含了完整得到 对象关系映射,在SQL语句上是比较耗性能
- Mybatis 手动写SQL可以对SQL语句进行优化
2、Spring MVC和Struts2的区别?
- 拦截器:Struts2 是类拦截器,一个类对应一个request上下文、Spring MVC是方法拦截器,一个方法对一个request上下文,而方法又跟一个url对应,所以,从架构本身老看,SpringMVC 就更容易实现restful url
3、Spring用了哪些设计模式?
- 工厂模式:BeanFactory、ApplicationContext创建中
- 模板模式:BeanFactory、ApplicationContext实现中
- 代理模式:在AOP实现中用到了JDK的动态代理
- 策略模式:加载资源文件的方式;AOP实现中采用了两种不同的代理,如JDK代理和CGLIB代理
- 单例模式:创建bean
4、Spring中AOP主要用来做什么?
- 事务
- 日志
- 权限
5、Spring注入bean的方式?
- @Component:标识它是一个受spring容器管理的基本组件
- @Respository:标识它是一个持久层的组件
- @Service:标识业务层组件
- @Controller:标识控制层的组件
6、什么是IOC,什么是依赖注入?
- IOC 控制反转,不是什么技术,而是一种思想,IOC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
- DI(依赖注入)在Spring创建对象的过程中,把对象依赖的属性注入到类中。
7、Spring是单例还是多例,怎么修改?
- 默认单例
8、Spring事务隔离级别和传播性?
- 事务传播属性:
- ①、REQUIRED(默认属性):如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
- ②、MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
- ③、NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
- ④、NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- ⑤、REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
- ⑥、SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- ⑦、NESTED:支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
- PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。
使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。 使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
- 事务隔离级别与事务并发引起的三种情况:
- 事务并发 :
- Dirty Reads 脏读:一个事务正在对数据进行更新操作,但是更新还未提交,另一个事务这时也来操作这组数据,并且读取了前一个事务还未提交的数据,而前一个事务如果操作失败进行了回滚,后一个事务读取的就是错误数据,这样就造成了脏读。
- Non-Repeatable Reads 不可重复读 :一个事务多次读取同一数据,在该事务还未结束时,另一个事务也对该数据进行了操作,而且在第一个事务两次次读取之间,第二个事务对数据进行了更新,那么第一个事务前后两次读取到的数据是不同的,这样就造成了不可重复读。
- Phantom Reads 幻像读 :第一个数据正在查询符合某一条件的数据,这时,另一个事务又插入了一条符合条件的数据,第一个事务在第二次查询符合同一条件的数据时,发现多了一条前一次查询时没有的数据,仿佛幻觉一样,这就是幻像读。
- 隔离级别:
- DEFAULT(默认):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与JDBC的隔离级别相对应。
- READ_UNCOMMITTED(读未提交):这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
- READ_COMMITTED(读已提交): 保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。
- REPEATABLE_READ(可重复读):这种事务隔离级别可以防止脏读、不可重复读,但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。
- SERIALIZABLE(串行化):这是花费最高代价但是最可靠的事务隔离级别,事务被处理为顺序执行。除了防止脏读、不可重复读外,还避免了幻像读。
- 事务并发 :
9、介绍下Mybatis/Hibernate的缓存机制?
- Mybatis 一级缓存:SqlSession级别的缓存,缓存的数据只在SqlSession内有效(SqlSession可以人为清除)默认开启的,且不可关闭。二级缓存: mapper级别的缓存,同一个namespace公用这一个缓存,所以对SqlSession是共享的;二级缓存默认是没有开启的。需要配置开启二级缓存
- Hibernate 一级缓存:Session缓存Session缓存是事务级缓存。伴随着事务的开启而开启,伴随着事务的关闭而关闭(即肯定会用到的)二级缓存:SessionFactory 缓存分为内置缓存和外置缓存。 外置缓存是一个可配置的插件,一般不会启用
10、Mybatis的mapper文件中#和$的区别?
- # String mysql = 1 ;select * from aa where a = #{mysql} 输出 select * from aa where a = 1
- $ String mysql = 1 ;select * from aa where a = ${mysql} 输出 select * from aa where a = mysql
- 但是,我们一般推荐使用的是#{},不使用${}的原因如下:会引起sql注入,因为{ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换{ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换
11、Mybatis的mapper文件中resultType和resultMap的区别?
- resultMap:将sql查询结果映射为java对象
- resultType :指定输出结果的类型(pojo、java基本数据类型、hashmap…)将sql查询结果映射为java对象
12、Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的,其内部是怎么实现的
- 通过JDK的代理类实现的
13、什么是Bean
- 在 Spring 中,构成应用程序主干并由Spring IOC容器管理的对象称为bean。bean是一个由Spring IOC容器实例化、组装和管理的对象。
14、@Autowired的实现原理
15、@autowired和@resource的区别
- @Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
- 共同点:@Resource和@Autowired都可以作为注入属性的修饰,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换,不影响使用
- 不同点:@Resource是Java自己的注解,@Resourece 有两个属性分别是name和type。Spring将@Resource 注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略
@Autowired是spring的注解,是spring2.5版本引入的,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰
16、@Qualifier用法,什么时候用?
- @Qualifier 是得跟 @Autowired 一起使用的,当@Autowired注解一个service的时候,恰好这个service被多个类实现,所以spring就不知道应该绑定哪个了,这时候加上@Qualifier("service2") 就表示使用的是 @Service("service2") 这个实现类了。
17、BeanFactory和ApplicationContext有什么区别?
- BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
- BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理Bean的加载、实例、控制Bean的生命周期,维护Bean之间的依赖关系。ApplicationContextApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
①继承MessageSource,因此支持国际化。
②统一的资源文件访问方式。
③提供在监听器中注册bean的事件。
④同时加载多个配置文件。
⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。 - BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常
- ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
- 相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
- BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
- BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册
18、Spring Bean的生命周期?
- 1、实例化Bean:对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
- 2、设置对象属性(依赖注入):实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
-
3、处理Aware接口:接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
- ①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值
- ②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身
- ③如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
- 4、BeanPostProcessor:如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用postProcessBeforeInitialization(Object obj, String s)方法。由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术
- 5、InitializingBean 与 init-method:如果Bean在Spring配置文件中配置了init - method属性,则会自动调用其配置的初始化方法
- 6、如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
- 7、DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
- 8、destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
19、Servlet的生命周期?
- 实例化
- 初始化init
- 接收请求service
- 销毁destroy
20、解释Spring支持的几种bean的作用域?
- singleton:默认,每个容器中只有一个Bean的实例,单例的模式由BeanFactory
- prototype:为每一个Bean请求提供一个实例
- request:为每一个网络请求创建一个实例,在请求完成之后,Bean会失效并被垃圾回收器回收
- session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session: 全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
21、有状态的bean和无状态的bean的区别?
- 有状态:就是有实例变量的对象,可以保持数据(类似set的类),是非线程安全的,每个用户有自己特有的一个实例,在用户的生存期内,bean保持了用户的信息,即"有状态";一旦用户灭亡(调用结束或实例结束),bean的生命期也告结束。即每个用户最初会得到一个初始的bean
- 无状态:就是没有实例变量的对象,不能保存数据(单纯的类似输出类),是不变类,是线程安全的。bean一旦实例化就被加进会话池中,各个用户都可以共用。即使用户已经消亡,bean 的生命期也不一定结束,它可能依然存在于会话池中,供其他用户调用。由于没有特定的用户,那么也就不能保持某一用户的状态,所以叫无状态bean。但无状态会话bean 并非没有状态,如果它有自己的属性(变量),那么这些变量就会受到所有调用它的用户的影响,这是在实际应用中必须注意的。
22、Spring框架中的单例Beans是线程安全的么?
- Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如 View Model 对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
23、SpringMVC的流程?
- 1、用户发送请求至前端控制器DispatcherServlet
- 2、DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle;
- 3、处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DipatcherServlet;
- 4、DispatcherServlet 调用HandlerAdapter处理器适配器;
- 5、HandlerAdapter 经过适配器调用 具体处理器(Handler,也叫后端控制器)
- 6、Handler执行完成返回ModelAndView
- 7、HandlerAdapter 将Handler执行结果ModelAndView返回给DispatcherServlet
- 8、DispatcherServlet 将ModelAndView传给ViewResolver视图解析器进行解析
- 9、ViewResolver解析后返回具体View
- 10、DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
- 11、DispatcherServlet响应用户
24、SpringMVC常用的注解有哪些?
- @RequestMapping:用于处理请求Url映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以改地址作为父路径
- @RequestBody:注解实现接收http请求的json数据,将json转换为java对象
- @ ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户
25、Spring Boot 的核心配置文件有哪几个?它们的区别是什么?
- SpringBoot的核心配置文件是 application 和 bootstrap 配置文件
- application 配置文件这个容易理解,主要用于 Spring Boot 项目的自动化配置
26、Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
- 启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
- @SpringBootConfiguration:组合了 @Configuration 注解,实现配置文件的功能
- @EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
- @ComponentScan:Spring组件扫描
27、@Configuation?
- @Configuration标注在类上,相当于把该类作为spring的xml配置文件中的<beans>,作用为:配置spring容器(应用上下文)
28、Spring Boot 自动配置原理是什么?
- 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下能否有这个类去自动配置。
29、SpringCloud 五大组件:
- Eureka:实现服务治理(服务注册与发现)
- Ribbon:主要提供客户侧的软件负载均衡算法
- Hystrix:断路器,保护系统,控制故障范围。
- Zuul:网关
- Config:配置管理
30、服务注册和发现是什么意思?Spring Cloud如何实现?
- 当我们开始一个项目时,我们通常在属性文件中进行所有的配置。随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂。有些服务可能会下降,而某些位置可能会发生变化。手动更改属性可能会产生问题。 Eureka服务注册和发现可以在这种情况下提供帮助。由于所有服务都在Eureka服务器上注册并通过调用Eureka服务器完成查找,因此无需处理服务地点的任何更改和处理
31、Spring Cloud 什么是Hystrix?它如何实现容错?
- Hystrix是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。
32、Spring AOP / AspectJ AOP的区别?
- Spring AOP属于运行时增强。基于代理
- AspectJ是编译时增强,基于字节码操作
- AspectJ相比与Spring AOP功能更加强大,但是Spring AOP相对来说更简单。如果切面比较少,那么两者性能差异不大。但是,当切面太多的话,最后选择AspectJ,它币SpringAop快很难躲
其他问题
1、介绍下栈和队列?
- 栈 LIFO (后进先出)
- 队列 FIFO(先进先出)
2、IO和NIO的区别?
- IO 面向流、阻塞IO、无选择器
- NIO 面向缓冲、非阻塞IO、有选择器
3、接口和抽象类的区别?
- 不同点:
- ①、抽象类可以有构造方法,接口不能有构造方法;
- ②、抽象类中可以有普通成员变量,接口中没有普通成员变量
- ③、抽象类中可以包含静态方法,接口中不能包含静态方法
- ④、一个类可以实现多个接口,但只能继承一个抽象类
- ⑤、接口可以被多重实现,抽象类只能被单一继承
- ⑥、如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法
- 相同点:
- ①、都不能被实例化
- ②、都可以包含方法声明
- ③、派生类必须实现未实现的方法
4、int和Integer的自动拆箱/装箱相关问题。 常量池相关问题?
- 理解自动装箱、拆箱:
Integer i = 10; //装箱
int n = i; //拆箱
- 思考题
integer a = 100;
integer b = 100;
integer c = 129;
integer d = 129;
System.out.println(a == b); // true
System.out.println(c == d); // false
//为什么 a == b 为true 而 c == b等于false
//因为Integer缓存区是-128到127 超过这个范围比对的就是地址值了
- Integer i = new Integer(1)和Integer i =1;这两种方式的区别:
- 第一种方式不会触发自动装箱的过程;而第二种方式会触发;
- 在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)
5、==和equals的区别?
- == 比较的是值是否相等:
- 基本类型比较的是指,引用地址比较的是地址值
- equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对象:
- 如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址
- String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
6、重载和重写的区别?
- 重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
- 重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表;
7、String和StringBuilder、StringBuffer的区别?
- String:适用于少量的字符串操作的情况,String是常量的,没有字符串拼接的时候都是重新new 一个新对象把之前的 和要拼接的赋值给新对象
- StringBuilder:适用单线程下载字符缓冲区进行大量操作的情况
- StringBuffer:适用多线程下载字符缓冲区进行大量操作的情况
8、静态变量、实例变量、局部变量、常量线程安全吗,为什么。 try、catch、finally都有return语句时执行哪个?
- 静态变量:线程不安全,静态变量即类变量,位于方法区,为所有对象共享,共享一份内存,一旦静态变量被修改,其他对象均对修改可见,故线程非安全。
- 实例变量:单例模式(只有一个对象实例存在)线程非安全,非单例线程安全
- 局部变量:线程安全,每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题。
- 常量:线程安全,常量一旦声明就禁止修改,所以是线程安全的。
- try、catch、finally都有return语句时执行哪个:
- 1、不管有没有出现异常,finally块中代码都会执行
- 2、当try和catch中有return时,finally仍然会执行
- 3、finally是在try中return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的
- 4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值
9、介绍下B树、二叉树?
10、ajax的4个字母分别是什么意思?
- Asynchronous JavaScript and XML 的缩写,异步的JavaScript和XML。在不重新加载整个页面的情况下,AJAX与服务器交互数据并更新部分网页
11、xml全称是什么?
- Extensible Markup Language 可扩展标记语言
12、分布式锁的实现?
- 数据库:
- redis:
13、分布式session存储解决方案?
- 我们使用spring-session以及集成好的解决方案,存放在redis中
14、常用的linux命令?
- 查看端口:netstat -anp | grep 端口号 (状态为LISTEN表示被占用)
- 查看进程:ps -ef | grep xxx ps -aux | grep xxx(-aux显示所有状态)
- 查看系统状态,cpu、内存的使用:top
- 打开(确保有权限前):./startup.sh
- 关闭(确保有权限前):./shutdown.sh
15、HashMap的底层原理
- HashMap底层数据结构在JDK1.7之前是由数组 + 链表组成的。1.8之后加入了红黑树;链表长度小于8的时候,发生Hash冲突后会增加链表的长度,当链表长度大于8的时候,会先判断数组的容量,如果容量小于64会先扩容(原因是数组容量越小,越容易发生碰撞,因此当容量过小的时候,首先要考虑的是扩容),如果容量大于64,则会将链表
16、什么是动态代理?
- 1.代理对象,不需要实现接口
- 2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
- 3.动态代理也叫做:JDK代理,接口代理
- newProxyInstance
17、什么是反射?
- JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象
18、i++ 和 ++i
- i++ 先赋值后++
- ++i 先++后赋值
19、下列代码中,编译正确的是( )?
byte b1 = 1,b2 = 2,b3,b6,b8;
final byte b4 = 4,b5 = 6,b7;
b3 = (b1 + b2); // A
b6 = b4 + b5; // B
b8 = (b1 + b4); // C
b7 = (b2 + b5); //D
//解:B
//两个byte类型相加,结果是int,也就是需要使用int接收
//final定义的变量将不可变
20、java事件机制包括哪三个部分?分别介绍。
- 事件:继承自java.util.EventObject类,封装了事件源对象及事件相关的信息
- 事件监听器:实现java.util. EventListener接口,注册在事件源上,当事件源的属性或状态改变时,取得相应的监听器调用其内部的回调方法
- 事件源:事件发生的地方,由于事件源的某项属性或状态发生了改变(比如BUTTON被单机、TEXTBOX的值发生改变等等)导致某项事件发生。换句话说就是生成了相应的事件对象。因为事件监听器要注册在事件源上,所以事件源类中应该要有盛装监听器的容器(List,Set等等)
21、final finally finalize区别?
-
final:修饰类、方法和变量。
修饰类时:表明该类不能被其他类所继承。当我们需要一个类永远不被继承,此时就可以用final修饰。但要注意:final类中所有的成员方法都会隐式的定义为final方法
修饰方法:①、把方法锁定,以防止继承类对其进行更改 ②、效率,在早期的Java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行优化。final方法意味着“最后的、最终的”含义,即此方法不能被重写。
修饰变量:final成员变量表示常量,只能被赋值一次,赋值后其值不再改变
-
finally:
- finally 作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常)
finalize:finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象)所以一般是不需要程序员去实现finalize的。
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
使用finalize还需要注意一个事,调用super.finalize();
一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。
消息中间件
ActiveMQ:
1、如何防止消息重复发送?
- 解决方法:增加消息状态表
通俗来说就是一个账本,用来记录消息的处理状态,每次处理消息之前,都去状态表中查询一次,如果已经有相同的消息存在,那么不处理,可以防止重复发送
2、如何防止丢消息
- 用持久化消息【可以使用对数据进行持久化JDBC,AMQ(日志文件),KahaDB和LevelDB】,或者非持久化消息及时处理不要堆积,或者启动事务,启动事务后,commit()方法会负责任的等待服务器的返回,也就不会关闭连接导致消息丢失了。
3、activeMQ如何持久化数据?
4、queue 与 topic 区别 ?
-
Queue消息传递模型 点对点(point-to-point,简称PTP):
- 在该消息传递模型下,一个消息生产者向消息服务器一个特定的队列发送消息,一个消费者从该队列中读取消息,
- Topic消息传递模型 发布/订阅(publish/subscribe,简称pub/sub):
5、JMS消息模型
分布式:
1、为什么用微服务?
2、为什么zookeeper能作为注册中心?
3、使用分布式碰到的bug?
4、zookeeper有集群吗?怎么实现的?
5、zookeeper宕机还能访问吗?
6、服务失效踢出zookeeper中临时节点的原理?
7、dubbo集群负载均衡策略?
8、分布式事务解决方案?
-
1、XA(2PC):两阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问各个数据库准备好了吗?假如每个数据库都回答ok,那么就正式提交事务,在各个数据库上执行操作;假如任何其中一个数据库答复不 ok,那么就回滚事务。
- 缺点:缺点:
单点问题:事务管理器在整个流程中扮演的角色很关键,如果其宕机,比如在第一阶段已经完成,在第二阶段正准备提交的时候事务管理器宕机,资源管理器就会一直阻塞,导致数据库无法使用。
同步阻塞:在准备就绪之后,资源管理器中的资源一直处于阻塞,直到提交完成,释放资源。
数据不一致:两阶段提交协议虽然为分布式数据强一致性所设计,但仍然存在数据不一致性的可能,比如在第二阶段中,假设协调者发出了事务commit的通知,但是因为网络问题该通知仅被一部分参与者所收到并执行了commit操作,其余的参与者则因为没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。
总的来说,XA协议比较简单,成本较低,但是其单点问题,以及不能支持高并发(由于同步阻塞)依然是其最大的弱点。
- 缺点:缺点:
-
2、TCC:全称是:Try、Confirm、Cancel。
Try:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留
Confirm:这个阶段说的是在各个服务中执行实际的操作
Cancel:假如任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
这种方案说实话几乎很少人使用,我们用的也比较少,但是也有使用的场景。由于这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常之恶心。
比方说我们,一般来说跟钱相关的,跟钱打交道的,支付、交易相关的场景,我们会用 TCC,严格保证分布式事务要么一律成功,要么一律自动回滚,严格保证资金的正确性,保证在资金上不会出现问题。
而且最好是你的各个业务执行的时间都比较短。-
对于TCC来说适合一些:
- 强隔离性,严格一致性要求的活动业务。
- 执行时间较短的业务
-
distributed-transacion-TCC(本地消息表)
- 当我们本地消息表实现分布式事务的最终一致性的时候,我们其实需要明白我们首先需要在本地数据库新建一张本地消息表,然后我们还必须要一个MQ(不一定MQ,只要是消息中间件即可)
- 消息表怎么创建呢?这个表应该包括这些字段: id, biz_id, biz_type, msg, msg_result, msg_desc,atime,try_count。分别表示uuid,业务id,业务类型,消息内容,消息结果(成功或失败),消息描述,创建时间,重试次数, 其中biz_id,msg_desc字段是可选的。
- 消息生产方(也就是发起方),需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。
- 消息消费方(也就是发起方的依赖方),需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作
- 生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。
- 当我们本地消息表实现分布式事务的最终一致性的时候,我们其实需要明白我们首先需要在本地数据库新建一张本地消息表,然后我们还必须要一个MQ(不一定MQ,只要是消息中间件即可)
-
MQ事务
- 基本流程如下: 第一阶段Prepared消息,会拿到消息的地址。
- 第二阶段执行本地事务。
- 第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。消息接受者就能使用这个消息。
+也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
-
Saga事务
- 其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。
Saga的组成:
每个Saga由一系列sub-transaction Ti 组成
每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果,这里的每个T,都是一个本地事务。
可以看到,和TCC相比,Saga没有“预留 try”动作,它的Ti就是直接提交到库。
Saga的执行顺序有两种:
T1, T2, T3, ..., Tn
T1, T2, ..., Tj, Cj,..., C2, C1,其中0 < j < n
Saga定义了两种恢复策略:
向后恢复,即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Saga的执行结果撤销。
向前恢复,适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, ..., Tj(失败), Tj(重试),..., Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。
这里要注意的是,在saga模式中不能保证隔离性,因为没有锁住资源,其他事务依然可以覆盖或者影响当前事务。
还是拿100元买一瓶水的例子来说,这里定义
T1=扣100元 T2=给用户加一瓶水 T3=减库存一瓶水
C1=加100元 C2=给用户减一瓶水 C3=给库存加一瓶水
我们一次进行T1,T2,T3如果发生问题,就执行发生问题的C操作的反向。
上面说到的隔离性的问题会出现在,如果执行到T3这个时候需要执行回滚,但是这个用户已经把水喝了(另外一个事务),回滚的时候就会发现,无法给用户减一瓶水了。这就是事务之间没有隔离性的问题
可以看见saga模式没有隔离性的影响还是较大,可以参照华为的解决方案:从业务层面入手加入一 Session 以及锁的机制来保证能够串行化操作资源。也可以在业务层面通过预先冻结资金的方式隔离这部分资源, 最后在业务操作的过程中可以通过及时读取当前状态的方式获取到最新的更新。
- 其核心思想是将长事务拆分为多个本地短事务,由Saga事务协调器协调,如果正常结束那就正常完成,如果某个步骤失败,则根据相反顺序一次调用补偿操作。
9、Dubbo和Spring Cloud有什么区别?
- 通信方式不同:
- Dubbo 使用的是RPC通信,而Spring Cloud 使用过的HTTP RESTful方式
- RPC vs REST:
- Dubbo 使用的是RPC通信,而Spring Cloud 使用过的HTTP RESTful方式
10、在微服务与RPC远程调用框架中?负载均衡是如何设计的?
11、为什么微服务与RPC远程调用框架都是使用本地负载均衡,而不是Nginx?
12、软负载均衡与硬负载均衡的区别?
- 软负载:软负载均衡通过服务器端上安装的负载软件或者本地负载算法实现负载均衡功能;例:Nginx、LVS
- 硬负载:F5负载均衡是硬件负载均衡的一种。硬件负载均衡顾名思义,在服务器节点之间安装专门的硬件进行负载均衡的工作
13、微服务架构与SOA架构的区别?
- 1、微服务架构基于SOA架构演变过来,继承SOA架构的优点,在微服务架构中除SOA架构中ESB消息总线,采用http + json(restFul)进行传输
- 2、微服务架构比SOA架构粒度会更加精细,让专业的人去做专业的事(专注),目的提高效率,每个服务于服务之间互不影响,微服务架构中,每个服务必须独立部署,微服务架构更加轻巧,轻量级。
- 3、SOA架构中可能数据库存储会发生共享,微服务架构强调每个服务都是单个数据库,保证每个服务于服务之间互不影响。
- 4、项目体现特征微服务架构比SOA架构更加适合于互联网公司敏捷开发,快速迭代版本。因为粒度非常精细。
14、SpringCloud 如果注册中心因为某个原因,出现故障,应该如何解决?
解决方法:搭建注册中心集群
搭建eureka集群环境,至少2台以上的服务器 思路:eureka搭建集群原理使用相互注册原理,形成一组 相互注册,注册中心。从而实现数据的相互同步实现高可用的效果
15、eureka自我保护机制
- 两种角色:
- Eureka server(注册中心服务端)
- Eureka Client(注册中心客户端)
16、基于Hystrix解决服务雪崩效应原理?
- 服务降级:在高并发情况下
- 服务隔离:
- 服务熔断: