本期是Java开发教程系列的第三期,本期主要向各位介绍两个"组织代码"的基础知识——分包和注释.若你已经熟悉 package/import 和 javadoc,可以跳过本期内容.
每期叠甲:
时间仓促,水平有限,文中难免存在缺点和错误,恳请广大开发者和从业者指正.
一、为什么需要包
第 2 期里,我们的 Student 和 Main 都直接放在默认包里——只要不写 package 声明,类就属于默认包.练习时没问题,但真实项目会有两个麻烦:
文件一多就没法找:几十上百个类全堆在一个文件夹里,想找"学生相关的"得来回翻
类名会撞车:两个功能没法写相同的类名
包(package)就是为了解决这两件事:把类按功能分到不同的"文件夹"里;同名的类只要在不同包里,就不冲突.
二、声明包:package
在 .java 文件的第一行写:
package com.mycodes.demo;
public class Student {
// ...
}
package 声明这个类属于哪个包,必须写在文件第一行,前面不能有其他代码(注释除外)
包名对应磁盘上的目录结构:包 com.mycodes.demo 对应目录 com/mycodes/demo/,类文件就放在这个目录里
包名规范:全小写,一般用反域名.比如你的网站是 mycodes.com,包名就用 com.mycodes.xxx.域名全球唯一,反着写就能避免全世界的类撞包名
不写 package 就是默认包.默认包不能被 import,只适合练习用,正式项目不要用
三、导入:import
不同包里的类,要先 import 进来才能直接用:
import com.mycodes.demo.Student;
public class Main {
public static void main(String[] args) {
Student s = new Student(); // 现在可以直接用 Student
}
}
- 想一次性引入一个包里的一堆类,可以偷懒用通配符 `*`:
import java.util.\*; // 引入 java.util 包里的所有类
但 * 有局限,别依赖它:
只管这一个包,不含子包:import java.util.* 只导入 java.util 包里直接定义的类,java.util.regex 这类子包里的类还是要单独 import——想用里面的 Pattern,得再写一行 import java.util.regex.Pattern; , * 帮不了你
撞名时照样救不了:如果两个包都用 * 导入了,而它们各有一个同名类(比如 java.util.* 和 java.awt.* 里都有 List),直接写 List 照样编译报错——这时只能写全限定名,或者去掉一个 *、改成显式导入其中某个类
看不清依赖:* 让读者看不出这个文件到底用了哪些类,代码量一大可读性就下降,很多工程规范直接禁止用 *
所以结论:单个类就写单个 import;同一包要用的类确实很多时才考虑 `*`,并且时刻记得它只管当前这一层.
同一个包里的类不需要 import,直接写类名就行
java.lang 包(里面有 String、System 等最常用的类)默认自动导入——所以前几期我们直接用 String、System.out,从没写过 import
想不 import,也可以写全限定名(包名加类名,如 com.mycodes.demo.Student),但那样太啰嗦(这个其实在IDE可以快捷import,因此我们推荐写import)
四、public 和默认访问
跨包用别人的类,得知道一个前提:类里的东西默认只能被同一个包访问,被 public 修饰的东西才允许任何地方访问.
package com.mycodes.demo;
public class Student {
public String name; // public:谁都能访问
String secret; // 默认:只有同包能访问
public void sayHello() {
System.out.println("你好,我是 " + name);
}
}
类前面写了 public(public class Student),别处的类才能 new 它
方法前面写了 public,别处的类才能调用它
什么都没写的字段/方法,只有同一个包里能用;跨包访问会编译报错
本期只需要记住这一个规则就够用,完整的四种访问级别(public/protected/ package-private/private)下一期专门讲.
五、注释与 javadoc
注释是写给人看的,编译器会直接忽略.有三种:
5.1 行注释 // xxxxx 和块注释 /* */
// 这是单行注释,直到这一行结束
/*
* 这是块注释,可以跨多行.
* 一般用来给一段代码做说明.
*/
5.2 javadoc 注释 /** */
/**
* 这是 javadoc 注释.
* 写在类或方法前面,描述它"是什么、怎么用".
*/
public class Student {
// ...
}
javadoc 比普通注释多两件事:
可以在里面写 @param(参数说明)和 @return(返回值说明)
IDE 会在你鼠标悬停到类名或方法名上时,把 javadoc 显示成小提示——自己写过的代码,几个月后翻回来,悬停一下就能想起它是干嘛的(下图就是)

给方法写 javadoc 的例子:
/**
* 判断学生是否及格.
*
* @param threshold 及格线,低于它算不及格
* @return 成绩大于等于及格线返回 true,否则返回 false
*/
boolean passed(double threshold) {
return score >= threshold;
}
写注释的三个建议:
六、小项目:带包名的学生类
把本期内容串起来,建一个真正带包的小项目.目录结构:
codes/
└── src/
├── com/mycodes/demo/ ← 包 com.mycodes.demo
│ └── Student.java
└── com/mycodes/app/ ← 包 com.mycodes.app
└── Main.java
文件一 com/mycodes/demo/Student.java:
package com.mycodes.demo;
public class Student {
public String name;
public double score;
/**
* 创建一个学生.
*
* @param name 学生姓名
* @param score 学生成绩
*/
public Student(String name, double score) {
this.name = name;
this.score = score;
}
/**
* 判断是否及格.
*
* @return 成绩大于等于 60 返回 true,否则返回 false
*/
public boolean passed() {
return score >= 60;
}
}
文件二 com/mycodes/app/Main.java:
package com.mycodes.app;
import com.mycodes.demo.Student;
public class Main {
public static void main(String\[\] args) {
Student s1 = new Student("小明", 92);
Student s2 = new Student("小红", 58);
System.out.println(s1.name + ":" + (s1.passed() ? "及格" : "不及格"));
System.out.println(s2.name + ":" + (s2.passed() ? "及格" : "不及格"));
}
}
运行结果:
小明:及格
小红:不及格
注意几个点:
Main 在 com.mycodes.app,Student 在 com.mycodes.demo,靠第一行的 import com.mycodes.demo.Student; 引入
两个类都标了 public,所以能跨包使用
每个类和方法前都写了 javadoc,鼠标悬停 new Student(...) 时,IDE 会显示"创建一个学生"的说明
小知识:IDE(如 IntelliJ IDEA / Eclipse)建包时,会在磁盘上自动生成对应的目录;编译后类的"完整名字"是"包名 + 类名".
朝花夕拾:终于看懂第 1 期的 import 了
第 1 期我们用 ArrayList 和 HashMap 时,开头写过:
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
当时没说为什么.现在你知道了:ArrayList、HashMap 这些类并不在 java.lang 里,而在 java.util 包里,所以要先用 import 把它们引进来才能直接用.java.util 就是 JDK 自带的"工具类"包——Scanner、Random、Collections 等等都在里面,后面会经常见到.
总结
本期学习了两个"组织代码"的基础知识:
分包:package 声明类所属的包,包名对应目录结构,用反域名规范避免撞名;不同包的类用 import 引入
public 与默认访问:public 允许任何地方访问,什么都不写只允许同包访问(完整规则下一期讲)
注释:行注释 //、块注释 /* */、javadoc /** */;用 @param/@return 写参数和返回值的说明
下一期我们学习访问控制与封装——把四种访问级别讲完整,并学会用 private + getter/setter 把数据保护起来.