Java入门(抽象类 与 接口)

目录

一、抽象类:为子类搭建骨架的 "模板类"

[1.1 什么是抽象类](#1.1 什么是抽象类)

[1.2 抽象类的语法规则](#1.2 抽象类的语法规则)

[1.3 抽象类的核心特性](#1.3 抽象类的核心特性)

[1.4 抽象类的实战使用](#1.4 抽象类的实战使用)

[1.5 抽象类的设计意义](#1.5 抽象类的设计意义)

二、接口:行为的规范,实现多态的 "桥梁"

[2.1 什么是接口](#2.1 什么是接口)

[2.2 接口的语法规则](#2.2 接口的语法规则)

[2.3 接口的使用:implements 实现](#2.3 接口的使用:implements 实现)

[2.4 接口的核心特性](#2.4 接口的核心特性)

[2.5 类的多实现:突破单继承限制](#2.5 类的多实现:突破单继承限制)

[2.6 接口的多继承:规范的组合](#2.6 接口的多继承:规范的组合)

[2.7 接口的设计意义](#2.7 接口的设计意义)

[三、抽象类 VS 接口:核心区别与使用场景](#三、抽象类 VS 接口:核心区别与使用场景)

核心使用场景

[四、Object 类:所有 Java 类的 "根"](#四、Object 类:所有 Java 类的 "根")

[4.1 toString ():获取对象的字符串表示](#4.1 toString ():获取对象的字符串表示)

[4.2 equals ():对象内容的比较](#4.2 equals ():对象内容的比较)

[4.3 hashCode ():对象的哈希码](#4.3 hashCode ():对象的哈希码)

[4.4 其他常用方法](#4.4 其他常用方法)

五、总结


在 Java 面向对象编程中,抽象类、接口是实现多态和代码解耦的核心机制,而 Object 类作为所有类的根类,为 Java 对象提供了基础行为规范。这三个知识点不仅是日常开发的高频考点,也是面试中考察面向对象思想的重点。本文将从概念、语法、特性、使用场景出发,结合实战代码深入讲解,让你彻底掌握这些核心知识点。

一、抽象类:为子类搭建骨架的 "模板类"

1.1 什么是抽象类

在面向对象的世界里,并非所有类都能描绘具体的对象。如果一个类只包含了子类的通用特征,却无法独立创建实例(因为其部分方法没有具体实现),这样的类就是抽象类

简单来说:抽象类是为子类提供统一模板的类,它包含了抽象方法(无实现体)和普通方法(有实现体),本身不能被实例化,只能被子类继承并实现其抽象方法。

生活类比:水果是一个抽象的概念,苹果、香蕉是具体的水果。水果有 "吃" 的行为,但无法定义具体怎么吃;而苹果可以 "洗了咬着吃",香蕉可以 "剥了皮吃"------ 这里的 "水果类" 就是抽象类,"吃" 就是抽象方法。

1.2 抽象类的语法规则

使用abstract关键字修饰类和方法,抽象方法没有方法体,以分号结尾。

java 复制代码
// 抽象类:用abstract修饰
public abstract class Fruit {
    // 普通属性
    protected String name;
    // 构造方法:抽象类可以有构造方法,供子类初始化父类成员
    public Fruit(String name) {
        this.name = name;
    }
    // 抽象方法:无实现体,子类必须重写
    public abstract void eat();
    // 普通方法:有实现体,子类可直接使用
    public void showName() {
        System.out.println("这是:" + this.name);
    }
}

1.3 抽象类的核心特性

  1. 不能直接实例化Fruit fruit = new Fruit("水果"); 编译直接报错,抽象类只是模板,无法创建具体对象。
  2. 抽象方法不能是 private :抽象方法需要被子类重写,private 修饰的方法子类无法访问,因此abstract private void eat(); 非法。
  3. 抽象方法不能被 final/static 修饰:final 方法不能被重写,static 方法属于类而非实例,均与抽象方法的 "子类重写" 特性冲突。
  4. 子类必须重写所有抽象方法:如果子类不重写,那么子类也必须被定义为抽象类。
  5. 抽象类可以包含普通方法 / 属性 / 构造方法:构造方法用于子类创建对象时初始化父类的成员变量。

1.4 抽象类的实战使用

定义具体的子类继承抽象类,并重写抽象方法:

java 复制代码
// 具体子类:苹果,继承水果抽象类
public class Apple extends Fruit {
    public Apple(String name) {
        super(name); // 调用父类构造方法
    }
    // 必须重写抽象方法eat()
    @Override
    public void eat() {
        System.out.println(name + ":洗干净咬着吃,嘎嘣脆!");
    }
}

// 具体子类:香蕉
public class Banana extends Fruit {
    public Banana(String name) {
        super(name);
    }
    @Override
    public void eat() {
        System.out.println(name + ":剥了皮吃,软糯香甜!");
    }
}

// 测试类
public class TestFruit {
    public static void main(String[] args) {
        Fruit apple = new Apple("红富士苹果");
        apple.showName(); // 调用父类普通方法
        apple.eat(); // 调用子类重写的抽象方法

        Fruit banana = new Banana("小米蕉");
        banana.showName();
        banana.eat();
    }
}

运行结果

复制代码
这是:红富士苹果
红富士苹果:洗干净咬着吃,嘎嘣脆!
这是:小米蕉
小米蕉:剥了皮吃,软糯香甜!

1.5 抽象类的设计意义

有人会问:普通类也能被继承,普通方法也能被重写,为什么非要用抽象类?

核心答案:编译器的强制校验。如果用普通类作为父类,不小心直接实例化父类时编译器不会报错;而抽象类会直接阻止实例化,让问题在编译阶段暴露,避免运行时错误。

抽象类的设计思想是 **"模板方法模式"**:将通用逻辑放在抽象类,具体实现放在子类,保证子类的一致性,同时提高代码复用性。

二、接口:行为的规范,实现多态的 "桥梁"

2.1 什么是接口

接口是多个类的公共行为规范 ,是一种引用数据类型,它只定义行为的标准,不定义行为的具体实现。接口的核心作用是实现解耦多实现,弥补 Java 中类 "单继承" 的限制。

生活类比:USB 接口是一种规范,U 盘、鼠标、键盘只要遵循这个规范,就能插在电脑上使用。电脑不需要关心设备具体是什么,只需要知道设备符合 USB 规范 ------ 这就是接口的 "面向规范编程"。

2.2 接口的语法规则

使用interface关键字定义接口,接口中的方法默认是public abstract,变量默认是public static final(全局常量)。

java 复制代码
// 定义接口:命名建议以I开头,形容词词性
public interface USB {
    // 全局常量:默认public static final
    String VERSION = "USB3.0";
    // 抽象方法:默认public abstract,可省略修饰符
    void connect(); // 连接设备
    void disconnect(); // 断开设备
}

2.3 接口的使用:implements 实现

接口不能直接实例化,需要通过实现类 使用implements关键字实现接口,并重写所有抽象方法

java 复制代码
// 实现类:U盘,实现USB接口
public class UDisk implements USB {
    private String brand;
    public UDisk(String brand) {
        this.brand = brand;
    }
    // 重写接口的所有抽象方法,修饰符必须是public
    @Override
    public void connect() {
        System.out.println(brand + "U盘,遵循" + VERSION + "规范,成功连接电脑!");
    }
    @Override
    public void disconnect() {
        System.out.println(brand + "U盘安全断开连接!");
    }
    // 实现类的独有方法
    public void transferFile() {
        System.out.println("U盘正在传输文件...");
    }
}

// 实现类:鼠标,实现USB接口
public class UsbMouse implements USB {
    private String type;
    public UsbMouse(String type) {
        this.type = type;
    }
    @Override
    public void connect() {
        System.out.println(type + "鼠标,遵循" + VERSION + "规范,成功连接电脑!");
    }
    @Override
    public void disconnect() {
        System.out.println(type + "鼠标断开连接!");
    }
    // 独有方法
    public void click() {
        System.out.println("鼠标左键单击!");
    }
}

2.4 接口的核心特性

  1. 不能实例化USB usb = new USB(); 编译报错。
  2. 方法默认是 public abstract:不能用 private、protected 修饰,重写时也必须是 public。
  3. 变量默认是 public static final:必须赋初始值,且不能被修改。
  4. 无构造方法和静态代码块:接口不是类,没有实例化的过程,因此不需要构造方法。
  5. 实现类必须重写所有抽象方法:否则实现类必须定义为抽象类。
  6. JDK8 + 新特性 :支持default方法(有实现体,子类可重写)和static方法(类方法,通过接口名调用)。

JDK8+ default 方法示例

java 复制代码
public interface USB {
    String VERSION = "USB3.0";
    void connect();
    void disconnect();
    // default方法:有实现体,子类可直接使用或重写
    default void showInfo() {
        System.out.println("这是一个" + VERSION + "接口设备");
    }
}

2.5 类的多实现:突破单继承限制

Java 中类与类之间是单继承 ,但一个类可以实现多个接口,用逗号分隔,这是接口最核心的价值之一。

示例:定义 "会跑"、"会飞"、"会游泳" 三个接口,让 "鸭子" 实现这三个接口:

java 复制代码
// 定义三个行为接口
public interface IRunning {
    void run();
}
public interface IFlying {
    void fly();
}
public interface ISwimming {
    void swim();
}

// 鸭子类:实现多个接口
public class Duck implements IRunning, IFlying, ISwimming {
    private String name;
    public Duck(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        System.out.println(name + "用两条腿快速跑!");
    }
    @Override
    public void fly() {
        System.out.println(name + "扇动翅膀低空飞!");
    }
    @Override
    public void swim() {
        System.out.println(name + "浮在水面用脚蹼游!");
    }
}

// 测试
public class TestDuck {
    public static void main(String[] args) {
        Duck duck = new Duck("唐老鸭");
        duck.run();
        duck.fly();
        duck.swim();
    }
}

运行结果

复制代码
唐老鸭用两条腿快速跑!
唐老鸭扇动翅膀低空飞!
唐老鸭浮在水面用脚蹼游!

2.6 接口的多继承:规范的组合

接口与接口之间可以通过extends实现多继承,将多个接口的规范组合成一个新接口,实现规范的复用。

java 复制代码
// 组合接口:两栖动物 = 会跑 + 会游泳
public interface IAmphibious extends IRunning, ISwimming {
}
// 青蛙类:实现两栖动物接口,只需重写run和swim
public class Frog implements IAmphibious {
    @Override
    public void run() {
        System.out.println("青蛙一蹦一跳地跑!");
    }
    @Override
    public void swim() {
        System.out.println("青蛙蹬着后腿在水里游!");
    }
}

2.7 接口的设计意义

接口的核心设计思想是 **"面向接口编程"**,让程序只依赖于行为规范,而不依赖于具体实现,从而降低代码耦合度,提高扩展性。

比如开发一个支付系统,定义IPay接口,包含pay()方法,然后实现AlipayWechatPayUnionPay等实现类。当需要新增支付方式时,只需新增实现类,无需修改原有代码,完美符合开闭原则

三、抽象类 VS 接口:核心区别与使用场景

抽象类和接口都是实现多态的重要方式,很多初学者容易混淆,我们从结构、权限、继承 / 实现、使用场景四个维度做核心对比:

对比维度 抽象类(abstract class) 接口(interface)
结构组成 普通类 + 抽象方法,可包含普通方法 / 属性 / 构造方法 抽象方法 + 全局常量,JDK8 + 支持 default/static 方法
访问权限 支持 public/protected/default/private 方法默认 public,变量默认 public static final
类的关联方式 子类用 extends单继承 实现类用 implements多实现
接口间关系 可实现多个接口 可 extends 多个接口(多继承)
设计思想 体现is-a的继承关系,为子类搭骨架 体现has-a的特性关系,定义行为规范

核心使用场景

  1. 使用抽象类 :当多个类之间存在继承关系,且有通用的属性和方法时,用抽象类封装通用逻辑。比如:水果类→苹果类 / 香蕉类,动物类→猫类 / 狗类。
  2. 使用接口 :当多个类之间无继承关系,但有共同的行为时,用接口定义行为规范。比如:U 盘 / 鼠标 / 键盘都有 USB 行为,鸭子 / 飞机 / 鸟都有飞的行为。

一句话总结抽象类管 "是什么",接口管 "能做什么"

四、Object 类:所有 Java 类的 "根"

Java 中除了 Object 类本身,所有类都直接或间接继承 Object 类,Object 类是 Java 类体系的根节点。所有对象都可以用 Object 类型接收,它提供了 Java 对象的基础行为,核心方法如下:

4.1 toString ():获取对象的字符串表示

Object 类中toString()的默认实现是:类名@十六进制哈希码,比如com.test.Apple@1b6d3586,这个结果对我们没有实际意义,因此子类通常需要重写 toString (),用于返回对象的具体属性信息。

示例

java 复制代码
public class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    // 重写toString()
    @Override
    public String toString() {
        return "Student{name='" + name + "', score=" + score + "}";
    }
}
// 测试
public class Test {
    public static void main(String[] args) {
        Student s = new Student("张三", 90);
        System.out.println(s); // 等价于System.out.println(s.toString())
    }
}

运行结果Student{name='张三', score=90}

4.2 equals ():对象内容的比较

在 Java 中,==的作用是:

  • 比较基本类型 :比较的是是否相等;
  • 比较引用类型 :比较的是地址是否相等(是否是同一个对象)。

而 Object 类中的equals()方法默认实现就是==,即比较地址。如果我们需要比较对象的内容是否相等,就必须重写 equals () 方法。

示例

java 复制代码
public class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    // 重写equals(),比较name和score是否都相等
    @Override
    public boolean equals(Object obj) {
        // 地址相同,直接返回true
        if (this == obj) return true;
        // obj为null或类型不同,返回false
        if (obj == null || getClass() != obj.getClass()) return false;
        // 向下转型,比较属性
        Student student = (Student) obj;
        return score == student.score && Objects.equals(name, student.name);
    }
}
// 测试
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 90);
        Student s2 = new Student("张三", 90);
        System.out.println(s1 == s2); // false:地址不同
        System.out.println(s1.equals(s2)); // true:内容相同
    }
}

4.3 hashCode ():对象的哈希码

hashCode()方法返回一个 int 类型的哈希码,用于标识对象的存储位置(底层与哈希表相关)。Object 类中 hashCode () 是本地方法,由 C/C++ 实现,默认返回对象的内存地址相关值。

核心规则

  1. 如果两个对象的equals()返回 true,那么它们的hashCode()必须返回相同的值;
  2. 如果两个对象的hashCode()返回相同的值,equals()不一定返回 true(哈希冲突);
  3. 重写equals()时,必须重写 hashCode (),否则会违反上述规则。

示例 :使用Objects.hash()快速生成哈希码

java 复制代码
public class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public boolean equals(Object obj) {
        // 省略实现,同上文
    }
    // 重写hashCode()
    @Override
    public int hashCode() {
        return Objects.hash(name, score); // 根据属性生成哈希码
    }
}
// 测试
public class Test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 90);
        Student s2 = new Student("张三", 90);
        System.out.println(s1.hashCode()); // 2441392
        System.out.println(s2.hashCode()); // 2441392
    }
}

4.4 其他常用方法

  • clone():创建对象的拷贝,分为浅拷贝深拷贝 ,使用前需要实现Cloneable接口;
  • getClass():返回对象的运行时类,用于反射;
  • wait()/notify()/notifyAll():用于多线程的同步通信;
  • finalize():对象被垃圾回收时调用,已被 JDK9 标记为过时,推荐使用显式的资源释放方式。

五、总结

本文围绕 Java 核心的抽象类、接口和 Object 类展开,核心知识点可以总结为以下几点:

  1. 抽象类 是带抽象方法的模板类,不能实例化,子类必须重写其抽象方法,用于封装子类的通用逻辑,体现is-a关系;
  2. 接口 是行为的规范,不能实例化,实现类必须重写其所有方法,支持多实现和多继承,弥补 Java 单继承的限制,体现has-a关系;
  3. 抽象类管 "是什么",接口管 "能做什么",这是两者最核心的设计区别;
  4. Object 类 是所有 Java 类的根类,提供了toString()equals()hashCode()等基础方法,子类通常需要重写这些方法以满足实际业务需求;
  5. 重写equals()时必须重写hashCode(),保证两者的规则一致性。

这些知识点是 Java 面向对象编程的基石,无论是日常开发中的代码设计,还是面试中的逻辑考察,都占据着重要地位。希望通过本文的讲解,你能真正理解其设计思想,并能在实际开发中灵活运用。

编程之路,道阻且长,行则将至。掌握这些核心机制,让你的 Java 代码更具扩展性、可读性和优雅性!

相关推荐
hanbr2 小时前
C++ string类模拟实现(完整版,含全运算符重载)
java·开发语言
xUxIAOrUIII2 小时前
【Go每日面试题】内存管理
java·开发语言·golang
勇闯逆流河2 小时前
【Linux】linux进程概念(fork,进程状态,僵尸进程,孤儿进程)
linux·运维·服务器·开发语言·c++
森屿山茶2 小时前
hot100题解 —— 146.LRU缓存
java·开发语言
gameboy0312 小时前
SpringbootActuator未授权访问漏洞
java
⑩-2 小时前
API 网关的作用?Spring Cloud Gateway 原理?
java·服务器·网络·spring cloud
大傻^2 小时前
LangChain4j 记忆架构:ChatMemory、持久化与跨会话状态
java·人工智能·windows·架构·langchain4j
vx-bot5556662 小时前
企业微信ipad协议的消息扩展字段与业务数据注入
java·企业微信·ipad
buyulian2 小时前
Bug防御体系:技术方案的优与劣
java·经验分享·bug·软件工程