目录
6.1 面向对象概述
6.1.1 对象
6.1.2 类
6.1.3 面向对象程序设计的特点
6.2 类与对象
6.2.1 成员变量
6.2.2 成员方法
6.2.3 构造方法
6.2.4 局部变量
6.2.5 局部变量的有效范围
6.2.6 对象的创建
6.2.7 访问对象的属性和行为
6.2.8 对象的销毁
6.2.9 this关键字
6.3 static 关键字
6.3.1 静态变量
6.3.2 静态常量
6.3.3 静态方法
6.3.4 静态代码块
6.4 类的主方法
6.1 面向对象概述
随着软件的规模越来越庞大,结构化语言的弊端也逐渐暴露出来,开发周期被无休止地拖延,产品的质量也不尽如人意,结构化语言已经不再适合当前的软件开发。这时人们开始将另一种开发思想引入程序中,即面向对象的开发思想。面向对象思想是人类最自然的一种思考方式,它将所有预处理的问题抽象为对象,同时了解这些对象具有哪些相应的属性以及行为,以解决这些对象面临的一些实际问题,这样就在程序开发中引入了面向对象设计的概念,面向对象设计实质上就是对现实世界的对象进行建模操作。
6.1.1 对象
现实世界中,随处可见的一种事物就是对象,对象是事物存在的实体,如人类、书桌、计算机、高楼大厦等。人类解决问题的方式总是将复杂的事物简单化,于是就会思考这些对象都是由哪些部分组成的。通常都会将对象划分为两个部分,即静态部分与动态部分。静态部分,顾名思义,就是不能动的部分,这个部分被称为“属性”,任何对象都具备其自身属性,如一个人,其属性包括高矮、胖瘦、性别、年龄等。然而具有这些属性的人会执行哪些动作也是一个值得探讨的部分,这个人可以哭泣、微笑、说话、行走,这些是这个人具备的行为(动态部分),人类通过探讨对象的属性和观察对象的行为了解对象。
在计算机的世界中,面向对象程序设计的思想要以对象来思考问题,首先要将现实世界的实体抽象为对象,然后考虑这个对象具备的属性和行为。
6.1.2 类
不能将所谓的一个事物描述成一类事物,如一只鸟不能称为鸟类。如果需要对同一类事物统称,就不得不说明类这个概念。
类就是同一类事物的统称,如果将现实世界中的一个事物抽象成对象,类就是这类对象的统称,如鸟类、家禽类、人类等。类是构造对象时所依赖的规范,如一只鸟具有一对翅膀,它可以用这对翅膀飞行,而基本上所有的鸟都具有翅膀这个特性和飞行的技能,这样具有相同特性和行为的一类事物就称为类,类的思想就是这样产生的。
类是封装对象的属性和行为的载体,反过来说具有相同属性和行为的一类实体被称为类。
6.1.3 面向对象程序设计的特点
面向对象程序设计具有以下特点: 封装性、继承性、多态性
1.封装
封装是面向对象编程的核心思想。将对象的属性和行为封装起来,其载体就是类,类通常对客户隐藏其实现细节,这就是封装的思想。例如,用户使用计算机时,只需要使用手指敲击键盘就可以实现一些功能,无须知道计算机内部是如何工作的,即使可能知道计算机的工作原理,但在使用计算机时也并不完全依赖于计算机工作原理这些细节。
采用封装的思想保证了类内部数据结构的完整性,应用该类的用户不能轻易地直接操作此数据结构,只能执行类允许公开的数据。这样就避免了外部操作对内部数据的影响,提高了程序的可维护性。
2.继承
类与类之间同样具有关系,如一个百货公司类与销售员类相联系,类之间的这种关系被称为关联。关联主要描述两个类之间的一般二元关系,例如,一个百货公司类与销售员类就是一个关联,学生类与教师类也是一个关联。两个类之间的关系有很多种,继承是关联中的一种。
继承性主要利用特定对象之间的共有属性。例如,平行四边形是四边形,正方形、矩形也是四边形,平行四边形与四边形具有共同特性,就是拥有4条边,可以将平行四边形类看作四边形的延伸,平行四边形复用了四边形的属性和行为,同时添加了平行四边形独有的属性和行为,如平行四边形的对边平行且相等。这里可以将平行四边形类看作是从四边形类中继承的。
3.多态
介绍了继承,了解了父类和子类,其实将父类对象应用于子类的特征就是多态,多态的实现并不依赖具体类,而是依赖于抽象类和接口。下面以图形类来说明多态。
图形类作为所有图形的父类,具有绘制图形的能力,这个方法可称为“绘制图形”,但如果要执行这个“绘制图形”的命令,没有人知道应该画什么样的图形,并且如果要在图形类中抽象出一个图形对象,没有人能说清这个图形究竟是什么图形,所以使用“抽象”这个词来描述图形类比较恰当。
6.2 类与对象
在6.1.2节中已经讲过类是封装对象的属性和行为的载体,Java中定义类使用class关键字,其语法如下:
class 类名称 {
//类的成员变量
//类的成员方法}
6.2.1 成员变量
在Java中对象的属性也称为成员变量,成员变量的定义与普通变量的定义一样,语法如下:
数据类型 变量名称[=值];
其中,[=值表示可选内容,即定义变量时可以为其赋值,也可以不为其赋值。
为了了解成员变量,首先定义一个鸟类,成员变量对应于类对象的属性,在Bird类中设置4个成员变量,分别为wing、claw、 beak和feather,分别对应于鸟类的翅膀、爪子、喙和羽毛。
例如,在项目中创建鸟类Bird,在该类中定义成员变量。
public class Bird {
String wing; //翅膀
String claw; // 爪子
String beak; //喙
String feather; //羽毛
}
Bird 类中定义了4个成最必量 层族图的来期得有了不建义尖,Bird是类的名称。同时在
类中定义了4个成员变量,成员变量的类型可以设置为Java中合法的数据类型,其实成员变量就是普通的变量,可以为它设置初始值,也可以不设置初始值。如果不设置初始值,则会有默认值。Java中常见类型的默认值如表6.1所示:
6.2.2 成员方法
成员方法对应于类对象的行为,它主要用来定义类可执行的操作,它是包含一系列语句的代码块。
1. 成员方法的定义
定义成员方法的语法格式如下:
[权限修饰符] [返回值类型] 方法名( [参数类型 参数名] ) [throws 异常类型] {
...//方法体
return 返回值;
}
其中,“权限修饰符”可以是private、public、protected中的任一个,也可以不写,主要用来控制方法的访问权限,关于权限修饰符将在下一章中详细讲解;“返回值类型”指定方法返回数据的类型,可以是任何类型,如果方法不需要返回值,则使用void关键字;一个成员方法既可以有参数,也可以没有参数,参数可以是对象也可以是基本数据类型的变量。
例如,定义一个showGoods方法,用来输出库存商品信息,代码如下:
public void showGoods() {
System.out.print1n("库存商品名称:");
System.out.println(FullName);
}
如果定义的方法有返回值,则必须使用retum关键字返回一个指定类型的数据,并且返回值类型要与方法返回的值类型一致。例如,定义一个返回值类型为int的方法,就必须使用return返回一个int类型的值,代码如下:
public int showGoods() {
System.out.println("库存商品名称:");
return 1;
}
2. 成员方法的参数
调用方法时可以给该方法传递一个或多个值,传给方法的值叫做实参,在方法内部,接收实参的变量叫做形参,形参的声明语法与变量的声明语法一样。形参只在方法内部有效。Java中方法的参数主要有3种,分别为值参数、引用参数和不定长参数。
(1) 值参数
值参数表明实参与形参之间按值传递,当使用值参数的方法被调用时,编译器为形参分配存储单元,然后将对应的实参的值复制到形参中,由于是值类型的传递方式,所以,在方法中对值类型的形参的修改并不会影响实参。
例6.1 定义一个add方法,用来计算两个数的和,该方法中有两个形参,但在方法体中,对其中的一个形参x执行加y操作,并返回x;在main方法中调用该方法,为该方法传入定义好的实参;最后分别显示调用add方法计算之后的x值和实参x的值。
代码如图所示:
运行结果如图所示:
(2) 引用参数
如果在给方法传递参数时,参数的类型是数组或者其他引用类型,在方法中对参数的修改会反映到原有的数组或者其他引用类型上,这种类型的方法参数,称之为引用参数。
例 6.2 定义一个change方法,该方法中有一个形参,类型为数组类型,在方法体中,改变数组的索引0、1、2这3处的值;在main方法中定义一个一维数组并初始化,然后将该数组作为参数传递给change方法,最后输出一维数组的元素。
代码如图所示:
运行结果如图所示:
(3) 不定长参数
声明方法时,如果有若干个相同类型的参数,可以定义为不定长参数,该类型的参数声明如下:
权限修饰符 返回值类型 方法名(参数类型... 参数名)
例 6.3 定义一个add方法,用来计算多个int类型数据的和,在具体定义时,将参数定义为int
类型的不定长参数;在main方法中调用该方法,为该方法传入多个int类型的数据,并输出计算结
果。
代码如图所示:
运行结果如图所示:
3.成员方法的使用
例6.4 创建猎豹类,用成员方法实现猎豹的行为。
代码如图所示:
运行结果如图所示:
6.2.3 构造方法
构造方法是一个与类同名的方法,对象的创建就是通过构造方法完成的。每当类实例化一个对象时,类都会自动调用构造方法。
构造方法的特点如下:
(1) 构造方法没有返回类型,也不能定义为void。
(2) 构造方法的名称要与本类的名称相同。
(3) 构造方法的主要作用是完成对象的初始化工作,它能把定义对象的参数传给对象成员。
构造方法的定义语法如下:
class Book {
public Book() { // 构造方法
}
}
Public: 构造方法修饰符
Book: 构造方法的名称。
在构造方法中可以为成员变量赋值,这样当实例化一个本类的对象时,相应的成员变量也将被初始化。如果类中没有明确定义构造方法,则编译器会自动创建一个不带参数的默认构造方法。
在类中定义构造方法时,还可以为其添加一个或者多个参数,即有参数构造方法,语法如下:
class Book {
public Book(int args) { //有参数构造方法
//对成员变量进行初始化
}
}
public:构造方法修饰符
Book:构造方法的名称
args: 构造方法的参数,可以是多个参数
例 6.5 创建一个图书类,将构造方法设为私有,这时如果需要创建图书类的对象,只能通过
义一个static方法,并调用该静态方法生成图书类的对象。
代码如图所示:
运行结果如图所示:
6.2.4 局部变量
如果在成员方法内定义一个变量,那么这个变量被称为局部变量。
局部变量在方法被执行时创建,在方法执行结束时被销毁。局部变量在使用时必须进行赋值操作或被初始化,否则会出现编译错误。
例如,在项目中创建一个类文件,在该类中定义getName)方法并进行调用。
public String getName() { //定义一个getName()方法
int id=0; //局部变量
setName("Java"); //调用类中其他方法
return id+this.name; //设置方法返回值
}
如果将id这个局部变量的初始值去掉,编译器将出现错误。
6.2.5 局部变量的有效范围
可以将局部变量的有效范围称为变量的作用域,局部变量的有效范围从该变量的声明开始到该变量的结束为止。图6.13描述了局部变量的作用范围。
6.2.6 对象的创建
对象可以认为是在一类事物中抽象出某一个特例,可以通过这个特例来处理这类事物出现的问题。在Java语言中通过new操作符来创建对象。前文在讲解构造方法时介绍过,每实例化一个对象就会自动调用一次构造方法,实质上这个过程就是创建对象的过程。准确地说,可以在Java语言中使用new操作符调用构造方法创建对象。语法如下:
Test test=new Test();
Test test=new Test("a");
Test: 类名
test: 创建Test类对象
new: 创建对象操作符
a: 构造方法的参数
每个对象都是相互独立的,在内存中占据独立的内存地址,并且每个对象都具有自己的生命周期,当一个对象的生命周期结束时,对象就变成垃圾,由Java虚拟机自带的垃圾回收机制处理,不能再被使用。
例6.6 在项目中创建CreateObject类,在该类中创建对象并在主方法中创建对象。
代码如图所示:
运行结果如图所示:
6.2.7 访问对象的属性和行为
用户使用new操作符创建一个对象后,可以使用“对象.类成员”来获取对象的属性和行为。前
文已经提到过,对象的属性和行为在类中是通过类成员变量和成员方法的形式来表示的,所以当对
象获取类成员时,也相应地获取了对象的属性和行为。
例 6.7 在项目中创建TransferProperty类,在该类中说明对象是如何调用类成员的。
代码如图所示:
运行结果如图所示:
在上述代码的主方法中首先实例化一个对象,然后使用“.”操作符调用类的成员变量和成员方法。但是在运行结果中可以看到,虽然使用两个对象调用同一个成员变量,结果却不相同,因为在打印这个成员变量的值之前将该值重新赋值为60,但在赋值时使用的是第二个对象t2调用成员变量,所以在第一个对象t1调用成员变量打印该值时仍然是成员变量的初始值。由此可见,两个对象的产生是相互独立的,改变了t2的i值,不会影响到tl的i值。在内存中这两个对象的布局如图6.19所示:
6.2.8 对象的销毁
每个对象都有生命周期,当对象的生命周期结束时,分配给该对象的内存地址会被回收。在其他语言中需要手动回收废弃的对象,但是Java拥有一套完整的垃圾回收机制,用户不必担心废弃的对象占用内存,垃圾回收器将回收无用的但占用内存的资源。
在谈到垃圾回收机制之前,首先需要了解何种对象会被Java虚拟机视为垃圾。主要包括以下两种情况:
(1) 对象引用超过其作用范围,这个对象将被视为垃圾,如图6.20所示:
(2) 将对象赋值为null,如图6.21所示:
虽然垃圾回收机制已经很完善,但垃圾回收器只能回收那些由new操作符创建的对象,如果某些对象不是通过new操作符在内存中获取一块内存区域,这种对象可能不能被垃圾回收机制所识别,所以在Java中提供了一个finalize方法。这个方法是Object类的方法,它被声明为protected,用户可以在自己的类中定义这个方法。如果用户在类中定义了finalize()方法,在垃圾回收时会首先调用该方法,在下一次垃圾回收动作发生时,才能真正回收被对象占用的内存。
说明:
有一点需要明确的是,垃圾回收或finalize)方法不保证一定会发生,如Java虚拟机内存损耗殆尽时,它是不会执行垃圾回收的。
由于垃圾回收不受人为控制,具体执行时间也不确定,所以finalize()方法也就无法执行,为此,Java提供了System.gc0方法强制启动垃圾回收器,这与给120打电话通知医院来救护病人的道理一样,告知垃圾回收器进行清理。
6.2.9 this关键字
例 6.8 创建Book2类,定义一个成员变量name并赋初值,再定义一个成员方法showName
(String name),输出方法中name的值。
代码如图所示:
运行结果如图所示:
从这个结果可以看出,输出的值不是成员变量的值,也就是说如果方法中出现了与局部变量同名的参数,会导致方法无法直接使用该成员变量。
在上述代码中可以看到,成员变量与在showName0方法中的形式参数的名称相同,都为name.那么该如何在类中区分使用的是哪一个变量呢?在Java语言中规定使用this关键字来代表本类对象的引用,this关键字被隐式地用于引用对象的成员变量和方法。
例 6.9 在Book3类的showName()方法中,使用this关键字。
代码如图所示:
运行结果如图所示:
明白了this可以调用成员变量和成员方法,但Java语言中最常规的调用方式是使用“对象.成员变量”或“对象.成员方法”进行调用。
事实上,this引用的就是本类的一个对象,在局部变量或方法参数覆盖了成员变量时,如上面代码的情况,就要添加this关键字明确引用的是类成员还是局部变量或方法参数。
如果省略this关键字直接写成 name=name,那只是把参数name赋值给参数变量本身而已,成员变量name的值没有改变,因为参数name在方法的作用域中覆盖了成员变量name。
其实,this除了可以调用成员变量或成员方法之外,还可以作为方法的返回值。
例如,在项目中创建一个类文件,在该类中定义Book类型的方法,并通过this关键字进行返回。
public class Book {
public Book getbook() {
return this;//返回book类引用
}
}
在getBook0方法中,方法的返回值为Book类,所以方法体中使用return this这种形式将Book类的对象进行返回。
介绍过this关键字,了解了this可以调用类的成员变量和成员方法,事实上this还可以调用类中的构造方法。
例6.10 我去买鸡蛋灌饼,我要求加几个蛋时,烙饼大妈就给饼加几个蛋,不要求的时候就只加一个蛋。创建鸡蛋灌饼EggCake类,创建有参数和无参数构造方法,无参数构造方法调用有参数实现初始化。
代码如图所示:
运行结果如图所示:
6.3 static 关键字
由static 修饰的变量、常量和方法被称作静态变量、静态常量和静态方法,也被称为类的静态
成员。静态成员是属于类所有的,区别于个别对象。
6.3.1 静态变量
不同的类之间需要对同一个变量进行操作,比如一个水池,同时打开入水口和出口,进水和出水这两个动作会同时影响到池中的水量,此时池中的水量就可以认为是一个共享的变量。在Java程序中,把共享的变量用static修饰,该变量就是静态变量。
可以在本类或其他类使用类名和“.”运算符调用静态变量。
语法如下:
类名,静态类成员
例6.11 创建一个水池类,创建注水方法和放水方法,同时控制水池中的水量。
代码如图所示:运行结果如图所示:
常见错误:
同一个类的不同实例对象,共用同一静态变量,如果一个类将其更改,另一个类静态变量也会更现
例6.12 建StaticVariable类,包含一个静态成员变量和普通成员变量,在构造方法中给两个变量赋初值,然后分别实例化两个不同的对象。
代码如图所示:运行结果如图所示:
代码注解:
从结果中我们发现,a.x的值并不是我们赋予的初值,而是和b.x的值一样。这是因为StaticVariable类的静态变量x是被本类共享的,当对象a给x赋值之后,b又给x赋了一次值,最后x就变成b.x的字面值了。在内存中的状态如图6.27所示,其实a.x和b.x都是同一个值,即StaticVariable.x。
说明:
当类首次被加载时,静态变量就被分配到内存中,直到程序结束才会释放。
6.3.2 静态常量
在处理问题时会需要两个类共享一个数据常量。例如,在球类中使用PI这个常量,可能除了本类需要这个常量之外,在另外一个圆类中也需要使用这个常量。这时没有必要在两个类中同时创建PI这个常量,因为这样系统会将这两个不在同一个类中的常量分配到不同的内存空间中,浪费了系统资源。为了解决这个问题,可以将这个常量设置为静态的。PI常量在内存中被共享的布局如图6.28所示:
用final static修饰一个成员变量,这个成员变量就会变成一个静态常量。例如:
final static double PI = 3.1415926;
例 6.13 将元的值赋给静态常量PI.使用PI计算圆类的面积和球类的体积。
代码如图所示:
运行结果如图所示:
注意:
给静态变量命名时所有字母都应该大写。
6.3.3 静态方法
如果想要使用类中的成员方法,需要先将这个类进行实例化,但有些时候不想或者无法创建类的对象时,还要调用类中的方法才能够完成业务逻辑,此时就可以使用静态方法。调用类的静态方法,无需创建类的对象。
语法如下:
类名.静态方法();
例6.14 不创建类对象,直接使用静态方法。
代码如图所示:
运行结果如图所示:
6.3.4 静态代码块
在类中除成员方法之外,用static修饰代码区域可以称之为静态代码块。定义一块静态代码块
可以完成类的初始化操作,在类声明时就会运行。
语法如下:
public class StaticTest {
static {
//此处编辑执行语句
}
}
例6.15 创建静态代码块、非静态代码块、构造方法、成员方法,查看这几处代码的调用顺序。
代码如图所示:运行结果如图所示:
从上面图中的运行结果可以看出:
(1) 静态代码块由始至终只运行了一次。
(2) 非静态代码块,每次创建对象的时候,会在构造方法之前运行。所以读取成员变量name时,只能获取到String 类型的默认值null。
(3) 构造方法只有在使用new创建对象的时候才会运行。
(4) 成员方法只有在使用对象调用的时候才会运行。
(5) 因为name是static修饰的静态成员变量,在创建s2对象时将字符串“s2”赋给了name,所以创建 s3对象时,重新调用了类的非静态代码块,此时name的值还没有被s3对象改变,于是就会输出“s2非静态代码决”。
6.4 类的主方法
主方法是类的入口点,它定义了程序从何处开始;主方法提供对程序流向的控制,Java编译器通过主方法来执行程序。主方法的语法如下:
public static void main(String[] args) {
//方法体
}
在主方法的定义中可以看到主方法具有以下特性:
(1) 主方法是静态的,所以如要直接在主方法中调用其他方法,则该方法必须也是静态的。
(2) 主方法没有返回值。
(3) 主方法的形参为数组。其中args[0]-args(分别代表程序的第一个参数到第四+1个参数。可以使用args.length获取参数的个数。
例6.16 在项目中创建TestMain类,在主方法中编写以下代码,并在Edlipse中设置程序参数。
代码如图所示:运行结果如图所示 :
在Eclipse中设置程序参数的步骤如下:
(1) 在Edlipse中,在包资源管理器的项目名称节点上单击鼠标右健,在弹出的快捷菜单中选择“Run As”→“Run Configurations”命令,弹出Run Configurations对话框。
(2) 在Run Configurations对话框中选择Arguments选项卡,在Program arguments文本框中输
入相应的参数,每个参数间按



