单例模式:
单例模式实现流程:
- 静态的单件模式类的成员变量 – 常驻内存
- 私有的构造方法 – 防止外部进行new实例化
- 提供static的全局的访问入口 – 提供静态方法获取实例对象。
获取单例模式的实例对象不需要new 直接获取。
@什么是设计模式?你用过或者了解哪些设计模式?
设计模式是一套被反复使用、多数人知晓、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码,让代码更容易被他人理解并且保证代码可靠性。
创建型模式:
单例模式、简单工厂模式、工厂模式、抽象工厂模式、原型模式、建造者模式。
结构型模式:
适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式、代理模式
行为型模式:
职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式
@什么是单例模式?单例模式实现的方式有哪些?(7种以上)饿汉模式和懒汉模式实现方式有什么区别?
单例模式:保证一个类仅有一个实例,并提供一个访问他的全局访问点。
懒汉模式可能存在线程安全问题
单例模式实现方式:
懒汉模式,线程不安全
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁synchronize,严格意义上它并不算是单例模式。懒汉模式Lazy初始化。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式,线程安全:
这种方式具备很好的Lazy loading,能够在多线程中很好的工作,但是效率很低,99%情况下不需要同步。第一次调用才会初始化,避免内存的浪费,但是使用的时候必须要加锁synchronize才能保证单例,加锁后会影响效率。该模式Lazy初始化,线程是安全的。
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉模式:
这种方式比较常用,但是容易产生垃圾对象。这种模式没有加锁,所以执行效率比较高,类加载时就初始化,会浪费内存。他是基于classloader机制避免了到县城的同步问题,不过instance在类加载时就实例化。这种模式没有Lazy初始化,但是是多线程安全。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
双检锁/双重锁验证(DCL:Double-checked locking)
这种方式采用双锁机制,安全且在多线程情况下能保持高性能,Lazy初始化,线程安全,实现难度较为复杂。getInstance()的性能对应用程序很关键。
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
登记式/静态内部类:
这种方式能达到双检锁方式一样的功效,但实现简单。只适用于静态域情况,双检锁方式可在实例域需要延迟初始化时使用。这种方式同样利用了classloader机制来保证初始化instance时只有一个线程。Singleton类被装载了,instance不一定被初始化,只有通过显式调用getInstance方法时,才会显式装载SingletonHolder类,从而实例化instance。
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举模式:
实现单例模式的最佳方法,更简洁,自动支持序列化机制,防止绝对多次实例化。不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。但是在实际工作中,很少使用。???
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
不建议使用懒汉模式,建议使用饿汉模式,只有在要明确实现Lazy loading效果时,才会考虑使用登记模式。如果涉及到反序列化创建对象时,可以尝试枚举模式。
@单例模式如何保证线程安全?
- 双重检查锁定(double-checked locking)
public class SingleTon {
// 静态实例变量加上volatile
private static volatile SingleTon instance;
// 私有化构造函数
private SingleTon() {}
// 双重检查锁
public static SingleTon getInstance() {
if (instance == null) {
synchronized(SingleTon.class){
if(instance == null){
instance = new SingleTon();
}
}
}
return instance;
}
}
- 静态内部类实现 单例模式
public class SingleTon {
// 私有化构造函数
private SingleTon() {}
// 利用静态内部类特性实现外部类的单例
private static class SingleTonBuilder {
private static SingleTon singleTon = new SingleTon();
}
public static SingleTon getInstance() {
return SingleTonBuilder.singleTon;
}
public static void main(String[] args) {
SingleTon instance = SingleTon.getInstance();
}
}
Java中静态内部类可以访问其外部类的静态成员属性,同时,静态内部类只有当被调用的时候才开始首次被加载,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗(加synchronized同步锁),这种实现更推荐。
@你用过volatile关键字?他是如何实现的?设计底层硬件实现机制。
参考:https://blog.csdn.net/justloveyou_/article/details/53672005
在 Java 并发编程中,要想使并发程序能够正确地执行,必须要保证三条原则,即:原子性、可见性和有序性。只要有一条原则没有被保证,就有可能会导致程序运行不正确。volatile关键字 被用来保证可见性,即保证共享变量的内存可见性以解决缓存一致性问题。一旦一个共享变量被 volatile关键字 修饰,那么就具备了两层语义:内存可见性和禁止进行指令重排序。在多线程环境下,volatile关键字 主要用于及时感知共享变量的修改,并使得其他线程可以立即得到变量的最新值,例如,用于 修饰状态标记量 和 Double-Check (双重检查)中。
关键字volatile与内存模型紧密相关,是线程同步的轻量级实现,其性能要比synchronize关键字更好。在作用对象和作用范围上,volatile用于修饰变量,而synchronize关键字用于修饰方法和代码块,而且synchronize语义范围不但包括volatile拥有的可见性,还包括volatile所不具有的原子性,但不包括volatile拥有的有序性,即允许指令重排序。
@工厂模式是什么?如何实现工厂模式?
工厂模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。创建过程在其子类中实现。
//第一步
//Shape.java
public interface Shape {
void draw();
}
//第二歩
//Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
//Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
//Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
//第三歩
//ShapeFactory.java
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
//第四步
//FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
//结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
@spring容器中id值和name值有什么区别
参考:https://www.cnblogs.com/alisonGavin/p/6870169.html
@什么是代理模式?代理模式有哪几种实现方式?动态代理的实现方式(jdk、cglib)这两张实现方式各有什么不同?
代理(Proxy)是一种设计模式,即通过代理对象访问目标对象,这样做的好处是:在不修改目标对象的源码下,对方法功能进行增强。也就是在间接访问目标对象的同时,可以在其前或后,添加其它的逻辑代码。
代理模式:动态代理、静态代理
JDK动态代理
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
cglib动态代理
1、 CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、 用CGlib生成代理类是目标类的子类。
3、 用CGlib生成 代理类不需要接口
4、 用CGLib生成的代理类重写了父类的各个方法。
5、 拦截器中的intercept方法内容正好就是代理类中的方法体
@什么是spring框架?用过spring框架?
Spring是一个开源的轻量级Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于简化企业级应用程序开发。应用程序是由一组相互协作的对象组成。
Spring框架除了帮我们管理对象及其依赖关系,还提供像通用日志记录、性能统计、安全控制、异常处理等面向切面的能力,还能帮我管理最头疼的数据库事务,本身提供了一套简单的JDBC访问实现,提供与第三方数据访问框架集成(如Hibernate、JPA),与各种Java EE技术整合(如Java Mail、任务调度等等),提供一套自己的web层框架Spring MVC、而且还能非常简单的与第三方web框架集成。
@什么是IOC和DI,说说你对IOC和DI的理解?
IOC:Inversion of Control,即“控制反转”。
IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则。
不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;
统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;
DI:Dependency Injection,即“依赖注入”。
组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。