当前位置: 首页 > 后端技术 > Java

浅谈设计模式(一)

时间:2023-04-02 00:15:07 Java

(一)单例模式单例模式在其核心结构中只包含一个特殊的类,称为单例。单例模式可以保证一个类在系统中只有一个实例。特点:1.一个单例类只能有一个实例。2.单例类必须创建自己的唯一实例。3.单例类必须将这个实例提供给所有其他对象。单例模式的要点:1.私有构造方法2.私有静态引用指向自己的实例3.静态公共方法single以自己的实例为返回值实例模式根据调用的时机分为两种实例化对象:一种是饿单例,一种是懒单例。饿式单例在单例类加载时实例化一个对象给自己引用;而懒惰风格的单例在调用实例方法时实例化对象。代码如下:饥饿式单例:publicclassSingleton{privatestaticSingletonsingleton=newSingleton();privateSingleton(){}publicstaticSingletongetInstance(){returnsingleton;}}惰性单例:publicclassSingleton{privatestaticSingletonsingleton;privateSingleton(){}publicstaticsynchronizedSingletongetInstance(){if(singleton==null){singleton=newSingleton();}返回单例;}}单例模式还有一种常见的形式:双锁形式publicclassSingleton{privatestaticvolatileSingletoninstance=null;privateSingleton(){//做点什么}}}返回实例;}}这种方式会把下面的内容同步到if里面,提高执行效率。不需要每次获取对象都同步,只需要第一次同步一次,创建后,在这种模式下不需要使用双判断和同步。与上面的例子相比,效率大大提高了,因为如果服务器允许单层if判断,假设有100个线程,则成本时间为100*(同步判断时间+if判断时间),而如果有双if判断,100个线程可以同时判断if,理论上消耗的时间只有一个if判断时间。所以如果你面对高并发的情况,而你又在使用惰性模式,最好的选择就是双判断同步的方式。单例模式的优点:1、内存中只有一个对象,节省内存空间。2.避免频繁创建和销毁对象,可以提高性能。3.避免对共享资源的多次占用。4.可以全局访问。单例模式的缺点:1.难以扩展,因为getInstance静态函数没有办法生成子类的实例。如果要扩展,只重写那个类。2.隐式使用导致类结构不清晰。3.可能导致程序内存泄漏。适用场景:由于单例模式的上述优点,它是一种在编程中使用较多的设计模式。以下是使用单例模式的场景:1.需要频繁实例化然后销毁的对象。2.花费太多时间或资源创建但经常使用的对象。3、在资源共享的情况下,避免资源操作造成的性能或损失。4.在资源控制的情况下,方便资源之间的相互通信。单例模式注意事项:1.只能使用单例类提供的方法获取单例对象,不要使用反射,否则会实例化一个新对象。2、不要在类中做断开单例类对象和静态引用的危险操作。3、多线程单例使用共享资源时要注意线程安全问题。Java中单例模式的一些常见问题:1.单例模式的对象如果长时间不用,会不会被jvm垃圾回收器回收?除非人为断开单例中静态引用与单例对象的联系,否则jvm垃圾回收器是不会回收单例对象的。JVM卸载一个类的判断条件如下:1.该类的所有实例都已经被回收,即java堆中没有该类的实例。2、加载该类的ClassLoader已经被回收。3、这个类对应的java.lang.Class对象没有被任何地方引用,任何地方都无法通过反射访问到这个类的方法。只有满足这三个条件,jvm才会在垃圾回收时卸载该类。显然,单例类不满足第一个条件,所以单例类不会被回收。2、一个jvm中会不会有多个单例?在分布式系统、多个类加载器和序列化的情况下,会生成多个单例。那么在同一个jvm中,会不会有单例呢?使用单例提供的getInstance()方法只能得到同一个单例,除非使用反射,否则会得到一个新的单例。代码如下:Classc=Class.forName(Singleton.class.getName());构造函数ct=c.getDeclaredConstructor();ct.setAccessible(真);单例singleton=(Singleton)ct.newInstance();每次运行都会生成一个新的单例对象。所以在使用单例模式的时候,注意不要使用反射来生成新的单例对象。3.在getInstance()方法上同步是否有优势,还是只同步必要的块更有优势?由于锁定仅在创建实例时有意义,而其他时候实例只能以只读方式访问,因此仅同步必要的块性能更高,是更好的选择。缺点:只有第一次调用时,才会产生两个对象,需要同步。而一旦单例不为null,系统还是要花费同步锁的开销,得不偿失。4、单例类可以继承吗?根据单例实例构造的时机和方法,单例模式可以分为几种类型。但是对于这种通过私有构造函数和静态方法提供实例的单例类,是不支持继承的。这种模式的单例实现要求每个具体的单例类自己维护一个单例实例,并限制多个实例的生成。但是您可以使用另一种方式来实现单例:注册单例以使单例对继承开放。典型的列子SpringIOC实现原理。(2)工厂模式这类设计模式是一种创建型模式,它提供了创建对象的最佳方式。工厂模式主要是为创建对象提供一个过渡接口,从而屏蔽和隔离创建对象的具体过程,从而达到提高灵活性的目的。工厂模式按照抽象程度分为三种:1.简单工厂模式(也叫静态工厂模式)2.工厂方法模式(也叫多态工厂)3.抽象工厂模式(也叫工具箱)简单工厂模式:本质是一个工厂类根据传入的参数动态决定创建一个产品类(这些产品类继承自父类或接口)的实例。简单工厂模式的创建目标,全部创建对象是扮演这个角色的特定类的实例。工厂方法模式:工厂方法是一种粒度非常小的设计模式,因为该模式的表现只是抽象方法。预先定义用于创建对象的接口,让子类决定实例化具体的类,即在工厂和产品之间增加一个接口,工厂不再负责产品的创建,接口返回根据不同条件的特定类实例。要实现的具体类实例。抽象工厂模式:当有多个抽象参与者时使用的工厂模式。抽象工厂模式可以为客户端提供一个接口,使得客户端可以创建多个产品对象,而无需指定产品的具体情况。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类实例。工厂方法模式在实践中应该用的比较多。我们以工厂方法模式为例,抽象产品类:定义car车辆类publicinterfaceCar{voidgotowork();}定义实际的产品类,一共定义两个,bikepublicclassBikeimplementsCar{@OverridepublicvoidgoToWork(){System.out.println("骑车上班!");}}publicclassBusimplementsCar{@OverridepublicvoidgoToWork(){System.out.println("坐公交去上班!");}}定义一个抽象工厂接口publicinterfaceICarFactory{CargetCar();}针对每个特定产品类的特定工厂子类创建不同的工厂子类publicclassBikeFactoryimplementsICarFactory{@OverridepublicCargetCar(){returnnewBike();}}公共类BusFactory实现ICarFactory{@OverridepublicCargetCar(){returnnewBus();}}简单的测试类来验证不同的工厂可以生产不同的产品对象publicclassTestFactory{@Testpublicvoidtest(){ICarFactoryfactory=null;//自行车工厂=newBikeFactory();自行车=factory.getCar();自行车.goToWork();//骑车去上班!//总线工厂=newBusFactory();汽车总线=factory.getCar();总线.goToWork();//坐公交上班!}}工厂模式的优点:1.如果调用者想创建一个对象,只需要知道它的名字,降低了耦合度。2.高可扩展性。如果你想增加一个产品,你只需要扩展一个工厂类。使代码结构更加清晰。3.屏蔽了产品的具体实现,调用者只关心产品的接口。工厂模式的缺点:每增加一个产品,都需要增加具体的类和对象来实现工厂,使系统中类的数量成倍增加,一定程度上增加了系统的复杂度,并且也增加了具体类的依赖。所以对于简单的对象,使用工厂模式会增加复杂度。工厂模式的适用场景:1、一个对象有很多子类。2.创建一个对象需要很多额外的操作。3、系统后期需要经常扩展。它将对象实例化的任务交给了实现类,具有很好的可扩展性。Java中工厂模式的一些常见问题:1.使用父类的向下转型(使用父类类型的引用指向子类的对象)可以达到类似工厂模式的效果,那为什么要用工厂模式呢?将指向子类对象的父类引用赋值给子类引用称为向下转型,如:ClassStudentextendsPersonPersons=newStudent();学生a=(学生)s;在客户端使用向下转型来实例化子类时,严重依赖具体子类的名称。当我们需要改变子类的构造方法时,比如增加一个参数,或者改变子类的类名,所有新的子类都需要做相应的改变。但是如果我们使用工厂模式,我们只需要在工厂中修改new的代码,其他使用这个实例的项目也会做相应的修改,不需要我们手动操作。总结:无论是简单工厂模式、工厂方法模式还是抽象工厂模式,它们本质上都是将不变的部分抽取出来,将可变的部分留作接口,以实现最大程度的复用。(3)模板方法模式在一次操作中定义了算法的框架,将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下重新定义算法中的一些具体步骤。类型:行为类模式类图:其实模板方法是编程中经常使用的模式。我们先来看一个例子。某天,程序员A接到一个任务:给定一个整数数组,将数组中的数字从小到大排序,然后打印出排序后的结果。经过分析,这个任务大致可以分为两部分,整理和打印。打印功能实现起来容易,排序有点麻烦。但是A有办法,先完成打印功能,然后找人做排序功能。abstractclassAbstractSort{/***将数组从小到大排序*@paramarray*/protectedabstractvoidsort(int[]array);publicvoidshowSortResult(int[]array){this.sort(array);System.out.print("排序结果:");for(inti=0;i