单利模式 类的继承和多态

单利模式

  • 单利类只能有一个实例
  • 单利类必须自己创建自己的唯一实例
  • 单利类必须给所有其他对象提供这一实例
  • 单利模式适用条件:一个类可以定义无数个对象,但是只能有一个实例

单利模式有两种:懒汉式、饿汉式

懒汉式单利模式,非线程安全:
代码中有四种创建对象的方法,第四种为完善的方法,保障了在多个线程进入的情况下,可以仅产生一个实例

class SingleClass1 {
    private static Object lock = new Object();
    private static SingleClass1 singleClass = null;

    private SingleClass1() {
        System.out.println("这是私有构造函数SigleClass1().init");
    }

    //提供一个全局访问点
    //可重入函数======线程安全的函数
    public static SingleClass1 getInstance() {
        //4.双重检验
        if (singleClass == null) {
            synchronized (lock) {
                if (singleClass == null) {
                    singleClass = new SingleClass1();
                }
            }
        }
//第一个线程在开辟内存时再进去一个线程,第二个线程也可以进入,在lock外等待,当第一个线程结束后,lock解锁,会产生两个对象
       /*3. if (singleClass == null) {
                synchronized (lock) {
                    singleClass = new SingleClass1();
            }
        }*/
//只有一个线程时也会产生lock,浪费资源
       /* 2.synchronized (lock) {
            if (singleClass == null) {
                singleClass = new SingleClass1();
            }
        }*/
//第一个线程在开辟内存时再进去一个线程,会产生两个对象
       /* 1.if (singleClass == null) {
            	singleClass = new SingleClass1();
        }*/
        return singleClass;
    }
}

饿汉式单利模式:

class SingleClass2 {
    private static SingleClass2 singleClass = new SingleClass2();

    private SingleClass2() {
        System.out.println("这是私有构造函数SigleClass2().init");
    }

    //提供一个全局的访问点
    public static SingleClass2 getInstance() {
        return singleClass;
    }
}

类的继承

继承:一种机制,可以进行代码的重用
派生类会继承基类除构造函数外的所有属性

在派生类中可以使用super:

  • super();—>调用基类的构造函数,必须放在基类构造函数的第一行
  • super.data—>访问基类的数据成员
  • super.methods1();—>调用基类的成员方法
class Father {
    int data1;
    
    public Father(int data1) {       
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    static {
        System.out.println("Father.static{}");
    }

    {
        System.out.println("Father.instance{}");
    }
}

class Son extends Father {
    private int data2;
    
    public Son(int a, int b) {
        super(a);      
        System.out.println("Son.init{}");
    }

    static {
        System.out.println("Son.static{}");
    }

    {
        System.out.println("Son.instance{}");
    }

    public void methods1() {
        System.out.println("Son.methods1()");
    }
}

派生类构造对象的初始化顺序:在这里插入图片描述
基类派生类静态块初始化 ====> 基类实例块、构造函数初始化 ====> 派生类实例块、构造函数初始化

函数的重写与重载
  • 重载:函数名相同,参数列表不同,与返回值无关,不一定在同一类中,继承关系也可以
  • 重写/覆盖:函数名相同,参数列表相同,返回值相同
class Father {
    int data1;

    public Father(int data1) {
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    public void methods1() {
        System.out.println("Father.methods1()");
    }    
}

class Son extends Father {
    private int data2;

    public Son(int a, int b) {
        super(a);     
        System.out.println("Son.init()");
    }
    
    public void methods1() {
        System.out.println("Son.methods1()");
    }  
    
	public void methods1(int a) {
        System.out.println("Son.methods1(int)");
    }    
}

如上述代码,
在基类和派生类中进行了methods1方法的重写
在派生类中有两个methods1方法,而参数不同,为函数的重载

基类和派生类之间的相互赋值

派生类可以赋值给基类
基类不可以赋值给派生类

public static void main(String[] args) {
        Father father = new Father(100);
        Son son = new Son(200, 300);
        father = son;//可以,不报错
        son = father;//不可以,error
    }
基类数据成员在派生类中访问权限

在这里插入图片描述

类的多态

多态:基类引用,引用了派生类对象,并且基类和派生类对象有同名的覆盖方法
在构造函数中,也可以发生多态

class Father {
    int data1;

    public Father(int data1) {
        System.out.println("Father.init()");
        this.data1 = data1;
    }

    public void methods1() {
        System.out.println("Father.methods1()");
    }

    public static void methods2() {
        System.out.println("Father.methods2()");
    }
}

class Son extends Father {
    private int data2;

    public Son(int a, int b) {
        super(a);
        System.out.println("Son.init()");
    }

    public void methods1() {
        System.out.println("Son.methods1()");
    }

    public static void methods2() {
        System.out.println("Son.methods2()");
    }
}

public class TestDemo1 {
    public static void main(String[] args) {
        Father father = new Son(200, 300);
        father.methods1();//动多态  运行的时候
        Father.methods2();//静多态  编译的时候
    }
}

输出结果:在这里插入图片描述

由图可知,代码中调用的是father.methods1()方法,而实际输出的是Son.methods1()方法,原因如下:

在这里插入图片描述
基类引用了派生类对象,Father生成的对象存在于堆上,而其中存在方法表,当函数编译时,会指向方法区中Class对象的地址
由图可知,该函数Class对象地址中有基类和派生类的地址,在函数运行的过程中,派生类方法的地址会覆盖基类方法的地址,所以输出的为派生类的方法

猜你喜欢

转载自blog.csdn.net/weixin_43289802/article/details/83582524