第07部分:包和Java命名空间

包由一些具名的类、接口和其他引用类型组成,目的是把相关的类组织在一起,并为这些类定义命名空间。

Java 平台的核心类放在一些名称以 java 开头的包中。例如,Java 语言最基本的类在 java.lang 包中,各种实用类在 java.util 包中,输入输出类在 java.io 包中,网络类在 java.net 包中。有些包还包含子包,例如 java.lang.reflect 和 java.util.regex。甲骨文标准化的 Java 平台扩展一般在名称以 javax 开头的包中。有些扩展,例如 javax.swing 及其各种子包,后来集成到了核心平台中。最后,Java 平台还包含几个被认可的标准,这些包以标准制定方命名,例如 org.w3c 和 org.omg。

每个类都有两个名称:一个是简称,定义时指定;另一个是完全限定名称,其中包含所在包的名称。例如,String 类是 java.lang 包的一部分,因此它的完全限定名称是 java.lang.String。

本文说明如何把自己的类和接口放到包里,以及如何选择包名,避免和其他人的包名有冲突。然后说明如何有选择性地把类型名称或静态成员导入命名空间,避免每次使用类或接口都要输入包名。


声明包

若想指定类属于哪个包,要使用 package 声明。如果 Java 文件中有 package 关键字,必须是 Java 代码的第一个标记(即除了注释和空格之外的第一个标记)。package 关键字后面是包的名称和一个分号。例如,有个 Java 文件以下述指令开头:

package org.apache.commons.net;

那么,这个文件中定义的所有类都是 org.apache.commons.net 包的一部分。

如果 Java 文件中没有 package 指令,那么这个文件中定义的所有类都是一个默认的无名包的一部分。此时,类的限定名称和不限定名称相同。

包的名称有可能冲突,所以不要使用默认包。项目在增长的过程中越来越复杂,冲突几乎是不可避免的,所以最好从一开始就创建包。


全局唯一的包名

包的重要功能之一是划分 Java 命名空间,避免类名有冲突。例如,只能从包名上区分java.util.List 和 java.awt.List 两个类。不过,因此包名本身就要独一无二。作为 Java的开发方,甲骨文控制着所有以 java、javax 和 sun 开头的包名。

常用的命名方式之一是使用自己的域名,倒序排列各部分,作为包名的前缀。例如,Apache 项目开发了一个网络库,是 Apache Commons 项目的一部分。Commons 项目的网址是 http://commons.apache.org/,因此这个网络库的包名是 org.apache.commons.net。

注意,API 开发者以前也使用这种包命名规则。如果其他程序员要把你开发的类和其他未知类放在一起使用,你的包名就要具有全局唯一性。如果你开发了一个 Java 程序,但是不会发布任何类供他人使用,那么你就知道部署这个应用需要使用的所有类,因此无需担心无法预料的命名冲突。此时,可以选择一种自己用着方便的命名方式,而不用考虑全局唯一性。常见的做法之一是,使用程序的名称作为主包的名称(主包里可能还有子包)。



导入类型

默认情况下,在 Java 代码中引用类或接口时,必须使用类型的完全限定名称,即包含包名。如果编写的代码需要使用 java.io 包中的 File 类处理文件,必须把这个类写成 java.io.File。不过这个规则有三个例外:

· java.lang 包中的类型很重要也很常用,因此始终可以使用简称引用;

· p.T 类型中的代码可以使用简称引用 p 包中定义的其他类型;

· 已经使用 import 声明导入命名空间里的类型,可以使用简称引用。

前两个例外叫作“自动导入”。java.lang 包和当前包中的类型已经导入到命名空间里了,因此可以不加包名。输入不在 java.lang 包或当前包中的常用类型的包名,很快就会变得冗长乏味,因此要能显式地把其他包中的类型导入命名空间。这种操作通过 import 声明实现。

import 声明必须放在 Java 文件的开头,如果有 package 声明的话,要紧随其后,并且在任何类型定义之前。一个文件中能使用的 import 声明数量不限。import 声明应用于文件中的所有类型定义(但不应用于 import 声明中的类型)。

import 声明有两种格式。若想把单个类型导入命名空间,import 关键字后面是类型的名称和一个分号:

import java.io.File; // 现在不用输入java.io.File了,输入File就行

这种格式叫“单个类型导入”声明。

import 声明的另一种格式是“按需类型导入”。在这种格式中,包名后面是 .* 字符,表示使用这个包里的任何类型时都不用输入包名。因此,如果除了 File 类之外,还要使用java.io 包中的其他几个类,可以导入整个包:

import java.io.*; // java.io包中的所有类都可以使用简称

按需导入句法对子包无效。如果导入了 java.util 包,仍然必须使用完全限定名称 java.util.zip.ZipInputStream 引用这个类。

按需导入类型和一个一个导入包中的所有类型作用不一样。按需导入更像是使用单个类型导入句法把代码中真正用到的各种类型从包中导入命名空间,因此才叫“按需”导入——用到某个类型时才会将其导入。

命名冲突和遮盖

import 声明对 Java 编程极其重要。不过,可能会导致命名冲突。例如,java.util 和java.awt 两个包中都有名为 List 的类型。

java.util.List 是常用的重要接口。java.awt 包中有很多客户端应用常用的重要类型,但java.awt.List 已经作废了,不是这些重要类型的其中一个。在同一个 Java 文件中既导入java.util.List 又导入 java.awt.List 是不合法的。下述单个类型导入声明会导致编译出错:

import java.util.List;

import java.awt.List;

使用按需类型导入句法导入这两个包是合法的:

import java.util.*; // 导入集合和其他实用类型

import java.awt.*; // 导入字体,颜色和图形类型

可是,如果试图使用 List 类型会遇到困难。这个类型可以从两个包中的任何一个“按需”导入,只要试图使用未限定的类型名引用 List 就会导致编译出错。这种问题的解决方法是,明确指定所需的包名。

因为 java.util.List 比 java.awt.List 常用得多,所以可以在两个按需类型导入声明后使用单个类型导入声明指明从哪个包中导入 List:

import java.util.*; // 导入集合和其他实用类型

import java.awt.*; // 导入字体,颜色和图形类型

import java.util.List; // 与java.awt.List区分开

这样,使用 List 时指的是 java.util.List 接口。如果确实需要使用 java.awt.List 类,只要加上包名就行。除此之外,java.util 和 java.awt 之间没有命名冲突了,在不指定包名的情况下使用这两个包中的其他类型时,会“按需”将其导入。


导入静态成员

除了类型之外,还可以使用关键字 import static 导入类型中的静态成员。和类型导入声明一样,静态成员导入声明也有两种格式:单个静态成员导入和按需静态成员导入。假如你在编写一个基于文本的程序,要向 System.out 输出大量内容,那么可以使用下述单个静态成员导入声明减少输入的代码量:

import static java.lang.System.out;

加入这个导入声明后,可以用 out.println() 代替 System.out.println()。又假如你编写的一个程序要使用 Math 类中的很多三角函数和其他函数。在这种明显要大量使用数字处理方法的程序中,重复输入类名“Math”不会让代码的思路更清晰,反而会起到反作用。遇到这种情况,或许应该按需导入静态成员:

import static java.lang.Math.*

加入这个导入声明后,可以编写 sqrt(abs(sin(x))) 这样简洁的表达式,而不用在每个静态方法前都加上类名 Math。

import static 声明另一个重要的作用是把常量导入代码,尤其适合导入枚举类型。假如你想在自己编写的代码中使用下述枚举类型中的值:

package climate.temperate;

enum Seasons { WINTER, SPRING, SUMMER, AUTUMN };

那么,可以导入 climate.temperate.Seasons,然后在常量前加上类型名,例如 Seasons.SPRING。如果想编写更简洁的代码,可以导入这个枚举类型中的值:

import static climate.temperate.Seasons.*;

使用静态成员导入声明导入常量一般来说比实现定义常量的接口更好。


静态成员导入和重载的方法

静态成员导入声明导入的是“名称”,而不是以这个名称命名的某个具体成员。因为 Java允许重载方法,也允许类型中的字段和方法同名,所以单个静态成员导入声明可能会导入多个成员。例如下述代码:

import static java.util.Arrays.sort;

这个声明把名称“sort”导入命名空间,而没有导入 java.util.Arrays 里定义的 19 个sort() 方法中的任何一个。如果使用导入的名称 sort 调用方法,编译器会根据方法的参数类型决定调用哪个方法。

从两个或多个不同的类型中导入同名的静态方法也是合法的,只要方法的签名不同就行。下面举个例子:

import static java.util.Arrays.sort;

import static java.util.Collections.sort;

你可能觉得上述代码会导致句法错误,其实不然,因为 Collections 类中定义的 sort() 方法和 Arrays 类中定义的所有 sort() 方法签名都不一样。在代码中使用“sort”这个名称时,编译器会根据参数的类型决定使用这 21 个方法中的哪一个。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,572评论 18 399
  • 发发牢骚因为忙着找工作,论文的事情还没有着落,最近的压力其实特别大。前一段时间学的ruby和haskell貌似在简...
    ArimaKisho阅读 327评论 2 2
  • 赌石实乃非赌 ,当以心问石,以学相之,若以赌念开,百切归空; 石之有灵,当如切如磋,如琢如磨,修心静性,豁达自来!...
    思考的九九阅读 385评论 0 0