单例设计模式与final关键字

目录

什么是单例模式

单例模式的两种方式:

饿汉式

懒汉式

饿汉式VS懒汉式

小结:

final关键字

基本介绍

final注意事项和使用细节

final习题


什么是单例模式

单例(单个的实例)

1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某 个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

单例模式的两种方式:

饿汉式

实现步骤

1. 将构造器私有化

2. 在类的内部直接创建对象(该对象是static)

3. 提供一个公共的static方法,返回 GirlFriend对象

代码演示:

饿汉式的缺点:饿汉式即使我们去使用类中的静态属性,也会导致类的加载,我们没有使用到这个对象,因为类的加载导致对象被创建,并且饿汉式,还有资源浪费的情况

package idea.chapter10.signle_;

/**
 * 演示单例设计模式——饿汉式
 */
public class SingleTon01 {
    public static void main(String[] args) {
        //因为没有将构造器私有化,所以创建出来的是两个对象,
//        GirlFriend xh = new GirlFriend("小红");
//        GirlFriend xb = new GirlFriend("小白");

        //通过方法可以获取对象
        GirlFriend instance = GirlFriend.getInstance();
        System.out.println(instance);

        GirlFriend instance2 = GirlFriend.getInstance();
        System.out.println(instance2);

        //即使我们创建了两个对象,因为我们将构造器私有化了,我们得到的对象,使用的都是提供的static方法,因此得到的两个对象都是相同的
        System.out.println(instance == instance2);//T
    }
}


//有一个类, GirlFriend,只能有一个女朋友
class GirlFriend {
    //一个私有的属性
    private String name;

    //为了能够在静态方法中,返回 gf对象,需要将其修饰为static,因为静态方法只能访问静态属性,不可以访问普通属性
    //在类中创建一个对象
    private static GirlFriend girlFriend = new GirlFriend("小花");
    //如何保障我们只能创建一个 GirlFriend 对象
    //步骤[单例模式-饿汉式]
    //1. 将构造器私有化
    //2. 在类的内部直接创建对象(该对象是static)
    //3. 提供一个公共的static方法,返回 GirlFriend对象

    private GirlFriend(String name) {
        this.name = name;
    }

    //提供一个公共的get方法返回GirlFriend对象
    public static GirlFriend getInstance() {
        return girlFriend;
    }

    @Override
    public String toString() {
        return "GirlFriend{" +
                "name='" + name + '\'' +
                '}';
    }
}

懒汉式

实现步驟

1.任然构造器私有化

2.定义一个static静态属性对象

3.提供一个public的static方法,可以返回一个Cat对象

4.懒汉式,只有当用户使用getInstance()方法时,才返回cat对象, 后面再次调用时,会返回上次创建的cat对象,从而保证单例

代码演示:

懒汉式的缺点:有线程不安全的情况,如果同时有三个线程去调用我们的方法,此时对象都没有创建,此时三个线程去判断的时候,发现对象都没有创建,那么就会导致创建三个对象,这样就破坏了单例模式

package idea.chapter10.signle_;

/**
 * 单例设计模式——懒汉式
 */
public class SingleTon02 {
    public static void main(String[] args) {
        //懒汉式只有在用户需要的时候在创建,但是会有线程的问题
        Cat instance = Cat.getInstance();
        System.out.println(instance);

        Cat instance1 = Cat.getInstance();
        System.out.println(instance);

        //因为只会创建一次对象,下一次创建的时候,会返回上次创建的cat对象
        System.out.println(instance = instance1);

        /*
        思路分析:
        1.饿汉式,没有直接new对象,而是通过一个公共的static方法,进入方法中会先进行判断,当前对象是否为null 如果是null那么才创建,如果已经创建了则返回上一个对象
         */
    }
}

//步驟
//1.任然构造器私有化
//2.定义一个static静态属性对象
//3.提供一个public的static方法,可以返回一个Cat对象
//4.懒汉式,只有当用户使用getInstance()方法时,才返回cat对象, 后面再次调用时,会返回上次创建的cat对象,从而保证单例

class Cat {
    private String name;
    private static Cat cat;//静态属性对象  默认是空

    private Cat(String name) {
        this.name = name;
    }

    public static Cat getInstance() {//只有当用户调用getInstance方法时才会创建对象
        if (cat == null) {//因为第一次创建的时候cat是null,所以第二次在创建时cat已经不为null了所以第二次在创建的时候就是返回上一次创建的对象
            cat = new Cat("xiao");
        }
        return cat;
    }

    @Override
    public String toString() {//重写之后方便输出类的信息
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

饿汉式VS懒汉式

1.二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。

2.饿汉式不存在线程安全问题,懒汉式存在线程安全问题。

3.饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。

4.在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。

小结:

1.单例模式的两种实现方式(1)饿汉式(2)懒汉式

2.饿汉式的问题:在类加载时候就创建,可能存在资源浪费问题

3.懒汉式的问题:线程安全问题

4.要求可以自己独立的写出单例模式

final关键字

基本介绍

final中文意思:最后的,最终的。

final可以修饰类、属性、方法和局部变量.在某些情况下

1)当不希望类被继承时,可以用final修饰.

2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。

3)当不希望类的的某个属性的值被修改,可以用final修饰.

4)当不希望某个局部变量被修改,可以使用final修饰

代码演示:

package idea.chapter10.final_;

/**
 * 演示final关键字的基本使用
 */
public class final01 {
    public static void main(String[] args) {
        /*
        final中文意思:最后的,最终的。
        final可以修饰类、属性、方法和局部变量.在某些情况下,
        1)当不希望类被继承时,可以用final修饰.
        2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
        3)当不希望类的的某个属性的值被修改,可以用final修饰.
        4)当不希望某个局部变量被修改,可以使用final修饰
         */

        F f = new F();
        f.cry();
    }
}

//1)当不希望类被继承时,可以用final修饰.
//因为A被final修饰了,因此不可以在被继承,所以B继承A会报错
final class A {
}

//class B extends A {}

class C {
    //2)当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。
    public final void hi() {
    }
}

class D extends C {
    //此时,子类在去重写父类的方法,就会报错,因为父类中的方法是final修饰,因为被final修饰后就不能够在重写了
//    @Override
//    public void hi() {
//        System.out.println("重写了C类的hi方法..");
//    }
}

//当不希望类的的某个属性的值被修改,可以用final修饰,如果修改了final修饰的属性,就会报错
class E {
    public final double TAX_RATE = 0.08;
}

//当不希望某个局部变量被修改,可以使用final修饰
class F {
    public void cry() {
        //这时,NUM 也称为 局部常量
        final double NUM = 0.01;
        //NUM = 0.9;
        System.out.println("NUM=" + NUM);
    }
}

final注意事项和使用细节

1)final修饰的属性又叫常量,一般用XX XX XX来命名

2)final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如 下位置之一【选择一个位置赋初值即可】:

①定义时: ②在构造器中 ③在代码块中。

3)如果final修饰的属性是静态的,则初始化的位置只能是 代码块 ①定义时②在静态代码块 不能在构造器中赋值。

4)final类不能继承,但是可以实例化对象。

5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。

代码演示:

package idea.chapter10.final_;

import com.alibaba.druid.support.json.JSONUtils;

/**
 * 讲解final关键字的细节
 */
public class finalDetail {
    public static void main(String[] args) {
        /*
        1)final修饰的属性又叫常量,一般用XX XX XX来命名
        2)final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如 下位置之一【选择一个位置赋初值即可】:
        1.定义时: 2.在构造器中 3.在代码块中。
        3)如果final修饰的属性是静态的,则初始化的位置只能是 代码块 1.定义时2.在静态代码块 不能在构造器中赋值。
        4)final类不能继承,但是可以实例化对象。
        5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
         */

        //final类可以实例化
        T2 t2 = new T2();

    }
}

class T {
    //2)final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如 下位置之一【选择一个位置赋初值即可】:1.定义时: 2.在构造器中 3.在代码块中。
    //第一种方式直接赋值
    public final int AGE = 10;

    //第二种方式,在构造器中赋值
    public T() {
        //AGE = 20;
    }

    //第三种方法在代码块中赋值,因为AGE属性不是static的,所以不能使用static静态代码块来赋值,因为静态代码块只能访问静态属性
//    {
//        AGE = 30;
//    }
}

class T1 {
    //3)如果final修饰的属性是静态的,则初始化的位置只能是 代码块 1.定义时2.在静态代码块 不能在构造器中赋值。
    //第一种方式,直接赋值
    public static int TAX_RATE = 10;

    //第二种方式,在静态代码块中赋值
    static {
        TAX_RATE = 20;
    }

}

//4)final类不能继承,但是可以实例化对象。
final class T2 {

}


//5)如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。
class T3 {
    public final void hi() {
        System.out.println("hi");
    }
}

class T4 extends T3 {

}

5)一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。

6)final不能修饰构造方法(即构造器)

7)final和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。

8)包装类(Integer,Double,Float,Boolean等都是final),String也是final类

代码演示:

package idea.chapter10.final_;

/**
 * 演示final关键字的细节2
 */
public class finalDetail02 {
    public static void main(String[] args) {
        /*
        5)一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
        6)final不能修饰构造方法(即构造器)
        7)final和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。
        8)包装类(Integer,Double,Float,Boolean等都是final),String也是final类
         */

        // 7)final和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。 如果不加final使用num属性,会导致类的加载
        System.out.println(BBB.num);

        //包装类,String 是final类,不能被继承
    }
}


//final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
class BBB {
    public final static int num = 10000;

    static {
        System.out.println("BBB 静态代码块被执行");
    }
}

final class AAA {
    //一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
    //public final void cry() {}
}

final习题

思路分析

1.只需要,初始化final修饰的变量在能在哪里声明即可

package idea.chapter10.final_;

/**
 * final关键字习题
 */

public class finalExercise {
    public static void main(String[] args) {
        Circle circle = new Circle(2);
        circle.print();
    }
}

class Circle {
    private double radius;
    //第一种方式直接赋值
    private final double PI = 3.14;

    //final修饰的变量可以在  定义时赋值   可以在构造器中赋值    可以在代码块中赋值
    //第二种方式构造器中赋值
    public Circle(double radius) {
        this.radius = radius;
        //PI=3.14;
    }

    //第三种方式代码块中赋值
//    {
//        PI=3.14;
//    }

    public void print() {
        System.out.println("面积为:" + radius * radius * PI);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_53616401/article/details/129885819
今日推荐