第4章 类 与 对 象   类与对象是进行面向对象编程的基础。类是具有相同属性特征和行为规则的多个对象的一种统一描述,对象是对类的实例化。在Java语言中,类是一种最基本的复合数据类型,是组成Java程序的基本要素。下面详细介绍Java语言中有关类与对象的定义方法。本章包括以下知识点。 * 类的创建; * 创建类的成员及方法; * 关键字this; * 对象的创建、使用及清除。   通过学习本章内容,读者可以掌握Java语言面向对象编程中有关类与对象的相关知识。具体包括如何创建类,如何创建对象、使用对象等。 4.1 创 建 类   面向对象的编程从创建类开始。创建了类,才谈得上封装、继承、多态,才能实例化生成对象。类的整体结构主要包括声明和类成员两大部分。本节详细介绍如何声明类及类成员的访问控制。 4.1.1 声明类   要使用一个类,必须先对其进行声明。声明类就是创建一种新的数据类型,利用声明的类可以定义类实例,也就得到了一个对象。声明类的语法如下所示。    {public} {abstract | final } class {extends } {implements } { //类主体 }      其中,参数public、abstract、final表示类修饰符是可选项,下面会详细说明。参数class是关键字。参数Classname表示类名。参数extends Classname表示继承自其他类。参数implements Interface表示实现了某些接口。继承和接口方面在后续章节会详细介绍。   【示例4-1】声明一个最简单的类,其代码如下所示。    class MyClass { //定义类MyClass }      分析:这是一个最简单的类,类的名字为MyClass,没有修饰符,不是继承自其他类,也没有实现接口。   下面对声明类中的一些细节作一下详细说明。从本质上讲,类是一种复合数据类型,类似面向过程语言中的结构体。类的定义主要包括声明和类主体两部分。类名必须符合Java标识符的规定,并且应该符合一定的编码规范,使程序具有很好的编程风格。在声明类的前面可以加上许多修饰符,不同的修饰符具有不同的含义。 * public修饰符:Java 语言中类的可访问控制符只有一个,即public。每个Java程序的主类都必须是public类,主类具有同文件名称相同的名字。作为公共工具供其他类和程序使用的应定义为public类。 * abstract修饰符:被修饰为abstract的类不能被实例化。类中的方法不能有方法体,继承它的子类必须实现其方法,除非子类也是抽象类。 * final修饰符:带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,比如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。   类声明好了以后,接下来就是定义类的主体。一个类所具有的属性、行为都是通过类主体来进行定义的。类主体就是对类的成员进行定义,类成员包括成员变量和成员方法。通过定义类的成员,一个类具有了自己的属性和方法,从而对外提供相应的服务。下面几节主要讲述类成员相关方面的内容。 4.1.2 类成员的访问控制   对于类的成员,不管是成员变量还是成员方法,都有一定的访问控制权限。访问控制权限限定了指定对象可以被访问的范围。类成员的访问控制符有public、private、protected和default。 * public:用public修饰的成分表示是公有的,也就是它可以被其他任何对象访问(前提是对类成员所在的类有访问权限)。 * private:类中限定为private的成员只能被这个类本身访问,在类外不可见。 * protected:用该关键字修饰的成分是受保护的,只可以被同一类及其子类的实例对象访问。 * default:public、private、protected这三个限定符不是必须写的。如果不写则表明是default,相应的成分可以被所在的包中各类访问。   对于变量及方法,其访问修饰符与访问能力之间的关系如表4-1所示(有关包的概念,后面会详细介绍)。 表4-1 类成员修饰符访问控制关系表 类 型 private default protected public 同一类   可访问   可访问   可访问 可访问 同一包中的子类   不可访问   可访问   可访问 可访问 同一包中非子类   不可访问   可访问   不可访问 可访问 不同包中的子类   不可访问   不可访问   可访问 可访问 不同包中非子类   不可访问   不可访问   不可访问 可访问 4.2 创建类的成员   类的成员变量定义了类的属性。所定义的成员变量,可以被类内的所有方法访问。比如,如果把电视机看作一个类,则电视机的颜色就是这个类的一个成员变量。该变量用来描述电视机的颜色。声明成员变量的语法如下所示。    {public | protected | private} {static} {final} {transient} {volatile} type variableName;      其中,参数public、private、protected表示成员变量的访问权限。参数static表示限定该成员变量为类变量。参数final表示声明为常量。参数transient表示声明为暂时性变量。参数volatile表示声明一个共享变量。参数type表示变量的类型。参数variableName表示变量 名称。   【示例4-2】声明一组成员变量,其代码如下所示。    class MyClass { //定义类MyClass String name; //定义成员变量name Int age; //定义成员变量age }      分析:MyClass类声明了两个简单的成员变量,一个是字符型,一个是整型。   类的成员变量作用域是整个类,类中的所有方法均可访问成员变量。成员变量一般定义在类体的最前面。变量名称必须符合标识符规定,应具有一定的含义,也应该遵循一定的编码规范。   类的成员变量声明时如果没有初始化,系统会自动为其进行初始化。对于数字类型,会自动初始化成0,字符会初始化成'o',对象引用会初始化成null.。 ?注意:被声明为static的成员变量在没有创建任何类实例的情况下,也可以通过类名被访问。而没有被声明为static属性的成员变量被称为实例变量和实例方法。实例变量和实例方法必须通过类的实例被访问,需要先创建对象。类变量属于一个类而被所有类的实例所共享。(static对于下面介绍的成员方法与成员变量用法相同) 4.3 创建类的方法   除了成员变量,方法也是类的重要成员。成员方法描述了对象所具有的功能或操作,反映对象的行为,是具有某种相对独立功能的程序模块。本节详细介绍如何创建类的方法以及类的特殊方法,即构造方法和main()方法。 4.3.1 定义类的成员方法   成员方法描述对象所具有的功能或操作,反映对象的行为,是具有某种相对独立功能的程序模块。它与过去所说的子程序、函数等概念相当。一个类或对象可以有多个成员方法,对象通过执行它的成员方法对传来的消息作出响应,完成特定的功能。成员方法一旦被定义,便可在不同的程序段中多次调用,故可增强程序结构的清晰度,提高编程效率。比如,如果把电视机看作一个类,则播放电视节目可以作为这个类的一个成员方法。该方法用来提供播放电视的功能。声明成员方法的语法如下所示。    {public|protected|private} {static} {final|abstract} {native} {synchronized} returnType methodName ({paramList}) {throws exceptionList} { //方法体 }      其中,参数public、private、protected表示成员方法的访问权限。参数static表示限定该成员方法为类方法。参数final或abstract表示该方法是否可以被重写。参数native表示用来把Java代码和其他语言的代码集成起来。参数synchronized表示用来控制多个并发线程对共享数据的访问。参数returnType表示方法的返回值类型。参数methodName表示方法名称。参数paramList表示方法的参数列表。参数throws exceptionList表示方法抛出的异常。有关异常的概念在后面章节详细介绍。   【示例4-3】声明一个简单的成员方法,其代码如下所示。    class MyClass { //定义类MyClass //定义方法sayHello public void sayHello() { System.out.println("Hello!"); //在控制台打印"Hello!" } }      分析:此函数完成在控制台打印“Hello!”的功能。函数的访问控制范围为公共的,返回类型void表示没有返回值,方法名称为sayHello,方法没有参数。   一个方法的定义,从整体上来说也是包含声明和方法体两部分。对于方法定义中的一些细节下面进行详细说明。   1.成员方法的返回值   若方法有返回值,则在方法体中用return语句指明要返回的值。其格式如下所示。    return 表达式;      或:    return (表达式);      表达式可以是常量、变量、对象等。return 语句后面表达式的数据类型必须与成员方法头中给出的返回值的类型returnType一致。   2.形式参数与实际参数   一般来说,可以通过以下方式来调用成员方法。    methodName({paramList})      在调用成员方法时应注意: * 对于无参成员方法来说,是没有实际参数列表的,但方法名后的括弧不能省略。 * 对于带参数的成员方法来说,实参的个数、顺序以及它们的数据类型必须与形式参数的个数、顺序以及它们的数据类型保持一致,各个实参间用逗号分隔。实参名与形参名可以相同也可以不同。   实参也可以是表达式,此时一定要注意使表达式的数据类型与形参的数据类型相同,或者使表达式的类型按Java类型转换规则达到形参指明的数据类型。   实参变量对形参变量的数据传递是“值传递”,即只能由实参传递给形参,而不能由形参传递给实参。程序中执行到调用成员方法时,Java把实参值复制到一个临时的存储区(栈)中,形参的任何修改都在栈中进行,当退出该成员方法时,Java自动清除栈中的内容。   3.方法体中的局部变量   在方法体内可以定义本方法所使用的变量,这种变量是局部变量,它的生存期与作用域是在本方法内。也就是说,局部变量只能在本方法内有效或可见,离开本方法则这些变量被自动释放。方法体内定义变量时,变量前不能加修饰符。局部变量在使用前必须明确赋值,否则编译时会出错。另外在一个方法内部,可以在复合语句中定义变量。这些变量只在复合语句中有效,这种复合语句也被称为程序块。 4.3.2 构造方法   Java中的每个类都有构造方法,它是类的一种特殊的方法。构造方法用来初始化类的一个新的对象。它必须具有和类名相同的名称,而且不指出返回类型,它的默认返回类型就是对象类型本身。每个类可以具有多个构造方法,它们各自包含不同的方法参数。构造方法的语法如下所示。    {public} {abstract|final} class {extends } {implements } { Classname({paramList}); //定义构造方法 Classname({paramList}); //定义其他构造方法 ... //类主体 }      其中,类声明部分的参数在前面已经介绍。其最大的特点是存在一到多个与类同名的方法,这些方法就是构造方法。   【示例4-4】构造方法声明的举例,其代码如下所示。    class MyClass { //定义类MyClass private int x; //定义私有变量 //定义没有参数的构造方法 MyClass () { x = 0; } //定义有参数的构造方法 MyClass (int x) { this.x = x; } }      分析:该示例定义了两个构造方法,分别是无参数的和有参数的。在一个类中定义多个具有不同参数的同名方法,这也就是方法的重载。这两个构造方法的名称都与类名相同,均为MyClass。在实例化该类的时候,就可以调用不同的构造方法进行初始化。   类的构造方法不是要求必须定义的。如果在类中没有明确地给出一个构造方法,Java就会自动构造一个默认的构造方法。默认的构造方法不包含任何参数。   只要类中定义了任何构造方法,Java就不再提供默认构造方法。 4.3.3 main()方法   main()方法是Java程序执行的入口。作为application执行的Java程序,有且只能有一个类具有main()方法。main()方法定义的语法如下所示。    public static void main(String args[]) { //方法体 }      其中,访问控制权限是公有的(public),并且是类的静态成员方法(static),没有返回值(void)。这些特性对main()方法来说是指定的且不能改变。main()方法具有一个字符串数组参数,用来接收执行Java程序时的命令行参数。命令行参数作为字符串,按照顺序依次对应字符串数组中的元素。   【示例4-5】输出main()方法参数个数及每个参数值,其代码如下所示。    class MyClass { //定义类MyClass //定义main方法 public static void main(String args[]) { int num = args.length; //定义参数个数变量 System.out.println("参数个数为:" + num); //输出参数个数 if (num > 0) { //参数个数大于0 for (int i = 0;i < num;i++) { System.out.println(args[i]); //输出每个参数 } } } }      程序编译成功后,用Java命令执行。键入如下命令:    java Myclass      程序执行结果如下。    参数个数为:0      加上命令行参数,键入如下命令:    java Myclass cs1 cs2      程序执行结果如下。    参数个数为:2 cs1 cs2      分析:由示例4-5可见,main()方法可以以字符串的形式接收命令行参数,然后在方法体内进行处理。 4.3.4 方法的可变参数   以上几节介绍了类中方法的创建。在具体实际开发过程中,有时方法中参数的个数是不确定的。为了解决这个问题,在J2SE 5.0 版本中引入了可变参数的概念。声明可变参数的语法格式如下所示。    methodName ({paramList},paramType...paramName)      其中,参数methodName表示方法名称。参数paramList表示方法的固定参数列表。参数paramType表示可变参数的类型。参数…是声明可变参数的标识。参数paramName表示可变参数名称。可变参数必须定义在参数列表的最后。   【示例4-6】输出可变参数的每个参数值的应用举例,其代码如下所示。    public class Test { //定义print方法 public void print(int... numbers) { //使用可变参数 for (int i = 0; i < numbers.length; i++) { System.out.print(numbers[i]); } System.out.println(); } public static void main(String args[]) { Test test = new Test(); test.print(1); test.print(1, 2); test.print(1, 2, 3); } }      分析:Test类定义了print()方法和main()方法。print()方法声明了一个int类型可变参数,方法体打印可变参数值。main()方法创建了Test类的实例,然后分别传入不同个数调用print()方法。 4.4 关键字this   在示例4-4中,出现了Java关键字this。this是Java语言中一个特殊的关键字,用来代表当前对象。   【示例4-7】使用关键字this为私有变量赋值的应用举例,其代码如下所示。    class MyClass { private int x; //定义私有变量x private int y; //定义私有变量y private int z; //定义私有变量z //定义初始化方法init void init(int z) { this.x = 1; //此处this可以省略 y = 2; //此处省略了this this.z = z; //此处this不能省略 } }      分析:在示例4-7中,init方法定义的形式化参数z与类的私有成员变量同名,在该方法内如果使用成员变量就必须使用this。在使用变量不会引起混淆的情况下,一般this是省略不写的,如变量y。   关键字this是与对象密切相关的,在容易混淆的地方使用this会使程序设计变得规范、简单、灵活。 4.5 对 象   对象是对类的实例化。对象具有状态和行为,变量用来表明对象的状态,方法表明对象所具有的行为。Java对象的生命周期包括创建、使用和清除。本节详细介绍对象的整个声明周期过程。 4.5.1 对象的创建   要使用一个对象,首先要创建它。通常创建对象的语法如下所示。    type objectName = new type({paramList});      其中,参数type表示对象的类型。参数objectName表示对象的名称。参数new为Java的关键字,下面会详细介绍。参数type({paramList})是对类型type构造方法的调用。   【示例4-8】创建两个对象,其代码如下所示。    Integer I = new Integer(1); //创建了Integer类型对象 MyClass myObject = new MyClass(); //创建了MyClass类型对象      分析:该示例创建了两个对象。第一个对象类型为Java语言定义的整型,并将其值初始化为1。第二个对象类型为自定义的类MyClass,使用其无参的构造方法初始化。   对象的创建主要由new关键字来完成。new关键字其实是一个运算符,创建对象的动作是由new运算符触发的。在程序底层,使用new运算符时会为对象分配内存空间。 ?说明:只有使用new关键字的地方才为对象分配内存空间。new调用对象的构造方法,返回该对象的一个引用(即该对象所在的内存地址)。 4.5.2 对象的使用   对象定义好了之后,才可以使用。对象的使用即是调用对象的变量或方法。使用对象的语法如下所示。    objectNmae.memberVariableName //调用对象的变量 objectNmae.memberMethodName //调用对象的方法      其中,参数objectNmae表示对象的名称。参数memberVariableName表示对象的成员变量名称。参数memberMethodName表示对象的成员方法名称。   【示例4-9】创建并使用对象,其代码如下所示。    class MyClass { //定义类MyClass public int x; //定义公共变量x //定义没有参数的构造方法 MyClass () { x = 0; } //定义sayHello方法 public void sayHello() { System.out.println("Hello!"); //在控制台打印"Hello!" } } public class useObject { //定义类useObject //定义main方法 public static void main(String args[]) { MyClass myObject = new MyClass(); //创建对象myObject System.out.println(myObject.x); //调用对象myObject的成员变量x myObject.sayHello(); //调用对象myObject的成员方法 sysHello } }      分析:该示例在类useObject的main()方法中创建了对象myObject,并访问了该对象的成员变量x和成员方法sayHello()。   从上面的语法形式和示例可看出,对对象中变量和方法的访问是通过点操作符完成的。上面的示例采用的是最常用的对象使用方式。除此之外,对象还可以作为方法参数、数组元素等方式使用。 ?技巧:如果创建的对象只使用一次,可以不定义对象变量而是用点操作符直接操作。如:new Integer(1).toString(); 创建了对象,但没有定义引用变量。 4.5.3 对象的清除   对象使用完之后需要对其进行清除。对象的清除是指释放对象占用的内存。在创建对象时,用户必须使用new操作符为对象分配内存。不过,在清除对象时,由系统自动进行内存回收,不需要用户额外处理。这也是Java语言的一大特色,某种程度上方便了程序员对内存的管理。   Java语言的内存自动回收称为垃圾回收(Garbage Collection)机制,简称GC。垃圾回收机制是指JVM用于释放那些不再使用的对象所占用的内存。Java语言并不要求JVM有GC,也没有规定GC如何工作。不过常用的JVM都有GC,而且大多数GC都使用类似的算法管理内存和执行收集操作。具体的垃圾收集实现策略有好多种,在此不再赘述。   Java语言允许用户使用finalize()方法在垃圾收集器中将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对它进行调用的。其是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。   【示例4-10】一般实现finalize()方法的代码如下所示。    protected void finalize() throws throwable { ...//释放占用的相关资源 super.finalize(); }      分析:finalize()是位于Object类的一个方法,该方法的访问修饰符为protected。由于所有类为Object的子类,因此用户类很容易访问到这个方法。由于finalize函数没有自动实现链式调用,我们必须手动地实现。因此finalize函数的最后一个语句通常是super.finalize()。super的概念在后续章节介绍。通过这种方式,可以实现从下到上实现finalize的调用。即先释放自己的资源,然后再释放父类的资源。 4.6 本 章 实 例   在开发某图书管理系统时,其中有三个类,即图书类Book、图书类别类BookType和图书管理类BookManager。图书类Book用于管理图书本身的相关信息,包括编号、书名、作者、出版社等;图书类别类BookType用于表示图书分类,包括类别编号、类别名称等;图书管理类BookManager用于管理所有图书,主要是针对数据库进行操作,包括增加、删除、修改等。本章实例将实现图书管理系统中的这些核心类。类图如图4-1所示。   1.编写程序   该实例的编写步骤如下所示。   (1)创建工程   启动Eclipse,在工作台的主菜单选择File | New | Project命令。Eclipse弹出新建工程(Project)向导界面,如图4-2所示。选择Java Project选项,单击Next按钮,弹出工程名称设置界面,如图4-3所示。 图4-1 图书管理系统部分类图 图4-2 Eclipse创建工程界面 图4-3 Eclipse命名工程界面   在Project name文本框输入工程名为“Chap04”,单击Finish按钮,完成工程创建。   (2)创建类   在Eclipse工作台的“Package”区中,右击工程“Chap04”,选择New | Class命令,Eclipse弹出新建类的界面,如图4-4所示。在Name文本框输入类名为“Book”,单击Finish按钮,Book类创建完成。采用同样的方法创建“BookType”类,如图4-5所示。   同样,创建“BookManager”和“Test”类分别如图4-6、图4-7所示。 图4-4 Eclipse创建类界面 图4-5 Eclipse创建类界面 图4-6 Eclipse创建类界面 图4-7 Eclipse创建类界面   (3)添加代码   在代码编辑区添加代码,Book类的代码如下所示。    public class Book { private int id; //图书编号 private String name; //图书名称 private BookType type; //图书类别 private String author; //作者 private String translator; //译者 private String publishing_company; //出版社 private float price; //价格 private int stock; //库存数量 //无参构造方法 public Book() { } //带参数的构造方法 public Book(int id, String name, BookType type, String author, String translator, String publishing_company, float price, int stock) { this.id = id; this.name = name; this.type = type; this.author = author; this.translator = translator; this.publishing_company = publishing_company; this.price = price; this.stock = stock; } /** * 获得图书作者 */ public String getAuthor() { return author; } /** * 设置图书作者 */ public void setAuthor(String author) { this.author = author; } /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } /** * @return the price */ public float getPrice() { return price; } /** * @param price the price to set */ public void setPrice(float price) { this.price = price; } /** * @return the publishing_company */ public String getPublishing_company() { return publishing_company; } /** * @param publishing_company the publishing_company to set */ public void setPublishing_company(String publishing_company) { this.publishing_company = publishing_company; } /** * @return the stock */ public int getStock() { return stock; } /** * @param stock the stock to set */ public void setStock(int stock) { this.stock = stock; } ...//限于篇幅,部分代码省略 }      图书类别类BookType的代码如下所示。    public class BookType { private int id; //图书类别编号 private String name; //图书类别名称 private int days; //可借天数 //无参构造方法 public BookType() { } //带参数的构造方法 public BookType(int id, String name, int days) { this.id = id; this.name = name; this.days = days; } /** * 获得可借天数 */ public int getDays() { return days; } /** * 设置可借天数 */ public void setDays(int days) { this.days = days; } /** * @return the id */ public int getId() { return id; } /** * @param id the id to set */ public void setId(int id) { this.id = id; } /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } }      图书管理类BookManager的代码如下所示。    import java.util.List; public class BookManager { //将图书book增加到数据库 public void add(Book book) { //... } //从数据库中删除图书book public void delete(Book book) { //... } //更新数据库中图书book的信息 public void update(Book book) { //... } //从数据库中查询编号为bookId的图书 public Book query(int bookId) { Book book = null; //... return book; } //从数据库中查询满足条件的所有图书 public List query(String sql) { List bookList = null; //... return bookList; } }      测试类Test的代码如下所示。    public class Test { public static void main(String args[]) { BookManager manager = new BookManager(); //增加图书 BookType type = new BookType(1,"科技类",30); //创建图书类别 Book book = new Book(); book.setId(1); book.setName("Java语言入门"); book.setAuthor("赵大"); book.setPublishing_company("电子工业出版社"); book.setPrice(49.00f); book.setStock(5); book.setType(type); manager.add(book); //查询并修改图书 book = manager.query(1); book.setStock(8); manager.update(book); } }      2.程序说明   (1)图书类Book用于管理图书本身的相关信息,定义的属性包括编号、书名、类别、作者、译者、出版社、价格、库存量。Book类定义了两个构造方法,一个是无参的构造方法,另一个是以所有属性为参数的构造方法。另外,由于所有属性的访问控制类型均为private,所以针对每个属性分别定义了相应的get()/set()方法。   (2)图书类别类BookType用于表示图书分类,定义的属性包括类别编号、类别名称、可借天数。BookType类也定义了两个构造方法,一个是无参的构造方法,另一个是以所有属性为参数的构造方法。并且也针对每个属性分别定义了相应的get()/set()方法。   (3)图书管理类BookManager用于管理所有图书,主要是针对数据库进行操作,包括增加、删除、修改等。BookManager类没有定义任何属性,定义了add()、delete()、update()、query()方法,分别用于图书的增加、删除、修改、查询。不过这里省略了方法体的具体实现代码,主要是一些数据库操作。   (4)最后定义了一个测试类Test,用于测试这几个类。main()方法首先创建了图书管理类BookManager的对象manager。然后创建了一个Book对象book,并设置了相关属性,并使用manager对象将book添加到数据库中。最后使用manager查询编号为1的图书,并将其库存量修改为8。 4.7 上 机 实 践   1.编写程序,创建一个汽车类(Car)。该类包含颜色和型号属性,并为其创建两个构造函数:   第一个为无形参的构造函数,其过程是将颜色和型号初始化为红色轿车。   第二个需定义两个形参,并将形参的值分别赋给颜色和型号。   另外,为该类创建两个方法,分别用来获得颜色和型号。在该类中定义一个main()方法,分别用上面的两个构造函数创建两个对象,并显示相关对象的各个属性。   汽车类(Car)类图如图4-8所示。   主要实现过程如下所示。   (1)使用Eclipse新建一个工程。   (2)新建一个类,类名为Car。   (3)构建属性和方法。其关键代码如下所示。    public class Car { private String color; private String type; public Car() { //... } public Car(String color, String type) { //... } public String getColor() { return color; } public String getType() { return type; } }      (4)编译该类文件并运行。   2.编写程序,定义一个复数类,该类可以通过构造函数给复数对象赋值,实部和虚部是它的私有属性,该类必须有获取和修改属性和定义复数间加、减、乘、除的方法。   复数类类图如图4-9所示。   主要实现过程如下所示。   (1)使用Eclipse新建一个工程。   (2)新建一个类。   (3)构建属性和方法。其关键代码如下所示。    public class Fushu { private double shibu; private double xubu; public Fushu(double shibu, double xubu) { //... } public double getShibu() { return shibu; } public void setShibu(double shibu) { this.shibu = shibu; } public double getXubu() { return xubu; } public void setXubu(double xubu) { this.xubu = xubu; } public Fushu add(Fushu fushu) { //... return fushu; } public Fushu sub(Fushu fushu) { //... return fushu; } public Fushu mul(Fushu fushu) { //... return fushu; } public Fushu div(Fushu fushu) { //... return fushu; } }      (4)编译该类文件并运行。 4.8 常见问题及解答   1.什么是对象的引用?对象和对象的引用有什么区别?   对于Java中的对象及引用是非常容易混淆却又必须掌握的基础知识。在上面章节中讲对象的创建时曾经提到“只有使用new关键字的地方才为对象分配内存空间”。new调用对象的构造方法,返回该对象的一个引用。   引用的概念比较抽象,初学者有些难以理解。Java最基本的概念就是类,类包括函数和变量。如果想要应用类,就要把类生成对象,这个过程被称作“类的实例化”。有几种方法可以把类实例化成对象,最常用的就是使用“new”操作符。类实例化成对象后,就意味着要在内存中占据一块空间存放实例。想要对这块空间操作就要应用到对象的引用。引用在Java语言中的体现就是变量,而变量的类型就是这个引用的对象。为了更好地理解引用的概念,请看如下代码。    Object o = new Object();      上面代码看上去非常简单,不过却包含了一定的深意。在代码中“new Object()”是实实在在地创建 了一个对象,对应着物理上的真实内存空间;而变量“o”只是对刚才创建对象的一个引用。其关系如图4-10所示。   打个形象的比喻,如果对象是一个真实存在的气球,那么引用就像是拴气球的绳子。当然,一个气球上可以拴多条绳子,也就是一个对象可以有多个引用。再来看下面这段代码。    Object o1 = new Object(); Object o2 = new Object(); o1 = o2; o2 = null;      执行代码“Object o1 = new Object();Object o2 = new Object();”时创建了两个对象,并且为这两个对象分别指定了引用o1和o2,如图4-11所示。   接下来,执行代码“o1 = o2;o2 = null;”后,引用o1指向了创建的第二个对象;而引用o2指向了一1个空对象,如图4-12所示。 图4-11 引用与对象关系 图4-12 引用与对象关系   这就好比有两个气球,1号气球和2号气球。刚开始1号气球用绳子o1拴着,2号气球用绳子o2拴着。然后解开绳子o1拴到2号气球上。最后解开绳子o2,什么也不拴。   希望读者能够结合上面的例子深入体会对象和引用的概念。   2.在Java语言中,“= =”和equals有什么区别?   Java语言的对象提供了equals方法。从表面上看,运算符“= =”和equals方法都是用来判断所比较的内容是否相等,实际上却有很大的区别。equals是值比较,比较两个对象中的内容是否相等;而运算符“==”比较两边是否指向同一个对象。其实在充分理解了问题1中所说的对象和引用后,理解“= =”和equals的区别也就变得相对容易些。下面通过一段代码来说明这个问题。    public class Test { public static void main(String[] args) { String s1 = new String("abc"); String s2 = new String("abc"); String s3 = s1; System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println(s1 == s2); System.out.println(s1 == s3); } }      程序的执行结果如下。    true true false true   由于三个字符串变量s1、s2、s3的内容相等,都为“abc”,所以执行语句“s1.equals(s2)”和“s1.equals(s2)”的返回值都为true。又因为s1和s2是两个不同的对象引用,所以语句“s1==s2”的返回值为false。而s3和s1则是相同的对象引用,所以语句“s1==s3”的返回值为true。 4.9 小 结   本章介绍了Java语言面向对象编程中有关类与对象的相关知识。主要包括类及成员变量、成员方法的创建,以及对象的创建、使用、清除等方面的知识。本章最后给出了一个完整的实例。其中类的成员变量、成员方法的定义和使用是本章的难点。在实际编程过程中,应该多通过具体实例加深理解。下一章将进一步深入介绍Java语言面向对象编程方面的知识,包括类的继承、封装、多态以及接口和包等。 4.10 本 章 习 题   1.定义一个类,包含成员变量、构造方法、成员方法。   2.构造方法与成员方法有哪些不同?   3.类和对象之间是什么关系,用具体实例说明如何创建一个类的对象。 第1篇 Java基础与面向对象编程篇    第4章 类与对象    ·68·       ·69·