在面向对象程序设计(Object-Oriented Programming,OOP)中,使用“类”对现实世界的实体进行抽象和概括。类是组成Java程序的基本要素,通常封装同一类对象共同的属性和行为,例如人类具有姓名、国家、年龄等属性,也具有说话、吃饭、睡觉等行为。类是具有共同特性的一群实体的抽象,设计和定义类的过程就是将实体共有的属性和行为封装进类的过程,类中的属性被称为类的成员变量,行为被称为类的成员方法。
对象是现实世界中存在的一个实体,是由类创建的一个具体对象。如果说类是模板,对象则是由模板创建的一个实例,对象创建的过程也被称为实例化过程。由类可以创建出很多对象,例如由人类可以创建一个中国人、一个美国人、一个法国人,这些人类个体都是一个独立的对象,他们具有姓名、国家、年龄的属性,也具有说话、吃饭、睡觉的行为,这些对象均具有人类共同的属性和行为。所以类和对象的关系是总体和个体的关系,抽象和具体的关系。图2.1给出了类和对象的对比。
图2.1 类和对象的对比
(1)类的定义
类定义的一般格式为:
class关键字表示创建了一个类,extends关键字表示该类继承了某一父类,implements关键字表示实现了某接口。类的名字要符合Java标识符规定,即名字可以由字母、下划线、数字和$符号组成,并且第一个字符不能是数字。给类命名时,通常要遵守下列编程习惯:①类名通常以字母开头,首字母必须使用大写字母,如Hello、Student、Book等。②类名要容易识别,一般可以由几个单词组合而成并且每个单词的首字母大写,如HelloChina、BeijingTime、StudentInfo等。
类体是类声明之后的一对大括号“{”、“}”以及它们之间的内容,主要描述该类共有的属性和行为,其中类的属性被成为成员变量,类的行为被成为成员方法。下面是一个类名为“Circle”的类,该类用来描述圆的属性(成员变量)和对圆的操作方法(成员方法),在类体的变量声明部分给出了double类型的成员变量radius、area分别表示圆的半径属性和面积属性,在方法定义部分给出了成员方法方法calcArea()、setRadius(),分别用来计算圆面积和设置半径。
(2)成员变量和成员方法
类体主要是对该类的成员变量进行声明以及成员方法进行定义,成员变量声明的一般格式为:
[修饰符]类型符成员变量名[=初始值];
成员变量名的类型可以是Java中任意的数据类型,既可以是int、float、char等简单类型,也可以是类、接口、数组等复杂数据类型。成员变量的初始值可以不赋值,如果不赋值成员变量将初始化为该类型的默认值。int、byte、short类型初始值为0,float、double类型初始值为0.0,char类型初始值为空字符串,boolean类型初始值为false,string、数组及其他对象类型初始值为null。
成员方法定义的一般格式为:
成员方法的类型符是该方法返回值的数据类型,如果该方法没有返回值则类型符为void,如果有返回值则类型符为该返回值类型。参数列表是一组变量的声明,多个参数由逗号隔开,参数可以是任意的Java数据类型。
关于成员变量和成员方法的使用说明:
①成员变量和成员方法的命名符合Java标识符规定,首字母使用小写字母,为了便于识别可以由几个单词组合而成,并且除第一个单词首字母小写外其后单词首字母大写,如变量radius、方法setRadius、方法countArea等。
②成员变量和成员方法的修饰符有public、protected 、缺省和private四个级别,四个级别的使用权限不同。考虑到封装型的要求,通常成员变量声明为private则禁止外部直接访问,成员方法一般声明为public则提供给外部访问。四个修饰符的访问权限如表2.1。
表2.1 成员修饰符访问权限
③类中除了有成员变量以外,还可以有局部变量。局部变量是指成员方法的参数或成员方法内部声明的变量。成员变量的作用范围是整个类,局部变量的作用范围只在声明它的方法内有效。当局部变量的名字和成员变量的名字相同时,成员变量会被隐藏,如果要使用被隐藏的成员变量,必须使用关键字this,如:
(3)对象的创建和使用
对象创建的过程包括了对象的声明、实例化和初始化3部分,对象创建的一般格式为:
类名对象名=new类名(参数列表);
①使用无参构造方法创建对象,如:Circle c1=new Circle();
其中Circle c1是对象的声明,c1变量作为该对象的引用在后面的程序中使用,new Circle()是对象的实例化,使用new运算符为对象分配内存空间并返回一个引用,调用构造方法对对象进行初始化,在这里将调用无参构造方法进行初始化。
②使用带参构造方法创建对象,如:Circle c2=new Circle(2.0,2.0,1.0);
该条语句声明了圆类对象c2,使用new运算符实例化圆类对象,并调用带参的构造方法进行该对象的初始化,并将对象的引用返回给c2。
对象使用的一般格式为:
①调用成员变量的格式:对象名。变量名
如:c1.radius=2.0;
②调用成员方法的格式:对象名。方法名(参数列表);
如:c1.setRadius(2.0);
(1)构造方法的定义和使用
构造方法是与类名同名,且不具有返回值的特殊函数,它的主要作用是进行对象的初始化工作,当对象被创建的时候会调用构造方法,构造方法定义的一般格式:
[修饰符]构造方法名(参数列表){
……∥构造方法体
}
关于构造方法定义和使用的说明:
①构造方法必须和类同名。
②构造方法没有返回值,它的工作是进行初始化。
③使用new运算符创建对象时构造方法即被调用,构造方法可以无参数也可以带参数,调用的时候会根据参数的情况来调用相应的构造方法。
④若类中没有定义构造方法,则Java虚拟机会提供一个默认的构造方法,该方法不带参数,方法体内无任何语句,什么工作也不做。
(2)构造方法的重载
在一个类中,可以有多个构造方法,也可以没有构造方法。如果类中定义了多个方法,这些方法的名字相同,只是参数的类型和个数不同,这种情况被称为方法的重载。在定义构造方法时,通常会显式定义多个构造方法,这些构造方法的名字与类名相同,只是参数不同,这即是构造方法的重载。下面是关于构造方法重载的例子:
在上面的例子中,Circle类中定义了2个构造方法,分别是无参构造方法Circle()和带参构造方法Circle(double x,double y,double radius),这两个方法名字相同参数不同,构成了构造方法的重载。
计算机世界与自然界一样,类之间也有继承和派生关系。被继承的类叫父类或基类,继承的类叫子类或派生类。通过继承,子类可以获得父类的属性和行为(也被称为属性和方法)。继承与派生能达到代码重用、简化编程的目的。类继承的语法格式为:
类的单一继承关系形成了清晰的层次结构,例如人类可以派生出学生类、教师类、工人类,学生类又可以派生出小学生类、中学生类、大学生类,教师类又可以派生出教授类、讲师类、助教类等。父类具有的属性和行为通过继承将会传递给子类,例如人类具有姓名、年龄等属性,具有吃饭睡觉等行为,学生类、教师类、工人类继承自人类,这些子类也将拥有姓名、年龄、吃饭、睡觉这些属性和行为。除了拥有人类共同的特征和行为,学生类还可以有学号、课程、学习、社团活动等子类独有的特征和行为。人类继承关系的树状结构如下图2.2所示。
图2.2 人类继承关系的树状图
在下面的例子中使用继承创建父类People和子类Student,子类继承父类的属性和方法并定义自己的属性和方法。
在上面的例子中Student类继承自People类,Student类被称为子类或派生类,People类被称为父类或基类。子类除了继承了父类的属性和方法之外,还可以定义自己的属性和方法,所以Student除了具有属性name、age,行为eat()、sleep()以外,还具有no属性、course属性、learn()行为。
关于类的继承的说明:
①Java中只支持单继承,也就是一个子类只能有一个父类,一个父类可以拥有多个子类。
②继承具有传递性,子类沿继承路径向上继承所有父类的属性和方法。
③Java中的最顶层父类为Object类,如果一个类声明中没有指明这个类的直接父类,则默认该类继承自Object类。
④子类的构造方法与类名同名,在子类被创建的时候调用。在创建子类对象时,会首先调用父类的构造方法再调用子类的构造方法,如果没有显示调用父类的构造方法,则会调用父类的默认无参构造方法。
⑤可以在子类中使用super关键字显式调用父类的一个构造方法,但需要写在子类构造方法的第一行。
使用super关键字调用父类构造方法进行初始化的例子如下:
上例中子类Student继承自父类People,Student继承了People类的name、age属性,并衍生了no属性。在创建子类对象s1时,会首先调用父类的无参构造方法再调用子类的无参构造方法,由于在无参构造里没有对s1进行任何初始化工作,所以输出s1的name、age、no属性时都将是null值。再创建子类对象s2,s2的初始化过程为先调用父类的构造方法对name和age进行初始化,再调用子类的构造方法对no进行初始化。在这个例子中父类的构造方法被显式调用了,即在Student子类构造方法通过super(name,age)这条语句显式调用了父类People的构造方法,但需要注意的是这条语句必须放在Student子类构造方法的第一句以保证父类构造方法首先被调用。