一.重构(refactoring),在不改变代码功能的基础上,改变代码结构(重写代码),使代码更便于阅读,更易理解,更易维护。
不妨看看这篇文章《有关于Refactor(重构)与Source(源)的比较》。
为什么要重构?因为你找到了更好的实现方法。
二.面向对象设计要考虑的一个基本问题:如何将变动的事物和不变的事物保持分离。
例如库,用户希望新版本出来时不需要重写他们的代码,而库设计者必须能够自由修改和改进库的实现(变动的事物),但前提是要保证客户代码(不变的事物)不受影响。
可以通过约定来实现。例如,库设计者必须保证改变类的时候,不能删除已有方法。但反过来,对于类的成员,和那些仅用于类自身实现而不应该直接用于客户代码的方法来说,设计不知道哪些被客户代码所使用,因此更改类的时候,改变任何一个成员,都可能破坏客户代码。
Java提供访问权限修饰符(access specifiers) 来解决这个问题。
三.访问权限有高至低:public, protected, package access(默认权限,无关键字),private。原则是:尽可能使用private,仅公开愿意客户代码使用的方法。
四.库(package):程序库单元。库由同一个命名空间(namespace)下的一组类构成。
使用import来导入库或具体的类。使用import,就是为了提供控制命名空间的机制,避免命名冲突(类名)。
未指定包,类位于默认包(default package)或称为未命名(unnamed)的包。
一个源文件,称为一个编译单元(compilation unit,或者translation unit)。
一个编译单元有且只能有一个public类,类名必须与文件名相同(包括大小写,但不包括扩展名(.java)),也就只有一个public组件。
编译文件,每个类产生一个.class文件,而不是一个文件产生一个.class文件。
多个文件中的类打包,package关键字。包名一般都小写。package语句必须是文件中的第一条语句。
package和import,就是要建立一个全局唯一的、没有命名冲突的命名空间。
通过将一个包的所有类放到同一个文件夹,或者通过jar来避免混乱。
包的类放到子目录,两个好处:创建唯一的包名,以及便于定位包中的类。
一般包名=反向的Internet域名+子目录路径。
Java解释器定位类文件的过程:
1. 查找classpath,作为根路径
2. 将包名的"."替换为"/"或"/",与根路径一起组成类的目录路径。
对于jar文件,classpath中必须包含完整的jar文件名。如C:/flavors/grape.jar
import两个包含同一类名的包,在使用该类时必须显示指定包名(当然此时也就不需要import)。
static import主要解决的问题是方便开发人员创建和使用全局的常量以及静态的方法。实现方法import static ...。不需要使用类名,可以直接使用常量和静态方法。
利用import不同的包实现类似条件编译的功能(java没有条件编译,主要是觉得C中的条件编译主要是为了跨平台。但条件编译也可以用于区分debug和release版本)。
一旦建立一个包,包中的类就必须置于包名称指定的目录结构层次中,否则JVM将无法定位(JAR文件中也是按照目录结构放置的)。
如果classpath中包含"."目录呢?是不是就可以把类放在当前目录?经测试,也不可以。
五.package access,默认访问权限(无关键字),或称friendly。同一个包内都可以访问,对于package之外的,==private。
一个文件只能属于一个package。
只有同一个包内的类才可以访问具有包访问权限的成员
默认包,当前目录的所有类都属于。必须在classpath中加入"."目录,比较怪的是:如果是同一个文件中定义,不加入".",编译能通过,但运行时报错;而如果是不同文件定义的,不加入".",编译就通不过。我想应该这样解释:同一个文件中,javac就不需要去查找classpath,但JVM定位class时必须查找classpath。还发现一点:如果classpath为空,JAVAC、JVM是可以自动找到当前目录的;而只要不为空,就必须加"."了。对于JAVA自带的包,不需要定义classpath。
六. private,成员的默认包访问权限并不够严。记住,尽量用private隐藏成员!多线程编程时尤其需要注意。
类的构造器如果为private,将不能继承该类。
类中的private对象引用,并不表示其它对象不能拥有该同一个对象的public引用(似乎很傻,很绕口,:)),例如通过某种方法把这个引用赋给别的引用,就可以获得一个public引用。如何限制?简单,不提供方法返回private引用,哈哈。
七.个人觉得,明确指定访问修饰符,而不过分依赖于package access是一种更好的策略。
八.protected也提供包访问权限。protected的权限大于package access。(C++,C#的protected权限没这么大?我印象中是没有)。
九.访问权限控制通常视作隐藏具体实现(implementation hiding)。把数据和方法包装进类中,与隐藏具体实现一起,通常称为封装(encapsulation)(人们通常认为implementation hiding == encapsulation)。
进行访问权限控制两个理由:
1. 设定用户可使用和不可使用的界限;
2. 完成接口与实现分离。这样,可以更改实现(protected, package access, private)(其实public的具体实现也是可变的,只要不改变接口,如返回值和参数),而不影响客户代码。
按照public, protected, private的顺序定义成员。有必要?
十. 类访问权限
1. 一个编译单元只能有一个public类。思想是:一个编译单元只有一个公共接口。
2. 编译单元的public类必须与文件同名,包括大小写。
3. 可能编译单元根本没有public类,此时文件名可以任意。不常用。
类可用的权限应该是public和package access,protected和private似乎没有意义。注意,内部类可以为protected和private。
如果要限制别人访问类,只需要把构造器设为private,此时只有类的创造者才可以在静态方法内部创建该类的对象。
Singleton模式:构造器为private,一个private static类自己的对象,一个静态方法返回这个静态对象。像这样:
此时有且只有一个该类的对象,因为静态。
将类构造器设为private,而通过静态方法返回类对象使得可以在创建对象之前进行额外的操作,如限制对象创建数量。
注意,虽然其它包的类不能创建package access权限的类对象,但如果该package access权限类有public static成员,客户仍然可以访问该static成员。(why do this?)可以么?我试了下好像不行啊,测试问题?
从习题8中是不是应该学到点什么:就是foreach的使用。foreach关于读和写的问题,参见《关于foreach的使用》。