0%

第五章 隐藏实施过程

摘要:
1.包:库单元 P123
2.访问修饰符 P130
3.类访问的特殊写法


包:库单元

  1. 每个编译单元只能有一个public类(可以没有);
  2. 一个有效的程序就是一系列.class文件,它们可以封装和压缩到一个JAR文件里面。java解释器负责对这些文件的寻找、装载和解释。(java并不强制使用解释器)
  3. 包的命名:所有字母小写,使用域名或indi,pers,priv,team,com等,尽量确保package名称的唯一性;
  4. 常见自己的包是,要求package语句必须是文件中的第一个“非注释”代码;
    1
    package pers.wxy.hello.test;
  5. 使用包的两种方式:
    1
    2
    import java.util.*;//导入包
    java.util.Random rand = new java.util.Random();//指定完整的名称,可以避免发生冲突

java解释器的工作程序:

首先,它找到环境变量CLASSPATH(将Java 或者具有Java 解释能力的工具(如浏览器)——安装到机器中时,通过操作系统进行设定)。CLASSPATH 包含了一个或多个目录,它们作为 一种特殊的“根”使用,从这里展开对.class文件的搜索。从那个根开始,解释器会寻找包名,并将每个点 号(句点)替换成一个斜杠,从而生成从CLASSPATH 根开始的一个路径名(所以package foo.bar.baz 会变 成foo\bar\baz或者foo/bar/baz;具体是正斜杠还是反斜杠由操作系统决定)。随后将它们连接到一起, 成为CLASSPATH 内的各个条目(入口)。以后搜索.class文件时,就可从这些地方开始查找与准备创建的类 名对应的名字。此外,它也会搜索一些标准目录——这些目录与 Java 解释器驻留的地方有关。


自动编译:

为导入的类首次创建一个对象时(或者访问一个类的static 成员时),编译器会在适当的目录里寻找同名 的.class 文件(所以如果创建类 X的一个对象,就应该是 X.class)。若只发现X.class,它就是必须使用 的那一个类。然而,如果它在相同的目录中还发现了一个 X.java,编译器就会比较两个文件的日期标记。如 果X.java 比X.class 新,就会自动编译X.java,生成一个最新的 X.class。 对于一个特定的类,或在与它同名的.java 文件中没有找到它,就会对那个类采取上述的处理。


CLASSPATH的陷阱: ???

P.java文件存在一个非常有趣的陷阱。特别是对于早期的 Java 实现方案来说,类路径的正确设定通常都是 很困难的一项工作。编写这本书的时候,我引入了P.java文件,它最初看起来似乎工作很正常。但在某些情 况下,却开始出现中断。在很长的时间里,我都确信这是 Java 或其他什么在实现时一个错误。但最后,我终 于发现在一个地方引入了一个程序(即第17 章要说明的 CodePackager.java),它使用了一个不同的类P。 由于它作为一个工具使用,所以有时候会进入类路径里;另一些时候则不会这样。但只要它进入类路径,那 么假若执行的程序需要寻找com.bruceeckel.tools中的类,Java 首先发现的就是CodePackager.java中的 P。此时,编译器会报告一个特定的方法没有找到。这当然是非常令人头疼的,因为我们在前面的类 P 里明明 看到了这个方法,而且根本没有更多的诊断报告可为我们提供一条线索,让我们知道找到的是一个完全不同 的类(那甚至不是public 的)。
乍一看来,这似乎是编译器的一个错误,但假若考察import 语句,就会发现它只是说:“在这里可能发现了 P”。然而,我们假定的是编译器搜索自己类路径的任何地方,所以一旦它发现一个P,就会使用它;若在搜 索过程中发现了“错误的”一个,它就会停止搜索。这与我们在前面表述的稍微有些区别,因为存在一些讨 厌的类,它们都位于包内。而这里有一个不在包内的P,但仍可在常规的类路径搜索过程中找到。 如果您遇到象这样的情况,请务必保证对于类路径的每个地方,每个名字都仅存在一个类。


java访问控制修饰符

修饰符 同类 同包 子类 不同的包
public 1 1 1 1
protected 1 1 1
friendly(default) 1 1
private 1 1

private的特殊用法:

1
2
3
4
5
6
7
8
9
10
public class IceCream {
//! Sundae x = new Sundae(); //构造器为private,不能直接调用
Sundae x = Sundae.makeASundae(); //static方法,直接使用类调用,不用创建对象
}
class Sundae{
private Sundae(){}
static Sundae makeASundae() {
return new Sundae();
}
}

这个例子体现了使用private的方便:

  1. 有时可能想控制对象的创建方式,并防止有人直接访问一个特定的构造器(或所有构造器);
  2. 由于默认构造器是唯一获得定义的,而且它的属性是private,所以可以防止对这个类的继承。(第六章)

类的访问

限制:

  1. 每个编译单元(文件)都只能有一个public类;
  2. public类的名字必须和文件名完全相同,包括大小写;
  3. 可能(但并常见)有一个编译单元没有public类。此时,可随意指定文件名。

特殊写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Lunch {
void test(){
//! Soup priv1 = new Soup();
Soup priv2 = Soup.makeSoup();
Soup.access().f();//2.
}
}
class Soup{
private Soup() {}
//1.同private的特殊用法
public static Soup makeSoup(){
return new Soup();
}
//2.creat a static object and return a reference upon request.
private static Soup ps1 = new Soup();
public static Soup access(){
return ps1;
}
public void f() {}
}

第二种采用了“设计模式”(Design Pattern)技术。通常方案叫作“独子”,以为它只允许创建一个对象。类Soup的对象被创建成一个static private成员,所以有且只有一个。