package com.yh.creator;/*** ------------------------------单例模式---------------------------------* 单例模式的四大原则:* 1:构造私有* 2:以静态方法或者枚举返回实例* 3:确保实例只有一个,尤其是在多线程的环境下* 4:确保反序列化是不会重新构建对象** 1.单例模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取资源,其拓展是有限多例模式* 2.没有遵循开闭原则:扩展开放,修改关闭* 开闭原则的含义:* 当应用的需求改变的时候,在不修改源代码的情况下可以扩展模块的功能* 开闭原则的作用:* 1.对软件的测试更方便:软件遵循开闭原则的话,测试时只需测试新增的代码,因为原来的代码依然能运行* 2.可以提高代码的可复用性:粒度越小,代码复用性越高,在面向对象的程序设计中,根据原子和抽象编程可以提高代码的复用性* 3.提高软件的可维护性:遵循开闭原则,其稳定性高* 开闭原则的实现方法:* 可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个稳定的抽象层,然后将相同的可变因素封装到相同的具体类中* 单例模式的特点:* 1.单例类只有一个实例对象* 2.该单例对象必须由单例类自行创建* 3.单例类对外提供一个访问该单例的全局访问点* 优点:* 1.单例模式可以保证内存只有一个实例,减少内存开销* 2.可以避免对资源的多重占用* 3.单例设置全局访问点,可以优化和共享资源的访问* 缺点:* 1.单例模式一般没有接口,扩展困难,如要扩展,则除了修改原来的代码,没有第二种办法,违背了开闭原则* 2.在并发测试中,单例模式不利于代码调试,在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个对象* 3.单例模式的功能代码通常写在一个类中,如果功能设计不合理,很容易违背单一职责原则* 应用场景:对于java来说,单例模式可以保证在一个JVM中只有一个实例* 1.需要平凡创建的一些类,使用单例可以降低系统内存压力,减少GC* 2.某类只要求生成一个对象时,比如说身份证号,或者一个班的班长* 3.某类创建实例时占用资源比较多,耗时长,经常使用* 4.某类需要频繁的实例化,而创建的对象又被频繁的销毁的时候,比如多线程的线程池,网络连接池* 5.频繁访问数据库或文件的对象* 6.对于一些控制硬件级别的操作,或者从系统上讲应是单一控制逻辑的操作,如果有多个实例操作,则系统会乱套* 7.当对象需要被共享的场合,由于单例只允许创建一个对象,共享该对象可以节省内存,并加快访问速度,如WEB中的配置对象,数据库的连接池* 结构:* 普通类的构造函数通常都是公有的,外部类可以通过"new 构造函数()"来创建多个实例,但是如果将类的构造函数设为私有的,外部类就无法调用该构造函数来创建实例,* 这时该类必须定义一个静态私有实例,并向外提供一个静态的公有函数,用于创建或获取该静态私有实例*//*** --------------------------------------懒汉式--------------------------------------* 优点:区别于饿汉式,这样会减少对资源的占用,需要用时才创建* 缺点:懒汉式面临一个问题,当高并发的时候很容易造成线程不安全,在这个时候可以在对外获取的方法上添加 synchronized 加上锁,但是会影响性能的问题~*/
public class LazySingleton {//静态私有实例,先定义好,在需要创建对象时方便直接使用private static LazySingleton singleton;//为了防止外部随便“new对象”private LazySingleton(){}//向外提供一个静态的公有函数,用于让外部创建或获取该静态私有实例public static LazySingleton getSing(){//因为单例要保证这个实例只有一个,所以先判断,是null的话证明没有创建对象,才可以创建if (singleton == null){//需要创建Singleton对象了,直接singleton = new Singleton();写就行//不用Singleton singleton = new Singleton();singleton = new LazySingleton();}//创建好对象之后返回,这样外部就可以直接获取了return singleton;}}/*** -----------------------------------static关键字-----------------------------------* 注意:* 1.static 修饰的成员变量和方法,从属于类。* 2.普通变量和方法从属于对象。* 3.静态方法不能调用非静态成员,编译会报错。* 一、静态变量* 1.类的成员变量可以分为以下两种:* A.静态变量(或称为类变量),指被 static 修饰的成员变量。* 运行时,Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配。* 在类的内部,可以在任何方法内直接访问静态变量。* 在其他类中,可以通过类名访问该类中的静态变量。* B.实例变量,指没有被 static 修饰的成员变量。* 每创建一个实例,Java 虚拟机就会为实例变量分配一次内存。* 在类的内部,可以在非静态方法中直接访问实例变量。* 在本类的静态方法或其他类中则需要通过类的实例对象进行访问。* 2.静态变量在类中的作用如下:* 静态变量可以被类的所有实例共享,因此静态变量可以作为实例之间的共享数据,增加实例之间的交互性。* 如果类的所有实例都包含一个相同的常量属性,则可以把这个属性定义为静态常量类型,从而节省内存空间。** 二、静态方法* 1.与成员变量类似,成员方法也可以分为以下两种* A.静态方法(或称为类方法),指被 static 修饰的成员方法。* 静态方法不需要通过它所属的类的任何实例就可以被调用,因此在静态方法中不能使用 this 关键字,也不能直接访问所属类的实例变量和实例方法,* 但是可以直接访问所属类的静态变量和静态方法。另外,和 this 关键字一样,super 关键字也与类的特定实例相关,所以在静态方法中也不能使用 super 关键字。* B.实例方法,指没有被 static 修饰的成员方法。* 实例方法中可以直接访问所属类的静态变量、静态方法、实例变量和实例方法。* ####-在访问非静态方法时,需要通过实例对象来访问,而在访问静态方法时,可以直接访问,也可以通过类名来访问,还可以通过实例化对象来访问。**三、静态代码块:静态代码块指 Java 类中的 static{ } 代码块,主要用于初始化类,为类的静态变量赋初始值,提升程序性能。* 静态代码块的特点如下:* 静态代码块类似于一个方法,但它不可以存在于任何方法体中。* 静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块。* Java 虚拟机在加载类时执行静态代码块,所以很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。* 如果类中包含多个静态代码块,则 Java 虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。* 静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过类的实例对象来访问。*//***--------------------------------饿汉式------------------------------* 优点:实例在加载时就创建了,所以可以很方便的获取* 缺点:当饿汉式模式下实例数量太多的话,因不是每个实例都时时用到,所以会造成资源浪费,优点不完美咯~*//*** --------------------------------final------------------------------* 一、final修饰变量* final 修饰的变量即成为常量,只能赋值一次,但是 final 所修饰局部变量和成员变量有所不同。* final 修饰的局部变量必须使用之前被赋值一次才能使用。* final 修饰的成员变量在声明时没有赋值的叫“空白 final 变量”。空白 final 变量必须在构造方法或静态代码块中初始化。* ###注意:final 修饰的变量不能被赋值这种说法是错误的,严格的说法是,final 修饰的变量不可被改变,一旦获得了初始值,该 final 变量的值就不能被重新赋值。** final 修饰基本类型变量和引用类型变量的区别:* 当使用 final 修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。 但对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。* ###注意:在使用 final 声明变量时,要求全部的字母大写,如 SEX,这点在开发中是非常重要的。** 二、final修饰方法* final 修饰的方法不可被重写,如果出于某些原因,不希望子类重写父类的某个方法,则可以使用 final 修饰该方法。* Java 提供的 Object 类里就有一个 final 方法 getClass(),因为 Java 不希望任何类重写这个方法,所以使用 final 把这个方法密封起来。但对于该类提供的 toString() 和 equals() 方法,都允许子类重写,因此没有使用 final 修饰它们。* final 修饰的方法仅仅是不能被重写,并不是不能被重载** 三、final 修饰符使用总结* 1. final 修饰类中的变量* 表示该变量一旦被初始化便不可改变,这里不可改变的意思对基本类型变量来说是其值不可变,而对对象引用类型变量来说其引用不可再变。其初始化可以在两个地方:一是其定义处,也就是说在 final 变量定义时直接给其赋值;二是在构造方法中。这两个地方只能选其一,要么在定义时给值,要么在构造方法中给值,不能同时既在定义时赋值,又在构造方法中赋予另外的值。* 2. final 修饰类中的方法* 说明这种方法提供的功能已经满足当前要求,不需要进行扩展,并且也不允许任何从此类继承的类来重写这种方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。在声明类中,一个 final 方法只被实现一次。* 3. final 修饰类* 表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。** 对于 final 类中的成员,可以定义其为 final,也可以不是 final。而对于方法,由于所属类为 final 的关系,自然也就成了 final 型。也可以明确地给 final 类中的方法加上一个 final,这显然没有意义。**/
class HungrySingleton{//再类加载时创建对象private final static HungrySingleton hungry = new HungrySingleton();//私有化构造器,防止外界创建对象private HungrySingleton(){}//提供外界获取对象的方法public static HungrySingleton hungrySingleton(){//返回单个实例(返回的始终是同一个实例)return hungry;}
}
懒汉式
package com.yh.creator;/*** 用懒汉式模拟产生A国当今总统对象*/
public class TestSingleton01 {public static void main(String[] args) {People p1 = People.getPeople();p1.getName();People p2 = People.getPeople();p2.getName();if(p1 == p2){System.out.println("他们是同一个人");}else{System.out.println("他们不是同一个人");}}}class People{private static People people = null;private People(){System.out.println("产生了一个总统");}public static People getPeople(){if(people == null){people = new People();}else{System.out.println("已经有一个总统了");}return people;}
public void getName(){System.out.println("我是A国总统,我叫奥特曼");
}
}
运行结果:
产生了一个总统
我是A国总统,我叫奥特曼
已经有一个总统了
我是A国总统,我叫特朗普
他们是同一个人
饿汉式:
//饿汉式实现创建猪八戒
package com.yh.creator;public class TestSingleton02 {public static void main(String[] args) {Bajie s = Bajie.getInstance();s.getname();Bajie s1 = Bajie.getInstance();s1.getname();if(s == s1){System.out.println("他们是同一个人");}else{System.out.println("他们不是同一个人");}}
}class Bajie{private static Bajie bajie = new Bajie();private Bajie(){System.out.println("创建了一个八戒");}public static Bajie getInstance(){return bajie;}public void getname(){System.out.println("猪八戒");}
}
运行结果:
创建了一个八戒
猪八戒
猪八戒
他们是同一个人