8 面向对象编程
8.5. final 关键字
8.5.1 基本介绍
final 中文意思:最后的, 最终的.
final 可以修饰类、属性、方法和局部变量.
在某些情况下,程序员可能有以下需求, 就会使用到final:
-
- 当不希望类被继承时,可以用final修饰.【案例演示】
-
- 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【案例演示:访问修饰符 final 返回类型 方法名 】
-
- 当不希望类的的某个属性的值被修改,可以用final修饰.【案例演示: public final double TAX_RATE=0.08】
-
- 当不希望某个局部变量被修改, 可以使用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方法/*@Overridepublic 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 使用注意事项和细节讨论
-
- final修饰的属性又叫常量,一般用XX_XX_XX来命名
-
- final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
① 定义时:如public final double TAX_RATE=0.08;
② 在构造器中
③ 在代码块中。
- final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
-
- 如果final修饰的属性是静态的,则初始化的位置只能是
① 定义时 ② 在静态代码块 不能在构造器中赋值。
- 如果final修饰的属性是静态的,则初始化的位置只能是
-
- final类不能继承,但是可以实例化对象。[A2类]
-
- 如果类不是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 { }
-
- 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。
-
- final不能修饰构造方法(即构造器)
-
- final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理。
class Demo{public static final int i=16; //static{System.out.println("~666");} } 此处若调用Demo.i 不会导致类加载故~666不会输出
-
- 包装类(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 抽象类的介绍
-
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{
}
- 用abstract 关键字来修饰一个类时,这个类就叫抽象类
-
- 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
- 用abstract 关键字来修饰一个方法时,这个方法就是抽象方法
-
- 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
-
- 抽象类,是考官比较爱问的知识点, 在框架和设计模式使用较多
8.6.4 抽象类使用的注意事项和细节讨论 AbstractDetail01.java
-
- 抽象类不能被实例化 [举例]
-
- 抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法 [举例]
-
- 一旦类包含了abstract方法,则这个类必须声明为abstract [说明]
-
- 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
-
- 抽象类可以有任意成员【抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等 [举例]
-
- 抽象方法不能有主体,即不能实现.如图所示
abstract void aaa(){}; (此处大括号上有红色叉号示意错误 )
- 抽象方法不能有主体,即不能实现.如图所示
-
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。[举例 A类,B类,C类]
-
- 抽象方法不能使用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 {@Overridepublic 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 模板设计模式能解决的问题
-
- 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
-
- 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
8.7.3 最佳实践
需求
-
- 有多个类,完成不同的任务job
-
- 要求统计得到各自完成任务的时间
-
- 精确性实现 TestTemplate.java
-
- 矫情的自然浓缩
感情的自然浓缩
- 矫情的自然浓缩
- 先用最容易想到的方法-》代码实现
- 分析问题,提出使用模板设计模式
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,完成累加任务@Overridepublic 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方法,完成累乘任务@Overridepublic 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();}
}