6.1包:库单元 每编写一个JAVA源文件时,每个编译单元只能有一个public类,而且该类名字必须与文件的名称相同(文件名字为.java),否则编译就器就不会接受。
在该编译单元之中还有额外的类的话,那么在包之外的世界是无法看到这些类的。(这是因为他们不是public类,而且他们主要用来为主public类提供支持)
6.1.1代码组织 在编译少量的.java文件的时候回产生大量的.class文件。
类库实际上就是一组类文件。其中每个文件都有一个public类,以及任意数量的非public类,因此每一个文件都有一个构件。如果希望这些构件(每一个都有他们自己的独立的.java和.class文件)从属于同一个群组,就可以使用package。
在文件起始处写:package access;表示你在声明该编译单元是access类库的一部分。就是说你在声明该编译单元中的public类名称是位于access名称的保护下。任何想要使用该名称的人都必须使用前面给出的选择,指定全名或者与access结合使用关键字import。
//:access/mypackage/MyClass.java
package access.mypackage;
public class MyClass{
//....
}
表明在这个access/mypackage/目录下的一个MyClass.java文件使用了包权限管理。
现在如果有人想用这个MyClass类或者是access中的任何其他public类,必须使用关键字import来使access的名称可用。另外一个选择是给出完整的名称:
//:access/QualifiefMyClass.java
public class QualifiedMyClass{
public static void main(String[],args){
access.mypackage.MyClass();
}
}
关键字import可以使以上代码更加简洁:
//:access/QualifiefMyClass.java
inport access.mypackage.*;
public class QualifiedMyClass{
public static void main(String[],args){
access.mypackage.MyClass();
}
}
注意:表示包权限的其实是目录的最后一层。
package和import关键字允许你做的,是将单一的全局名字空间分隔开,使得无论多少人使用Internet以及Java开始编写类,都不会冲突。
6.1.2创建独一无二的包名字。
一个包从未真正的将被打爆的东西包装成单一文件,并且一个包可以有许多.class文件构成。
将所有的文件收入一个子目录还可以解决另外两个问题:怎样创建独一无二的名称以及怎样查找可能隐藏在目录结构中某处的类。
Java解释器的运行过程如下:首先,找出环境变量CLASSPATH,它包含一个或者多个目录,用作查找.class的根目录。从根目录开始,解释器获取包的名称并将每个据点替换成反斜杠,以从CLASSPATH根中产生一个路径名称(于是package foo.bar.baz就变成为foo\bar\baz或者foo/bar/baz)。得到的路径会与CLASSPATH中的各个不同的项相连接,解释器就在这些目录中查找你所要创建的类名称相关的.class文件。
对于112-113页的例子:当编译器碰到simple库的import语句时,就开始在CLASSPATH所指定的目录中查找,查找子目录net\mindview\simple,然后从已经编译的文件名称中找出名称相符者(对于Vetor而言是Vetor.class,对于List而言是List.class)。当然Vector和List中的类以及要使用的方法都必须是public的。
如何设置CLASSPATH:?
如果将两个含有相同名称的类库以“*”形式同时导入,如下:
import net.mindview.simple.*;
import java.util.*;
由于java.util.*也包含一个Vector类,这就潜在冲突了。
如果你像这样的代码 Vector v = new Vector();编译器就会出错误信息,强制你明确指明。你可能需要这样写
java.util.Vector v = new java.util.Vector();这样完全可以指明该Vector类的位置(配合CLASSPATH)所以除非还要使用java.util.*中的其他东西,否则就没有必要写import java.util.*语句了。
6.1.3定制工具库
我们创建自己代码减少或者消除重复的代码。例如我们已经用到的System.out.println()的别名可以减少输入负担,这种机制可以用于名为Print的类中,这样,我们在使用该类时可以用一个更具可读性的静态import语句来倒入:
//:net/mindview/util/Print.java
//Print methods that can be used without qualitfiers,using Java SE5 static import:
package net.mindview.util;
import java.io.*;
public class Print{
//Print with a newline;
public static void print(Object obj){
System.out.println(obj);
}
//Print with a newline by itself;
public static void print(){
System.out.println();
}
//Print with no line break;
public static void print(Object obj){
System.out.print(obj);
}
//The new Java SE5 printf() (from C );
public static PrintStream printf(String format,Object ...args){
return System.out.printf(format,args);
}
}///:-
可以使用打印便捷工具来打印String,无论需要换行(print()还是不需要换行printnb())。
可以猜到这个文件的位置一定是在某一个以CLASSPATH位置开始,然后接着是net/mindview的目录下。编译完以后,就可以使用import static语句在你的系统上使用静态的print()和printnb()方法了。以如下方式使用:
//:access/PrintTest.java
// Uses the static printing methods in Print.java.
import static net.mindview.util.Print.*; //这里不应该是import static net.mindview.util.*;?
public class PrintTest{
public static viod main(String[] args){
print("available from now on!");
print(100);
print(100L);
printI(3.14159);
}
}
....115页实例;
从现在开始,你无论何时创建了有用的工具,都可以将它添加到你自己的类库中,你将看到本书中海油更多的构件添加到了net.mindview.util类库中。
6.1.4用import改变行为
Java没有C的条件编译,但是可以使用通过修改杯倒入的package的方法来实现调试目的,修改的方法是将你的程序中用到的代码从调试版改为发布版。
6.1.5对包使用的忠告
无论何时创建包,都已经在给定包名称的时候隐含指定了目录结构。这个包必须位于其名称所指定的目录之中,而该目录必须是在以CLASSPATH开始的目录中可以查询到的。
写程序的时候一定遵守“包的名称对应目录路径”。
6.2Java访问权限修饰词
public、protected和private这几个Java访问权限修饰词在使用时,是置于类中每个成员的定义之前的,无论他是一个域还是一个方法。每个访问权限修饰词仅控制它所修饰的特定定义的访问权。
如果不提供任何访问权限修饰词,则它意味着“包访问权限”。因此,无论如何,所有事物都具有某种形式的访问权限控制。
6.2.1包访问权限
默认访问权限没有任何关键字,但是通常是指包访问权限(有时也表示成friendly)。这就意味着当前包中所有的其他类对哪个成员都有访问权限。但是对于这个包之外的所有类,这个成员却是private。由于一个编译单元(一个文件)只能隶属于一个包,所以经由包访问权限,处于同一编译单元中所有彼此之间都是自动可以访问的。(处于同一包中的不同编译单元(不同文件)中的类是不是可以相互访问??)
包访问权限允许将包内所有相关的类组合起来,以使它们彼此之间可以轻松的相互作用。当把类组织起来放进一个包内时,也就给他们的包访问权限的成员赋予了相互访问的权限,你“拥有”了该包内的程序代码,“只有你拥有的程序代码才可以访问你所拥有的其他程序代码”。应该说包访问权限为把类群聚在一个包中的做法提供了意义和理由。在Java中需要强制你一种合理的方式对他们加以组织。
有时候你可能想要排除这样的类-他们不应该访问在当前包中的类。与前面的包访问权限相反。
类控制着那些代码有权访问自己的成员,其他包内的类不能直接访问。取得对某成员的访问权限的唯一途径是:
1.使该成员成为public。于是无论是谁在哪里都可以访问该成员。
2,通过不加访问权限修饰词并将其他类放置于同一个包内的方式给成员赋予包访问权,于是包内的其他类就可以访问该成员了。
3.在下一章介绍的继承技术,继承而来的类既可以访问public成员还可以访问protected成员(不能访问private成员)。只有在两个类都处于同一个包内时候,他才可以访问访问包访问权限成员。
4.提供访问器(accessor)和变异器(mutator)方法(也称get/set方法),以读取和改变数值。对于OOP这是最优雅的方式。
6.2.2public:接口访问权限
使用public就意味着public之后紧跟着的成员声明自己对每个人都是可用的。尤其是使用类库的客户程序员更是如此。假设定义了一个包含下面编译单元的dessert包:
//:access/dessert/Cookie.java
//Creates a library
package access.dessert;
public class Cookie{
public Cookie(){
System.out.println("Cookie constructor");
}
void bite(){
System.out.println("bite");
}
}
Cookie.java问价必须位于dessert的子目录中,该子目录在access下(有问题没看第六章)不要错误的认为Java总是将当前目录视作查找行为的起点之一,如果你的CLASSPATH中缺少一个“.”作为路径之一的话,Java就不会查找那里。
现在创建一个使用Cookie的程序:
//:access/Dinner.java
//Uses the libary
import access.dessert.*;
public class Dinner{
public static void main(String[] args){
Cookie x = new Cookie();
//! x.bite();//can't access
}
}
可以创建一个Cookie对象,因为它的构造器是public而且类也是public的。(以后我们也会了解更多的public)但是由于bite()只向在dessert包中的类提供访问权,所以bite()成员在 Dinner.java中是无法访问的,因此编译器禁止你使用它。
默认包
但是下面的代码视乎坏了上述规则但是它可以编译:
//:access/Cake.java
//Access a class in a separate complation unit.
class Cake{
public static void main(String[] args){
Pie x = new Pie();
x.f();
}
Pie.f();
}
在第二个处于相同目录的文件中:
//:access/Pie.java
//The other class
class Pie(){
void f(){
System.out.println("Pie.f()");
}
}
或许你认为这两个文件毫不相干,但是Cake却可以创建一个Pie对象并调用他的f()方法!(为了是文件被编译,CLASSPATH中必须有“.”)通常会认为Pie和f()享有包访问权限,因而是不可以为Cake所用的。他们的确享有包访问权限,但这只是部分正确的。Cake.java可以访问他们的原因是因为他们处于相同的目录并且没有给自己设定任何包名称。java将这样的文件自动看作是隶属于该目录的默认包中,于是他们为该目录中所有其他文件的都提供了包访问权限。
6.2.3private:你无法访问
关键字private的意思是除了包含该成员的类以外,其他任何类都不能访问这个成员。由于处于同一个包内的其他类是不可以访问private成员的,因此等于说是自己隔离了自己。从另外一方面说让许多人共同合作来创建一个大包也是不大可能的,所以你睡意改变使用private修饰的成员不用考虑是不是会影响到其他的类。
默认的包访问权限通常已经提供了充足的隐藏措施。使用类的客户端程序员是无法访问包权限成员的。这样做很好,因为默认访问权限是一种我们常用的访问权限,同时也是一种在忘记添加任何访问权限控制时候能自动得到的权限,因此通常考虑是,哪些成员是要想明确公开给客户端程序员使用的,从而将它们什么味public而在最初,你可能不会认为自己经常会需要使用关键字private因为没有他照样可以工作。然而,事实很快证明。
对private的使用是多么重要,在多线程的环境下更是如此。
举例说明:
总结:类修饰符
1.类的可见性(public 与默认的区别)
控制符 | 对同个源文件中的类可见 | 对同个包中的类可见 | 对不同包中的类可见 |
public | ![]() | ![]() | ![]() |
默认 | ![]() | ![]() | ![]() |
2.区分类的可见性与import的不同
只有可见才能被import
3.Java为什么要限制可见性
有的类似专门设计的不希望被外界看到
关键字final修饰class:使被修饰的类不能够被继承,主要应用于类库。普通开发人员用的较少。
方法修饰符
方法是类中的定义的方法。方法的访问控制符包括:
public:没有使用限制;
默认:只能在同一个包中被使用;
protected:只能被子类使用;(注意)
private:只能在本类中使用;(注意)
关于网上说的protected的使用的真实含义应该是:本包可以访问,子类可以继承;这两句话的含义很深。前半句,本包可以访问的意思是,只有在本包里面你才能够使用对象的实例来访问这个protected方法,在包外,就算你创建了该类的实例,也不能通过该实例来调用该protected方法。通过继承,子类不管在不在同一个包内都可以访问这个protected方法或者成员,就相当于访问子类自身的量是一样的。有的书上说的仅在本包中可见,意思就是在包外你除了使用规定的方式,是不能够使用的。