# 包

在 Java 中，我们使用`package`来解决名字冲突。

在 Java 虚拟机执行的时候，JVM 只看完整类名，因此，只要包名不同，类就不同。

> 要特别注意：包没有父子关系。java.util和java.util.zip是不同的包，两者没有任何继承关系。

没有定义包名的`class`，它使用的是默认包，非常容易引起名字冲突，因此，不推荐不写包名的做法。

## 包结构

需要按照包结构把 Java 文件组织起来。假设以`package_sample`作为根目录，`src`作为源码目录，那么所有文件结构就是：

```
package_sample
└─ src
    ├─ hong
    │  └─ Person.java
    │  ming
    │  └─ Person.java
    └─ mr
       └─ jun
          └─ Arrays.java
```

```java
package mr.jun; // 申明包名mr.jun

public class Arrays {
}
```

```java
package ming; // 申明包名ming

public class Person {
}
```

编译后的`.class`文件也需要按照包结构存放。如果使用 IDE，把编译后的`.class`文件放到`bin`目录下，那么，编译的文件结构就是：

```
package_sample
└─ bin
   ├─ hong
   │  └─ Person.class
   │  ming
   │  └─ Person.class
   └─ mr
      └─ jun
         └─ Arrays.class
```

> 在 IDE 中，会自动根据包结构编译所有 Java 源码，所以不必担心使用命令行编译的复杂命令。

## 寻找Class

Java 编译器最终编译出的`.class`文件只使用 _完整类名_，因此，在代码中，当编译器遇到一个`class`名称时：

* 如果是完整类名，就直接根据完整类名查找这个`class`；
* 如果是简单类名，按下面的顺序依次查找：
  * 查找当前`package`是否存在这个`class`；
  * 查找`import`的包是否包含这个`class`；
  * 查找`java.lang`包是否包含这个`class`。

例子：

```java
// Main.java
package test;

import java.text.Format;

public class Main {
    public static void main(String[] args) {
        java.util.List list; // ok，使用完整类名 -> java.util.List
        Format format = null; // ok，使用import的类 -> java.text.Format
        String s = "hi"; // ok，使用java.lang包的String -> java.lang.String
        System.out.println(s); // ok，使用java.lang包的System -> java.lang.System
        MessageFormat mf = null; // 编译错误：无法找到MessageFormat: MessageFormat cannot be resolved to a type
    }
}
```

如果有两个`class`名称相同，例如，`mr.jun.Arrays`和`java.util.Arrays`，那么只能`import`其中一个，另一个必须写完整类名。

## 包的最佳实践

为了避免名字冲突，我们需要确定唯一的包名。推荐的做法是使用**倒置的域名**来确保**唯一性**。例如：

* org.apache
* org.apache.commons.log
* com.tobebetterjavaer.sample

子包就可以根据功能自行命名。

要注意不要和`java.lang`包的类重名，即自己的类不要使用这些名字：

* String
* System
* Runtime
* ...

要注意也不要和 JDK 常用类重名：

* java.util.List
* java.text.Format
* java.math.BigInteger
* ...
