隐藏实施过程(封装)
Think in java 第五章内容更新,把书中内容总结到博客上,学习打卡
“进行面向对象的设计时,一项基本的考虑是:如何将发生变化的东西与保持不变的东西分隔开。”
这一点对于库来说是特别重要的。那个库的用户(客户程序员)必须能依赖自己使用的那一部分,并知道一旦新版本的库出台,自己不需要改写代码。而与此相反,库的创建者必须能自由地进行修改与改进,同时保证客户程序员代码不会受到那些变动的影响。(由此,引出了封装性,访问控制关键字private public protected)
Java推出了“访问指示符”的概念,允许库创建者声明哪些东西是客户程序员可以使用的,哪些是不可使用的。这种访问控制的级别在“最大访问”和“最小访问”的范围之间,分别包括:public,“友好的”(无关键字),protected以及private。
库设计者,应将所有东西都尽可能保持为“private”(私有),并只展示出那些想让客户程序员使用的方法。
把要封装的使用 private 私有,从而实现封装,开放提供操作的接口public
将组件绑定到单独一个统一的库单元里。这是通过Java的package(打包)关键字来实现的,而且访问指示符要受到类在相同的包还是在不同的包里的影响。
包:库单元
import关键字导入一个完整的库时,就会获得“包”(Package)。例如:
import java.util.*;
它的作用是导入完整的实用工具(Utility)库
由于Vector位于java.util里,所以现在要么指定完整名称“java.util.Vector”(可省略import语句),要么简单地指定一个“Vector”(因为import是默认的)。
建议导入具体的
import java.util.Vector;
为Java创建一个源码文件的时候,它通常叫作一个“编辑单元”(有时也叫作“翻译单元”)。每个编译单元都必须有一个以.java结尾的名字。而且在编译单元的内部,可以有一个公共(public)类,它必须拥有与文件相同的名字(包括大小写形式,但排除.java文件扩展名)。如果不这样做,编译器就会报告出错。每个编译单元内都只能有一个public类(同样地,否则编译器会报告出错)。那个编译单元剩下的类(如果有的话)可在那个包外面的世界面前隐藏起来,因为它们并非“公共”的(非public),而且它们由用于主public类的“支撑”类组成。
编译一个.java文件时,我们会获得一个名字完全相同的输出文件;但对于.java文件中的每个类,它们都有一个.class扩展名。因此,我们最终从少量的.java文件里有可能获得数量众多的.class文件。(新知)
Java并没有强制一定要使用解释器。一些固有代码的Java编译器可生成单独的可执行文件。(新知)
假定文件名是MyClass.java。它意味着在那个文件有一个、而且只能有一个public类。而且那个类的名字必须是MyClass(包括大小写形式):
package mypackage;
public class MyClass {
// . . .
创建独一无二的包名
根据约定,编译器强迫package名的第一部分是类创建者的因特网域名。由于因特网域名肯定是独一无二的
所以
com.xxx
org.xxx
net.xxx
edu.xxx
都是常见的包名
所以假如按这一约定行事,package的名称就肯定不会重复,所以永远不会遇到名称冲突的问题。换句话说,除非将自己的域名转让给其他人,而且对方也按照相同的路径名编写Java代码,否则名字的冲突是永远不会出现的。当然,如果你没有自己的域名,那么必须创造一个非常生僻的包名(例如自己的英文姓名),以便尽最大可能创建一个独一无二的包名。如决定发行自己的Java代码,那么强烈推荐去申请自己的域名,它所需的费用是非常低廉的。
所以,我们常常在搭建项目结构时,总是 com / hwq / demo /controller
这就是为什么 以这样的包名 创建文件结构的原因 ,不会和网上的冲突
下面以我自己的域名为例,它是bruceeckel.com。将其反转过来后,com.bruceeckel就为我的类创建了独一无二的全局名称(包名称)
创建自己的包时,要求package语句必须是文件中的第一个“非注释”代码(即 package 语句必须在第一行)。
重名类时,使用包名.类名 进行区分重名的类
Java访问指示符
public private protected default (friendly)
“友好的”
默认的访问没有关键字,但它通常称为“友好”(Friendly)访问。这意味着当前包内的其他所有类都能访问“友好的”成员,但对包外的所有类来说,这些成员却是“私有”(Private)的,外界不得访问 (包内有效)
将类组合到一个包内以后(这样便允许友好成员的相互访问,亦即让它们“交朋友”),我们便“拥有”了那个包内的代码。只有我们已经拥有的代码才能友好地访问自己拥有的其他代码。我们可认为友好访问使类在一个包内的组合显得有意义,或者说前者是后者的原因。
为获得对一个访问权限,唯一的方法就是:
(1) 使成员成为“public”(公共的)。这样所有人从任何地方都可以访问它。
(2) 变成一个“友好”成员,方法是舍弃所有访问指示符,并将其类置于相同的包内。这样一来,其他类就可以访问成员。
(3) 正如以后引入“继承”概念后大家会知道的那样,一个继承的类既可以访问一个protected成员,也可以访问一个public成员(但不可访问private成员)。只有在两个类位于相同的包内时,它才可以访问友好成员。但现在不必关心这方面的问题。
(4) 提供“访问器/变化器”方法(亦称为“获取/设置”方法),以便读取和修改值。这是OOP环境中最正规的一种方法,也是Java Beans的基础——具体情况会在第13章介绍。
访问控制范围,也解释了protected 与 friendly 的范围区分
在同一个类中,可以访问一下修饰符在变量:
public变量
protected变量
friendly变量
private变量(私有,只在本类内可访问,但是可以提供开发public接口,通过本类public方法,操作 private变量)
在同一个包中,可以访问以下修饰符的变量:
public变量
protected变量 (可以看到 protected 的范围 > friendly )
friendly变量
在不同的包中的子类中,可以访问以下修饰符在变量:
public变量
protected变量
(若新建一个包,并从另一个包内的某个类里继承,则唯一能够访问的成员就是原来那个包的public成员。当然,如果在相同的包里进行继承,那么继承获得的包能够访问所有“友好”的成员。有些时候,基础类的创建者喜欢提供一个特殊的成员,并允许访问衍生类。这正是protected的工作:不同包之间,提供给子类访问的protected, 在基类中用protected修饰的、public的才能在包外访问到)
在不同的包的非子类中,可以访问以下修饰符在变量:
public变量
接口与实现
interface implements class extends
代码的可读性
用特殊的样式创建一个类:将public成员置于最开头,后面跟随protected、友好以及private成员。这样做的好处是类的使用者可从上向下依次阅读,并首先看到对自己来说最重要的内容(即public成员,因为它们可从文件的外部访问),并在遇到非公共成员后停止阅读
注意不可将类设成private(那样会使除类之外的其他东西都不能访问它),也不能设成protected。因此,我们现在对于类的访问只有两个选择:“友好的”或者public。若不愿其他任何人访问那个类,可将所有构建器设为private(私有构造) ---- 单例模式,Pattern Design :Singleton Pattern
如果不针对类的访问设置一个访问指示符,那么它会自动默认为“友好的”。这意味着那个类的对象可由包内的其他类创建,但不能由包外创建。请记住,对于相同目录内的所有文件,如果没有明确地进行package声明,那么它们都默认为那个目录的默认包的一部分。然而,假若那个类一个static成员的属性是public,那么客户程序员仍然能够访问那个static成员——即使它们不能创建属于那个类的一个对象。
总结
如果不制订规则,客户程序员就可以随心所欲地操作一个类的所有成员,无论我们本来愿不愿意其中的一些成员被直接操作。所有东西都在别人面前都暴露无遗。
本章讲述了如何构建类,从而制作出理想的库。首先,我们讲述如何将一组类封装到一个库里(package)。其次,我们讲述类如何控制对自己成员的访问。
练习
(1) 用public、private、protected以及“友好的”数据成员及方法成员创建一个类。创建属于这个类的一个对象,并观察在试图访问所有类成员时会获得哪种类型的编译器错误提示。注意同一个目录内的类属于“默认”包的一部分。
(2) 用protected数据创建一个类。在相同的文件里创建第二个类,用一个方法操纵第一个类里的protected数据。
(3) 新建一个目录,并编辑自己的CLASSPATH,以便包括那个新目录。将P.class文件复制到自己的新目录,然后改变文件名、P类以及方法名(亦可考虑添加额外的输出,观察它的运行过程)。在一个不同的目录里创建另一个程序,令其使用自己的新类。