第3章 Java面向对象程序设计基础副本.docx

上传人:b****6 文档编号:4718927 上传时间:2022-12-07 格式:DOCX 页数:35 大小:129.03KB
下载 相关 举报
第3章 Java面向对象程序设计基础副本.docx_第1页
第1页 / 共35页
第3章 Java面向对象程序设计基础副本.docx_第2页
第2页 / 共35页
第3章 Java面向对象程序设计基础副本.docx_第3页
第3页 / 共35页
第3章 Java面向对象程序设计基础副本.docx_第4页
第4页 / 共35页
第3章 Java面向对象程序设计基础副本.docx_第5页
第5页 / 共35页
点击查看更多>>
下载资源
资源描述

第3章 Java面向对象程序设计基础副本.docx

《第3章 Java面向对象程序设计基础副本.docx》由会员分享,可在线阅读,更多相关《第3章 Java面向对象程序设计基础副本.docx(35页珍藏版)》请在冰豆网上搜索。

第3章 Java面向对象程序设计基础副本.docx

第3章Java面向对象程序设计基础副本

第3章

JAVA面向对象程序设计基础

[本章内容简介]本章是面向对象基本概念在Java中的实现,运用Java实现前面章节中面向对象的基本概念;讨论了类的定义、属性与方法;详细的讲述了类与对象的关系;访问控制符的使用;关键字static;以及在类中方法的各方面的知识和概念。

3.1类的定义

在面向对象程序设计中,先要做的工作就是定义类,然后根据这个类来建立类的个体,即对象。

3.1.1类的定义格式

在JAVA定义类的通用格式为:

访问控制符class类名 {

//定义属性

 访问控制符数据类型1变量1,变量2;

访问控制符数据类型2变量3;

...

 访问控制符数据类型N变量N;

//定义方法

访问控制符返回类型方法1(参数列表)

{

//方法实现细节

}

访问控制符返回类型方法2(参数列表){ 

  //方法实现

 }

...

}

其中,访问控制符号为:

public,private或者protected分别代表:

公共的,私有的和保护的,数据类型可以是Java已经定义的基本类型和集合类型,也可以是自定义的类,方法的返回值除了基本数据类型、集合类型以及自定义的类外,还可以是无返回数据(Void),其中构造函数没有返回数据,而且不需要访问控制符号。

类头使用关键字class标志类定义的开始,类体用一对大括号括起,包括属性和方法两大部分,其中属性对应类的静态部分、方法对应类的行为和操作,一个类中可以定义多个属性和方法。

下面的程序片段定义了一个汽车类:

classCar{

//定义汽车的属性

StringengineNo;

inttopSpeed;

intcurrentSpeed=0;

//定义汽车的函数方法

voidspeedUp(intfaster){

if((currentSpeed+faster)>=topSpeed)

{

currentSpeed=topSpeed;

}

else{

currentSpeed+=faster;

}

System.out.println("当前的汽车速度为:

"+currentSpeed);

}

}

以上代码定义了汽车类(Car),包括三个属性和一个方法。

其中engineNo是字符串类型变量,代表发动机号码;topSpeed和currentSpeed是整型变量,代表汽车的最高速度和当前速度。

SpeedUp()方法实现汽车的加速操作,加速的速度为当前速度与增加的速度之和,增加的速度为该方法的参数,该方法没有返回值。

一个类定义一个新的数据类型。

在本例中,新的数据类型名为Car。

可以使用这个名字来声明Car类型的对象。

记住类声明只是创建一个模板(或类型描述),它并不会创建一个实际的对象。

因此,上述代码不会生成任何Car类型的对象实体。

要真正创建一个Car对象,你必须使用下面的语句:

Carmycar=newCar();//创建一个类型为Car,名字为mycar的对象

这个语句执行后,mycar就是Car的一个实例了。

因此,它将具有“物理的”真实性。

现在,先不必考虑这个语句的一些细节问题。

每次创建类的一个实例时,是在创建一个对象,该对象包含它自己的由类定义的每个实例变量的拷贝。

因此,每个Car对象都将包含它自己的实例变量拷贝,这些变量即engineType,bodyType,topSpeed和currentSpeed。

要访问这些变量,要使用点号“.”运算符。

点号运算符将对象名和成员名连接起来。

例如,要将mycar的topSpeed变量赋值为200,使用下面的语句:

mycar.topSpeed=200;

该语句告诉编译器对mycar对象内包含的topSpeed变量拷贝的值赋为200。

通常情况下,可以使用点号运算符来访问一个对象内的实例变量和方法。

下面是使用Car类的完整程序:

classCar{

StringengineNo;

inttopSpeed;

intcurrentSpeed=0;

voidspeedUp(intfaster){

if((currentSpeed+faster)>=topSpeed)

{

currentSpeed=topSpeed;

}

else{

currentSpeed+=faster;

}

System.out.println(engineNo+"汽车速度为:

"+currentSpeed);

}

}

//该类中声明了Car类的对象

publicclassCarDemo{

publicstaticvoidmain(Stringargs[])

{

Carmycar=newCar();

mycar.engineNo="20040314";

mycar.topSpeed=200;

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

}

}

将该名字命名为CarDemo.java,除了main()方法在名为CarDemo的类中外,最主要的原因是因为该类的访问控制符为public。

当编译这个程序时,生成了两个“.class”文件,一个属于Car,另一个属于CarDemo。

Java编译器自动将每个类保存在它自己的class文件中。

没有必要分别将Car类和CarDemo类放在同一个源文件中。

可以分别将它们放在各自的文件中,并分别命名为Car.Java和CarDemo.java。

要运行这个程序,执行CarDemo.class。

运行该程序后,有如下输出:

在上面的例子中,类Car和类CarDemo可以放在两个Java文件中,也可以放在一个Java文件中,但是在一个Java文件当中不能有多于一个类的访问控制符定义为public,否则系统编译出错。

每个对象都含有它自己的、由它的类定义的实例变量的拷贝。

因此,假设有两个Car对象,每个对象都有其自己的engineNo,topSpeed等拷贝。

改变一个对象的实例变量对另外一个对象的实例变量没有任何影响,下面的程序定义了两个Car对象:

classCar{

StringengineNo;

inttopSpeed;

intcurrentSpeed=0;

voidspeedUp(intfaster){

if((currentSpeed+faster)>=topSpeed){

currentSpeed=topSpeed;

}

else{

currentSpeed+=faster;

}

System.out.println(engineNo+"汽车速度为:

"+currentSpeed);

}

}

//类CarDemo2中声明了两个Car类的对象

publicclassCarDemo2{

publicstaticvoidmain(Stringargs[]){

Carmycar=newCar();

Caryoucar=newCar();

mycar.engineNo="20040314";

mycar.topSpeed=200;

youcar.engineNo="19751017";

youcar.topSpeed=100;

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

youcar.speedUp(15);

youcar.speedUp(15);

}

}

在程序中,声明了Car类的两个对象,分别是mycar和youcar,这两个对象的属性的值都不同;该程序产生的输出如下所示:

可以看到,mycar的数据与youcar的数据完全分离。

3.1.2对象的定义与使用

当创建一个类时,创建了一种新的数据类型。

你可以使用这种类型来声明该种类型的对象。

然而,要获得一个类的对象需要两步。

第一步,你必须声明该类类型的一个变量,这个变量没有定义一个对象。

实际上,它只是一个能够引用对象的简单变量。

第二步,该声明要创建一个对象的实际的物理拷贝,并把对于该对象的引用赋给该变量。

这是通过使用new运算符实现的。

new运算符为对象动态分配(即在运行时分配)内存空间,并返回对它的一个引用。

这个引用或多或少的是new分配给对象的内存地址。

然后这个引用被存储在该变量中。

这样,在Java中,所有的类对象都必须动态分配。

让我们详细看一下该过程。

在前面的例子中,用下面的语句来声明一个Car类型的对象:

Carmycar=newCar();

本例将上面讲到的两步组合到了一起,可以将该语句改写为下面的形式,以便将每一步讲的更清楚:

Carmycar;//申明指向汽车类的对象

mycar=newCar();//分配内存空间给mycar

第一行声明了mycar,把它作为对于Car类型的对象的引用。

当本句执行后,mycar包含的值为null,表示它没有引用对象。

这时任何引用mycar的尝试都将导致一个编译错误。

第二行创建了一个实际的对象,并把对于它的引用赋给mycar。

现在,你可以把mycar作为Car的对象来使用。

但实际上,mycar仅仅保存实际的Car对象的内存地址。

这两行语句的效果如图6-1所示。

图3-1声明Car类型的对象

刚才已经解释过,new运算符动态地为一个对象分配地址。

它的通用格式如下:

class-var=newclassname( );

其中,class-var是所创建类类型的变量。

classname是被实例化的类的名字。

类的后面跟的圆括号指定了类的构造函数。

构造函数定义当创建一个类的对象时将发生什么。

构造函数是所有类的重要组成部分,并有许多重要的属性。

大多数类在他们自己的内部显式地定义构造函数。

如果一个类没有显式的定义它自己的构造函数,那么Java将自动地提供一个默认的构造函数。

对类Car的定义就是这种情况。

现在,我们将使用默认的构造函数。

不久,你将看到如何定义自己的构造函数。

这时,你可能想知道为什么对整数或字符这样的简单变量不使用new运算符。

答案是Java的简单类型不是作为对象实现的。

出于效率的考虑,它们是作为“常规”变量实现的。

你将看到,对象有许多特性和属性,使Java对对象的处理不同于简单类型。

由于对处理对象和处理简单类型的开销不同,Java能更高效地实现简单类型。

对象变量的赋值实际上是将其中一个实例赋给另一个实例,那么这两个实例变量都引用同一个对象。

以下例子说明了对象的引用。

publicclassPeople{

privateintage;

publicintgetage(){

returnthis.age;

}

publicvoidsetage(intage){

this.age=age;

}

publicstaticvoidmain(String[]args){

Peopleyou=newPeople();

Peopleme=newPeople();

you.setage(15);

me.setage(20);

System.out.println("Beginning:

");

System.out.println("you="+you.getage());

System.out.println("me="+me.getage());

you=me;//赋值

System.out.println("\nAfterassigningtwotoone:

");

System.out.println("you="+you.getage());

System.out.println("me="+me.getage());

//更改对象的值

you.setage(30);

System.out.println("\nAftermodifyingtwo:

");

System.out.println("you="+you.getage());

System.out.println("me="+me.getage());

}

}

在上面的例子中,you和me将引用同样的对象。

将you赋值给me并没有分配任何内存或对原对象做任何部分的拷贝。

由于它们是同一个对象,因此通过变量you对对象的改变也将影响me所对应的对象。

程序的运行结果为:

3.1.3构造函数

每次在创建实例变量,对类中的所有变量都要初始化是很乏味的。

如果在一个对象最初被创建时就把对它的设置做好,那样的话,程序将更简单并且更简明。

因为对初始化的要求是共同的,Java允许对象在他们被创造时初始化自己。

这种自动的初始化是通过使用构造函数来完成。

JAVA在构造类的时候,会执行以下几个操作:

1.为对象分配内存。

2.创建并初始化类变量

3.调用合适的构造函数来进行附加的初始化。

4.返回引用,该引用指向第一步中分配内存位置。

构造函数在对象创建时初始化。

它是类的一种特殊方法。

它与它的类同名,它的语法与方法类似。

一旦定义了构造函数,在对象创建后,在new运算符完成前,构造函数立即自动调用。

构造函数没有任何返回值,即使是void型的值也不返回。

这是因为一个类的构造函数内隐藏的类型是它自己类的类型。

构造函数的任务就是初始化一个对象的内部状态,以便使创建的实例变量能够完全初始化,可以被对象马上使用。

在前面的例子中,我们用到以下语句:

Carmycar=newCar();

Peopleyou=newPeople();

其中:

new后面的Car()和People()为类的构造函数。

JAVA创建对象的一般格式为:

类名新建对象名=new构造函数(参数列表);

newCar( )调用Car( )构造函数。

如果你不显式为类定义一个构造函数,Java将为该类创建一个默认的构造函数,并为属性赋初值。

这就是在Car早期版本没有定义构造函数工作的原因。

默认构造函数自动地将所有的实例变量初始化为零。

默认构造函数对简单的类是足够的,但是对更复杂的类它就不能满足要求了。

一旦你定义了你自己的构造函数,默认构造函数将不再被使用。

例如,可以为Car类定义构造函数,初始化它的属性,以下是带构造函数的Car类:

classCar{

StringengineNo;

inttopSpeed;

intcurrentSpeed;

Car(Strings,inttspeed,intcurspeed){

engineNo=s;

topSpeed=tspeed;

currentSpeed=curspeed;

}

voidspeedUp(intfaster){

if((currentSpeed+faster)>=topSpeed){

currentSpeed=topSpeed;

}

else{

currentSpeed+=faster;

}

System.out.println(engineNo+"汽车速度为:

"+currentSpeed);

}

}

//该类中声明了Car类的对象

publicclassCarDemo{

publicstaticvoidmain(Stringargs[]){

//Carmycar=newCar();

Carmycar=newCar("2000100",150,50);

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

mycar.speedUp(10);

}

}

在上面的程序中,如果用以下语句初始化Car类,系统将会出现编译错误。

Carmycar=newCar();

因为用户自定义了Car类的构造函数,系统原来默认的构造函数将无效,所以这时创建并初始化Car对象的语句为:

Carmycar=newCar("2000100",150,50);

对象编程的最重要的好处之一是对数据和操作该数据的代码的封装。

在Java中,就是通过类这样的机制来完成封装性。

在创建一个类时,你正在创建一种新的数据类型,不但要定义数据的属性,也要定义操作数据的代码。

进一步,因此,你可以通过类的方法来使用类,而没有必要担心它的实现细节或在类的内部数据实际上是如何被管理的。

事实上,既然细节被隐蔽,当需要时,它的内部工作可以被改变。

只要你的代码通过类的方法来使用它,内部的细节可以改变而不会对类的外部带来负面影响。

为了看看前面讨论概念的一个实际的应用,让我们开发一个封装的典型例子:

堆栈。

堆栈用先进后出的顺序存储数据。

堆栈通过两个传统的操作来控制:

压栈和出栈。

下面是一个叫做Stack的类,实现整数的堆栈。

classStack{

intst[]=newint[5];

intpos;

Stack(){

pos=-1;

}

//实现堆栈的push操作

voidpush(intitem){

if(pos==4)

System.out.println("堆栈满了!

");

else

st[++pos]=item;

}

//实现堆栈的pop操作

intpop(){

if(pos<0){

System.out.println("堆栈已经为空!

");

return0;

}

else

returnst[tos--];

}

}

正如你看到的,Stack类定义了两个数据项、三个方法。

整数堆栈由数组st存储。

该数组的下标由变量pos控制,该变量总是包含堆栈顶层的下标。

Stack()构造函数将pos初始化为-1,它指向一个空堆栈。

方法push()将一个项目压入堆栈。

为了重新取回压入堆栈的项目,调用pop()。

既然存取数据通过push()和pop(),数组中存储堆栈的事实实际上和使用的堆栈不相关。

例如,堆栈可以被存储在一个更复杂的数据结构中,例如一个链表,但push()和pop()定义的接口仍然是一样的。

下面示例的类TestStack,验证了Stack类。

该类产生两个整数堆栈,将一些值存入,然后将它们取出。

classTestStack{

publicstaticvoidmain(Stringargs[]){

Stackmystack1=newStack();

Stackmystack2=newStack();

//向堆栈中push一些数字

for(inti=0;i<5;i++)mystack1.push(i);

for(inti=10;i<16i++)mystack2.push(i);

//从堆栈中pop数据

System.out.println("mystack1堆栈中的数据为:

");

for(inti=0;i<5;i++)

System.out.println(mystack1.pop());

System.out.println("mystack2堆栈中的数据为:

");

for(inti=0;i<5;i++)

System.out.println(mystack2.pop());

}

}

该程序产生的输出如下:

3.1.4访问控制符号的使用

Java允许使用访问控制符来控制对方法和属性的访问。

可将方法或属性定义为public(公共)类型,这样外部类可以访问该方法或属性,也可以定义为private(私有)类型,以保证只有该类内部的方法才能访问它们。

要理解public和private对访问的作用,以下的例子说明二者的区别:

classTest{

inta;//默认的访问类型

publicintb;//公共类型

privateintc;//私有类型

voidsetc(inti){//访问控制c的方法

c=i;

}

intgetc(){

returnc;

}

}

classAccessTest{

publicstaticvoidmain(Stringargs[]){

Testob=newTest();

//以下的语句正确

ob.a=10;

ob.b=20;

//下面的语句系统将报错

//ob.c=100;

//必须通过以下语句访问私有成员

ob.setc(100);

System.out.println("a,b,andc:

"+ob.a+""+ob.b+""+ob.getc());

}

}

可以看出,在Test类中,a使用默认访问指示符,在本例中与public相同。

b被显式地指定为public。

成员c被指定为private,因此它不能被它的类之外的代码访问。

所以,在AccessTest类中不能直接使用c。

对它的访问只能通过它的public方法:

setc()和getc()。

如果你将下面语句开头的注释符号去掉:

//ob.c=100;

则由于违规,你不能编译这个程序。

上节的Stack类中,通过Stack类外面的代码可以改变保存堆栈的数组st。

这样的Stack是开放的,容易被破坏,通过访问控制符的运用,可以有效的保证数据的封装性,的下面的程序表明了改进的Stack类。

classStack{

privateintst[]=newint[5];

privateintpos;

Stack(){

pos=-1;

}

voidpush(intitem){

if(pos==4)

System.out.println("堆栈已经满!

");

else

st[++pos]=item;

}

intpop(){

if(pos<0){

System.out.println("堆栈已经为空!

");

return0;

}

else

returnst[pos--];

}

}

在本例中,现在存储堆栈的st和指向堆栈顶部的下标pos,都被指定为private。

这意味着除了通过push()或pop(),它们不能够被访问或改变。

例如,将pos指定为private,阻止你程序的其他部分无意中将它的值设置为超过st数组下标界的值。

下面的程序表明了改进的Stack类。

试着删去注释前面的线条来证明st和pos成员确实是不能访问的。

classTestStack{

publicstaticvoidmain(Stringargs[]){

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 外语学习 > 法语学习

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1