JAVA的访问控制1-访问控制的必要性,包

什么是访问控制

Java中,一个类或者类的字段能否被其它类访问,是可以被控制的。比如,我们有一个热水器的类,外部可以升高其温度(一个升温方法),当温度到达100度(热水器内部的一个变量)时,热水器会发出提示。这时我们需要控制其他类的访问权限,使他们可以访问升温的方法(设置为public),但是也希望保留内部实现,不想让他们看到内部还有一个100度的变量(设置为private),这就是访问控制了。

为什么需要访问控制

我们实际工作中,代码不是一成不变的,经常会因为BUG,需求或者是设计等原因,持续修改代码。但是,也有一些人在用我们编写的代码,比如我们编写了一个日期转换工具,其他人就会使用。我们期望我们可以顺畅的修改代码,完成我们的目标,同时类库的使用方也希望他们不必修改使用方式,换言之,他们不必修改代码。也就是说,我们的软件中有变化的部分,也有不变的部分,软件设计的一个考虑因素就是,如何将变化的与不变的相隔离
如果我们要写一个类库,在第一次发布后,已经有很多人在用了,这时我们想要对功能做一些升级,如何才能保证使用方不必修改代码呢?我们并不能知道使用方使用了哪些方法,使用了哪些字段,如果贸然修改,使用方必然会受到影响。如果我们只能新增,那么代码会越来越臃肿不提,很多功能也无法只通过新增代码来实现,这就陷入了这个两难的境地:我们想修改代码,但是无法保证使用方的安全。
为了能做到这样的事情,java提供了访问权限控制符,分别是publicprotected,private和啥也不加的package-private(默认权限,也称包内访问)。在编程过程中,我们可以通过public来对外提供方法,比如ArrayList提供存一个对象,取一个对象,获取列表大小等方法。对于内部的字段和一些内部方法,我们使用private进行隐藏,隐藏后,外部就无法获取这些字段和方法了,我们可以方便的对内部结构和实现进行调整。protected可以用于子类继承,package-private可以用于同一个包之间相互访问。
比如,我们常用的ArrayList类,这个可以说是java中最常用的类之一了,这个类就在jdk1.8的时候进行了一次变更,修改了底层的实现方式,提升了性能。但是,对于我们无数的使用者来说,升级到java8并不需要我们重写自己使用了ArrayList的代码。这就是访问控制的好处。

JAVA的包

包的由来及概述

在介绍具体的访问控制之前,我们还需要知道什么是包。
C语言中,就没有包的概念,如果模块A写了一个结构体a,模块B也写一个结构体a,那么名称就会冲突,导致编译失败。在小型项目中,这种冲突还不明显,因为参与的人少,相互之间约定一下就可以避免了。但是,在大型项目中,可能都是很多团队来一起完成,这时为了避免冲突,就需要约定名称防止冲突,比如A模块的都用A_开头,B模块的都用B_开头,那么模块A的中a方法名就是A_a,模块B中a的方法名就是B_a,这样子来避免冲突。不过,这只是权宜之计,因为C语言没有提供命名空间的能力,不得已而为之。
参考我们系统的目录,不同的目录可以有层级结构,文件可以放在不同的目录里面,每个目录都是相关的一组文件,每个目录中的文件名只要不冲突就可以,而无需关注全局的文件名是否冲突。那么,从根目录到具体的目录全部拼起来,比如D:/project/a/,这个就构成了一个命名空间,而D:/project/b/就是另一个命名空间,每个命名空间内部的文件的名称可以和其他命名空间的文件的名称重复,比如D:/project/a/1.txtD:/project/b/1.txt就可以同时存在而且互不冲突,这样就可以满足大型软件开发的要求了。
现代编程语言基本都提供了类似的命名空间的能力,JAVA也不例外。JAVA中的命名空间叫做包package。类放在包里面,不同的包里面的名称可以重复。比如模块A在a包下,有一个类ClassA,模块B的包是b,它里面也可以有一个ClassA,这样是可以完成编译的,并且不会冲突。
包除了可以避免命名冲突以外,还可以实现访问控制。不同的包之间的类想要相互访问,就需要满足访问控制权限,这就给作者留下了隐藏实现的机会,可以只将必要的方法开放出去。后续文章会详细说明这一内容。本文暂不展开。

包的声明通常在java文件的最顶行,语法是package xxx.xxx.xxx。比如:

package a;

public class ClassA{}

引用ClassA有两种方法,一种是使用全名,就是包名.类名,这里就是a.ClassA,再比如ArrayList的全名就是java.util.ArrayList。这样子写起来比较繁琐,所以java还提供了简单的写法,使用import关键字,比如,为了使用a.ClassA,我们可以这样写:

package b;

import a.ClassA;

public class ClassB{
    private ClassA classa;
    // ...更多代码
}

包名

通常,包名的前缀是公司域名的反转。比如,google公司的域名是google.com,那么google公司的程序包的包名就一般都是com.google开头的。
除了公司域名以外,一般在前面还会加上项目名称,比如com.google.projecta
商业公司的包名一般是com开头的,开源软件或者基金会的一般都是org开头的。

源码文件需要放在和包名同样层级的目录里,比如org.apache.commons.lang3.StringUtils会放在org/apache/commons/lang3/目录下。

某个包的包名最好是包里面的程序相关,比如某个包里面是一些工具类,包名可能就是xxx.util,某个包都是配置,那么包名可能就是xxx.configuration