单利模式
- 定义
- 1、一个类只产生一个实例
- 2、单利类必须自己创建自己的唯一实例。
- 3、单利类必须给所有其他对象提供这一实例 =====》提供一个全局访问点
- 场景:(什么时候用单利模式)
一个类可以定义无数个对象,“但是只能有一个实例” =====》 控制构造函数- 懒汉式单利模式
//懒汉式单利模式 =====》 线程不安全 (new的时候才调用一个对象 不是线程安全的)
// 1、将构造函数写成私有的
//2、提供一个全局访问点
class MysingleTon{ //单例类
private static MysigleTon sigleTon = null;
//public MysingleTon(){ //无参的构造函数 ( 通过在主函数中new一下,产生对象)new无数个对象 }
private MysingleTon(){
System.out.println("MysingleTon {} init"); //私有的,只能在当前类中使用
}
//得到对象
//单利模式访问的时候必须提供一个全局的访问点 === 通过类名调用
publlic static MysingleTon getInstance(){
//懒汉式的竟态条件代码段
if(singleTon == null){
singleTon = new MysingleTon(); //new分两步,1、创建内存2、创建对象
}
return singleTon;
}
}
public class Test1030 {
public static void main(String[] args) {
MysingleTon mysingleTon = MysingleTon.getInstance();
MysingleTon mysingleTon2 = MysingleTon.getInstance();
MysingleTon mysingleTon3 = MysingleTon.getInstance();
MysingleTon mysingleTon4 = MysingleTon.getInstance();
System.out.println( mysingleTon );
System.out.println( mysingleTon2 );
System.out.println( mysingleTon3 );
System.out.println( mysingleTon4 );
//打印的地址都一样 一个类只生成了一个实例
}
}
- 饿汉式单利模式
//饿汉式单利模式 ====>线程安全 只初始化一次
class MysingleTon2{
private static MysingleTon2 sigleTon = new MysingleTon2(); // “static"只初始化一次 已经得到对象
private MysingleTon2() {
System.out.println("MysingleTon2() init");
}
//单利模式访问的时候必须提供一个全局的访问点 === 通过类名调用
public static MysingleTon2 getInstance() {
return sigleTon;
}
}
- 线程安全的单利模式
- 线程安全======》是否有竟态条件 》 临界区代码段》原子性操作(一个线程执行的时候,另一个不能执行)=====》加锁(互斥锁、自旋锁、读写锁)
- 每一个线程都有自己的执行时间,执行时间取决于CPU
class MysingleTon {
private static Object lock = new Object(); //实例化新建锁
private static MysingleTon singleTon = null;
private MysingleTon() {
System.out.println("MysingleTon() init"); //私有的,只能在当前类中使用
}
//单利模式访问的时候必须提供一个全局的访问点 === 通过类名调用
public static MysingleTon getInstance() {
//加锁
// 4、可重入函数======》线程安全的函数
// double--checked llocking 双重检验
if (singleTon == null) {
synchronized (lock) {
if (singleTon == null) {
}
singleTon = new MysingleTon();
}
}
//单线程可以 单线程进来加锁,得到对象,释放锁
// 多线程不合适 第一个线程还没有将对象MysigleTon()赋值给sigleTon,第二个线程会从if中进来在锁外等待第一个线程释放锁
/*
3、if (sigleTon == null) {
synchronized (lock) {
sigleTon = new MysigleTon(); //临界区代码段
//同时有两个线程,次处至少执行两遍
}
}*/
//多线程安全 第一个线程先进来,判断为空,赋值,释放锁,第二个线程才能进来
//单线程不合适 来一个线程,加锁===释放锁 (浪费CPU)
/*2、synchronized (lock) {
if (sigleTon == null) {
sigleTon = new MysigleTon(); //CPU浪费
}*/
/* //懒汉式的竟态条件代码段
1、if (sigleTon == null) {
sigleTon = new MysigleTon(); //new分两步,1、创建内存2、创建对象
}*/
return singleTon; //返回对象
} //释放锁
}
- 静态内部类实现单利模式
class MysingleTon3 {
private MysingleTon3(){
}
//只有访问静态内部类的时候,才会创建对象
private static class SingleTon{
public static MysingleTon3 c = new MysingleTon3();
}
public static MysingleTon3 getInstance(){
return SingleTon.c;
}
}
继承
- 继承: 一种机制,可以进行代码的重用
- 面试问题1、派生类继承了父类的除什么没继承?? 除构造函数外的所有属性(包括:数据)
- 继承了基类,还要为基类的数据成员进行构造函数
- 派生类的构造函数只能构造自己的数据成员
- 基类的构造函数没有被继承,只是调用构造函数 用super调用 super写在第一行
-
super:
- super()===》调用基类的构造函数 ( 必须放在第一行)
- super.data===》访问基类的数据成员
- super.func()=====》调用基类的成员方法
class Base {
public int ma;
public Base (int ma) {
this.ma = ma;
}
public void fun1() {
System.out.println("Base.fun1()");
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{
private int mb;
public Derieve(int a,int b){ //派生类的构造函数
super(a); //super关键字 必须放在第一行 基类的构造函数没有被继承,只是调用构造函数 用super调用基类的数据成员
super.ma = 10;
super.fun1(); //调用基类的成员方法
// ma = a; //error
this.mb = b; //派生类的构造函数只能构造自己的数据成员
System.out.println("Derieve.init{}");
}
}
class Base {
public int ma;
public Base (int ma) {
System.out.println("Base.init()");
this.ma = ma;
}
public void fun1() {
System.out.println("Base.fun1()" + this.ma);
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{
private int mb;
public Derieve(int a,int b){ //派生类的构造函数
super(a); //super关键字 必须放在第一行 基类的构造函数没有被继承,只是调用构造函数 用super调用基类的数据成员
super.ma = 10;
super.fun1(); //调用基类的成员方法
// ma = a; //error
this.mb = b; //派生类的构造函数只能构造自己的数据成员
System.out.println("Derieve.init{}");
}
public void fun1() {
System.out.println("Derieve.fun1()");
}
public static void fun2() {
System.out.println("Derieve.fun2()");
}
}
public class Test10301 {
public static void main1(String[] args) {
Base base = new Base(100);
Derieve derieve = new Derieve(1000, 9999);
}
}
结果:Base.init()
Base.fun1()10
- 对象的初始化过程: 静态代码块初始化====》实例代码块初始化====》构造函数
class Base {
public int ma;
public Base (int ma) {
System.out.println("Base.init()");
this.ma = ma;
}
//基类
// 静态代码块
static {
System.out.println("Base.static{}");
}
//实例代码块
{
System.out.println("Base.instance{}");
}
public void fun1() {
System.out.println("Base.fun1()" + this.ma);
}
public static void fun2() {
System.out.println("Base.fun2()");
}
}
class Derieve extends Base{
private int mb;
public Derieve(int a,int b){ //派生类的构造函数
super(a); //super关键字 必须放在第一行 基类的构造函数没有被继承,只是调用构造函数 用super调用基类的数据成员
super.ma = 10;
super.fun1(); //调用基类的成员方法
// ma = a; //error
this.mb = b; //派生类的构造函数只能构造自己的数据成员
System.out.println("Derieve.init{}");
}
//派生类
// 静态代码块
static {
System.out.println("Derieve.static{}");
}
//实例代码块
{
System.out.println("Derieve.instance{}");
}public void fun1() {
System.out.println("Derieve.fun1()");
}
public static void fun2() {
System.out.println("Derieve.fun2()");
}
}
public class Test10301 {
public static void main1(String[] args) {
Base base = new Base(100);
Derieve derieve = new Derieve(1000, 9999);
}
}
结果:Base.static {}
Derieve.static{}
Base.instance{}
Base.init{}
Derieve.instance{}
Base.fun1()10
Derieve.init{}
派生类构造对象的初始化顺序:
-
Base.static{}
-
Derieve.static{}
-
Base.instance{}
-
Base.init()
-
Derieve.instance{}
-
Derieve.init{}
-
基类和派生类之间的相互赋值 =====>多态的基础
( 派生类可以赋值给基类,基类不能赋值给派生类) -
重载: overload: 函数名相同,参数列表不同,与函数返回值无关
( 并不一定在同一个类当中,在继承关系上也可以构成重载)
public void fun1() {
System.out.println("Derieve.fun1()");
}
public void fun1(int a) {
System.out.println("Derieve.fun1(int)");
}
public void fun1(int a) {
System.out.println("Derieve.fun1(int)");
}
public void fun1() {
System.out.println("Base.fun1()"+this.ma);
}
- 重写/覆盖overwrite: 函数名相同,参数列表相同,函数返回值相(基类和派生类) 可以遵守协变类型
public void fun1() {
System.out.println("Base.fun1()"+this.ma);
public void fun1() {
System.out.println("Derieve.fun1()");
}
- 方法表是在编译的时候生成的
- 多态:基类引用 引用了派生类的对象,并且,基类和派生类都有同名的覆盖方法
- 动多态: 运行期间发生的多态 地址被覆盖 基类引用 引用了派生类的对象
public static void main2(String[] args) {
Base base = new Base(100 ); //调用的是基类的fun()方法
base.fun1();
System.out.println("==========");
Derieve derieve = new Derieve(100, 9999); //调用的是派生类的fun()方法
derieve.fun1();
- 静多态: 发生在编译期间
public static void main3(String[] args) {
Base base = new Derieve(100, 999);
Base.fun2();//静多态
构造函数内能发生多态
class Base {
public int ma;
public Base (int ma) {
fun1();
System.out.println("Base.init()");
this.ma = ma;
}
- (public基类)基类的数据成员在派生类中的访问权限