跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 包、抽象类与接口详解

Java 包用于组织类并保证唯一性,通过 import 语句引入。抽象类包含抽象方法,不可实例化,用于被继承并提供部分实现。接口定义行为规范,仅含抽象方法和常量,支持多实现。文章详细讲解包的使用规则、抽象类语法与限制、接口定义及多继承模拟,并通过对象排序(Comparable/Comparator)和对象克隆(浅拷贝/深拷贝)实例演示接口应用,最后对比抽象类与接口的核心区别。

狂少发布于 2026/3/15更新于 2026/7/1841 浏览
Java 包、抽象类与接口详解

包

包 (package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性。例如,你在代码中写了一个 Test 类,然后你的同事也可能写一个 Test 类。如果出现两个同名的类,就会冲突,导致代码不能编译通过。

导入包

Java 中已经提供了很多现成的类供我们使用。例如:

public class Test {
    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();
        // 得到一个毫秒级别的时间戳
        System.out.println(date.getTime());
    }
}

可以使用 java.util.Date 这种方式引入 java.util 这个包中的 Date 类。但是这种写法比较麻烦一些,可以使用 import 语句导入包。如果需要使用 java.util 中的其他类,可以使用 import java.util.*,但是我们更建议显式的指定要导入的类名。否则还是容易出现冲突的情况。

注意事项: import 和 C++ 的 #include 差别很大。C++ 必须 #include 来引入其他文件内容,但是 Java 不需要。import 只是为了写代码的时候更方便。import 更类似于 C++ 的 namespace 和 using。

静态导入

使用 import static 可以导入包中的静态的方法和字段。

import static java.lang.System.*;
public class Test {
    public static void main(String[] args) {
        out.println("hello");
    }
}

使用这种方式可以更方便地写一些代码,例如:

import static java.lang.Math.*;
public class Test {
    public static void main(String[] args) {
        double x = 30;
        double y = 40;
        // 静态导入的方式写起来更方便一些。
        // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
        double result = sqrt(pow(x, 2) + pow(y, 2));
        System.out.println(result);
    }
}

将类放入包

基础规则

在文件的最上方加上一个 package 语句指定该代码在哪个包中。包名需要尽量指定成唯一的名字,通常会用公司的域名的颠倒形式(例如 com.bit.demo1)。包名要和代码路径相匹配。例如创建 com.bit.demo1 的包,那么会存在一个对应的路径 com/bit/demo1 来存储代码。如果一个类没有 package 语句,则该类被放到一个默认包中。

常见的系统包

  1. java.lang: 系统常用基础类(String、Object),此包从 JDK1.1 后自动导入。
  2. java.lang.reflect: Java 反射编程包。
  3. java.net: 进行网络编程开发包。
  4. java.sql: 进行数据库开发的支持包。
  5. java.util: 是 Java 提供的工具程序包(集合类等)。
  6. java.io: I/O 编程开发包。

抽象类

语法规则

像这种没有实际工作的方法,我们可以把它设计成一个抽象方法(abstract method),包含抽象方法的类我们称为抽象类(abstract class)。

abstract class Shape {
    abstract public void draw();
}

在 draw 方法前加上 abstract 关键字,表示这是一个抽象方法。同时抽象方法没有方法体(没有 {},不能执行具体代码)。对于包含抽象方法的类,必须加上 abstract 关键字表示这是一个抽象类。

总结

  1. 抽象类是被 abstract 修饰的。
  2. 被 abstract 修饰的方法称为抽象方法,该方法可以没有具体的实现。
  3. 当一个类中含有抽象方法的时候,该类必须使用 abstract 修饰。
  4. 抽象类当中可以有和普通类一样的成员变量和一样的成员方法。
  5. 抽象类是不可以被实例化的。
  6. 抽象类既然不能实例化对象,那么要抽象类干什么?就是为了被继承。
  7. 当一个普通的类继承了这个抽象类之后,这个普通类一定要重写这个抽象类当中所有的抽象方法。
  8. final 和 abstract 是不能同时存在的,抽象方法不能被 private 和 static 修饰!
  9. 当一个抽象类 A 不想被一个普通类 B 继承,此时可以把 B 这个类变成抽象类,那么再当一个普通类 C 继承这个抽象类 B 之后,C 要重写 B 和 A 里面所有的抽象方法。

注意事项

  1. 抽象类不能直接实例化
Shape shape = new Shape(); // 编译出错 Error:(30, 23) java: Shape 是抽象的;无法实例化
  1. 抽象方法不能是 private 的
abstract class Shape { abstract private void draw(); } // 编译出错 Error:(4, 27) java: 非法的修饰符组合:abstract 和 private
  1. 抽象类中可以包含其他的非抽象方法,也可以包含字段。这个非抽象方法和普通方法的规则都是一样的,可以被重写,也可以被子类直接调用
abstract class Shape {
    abstract public void draw();
    void func() { System.out.println("func"); }
}
class Rect extends Shape { ... }
public class Test {
    public static void main(String[] args) {
        Shape shape = new Rect();
        shape.func();
    }
}
// 执行结果 func
  1. 抽象类不一定有抽象方法,但有抽象方法的类一定是抽象类。
  2. 抽象类中可以有构造方法,供子类创建对象时,初始化父类的成员变量。

抽象类的作用

抽象类存在的最大意义就是为了被继承。抽象类本身不能被实例化,要想使用,只能创建该抽象类的子类。然后让子类重写抽象类中的抽象方法。

接口

接口是抽象类的更进一步。抽象类中还可以包含非抽象方法,和字段。而接口中包含的方法都是抽象方法,字段只能包含静态常量。

语法规则

interface IShape {
    void draw();
}
class Cycle implements IShape {
    @Override
    public void draw() {
        System.out.println("○");
    }
}
public class Test {
    public static void main(String[] args) {
        IShape shape = new Rect();
        shape.draw();
    }
}
  1. 使用 interface 定义一个接口。
  2. 接口中的方法一定是抽象方法,因此可以省略 abstract。
  3. 接口中的方法一定是 public,因此可以省略 public。
  4. Cycle 使用 implements 继承接口。此时表达的含义不再是'扩展',而是'实现'。
  5. 在调用的时候同样可以创建一个接口的引用,对应到一个子类的实例。
  6. 接口不能单独被实例化。

接口中只能包含抽象方法。对于字段来说,接口中只能包含静态常量(final static)。

interface IShape {
    void draw();
    public static final int num = 10;
}

其中的 public, static, final 的关键字都可以省略。省略后的 num 仍然表示 public 的静态常量。

文章配图

总结

  1. 使用 interface 来定义一个接口。
  2. 接口当中的成员变量默认是 public static final 的,一般情况下我们不写。
  3. 接口当中的成员方法默认是 public abstract,一般情况下我们不写。
  4. 接口当中不可以有普通的方法。
  5. Java 8 开始允许在接口当中定义一个 default 方法,可以有具体的实现的。
  6. 接口当中的方法如果是 static 修饰的方法那么是可以有具体的实现的。
  7. 接口不能通过 new 关键字进行实例化。
  8. 类和接口之间可以通过关键字 implements 来实现接口。
  9. 接口也可以发生向上转型和动态绑定的。
  10. 当一个类实现接口当中的方法之后,当前类当中的方法不能不加 public。
  11. 接口当中不能有构造方法和代码块。
  12. 一个接口也会产生独立的字节码文件。

实现多个接口

有的时候我们需要让一个类同时继承自多个父类。这件事情在有些编程语言通过多继承的方式来实现的。然而 Java 中只支持单继承,一个类只能 extends 一个父类。但是可以同时实现多个接口,也能达到多继承类似的效果。

现在我们通过类来表示一组动物。

class Animal {
    protected String name;
    public Animal(String name) {
        this.name = name;
    }
}

另外我们再提供一组接口,分别表示'会飞的'、'会跑的'、'会游泳的'。

interface IFlying { void fly(); }
interface IRunning { void run(); }
interface ISwimming { void swim(); }

接下来我们创建几个具体的动物猫,是会跑的。

class Cat extends Animal implements IRunning {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用四条腿跑");
    }
}

鱼,是会游的。

class Fish extends Animal implements ISwimming {
    public Fish(String name) {
        super(name);
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在用尾巴游泳");
    }
}

青蛙,既能跑,又能游(两栖动物)。

class Frog extends Animal implements IRunning, ISwimming {
    public Frog(String name) {
        super(name);
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在往前跳");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在蹬腿游泳");
    }
}

还有一种神奇的动物,水陆空三栖,叫做'鸭子'。

class Duck extends Animal implements IRunning, ISwimming, IFlying {
    public Duck(String name) {
        super(name);
    }
    @Override
    public void fly() {
        System.out.println(this.name + "正在用翅膀飞");
    }
    @Override
    public void run() {
        System.out.println(this.name + "正在用两条腿跑");
    }
    @Override
    public void swim() {
        System.out.println(this.name + "正在漂在水上");
    }
}

上面的代码展示了 Java 面向对象编程中最常见的用法:一个类继承一个父类,同时实现多种接口。 继承表达的含义是 is-a 语义,而接口表达的含义是具有 xxx 特性。 猫是一种动物,具有会跑的特性。 青蛙也是一种动物,既能跑,也能游泳。 鸭子也是一种动物,既能跑,也能游,还能飞。

接口间的继承

接口可以继承一个接口,达到复用的效果。使用 extends 关键字。

interface IRunning { void run(); }
interface ISwimming { void swim(); }
// 两栖的动物,既能跑,也能游
interface IAmphibious extends IRunning, ISwimming { }
class Frog implements IAmphibious { ... }

通过接口继承创建一个新的接口 IAmphibious 表示'两栖的'。此时实现接口创建的 Frog 类,就继续要实现 run 方法,也需要实现 swim 方法。

接口使用实例

给对象数组排序

class Student {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}
public class test {
    public static void main(String[] args) {
        Student[] students = new Student[] {
            new Student("张三", 95),
            new Student("李四", 96),
            new Student("王五", 97),
            new Student("赵六", 92),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

运行会发现,抛异常了,原因是我们是对学生对象进行排序的,而非像整数这样显而易见能比大小的,因此我们需要实现 Comparable 接口,并实现其 compareTo() 方法。

文章配图

(法一)实现 Comparable 接口的 compareTo() 方法
class Student implements Comparable<Student> {
    private String name;
    private int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
    // @Override
    // public int compareTo(Object o) {
    //     Student s=(Student)o;
    //     return this.score-s.score;
    // }
    @Override
    public int compareTo(Student o) {
        return this.score-o.score;
    }
}
public class test {
    public static void main(String[] args) {
        Student[] students = new Student[] {
            new Student("张三", 95),
            new Student("李四", 96),
            new Student("王五", 97),
            new Student("赵六", 92),
        };
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));
    }
}

运行结果

文章配图

(法二)实现 Comparator 比较器的 compare() 方法
class Student {
    public String name;
    public int score;
    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }
    @Override
    public String toString() {
        return "[" + this.name + ":" + this.score + "]";
    }
}
class AgeComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.score-o2.score;
    }
}
class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
public class test {
    public static void main(String[] args) {
        Student student1 = new Student("zhangsan",10);
        Student student2 = new Student("lisi",15);
        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1, student2));
        NameComparator nameComparator = new NameComparator();
        System.out.println(nameComparator.compare(student1,student2));
    }
}

运行结果

文章配图

Clonable 接口和深拷贝

Java 中内置了一些很有用的接口,Clonable 就是其中之一。 Object 类中存在一个 clone 方法,调用这个方法可以创建一个对象的'拷贝'。但是要想合法调用 clone 方法,必须要先实现 Clonable 接口,否则就会抛出 CloneNotSupportedException 异常。

class Money {
    public double money = 19.9;
}
class Person implements Cloneable {
    public int age;
    public Money m;
    public Person(int age) {
        this.age = age;
        this.m = new Money();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Person{" + " age=" + age + '}';
    }
}
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(10);
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("==========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }
}

运行结果

文章配图

与我们预期的 19.9 99.99 不符,显然是因为这里是浅拷贝,因此我们需要对 m 实现深拷贝。

原因如下图:

文章配图

实现深拷贝后

class Money implements Cloneable {
    public double money = 19.9;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person implements Cloneable {
    public int age;
    public Money m;
    public Person(int age) {
        this.age = age;
        this.m = new Money();
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tmp = (Person) super.clone();
        tmp.m = (Money) this.m.clone();
        return tmp;
    }
    @Override
    public String toString() {
        return "Person{" + " age=" + age + '}';
    }
}
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person(10);
        Person person2 = (Person)person1.clone();
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
        System.out.println("==========================");
        person2.m.money = 99.99;
        System.out.println(person1.m.money);
        System.out.println(person2.m.money);
    }
}

运行结果:

文章配图

实现方法如下图:

文章配图

抽象类和接口的区别

核心区别:抽象类中可以包含普通方法和普通字段,这样的普通方法和字段可以被子类直接使用(不必重写),而接口中不能包含普通方法,子类必须重写所有的抽象方法。

文章配图

目录

  1. 包
  2. 导入包
  3. 静态导入
  4. 将类放入包
  5. 常见的系统包
  6. 抽象类
  7. 语法规则
  8. 注意事项
  9. 抽象类的作用
  10. 接口
  11. 语法规则
  12. 实现多个接口
  13. 接口间的继承
  14. 接口使用实例
  15. (法一)实现 Comparable 接口的 compareTo() 方法
  16. (法二)实现 Comparator 比较器的 compare() 方法
  17. Clonable 接口和深拷贝
  18. 抽象类和接口的区别
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Python 实现智能 PDF 文档助手 AI 小工具
  • Android 设备使用 Termux 安装 llama.cpp 并启动 WebUI
  • AI 技术:8 个实用 AI 绘画与生成工具推荐
  • 数据库迁移 TCO 全景账本:MySQL 替代中的隐性成本与工程化工具链实测
  • NestJS 接口响应 Message 编写规范与 API 提示标准化
  • Mycat 实践指南:一致性 Hash 分片下的水平分表详解
  • VR + 具身智能 + 人形机器人:通往现实世界的智能接口
  • Android 开发春招准备指南:面试题汇总与复习策略
  • OpenClaw 多飞书机器人接入与多 Agent 团队协作实战
  • 使用 Gitee、PicGo 和 Typora 搭建免费个人笔记工具
  • 阿里通义千问 Qwen3-Coder:智能代码生成与代理式编程
  • Python 安全有效地处理配置的最佳实践
  • 前端权限控制设计:避免硬编码权限判断
  • Python 网址匹配正则表达式实战
  • Python 基础:输入输出与格式化技巧
  • 腾讯云智能客服 Java 集成实战与生产环境优化
  • OpenClaw 本地 AI 智能体:从入门到实战部署指南
  • Dify MCP Server:将工作流发布为第三方可调用服务
  • Vue Vant van-uploader 文件上传接口封装方法
  • AI 绘画 Face Fusion 人脸融合技巧与云端部署

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online