数据类型指的是一组值和一组对这些值的操作的集合。目前,我们已经详细讨论过Java的原始数据类型:例如,原始数据类型int的取值范围是-2” 到2"-1之间的整数,int的操作包括+、*、-、/、%、<和>。原则上所有程序都只需要使用原始数据类型即可,但在更高层次的抽象上编写程序会更加方便。
Java 编程的基础主要是使用class关键字构造被称为引用类型的数据类型。这种编程风格也称为面向对象编程,因为它的核心概念是对象,即保存了某个数据类型的值的实体。如果只有Java的原始数据类型,我们的程序会在很大程度上被限制在算术计算上,但有了引用类型,我们就能编写操作字符串、图像、声音以及Java的标准库中或者抽象类型的程序。
抽象数据类型是一种能够对使用者隐藏数据表示的数据类型。用Java类来实现抽象数据类型和用一组静态方法实现一个函数库并没有什么不同。抽象数据类型的主要不同之处在于它将数据和函数的实现关联,并将数据的表示方式隐藏起来。在使用抽象数据类型时,我们的注意力集中在API描述的操作上而不会去关心数据的表示:在实现抽象数据类型时,我们的注意力集中在数据本身并将实现对该数据的各种操作。
抽象数据类型之所以重要是因为在程序设计上它们支持封装。
要使用xxx对象,首先需要了解应该如何定义数据类型的操作,以及在Java语言中应该如何创建和使用某个数据类型的对象。
我们使用应用程序编程接口(API)来说明抽象数据类型的行为。它将列出所有构造函数和实例方法(即操作)并简要描述它们的功用,
尽管数据类型定义的基础是一组值的集合, 但在API可见的仅是对它们的操作,而非它们的意义。因此,抽象数据类型的定义和静态方法之间有许多共同之处:
1.两者的实现均为Java类;
2.实例方法可能接受0个或多个指定类型的参数,由括号表示并由逗号分隔:
3.它们可能会返回一个指定类型的值,也可能不会(用void表示)。
它们也有三个不同点:
1.API中可能会出现若千个名称和类名相同且没有返回值的函数。这些特殊的函数被称为构造函数。
2.实例方法不需要static关键字。它们不是静态方法——它们的目 的就是操作该数据类型中
3.某些实例方法的存在是为了尊重Java的习惯——我们将此类方法称为继承的方法。
根据Java的约定,任意数据类型都能通过在API中包含特定的方法从Java的内在机制中获益。例如,Java中的所有数据类型都会继承toString()方法来返回用String表示的该类型的值。Java会在用+运算符将任意数据类型的值和String值连接时调用该方法。该方法的默认实现并不实用(它会返回用字符串表示的该数据类型值的内存地址),因此我们常常会提供实现来重载默认实现,并在此时在API中加上toStrin()方法。此类方法的例子还包括equals()、compareTo() 和hashCode() 。
和基于静态方法的模块化编程一样,API 允许我们在不知道实现细节的情况下编写调用它的代码(以及在不知道任何用例代码的情况下编写实现代码)。这样,只要抽象数据类型的源代码java文件和我们的程序文件在同一个目录下,或是可以通过import语句访问,该程序就能够使用这个抽象数据类型,模块化编程的所有优势就都能够继续发挥。通过将实现某种数据类型的全部代码封装在一个 Java类中,我们可以将用例代码推向更高的抽象层次。在用例代码中,你需要声明变量。创建对象来保存数据类型的值并允许通过实例方法来操作它们。
一般来说,可以声明一个变量heads并将它通过以下代码和int类型的数据关联起来:int heads;
但如何为它赋值或是对它进行操作呢?这个问题的答案涉及数据抽象中的一个基础概念:对象是能够承载数据类型的值的实体。所有对象都有三大重要特性:状态、标识和行为。对象的状态即数据类型中的值。对象的标识份能够将一个对象区别于另一个对象。可以认为对象的标识就是它在内存中的位置。对象的行为就是数据类型的操作。数据类型的实现的唯一职责就是维护一个对象的身份,这样用例代码在使用数据类型时只需遵守描述对象行为的API即可,而无需关注对象状态的表示方法。对象的状态可以为用例代码提供信息,或是产生某种副作用,或是被数据类型的操作所改变。但数据类型的值的表示细节和用例代码是无关的。引用是访问对象的一种方式。Java 使用术语引用类型以示和原始数据类型(变量和值相关联)的区别。不同的Java实现中引用的实现细节也各不相同,但可以认为引用就是内存地址。
每种数据类型中的值都存储于一个对象中。要创建(或实例化)一个对象,我们用关键字new并紧跟类名以及((或在括号中指定一系列的参数,如果构造函数需要的话)来触发它的构造函数。构造函数没有返回值,因为它总是返回它的数据类型的对象的引用。每当用例调用了new(),系统都会:
1.为新的对象分配内存空间;
2.调用构造函数初始化对象中的值;
3.返回该对象的一个引用。
在用例代码中,我们一般都会在一条声明语句中创建一个对象并通过将它和-一个变量关联来初始化该变量,和使用原始数据类型时一样。和原始数据类型不同的是,变量关联的是指向对象的引用而并非 数据类型的值本身。我们可以用同一个类创建无数对象——每个对象都有自己的标识,且所存储的值和另一个相同类型的对象可以相同也可以不同。例如,以下代码创建了两个不同的对象:
Person p1 = new Person("小明");
Dog d= new Dog("小黄");
抽象数据类型向用例隐藏了值的表示细节。
Person p1 = new Person("小明");
Person p1 —— 将变量和对象的引用关联的声明语句
new Person("小明") —— 调用构造语句来创建一个对象