8 面向对象编程 8.5. final 关键字 8.6 抽象类 8.7 抽象类最佳实践-模板设计模式

8 面向对象编程

8.5. final 关键字

8.5.1 基本介绍

final 中文意思:最后的, 最终的.
final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求, 就会使用到final:

    1. 当不希望类被继承时,可以用final修饰.【案例演示】
    1. 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【案例演示:访问修饰符 final 返回类型 方法名 】
    1. 当不希望类的的某个属性的值被修改,可以用final修饰.【案例演示: public final double TAX_RATE=0.08】
    1. 当不希望某个局部变量被修改, 可以使用final修饰【案例演示: final double TAX_RATE=0.08 】
public class Final01 {
    public static void main(String[] args) {
        E e = new E();
        // e.TAX_RATE = 0.09; // 编译错误,不能修改final属性的值
    }
}

// 如果我们要求A类不能被其他类继承,可以使用final修饰A类
final class A { }

// class B extends A {} // 编译错误,不能继承final类

class C {
    // 如果我们要求hi不能被子类重写,可以使用final修饰hi方法
    public final void hi() {}
}

class D extends C {
    // 编译错误,不能重写final方法
    /*
    @Override
    public void hi() {
        System.out.println("重写了 C 类的 hi 方法..");
    }
    */
}

// 当不希望类的某个属性的值被修改,可以用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; // 编译错误,不能修改final局部变量的值
        System.out.println("NUM=" + NUM);
    }
}

8.5.2 final 使用注意事项和细节讨论

    1. final修饰的属性又叫常量,一般用XX_XX_XX来命名
    1. final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
      ① 定义时:如public final double TAX_RATE=0.08;
      ② 在构造器中
      ③ 在代码块中。
    1. 如果final修饰的属性是静态的,则初始化的位置只能是
      ① 定义时 ② 在静态代码块 不能在构造器中赋值。
    1. final类不能继承,但是可以实例化对象。[A2类]
    1. 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承。[A3类]
    public class FinalDetail01 {
        public static void main(String[] args) {
            CC cc = new CC();  // 修正了变量名的语法错误
            new EE().cal();
        }
    }
    
    class AA {
        /*
         1. 定义时:如 public final double TAX_RATE=0.08;
         2. 在构造器中
         3. 在代码块中
        */
        public final double TAX_RATE = 0.08;  // 1.定义时赋值
        
        public final double TAX_RATE2;
        public final double TAX_RATE3;
        
        public AA() {  // 构造器中赋值
            TAX_RATE2 = 1.1;
        }
        
        {  // 在代码块中赋值
            TAX_RATE3 = 8.8;
        }
    }
    
    class BB {
        /*
        如果final修饰的属性是静态的,则初始化的位置只能是
        1. 定义时 
        2. 在静态代码块 
        不能在构造器中赋值。
        */
        public static final double TAX_RATE = 99.9;  // 静态常量定义时赋值
        public static final double TAX_RATE2;
        
        static {  // 静态代码块中为静态常量赋值
            TAX_RATE2 = 3.3;
        }
    }
    
    // final类不能继承,但是可以实例化对象
    final class CC { }
    
    // 非final类可以被继承
    class DD {
        // final方法不能被重写,但可以被继承
        public final void cal() {
            System.out.println("cal()方法");
        }
    }
    
    // 继承DD类,可以使用父类的final方法
    class EE extends DD { }
        
    
    1. 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
    1. final不能修饰构造方法(即构造器)
    1. final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。
    class Demo{
      public static final int i=16; //
      static{
          System.out.println("~666");
      }
    }
    此处若调用Demo.i 不会导致类加载故~666不会输出
    
    1. 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。
    public class FinalDetail02 {
        public static void main(String[] args) {
            System.out.println(BBB.num);
            
            // 包装类和String是final类,不能被继承
            // 例如: class MyString extends String {} // 编译错误
            // 例如: class MyInteger extends Integer {} // 编译错误
        }
    }
    
    // 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修饰
    }
    
    

8.6 抽象类

8.6.1 先看一个问题 Abstract01.java

public class Abstract01 {
    public static void main(String[] args) {
        // 抽象类不能被实例化
        // Animal animal = new Animal("动物"); // 编译错误
    }
}
 //思考:这里eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 考虑将该方法设计为抽象(abstract)方法
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法
abstract class Animal {
    private String name;
    
    public Animal(String name) {
        this.name = name;
    }
    
    // 原有的具体方法实现(已注释,因为与抽象方法冲突)
    /*
    public void eat() {
        System.out.println("这是一个动物,但是不知道吃什么..");
    }
    */
    
    // 抽象方法:没有方法体,需要子类实现
    public abstract void eat();
}

8.6.2 解决之道-抽象类快速入门

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法, 这个方法就是抽象方法,用abstract 来修饰该类就是抽象类。
我们看看如何把Animal做成抽象类, 并让子类Cat类实现。
abstract class Animal{
String name;
int age;
abstract public void cry();
}

8.6.3 抽象类的介绍

    1. 用abstract 关键字来修饰一个类时,这个类就叫抽象类
      访问修饰符 abstract 类名{
      }
    1. 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法
      访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
    1. 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
    1. 抽象类,是考官比较爱问的知识点, 在框架和设计模式使用较多

8.6.4 抽象类使用的注意事项和细节讨论 AbstractDetail01.java

    1. 抽象类不能被实例化 [举例]
    1. 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 [举例]
    1. 一旦类包含了abstract方法,则这个类必须声明为abstract [说明]
    1. abstract 只能修饰类和方法,不能修饰属性和其它的。[说明]
public class AbstractDetail01 {
    public static void main(String[] args) {
        // 抽象类不能被实例化
        // new A(); // 编译错误
    }
}

// 抽象类不一定要包含abstract方法,也可以有实现的方法
abstract class A {
    public void hi() {
        System.out.println("hi");
    }
}

// 一旦类包含了abstract方法,则这个类必须声明为abstract
abstract class B {
    public abstract void hi();
}

// abstract只能修饰类和方法,不能修饰属性和其它元素
class C {
    // public abstract int n1 = 1; // 编译错误,abstract不能修饰属性
}

8.6.5 抽象类使用的注意事项和细节讨论 AbstractDetail02.java

    1. 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等 [举例]
    1. 抽象方法不能有主体,即不能实现.如图所示
      abstract void aaa(){}; (此处大括号上有红色叉号示意错误 )
    1. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。[举例 A类,B类,C类]
    1. 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。
    public class AbstractDetail02 {
        public static void main(String[] args) {
            System.out.println("hello");
        }
    }
    
    // 抽象方法不能使用private、final和static来修饰,因为这些关键字与重写相违背
    abstract class H {
        // 以下都是错误的抽象方法声明方式
        // private abstract void hi();   // 错误:private与重写冲突
        // final abstract void hi();    // 错误:final与重写冲突
        // static abstract void hi();   // 错误:static与重写冲突
        
        public abstract void hi(); // 正确的抽象方法声明
    }
    
    // 如果一个类继承了抽象类,则它必须实现所有抽象方法,除非自身也声明为abstract
    abstract class E {
        public abstract void hi();
    }
    
    // F类继承了抽象类E,但没有实现抽象方法,因此自身必须声明为abstract
    abstract class F extends E {
    }
    
    // G类继承了抽象类E,必须实现所有抽象方法
    class G extends E {
        @Override
        public void hi() { 
            // 实现父类的抽象方法,必须有方法体
            System.out.println("G类实现了E类的hi()方法");
        }
    }
    
    // 抽象类的本质还是类,所以可以有类的各种成员
    abstract class D {
        // 非静态属性
        public int n1 = 10;
        // 静态属性
        public static String name = "韩顺平教育";
        
        // 非抽象方法
        public void hi() {
            System.out.println("hi");
        }
        
        // 抽象方法
        public abstract void hello();
        
        // 静态方法
        public static void ok() {
            System.out.println("ok");
        }
    }
    
    

8.7 抽象类最佳实践-模板设计模式

8..7.1 基本介绍

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

8.7.2 模板设计模式能解决的问题

    1. 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
    1. 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。

8.7.3 最佳实践

需求

    1. 有多个类,完成不同的任务job
    1. 要求统计得到各自完成任务的时间
    1. 精确性实现 TestTemplate.java
    1. 矫情的自然浓缩
      感情的自然浓缩
  1. 先用最容易想到的方法-》代码实现
  2. 分析问题,提出使用模板设计模式
package com.ming.abstract_;

// 抽象类-模板设计模式
public abstract class Template {
    // 抽象方法,由子类实现具体任务
    public abstract void job();
    
    // 模板方法,计算任务执行时间
    public void calculateTime() {
        // 记录开始时间
        long start = System.currentTimeMillis();
        
        job(); // 动态绑定,调用子类的实现
        
        // 记录结束时间
        long end = System.currentTimeMillis();
        
        System.out.println("任务执行时间: " + (end - start) + " 毫秒");
    }
}
package com.ming.abstract_;

public class AA extends Template {
    // 实现Template的抽象方法job,完成累加任务
    @Override
    public void job() {
        long num = 0;
        for (long i = 1; i <= 800000; i++) {
            num += i;
        }
    }
}
package com.ming.abstract_;

public class BB extends Template {
    // 重写Template的job方法,完成累乘任务
    @Override
    public void job() {
        long num = 1;
        for (long i = 1; i <= 80000; i++) {
            num *= i;
        }
    }
}

package com.ming.abstract_;

public class TestTemplate {
    public static void main(String[] args) {
        // 创建AA对象并计算其任务执行时间
        AA aa = new AA();
        aa.calculateTime(); // 利用多态调用模板方法
        
        // 创建BB对象并计算其任务执行时间
        BB bb = new BB();
        bb.calculateTime();
    }
}

posted @ 2025-08-11 23:08  *珍惜当下*  阅读(5)  评论(0)    收藏  举报