4.5包

前面我们已经听过包(package)这个概念了,比如String类在java.lang包下,Arrays类在java.util包下。那么为什么要引入包的概念呢?我们思考一个问题:java类库提供了上千个类,我们很难完全记住他们,如果我们编写了一个类,类名和类库中的某个类名字重复了怎么办?

其实一个操作系统的文件系统也会遇到类似的问题,那么windows系统如何解决的?这个你肯定知道,就是采用目录层次结构。我们把硬盘分成很多分区,例如c盘、d盘等,这个叫做根目录。然后再一级一级的建立文件夹,看图:

我们在workspace和workspace2下分别创建同名文件:hello.java,那么这2个文件的完整路径为:

D:\Java大失叔\workspace\hello.java

D:\Java大失叔\workspace2\hello.java

因此不会有冲突。

4.5.1包的概念

  在Java中,是用包来解决这个问题的。包就类似于文件目录层次结构,是采用圆点(.)来分割,例如java.util。包类似于命名空间,我们平时说的类名,其实是类名的简写,一个类真正的名字是包名.类名,我们称之为完整类名。例如String类的的完整类名是java.lang.String,Arrays类的完整类名是java.util.Arrays。有了包之后,我们只需把我们自己编写的类放到我们自己的包中,这样即使类名和类库中的名字重复,也不会有冲突了(当然我们不建议这么做),例如我们也编写一个String类,放在我们自己的包javadashishu下,则我们的String类的完整类名是javadashishu.String,和java.lang.String不一样,就不会有冲突。

  为了保证包名不冲突,针对包名我们会有一套推荐的命名方法,Sun公司的建议是:

  • 包名都采用小写英文字母或数字,不能以圆点(.)开头或结尾
  • 用倒置的域名作为包名前缀,例如

org.apache

com.google

  • 子包名使用项目或功能的名字,尽量使用有意义的单词
  • 尽量避免和JDK中的类同名

例如,笔者可以把《Java从入门到失业》的例子都放到包:com.javadss.javase下。

4.5.2创建包

我们已经了解了包的概念,那么怎么把一个类放到一个包下呢?下面们用Eclipse来演示创建类和包的过程,首先我们先创建本书第四章的包:com.javadss.javase.ch04。右键点击工程src文件,如下图:

在弹出的如下页面输入包名:

点击“Finish”按钮,我们发现,工程目录多了一个包,然后我们在包目录上点击右键,创建一个类PackageTest,Eclipse会自动生成代码如下:

package com.javadss.javase.ch04;  

public class PackageTest {  

} 

我们发现,该文件的第一行多了一句代码:

package com.javadss.javase.ch04;

这句代码的含义就是表示该源文件中的所有类都将被放到包com.javadss.javase.ch04的下面。如果我们在该源文件中继续写一个类PackageTest2如下:

package com.javadss.javase.ch04;  

public class PackageTest {  

}  

class PackageTest2 {  

}  

这样源文件PackageTest.java中的2个类PackageTest和PackageTest2 都被放到包com.javadss.javase.ch04中了。因此它们的完整类名分别为:

  com.javadss.javase.ch04.PackageTest

  com.javadss.javase.ch04.PackageTest2

假如我们的源文件开头没有声明包语句,那么这个源文件会被放置到一个默认包中,默认包是一个没有名字的包,像我们之前的例子,都没有声明包,因此它们都是放置在默认包中的。不过在实际运用中,非常不推荐把源文件放置在默认包中。

定义好了类的包后,我们看看这个源文件被放在什么地方了。笔者的Eclipse的工作空间目录为D:\Java大失叔\workspace,本书的工程为BaseJava,工程中src为源代码目录,bin为class文件目录。则最终源文件PackageTest.java的路径为:

D:\Java大失叔\workspace\BaseJava\src\com.javadss.javase.ch04\PackageTest.java

目录层次结构如下:

因为我们使用了Eclipse,它会自动帮我们编译类,还记得我们在3.1演示HelloWorld的时候教大家如何创建工程吗?Eclipse创建工程默认会使用项目文件目录作为根目录,下面会创建2个文件夹:src和bin。src用来存放源代码文件,bin用来存放编译后的字节码class文件。我们会发现,最终src和bin变成如下结构:

实际上,如果我们不用IDE,我们手工在src目录下编写上述源文件,然后用命令行工具编译(我们也编译到bin目录下),命令如下(还记得编译命令吗?红色表示编译后的class文件的目录,蓝色表示源文件的路径):

javac -d D:\Java大失叔\workspace\BaseJava\bin D:\Java大失叔\workspace\BaseJava\src\com\javadss\javase\ch04\PackageTest.java

编译成功后,也会在bin目录下形成上述结构。

4.5.3包作用域

  上面我们说过包可以用来解决同名类的冲突问题,实际上,包还有一个作用,就是可以控制权限。

  前面我们接触过访问修饰符public、private,其实还有一个protected,它们可以用来修饰类、属性及方法,当类、属性或方法不用任何访问修饰符修饰的时候,我们可以认为有一个default修饰符在修饰它们,这样一来,可以认为有4种访问修饰符,这4个修饰符可以控制对同一个类、同一个包、不同包的子类、不同包非子类的访问权限,下面我们用表格的形式列出它们的权限范围:

 

同一个类

同一个包

不同包子类

不同包非子类

public

protected

default

private

我们看到,当不用任何修饰符修饰的时候,类、方法和属性对同一个包下的其他类是开放的。我们用一个例子演示一下,看如下2个类:

package com.javadss.javase.ch04;  

class PackageTest {
private String sPrviate = "我是私有的";
String sDefault = "我是默认的"; void testYes() {
System.out.println("同一个包可以访问我");
} private void testNo() {
System.out.println("只有我自己可以访问我");
}
}
package com.javadss.javase.ch04;  

class PackageTest2 {
public static void main(String[] args) {
PackageTest test = new PackageTest();
test.testYes();
System.out.println(test.sDefault);
}
}

我们看到,PackageTest和PackageTest2在同一个包中,因此PackageTest2可以访问PackageTest的testYes方法和sDefault属性,但是不能访问private修饰的testNo方法和sPrivate属性。

这里其实有一个问题,就是PackageTest2可以修改PackageTest的sDefault属性,其实对于有些情况来说,算是破坏了封装性。比如我编写了一个小工具类DssUtil提供给别人使用,包名是com.javadss.util。其中有很多默认修饰的属性,我本意是不想对外界开放。但是实际上使用者可以把他的类也放到包com.javadss.util中,这样他的类就可以随意访问DssUtil中的默认修饰属性了。不过这一点也有办法控制,后面我们有机会可以讨论包密封机制来解决这个问题(Java虚拟机从类加载上禁止加载用户自定义的以java.开头的类来解决这个问题)。

另外,还有一个问题需要注意,包并没有包含关系,例如对于包com.javadss.javase和com.javadss.javase.ch04这是2个包,因此这2个包下的类,不能互相访问默认作用域的类、属性和方法。

4.5.4包的导入

  一个类可以访问同包中的所有类和其他包中的public类,如果需要访问其他类,需要导入以后才能访问。导入类有几种方式,我们一一介绍。

4.5.4.1直接写完整类名

第一种方式,是直接写完整类名。例如我们要使用Arrays类对一个数组排序,可以这样:

class PackageTest2 {
public static void main(String[] args) {
int[] a = new int[] { 4, 1323, 1, 33 };
java.util.Arrays.sort(a);//直接写完整类名
}
}

但是这种方式显然是比较痛苦和令人讨厌的,一般我们采用下面这种方式。

4.5.4.2import语句

第二种方式就是用import语句导入。import语句可以导入某个特定的类或导入整个包。例如:

import java.util.Arrays;//导入特定的类  

或者

import java.util.*;//导入整个包  

使用import语句导入后,就可以不用写完整类名了。这里需要注意的是,导入整个包的方式,并不能把子包导入进来。一般情况下,我们推荐导入特定类的方式,这样可以直接看到某个类在哪个包下。

聪明的同学可能要问了,我记得我们之前一直使用System类来打印,但是好像也没有导入包啊?算你厉害,这里就牵涉到编译器在编译的时候,是如何定位类的:

  1. 如果是完整类名,则直接定位到该类
  2. 如果是简单类名,则按下面顺序:
    • 从当前包下查找是否存在该类
    • 从import语句中查找是否存在该类
    • 从java.lang包中查找是否存在该类

看到了吗?实际上就相当于编译器会帮我们导入当前包和java.lang包下的类。因为System是java.lang包下的类,因此我们可以不必显式的导入。

有的时候还存在一个问题,当2个包下存在同名的类时,比如在JDK中存在java.util.Date和java.sql.Date,如果我们用了如下语句:

import java.util.*;
import java.sql.*;

那么我们无法使用简单的类名Date。因为无法区分是用哪个包下的Date类。假如我们只使用其中一个,比如我们用java.util.Date,可以有一个取巧的办法:

import java.util.*;
import java.sql.*;
import java.util.Date;

但是这种办法解决不了我们同时需要使用2个Date类。如果同时需要使用的时候,只能用完整类名的方式了。

4.5.4.3静态导入

从Java5.0开始,增加一种新的导入方式,可以导入静态方法和静态属性。前面我们经常使用System.out.println()来打印,其实out就是System类的一个静态属性。如果我们在源文件进行如下导入:

import static java.lang.System.out;

这样我们就可以直接使用out.println()来打印输出了:

 1 package com.javadss.javase.ch04;
2
3 import static java.lang.System.exit;
4 import static java.lang.System.out;
5
6 class PackageTest2 {
7 public static void main(String[] args) {
8 out.println("静态导入");
9 exit(0);
10 }
11 }

注意看第3行,exit()是System的一个静态方法,这里不管这个方法有啥用,只是演示可以导入静态方法。当然,我们可以静态导入整个类的静态属性和方法:

import static java.lang.System.*;  

不过说实话,我不太喜欢使用静态导入,有的时候反而引起一些阅读障碍。

4.5.5小结

通过本小结的讨论,我们知道:

  • 包可以解决类名冲突,一个类的完整类名是包名.类名
  • 在一个类中访问其他类,可以写完整的类名,也可以用import语句导入;从Java5.0开始还可以导入静态方法和静态属性。
  • 包可以隔离访问权限,默认修饰的类、方法、属性可以被同包的其他类访问。

另外,包主要是让编译器知道如何定位到类,当编译成字节码class文件后,class文件中都是采用完整类名的方式来引用其他类。最后用一张图来总结一下:

《Java从入门到失业》第四章:类和对象(4.5):包的更多相关文章

  1. Java学习日记基础篇(四)——类,对象之成员变量,成员方法,构造方法

    面向对象(Object Oriented) 一.面向对象杂谈 面向对象(Object Oriented),我的翻译是以物体为目标的,就是说编程的时候是建立一个物体,然后对这个物体进行操作. Java语 ...

  2. 《Java入门第二季》第一章 类和对象

    什么是类和对象 如何定义 Java 中的类 如何使用 Java 中的对象 Java中的成员变量和局部变量1.成员变量:在类中定义,描述构成对象的组件. 2.局部变量:在类的方法中,用于临时保存数据. ...

  3. 《Java从入门到失业》第二章:Java环境(四):IDE集成环境

    2.4IDE集成环境 在掌握了编写.编译和运行Java程序的基本步骤以后,你肯定就在想,这太麻烦了,有没有更好的工具?当然有了,那就是IDE.IDE就是专业的集成开发环境(Integrated Dev ...

  4. 《Java从入门到失业》第一章:计算机基础知识(一):二进制和十六进制

    0 前言 最近7年来的高强度工作和不规律的饮食作息,压得我有些喘不过气,身体也陆续报警.2018年下半年的一场病,让我意识到了这个问题的严重性,于是开始强制自己有规律饮食和作息,并辅以健身锻炼,不到2 ...

  5. 《Java从入门到失业》第二章:Java环境(一):Java SE安装

    从这一章开始,终于我们可以开始正式进入Java世界了.前面我们提到过,Java分三个版本,我们这里只讨论Java SE. 2.1Java SE安装 所谓工欲善其事,必先利其器.第一步,我们当然是要下载 ...

  6. 《Java从入门到失业》第一章:计算机基础知识(三):程序语言简介

    1.3程序语言简介 我们经常会听到一些名词:低级语言.高级语言.编译型.解释型.面向过程.面向对象等.这些到底是啥意思呢?在正式进入Java世界前,笔者也尝试简单的聊一聊这块东西. 1.3.1低级语言 ...

  7. 《Java从入门到失业》第二章:Java环境(三):Java命令行工具

    2.3Java命令行工具 2.3.1编译运行 到了这里,是不是开始膨胀了,想写一段代码来秀一下?好吧,满足你!国际惯例,我们写一段HelloWorld.我们在某个目录下记事本,编写一段代码如下: 保存 ...

  8. 《Java从入门到失业》第二章:Java环境(二):JDK、JRE、JVM

    2.2JDK.JRE.JVM 在JDK的安装目录中,我们发现有一个目录jre(其实如果是下一步下一步安装的,在和JDK安装目录同级目录下,还会有一个jre目录).初学Java的同学,有时候搞不清楚这3 ...

  9. 《Java从入门到失业》第一章:计算机基础知识(二):计算机组成及基本原理

    1.2计算机组成及基本原理 1.2.1硬件组成 这里说的计算机主要指微型计算机,俗称电脑.一般我们见到的有台式机.笔记本等,另外智能手机.平板也算.有了一台计算机,我们就能做很多事情了,比如我在写这篇 ...

  10. java面向对象编程——第四章 类和对象

    OO:面向对象 OOP:面向对象编程 OOA:面向对象分析 OOD:面向对象设计 结构化编程:从顶向下,将一个大问题分解成更小的任务,然后为每一个更小的任务编写一个过程.最后程序员会编写一个主过程来启 ...

随机推荐

  1. SQL获取多个字段中最大小值

    1.语法最大值: GREATEST(expr_1, expr_2, ...expr_n)最小值: LEAST(expr_1, expr_2, ...expr_n) 2.说明GREATEST(expr_ ...

  2. 痞子衡嵌入式:解锁i.MXRTxxx上FlexSPI模块自带的地址重映射(Remap)功能

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT三位数系列隐藏的FlexSPI Remap功能. 前段时间痞子衡写了一篇文章 <利用i.MXRT1060,1010上新 ...

  3. golang gorm 基本使用

    原文链接:golang orm 框架之 gorm gorm 用法介绍 库安装 go get -u github.com/jinzhu/gorm 数据库连接 import ( "github. ...

  4. 思索-js 页面ID识别及数据缓存

    思索-页面ID识别及数据缓存 页面 ID 识别的思路 多页应用在移动端是较为常见的一种架构,它可以和APP 内的 webview 配合,达到类似原生的体验,这一点是单页应用无法做到的(比如手势滑动等, ...

  5. 第2篇 Scrum 冲刺博客

    1.站立会议 照骗 进度 成员 昨日完成任务 今日计划任务 遇到的困难 钟智锋 无 确定客户端和服务器通信的形式 各成员的代码难以统一 庄诗楷 无 编写客户端UI 加入图片总是失败 易德康 无 马,车 ...

  6. 在服务器上使用python-gym出现的关于显示的问题

    疫情期间在家连着实验室的服务器继续学习,用到关于强化学习相关的包 gym 时,在render上出现了问题. 环境: Ubuntu18.04 服务器python 3.7.6gym 0.17.1jupyt ...

  7. MPI中的cannon算法

    Cannon算法 算法过程 假设矩阵\(A,B\)和\(C\)都可以分成\(m\times m\)块矩阵,即\(A = (A_{(ij)})_{m\times m},B = (B_{(ij)})_{m ...

  8. 快速解决Ubuntu/linux 环境下QT生成没有可执行文件(application/x-executable)

    快速解决Ubuntu/linux 环境下QT生成没有可执行文件(application/x-executable)(转载)   问题描述 与windows环境下不同,linux选择debug构建时并不 ...

  9. JavaScript中pipe实战

    JavaScript中pipe原理 代码示例 const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x); 原理 一行代码 ...

  10. Min Cost Climbing Stairs [746]

    Min Cost Climbing Stairs [746] 题目描述 简单来说就是:要跳过当前楼梯需要花费当前楼梯所代表的价值cost[i], 花费cost[i]之后,可以选择跳一阶或者两阶楼梯,以 ...