目录
什么是单例模式
单例(单个的实例)
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);
}
}