Java面试16|设计模式
1、单例模式:
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式有以下几个要素:
- 私有的构造方法
- 指向自己实例的私有静态引用
- 以自己实例为返回值的静态的公有的方法
单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。饿汉式单例在单例类被加载时候,就实例化一个对象交给自己的引用;而懒汉式在调用取得实例方法的时候才会实例化对象。
饿汉式:
public class Singleton_Simple {
private static final Singleton_Simple simple = new Singleton_Simple();
private Singleton_Simple(){}
public static Singleton_Simple getInstance(){
return simple;
}
}
懒汉式:
//双锁机制
class Singleton {
private volatile static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) { // 由于每次调用都需要进行同步,所以可以在前面进行判断即可提高效率,同时注意使用的是Singleton.class的类锁
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
为了提高效率。我们可以用double check机制,现在来看两个问题:
(1)为何用double check的检测机制?
由于两个线程都可以通过第一重的判断 ,进入后,由于存在锁机制,所以会有一个线程进入同步块和第二重判断,而另外的一个线程在同步块处阻塞。
当第一个线程退出同步代码块后,第二个进入,没有经过第二重判断,保证了单例。所以这里必须要使用双重检查锁定。
其实没有第一重的判断,我们也可以实现单例,只是为了考虑性能问题。
(2)为何要对实例变量加volatile关键字?
1.为对象分配内存
2.初始化实例对象
3.把引用instance指向分配的内存空间
ps:对象创建可参看《深入理解Java虚拟机》第44页
这个三个步骤并不能保证按序执行,处理器会进行指令重排序优化,存在这样的情况:
优化重排后执行顺序为:1,3,2, 这样在线程1执行到3时,instance已经不为null了,线程2此时判断instance!=null,则直接返回instance引用,但现在实例对象还没有初始化完毕,此时线程2使用instance可能会造成程序崩溃。
现在要解决的问题就是怎样限制处理器进行指令优化重排。
volatile的作用:
(1)volatile变量不会以被缓存到寄存器或者对其它处理器不可见的地方,因此在读取volatile类型的变量时总是返回最新写入的值。
(2)volatile关键字能够通过提供内存屏障,来保证某些指令顺序处理器不能够优化重排,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
JVM会对分配内存空间的动作进行同步处理。
(1)可能实际上采用CAS(Compare And Set)配上失败重试的方式保证更新操作的原子性
(2)把内存分配的动作按照线程划分在不同的空间之中进行。就是每个线程在Java堆中预先分配一小块内存,
参考Java虚拟机第45页
其实还有种写法,如下:
public class Singleton {
private static class SingletonClassInstance { // 私有静态内部类
// 可能是final解决了并发的问题,基本类型声明就可,但是对象类型时,这个对象不能有状态,
// 如果有状态,则一定要声明为final,例如String类就是一个不可变类
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonClassInstance.instance; // 只在这里调用了静态内部类,私有属性让他人无法使用这个类型
}
private Singleton() { }
}
由于Singletom没有static属性且SingletonClassInstance是私有静态内部类,不会被其他类调用,所以不会被提前初始化,实现了懒汉式的构造实例。
重点要知道为什么静态内部类能保证单例:因为只有一个线程去初始化这个类,在初始化后static final就是一个常量了,当然线程安全了。
其在实际中有重要的应用,如:
2、迭代器模式:
提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节
interface Iterable{
public Iterator iterator();
}
interface Aggregate extends Iterable{
public void add(Object obj);
public void remove(Object obj);
public Iterator iterator();
}
class ConcreteAggregate implements Aggregate {
private List list = new ArrayList();
public void add(Object obj) {
list.add(obj);
}
public Iterator iterator() {
return new ConcreteIterator(list);
}
public void remove(Object obj) {
list.remove(obj);
}
}
调用iterator()方法后返回一个Iterator迭代器,实现如下:
interface Iterator {
public Object next();
public boolean hasNext();
}
class ConcreteIterator implements Iterator {
private List list = new ArrayList();
private int cursor = 0;
public ConcreteIterator(List list) {
this.list = list;
}
public boolean hasNext() {
if (cursor == list.size()) {
return false;
}
return true;
}
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = this.list.get(cursor++);
}
return obj;
}
}
测试一下:
Aggregate ag = new ConcreteAggregate();
ag.add("小明");
ag.add("小红");
ag.add("小刚");
Iterator it = ag.iterator();
while (it.hasNext()) {
String str = (String) it.next();
System.out.println(str);
}
3、组合设计模式:
蜜蜂是昆虫:继承实现
蜜蜂有向前移动后攻击人的行为:组合实现
昆虫:
class Insect {
private String name;
public Insect(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
攻击行为:
interface Attack {
public void move();
public void attack();
}
class AttackImpl implements Attack {
private String move;
private String attack;
public Attack Impl(String move, String attack) {
this.move = move;
this.attack = attack;
}
@Override
public void move() {
System.out.println(move);
}
@Override
public void attack() {
move();
System.out.println(attack);
}
}
蜜蜂是昆虫,有向前一步攻击人的属性:
class Bee extends Insect implements Attack {
private Attack attack;
public Bee(String name, Attack attack) {
super(name);
this.attack = attack;
}
public void move() {
attack.move();
}
public void attack() {
attack.attack();
}
}
由于继承机制太过依赖于父类的实现细节,如果父类变动,则子类也会跟着变动,这是糟糕的。
4、策略设计模式:
// Different types of function objects:
interface Combiner<T> {
T combine(T x, T y);
}
public class Functional {
// Calls the Combiner object on each element to combine
// it with a running result, which is finally returned:
public static <T> T reduce(Iterable<T> seq, Combiner<T> combiner) {
Iterator<T> it = seq.iterator();
if (it.hasNext()) {
T result = it.next();
while (it.hasNext()){
result = combiner.combine(result, it.next());
}
return result;
}
// If seq is the empty list:
return null; // Or throw exception
}
// To use the above generic methods, we need to create
// function objects to adapt to our particular needs:
static class IntegerAdder implements Combiner<Integer> {
public Integer combine(Integer x, Integer y) {
return x + y;
}
}
static class IntegerSubtracter implements Combiner<Integer> {
public Integer combine(Integer x, Integer y) {
return x - y;
}
}
static class BigIntegerAdder implements Combiner<BigInteger> {
public BigInteger combine(BigInteger x, BigInteger y) {
return x.add(y);
}
}
public static void main(String[] args) {
// Generics, varargs & boxing working together:
List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
Integer result = reduce(li, new IntegerAdder());
print(result); // 28
result = reduce(li, new IntegerSubtracter());
print(result);
// Use the prime-generation facility of BigInteger:
List<BigInteger> lbi = new ArrayList<BigInteger>();
BigInteger bi = BigInteger.valueOf(11);
for (int i = 0; i < 11; i++) {
lbi.add(bi);
bi = bi.nextProbablePrime();
}
print(lbi);
BigInteger rbi = reduce(lbi, new BigIntegerAdder());
print(rbi);
}
}
如上例子参考了Java编程思想关于泛型实现策略模式的例子。
5、原型设计模式
class bottle implements Cloneable {
public Wine wine;
public bottle(Wine wn) {
this.wine = wn;
}
// 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
bottle newBottle = (bottle) super.clone();
newBottle.wine = (Wine) wine.clone();
return newBottle;
}
}
class Wine implements Cloneable {
int degree;
String name="法国白兰地"; // 字符串虽然是引用类型,但是新对象中修改时并不会影响到原对象值
public int getDegree() {
return degree;
}
public void setDegree(int degree) {
this.degree = degree;
}
// 覆写clone()方法
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
下面来总结一下各个设计模式在Java中的具体应用。
|
结构型模式 |
|
|
单例模式(Single Pattern) |
1、Log4j中将日志输出到一个文件中必须使用单例模式,否则会覆盖文件的原有内容 2、数据库连接池中的应用 |
|
工厂模式(Factory) |
Spring工厂模式:通过XML文件或Java注解来表示Bean之间的依赖关系,很少直接new一个类进行代码编写 |
|
建造者模式(Builder) |
Struts2中创建容器(Container)对象 |
|
原型模式(Protype) |
Java的克隆clone() |
|
结构型模式 |
|
|
适配器模式(Adapter) |
1、在Java的I/O类库中,StringReader将一个String类适配到Reader接口,InputStreamReader将InputStream适配到Reader类。 2、在Spring中的AOP中,由于Advisor需要的是MethodInterceptor对象,所以每一个Advisor中的Advice都要配成对应的MethodInterceptor对象。 |
|
桥接模式(Bridge) |
|
|
装饰模式(Decorator) |
1、Collections.synchronizedList例子也是一个装饰器模式 2、装饰器在Junit中的应用。TestDecorator是Test的装饰类,其TestDecorator有一些扩展功能的子类。如RepeatedTest类,TestSetup类等 3、一般情况下,需要使用FileReader和StringReader,如果要增加缓存功能的类有很多,那么子类也就需要很多,所以Java就使用了装饰模式,BufferedReader就是这样的装饰类。其实Java I/O 库中的所有输入流、输出流的类都采用了装饰器模式,它们可以无限次地进行装饰转换,转换的目的就是得到自己想要的数据类型的流对象 优点:动态扩展类功能属性,而无需通过继承层次结构实现,更方便灵活给类添加职责 |
|
组合模式(Composite) |
|
|
外观模式(Façade) |
1、在Spring中已经提供了很好的封装,在org.springframework.jdbc.support包下提供的JdbcUtils类就是这样的工具类 2、在Hibernate的配置工作中,有一个Configuration类,负责管理运行时需要的一些信息 |
|
享元模式(Flyweight) |
1、数据据连接池是享元模式的重要应用 2、在Java中,对于基本类型和字符串类型的实现就采用了享元模式 |
|
代理模式(Proxy) |
1、在Spring中已经提供了很好的封装,在org.springframework.jdbc.support包下提供的JdbcUtils类就是这样的工具类 2、在Hibernate的配置工作中,有一个Configuration类,负责管理运行时需要的一些信息 |
|
行为型模式 |
|
|
模版方法模式(Template Method) |
1、在Junit中的TestCase类中 2、在MVC框架的HttpServlet中 3、Spring采用开放式的处理方式,在使用JpaTemplate时,只需要处理具体的SQL语句即可,而创建连接、关闭连接等方法都不需要编写代码,因为不管理是哪种持久层的操作应运,其创建和关闭连接都是一致的,按照相同的顺序执行,这就是模板方法模式的应用 |
|
命令模式(Command ) |
命令模式在Struts2中的应用。其中action就是命令模式中命令接口,ActionInvocation就是命令模式中命令的调用者 |
|
迭代器模式(Iterator ) |
java中的Collection、List、Set、Map等,这些集合都有自己的迭代器。 |
|
观察者模式(Oberver Pattern) |
|
|
中介者模式(Mediator) |
|
|
备忘录模式(Memento) |
|
|
解释器模式(Interpreter) |
|
|
状态模式(State) |
|
|
职责链模式(Chain of Responsibility) |
责任链在Struts2中的拦截器上有重要应用 |
|
策略模式(Strategy) |
1、策略模式允许在程序执行时选择不同的算法.比如在排序时,传入不同的比较器(Comparator),就采用不同的算法 2、Spring的Resource实现思想是典型的策略模式的应用 |
|
访问者模式(Visitor) |
1、javac中关于语法树各个节点的语义分析就使用访问者模式来实现的 |
Java面试16|设计模式的更多相关文章
- java面试07——设计模式
1.什么是设计模式 设计模式就是经过前人无数次的实践总结出的,设计过程可以反复使用的,可以解决特定问题的设计方法. 2.常用的设计模式有哪些 2.1单例模式(饱汉模式.饿汉模式.双重锁模式) http ...
- 转:最近5年133个Java面试问题列表
最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...
- JAVA面试精选【Java基础第一部分】
这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对于那些正打算找工作JAVA软件开发工作的童 ...
- java面试和笔试大全 分类: 面试 2015-07-10 22:07 10人阅读 评论(0) 收藏
2.String是最基本的数据类型吗? 基本数据类型包括byte.int.char.long.float.double.boolean和short. java.lang.String类是final类型 ...
- 近5年133个Java面试问题列表
Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来越高级,面试官问的问题也更深入. 在我 ...
- JAVA面试精选
JAVA面试精选[Java基础第一部分] 这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱.只要能够搞明白这个系列的绝大多数题目,在面试过程中,你就能轻轻松松的把面试官给忽悠了.对 ...
- java面试笔试大汇总
java面试笔试题大汇总5 JAVA相关基础知识 1.面向对象的特征有哪些方面 1.抽象:2.继承:3.封装:4. 多态性: 2.String是最基本的数据类型吗? 基本数据类型包括byte.int. ...
- Java 面试宝典-2017
http://www.cnblogs.com/nelson-hu/p/7190163.html Java面试宝典-2017 Java面试宝典2017版 一. Java基础部分........... ...
- Java面试宝典-2017
Java面试宝典2017版 一. Java基础部分........................................................................... ...
随机推荐
- 丢掉DDL,我用这招3分钟清空 MySQL 9亿记录数据表
摘要:最近由于福建开机广告生产环境的广告日志备份表主键(int类型)达到上限(21亿多),不能再写入数据,需要重新清空下该表并将主键重置,但由于表里有8亿多记录的数据量,使用重置命令及DDL命令执行地 ...
- php 数组对象之间的转换
在之前我写过php返回json数据简单实例 从5.2版本开始,PHP原生提供json_encode()和json_decode()函数,前者用于编码,后者用于解码. 一.json_encode() 1 ...
- 浅谈移动端适配-rem
对于移动端开发来说,无可避免的就是直面各种设备不同分辨率和不同DPR(设备像素比)的问题,在此忽略其他兼容性问题的探讨. 一. 移动端开发有关于像素的概念: 1.设备像素(dp),也叫物理像素.指设备 ...
- 将Tomcat添加进服务启动
tomcat有解压版和安装版2种版本,安装版已经做好了将tomcat添加进服务的操作,而解压版需要我们自己来实现,应用场景主要是在服务器端需要在服务器启动时就启动tomcat. 1.首先需要配置好jd ...
- c语言一个显示星号的函数(隐藏密码)
显示星号 void star(char p[]) //显示星号 { int j; while((p[j] = getch())!='\r') { if(p[j] !='\b') { pr ...
- python常用的一些东西——sys、os等(转)
1.常用内置函数:(不用import就可以直接使用) help(obj) 在线帮助, obj可是任何类型 callable(obj) 查看一个obj是不是可以像函数一样调用 ...
- jacascript DOM变动事件
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! DOM变动事件 变动事件(MutationEvent)能在DOM中的某一部分发生变化时给出提示,这类事件非 ...
- JProfiler简明使用教程
JProfile是一款性能瓶颈分析工具,监控粒度可以细化到某一个类包,堪称神器!我安装了一下9.11的版本,并简单说说使用方法. 1:创建一个监控任务 2:选择tomcat版本 3:监控远程服务器 4 ...
- PHP多个一维数组合并成二维数组的简易方法
当我们需要进行数组遍历数据的时候,需要将多个一维数组进行二维的转换,方法很简单.如下: <?php $a= array('张三','李四','王五'); $b= array ('23','24' ...
- jqurey datatables属性
$('selector').dataTable( { /* * 默认为true * 是否自动计算列宽,计算列宽会花费一些时间,如果列宽通过aoColumns传递,可以关闭该属性作为优化 */ &quo ...