日期:2026-07-23
项目:c260723
作者:WanJL
文件:Demo01.java
在父子类继承时,提取子类的共性功能时,有些方法在父类中并没有具体的实现。
例如员工体系:
员工类
|- 开发工程师
|- Java开发工程师
|- 前端开发工程师
|- 测试工程师
|- 后端测试工程师
|- IOS测试工程师
每一个子类的工作内容都是不一样的,所以都需要重写父类的 work() 方法,导致父类的 work() 方法代码没有实际作用。
public class 员工 {
public void work() {
// 每一个子类的工作内容都不一样,此方法体没有意义
}
}
在 Java 中,一个没有方法体的方法,应该定义为 抽象方法;如果类中包含抽象方法,那么这个类必须是 抽象类。
文件:Demo01.java
使用 abstract 关键字来定义抽象类和抽象方法。
定义抽象类:
public abstract class 类名 {
// 成员
}
定义抽象方法:
public abstract 返回值类型 方法名(参数列表);
注意:抽象方法没有方法体(没有
{},直接以分号结尾)。
文件:Demo01.java
| 特性 | 说明 |
|---|---|
| 关键字 | 抽象类和抽象方法必须使用 abstract 关键字修饰 |
| 抽象方法所在类 | 有抽象方法的类必须是抽象类 |
| 抽象类中的抽象方法 | 抽象类中不一定要有抽象方法,但抽象方法必须在抽象类中 |
| 实例化 | 抽象类不能实例化(不能创建对象) |
| 构造方法 | 抽象类可以有构造方法(供子类通过 super() 调用) |
| 子类选择 | ① 重写父类所有的抽象方法;② 子类也是抽象类,直接继承父类的抽象方法 |
| 继承关系 | 抽象父类和具体子类之间是继承关系,子类仍然属于父类类型 |
文件:Animal.java
package course;
public abstract class Animal {
public Animal() {
System.out.println("调用动物的构造方法....");
}
public abstract void eat();
public abstract void sleep();
public abstract void move();
public abstract void play();
}
eat()、sleep()、move()、play()super() 调用文件:Cat.java
package course;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
// 空实现
}
@Override
public void move() {
// 空实现
}
@Override
public void play() {
// 空实现
}
}
extends 继承抽象类 Animal@Override 注解重写父类的所有抽象方法(必须全部重写,否则子类也要声明为抽象类)文件:Dog.java
package course;
public class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
@Override
public void sleep() { }
@Override
public void move() { }
@Override
public void play() { }
}
Animal,重写所有抽象方法eat() 方法没有写 @Override 注解,但仍然是对父类抽象方法的重写(注解只是编译检查,不写也能重写)文件:Test01.java
package course;
public class Test01 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 输出:狗吃肉
}
}
Dog 对象,调用重写后的 eat() 方法文件:Emp.java
package course;
// 有抽象方法的类必须是抽象类
public abstract class Emp {
/**
* 抽象方法——没有方法体
*/
public abstract void work();
}
work()abstract 修饰文件:javaEmp.java
package course;
public class javaEmp extends Emp {
@Override
public void work() {
System.out.println("Java开发工程师在开发Java项目");
}
}
work() 抽象方法,给出具体的实现前端开发工程师、后端测试工程师 等)文件:BaseService.java
package course;
public abstract class BaseService {
public abstract void add();
public abstract void remove();
public abstract void update();
public abstract void query();
}
文件:UserService.java
package course;
public class UserService extends BaseService {
@Override
public void add() { }
@Override
public void remove() { }
@Override
public void update() { }
@Override
public void query() { }
}
文件:Demo02.java
在实际开发中,有些类、方法或变量不希望被改变:
final 关键字就是用来解决这些问题的——用于限制类、方法或变量被修改。
文件:Demo02.java、FinalClass.java
// 使用 final 修饰的类,不能被继承
public final class 类名 {
// ...
}
应用场景:
Math、Arrays),只能被调用,不能被继承String 类就是用 final 修饰的,不能被继承文件:FinalClass.java、SubClass.java
// 父类中的 final 方法
public class FinalClass {
final String SPRING = "春天";
public final void showInfo() {
System.out.printf(SPRING);
}
}
// 子类无法重写父类的 final 方法
public class SubClass extends FinalClass {
// ❌ 编译报错:不能重写父类的 final 方法
// public final void showInfo() {
// System.out.printf(SPRING);
// }
}
FinalClass 类本身没有用 final 修饰,所以 SubClass 可以继承它FinalClass 中的 showInfo() 方法用 final 修饰,子类 不能重写 该方法SubClass 中的重写代码,否则编译报错文件:Demo02.java、Test02.java
// final 修饰基本类型变量——值不可修改
final int i = 10;
// i = 20; // ❌ 编译报错:不能修改 final 变量的值
// final 修饰引用类型变量——引用地址不可变,但对象内容可以修改
final Person p = new Person("1001", "张三");
// p = new Person("1002", "李四"); // ❌ 编译报错:不能修改引用地址
p.setName("李四"); // ✅ 可以修改对象的属性
p.setId("1002");
System.out.printf(p.showInfo()); // 输出:Person{id='1002', name='李四'}
常量命名规范:Java 中一般使用 final 修饰的变量作为常量,常量名称通常 全部大写,单词之间用下划线 _ 分隔。例如:
final String SPRING = "春天"; // FinalClass.java
final int MAX_STUDENT_COUNT = 100;
final double PI = 3.1415926;
关键区别:
| 变量类型 | final 的作用 | 示例 |
|---|---|---|
| 基本类型(int、double 等) | 数值本身不可修改 | final int i = 10; → i=20; 报错 |
| 引用类型(对象、数组等) | 引用地址不可修改,但对象 属性 可修改 | final Person p = new Person(...) → p.setName(...) 可以 |
注意:
final修饰引用类型变量时,只是保证这个变量不再指向其他对象,但该对象内部的 成员变量 仍然可以通过 setter 修改。
| 关键字 | 修饰类 | 修饰方法 | 修饰变量 |
|---|---|---|---|
abstract |
类必须被继承 | 方法必须在子类中实现(无方法体) | 不能修饰变量 |
final |
类不能被继承 | 方法不能被重写 | 变量变成常量 |
⚠️
final和abstract不能同时使用,二者语义完全冲突:
abstract表示「需要被继承/重写」final表示「不能被继承/重写」
代码位于
exercise/exercise02包中,对应课堂练习题第 2 题。
文件:exercise/exercise02/Shape.java
package exercise.exercise02;
public abstract class Shape {
private String color;
public Shape(String color) {
this.color = color;
}
/** 计算面积——抽象方法 */
public abstract double getArea();
/** 计算周长——抽象方法 */
public abstract double getPerimeter();
/** 输出信息——普通方法(调用抽象方法) */
public void showInfo() {
System.out.print("颜色:" + this.color);
System.out.printf(",面积:%.2f", getArea());
System.out.printf(",周长:%.2f", getPerimeter());
System.out.println();
}
}
getArea() 和 getPerimeter()showInfo(),内部调用抽象方法输出结果文件:exercise/exercise02/Circle.java
package exercise.exercise02;
public class Circle extends Shape {
private double radius;
private double pi = 3.1415926;
public Circle(String color, double radius) {
super(color); // 调用父类构造方法
this.radius = radius; // 初始化子类属性
}
@Override
public double getArea() {
return pi * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * pi * radius;
}
}
文件:exercise/exercise02/Test.java
package exercise.exercise02;
public class Test {
public static void main(String[] args) {
Circle circle = new Circle("红色", 5);
circle.showInfo();
// 输出:颜色:红色,面积:78.54,周长:31.42
}
}
文件:Demo03.java
模板方法模式是抽象类的经典应用场景。核心思想:父类定义算法的「骨架」(通用结构),子类实现特定的「步骤」(定制化需求)。
Java中的设计模式:模板模式
是把抽象类整体看成是一个模版,模板中不能决定的东西,抽象成方法
让使用模板的类(也就是抽象类的子类)去重写抽象方法,实现定制化的需求。
模板模式的优势:
模板已经定义了通用的结构,使用者只需要关心自己需要实现的功能就可以了
文件:MyOneDayTemplate.java
一般模板类分为两部分:
| 组成部分 | 关键字 | 说明 |
|---|---|---|
| 模板方法 | final |
定义执行步骤的骨架,子类不能修改(不能重写) |
| 抽象方法 | abstract |
需要被子类重写的方法,具体行为由子类决定 |
package course;
public abstract class MyOneDayTemplate {
/**
* 模板方法——使用 final 修饰,只能使用,不能修改
*/
public final void myOneDay() {
System.out.print("《我的一天》");
System.out.print("今天我吃了");
this.eat(); // 步骤 1:吃(抽象方法,子类实现)
System.out.print("然后我又");
this.sleep(); // 步骤 2:睡(抽象方法,子类实现)
System.out.print("最后我又玩了");
this.play(); // 步骤 3:玩(抽象方法,子类实现)
System.out.print("好开心哟~");
System.out.println();
}
// 抽象方法——由子类提供具体的实现
public abstract void eat();
public abstract void sleep();
public abstract void play();
}
关键点:
myOneDay()用final修饰,确保子类不能篡改这个方法的结构。子类只需关注eat()、sleep()、play()三个抽象方法的具体实现。
文件:ZhSanOneDay.java、LiSiOneDay.java
// 张三的一天
public class ZhSanOneDay extends MyOneDayTemplate {
@Override
public void eat() {
System.out.println("饭");
}
@Override
public void sleep() {
System.out.println("菜");
}
@Override
public void play() {
System.out.println("小狗");
}
}
// 李四的一天
public class LiSiOneDay extends MyOneDayTemplate {
@Override
public void eat() {
System.out.println("饭");
}
@Override
public void sleep() {
System.out.println("菜");
}
@Override
public void play() {
System.out.println("小狗");
}
}
文件:Test03.java
public class Test03 {
public static void main(String[] args) {
ZhSanOneDay zhSanOneDay = new ZhSanOneDay();
zhSanOneDay.myOneDay();
LiSiOneDay li = new LiSiOneDay();
li.myOneDay();
}
}
预期输出:
《我的一天》今天我吃了饭然后我又菜最后我又玩了好开心哟~
《我的一天》今天我吃了饭然后我又菜最后我又玩了好开心哟~
| 优势 | 说明 |
|---|---|
| 复用性 | 公共的流程代码写在父类的模板方法中,所有子类共用 |
| 扩展性 | 新增一种「一天」只需新建子类,重写抽象方法即可 |
| 规范性 | 父类通过模板方法强制规定了执行顺序和结构,子类无法改变 |
| 关注分离 | 使用者只需关心自己的定制逻辑(吃什么/睡什么/玩什么),不用关注整体流程 |
| 对比维度 | 普通继承 + 方法重写 | 模板方法模式 |
|---|---|---|
| 父类方法 | 普通方法,子类可选择重写或不重写 | 模板方法用 final 修饰,禁止重写 |
| 执行流程 | 由调用方决定调用顺序 | 父类模板方法定义固定执行顺序 |
| 强制约束 | 弱——子类可以不重写 | 强——子类必须重写抽象方法 |
| 设计意图 | 复用代码 | 定义算法骨架 + 强制子类实现特定步骤 |
文件:Demo04.java
在 Java 中,使用 {} 括起来的代码称为 代码块。
{
// 代码....
}
Java 中分为三种代码块:
| 代码块类型 | 位置 | 执行时机 |
|---|---|---|
| 局部代码块 | 方法或构造方法内部 | 调用到该方法时执行 |
| 构造代码块 | 方法外部、类内部 | 每次创建对象时执行(在构造方法之前) |
| 静态代码块 | 类内部,使用 static 修饰 |
类加载时执行(仅一次) |
文件:Demo04.java
package course;
public class Demo04 {
public Demo04() {
// 局部代码块——存在于方法或构造方法里面
{
System.out.println("我是局部代码块,我存在于方法或构造方法里面");
}
System.out.println("我是构造方法");
}
// 构造代码块——在方法外面类里面
{
System.out.println("我是构造代码块....我是在构造方法前面执行....");
}
// 静态代码块——使用 static 修饰
static {
System.out.println("我是静态代码块....我最先执行");
}
public static void main(String[] args) {
Demo04 d = new Demo04();
}
}
执行 main 方法中的 new Demo04(),输出结果:
我是静态代码块....我最先执行
我是构造代码块....我是在构造方法前面执行....
我是局部代码块,我存在于方法或构造方法里面
我是构造方法
执行流程说明:
| 步骤 | 执行的代码 | 说明 |
|---|---|---|
| ① | static { ... } |
类加载时执行静态代码块(仅执行一次) |
| ② | { ... }(构造代码块) |
每次创建对象时,在构造方法之前执行 |
| ③ | { ... }(局部代码块) |
构造方法内部按顺序执行到局部代码块 |
| ④ | 构造方法体 |
构造方法的其他代码最后执行 |
注意:如果
main方法中只声明变量Demo04 d;而不创建对象,则只有静态代码块执行,构造代码块和构造方法都不会执行。
| 代码块类型 | 执行次数 | 执行时机 | 典型用途 |
|---|---|---|---|
| 静态代码块 | 1 次(类加载时) | 最早执行 | 加载配置文件、初始化静态资源 |
| 构造代码块 | 每次创建对象 | 在构造方法之前 | 提取多个构造方法的共同代码 |
| 局部代码块 | 调用到该位置时 | 方法执行过程中 | 限制局部变量的作用域 |
构造代码块的一个重要特性:把里面的代码在运行的时候,整合到被调用的构造方法中。这意味着:
避免多个构造方法中的代码重复
public class Example {
// 构造代码块——所有构造方法都会执行
{
System.out.println("初始化公共资源...");
}
public Example() {
System.out.println("无参构造");
}
public Example(String name) {
System.out.println("有参构造:" + name);
}
}
文件:Demo05.java
Java 中的继承是 单继承,一个子类只能直接继承一个父类。这种方式逻辑清晰,但扩展性较低。
学生类 extends 人类
鹦鹉 extends 鸟类
鸟人 extends ? ← 既有人类的特征,又有鸟类的飞行能力,单继承无法解决
为了解决单继承带来的局限性,Java 提出了 接口 的概念。
接口(Interface) 是一种公共规范标准,只要符合这个规范标准,大家都可以通用。
鸟人 extends 人类 实现 飞行接口 ← 继承父类 + 实现接口,两者互补
接口存在的意义:
俗话说的好:「单继承 + 多实现」——一个类只能继承一个父类,但可以同时实现多个接口。
文件:Demo05.java、MyInterface.java
接口的关键字是 interface。接口也是一种引用数据类型(或者说接口也是一种类)。
public interface 接口名 {
// 成员
}
接口的成员特点:
| 成员类型 | 默认修饰符 | 说明 |
|---|---|---|
| 变量 | public static final |
接口中定义的变量都是 公开的静态常量 |
| 方法 | public abstract |
接口中定义的方法都是 公开的抽象方法 |
public interface MyInterface {
// 变量——默认使用 public static final
public static final String name = "张三";
// 方法——默认使用 public abstract
public abstract void method();
}
注意:接口和抽象类一样 不能实例化(不能创建对象),且接口 没有构造方法。
文件:Demo05.java
子类继承接口称为「实现 接口」,使用关键字 implements。
public class 实现类 implements 接口 {
// 必须重写(实现)接口中所有的抽象方法
}
// 类可以同时继承父类 + 实现接口
public class 子类 extends 父类 implements 接口1, 接口2, ... {
// 必须重写所有接口中的抽象方法
}
接口的子类(实现类)规则:
接口与抽象类的对比:
| 对比维度 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class |
interface |
| 构造方法 | 可以有 | 不能有 |
| 成员变量 | 可以是任意类型 | 只能是 public static final 常量 |
| 成员方法 | 可以有抽象/非抽象方法 | 只能是 public abstract 抽象方法 |
| 子类方式 | extends(继承) |
implements(实现) |
| 单/多 | 单继承 | 可同时实现多个接口 |
文件:Usb.java
package course;
public interface Usb {
void startUsb(); // 默认 public abstract
void useUsb(); // 默认 public abstract
void stopUsb(); // 默认 public abstract
}
USB 接口定义了一个规范标准:任何 USB 设备都必须具备「启动 → 使用 → 关闭」三个行为。
文件:Mouse.java
public class Mouse {
private String brand;
private double price;
public Mouse(String brand, double price) {
this.brand = brand;
this.price = price;
}
// getter/setter 省略...
public void showInfo() {
System.out.println("品牌:" + brand + ",价格:" + price);
}
}
文件:LogiMouse.java
// 继承父类 Mouse + 实现 Usb 接口
public class LogiMouse extends Mouse implements Usb {
public LogiMouse(String brand, double price) {
super(brand, price);
}
@Override
public void startUsb() {
System.out.println("罗技鼠标启动");
}
@Override
public void useUsb() {
System.out.println("罗技鼠标使用");
}
@Override
public void stopUsb() {
System.out.println("罗技鼠标关闭");
}
}
文件:ROGMouse.java
public class ROGMouse extends Mouse implements Usb {
public ROGMouse(String brand, double price) {
super(brand, price);
}
@Override
public void startUsb() {
System.out.println("ROG鼠标启动");
}
@Override
public void useUsb() {
System.out.println("ROG鼠标使用");
}
@Override
public void stopUsb() {
System.out.println("ROG鼠标关闭");
}
}
文件:Test04.java
public class Test04 {
public static void main(String[] args) {
LogiMouse lm = new LogiMouse("罗技", 13.6);
lm.showInfo(); // 调用父类 Mouse 的方法
lm.startUsb(); // 实现 Usb 接口的抽象方法
lm.useUsb(); // 实现 Usb 接口的抽象方法
lm.stopUsb(); // 实现 Usb 接口的抽象方法
}
}
预期输出:
品牌:罗技,价格:13.6
罗技鼠标启动
罗技鼠标使用
罗技鼠标关闭
┌──────────┐ ┌──────────┐
│ Mouse │ │ Usb │
│ (父类) │ │ (接口) │
└────┬─────┘ └────┬─────┘
│ extends │ implements
┌────────┴──────────┐ │
│ │ │
┌───┴────────┐ ┌──────┴─────┐ │
│ LogiMouse │ │ ROGMouse ├────┘
│ extends │ │ extends │
│ Mouse │ │ Mouse │
│ implements │ │ implements│
│ Usb │ │ Usb │
└────────────┘ └────────────┘
文件:Demo05.java
接口与接口:接口之间可以 多继承(一个子接口可以继承多个父接口)
// 类实现多个接口
public class MyClass implements InterfaceA, InterfaceB, InterfaceC {
// 必须重写所有接口中的抽象方法
}
// 接口继承多个接口
public interface SubInterface extends InterfaceA, InterfaceB {
// 子接口继承多个父接口的方法
}
文件:Demo06.java
传统接口中的方法都是 public abstract 抽象方法(没有方法体)。从 JDK8 开始,接口的组成逐渐丰富:
| JDK 版本 | 新增特性 | 说明 |
|---|---|---|
| JDK1.0~7 | 抽象方法 + 常量 | 方法只能是 public abstract,变量只能是 public static final |
| JDK 8 | 默认方法(default) | 带有方法体,实现类不强制重写(可选重写,重写时去掉 default) |
| JDK 8 | 静态方法(static) | 带有方法体,通过 接口名 直接调用 |
| JDK 9 | 私有方法(private) | 带有方法体,用于抽取多个默认方法/静态方法中的公共代码 |
// 不同版本 JDK 接口的组成
public interface 接口名 {
// JDK1.0~7:抽象方法 + 常量
public static final String NAME = "常量";
public abstract void abstractMethod();
// JDK8:默认方法(带方法体,不强制重写)
public default void defaultMethod() {
// 方法体...
}
// JDK8:静态方法(带方法体,通过接口名调用)
public static void staticMethod() {
// 方法体...
}
// JDK9:私有方法(带方法体,抽取公共代码)
private void privateMethod() {
// 方法体...
}
}
文件:MyDefaultInterface.java
public interface MyDefaultInterface {
// 默认方法——带有方法体,不强制实现类重写
default void method() {
this.privateMethod();
System.out.println("我是接口的默认方法......");
}
default void method02() {
this.privateMethod();
System.out.println("我是接口的默认方法......");
}
// 抽象方法——实现类必须重写
void add(int a, int b);
}
默认方法的特点:
| 特性 | 说明 |
|---|---|
| 关键字 | default(不能省略,public 可以省略) |
| 方法体 | 可以有方法体(不再是纯抽象) |
| 是否强制重写 | 不强制,实现类可以选择重写或不重写 |
| 重写时的注意点 | 重写时 去掉 default 关键字 |
| 多接口冲突 | 多个接口中存在相同签名的默认方法时,实现类 必须重写 该方法 |
文件:MyDefaultInterface.java、Demo06.java
引入原因:JDK8 允许接口定义带方法体的默认方法和静态方法后,可能出现多个默认方法/静态方法中包含 相同代码 的情况。需要把这段代码抽取成共性方法,但又不想被外部使用,所以 JDK9 增加了私有方法。
public interface MyDefaultInterface {
// 多个默认方法调用私有方法,避免代码重复
default void method() {
this.privateMethod(); // 调用私有方法
System.out.println("我是接口的默认方法......");
}
default void method02() {
this.privateMethod(); // 调用私有方法
System.out.println("我是接口的默认方法......");
}
// JDK9 私有方法——抽取多个默认方法的公共代码
private void privateMethod() {
System.out.println("我是属于MyDefaultInterface的我又n行代码...");
}
}
注意事项:
文件:MyDefaultInterface.java
public interface MyDefaultInterface {
// 静态方法——直接通过接口名调用
static void staticMethod() {
System.out.println("我是接口的静态方法");
}
}
调用方式:接口名.静态方法名(),例如:
MyDefaultInterface.staticMethod();
文件:MyDefaultInterfaceImpl.java、Test05.java
// 实现类——只需要重写抽象方法,默认方法可以不用重写
public class MyDefaultInterfaceImpl implements MyDefaultInterface {
@Override
public void add(int a, int b) {
System.out.println(a + b);
}
// method() 和 method02() 是默认方法,不强制重写
}
public class Test05 {
public static void main(String[] args) {
MyDefaultInterfaceImpl mdii = new MyDefaultInterfaceImpl();
mdii.add(1, 2); // 调用实现类重写后的抽象方法 → 输出:3
mdii.method(); // 调用接口的默认方法 → 输出:我是属于... + 我是接口的默认方法......
MyDefaultInterface.staticMethod(); // 通过接口名直接调用静态方法 → 输出:我是接口的静态方法
}
}
预期输出:
3
我是属于MyDefaultInterface的我又n行代码...
我是接口的默认方法......
我是接口的静态方法
| 成员类型 | 修饰符 | JDK 版本 | 是否有方法体 | 实现类是否必须重写 | 调用方式 |
|---|---|---|---|---|---|
| 常量 | public static final |
所有版本 | — | — | 接口名.常量名 |
| 抽象方法 | public abstract |
所有版本 | ❌ 无 | ✅ 必须 | 对象调用 |
| 默认方法 | default |
JDK 8+ | ✅ 有 | ❌ 可选(冲突时强制) | 对象调用 |
| 静态方法 | static |
JDK 8+ | ✅ 有 | — | 接口名.方法名() |
| 私有方法 | private |
JDK 9+ | ✅ 有 | — | 接口内部调用 |
练习目标:理解抽象类、接口、代码块的基本使用
public abstract class 声明,定义抽象方法时不要写方法体extends 继承抽象类,使用 @Override 重写所有抽象方法Q1:抽象类能不能被实例化?
不能。抽象类不能直接
new创建对象。
Q2:抽象类中一定要有抽象方法吗?
不一定。抽象类中可以没有抽象方法,但有抽象方法的类必须是抽象类。
Q3:抽象类的子类必须重写所有抽象方法吗?
不一定。子类有两个选择:① 重写父类所有抽象方法变为具体子类;② 子类也声明为抽象类,继承父类的抽象方法。
Q4:抽象类可以有构造方法吗?
可以。虽然抽象类不能直接创建对象,但其构造方法可以在子类实例化时通过
super()调用,用于初始化父类成员。
Q5:abstract 和 final 能否同时修饰一个类或方法?
不能。
abstract表示需要被继承/重写,final表示不能被继承/重写,二者语义冲突。
Q6:final 修饰引用类型变量时,对象内容还能修改吗?
可以。
final修饰引用类型变量时,只是保证引用地址不变(不能重新指向其他对象),但该对象内部的成员变量仍然可以通过 setter 修改。
Q7:final 修饰的类有哪些常见例子?
String类、Math类、Integer等包装类都是用final修饰的,它们不能被继承。
Q8:什么是模板方法设计模式?它的核心思想是什么?
模板方法模式(Template Method Pattern)是抽象类的经典应用场景。核心思想是:父类定义算法的「骨架」(通用结构),子类实现特定的「步骤」(定制化需求)。父类中的模板方法用
final修饰禁止重写,抽象方法用abstract修饰强制子类实现。
Q9:模板方法模式中,模板方法为什么用 final 修饰?
为了防止子类篡改算法的执行流程。如果模板方法不被
final修饰,子类可以重写它,破坏父类定义好的步骤结构,失去了「模板」的意义。
Q10:Java 中的代码块有哪几种?它们的执行顺序是什么?
有三种:① 静态代码块(
static {})② 构造代码块({})③ 局部代码块(方法内的{})。执行顺序:静态代码块(类加载时执行一次)→ 构造代码块(每次创建对象时执行,在构造方法之前)→ 构造方法(含内部的局部代码块)。
Q11:构造代码块有什么作用?
构造代码块可以把多个构造方法中共同的代码提取出来,避免重复。每次创建对象时,构造代码块会在构造方法之前自动执行,无论调用的是哪个重载的构造方法。
Q12:接口和抽象类有什么区别?
① 关键字不同:抽象类是
abstract class,接口是interface;② 构造方法:抽象类可以有,接口不能有;③ 成员变量:抽象类可以是任意类型,接口只能是public static final常量;④ 成员方法:抽象类可以有抽象/非抽象方法,接口只能是public abstract抽象方法;⑤ 子类方式:抽象类用extends继承,接口用implements实现;⑥ 单/多:抽象类是单继承,接口可以同时实现多个。
Q13:一个类可以同时继承一个父类并实现多个接口吗?
可以。Java 中的类虽然只能单继承(一个父类),但可以同时实现多个接口。格式:
public class 子类 extends 父类 implements 接口1, 接口2 { ... }。
Q14:接口中的变量默认使用什么修饰符?方法呢?
接口中的变量默认使用
public static final修饰(公开的静态常量),方法默认使用public abstract修饰(公开的抽象方法)。
Q15:接口为什么不能实例化?接口有构造方法吗?
接口没有构造方法,因此不能实例化(不能创建对象)。接口是一种规范标准,需要通过实现类来提供具体的功能实现。
Q16:JDK 8 在接口中新增了哪些特性?
JDK 8 新增了 默认方法(default) 和 静态方法(static),它们都可以带有方法体。默认方法通过对象调用,静态方法通过接口名直接调用。
Q17:接口的默认方法和抽象方法有什么区别?
① 默认方法有方法体,抽象方法没有;② 默认方法用
default修饰,抽象方法用abstract修饰;③ 默认方法不强制实现类重写,抽象方法必须重写;④ 重写默认方法时要去掉default关键字。
Q18:JDK 9 为什么要在接口中增加私有方法?
JDK 8 允许接口中有带方法体的默认方法和静态方法后,可能出现多个默认方法或静态方法包含相同代码的情况。JDK 9 的私有方法就是为了抽取这些公共代码,避免重复,同时又不对外暴露。