摘要
阅读完本章后,大家应能根据需要定义并使用一个用户类,并能够根据需求说明这个用户类的特性。另外,还应该能够使用继承的方法创建子类,实现类的最大化使用。
--------------------------------------------------------------------------------
本章目标:
阅读完本章后,大家应能根据需要定义并使用一个用户类,并能够根据需求说明这个用户类的特性。另外,还应该能够使用继承的方法创建子类,实现类的最大化使用。
8.1 定义并使用一个新类
传授新知
在前面几章中,我们遇到了不少用java语言编写的程序,它们可以分为两类:
1) java应用程序;
2) java小应用程序。
它们都有一个共同的特点,那就是整个程序中只包含一个类。不同在于,这个唯一的类的定义不尽相同。
在java应用程序中,形如:
public class testbranch1
而在java小应用程序中,则形如:
public class variablescope extends applet
其实,在这些程序中都使用到了其它类。在小应用程序中,很明显,所有的类都是从applet类中继承来的。在应用程序中,也使用了其它的类,不过不是十分明显,大家想想,system.out.println是从哪来的呢?它是从其它类中继承过来的,它包含在java.lang包中,无须指明,编译器能够自动处理。
java语言的开发工具包(jdk)中就包含了许许多多的已开发定义的类,我们可以通过使用它们迅速地构建自己的程序。如果你能够熟练地使用它们就能够使用java语言写出自己所需要的程序。而如果你想要成为java语言的高手,就一定要学会自己定义类,以供今后的程序使用。下面我们一起来看一下下面这个例子。
实例说明
1.首先,我们使用文字编辑软件输入下面这个源程序。
源程序:birthday.java
public class birthday
{
public string year;
public string month;
public string day;
public birthday()
{
year=”0000”;
month=”00”;
day=”00”;
}
public birthday(string y,string m,string d)
{
year=y;
month=m;
day=d;
}
public string getbirthday()
{
string fullbirthday=month+”/”+”/”+day+”/”+year;
return fullbirthday;
}
}
2.编译这个程序,如果顺利完成,将在当前目录下生成一个名为birthday.class的文件;
c:javastudy> javac birthday.java
3.然后输入以下命令,运行这个程序:
c:javastudy> java birthday
执行这个程序,将输出一些信息,如下图所示:
图8-1 执行birthday类的输出
java语言责怪我们没有定义main方法。
4.接下来,我们再用文字编辑软件输入以下源程序。
源程序:usebirthday.java
public class usebirthday
{
public static void main(string argv[])
{
birthday birthday1=new birthday();
birthday birthday2=new birthday("1949","10","01");
system.out.println(birthday1.getbirthday());
system.out.println(birthday2.getbirthday());
}
}
3.使用javac编译后,执行以下命令,运行这个程序:
c:javastudy> java usebirthday
执行这个程序,将输出一些信息,如下图所示:
图8-2 执行usebirthday类的输出
传授新知
我们先来看一下第二个程序usebirthday.java,这是个java应用程序,main方法中共有四条语句。前两条,看上去象是定义变量:
birthday birthday1=new birthday();
birthday birthday2=new birthday("1949","10","01");
但是,我们从来没有向大家介绍过birthday这种变量类型呀!是的,在java语言中并没有birthday这种变量类型。
可是这条语句在编译过程中并没有报错呀!这是因为,java语言编译器在usebirtday.java程序所在目录中发现了包含birthday类定义的birthday类:birthday.class。
也就是说,这两条语句定义了2个属于birthday类的对象:birthday1和birthday2。从birthday.java程序中,我们可以看到birthday类包含三个字符型成员变量:
public string year;
public string month;
public string day;
一些提示:
我们将birthday称为类,而将birthday1和 birthday2称为对象。请大家一定要明明白白地知道其中原委。类是一类事物,而对象则是一个个体。
成员变量是一个形象的术语,表示这些变量属于这个对象,属于这个类。
同时,分别使用new birthday()和new birthday(“1949”,”10”,”01”)为它们赋初值。
1)birthday birthday1=new birthday();
在这一句中,调用的是birthday类中的birthday()方法,我们从birthday.java中可以看到这个方法实现:
public birthday()
{
year=”0000”;
month=”00”;
day=”00”;
}
也就是说,它将对象birthday1的三个成员变量year,month和day分别赋予了初值“0000”,“00”和“00”。
2) birthday birthday2=new birthday("1949","10","01");
在这一条语句中,虽然方法名同是birthday,但它带上了参数,所以它调用的是birthday类中的birthday(string y,string m,string d)方法。这个方法的实现是:
public birthday(string y,string m,string d)
{
year=y;
month=m;
day=d;
}
也就是将其所带的参数值,分别赋值给三个成员变量。在本例中,“1949”赋值给year,“10”赋值给month,“01”赋值给day。
这个程序中另两条语句则十分相似:
system.out.println(birthday1.getbirthday());
system.out.println(birthday2.getbirthday());
很明显,这两条语句的用途是打印出birthday1和birthday2两个对象的一些信息。什么信息呢?我们可以发现这两条语句中都使用了birthday类中的getbirthday方法。
一些提示:
大家注意观察在上面的语句是如何调用getbirthday方法的:
birthday1.getbirthday()
前面是对象名,后面是方法名,中间用“.”连接。请记住这个“.”,它经常被使用。形象的说,它就表示“的”。即birthday1的getbirthday方法。同样,我们可以使用这样在方法来访问它的成员变量:birthday1.year,即对象birthday1的成员变量year。
我们就来看一下这个方法做了什么:
public string getbirthday()
{
string fullbirthday=month+”/”+”/”+day+”/”+year;
return fullbirthday;
}
大家还记得“+”在字符串操作中的用途吧!对,我们在第6章中就跟大家说过,它是用来进行字符串合并的。我们通过month+”/”+”/”day+”/”+year语句,把birthday1和birthday2两个对象的三个成员变量组成了一个“月/日/年”的常用日期表示方法。
然后,getbirthday方法将这个用“月/日/年”表示法表示的生日日期返回(使用return方法)给system.out.println,这样,我们就得到了如图8-2的输出:
00/00/0000
10/01/1949
通过上面的实例与讲解,我们可以得出创建一个新类的方法:
1) 构思所需类的成员变量和成员方法;
2) 用以下格式来编写类:
类修饰符 class 类名
{
成员变量定义;
……
成员方法定义;
……
}
3) 使用javac编译这个类;
4) 然后我们就可以在其它类中使用这个类。
注意:
当你编译使用自定义类(如birthday)的程序(如usebirthday.java)时,这个类(birthday.class)必须与程序(usebirthday.java)位于相同的目录中,或者在系统变量classpath定义的目录中。否则编译时将找不到这个类,以致程序无法编译成功。
一些提示:
当你需要同时编译几个java文件时,你可以使用一条命令来完成。例如:
javac birthday.java usebirthday.java
javac程序将一起编译所有这些文件。我们知道编译usebirthday.java时,需要用到birthday.class(就是由birthday.java编译生成的)。那么大家可能会以为,我们一定要将birthday.java放在前面,以确保能够先编译生成birthday.class。
其实并不需要,java编译器能够智能地处理,你完全可以将usebirthday.java放在birthday.java前面。
我们在birthday类定义中,发现有两个与类birthday同名的成员方法:
public birthday()
public birthday(string y,string m,string d)
这种与类同名的成员方法称为构造器。每当使用new操作符创建属于这个类的对象时,就会执行构造器方法。
一个类可以有多个构造器,在使用new操作符时,执行哪个构造器则取决于它所还的参数。如:
1)birthday birthday1=new birthday();
在这条语句中,由于调用birthday方法时,并未有任何参数,所以将调用birthday类的构造器birthday()。
2) birthday birthday2=new birthday("1949","10","01");
而在这条语句中,调用birthday方法所带的参数与birthday(string y,string m,string d)相吻合,所以将调用birthdya类的构造器birthday(string y,string m,string d)。因些,将完成以下赋值工作:
1)year=y; à year=“1949”
2)month=m; à month=“10”
3)day=d; à day=“01”
注意:
如果调用时的参数与所有构造器不吻合,将使java编译器在编译程序时报错,使得无法编译成功。
自测练习
1) 如果我们定义一个名为car的类,那么存储这个类的源文件名可以是:________。
a. car.class b.任意取名 c.class.java
2) 一个类中,只能拥有一个构造器方法。_________
a. 不对 b.对
3) 一个构造器方法的方法名,___________________。
a.与类同相同 b.可以根据需要取名 c.可以有多个不同名的构造器
4) 在______________________时,将调用构造器方法。
a.定义一个属于该类的对象 b.调用new操作符
c.打印属于该类的对象信息
5) 当__________________时,编译使用自定义类的程序时将出错。
a. 自定义类放在系统环境变量path(classpath未指定该目录)指定的目录中
b.与程序在同一个目录下 c.放在系统环境变量calsspath指定的目录中
6) 修改usebirthday.java程序中的语句:
birthday birthday1=new birthday();
改为使用构造器birthday(string y,string m,string d),使其结果相同。
____________________________________________________________________
7) 编写一个username类,包含两个成员变量:firstname,lastname;并仿照birthday类构建两个构造器方法,一个是带参数,一个是不带参数(将它们初始化为firstname,lastname);最后构建一个fullname方法,用来返回全名。
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
练习答案
1)c 在java语言中,要求存放类的文件必须与类同名,甚至连大小写都要完全一样。而car.class是编译后生成的,存放源文件应该是car.java。
2)a 不对,在java的类中,可以定义多个构造器方法。
3)a 在java语言中,要求构造器方法名与类名相同。
4)b 调用new操作符时,才真正创建这个对象,这时调用构造器方法来构造它。这也是构造器名字的来历。
5)a java编译器会在当前目录和classpath指定的目录中寻找相应的类,而不会在path路径中查找。
6) 这个很简单,因为不带参数的构造器方法为其赋予了值“0000”、“00”和“00”,因此,我们可以使用带参的构造器来实现相同的工作:
birthday birthday2=new birthday(“0000”,”00”,”00”);
7) 这里所要的新类很简单,它与我们前面的实例birthday类十分类似,我们只需要在前面的定义中做一些修改就可以得到了。
以下就是一个实例:
源程序:birthday.java
public class username
{
public string firstname;
public string lastname;
public username()
{
firstname=”firstname”;
lastname=”lastname”;
}
public username(string first,string last)
{
firstname=first;
lastname=last;
}
public string fullname()
{
string fullname=firstname+” ”+lastname;
return fullname;
}
}
8.2 说明类的特性
8.2.1 private特性
实例说明
1.首先,我们输入以下源程序,构建一个people类:
源程序:people.java
public class people
{
public string name;
public string sex;
private string age;
}
2.接着使用javac编译这个类:
c:javastudy> javac people.java
3.接下来,我们构建一个使用people类的程序usepeople.java
源程序:usepeople.java
public class usepeople
{
public static void main(string argv[])
{
people people1=new people();
people1.name=”kate”;
people1.sex=”female”;
people1.age=”23”;
system.out.println(“name:”+people1.name);
system.out.println(“sex:”+people1.sex);
system.out.println(“age:”+people1.age);
}
}
3. 执行以下命令,编译usepeople.java:
c:javastudy> javac usepeople.java
这时,编译器将报告错误,编译失败,如下图所示:
图8-3 编译错误输出
传授新知
在上面这个例子中,我们遇到了一个错误:
variable age in class people not accessible from class usepeople:
people1.age=”23”; 1 error
这个错误信息的中文信息就是:“在语句people1.age=”23”中有一个错误,usepeople类无权访问people类中的变量age。”
为什么会出现这个错误呢?难道是语句写错了!但是java编译器并未对相类似的另两条语句报错呀。
我们一起来看一下,在people这个类的定义:
public string name;
public string sex;
private string age;
我们发现,没有报错的两条语句所访问的变量name与sex,在people类中定义时使用了public;而报错的这条语句所访问的变量age在定义时使用的是private。对,区别就在这里。
英语单词public的意思是:公用的,而private的意思则是:个人的、私有的。噢!people类中的成员变量age与另两个变量name和sex不同,它是私有的。所以usepeople无权访问age这个成员变量。因此,引起了出错。
在java语言中,我们可以将类的某些数据、方法声明为private,这样这个数据或方法则仅能够被这个类使用,而不允许其它类使用。
这样做有什么好处呢?这样可以实现信息隐藏。我们可以将一个类的所有变量都定义为私有的,然后构造一些公用的方法来访问和处理数据。这样就可以有效地实现数据的封装,利用程序设计。
下面我们来看一个这样的例子:
实例说明
1.首先,我们输入以下源程序,构建一个新的people类:
源程序:people.java
public class people
{
private string name;
private string sex;
private string age;
public people(string n,string s,string a)
{
name=n;
sex=s;
age=a;
}
public void infoprint()
{
system.out.println("name:"+name);
system.out.println("sex:"+sex);
system.out.println("age:"+age);
}
}
2.接着使用javac编译这个类:
c:javastudy> javac people.java
3.接下来,我们构建一个使用people类的程序usepeople.java
源程序:usepeople.java
public class usepeople
{
public static void main(string argv[])
{
people people1=new people("kate","female","23");
people1.infoprint();
}
}
3. 使用javac编译后,执行以下命令运行程序:
c:javastudy> java usepeople
这时,我们如愿以偿地输出了对象people1的信息:
图8-4 usepeople程序的输出
在这个实例中,people将数据都封装起来,usepeople则使用people类提供的两个公用的(public)方法来设置成员变量的值,以及打印这些成员变量的值。
8.2.2 static特性
实例说明
1.首先,我们构建一个类mybook:
源程序:mybook.java
public class mybook
{
public string bookname;
public static string ownername=”xufen”;
public mybook(string bn)
{
bookname=bn;
}
public void infoprint()
{
system.out.println("book name:"+bookname);
system.out.println("owner:"+ownername);
}
}
2.接着使用javac编译这个类:
c:javastudy> javac mybook.java
3.接下来,我们构建一个使用mybook类的程序usemybook.java
源程序:usemybook.java
public class usemybook
{
public static void main(string argv[])
{
mybook mybook1=new mybook("c progamming");
mybook mybook2=new mybook("java programming");
mybook1.infoprint();
mybook2.infoprint();
system.out.println("--------------------------------------");
mybook1.bookname="c programming";
mybook1.ownername=”xufeng”;
mybook1.infoprint();
mybook2.infoprint();
}
}
3. 使用javac编译后,执行以下命令运行程序:
c:javastudy> java usemybook
程序将产生如下图所示的输出:
图8-5 usemybook程序的输出
传授新知
我们一起来看一下这个程序,以及它所产生的输出:
1)
mybook mybook1=new mybook("c progamming");
mybook mybook2=new mybook("java programming");
mybook1.infoprint();
mybook2.infoprint();
在这一段程序中,我们定义了两个mybook类的对象mybook1和mybook2,并且调用它的构造器方法,将其成员变量bookname的值分别设置为“c progamming”和“java programming”。
接下来,我们调用了mybook类的成员方法infoprint打印它们的信息,程序输出了:
book name: c progamming
owner: xufen
book name: java programming
ower: xufen
我们并没有为ownername赋值呀,为什么会输出呢?这是因为我们在mybook类的定义中就为其赋值为“xufen”了。所有每一个mybook类的对象都有这个成员变量了。
由于,我们发现了mybook1的书名与拥用者名字都错了,所以我们又重新修改,再打印出新的信息:
2)
mybook1.bookname="c programming";
mybook1.ownername=”xufeng”;
mybook1.infoprint();
mybook2.infoprint();
首先,我们修改了成员变量bookname的值,加上了少掉的字母“r”,接着修改了成员变量ownername的值,加上了少掉的字母“g”。
接着,我们再看一下输出:
book name: c programming
owner: xufeng
book name: java programming
owner: xufeng
这时,我们发现mybook1的bookname与ownername的值就修改过来了。但我们惊奇地发现,mybook2的ownername也从“xufen”变成了“xufeng”。这是怎么回事呢?我们并没有对它做过修改呀!
这时因为,我们定义ownername时,使用了一个特殊的特性说明符static:
public static string ownername=”xufen”;
英文单词static的意思是:静态。也就是说,我们让成员变量ownername成为静态的变量。这里静态的意思不是指它的值是不变的。而是它的存储位置是不变的。如下图所示:
图8-6 static变量示意图
从上图中,我们可以很清晰地知道,mybook类的两个对象mybook1和mybook2虽然都包含自己的成员变量ownername,但它们都是指向同一个存储位置的。所以实际上只有一个。
你现在明白为什么修改了mybook1的成员变量ownername的值,mybook2的成员变量ownername的值也跟着改变了吗?
一些提示:
同样的,static也可以用来修饰方法,即静态方法,静态方法只能访问静态数据,而不能访问非静态的变量和方法,否则将引起编译器出错。
自测练习
1) 定义私有变量、方法的关键字是__________。
a.pulic b.static c.private
2) 定义静态变量、方法的关键字是__________。
a.pulic b.static c.private
3) 以下几个方法,可以被外部类访问的是___________。
a. pulic void infoprint() b.void public infoprint()
c.private void infoprint() d.void private infoprint()
4) public方法不能够访问其它类的private数据。___________
a.对 b.不对
5) 外部类不能访问静态(static)变量。___________
a.对 b.不对
6) 外部类不能访问_________变量。
a.static b.private c.public
7) 私有变量允许________访问。
a.任何类 b.特权类 c.定义这个变量的类
8) 现有一个如下所示的类:
public class people
{
private string name;
private string sex;
private string age;
}
请构建一个方法getname,使得其它类能够访问它的成员变量name:
____________________________________________________________________
____________________________________________________________________
____________________________________________________________________
练习答案
1)c java语言中使用关键字private定义私有变量、方法。
2)b java语言中使用关键字static定义私有变量、方法。
3)a c和d使用了private关键字,肯定不能被外部类访问,所以答案就在a和b之间。而b将void放在了public之前,不符合java语言的语法规则。
4)a private定义的数据不能够被外部类访问。
5)b 静态变量是可以被外部类访问的。
6)b 仅有私有变量是不能够被外部类访问的。
7)c 私有变量只允许定义它的类访问,在java中没有什么特权类。
8)这个方法很简单:
public int getname()
{
return name;
}
8.3 创建子类
传授新知
至此,我们已经学会了如何创建自己需要的自定义类,已经学会了创造!
正如创建java的类一样,人们在认知了大自然后,也开始创建属于自己的“类”,如“电灯”、“电视”、“电脑”、“电话”、“飞机”等……。这些伟大的发明使得历史的车轮迅速地向前滚去。
然而,人类之所以得以这样迅速的发展,不仅得益于杰出科学家们的不断创新,还得益于继承,使得每一项新的发明与创造都是“站在巨人头上的进步”。人们在创造新事物的同时,还总结了一些用于创造的基础配件,如“集成电路”、“模板”,新的发明,就从组装、改进、复用它们而得到。
软件危机后,人们意识到在软件工程领域也采用这种思想,必将使软件得于更大的发展。这就是软件复用。而在面向对象方法学中,创建子类就是用以实现这种思想的有效工具。在这一小节中,我们将学习如何继承一个类,创建出更有特色的子类。
举个例子,我们在程序中定义了一个类:“交通工具”,而这时我们需要一个“客机”类,我们就可以通过继承“交通工具”类共性(公用变量和方法),然后新增一些“客机”类独有的属性和方法,就能够很轻松地构建出新的类来。如下图所示:
图8-7 继承示意图
下面,我们一起来看一个创建子类的例子。假设我们已有一个交通工具类vehicle:
源程序:vehicle.java
public class vehicle
{
public int passenger;
public float carfare;
public vehicle()
{
passenger=0;
}
public vehicle(int p)
{
passenger=p;
}
public getgrossearnings()
{
float grossearnings= passenger*carfare;
return grossearnings;
}
}
在这个类中,有两个成员变量:
1) passenger:定义为公用的,整型数,用于存放乘客总数;
2) carfare:定义为公用的,浮点数,用于存放票价。
同时,我们构造了两个构造器方法:
1) 如果没带参数,则将成员变量passenger赋值为0;
2) 如果带上了一个int型参数,则将成员变量passenger赋值为这个数。
另外,在程序中还有一个有用的方法:getgrossearnings(),它用来统计这次的总收入(当然是乘客数乘以票价)。
分析完这个类后,我们现在需要构建一个“客机”类,完成的功能与“交通工具”类相同。这时,我们就可以使用创建子类的方法来构建这个“客机”类:plane。
源程序:plane.java
public class plane extends vehicle
{
public int baggage;
public float bprice;
public plane()
{
super();
baggage=0;
}
public vehicle(int p,int b)
{
super(p);
baggage=b;
}
public getgrossearnings()
{
float grossearnings= passenger*carfare+baggage*bprice;
return grossearnings;
}
}
下面,我们来分析一下这段程序:
1)public class plane extends vehicle
第1句,是用来定义类的。它与其它类的定义不同的地方是后面加上了extends vehicle,其中extends的意思是扩展,也就是plane类是从vehicle类中扩展的,也就是说plane是vehicle的子类。
大家应该还记得,我们在编写java小应用程序的时候,定义类的时候,后面都有一个extends applet,说明所有的小应用程序是从applet类中扩展的。其中applet是java语言为小应用程序提供的一个类。
2)
public int baggage;
public float bprice;
由于在客机上,行李也是计费的,因此,我们需要加上两个变量baggage和bprice,这样,plane类就有了四个成员变量:
§ passenger:从超类vehicle中继承,用于存放乘客总数;
§ carfare:从超类vehicle中继承,用于存放票价;
§ baggage:新的成员变量,整型,用于存放计费行李总公斤数;
§ bprice:新的成员变量,浮点数,用于存放每公斤行李的运费。
3)
public plane()
{
super();
baggage=0;
}
这一段程序构建了一个构造器方法。在这个方法中使用了一个第一次遇到的语句:
super();
这个语句是用来调用超类的构造器,也就是调用了vehicle类的构造器vehicle()。
同样,我们以相同的方法定义了另一个构造器plane(int p,int b)。
4)
public getgrossearnings()
{
float grossearnings= passenger*carfare+baggage*bprice;
return grossearnings;
}
由于行李也要计费了,所以总收入的计算方法也就不一样了,因此,在plane类中重新定义了方法getgrossearnings,这叫做“方法overlay”。实际上就是指在子类中,重新定义一个新的同名方法,替换掉超类中定义的同名方法。
自测练习
1) 创建子类时,我们使用了一个新的关键字,是___________。
a. subclass b.modifyclass c.extends d.inherit
2) 子类继承时,___________不被继承。
a.静态变量 b私有变量 c.公用变量
3) 子类继承时,___________不被继承。
a.私有方法 b公用方法 c.静态方法
4) 如果你想要在子类中引用超类的构造器方法,应该使用___________。
a.不可以引用 b.super() c.直接使用超类的构造器方法名
5) 子类中的“方法overlay”是指:_______________。
a. 在子类中,将超类里定义的某个方法去掉,使其不生效
b.在子类中,重新定义超类里定义过的某个方法,使其更适合于子类。
练习答案
1)c 在java语言中,用extends来表示继承一个类。
2)b 用private定义的私有变量将不会被继承到子类中去。
3)a 用private定义的私有方法将不会被继承到子类中去。
4)b 在java语言中定义了一个super()的方法,为子类提供了使用超类 的构造器方法的手段。
5)b 方法overlay就是对其重新定义,使其更适合子类。
闽公网安备 35060202000074号