第1章
Java 面向对象编程基础
Java 语言是纯粹的面向对象的编程语言。因此,面向对象设计开发程序的思想,是 Java 的核心内容之一。在这一章中,主要介绍 Java 面向对象编程的基础知识,包括:类、对象、接口等内容。
1.1 什么是面向对象编程
所谓面向对象编程( Object Oriented Programming ).是指一种计算机系统程序设计思想。面向对象的一条重要原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成的。面向对象程序设计思想实现了软件工程的3个主要目标:重用性、扩展性、灵活性。计算机信息系统内部由一个个既有属性又有行为的对象组成,这些逻辑对象相互联系,为了实现系统整体目标,每个对象都能够接收来自其他对象的信息、分析处理收到的信息并向其他对象发送处理好的信息。
面向对象技术具有封装性、继承性、多态性3大特点。这3大特点,为软件开发提供了一种新的方法学。下面进行详细介绍。
1.1.1 封装性
所谓封装性( Encapsulation ),是指将相关的信息、操作与处理包含在一个对象中。封装就是隐藏信息,这是面向对象技术的核心。也是面向对象程序设计的基础。在现实世界中,封装性的例子很多、例如人们无需了解汽车的内部构造就可以驾驶汽车。下面汽车类就描述了封装性在面向对象技术中的主要应用。
public class Car
private Engine engine ; //引擎
private Oi1Box ob ; //油箱
private WaterBox wb ; //水箱
private Gearll gears ; //齿轮
private Wheel [] wheels ; /车轮
public boolean run ()
{
//开车
}
pirvate boolean engineWorking ()
{
//引擎工作
}
private void gearWorking ()
{
//齿轮工作
}
public void turnRight ()
{
//右拐
}
public void turnLeft ()
{
∥左拐
}
在上面的例子中,声明了汽车类( Car ),在类中包含了一些属性和行为,详细说明如下。(1)类中封装了一系列的属性,如引擎、油箱、水箱等。
(2)类中封装了一系列的行为,如开车、发动机工作、齿轮工作、左拐、右拐等。(3)其他类不能直接操作汽车类的属性。
(4)其他类不能直接操作汽车类的引擎工作、齿轮工作行为。(5)其他类可以操作开汽车、转向等行为。
通过这样的声明和定义,对象的使用者只能看到对象封装界面的信息,如开汽车、转向等,而汽车内部的处理过程是隐藏的,无法直接加以控制和干预。由于数据和代码封装在对象中,不易被破坏。封装性保证了系统模块化,降低了模块间耦合程度,从而提高了程序的可维护性,简化了软件开发过程,并提高了计算机程序的可靠性、安全性和独立性。
1.1.2 继承性
所谓继承性( Inheritance ),是指子类自动共享父类数据结构和方法的机制,这是类之间的一种特殊的关系。在定义和实现一个类时,可以在一个已经存在的类的基础上进行,把这个已经存在的类所定义的内容作为自己的内容,并加人若干新的内容。继承性是面向对象技术的最重要的特点。
在类层次中,子类只继承一个父类的数据结构和方法称为“单继承”,而子类继承了多个父类的数据结构和方法称为“多维承”。 Java 语言中只允许单继承,并且所有类都有且仅有唯一的一个最顶层类, Object 类。
在计算机程序开发过程中,类的继承性使所建立的程序具有高度的开放性和可扩充性,进一步简化了对象与类的创建工作量,增加了代码的可重用性。通过采用继承性,进一步规范了类的组织结构。
在客观世界中,常常把具有相同特征的事物归为一类事物,例如客车、卡车、轿车都是汽车。从对象的观点来看,具有共同属性、共同操作,并遵守相同规则的对象的集合就是类。类具有3大要素,属性、事件和方法,而单个对象则是类的具体实例。
public class Bus extends car
{
private Seat [] seats ; / /客车座位
private Route r : / /客车路线
private double price : //票价
public boolean checkTicket ()
{
//查票
}
}
public class Truck extends car
{
private Carriage c ; //车厢
private Goods g://货物
public void load :
{
//装货
}
public void unload ()
{
//卸货
}
}
本例清楚的定义了客车类( Bus )和货车类( Truck ),继承了父类汽车类的所有属性和行为,又加人了各自特殊的属性与行为,详细说明如下。
(1)客车类( Bus )加人了客车座位、客车路线、车票票价等属性。(2)客车类( Bus )加人了查票这一特殊行为。
(3)卡车类( Truck )加人了车厢、货物等特殊属性。(4)卡车类( Truck )加入了装货、卸货等特殊行为。
继承性自动在父类与子类间共享属性和行为,当父类做了某项修改后,子类会自动继承其父类改变后的所有特性与行为模式。采用继承性有利于进一步提高计算机程序的开发效率,更加容易达到一致性目的。
1.1.3多态性
所谓多态性( Polymorphism ),是指相同的操作或方法作用于多种类型的对象上并获得不同的结果。即不同的对象,收到同一消息可以产生不同的结果。多态性允许每个对象以适合自身的方式去响应共同的消息,采用多态性可以增强计算机程序的灵活性和可重用性。
例如,汽车类新增了一个新的方法addOil0,通过该方法实现汽车加油的逻辑。
protected double addOil ( double d )
{
//加油
}
针对客车类( Bus ),继承了汽车类的所有方法,包含这个加油方法,但是客车类的具体逻辑为加汽油,如下所示。
protected double add011( double d )
{
if (汽油) //如果是汽油
{
if ( d <—油箱体积—目前油量)1/如果想加 油量在范围内
{
加油量为 d
return d ;
}
else
{
double realoil=油箱体积—目前油量; // 计算实际加油量
加油量为realoil。
return real011;
}
}
return 0.0;//返同实际加油量
}
上面的例子定义了客车类加油的方法,详细说明如下。
(1)加油时判断是否为汽油,如果是就继续加油,否则就不加油。
(2)如果想加的油量小于油箱空余空间,加人想加的油量。
(3)如果想加的油量大于油箱空余空间,把油箱加满为止。
针对货车类( Truck ),继承了汽车类的所有方法,包含这个加油方法,但是卡车类的具体逻辑定义为加满柴油,如下所示。
protected double addOil ( double d )
{
if (柴油) //如果是柴油
{
double rea10il=油箱体积—目前油量; //计算实际加油量
加油量为real0il
return real0il;
}
return 0.0;//返回实际加油量
}
上面的例子定义了卡车类( Truck )加油的方法,详细说明如下。(1)加油时判断是否为柴油,如果是继续加油,否则就不加油。(2)货车加油量跟想加油量无关,直接计算实际需要的加油量。(3)加油时直接加满油箱为止。
虽然汽车类( Car )、客车类( Bus )和货车类( Truck )都有addOil0方法,但是每个类执行的逻辑是截然不同的。多态性允许每个对象以适合自己的方式去响应共同的消息,可以实现计算机程序的简洁性和一致性,符合真实世界的习惯与要求。
1.2 Java 的类
类是组成 Java 程序的基本要素,是同种事物或活动的集合与抽象。换句话说,任何一个事物、任何一项活动,都可以抽象为一个类。这个类中有对应的数据来捕述该事物或活动的属性,并将其称之为类的成员变量,同时也包括对该事物或活动的一系列操作,并将其称之为类的成员方法。
1.2.1类的基本结构
类主要包含3个部分的内容:类属性、类方法和构造方法。(1)类属性:用来描述类本身所抽象出的事物的属性。(2)类方法:用来描述这个被抽象出的事物可以做什么。
(3)构造方法:每一个类都至少会有一个特殊的方法,并将其称为构造方法。该方法提供了创建类对象的初始化机制。
如下代码片段所示,即为一个类的基本结构。在这里读者可以不理解其细节,只需要建立一个大概的类的框架,以便于后面的学习。
class Demo
{
int a , b , c ; //成员变量
public Demo //构造方法,可以用于初始化成员变量
{
a=10
b=20
}
public int add () //类方法,于实现加法运算,并将结果返回
{
e = a + b ;
return c ;
}
}
从以上代码片段可以看出,将加法运算抽象为一个类,使用构造方法可以对类中的属性(变量)进行初始化,而类中的方法 add (则是完成了加法运算。
1.2.2类的定义
类是现实中某些事物或是某些活动的抽象体。因此它应该包括:被抽象事物或活动的属性以及方法。换句话说,属性是用来描述抽象事物或活动的,而方法则是用来获取或改变属性值的。具体来说,定义类的过程实际上就是定义类的属性和方法的过程。定义类的一般语法格式如下所示。
[修饰符] class 类名[ extends 父类名][ implements 接口名]
类成员变量的声明;类方法声明:
需要注意的是, clas 是关键字用来定义类。习惯上,类名的第一个字符都会使用大写字母,而方法名的第一个字符都会使用小写字母。
1.2.3 Java 源文件结构
Java 程序的源文件是一个以“ java ”结尾的文件,同时该文件中只能有一个类被声明为 public 类,若存在被声明为 public 的类时,类的名字必须与“ java ”源文件名相同。源文件通过编译,可以产生字节码文件,也就是类文件,该文件是以“ class ”作为文件名扩展名。
在一个源文件中,可以包含3种顶层元素,分别为 package (包声明)、 import (类库导人语句)和 class (类声明)。这3种顶层元素如果同时出现,必须按 package 、 import 、 class 的顺序出现,如以下代码片段所示:
package 包名;
import 所要导人的类库;
class Student
{
...
}
1.2.4 main ()入口方法
main ()是 Java 程序中的一个特殊的方法,是一个 Java application 应用程序的人口。为了能够运行一个应用程序,就必须在类中定义一个main0方法。包含有 main (方法的类通常称之为主类(即:可以被运行的类)。 main (方法完整定义的语法格式如以下代码所示。
public static void main ( String argv [])
{
...... //方法体
}
在这里,main0方法前的修饰符 public 不是必须的,之所以声明为 public 是为了main0)方法可以从任意的一个 Java 运行环境中调用。但是 static 修饰符是必须的,只有这样才可以使 main ()方法成为程序的人口点,并通过它直接执行应用程序。
main (方法中的参数定义了一个字符串数组 argy ,是用来从命令行接收用户参数的。采用命令行执行 Java 程序的语句由4个部分组成:第1部分为命令名;第2部分是命令参数,是可选的;第3部分为应用程序的名称,即源文件中的主类类名;第4部分为用户输入的参数,多个参数之间用空格分隔。若用户输人参数,则从类名后开始,第1个参数存储于字符串数组元素 argv [0]中,第2个参数存于 argv []中,依此类推。语法格式如下所示。
java 【命令参数]类名【用户参数( argv [01)][用户参数( argv [11)]……
其中“0”中的内容均为可选项。代码41中,利用命令行执行 Java 程序,根据用户输入的不同参数,来输出不同的字符串,并显示在屏幕上。
1.3 类的成员变量与方法
类的成员变量和方法是组成类的基本元素。成员变量是用来描述类所抽象出的属性,而方法则是用来获取或改变这些属性的值。在这一节中,将主要介绍声明和使用类中的成员变量。
1.3.1访问修饰符: public , private , protect 和 default
Java 程序中,访问修饰符可以出现在类、成员变量、构造方法或方法前,是用来控制访问权限的修饰符。 Java 语言中提供了4种形式的访问修饰符。
(1) public : Java 语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包( package )的访问。
(2) private : Java 语言中对访问权限限制最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不允许跨包访问。
(3) protect :介于 public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法及子类方法访问,即使子类在不同的包中也可以访问。
(4) default :不加任何的访问修饰符,通常称为“默认访问模式”。该模式下,只允许在同一个包中进行访问。
1.3.2构造方法
构造方法是类中一个特殊的方法,与其他方法不同,构造方法主要是用来初始化类,创建类的实例(也就是类的对象,之后会详细介绍)。构造方法的名字和类的名字是相同的,并且没有返回值类型。在构造方法前,只可以使用访问修饰符 public 、 private 及 protected 。
在 Java 程序中创建一个类,若要让这个类工作,就必须创建这个类的对象。也就是说,类是一个抽象的集合体,而类的对象是满足类条件的一个实际存在的实例。举一个简单的例子:学生类是抽象的集合,而学生“王某”则是具备学生条件的一个实例,也称为学生类的一个对象。而在 Java 程序中创建一个类的对象,就需要调用构造方法来实现,其过程实际上就是程序内部,为所创建的对象分配内存空间。
构造方法的一般写法代码片段如下所示。
{class Employee
private String name ; /姓名
private double salary : //薪水
private String hireDay ; //入职日期
public Employee () //构造方法
{
name =“ Wang Xiao Yue ”; //初始化姓名
salary 4000.00; //初始化薪水
hireDay ="2005/05/20”; //初始化人职日期
}
使用构造方法创建类的对象,代码片段如下所示,在类的主方法main0)中创建类的对象,该过程为所创建的对象分配内存空间,并进行初始化。
class Employee
{
private String name ; / /姓名
private double salary : //薪水
private String hireDay://入职日期
public Employee () //构造方法
{
name =" Wang Xiao Yue ";
salary =4000.00;
hireDay ="2005/05/20";
}
......;
public statie void main ( String argv [])
Employee enp = new Employee (): / /创建类对象
}
}
在这段代码中创建了一个 Employee 的对象 emp .从而 emp 对象就代表了一个具体员工的实例,该员工姓名为“ Wang Xiao Yue ”,薪水为4000.00元,人职日期为:“2005/05/20”。
1.3.3定义变量
Java 中存在两种形式的变量,即成员变量和局部变量。
(1)成员变量:在方法体外定义的变量,即类所拥有的变量,可以由系统自动初始化。
(2)局部变量:在方法体内定义的变量,即方法所拥有的变量,不能自动的初始化,必须由程序员为其指定初始值。
成员变量和局部变量由于所属对象的生命周期不同,因此其生存周期也不相同。成员变量在类的实例被创建时产生,其生命周期和该类的实例对象的生命周期相同;而局部变量,在定义该变量的方法被调用时被创建,而在该方法调用结束后,该变量也就随之消失了。成员变量和局部变量的声明代码片段如下所示。
class VarDemo
{
private int a ; //声明成员变量 a
protect int b =10; //声明并初始化成员变量
publie void method ()
{
int c =0; //声明并初始化局部变量 c
...
}
......;
}
1.3.4定义成员方法
成员方法可以用来获取或修改对象的属性,同时也可以用来接收其他对象的信息,以及向其他的对象发送消息。在通常情况下成员方法中都包含了一组相关的语句,用来执行相应的操作。其使用方式和其他编程语言中的函数调用类似。方法声明的基本格式如下所示。
返回值类型方法名(参数1,参数2,…,参数 n )
{
......; //方法体
}
代码1—2中,创建了 Employee 雇员类的一个对象 emp ,用来表示一个特定的员工,同时在
Employee 雇员类中,定义了三个成员方法,可以用来输出 emp 对象的属性值。
1.3.5 参数的传递
同其他的编程语言一样,既然存在方法,就会有参数的传递。方法参数就是方法圆括号内的常量值、变量、表达式或函数。当定义方法时,这时方法中的参数称为形式参数(形参),使用来反映参数的类型及个数的;而当调用该方法时,这时的参数称之为实际参数(实参)。通过参数的传递,可以将变量的值传递给方法内部进行处理,但需要注意的是,在 Java 中,传递的参数共有两种。
(1)基本类型参数。基本类型参数所传递的参数都是基本数据类型,包括数值型、布尔型等。在这种情况下,所有的参数传递都采用值传递的方式。也就是说,当传递一个参数到方法中时,方法获得的只是该参数值的一个复制。因此,方法不会改变参数变量的值,只会使用该变量的值。
(2)对象引用型参数。如果参数传递的是对象引用型参数(即一个对象),则方中获得的是该对象的内存地址,因此方法可以改变该对象中的属性,但是不能改变对象本身。
代码43中,使用基本类型参数的传递,同时会发现,方法调用后并不会改变该变量的值,而只是使用了该变量的值。
【代码1—3】基本类型参数的传递:
public class ParamEx {
public static void add ( int a , int b )//自定义方法,实现加法运算
int c = a + b ; //定义变量 c ,其值等于参数 a + b 的值
System , out . println (" c ="+ c ); //输出变量 c 的值
a =C1 ;/ /将变量 c 的值赋给参数 a
}
public static void main ( Stringtl args )
{
//定义并初始化变量 a , b int a =10:
int b =20;
//调用add0方法 add ( a , b ):
System , out . println (" a ="+ a );
}
}
程序运行结果如图1—3所示。
由此可以看出,传递的参数为基本变量类型时,方法是不能改变参数变量的值的。
代码44中,使用对象引用型参数,使用字符串对象和字符数组对象作为传递的参数。使用时会发现,方法调用后并不会改变字符串对象,但是可以改变字符数组对象变量的内容。
【代码1-4】对象引用型参数的传递:ParamEx2java public class ParamEx2{
//自定义方法用来改变参数
public static void change ( String str , char ch [])
{
str =" Changed ";
//将字符 C 赋值给字符数组的第一个元素
}
public static void main ( String [] args )
{
//创建并初始化字符串对象,及字符数组对象 String sne М String (" World ");
char ch []={'н',' e ','1','1',''};
//调用change0)方法
change ( s , ch ):
System . out . println (" s -"+ s );
System . out . print (" ch =");
for ( in i -0; i < ch . length ;1++)
System . out . print ( ch [ i ]):
}
}
1.3.6方法的返回值
在定义每一个方法时,都会定义该方法的返回值类型。这个类型是方法本身的一个结果类型。如果返回类型为“ void ”,则说明该方法不返回值,只是执行了一些操作。如果有返回类型,该类型可以是任何基本类型或对象类型,这时也就可以把这个方法看作一个复杂的变量。但需要注意,如果方法指定的返回类型不是“ void ”,那么方法中必须包含一条 return 语句,返回指定类型的值,否则是一种语法错误。
1.3.7成员方法重载与过载
在 Java 语言中,提供了两种方式来实现对同一个方法名的复用,分别为方法重载和方法过载。(1)方法重载:存在于同一个类中的方法。方法重载指的是多个方法拥有同一个名字,每个方法都有一套唯一的、不同于其他同名方法的参数列表。简单地说就是方法名相同,参数列表不同。
(2)方法过载:即方法覆盖,是面向对象语言的特性,只是存在于父类和子类之间。当父类中的一个方法在继承它的子类中重新获得定义时,若该方法的方法名、参数、返回值类型均不变,只有方法体发生了变化时,就称子类的方法过载了父类方法。
方法重载与方法过载之间的主要差别体现在以下几点:
(1)重载的方法间是彼此独立的,可以看作是完全不同的方法,而过载(覆盖)的方法之间是一种取 的关系;
1.3.6方法的返回值
在定义每一个方法时,都会定义该方法的返回值类型。这个类型是方法本身的一个结果类型。如果返回类型为“ void ”,则说明该方法不返回值,只是执行了一些操作。如果有返回类型,该类型可以是任何基本类型或对象类型,这时也就可以把这个方法看作一个复杂的变量。但需要注意,如果方法指定的返回类型不是“ void ”,那么方法中必须包含一条 return 语句,返回指定类型的值,否则是一种语法错误。
1.3.7成员方法重载与过载
在 Java 语言中,提供了两种方式来实现对同一个方法名的复用,分别为方法重载和方法过载。(1)方法重载:存在于同一个类中的方法。方法重载指的是多个方法拥有同一个名字,每个方法都有一套唯一的、不同于其他同名方法的参数列表。简单地说就是方法名相同,参数列表不同。
(2)方法过载:即方法覆盖,是面向对象语言的特性,只是存在于父类和子类之间。当父类中的一个方法在继承它的子类中重新获得定义时,若该方法的方法名、参数、返回值类型均不变,只有方法体发生了变化时,就称子类的方法过载了父类方法。
方法重载与方法过载之间的主要差别体现在以下几点:
(1)重载的方法间是彼此独立的,可以看作是完全不同的方法,而过载(覆盖)的方法之间是一种取代的关系;
(2)重载的方法存在于同一个类中,数量没有限制,而过载(覆盖)的方法是存在于子类与父类之间,子类最多包含一个过载(覆盖)方法;
(3)重载的方法必须含有不同的参数列表,而过载(覆盖)的方法必须具有相同的参数列表;
(4)重载的方法返回值类型是任意的,而过载(覆盖)的方法必须具有相同的返回值类型。代码4—5中,实现了两个重载的方法,方法名相同、但参数列表不同,属于两个不同的方法。
【代码1—5】方法重载实例:
public class OverLoading {
//声明并初始化变量
int a10;
1nt b 20;
int c =30;
public OverLoading()//构造方法
{
//调用 add ( int x , int y )方法
System . out . println (" a + b ="+ add ( a , b ));
//调用 add ( int x , int y , int z )方法
System . out . println (" a + b + c ="+ add ( a , b , c ));
}
//自定义方法。实现2个数相加,并将结果返回 public int add ( int x , int y )
{
return X + y]
//自定义方法,实现3个数相加,并将结果返回 public int add ( int x , int y , int z )( return {
x + y +2;
public static void main ( String ] args )
{
new OverLoading():
}
}