经典面试题

一.equals和==的区别
○1== 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
○2Equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String类的equals方法是被重写过的,而且String类在日常开发中用的比较多,久而久之,形成了equals是比较值的错误观点。
○3具体要看这有没有重写Object的hashCode方法和equals方法来判断。
 
equals的重写
以Student为例,何时需要重写equals()?
当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改
写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例
有可能在逻辑上是相等的,但是,根据Object.hashCode方法,它们仅仅是两
个对象。
  因此,违反了“相等的对象必须具有相等的散列码”。
  结论:复写equals方法的时候一般都需要同时复写hashCode方法
 
问题:为什么用eclipse复写hashCode方法,有31这个数字?
计算机的乘法涉及到移位计算。当一个数乘以2时,就直接拿该数左移一位
即可!选择31原因是因为31是一个素数!所谓素数:质数又称素数(在一个
大于1的自然数中,除了1和此整数自身外,没法被其他自然数整除的数)
在存储数据计算hash地址的时候,我们希望尽量减少有同样的hash地址,所
谓“冲突”。
因为任何数n * 31就可以被JVM优化为 (n << 5) -n,移位和减法的操作效率要比
乘法的操作效率高的多,对左移虚拟机里面都有做相关优化,并且31只占用
5bits!
 
例如
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
System.out.println(s1 == s2);//false
System.out.println(s1.equals(s2));//true
Set<String> set01 = new HashSet<String>();
set01.add(s1);
set01.add(s2);
System.out.println(set01.size());//1
System.out.println("================================");
Person p1 = new Person("abc");
Person p2 = new Person("abc");
System.out.println(p1 == p2);//false
System.out.println(p1.equals(p2));//false
Set<Person> set02 = new HashSet<Person>();
set02.add(p1);
set02.add(p2);
System.out.println(set02.size());//2
}
 
二 String 相关
1.String类不能被继承
2.String类是不可变的
3.常量找池,变量找堆
4.Stirng s1 = new String("abc");
这一句生成了二个对象,分别是abc在常量池中,s1在堆空间中
5.在String拼接字符串时,无论字符串有多长,只要+左右两边又一个变量,则在堆中生成 新的对象
 
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
String s3 = new String("abc");
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s2 == s3);//false
System.out.println("====================");
 
/**返回字符串对象的规范化表示形式。 一个初始为空的字符串池,它由类 String 私有地维护。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。 它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。所有字面值字符串和字符串赋值常量表达式都使用 intern 方法进行操作。字符串字面值在 Java Language Specification 的 §3.10.5 定义。返回:一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。 */
System.out.println(s1 ==s1.intern());//false(后者返回的池中的对象,前者是堆中的对象)
System.out.println(s2 ==s2.intern());//true
System.out.println(s1.intern() ==s2.intern());//true(二者都是池中的对象)
System.out.println("====================");
String s4 = "java";
String s5 = "ja";
String s6 = "va";
System.out.println(s4 == "java");//true
System.out.println(s4 == (s5+s6));//false
System.out.println(s4 == "ja"+s6);//false
 
 
三.值传递问题
1.多态的定义:父类的引用可以指向子类的实例。
java中通过方法重载的重写来体现多态?错误,多态是一种运行时行为
2.对于八种基本数据类型而言,传值只传复印件
 
public void changeValue1(int age){
age = 30;
}
public void changeValue2(Person person){
person.setPersonName("xxx");
}
public void changeValue3(String str){
str = "xxx";
}
public static void main(String[] args){
TestTransferValue test = new TestTransferValue();
int age = 20;
test.changeValue1(age);
System.out.println("age----"+age);//20
 
Person person = new Person("abc");
test.changeValue2(person);
System.out.println("personName-----"+person.getPersonName());//xxx
 
String str = "abc";
test.changeValue3(str);
System.out.println("String-----"+str);//abc
}
 
 
四.静态代码块
1.从父到子,静态先行
 
class Father
{
public Father(){
System.out.println("111111");
}
{
System.out.println("222222");
}
static{
System.out.println("333333");
}
}
class Son extends Father
{
public Son(){
System.out.println("444444");
}
{
System.out.println("555555");
}
static{
System.out.println("666666");
}
}
public class TestStaticSeq
{
public static void main(String[] args)
{
new Son(); // 3 6 2 1 5 4
System.out.println("======================");
new Son(); // 2 1 5 4
System.out.println("======================");
new Father();2 1
}
}
 
五.Serializable
•实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
 
六.谈谈你对HashMap中put/get方法的认识?如果了解再谈谈HashMap的扩容机制?默认大小是多少?什么是负载因子?什么是吞吐临界值?
1 HashSet底层是采用HashMap实现
2集合里面放置的永远是对象的引用而不是对象本身
3当你在HashSet里add对象的时候,实际是HashMap里面put了key-value
键值对,其中key就是你add进来的对象,value是一个固定的Object常量
4 HashMap底层是个Entry类型的,名字叫table的数组
5put:当程序试图将一个key-value对放入HashMap中时,程序首先根据该key的hashCode()返回值决定该Entry的存储位置:如果两个Entry的key的hashCode()返回值相同,那它们的存储位置相同。如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。如果这两个Entry的key通过equals比较返回false,新添加的Entry将与集合中原有Entry形成Entry链,而且新添加的Entry位于Entry链的头部——具体说明继续看addEntry()方法的说明。
6.HashMap底层数据结构是数组加链表,数组是一个长度为16,负载因子为0.75,类型为Entry()[]名字为table的数组
7.LinkedList装进去的是一个Node节点,是双端循环列表
 
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
 
modCount++;
addEntry(hash, key, value, i);
return null;
}
 
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size >= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
}
 
createEntry(hash, key, value, bucketIndex);
}
 
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
七.多线程
1.获取多线程的方法有,继承Thread类,实现Runnable接口,实现Callable接口三种方式
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask,"AA").start();
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("call().......");
return 200;
}
2.wait()方法要在while中使用,防止线程的虚假唤醒
多线程的生产消费者模式
/**
* 题目:现在两个线程,可以操作同一个变量,实现一个线程对该变量加1,一个线程对该变量减1,
* 实现交替,来5轮,变量初始值为零。
*
* @author admin
*/
public class TestThread3 {
 
public static void main(String[] args) {
 
final ShareData shareData = new ShareData();
 
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(400);
shareData.increment();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}, "AA").start();
 
new Thread(new Runnable() {
 
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
shareData.decrement();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}, "BB").start();
 
new Thread(new Runnable() {
 
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(400);
shareData.increment();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}, "CC").start();
 
new Thread(new Runnable() {
 
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
shareData.decrement();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}, "DD").start();
 
}
 
}
class ShareData {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws Exception {
lock.lock();
try {
while (num != 0) {
// this.wait();
condition.await();
}
++num;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// this.notifyAll();
condition.signalAll();
 
} catch (Exception e) {
// TODO: handle exception
} finally {
lock.unlock();
}
 
}
public synchronized void decrement() throws Exception {
lock.lock();
try {
while (num == 0) {
// this.wait();
condition.await();
}
--num;
System.out.println(Thread.currentThread().getName() + "\t" + num);
// this.notifyAll();
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
 
3.经典的线程8锁问题
抓住三个要点:①看操作的是否是同一个资源
②看是否是同一个对象 ③看是否是同一把锁
//判断:两个线程访问Phone,请打印出先访问的是苹果还是安卓
//1 标准版,先打印苹果还是安卓?苹果(同一资源,一个对象,同一把锁)
//2 增加Thread.sleep()给苹果方法,先打印苹果还是安卓?苹果 (同一资源,一个对象,同一把锁)
//3 增加Hello方法,先打印苹果还是Hello Hello(不同资源,一个对象,同一把锁)
//4 有两部手机,先打印苹果还是安卓? 安卓 (同一资源,不同对象,同一把锁)
//5 两个静态同步方法,有一部手机,先打印苹果还是安卓?苹果(同一资源,一个对象,同一把锁)
//6 两个静态同步方法,有两部手机,先打印苹果还是安卓?苹果(同一资源,一个对象,同一把锁静态方法锁的是整个类,而普通方法锁的是一个方法)
//7 一个普通同步方法,一个静态同步方法,有一部手机,先打印苹果还是安卓?安卓(同一资源,一个对象,不同锁)
//8 一个普通同步方法,一个静态同步方法,有两部手机,先打印苹果还是安卓?安卓
(同一资源,一个对象,不同锁)
class Phone
{
public synchronized void getIOS() throws Exception
{
Thread.sleep(400);
System.out.println("----------getIOS");
}
public static synchronized void getAndroid() throws Exception
{
System.out.println("----------getAndroid");
}
 
public void getHello()
{
System.out.println("----------getHello");
 
}
}
 
public class TestThread4
{
public static void main(String[] args)
{
final Phone phone = new Phone();
final Phone phone2 = new Phone();
 
new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i <1; i++)
{
try
{
phone.getIOS();
} catch (Exception e){
e.printStackTrace();
}
}
}
},"AA").start();
 
new Thread(new Runnable()
{
@Override
public void run()
{
for (int i = 0; i <1; i++)
{
try
{
phone.getAndroid();
//phone.getHello();
//phone2.getAndroid();
} catch (Exception e){
e.printStackTrace();
}
}
}
},"BB").start();
 
}
}
 
4.经典的呼啦圈模式的线程,A-B-C-A..........
package com.atguigu.myjob.test;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
*
* @author zhouyang 备注:多线程之间按顺序调用,实现A-B-C 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次 接着 AA打印5次,BB打印10次,CC打印15次 共计来20轮
*/
class ShareResoure {
 
int num = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
 
public void loopAA(int loopAA) {
 
lock.lock();
try {
while (num != 1) {
condition1.await();
}
for (int i = 1; i <= 5; i++) {
Thread.sleep(400);
System.out.println(Thread.currentThread().getName()+"第" + loopAA + "轮:数值为:" + i);
}
num = 2;
condition2.signal();
 
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
 
public void loopBB(int loopBB) {
 
lock.lock();
try {
while (num != 2) {
condition2.await();
}
for (int i = 1; i <= 10; i++) {
Thread.sleep(400);
System.out.println(Thread.currentThread().getName()+"第" + loopBB + "轮:数值为:\t" + i);
}
num = 3;
condition3.signal();
 
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
 
public void loopCC(int loopCC) {
 
lock.lock();
try {
while (num != 3) {
condition3.await();
}
for (int i = 1; i <= 15; i++) {
Thread.sleep(400);
System.out.println(Thread.currentThread().getName()+"第" + loopCC + "轮:数值为:" + i);
}
num = 1;
condition1.signal();
 
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
 
public class TestThread5 {
 
public static void main(String[] args) {
final ShareResoure sr = new ShareResoure();
 
new Thread(new Runnable() {
@Override
public void run() {
 
for (int i = 1; i <= 20; i++) {
sr.loopAA(i);
}
 
}
},"AA").start();
 
new Thread(new Runnable() {
@Override
public void run() {
 
for (int i = 1; i <= 20; i++) {
sr.loopBB(i);
}
 
}
},"BB").start();
 
new Thread(new Runnable() {
@Override
public void run() {
 
for (int i = 1; i <= 20; i++) {
sr.loopCC(i);
}
 
}
},"CC").start();
}
 
}
 
5.读写
package com.atguigu.thread;
 
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
class MyQueue
{
private Object obj;
 
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
 
//读
public void get()
{
readWriteLock.readLock().lock();
try
{
System.out.println(Thread.currentThread().getName()+"正在读取\t"+obj);
} catch (Exception e){
e.printStackTrace();
}finally{
readWriteLock.readLock().unlock();
}
}
 
public void set(Object obj)
{
readWriteLock.writeLock().lock();
try
{
this.obj = obj;
System.out.println(Thread.currentThread().getName()+"正在写入\t"+obj);
} catch (Exception e){
e.printStackTrace();
}finally{
readWriteLock.writeLock().unlock();
}
}
}
 
 
 
/**
*
* @author zhouyang
* 读读可以共享,读写,写写锁必互斥
*
* 1个线程写,100个线程可读,尝试构建该类。
*
*
*/
public class ThreadDemo5
{
public static void main(String[] args)
{
final MyQueue myQueue = new MyQueue();
 
new Thread(new Runnable()
{
@Override
public void run()
{
myQueue.set(new Random().nextInt(100));
}
},"AA").start();
try
{
Thread.sleep(100);
System.out.println();
} catch (InterruptedException e){
e.printStackTrace();
}
for (int i = 1; i <=100; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
myQueue.get();
}
},String.valueOf(i)).start();
}
 
}
}
 
写出Java中五个常见的运行时异常
java.lang.NullPointerException
java.lang.ClassCastException
java.lang.IndexOutOfBoundException
java.lang.NumberFormatException
java.lang.ArithmeticException
 
jsp页面有多少个隐含对象?分别是什么?
九个
page:jsp网页本身
pageContext:网页的属性
request:客户端请求,该请求包含get/post的参数
session:与请求会话有关
application:正在执行的内容
response:网页传回客户端的响应
out:用来传送回应的输出
config:sevlet的架构部件
exception:针对网页的错误
请写出SpringMVC中 5 个常用的注解(SpringMVC 独有,@Controller 等 Spring 固有的不算),并简述其意义
@RequestMapping:用来定义访问的url
@RequestParam:它将和url所以带参数进行绑定
@PathVariable:用于方法中的参数,表示方法参数绑定到url的模板
@ResponseBody:这个方法可以直接放在方法上,表示返回字符串,可以用于ajax
@ModelAttribute:用于方法参数,参数可以直接在页面获取
二种单例模式的区别
//饿汉 class Singleton{     private static Singleton instance = new Singleton();     private Singleton(){     }     public static Singleton getInstance(){         return instance;     }   }
//懒汉 class Singleton1{     private static Singleton1 instance = null;
private Singleton1(){     }     public static Singleton1 getInstance(){         if(instance == null){             synchronized (Singleton1.class) {                 if(instance == null){                     instance = new Singleton1();                 }             }         }         return instance;     }
} 区别: 1. 懒汉存在线程安全问题 2. 创建实例的时机不同,饿汉在类加载时创建实例,懒汉在调用方法时(应用时),创建实例
 
servlet中forward()和redirect()的区别
转发和重定向比较:
             浏览器发送的请求次数     发生的位置     浏览器地址栏  浏览器是否感知
 转发(request)         1           服务器        不发生改变     不知道
 重定向(response)        2           浏览器        发生改变        知道
 
 
在javaScript中如何创建一个对象
var obj = new object();
var obj = {};
 
简述springMVC的运行流程
①. 用户向服务器发送请求, 请求被 Spring 的 DispatcherServlet 捕获 ②. DispatcherServlet 根据请求的 URI,调用 HandlerMapping 获取和目标 Handler 对应的 HandlerExecutionChain 对象(包括Handler对象以及Handler对象对应的拦截器) ③. DispatcherServlet 根据获得的 Handler,获取 HandlerAdapter 对象。 ④. 通过 HandlerExecutionChain 调用拦截器的 preHandle 方法. ⑤. 调用目标 Handler 方法,向 DispatcherServlet 返回一个ModelAndView对象; ⑥. 根据返回的 ModelAndView,选择一个适合的 ViewResolver 解析视图, 得到实际的 View 对象 ⑦. 调用 View 的 render 方法, 将渲染结果返回给客户端。
 
 
简述springMVC与Struts2的区别
①. Spring MVC 的入口是 Servlet、而 Struts2 是 Filter ②. Spring MVC 基于方法设计, 而 Sturts2 是基于类, 每发一次请求都会创建一个 Action 实例. 所以Spring MVC 会稍微比 Struts2 快些. ③. Spring MVC 使用更加简洁, 开发效率Spring MVC确实比 struts2 高: 支持 JSR303, 处理 Ajax 的请求更方便. ④. Struts2 页面的开发效率相比 Spring MVC 高些.
 
 
mybatis如何接受多个参数
①通过@param注解
②通过注入一个javaBean对象
③通过注入Map集合
 
谈谈你对ArrayLis等底层数据结构的命令
以下均为JDK1.7版: ArrayList:是Object类型的数组实现,允许重复。超出限制时会增加50%的容量(grow()方法中实现),每次扩容都底层采用Arrays.arrayCopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建数组的大小为10. LinkedList:底层是Node节点,数据结构是双向循环链表。 ArrayList和LinkedList的区别 ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。 HashSet:它底层就是HashMap。 HashMap: 1 HashSet底层是采用HashMap实现 2 集合里面放置的永远是对象的引用而不是对象本身 3 当你在HashSet里add对象的时候,实际是HashMap里面put了key-value键值对,其中key就是你add进来的对象,value 是一个固定的Object常量 4  HashMap底层是个Entry类型的,名字叫table的数组 5 当程序试图将一个key-value对放入HashMap中时,程序首先根据该key的hashCode()返回值决定该Entry的存储位置:如果两个Entry的key的hashCode()返回值相同,那它们的存储位置相同。如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。如果这两个Entry的key通过equals比较返回false,新添加的Entry将与集合中原有Entry形成Entry链,而且新添加的Entry位于Entry链的头部——具体说明继续看addEntry()方法的说明。
 
在java中&和&&的区别
&和&&都是逻辑运算符,都是判断两边同时真则为真,否则为假;但是&&当第一个条件不成之后,后面的条件都不执行了,而&则还是继续执行,直到整个条件语句执行完为止。如&&例子中的i++>5被执行了,而i++<9并没有被执行,这就是他们的差别。&例子中的i++>5和i++<9都被执行了。
 
XML文档定义有几种形式?他们之间有何本质区别?解析XML文档有哪几种方式?
a: 两种形式 dtd schema,b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的),c:有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)
 
Hibernate中有哪几种数据查询方式
分别是:
1.QBC查询:
QBC查询就是通过使用Hibernate提供的Query By Criteria API来查询对象,这种API封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口。
2.HQL查询:
HQL则是基于对象的查询语言,hibernate会结合对象的配置文件以及方言,将写的hql翻译成sql。
3.SQL语句查询
 
Hibernate中load和get的区别
Get: (1)不支持延迟加载即执行get()方法的时候就发Select语句 (2)当在数据库中查不到记录的时候不会抛出异常,会返回一个null;
Load: (1)支持延迟加载即需要用到记录数据的时候在发Select语句; (2)当在数据库中查不到记录的时候会抛出ObjectNotFoundException异常;当使用session中的load方法查询数据库中的记录时,我们返回的是一个代理对象,而不是真正需要的那个对象;
 
Spring提供几种事务管理机制?提供哪几种事务传播行为?
Spring的事务管理机制实现的原理,就是通过这样一个动态代理对所有需要事务管理的Bean进行加载,并根据配置在invoke方法中对当前调用的 方法名进行判定,并在method.invoke方法前后为其加上合适的事务管理代码,这样就实现了Spring式的事务管理。Spring中的AOP实 现更为复杂和灵活,不过基本原理是一致的。
事务的实现方式:
实现方式共有两种:编码方式;声明式事务管理方式。
基于AOP技术实现的声明式事务管理,实质就是:在方法执行前后进行拦截,然后在目标方法开始之前创建并加入事务,执行完目标方法后根据执行情况提交或回滚事务。
声明式事务管理又有两种方式:基于XML配置文件的方式;另一个是在业务方法上进行@Transactional注解,将事务规则应用到业务逻辑中。
 
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,
它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
说明
PROPAGATION_REQUIRED如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY使用当前的事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
 
简述final、finally、finalize的区别
final用于声明属性,方法和类,分别表示属性不可交变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。
posted @ 2017-10-10 15:31  斜杠小青年  阅读(118)  评论(0)    收藏  举报