1.单例模式创建方式
1.饿汉式(静态常量)【可用】
1 2 3 4 5 6 7 8 9 10
| public class Singleton{ private final static Singleton INSTANCE=new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } }
优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。 缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
|
2.饿汉式(静态代码块)【可用】
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton{ private static Singleton instance; static{ instance=new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return instance; } } 这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中, 也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。
|
3.懒汉式(线程不安全)【不可用】
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static Singleton{ private static Singletons singleton; private Singleton(){ public static Singleton getInstance(){ if(singleton==null){ singleton=new Singleton(); } return singleton; } } 这种写法起到了LazyLoading的效果,但是只能在单线程下使用。如果在多线程下, 一个线程进入了if (singleton ==null)判断语句块,还未来得及往下执行, 另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
|
4.懒汉式(线程安全,同步方法)【不推荐用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Singleton{ private static Singleton singleton; private Singleton(){} public static synchronized Singleton getInstance(){ if(singleton==null){ singleton=new Singleton(); } return singleton; } } 解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对getInstance()方法进行了线程同步。 缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。 而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。 方法进行同步效率太低要改进。
|
5.懒汉式(线程安全,同步代码块)【不可用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Singleton{ private static Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if(singleton==null){ synchronized (Singleton.class){ singleton=new Singleton(); } } return singleton; } } 由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的的代码块。 但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程 进入了if (singleton ==null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句, 这时便会产生多个实例。
|
6.双重检查【推荐使用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Singleton{ private static volatile Singleton singleton; private Singleton(){} public static Singleton getInstance(){ if(singleton==null){ synchronized (Singleton.class){ if(singleton==null){ singleton=new Singleton(); } } } return singleton; } } Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton ==null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if(singleton == null),直接return实例化对象。 优点:线程安全;延迟加载;效率较高。
|
7.静态内部类【推荐使用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Singleton{ private Singleton(){} private static class SingletonInstance{ private static final Singleton INSTANCE=new Singleton(); } public static Singleton getInstance(){ return SingletonInstance.INSTANCE; } } 这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。 不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
优点:避免了线程不安全,延迟加载,效率高。
|
8.枚举【推荐使用】
1 2 3 4 5 6 7
| public enum Singleton{ INSTANCE; private static Singleton getInstance(){ return INSTANCE; } } 借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
|
2.集合概述
Java集合类主要由两个接口Collection和Map派生出来的,Collection有三个子接口:List、Set、Queue。
Java集合框架图如下:
List代表了有序可重复集合,可直接根据元素的索引来访问;
Set代表无序不可重复集合,只能根据元素本身来访问;
Queue是队列集合;
Map代表的是存储key-value对的集合,可根据元素的key来访问value。
集合体系中常用的实现类有ArrayList、LinkedList、HashSet、TreeSet、HashMap、TreeMap
等实现类。
3.线程创建方式
- 通过扩展
Thread
类来创建多线程
- 通过实现
Runnable
接口来创建多线程
- 实现
Callable
接口,通过FutureTask
接口创建线程。
- 使用
Executor
框架来创建线程池。
4.异常概述
常见的Exception有哪些?
常见的RuntimeException:
ClassCastException
//类型转换异常
IndexOutOfBoundsException
//数组越界异常
NullPointerException
//空指针
ArrayStoreException
//数组存储异常
NumberFormatException
//数字格式化异常
ArithmeticException
//数学运算异常
unchecked Exception:
NoSuchFieldException
//反射异常,没有对应的字段
ClassNotFoundException
//类没有找到异常
IllegalAccessException
//安全权限异常,可能是反射时调用了private方法
Error和Exception的区别?
Error:JVM 无法解决的严重问题,如栈溢出StackOverflowError
、内存溢出OOM
等。程序无法处理的错误。
Exception:其它因编程错误或偶然的外在因素导致的一般性问题。可以在代码中进行处理。如:空指针异常、数组下标越界等。
运行时异常和非运行时异常(checked)的区别?
unchecked exception
包括RuntimeException
和Error
类,其他所有异常称为检查(checked)异常。
RuntimeException
由程序错误导致,应该修正程序避免这类异常发生。
checked Exception
由具体的环境(读取的文件不存在或文件为空或sql异常)导致的异常。必须进行处理,不然编译不通过,可以catch或者throws。
5.SpringMVC的RequestMapping参数
1 2 3 4 5 6 7
| @Controller public class HelloController{ @RequestMapping (value = "/hello") public void hello(HttpServletRequest request, HttpServletResponse response) throws IOException { } }
|
param 指定某一种参数名类型,当请求数据中包含该名称的请求参数时,才能进行相应,否则拒绝此次请求。
1
| @RequestMapping(value = "/findUser", param = "username")
|
method 请求方法,支持GET,POST,PUT,DELETE
1 2 3 4
| @RequestMapping (value = "/hello", method = RequestMethod.GET) public void hello (HttpServletRequest request, HttpServletResponse response) throws IOException { }
|
consumes 接受的Content-Type类型
1 2 3 4
| @RequestMapping (value = "/hello", consumes = "application/json") public void hello (HttpServletRequest request, HttpServletResponse response) throws IOException { }
|
1 2 3 4
| @RequestMapping (value = "/hello", produces = "application/json") public void hello (HttpServletRequest request, HttpServletResponse response) throws IOException { }
|
1 2 3 4
| @RequestMapping (value = "/hello", headers = "Content-Type:text/html;charset=UTF-8") public void hello (HttpServletRequest request, HttpServletResponse response) throws IOException { }
|
6.Spring事务和隔离级别
Spring 事务实现方式有哪些?
事务就是一系列的操作原子执行。Spring事务机制主要包括声明式事务和编程式事务。
- 编程式事务:通过编程的方式管理事务,这种方式带来了很大的灵活性,但很难维护。
- 声明式事务:将事务管理代码从业务方法中分离出来,通过aop进行封装。Spring声明式事务使得我们无需要去处理获得连接、关闭连接、事务提交和回滚等这些操作。使用
@Transactional
注解开启声明式事务。
@Transactional
相关属性如下:
属性 |
类型 |
描述 |
value |
String |
可选的限定描述符,指定使用的事务管理器 |
propagation |
enum: Propagation |
可选的事务传播行为设置 |
isolation |
enum: Isolation |
可选的事务隔离级别设置 |
readOnly |
boolean |
读写或只读事务,默认读写 |
timeout |
int (in seconds granularity) |
事务超时时间设置 |
rollbackFor |
Class对象数组,必须继承自Throwable |
导致事务回滚的异常类数组 |
rollbackForClassName |
类名数组,必须继承自Throwable |
导致事务回滚的异常类名字数组 |
noRollbackFor |
Class对象数组,必须继承自Throwable |
不会导致事务回滚的异常类数组 |
noRollbackForClassName |
类名数组,必须继承自Throwable |
不会导致事务回滚的异常类名字数组 |
有哪些事务传播行为?
在TransactionDefinition接口中定义了七个事务传播行为:
PROPAGATION_REQUIRED
如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。如果嵌套调用的两个方法都加了事务注解,并且运行在相同线程中,则这两个方法使用相同的事务中。如果运行在不同线程中,则会开启新的事务。
PROPAGATION_SUPPORTS
如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。
PROPAGATION_MANDATORY
如果已经存在一个事务,支持当前事务。如果不存在事务,则抛出异常IllegalTransactionStateException
。
PROPAGATION_REQUIRES_NEW
总是开启一个新的事务。需要使用JtaTransactionManager作为事务管理器。
PROPAGATION_NOT_SUPPORTED
总是非事务地执行,并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。
PROPAGATION_NEVER
总是非事务地执行,如果存在一个活动事务,则抛出异常。
PROPAGATION_NESTED
如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。
PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:
使用PROPAGATION_REQUIRES_NEW
时,内层事务与外层事务是两个独立的事务。一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。
使用PROPAGATION_NESTED
时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。
事务隔离级别
名称 |
结果 |
脏读 |
不可重复读 |
幻读 |
Read UnCommitted(读未提交) |
什么都不解决 |
√ |
√ |
√ |
Read Committed(读已提交) |
解决了脏读的问题 |
– |
√ |
√ |
Repeatable Read(可重复读) |
(mysql的默认级别)解决了不可重复读 |
– |
– |
√ |
Serializable(序列化) |
解决所有问题 |
- |
- |
- |
READ UNCOMMITTED(读未提交数据):允许事务读取未被其他事务提交的变更数据,会出现脏读、不可重复读和幻读问题。
READ COMMITTED(读已提交数据):只允许事务读取已经被其他事务提交的变更数据,可避免脏读,仍会出现不可重复读和幻读问题。
REPEATABLE READ(可重复读):确保事务可以多次从一个字段中读取相同的值,在此事务持续期间,禁止其他事务对此字段的更新,可以避免脏读和不可重复读,仍会出现幻读问题。
SERIALIZABLE(序列化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,可避免所有并发问题,但性能非常低。
Spring隔离级别
1 2 3 4 5
| int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED; int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED; int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ; int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
|
7.强引用、软引用、弱引用、虚引用是什么,有什么区别?
强引用:在程序中普遍存在的引用赋值,类似Object obj = new Object()
这种引用关系。只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。
软引用:如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
1 2
| SoftReference<String> softRef = new SoftReference<String>(str);
|
弱引用:在进行垃圾回收时,不管当前内存空间足够与否,都会回收只具有弱引用的对象。
1 2
| WeakReference<String> weakRef = new WeakReference<String>(str);
|
虚引用:虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要是为了能在对象被收集器回收时收到一个系统通知。
总结
- 突然打来的电话面试,都不知道什么公司,虽然模模糊糊,也努力回答了,不知结果!
- 但是学习还是得继续!
- 加油!