单利模式 类的继承和多态
单利模式
- 单利类只能有一个实例
- 单利类必须自己创建自己的唯一实例
- 单利类必须给所有其他对象提供这一实例
- 单利模式适用条件:一个类可以定义无数个对象,但是只能有一个实例
单利模式有两种:懒汉式、饿汉式
懒汉式单利模式,非线程安全:
代码中有四种创建对象的方法,第四种为完善的方法,保障了在多个线程进入的情况下,可以仅产生一个实例
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对象地址中有基类和派生类的地址,在函数运行的过程中,派生类方法的地址会覆盖基类方法的地址,所以输出的为派生类的方法