Java 为什么一个 .java 文件不能写两个 public class?—— 详细解析Java访问修饰符、package的作用、以及“一文件一类”的意义

在写 Java 入门代码时,很多人都会遇到这样的疑惑:

package test;

public class TestClass {
    public static void main(String[] args) {
        TestStudent stu = new TestStudent();
    }
}

// 不能写 public class TestStudent,为什么?
class TestStudent {

}

如果我把下面这个类也改成 public

public class TestStudent {}

IDEA/Eclipse 立刻报错。
这篇文章就把这个问题一次性讲透,并讲清楚这个问题的相关知识:

  1. 为什么一个 .java 文件只能有一个 public 类?
  2. public / protected / 默认 / private 四种访问权限到底怎么用?
  3. package 真正的作用是什么?
  4. 为什么很多时候我们坚持“一文件一类”?


一、为什么一个 .java 文件不能写两个 public class

1. Java 语法规定:一个源文件最多只能有一个 public 顶级类

顶级类指的是直接写在文件里、不是内部类的那种:

public class A {} // 顶级类
class B {}        // 顶级类

Java 允许一个文件中存在多个顶级类,但最多只能有一个是 public

2. Java 语法规定:public 类名必须和文件名完全一致

假设文件名叫:

TestClass.java

那么文件里唯一的 public 类必须是:

public class TestClass {}

如果你写成:

public class TestStudent {}

编译器会报错类似于:

The public type TestStudent must be defined in its own file

因为 public class TestStudent 必须放在 TestStudent.java 文件中。

3. 为什么 Java 要这么规定?

核心原因可以概括为两点:

  • 方便类加载与定位:public 类是对外“公开”的主要入口,要求“类名=文件名”可以让 Java 编译器/JVM 更容易定位对应源码/字节码。
  • 避免项目混乱:如果一个文件多个 public 类,对应文件名只能有一个,会造成管理困难、可读性下降、协作冲突增多。

4. 正确写法是什么?

✅ 写法 A:保持一个 public 类,其他类不加 public

// TestClass.java
package test;

public class TestClass {
    public static void main(String[] args) {
        TestStudent stu = new TestStudent();
    }
}

class TestStudent {

}

✅ 写法 B:如果 TestStudent 也想 public,那就分文件:

// TestClass.java
package test;

public class TestClass {
    public static void main(String[] args) {
        TestStudent stu = new TestStudent();
    }
}
// TestStudent.java
package test;

public class TestStudent {}

二、访问修饰符详解:public / protected / 默认 / private

Java 的访问权限(Access Modifier)是面向对象里非常重要的一块,决定了“谁能访问类/方法/变量”。

1)private(最严格)

  • 只能在本类内部访问
  • 最常用于成员变量(封装)
class Student {
    private int age;

    public void setAge(int age) {
        this.age = age; // ✅ 本类内可访问
    }
}

外部访问会报错:

Student s = new Student();
s.age = 18; // ❌ private 不可访问

2)默认权限(不写修饰符,package-private)

  • 同包可访问
  • 不同包不可访问
class Student { // 默认权限
}

3)protected

  • 同包可访问
  • 不同包的子类也可访问
public class Person {
    protected String name;
}

4)public(最开放)

  • 任何地方都可访问
  • 通常用于对外 API、入口类、公共工具类
public class Student {}

5)一张表总结Java访问权限修饰符

修饰符 本类 同包 不同包子类 不同包非子类
private
默认
protected
public

三、package 的真正作用是什么?不只是“文件夹”这么简单

很多人觉得 package 只是“目录分类”,其实它在 Java 里有非常核心的意义。

1)防止类名冲突

你可以有很多个 Student

  • com.school.Student
  • com.bank.Student
  • com.game.Student

包名 + 类名 才是全限定名(唯一标识)。

( P.S. 你可以联想 Python 中那种 import 导入的包,可以参考我的这篇文章中的相关介绍:【一文搞懂】万字长文带你彻底理解 Python 的 import 导入机制 与 if name == “main“: main() 的原理与作用 。)

2)参与权限控制(默认权限的边界就是包)

默认权限(package-private)本质上是:

“只给同一个 package 的类访问”

这也是你前面例子里 class TestStudent {} 依然能被 TestClass 使用的原因:它们都在 package test 下。

3)组织大型项目结构

真实项目中通常是:

com.company.project
    ├── controller
    ├── service
    ├── dao
    ├── entity

这样可以把职责分层,让项目更可维护。

4)JVM/编译器查找类的依据(包名对应目录)

例如:

package com.test.demo;

对应目录结构应为:

com/test/demo/

否则容易出现类找不到、编译路径不一致等问题。


四、为什么很多时候建议“一文件一类”?

虽然 Java 允许一个文件写多个非 public 类,但实际开发中通常坚持“一文件一类”,原因很现实。

1)符合 Java 语法规则:public 类必须单独一个文件

既然对外公开的类必须单独文件,那自然会形成“一类一文件”的惯例,不然就报错啦~
(👉👈这句话不像是在说这种做法的意义,请读者接着往下看⬇️)

2)更易维护、更清晰

一个文件塞很多类:

  • 不好找
  • 不好读
  • 结构混乱

一个类一个文件:

  • 文件名就是类名
  • 直接定位
  • IDE 搜索更高效

3)便于团队协作(减少 Git 冲突)

多人改同一个文件最容易产生冲突。
拆成多个类文件,协作成本立刻下降。

4)符合面向对象思想(一个类描述一个事物)

面向对象强调职责清晰:
一个类负责一件事,一个文件承载一个类,就更自然。


五、什么时候可以一个文件写多个类?

也不是完全禁止。常见合理场景:

  1. 内部类(Inner Class)
  2. 演示/练习代码
  3. 工具类里放少量小型静态类(不对外暴露)

例如内部类:

public class Outer {
    class Inner {}
}

六、总结

✅ 你遇到的“不能写两个 public class”,结论就是两条硬规则:

  1. 一个 .java 文件最多只能有一个 public 顶级类
  2. public 类名必须与文件名完全一致

同时你也应该掌握:

  • 四种访问修饰符的可见范围(private / 默认 / protected / public)
  • package 不只是目录分类,而是:命名空间 + 权限边界 + 项目组织单位
  • “一文件一类”是实际工程中的最佳实践(维护、协作、规范都更好)
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐