Java 核心速成知识 :单例设计模式,final,abstract,接口,内部类

发布于:2025-03-22 ⋅ 阅读:(17) ⋅ 点赞:(0)
1. 单例设计模式
1.1 设计模式

设计模式是一种代码模式,编程风格,以及解决问题的思考方式。

类似学生时期做题的“ 套路 ”,“ 答题模板 ”;

1.2 单例设计模式

单例指单个对象实例;

通过某种方法保证程序运行过程中,某个类只能存在且只提供某一个对象实例的方法;

1.首先必须将类的构造器的访问权限设置为 private,

这样,就不能使用 new 在类的外部任意创造类的对象;

2. 但在内部可以产生类的对象。

由于无法在外部得到该类的对象,

只能调用该类的某个静态方法以返回类内部创建的对象;

3. 静态方法只能访问类中的静态成员变量(静态方法只能访问类的静态方法和静态属性)。

因此,对于该类内部的对象的变量也须定义为静态的;

1.3 懒汉式

1.私有化类的构造器

2.内部声明类的静态对象:方便接下来调用,但不初始化

3.声明 public、static 的返回当前对象的方法

好处:延迟对象的创建(创建静态对象时赋值为 null,用时再创建)

坏处:以下内容存在线程不安全问题,需改善;

//懒汉式
class  SignalMode2{
    public static void main(String[] args) {
        Order instance1 = Order.getInstance();
        Order instance2 = Order.getInstance();
        System.out.println(instance1 == instance2);//true
    }
}
class Order{
    //1.私有化类的构造器
    private Order(){}

    //2.内部声明类的静态对象:方便接下来调用,但不初始化
    private static Order order = null;

    //3.声明 public、static 的返回当前对象的方法
        //每次调用该方法
    public static Order getInstance(){
        if(order == null){
            order = new Order();
        }
        return order;
    }
}
1.4 饿汉式

1.私有化类的构造器:避免类的外部任意创建对象        

2.内部创建类的静态对象:方便接下来调用

3.提供静态方法,外部调用该方法返回该类的对象

好处:饿汉式线程安全;

坏处:对象加载时间过长(创建静态对象时便初始化,用时便直接拿过来)

public class SignalModel {
    public static void main(String[] args) {
        //调用静态方法,返回类的静态对象
        Bank instance1 = Bank.getInstance();
        Bank instance2 = Bank.getInstance();
        //即使创建多个对象,也指向同一个地址值
        System.out.println(instance1 == instance2);//true
    }
}

//饿汉式单例设计模式
class Bank{
    //1.私有化类的构造器:避免类的外部任意创建对象
    private Bank(){};
    //2.内部创建类的静态对象:方便接下来调用
    private static Bank bank = new Bank();//习惯上使用 private
    //3.提供静态方法,外部调用该方法返回该类的对象
    public static Bank getInstance(){
        return bank;
    }
}
2. final

1. final 可以修饰类,方法,变量;

2. final 修饰类:此类不能被其它类继承

        比如:String 类,System 类,StringBuffer 类

3. final 修饰方法,表明此方法不可以被重写

        比如:Object 类中 getClass();

4. final 修饰变量。(属性是变量的一种)

        4.1 修饰属性。可以考虑赋值的位置:显式初始化,代码块,构造器

        4.2 修饰局部变量。

                修饰形参时,表明形参是一个常量。调用此方法,给常量形参赋实参以后,便不能重新赋值 

/**
 * final:最终的
 *      1. final 可以修饰类,方法,变量;
 *      2. final 修饰类:此类不能被其它类继承
 *          比如:String 类,System 类,StringBuffer 类
 *      3. final 修饰方法,表明此方法不可以被重写
 *          比如:Object 类中 getClass();
 *      4. final 修饰变量。(属性是变量的一种)
 *           4.1 修饰属性。可以考虑赋值的位置:显式初始化,代码块,构造器
 *           4.2 修饰局部变量。
 *                  修饰形参时,表明形参是一个常量。调用此方法,给常量形参赋实参以后,便不能重新赋值
 *
 *      static final:
 *          static:表示共有的;可修饰 属性,方法,代码块,内部类
 *          final:表示最终的;可修饰 类,方法,变量
 *        故可修饰属性(多见):全局常量
 *                方法(少见):自定义方法很少如此修饰
 *
 */

public class FinalTest {
    //修饰属性。可以考虑赋值的位置:显式初始化
    final int AGE = 10;
    //  final int age;//报错

    //修饰属性。可以考虑赋值的位置:代码块,否则会报错
    final int A;
    {
        A = 100;
    }

    //修饰属性。可以考虑赋值的位置:构造器,否则会报错
    final int C;
    public FinalTest(){
        C = 10;
    }

//    修饰局部变量。
    public void show(){
        final int A = 100;
//        A += 10;常量,不可以改变其值
    }

    public void getAdd(final int A){//形参 A 为常量,方法内不可以改变其值
        System.out.println(A);
    }

    public static void main(String[] args) {
        FinalTest finalTest = new FinalTest();
        finalTest.getAdd(10);
    }

}
3. abstract
3.1 引入

父类的某些方法需要引入,但不知如何定义,可以将其声明为抽象方法,该类必须为抽象类

作用:价值在于设计。设计者设计好后,让子类继承并实现

3.2 注意事项

1. 抽象类不能被实例化;

2. 抽象类不一定包含抽象方法,但抽象方法一定在抽象类中;

3. abstarct 只修饰类和方法

4. 抽象类可以有任意成员(本质还是类):非/抽象方法,构造器,静态属性等

5. 抽象方法不能有主体(包括花括号)

6. 若抽象类被普通类继承,则需重写所有抽象方法,被抽象类继承,则不需要重写方法

7. 抽象方法不能使用 private,final,static 修饰

        private,私有方法外部不能调用

        final,最终方法不允许重写

        static,静态方法,为类所共用

public class AbstractClass {
    public static void main(String[] args) {
    //1.AbsTest是抽象的; 无法实例化
         // new  AbsTest();
    }
}

abstract class AbsTest{
    //2.抽象类不一定包含抽象方法,但抽象方法一定在抽象类中;
   public abstract void show();

   public void getAns(){
       System.out.println("哈哈哈");
   }
}

//6.若抽象类被普通类继承,则需重写所有抽象方法,被抽象类继承,则不需要重写方法
abstract class A{
    //5.抽象方法不能有主体(包括花括号)
   public abstract void Aa();
}
abstract class B extends A{

}
class C extends B{
    @Override
    public void Aa(){
        System.out.println("结合绝");
    }
}
3.3 抽象模板模式

抽象模板模式的使用:

计算完成后任务的时间体会抽象类/方法;

下面是第一版本;

/**
 *抽象模板模式
 */

public class Sum {
    public static void main(String[] args) {
        Sum sum = new Sum();
        sum.Cir();

        SumA sumA = new SumA();
        sumA.Cir();
    }
    public void Cir(){
        //开始的时间
        long start = System.currentTimeMillis();

        int num = 0;
        for (int i = 1; i <= 90000000; i++) {
            num += 1;
        }

        //结束的时间
        long end = System.currentTimeMillis();

        //执行的时间
        System.out.println("任务 A 执行的时间:" + (end - start) + "毫秒");
    }
}

class SumA {
    public void Cir(){
        //开始的时间
        long start = System.currentTimeMillis();

        long num = 0;
        for (long i = 1; i <= 9000000; i++) {
            num += i * 2;
        }

        //结束的时间
        long end = System.currentTimeMillis();

        //执行的时间
        System.out.println("任务 B 执行的时间:" + (end - start) + "毫秒");
    }
}

 下面是第二版本;

//但是上方代码计算任务执行时间重复性较高
    //可如下简化,提高复用性

class GetSum{
    public static void main(String[] args) {
        //改进后,提高了代码的复用性
        SumC  sumC = new SumC();
        sumC.Time();

        SumD sumD = new SumD();
        sumD.Time();
    }
}

class SumC {

    public void Time(){
        //开始的时间
        long start = System.currentTimeMillis();

        Cir();//调用计算执行时间的任务即可

        //结束的时间
        long end = System.currentTimeMillis();

        //执行的时间
        System.out.println("任务 B 执行的时间:" + (end - start) + "毫秒");
    }

    public void Cir(){
        long num = 0;
        for (long i = 1; i <= 9000000; i++) {
            num += i * 2;
        }
    }

}


class SumD {

    public void Time(){
        //开始的时间
        long start = System.currentTimeMillis();

        Cir();//调用计算执行时间的任务即可

        //结束的时间
        long end = System.currentTimeMillis();

        //执行的时间
        System.out.println("任务 A 执行的时间:" + (end - start) + "毫秒");
    }

    public void Cir(){
        int num = 0;
        for (int i = 1; i <= 90000000; i++) {
            num += 1;
        }
    }
}

 下面是 final 版本;

//上述每个类中都有一个 Time() ,仍然没有彻底提高代码的复用性
    //继承抽象类,重写抽象方法,实现方法调用抽象方法,子类再调用实现方法
class Template{
    public static void main(String[] args) {
        SumE sumE = new SumE();
        SumF sumF = new SumF();
        sumE.Time();//继承了父类方法
        sumF.Time();
    }
}

//抽象类 - 模板设计模式
abstract class Tem{
    //抽象方法
    public abstract void Work();

    //实现方法
    public void Time(){
        //开始的时间
        long start = System.currentTimeMillis();

        //调用计算执行时间的任务即可
        Work();//动态绑定,子类会调用重写的方法

        //结束的时间
        long end = System.currentTimeMillis();

        //执行的时间
        System.out.println("任务执行的时间:" + (end - start) + "毫秒");
    }
}

class SumE extends Tem {
    @Override
    public void Work(){//重写了抽象方法的抽象类
        int num = 0;
        for (int i = 1; i <= 90000000; i++) {
            num += 1;
        }
    }
}

class SumF extends Tem{
    @Override
    public void Work(){//重写了抽象方法的抽象类
        long num = 0;
        for (long i = 1; i <= 9000000; i++) {
            num += i * 2;
        }
    }
}
4. 接口
4.1 使用背景

举例:项目经理需要三位程序猿编写连接三种数据库的类。

若无接口进行规范,可能出现以下情况:

程序员 A :可能出现 f1 ( ),f2 ( )  连接和关闭数据库;

程序员 B:可能出现 con ( ),close ( )  连接和关闭数据库;

程序员 C :可能出现 connect( ),shutdown ( ) 连接和关闭数据库;

这产生了不必要的麻烦;

倘若写之前,项目经理提供连接数据库的抽象类,

并定义抽象方法 connection ( ),close ( ) 连接和关闭数据库,

程序员只管实现即可;

从中可体会到接口的用处

public class example {
    public static void main(String[] args) {
        MysqlDB mysqlDB = new MysqlDB();
        show(mysqlDB);
    }
    public static void show(DBInterface dbInterface){// new MysqlDB();多态
        dbInterface.connect();
        dbInterface.close();
    }
}


//接口:规范连接/关闭数据库的方法名
interface DBInterface{
   void connect();//连接数据库
   void close();//关闭数据库
}


//A 程序员
class MysqlDB implements DBInterface{

    @Override
    public void connect() {
        System.out.println("连接 mysql");
    }

    @Override
    public void close() {
        System.out.println("关闭 mysql");
    }
}

//B 程序员
class OracleDB implements DBInterface{

    @Override
    public void connect() {
        System.out.println("连接 Oracle");
    }

    @Override
    public void close() {
        System.out.println("关闭 Oracle");
    }
}

//C 程序员
class DB2 implements DBInterface{

    @Override
    public void connect() {
        System.out.println("连接 DB2");
    }

    @Override
    public void close() {
        System.out.println("关闭 DB2");
    }
}
4.2 接口定义

interface 接口名

         {

                1.属性

                        全局常量( 被 public sttaic final 修饰,且可省略)

                2.方法

                        静态方法

                        默认方法

                        抽象方法(默认的,public static 可省略)

}

/**
 *  接口定义:
 *      interface 接口名 {
 *          1.属性
 *          2.方法
 *              静态方法
 *              默认方法
 *              抽象方法(默认的,public static 可省略)
 *
 *      }
 */
public interface Interface02 {
    //方法:抽象方法,可省略 public abstract
    public void test();
    //属性
    public int age = 18;
    // jdk8 之后,可以有默认实现方法
    default void show(){
        System.out.println("默认实现方法");
    }
    // jdk8 之后,可以有静态方法
    public static void method(){
        System.out.println();
    }

}
4.3 注意事项

1. 接口不能被实例化

2. 接口中的 public 方法,默认是 abstract 方法,且都可被省略

3. 类实现接口,必须实现接口的所有抽象方法

4. 抽象类可以实现接口,且不用重写接口的方法

5. 一个类可以同时实现多个接口

6. 接口与接口是继承关系,接口和类是实现关系

/**
 * 接口注意事项:
 *      1.接口不能被实例化
 *      2.接口中的 public 方法,默认是 abstract 方法,且都可被省略
 *      3.方法实现接口,必须实现接口的所有抽象方法
 *      4.抽象类可以实现接口
 *      5.一个类可以同时实现多个接口
 *      6.接口与接口是继承关系,接口和类是实现关系
 */
public class Warns {
}

//4.抽象类可以实现接口
interface Test{
    void study();
    void walk();
}
abstract class A implements Test{

}
 4.4 多态特性
4.4.1 多态数组
//多态数组
class Interface{
    public static void main(String[] args) {
        //声明接口数组
        Usb[] usbs = new Usb[2];

        usbs[0] = new Camera_();

        usbs[1] = new Phone_();

        for (int i = 0; i < 2; i++) {
            usbs[i].show();//接口的两个实现类都重写了该方法
            //调用手机的特有方法,报错,需要向下转型
            //usbs[i].call();
            if(usbs[i] instanceof Phone_){//判断运行类型
                ((Phone_) usbs[i]).call();//自动转换为此样式
            }
        }
    }
}
interface Usb{
    void show();
}
class Camera_ implements Usb{

    @Override
    public void show() {
        System.out.println("相机开机啦");
    }
}
class Phone_ implements Usb{

    @Override
    public void show() {
        System.out.println("手机开机啦");
    }

    //Phone 特有方法
    public void call(){
        System.out.println("手机打电话!");
    }
}
 4.4.2 多态传递
//多态传递
public class MultipleType {
    public static void main(String[] args) {
        //多态。子类引用指向父类变量
        TestA testA = new student();

        //接口多态的传递性
        TestB testB = new student();
    }
}

interface TestB{
    void show();
}
interface TestA extends TestB{}
class student implements TestA{

    @Override
    public void show() {

    }
}
 4.5 练习题
//练习题
interface ExerciseA{
    int x = 0;
}

class B{
    int x = 1;
}

class C extends B implements ExerciseA{
    public static void main(String[] args) {
        new C().show();
    }

    int x = 100;
    public void show(){
        //指定不明确
        //System.out.println(x);

        // 实现的接口的 x
        System.out.println(ExerciseA.x);//0

        //父类的 x
        System.out.println(super.x);//1

        //当前对象的 x
        System.out.println(this.x);//100
    }
}

5. 接口 VS 继承

1. 子类继承父类,可以获得父类的方法和属性

若子类需要扩展功能,可以通过接口的方式扩展

2. 继承是为了提高代码复用性,降低代码冗余度

接口是为了设计各种规范,让其他类实现接口

3.接口比继承更加灵活,接口在一定程度上实现代码解耦

解耦:接口规范性 + 动态绑定

总结:实现接口是对 Java 单继承的一种补充

6. 接口 VS 抽象类

一句话:

接口是精简的抽象类,只有全局常量和方法,比抽象类少了构造器和成员属性

更能体现标准和规范的含义,

因此,总说,面向接口开发;

7. 内部类概述

外部类局部位置

        局部内部类

        匿名内部类(重点!!!)

外部类成员位置

        成员内部类

        静态内部类

/**
 * 类的五大成员:
 *  属性,方法,构造器,代码块,内部类
 *
 *  分类:
 *      外部类局部位置
 *          局部内部类
 *          匿名内部类(重点!!!)
 *      外部类成员位置
 *          成员内部类
 *          静态内部类
 *
 */
public class InnerClass {

}

//外部类
class Outer{
    private int i = 100;//属性

    //构造器
    public Outer(){};
    public Outer(int i){
        this.i = i;
    };

    public void Method(){//方法
        System.out.println("Method()");
    }

    //代码块
    {
        System.out.println("代码块");
    }

    //内部类
    class InnerClass{

    }
}