《JavaTM程序设计语言基 础》第 2 部分
第 8 课:面向对象的程序设计

[<<后退]] [目录]

也许您已听到过大量有关面向对象的程序设计的话题。而且,如果 JavaTM 程序设计语言是您使用的第一种面向对象的程序设计语言,那么您很可能迫切希望 全面了解这种语言。

在实际操作了 《Java 程序设计语言基 础》第1部分1第2部分 中的示例程序后,您 应该对类、对象、实例和继承性以及访问等级 publicprivate 等面向对象的概念已比较熟悉了,进而也就对面向对象的程序设计有所了解。不过 ,许多情况下您已经在进行面向对象的程序设计,但却并没有意识到这一点。

这就是有关 Java 程序设计语言的重要问题之一。它本来就是面向对象的。

为了帮助您对面向对象的程序设计及其优点有更深入的理解,本课将简洁地介 绍一些与本教程中某些示例程序相关的、面向对象的概念和术语。


面向对象程序设计的定义

面向对象的程序设计是基于类以及界定分明的并能互操作的对象的层次结构的 一种程序设计方法。

类是定义数据和处理该数据的方法的一种结构。当您在使用 Java 语言编写程 序时,所有程序数据都包含在一个类中;该类可以是您自己编写的类,也可以是 从 Java 平台 API 库中调用的类。

第1部分第一课中简单程序的 ExampleProgram 类是程序设计者 利用 Java 平台 API 库中的 java.lang.System 类编写的类,其作 用是把字符串输出到命令行中。

class ExampleProgram {
  public static void main(String[] args){
    System.out.println("I'm a simple Program");
  }
}

Java 平台 API 库中的类定义了一套共享通用结构和动作的对象。示例程序中 所使用的 java.lang.System 类定义了标准输入/输出和错误流以及 对系统属性的访问权限等内容。相反 java.lang.String 类则定义 了字符串。

示例中是看不到 String 类的显式使用的,但是在 Java 语言中 ,要求获得 String 对象的方法可在任何位置使用字符串。在运行 期间,Java 平台会从传递给 System.out.println 调用的字符串 创建一个 String 对象;但是由于您的程序并没有实例化 String 对象,所以不能调用任何 String 类方法。

若想访问 String 方法,可以重新编写示例程序,使之创建下 述 String 对象。这样,您就可以调用诸如用于把文本添加到原串 中的 String.concat 方法等类似的方法。

class ExampleProgram {
  public static void main(String[] args){
    String text = new String("I'm a simple Program ");
    System.out.println(text);
    String text2 = text.concat(
      "that uses classes and objects");
    System.out.println(text2);
  }
}
以上方法的输出如下:
I'm a simple Program
I'm a simple Program that uses classes and objects

对象

实例是类的可执行备份。实例又名对象。任何时候内存中都可以有某个类的多 个对象。

前述示例为合并运算、text 对象、text2 对象 从传递给 String.concat 方法的字符串“that uses classes and objects”在后台创建的 String 对象构建了四个不同的 String 对象。

而且,由于 String 对象不可编辑,所以 java.lang.String.concat 方法把 String 对象转换成 StringBuffer (可编辑) 串对象以进行合并运算。

String 对象外,存储器中还有 ExampleProgram.java 类的实例。

System 类从来都不能被 ExampleProgram 类实例 化,这是因为它只包含静态变量和方法,因而不能被程序实例化,但是可在后台被 JavaTM 虚拟机1 (VM)实例化。

明确界定及互操作

类定义必须允许对象能在执行期间互操作。在前一节中,您已看到 SystemStringStringBuffer 对象是如何通过互操作完 成向命令行输出合并字符串的工作的。

本节将对示例程序加以改动,使之能在用户界面的 JLabel 组件 中放置和显示字符串,以进一步阐释有关界定分明的类边界和对象互操作的概念。

把文本信息放置在标签中并显示在用户界面上的程序代码使用了大量可互操作的类。每个类都有各自的功能和作用(如下文所述),而且在适当的位置定义了可与其他类的对象互操作的类。

  • ExampleProgram 定义程序数据及处理该数据的方法。
  • JFrame 定义包含窗口标题和方框菜单的顶层窗口。
  • WindowEvent 定义方框菜单上 Close(关闭)开关(相关) 的动作。
  • String 定义创建标签的字符串。
  • JLabel 定义用于显示静态文本的用户界面组件。
  • JPanel 定义背景颜色、包含标签并使用缺省布局管理器 (java.awt.FlowLayout)来使标签定位在显示屏上。
由于每个类都有自己特定的用途,所以它们需共同协作创建以下的简单用户界面。

 

import javax.swing.*;
import java.awt.Color;
import java.awt.event.*;

class ExampleProgram extends JFrame {

  public ExampleProgram(){
    String text = new String("I'm a simple Program ");
    String text2 = text.concat(
      "that uses classes and objects");

    JLabel label = new JLabel(text2);
    JPanel panel = new JPanel();
    panel.setBackground(Color.white);

    getContentPane().add(panel);
    panel.add(label);
  }

  public static void main(String[] args){
    ExampleProgram frame = new ExampleProgram();

    frame.setTitle("Fruit $1.25 Each");
    WindowListener l = new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    };

    frame.addWindowListener(l);
    frame.pack();
    frame.setVisible(true);
  }
}

继承性

使对象互操作的一个面向对象的概念就是继承性。继承性定义了面向对象的语 言中各种类之间的相互关系。在 Java 程序设计语言中,所有的类都来自 java.lang.Object 并实现其方法。

下图表示出了上文用户界面示例程序中各种类从 java.lang.Object 派生时的类层次结构。由于为所有子类(Java API 库中的各种类)所继承和实现, 所以 java.lang.Object 方法也显示出来了。java.lang.Object 定义了所有类一般都具有的主要动作属性集。

当您在向下移动层次结构时,每一个类都把它们自己特定的域和方法组添加到 它们从一个或多个超类继承下来的域和方法组中。java.awt.swing.JFramejava.awt.Frame 继承域和方法,java.awt.Framejava.awt.Container 继承域和方法,java.awt.Containerjava.awt.Component 继承域和方法,最后 java.awt.Componentjava.lang.Object 继承域和方法,而每一个子类都按需要添加 自己的域和方法。

多态性

使对象互操作的另一种方法是,定义把其他对象当作自己的参数的方法。当对 象被一个通用的超类统一在一起时,这些对象就能够更加密切地互操作而且效率更 高。Java 程序设计语言中的所有类都有继承关系。

例如,如果定义了用 java.lang.Object 作为其参数之一的方法 ,该方法就可以接收整个 Java 平台中的任何对象。如果定义了用 java.awt.Component 作为其参数之一的方法,该方法就可以接收 任何成员对象。这种互操作叫做多态性。

第2部分、第5课:集合 中就有一个多态性的示 例,其中的集合对象可包含来自 java.lang.Object 的任何类型的 对象。以下将再次对这个示例程序进行说明,以使您明白:由于 Set.add 方法的定义可以接收追溯到 java.lang.Object 类的任何类实例, 所以 Set 集合可把 String 对象和 Integer 对象添加到 Set 中。

  String custID = "munchkin";
  Integer creditCard =  new Integer(25);

  Set s = new HashSet();
  s.add(custID);
  s.add(creditCard);

数据访问级别

类互操作的再一种方法是通过访问级别进行控制。类及其域和方法都有访问级 别,访问级别用于指明它们在程序运行期间对于其它对象的可使用等级。在需要 对象之间的互操作时,有时需要明确地控制访问权限,而指定访问级别就是实现 这种控制的方法。如果没有指明访问级别,系统就会采用默认访问级别。

缺省状态下,类只能被同一程序包中其他类的实例使用。类可以定义为 public,这样它就可以被来自任何程序包的所有类实例所访问。您 可以回想一下第1部分、第3课:创建 Applet 程序 中介绍的内容:applet 程序类必须声明为公有类(public)才能被 appletviewer 工具所访问,这是因为 appletviewer 程序是从其它程序包中的类 创建的。

以下代码对 applet 类进行定义,使之具有 public 访问级别:

public class DbaAppl extends Applet
                 implements ActionListener {
如果没有定义为 public 访问级别(如下所述),其访问级别就默 认为 package。在试图利用 appletviewer 工具解释访问等级为 package 的类时,将会出现错误。访问级别为 protectedprivate 时,情况也是如此。
class DbaAppl extends Applet
                 implements ActionListener {
第2部分、第6课:全球化中,服务器类也被定义为公 有类,以便客户类能够访问它们。

域和方法

域和方法可以声明为 privateprotectedpublicpackage。若未指明访问级别,域或方法 的访问级别就默认为 package

private: 私有域和方法只能被定义该域或方法的类访问。 第1部分, 第7课:数据库访问及许可权 中用于定义数 据库访问权限的连接、用户名和密码均属 private(私有)。这是为了防止外部类 访问它们,从而危及数据库连接或破坏作为保密信息的用户名与密码信息。

  private Connection c;

protected: 被保护的域或方法只能被类本身、类的子类和同一 程序包中的类所访问。

public: 公共域或方法可被任何程序包中任意系列的类访问。 第2部分、第6课:国际化 中客户程序所访问的服务器 数据就被定义为 public。

package: 程序包域或方法可被同一程序包中的其它类访问。

自定义类

只要您使用了 Java API 库中的类,以上概念就存在于您的脑海中了。这些类 都来自给予它们继承关系的 java.lang.Object;它们都界定分明, 并且可以在适当的时候互操作。

例如,您不会发现任何以 Integer 为输入的 String 类,这是因为它超出了 String 的明确界定。但是,您会发现, Integer 类有一个把整型值转换为 String, 并使 该值能显示到只接受 String 对象的用户界面单元上的方法。

但是,当编写自己的类时情况会怎样呢?您又如何保证您所编写的类具有明确 的界定、能互操作并能使用继承性呢?方法之一是,明确您需要程序实现的功能并 把它们分别放置在不同的模块中。每一个功能模块都由自己的类和类群定义。

界定明确和互操作的类

分析第2部分、第5课:集合RMIClient2 ,您会发现它实现了以下功能:提取数据、显示数据、存放客户ID、打印客户ID 并重置显示区。

提取数据、显示数据和重置显示区都是密切相关的,并可以轻易地形成功能模 块。但是,在一个需要处理更多数据的较大程序中,就需要把客户 ID 的存放和 打印功能进行扩展,使之能够存放和打印范围更广的数据。此时,就需要一个单 独的类来实现数据存放,并需要另一个类来打印各种形式的数据。

例如,您可以用一个类来定义如何存放客户 ID 和跟踪本年度内苹果、桃和梨 的销售量。您也可以利用另一个类来定义报告打印。可读取已保存数据来按月、 按客户或按指定季度输出苹果、桃和梨的销售信息。

通过划分功能单元使应用程序编码模块化后,应用程序就更容易实现源代码的 更新和维护。当您改变了某个类时,只要您没有改变其公有接口的任何部分,您就 不必对这个类进行重新编译。

继承性

确定您的程序所需要的类,就是把功能分割成模块,而使程序代码更高效,而 更易于维护则是找出可以使用继承性的通用功能。如果需要编写一个具有功能类 似于 Java API 库中的类的类,就可以对 API 库中的这个类加以扩展并借用其方 法,而无需重新编写所有代码。

第2部分、第5课:集合 中的RMIClient2 类是把 JFrame 进行扩展并充分利用了它为程序的顶层窗口所提供的 现成功能,包括方框菜单关闭功能、背景颜色设置功能和一个自定义标题。

与此类似,如果您想在现有类中添加自定义动作,您就可以对现有类进行扩展 并添加您所需要的功能。例如,您可以创建一个外观不同的自定义 JButton 类。为此,您需要编写一个扩展 JButton 的类并使它实现后能得 到您想要的外观。然后,当您需要一个外观新颖的按钮时,您的程序就可以实例 化自己的按钮类而不是 JButton 类。

访问级别

声明类、域和方法时,应始终注意它们的访问级别。同时应考虑哪些对象真正 需要访问数据并需要使用程序包和访问级别,来避免系统中运行的所有其它对象 破坏您的应用程序数据。

大多数面向对象的应用程序的类对象通过把域定义为私有来禁止其它对象直接 访问这些域。而且,这些面向对象的应用程序还会在需要时把它们的方法定义为 受保护、公有型或程序包类的方法,并仅允许其它对象通过调用相应的方法来操 作它们的私有数据。这样,您就可以通过修改域定义和对应的方法实现代码来更 新自己的类,而不需要修改访问该数据的其它对象,因为它们与数据(方法符号) 的接口并没有改变。

程序的改进

本课涉及的程序改进包括设置合适的访问级别并把程序组织到功能单元中。

设置访问级别

最好的办法始终是尽可能多地限制访问。请回顾 第2部 分、第7课:程序包和JAR 文件 中的内容:服务器类必须声明为 publicDataOrder 类域也必须声明为 public,这样客户 程序才能访问到它们。

同时,并没有为其它类和域指定访问级别,所以它们的访问级别为默认的 package。所有方法的访问级别都是 public

最好回顾一下前文中有关客户类的内容,并为类、域和方法设置一个访问级别, 以使它们不致被其它对象访问。

客户程序RMIClient1.javaRMIClient2.java 的参考答案见 RMIClient1.java 和 RMIClient2.java。您能解释 actionPerformed 方法为何不能定义为 private 吗? 如果不能,可以尝试把它定义为 private,然后运行 javac 命令对其进行编译,然后观察编译器会给出什么信息。

把程序代码组织到功能单元中

把程序代码分割成功能单元的方法之一是,把用户接口程序放置到一个类中而 把对用户接口进行交互操作的相应代码放置到另一个类中。请回顾 第6课:国际化,然后把 RMIClient1.java 程序和 actionPerformed 方法移动到其自有类中。

提示:RMIClient1类将变为两个类。

  • 用于创建 UI 的类有一个 main 方法,第二个类则没有。
  • 带有 main 方法的类将创建第二个类的实例并把自身的实例 传递给第二个类。
  • 第二个类通过引用该类实例来访问第一个类的用户接口组件。
  • 需要了解第二个类如何访问带有翻译文本的信息包。
  • 必须对某些访问级别加以改变,以便这些类能够访问各自的成员。

以下为参考方案:

更多信息

有关面向对象的程序设计概念文件的详细说明,请参阅 Java 教程 中的 面向对象的程序设计概念


1术语“Java virtual machine(Java 虚拟机)”或“JVM”在本网站中引用时是指适用于 Java 平台的虚拟机。

[TOP]

 

 

常见问答
下载中心
产品简介
 
 
Solaris论坛
 
   
 
null