Java 容许使用包(package)将类组织在一个集合中。借助包能够方便地组织本身的代码,并将本身的代码与别人提供的代码库分开管理。java
标准的 Java 类库分布在多个包中,包括 java.long、java.util 和 java.net 等。标准的 Java 包具备一个层次结构。全部标准的 Java 包都处于 java 和 javax 包层次中。程序员
使用包的主要缘由是确保类名的惟一性。事实上,为了确保包名的绝对惟一性,要用一个因特网域名(这显然是惟一的)以逆序的形式做为包名,而后对于不一样的工程使用不一样的子包。例如,域名 xiang117.com
,逆序来写,获得包名 com.xiang117
。而后能够追加一个工程名,如 com.xiang117.corejava
。若是在把 Employee 类放在这个包里,那么这个类的 “彻底限定” 名就是 com.xiang117.corejava.Employee
。sql
从编译器的角度看来,嵌套的包之间没有任何关系。例如,java.util 包与 java.util.jar 包毫无关系。每个包都是独立的类集合。ide
一个类可使用所属包中的全部类,以及其余包中的公共类(public class)。
能够采用两种方式访问另外一个包中的公有类:this
java.time.LocalData today = java.time.LocalDate.now();
使用 import 语句。 .net
可使用 import 语句导入一个特定的类或者整个包。import 语句应该位于资源文件的顶部(但位于 package 语句的后面)。code
import 语句是一种引用包中各个类的简捷方式。一旦使用了 import 语句,在使用类时,就没必要写出类的全名了。 资源
import 语句的惟一好处是简捷。可使用简短的名字而不是完整的包名来引用一个类。 get
可使用下面语句导入 java.time 包中的全部类。
import java.time.*;
就可使用
LocalDate today = localDate.now();
无需在前面加上包的前缀。编译器
java.time.*
的语法比较简单,对代码的规模也没有任何负面影响。固然,若是可以明确地指出所导入的类,将会使代码的读者更加准确地知道你使用了哪些类。
须要注意的是,只能使用星号()导入一个包,而不能使用 `import java.或
import java..` 导入以 java 为前缀的全部包。
能够导入一个包中的特定类:
import java.time.LocalDate;
在大多数状况下,能够只导入所须要的包,并没必要过多地考虑它们。但在发生命名冲突的时候,就要注意包了。例如,java.util 和 java.sql 包都有 Date 类。若是在程序中导入了这两个包:
import java.util.*;
import java.sql.*;
在程序使用 Date 类的时候,就会出现一个编译错误:
Date today; // Error--java.util.Date or java.sal.Date?
此时编译器没法肯定程序使用的是哪个 Date 类。能够采用增长一个特定的 import 语句来解决这个问题:
import java.util.*;
import java.sql.*;
import iava.util.Date;
若是这两个 Date 类都须要使用,须要在每一个类名的前面加上完整的包名。
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date();
在包中定位类是编译器(compiler)的工做。类文件中的字节码老是使用完整的包名引用其余类。
有一种 import 语句容许导入静态方法和静态字段,而不仅是类。
若是在源文件顶部,添加一条指令:
import static java.lang.System.*;
就可使用 System 类的静态方法和静态字段。而没必要加类名前缀:
out.println("Goodbye, World!"); // i.e., System.out
exit(0); // i.e., System.exit
能够导入特定的方法或字段:
import static java.lang.System.out;
实际上,是否有不少程序员想要用简写 System.out 或 System.exit,这一点让人怀疑。这样写出的代码看起来不太清晰。不过,
sqrt(pow(x, 2) + pow(y, 2))
看起来比
Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))
清晰得多。
要想将类放入包中,就必须将包的名字放在源文件的开头,即放在定义这个包中各个类的代码以前。例如在 com.xiang017.corejava
包中增长 Employee 类:
Employee.java
package com.xiang017.corejava; public class Employee { ... }
若是没有在源文件中放置 package 语句,这个源文件中的类就属于无名包(unnamed package)。无名包没包名。
PackageTest.java
import com.xiang017.javacore.*; public class PackageTest { public static void main(String[] args) { Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1); } }
将源文件放到与完整包名匹配的子目录中。例如,com.xiang017.corejava
包中的全部源文件应该放置在子目录 com/xiang017/corejava
中。编译器将类文件也放在相同的目录结构中。例如:
. (基目录) │ PackageTest.java │ └─com └─xiang017 └─javacore Employee.java
要想编译这个程序,只需切换到基目录,并运行命令:
javac PackageTest.java
编译器就会自动地查找文件 com/xiang017/corejava/Employee.java
并进行编译。编译后的结果以下:
. (基目录) │ PackageTest.class │ PackageTest.java │ └─com └─xiang017 └─javacore Employee.class Employee.java
要运行这个程序,须要在基目录下执行命令:
java PackageTest
假如 PackageTest.java 源文件没有在基目录下,在 mycompany 包下,如:
.(基目录) |-- com/ |-- corejava/ | |-- Employe.java | |-- Employe.class | |-- mycompany/ |-- PackageTest.java |-- PackageTest.class
仍然要从基目录编译和运行类,即包含 com 目录的目录:
javac com/mycompany/PackageTest.java
java com.mycompany.PackageTest
须要注意,编译器对文件(带有文件分隔符的扩展名 .java 的文件)进行操做。而 Java 解释器加载类(带有 . 的分隔符)。
警告: 编译器在编译源文件的时候不检查目录结构。例如,假定有一个源文件开头有如下指令:
package com.mycompany;
即便这个源文件不在子目录 com/mycompany
下,也能够进行编译。若是它不依赖于其余包,就能够经过编译而不会出现编译错误。可是,最终的程序将没法运行,除非先将全部的类文件移到正确的位置上。若是包与目录不匹配,虚拟机就找不到类。
PackageTest.java
import com.xiang017.javacore.*; public class PackageTest { public static void main(String[] args) { Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1); System.out.println("name=" + harry.getName() + ",salary=" + harry.getSalary() + ",hireDay=" + harry.getHireDay()); } }
com/mycompany/Employee.java
package com.xiang017.javacore; import java.time.LocalDate; import java.util.Objects; public class Employee { private String name; private double salary; private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day) { this.name = name; this.salary = salary; hireDay = LocalDate.of(year, month, day); } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }
标记为 public 的部分能够由任意类使用;标记为 private 的部分只能由定义它们的类使用。若是没有指定 public 或 private,这个部分(类、方法或变量)能够被同一个包中的全部方法访问。
没有标记修饰符的类只有在同一个包中其余类能够访问。对于类来讲,这种默认方式是合乎情理的。可是,对于变量来讲就有些不适宜了,变量必须显式地标记为 private,否则的话将默认为包可见。这样会破坏封装性。
在默认状况下,包不是一个封闭的实体。也就是说,任何人均可以向包中添加更多的类。
从 1.2 版开始,JDK 的实现者修改了类加载器,明确禁止加载包名以 “java.” 开头的用户自定义类!固然,用户自定义的类没法从这种保护中受益。另外一种机制让 JAR 文件声明包为密封的(sealed),以防止第三方修改,但这种机制已通过时。如今应当使用模块封装包。