第一章 简介
JAVA是有SUN公司开发的新一代编程语言它可以用在各种不同的机器操作系统的网络环境中进行开发不论你使用哪种浏览器或者使用哪种操作系统WindowsUnix等等只要浏览器支持JAVA你就可以看到生动的主页JAVA正在逐步成为Internet应用的主要开发语言它彻底改变了应用软件的开发模式为迅速发展的信息世界增添了新的活力所以作为Internet应用的开发技术人员不可不看JAVA而JAVA程序不可不先从基础学起这正是JAVA程序员必读基础篇的用意 本JAVA教程将讨论JAVA编程语言的基础知识其中内容涉及以下几个方面 1.面向对象编程概念 将教会你面向对象编程的核心概念比如对象消息类和继承等等本节教程最后将给出实例将这些概念编程代码实现如果你对面向对象编程很熟悉了你可以跳过本节日教程直接进入第二节教程 2.语言基础 本节教程讲述了传统的语言特性其中包括变量数据类型运算符以及控制流程等等 3.对象和简单数据对象 本节教程将介绍常规的创建和使用任何类型对象的原则然后本教程还描述了怎样使用数组字符串以及数字对象它们都通常被用于对象类型最后还将介绍怎样为输出格式化数据 4.类和继承 描述了怎样编写从已经创建了的对象来编写类 5.接口和包 界面和包是JAVA编程语言的特性它帮助你组织和构造类以及跟其它类的关系 6.编程中的一些共同的问题 解释了在学习JAVA语言过程中可以出现的问题的解决方法 |
第二章 面向对象编程概念
如果你以前从来没有使用面向对象语言你需要在开始编写JAVA代码之前先理解这个概念你需要理解什么是对象什么是类对象和类的关系怎样以及使用消息怎样在对象之间进行通讯本教程的前面部分将描述面向对象编程的概念而后面的教程将教你怎样将这个概念编成代码
2.1什么是对象
对象是一些相关的变量和方法的软件集软件对象经常用于模仿现实世界中我们身边的一些对象对象是理解面向对象技术的关键你在学习之前可以看看现实生活中的对象比如狗桌子电视自行车等等你可以发现现实世界中的对象有两个共同特征它们都有状态和行为比如狗有自己的状态比如名字颜色生育以及饥饿等等和行为比如摇尾巴等等同样自行车也有自己的状态比如当前档位两个*等等和行为比如刹车加速减速以及改变档位等等
而软件对象实际上是现实世界对象的造型因为它同样有状态和行为一个软件对象利用一个或者多个变量来维持它的状态变量是由用户标识符来命名的数据项软件对象用它的方法来执行它的行为方法是跟对象有关联的函数子程序
你可以利用软件对象来代表现实世界中的对象你可能想用一个动画程序来代表现实世界中的狗或者用可以控制电子自行车的程序来代表现实世界的自行车同样你可以使用软件对象来造型抽象的概念比如事件是一个用在GUI窗口系统的公共对象它可以代表用户按下鼠标按钮或者键盘上的按键的反应
如图1是一个软件对象的公共可视代表
图1
软件对象的状态和行为都可以用在对象中的变量和方法来表达构造现实世界的自行车的软件对象要有指示自行车的当前状态的变量速度为20mph它的当前档位为第三档这些变量就是我们熟知的实例变量因为它们包含了用于特殊自行车对象的状态并且在面向对象技术中特殊的对象称为实例
如图2所示是作为软件对象的自行车造型
图2
2.1什么是对象
除了变量软件自行车同样有用于刹车改变踏板步调以及改变档位的方法这些方法就是熟知的实例方法因为它们检查或者改变特殊自行车实例的状态
以上的对象图显示了对象的变量组成了圆心部分方法处在变量的四周并且在程序中从其它对象隐藏了对象的核心用保护方法的方法来包装对象的变量称为封装这个对象图就是对象理想的表示法也是面向对象系统设计者努力的最后目标然而这并不是全部的内容
通常出于某种现实的理由对象可能暴露它的一些变量或者隐藏一些方法在JAVA编程语言中一个对象可以为变量和方法指定四种访问等级中的一种这个访问等级决定哪个对象和类可以访问变量或者方法在JAVA中访问变量和方法可以转换为控制访问类的成员函数封装相关的变量和方法到一个简洁的软件集是一个简单而且强有力的方法它为软件开发者提供了两个主要好处
模块性对象的源代码可以独立于其它的对象源代码来进行编写和维护同样对象可以很容易地在系统中传递你可以将你的自行车对象给其它的对象它仍然可以正常工作
信息隐藏一个对象如果有一个公共的界面那么其它的对象就可以与之进行通讯这个对象可以维护私人的信息和方法它可以在任何时候被改变而不影响依耐于它的其它对象所以你不必理解自行车中齿轮的机理就可以使用它
2.2什么是消息
软件对象之间进行交互作用和通讯是利用消息的
单一的一个对象通常不是很有用的相反一个对象通常是一个包含了许多其它对象的更大的程序或者应用程序通过这些对象的交互作用程序员可以获得高阶的功能以及更为复杂的行为你的自行车如果不使用它的时候它就是一堆铝合金和橡胶它没有任何的活动而只有当有其它的对象来和它交互的时候才是有用的
软件对象与其它对象进行交互与通讯是利用发送给其它对象来实现的当对象A想对象B来执行一个B中的方法对象A就会消息给对象B如图3所示
图3
有时候接收的对象需要更多的信息就至于它可以正确知道该如何做比如当你想改变自行车的齿轮你就必须指出哪个齿轮这个信息是将信息作为参数来传递的如图4所示的现实了一个信息由三个组件组成
1. 被寻址消息的对象YourBicycle
2. 要执行方法的名字(changeGears)
3. 这个方法需要的所有参数lowerGear
图4
上面的三个组件对于接收方的对象执行相应的方法是给出了充分的信息再也不需要其它的信息或者上下文了
消息提供了两个重要的好处
1. 对象的行为是通过它的方法来表达的因此消息传递支持所有在对象之间的可能的交互
2. 对象不需要在相同的进程或者相同的机器上来发送和接收消息给其它的对象
2.3什么是类
类实际上是对某种类型的对象定义变量和方法的原型
在现实世界中你经常看到相同类型的许多对象比如你的自行车只是现实世界中许多自行车的其中一辆使用面向对象技术我们可以说你的自行车是自行车对象类的一个实例通常自行车有一些状态当前档位两个*等等以及行为改变档位刹车等等但是每辆自行车的状态都是独立的并且跟其它自行车不同
当厂家制造自行车的时候厂商利用了自行车共有的特性来根据相同的蓝图制造许多自行车如果制造一辆自行车就要产生一个新蓝图那效率就太低了
在面向对象软件中同样地可以让相同种类地许多对象来共有一些特性比如矩形雇员记录视频夹等等就象自行车制造商人你可以利用相同种类的对象是相似的事实并且你可以为这些对象创建一个蓝图对对象的软件蓝图叫做类
自行车的类需要定义一些实例变量来包括当前档位当前速度等等这个类将为实例方法定义和提供实施方法它允许骑车者改变档位刹车以及改变脚踏板的节奏如图5所示
图5
当你创建了自行车类以后你可以从这个类创建任意个自行车对象当你创建了一个类的实例后系统将为这个对象和的实例变量分配内存每个实例将给所有实例变量的副本定义在类中如图6所示
图6
除了实例变量类还要定义类的变量类变量包含了被类所有实例共享的信息比如假设所有的自行车有相同的档位数在本例子中要定义一个实例变量来容纳档位数每一个实例都会有变量的副本但是在每一个实例中数值都是相同的在这样的情况下你可以定义一个类变量来包含档位数这样所有的类的实例都共享这个变量如果一个对象改变了变量它就为改变那个类的所有对象类同样可以定义类方法你可以直接从类中调用类方法然而你必须在特定的实例中调用实例方法如图7所示
图7
2.4实例和类成员
2.4.1理解实例和类成员
下面详细讨论一下实例和类成员具体涉及变量和方法以及类变量和方法
你这样声明一个成员变量比如在类Myclass中有一个float型的aFloat:
class MyClass {
float aFloat;
}
这样你就声明一个实例变量每次你创建一个类的实例的时候系统就为实例创建了类的每一个实例变量的副本你可以从对象中访问对象的实例变量
实例变量跟类变量是不一样的类变量示使用静态修改量来声明的不管类创建了多少个实例系统为每个类变量分配了类变量系统为类变量分配的内存是在它第一次调用类的时候发生的所有的实例共享了类的类变量的相同副本你可以通过实例或者通过类本身来访问类变量
它们的方法是类似的你的类可以有实例方法和类方法实例方法是对当前对象的实例变量进行操作的而且访问类变量另外一个方法类方法不能访问定义在类中的实例变量除非它们创建一个新的对象并通过对象来访问它们同样类方法可以在类中被调用你不必需要一个实例来调用一个类方法
缺省地除非其它的成员被指定一个定义在类中成员就是一个实例成员这个在下面定义的类有一个实例变量有一个整型的x两个实例方法x和setX它们设置其它对象以及查询x的数值
class AnIntegerNamedX {
int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
每次你从一个类实例化一个新的对象你可以得到每个类的实例变量的副本这些副本都是跟新对象有关系的因此每次你从这个类实例化一个新的AnIntegerNamedX对象的时候你得以得到跟新的AnIntegerNamedX对象有关的新副本
一个类的所有实例共享一个实例方法的相同的实行所有的AnIntegerNamedX实例都共享x和setX的相同执行这里注意两个方法x和setX是指对象的实例变量x但是你可能会问如果所有AnIntergerNamedX共享x和setX的相同执行会不会造成模棱两可的状态答案当然是不是在实例方法中实例变量的名字是指当前对象的实例变量假如实例变量不是由一个方法参数来隐藏的这样在x和setX中x就等价于这个x而不会造成混乱
2.4实例和类成员
2.4.1理解实例和类成员
对于AnIntegerNamedX外部的对象如果想访问x它必须通过特定的AnIntegerNamedX的实例来实现假如这个代码片段处在其它对象的方法中它创建了两种不同类型的AnIntegerNamedX它设置了x为不同的数值然后显示它们
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
这里注意代码使用setX来为myX设置x的值而直接给anotherX.x指定一个数值不管用什么方法代码是在操作两个不同的x副本一个包含在myX对象中一另外一个包含在anotherX对象中其输出是用以下代码片段来实现的
myX.x = 1
anotherX.x = 2
上面代码显示了类AnIntegerNamedX的每一个实例有自己实例变量x的副本以及每个x有自己的数值
你可以在声明成员变量的时候指定变量是一个类变量而不是一个实例变量相似地你可以指定方法是一个类方法而不是一个实例方法系统在第一次调用类来定义变量的时候创建了一个类变量的副本所有的类实例共享了类变量的相同副本类方法可以只操作类变量它们不能访问定义在类中的实例变量
2.4实例和类成员
2.4.1理解实例和类成员
为了指定一个成员变量为一个类变量你可以使用static关键字比如我们可以修改一下上面的AnIntegerNamedX类使得x变量现在是一个类变量
class AnIntegerNamedX {
static int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
现在设置它们的x数值并显示不同的输出
myX.x = 2
anotherX.x = 2
这次的输出不同是因为x现在是一个类变量所以就只有这个变量的副本它是被AnIntegerNamedX的所有实例所共享的包括myX和anotherX当你在其它实例中调用setX的时候你可以为所有的AnIntegerNamedX的所有实例改变x的数值
同样当我们声明一个方法的时候你可以指定方法为类方法而不是实例方法类方法只可以在类变量中进行操作并且不能访问定义在类中的所有实例变量
为了指定方法为类方法你可以在方法声明处使用static关键字下面我们再次来修改AnIntegerNamedX类使它的成员变量x为一个实例变量以及它的两个方法为类方法
class AnIntegerNamedX {
int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
当你想编译这个版本的AnIntegerNamedX编译器就会显示如下的错误 AnIntegerNamedX.java:4: Can't make a static reference to
nonstatic variable x in class AnIntegerNamedX.
return x;
^
出现这些错误的原因是类方法不能访问实例变量除非方法先创建AnIntegerNamedX的一个实例并且通过它来访问变量
2.4实例和类成员
2.4.1理解实例和类成员
下面我们修改一下AnIntegerNamedX让x变量成为类变量
class AnIntegerNamedX {
static int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
现在为x设置数值并打印出x数值
myX.x = 2
anotherX.x = 2
再一次我们通过myX来改变x并将它改变为AnIntegerNamedX的其它实例
实例成员和类成员之间的另外一个差别是类成员可以从类本身进行访问你不必实例化类来访问它的类成员下面让我们编写一段代码来直接从AnIntegerNamedX类中访问x和setX
. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x());
. . .
值得一提的是你现在已经不用再创建myX和anotherX了你可以设置x并直接AnIntegerNamedX类中检索x你不能利用实例成员来处理它你只能从一个对象来调用实例方法并且只可以从对象中访问实例变量而你可以从类的实例或者从类本身来访问类变量和方法
2.4实例和类成员
2.4.2初始化实例和类成员
下面讲讲初始化实例和类成员
你可以在类中定义它们的时候使用static初始化程序和实例初始化程序来为类和实例成员提供初始化数值
class BedAndBreakfast {
static final int MAX_CAPACITY = 10;
boolean full = false;
}
这个对于原始数据类型是没有问题的有时候它可以用在创建数组和对象但是这个初始化表单有它的限制如下
1. 初始化程序只可以执行用赋值语句表达的初始化
2. 初始化程序不能调用任何的包含异常错误的方法
3. 如果初始化程序调用一个包含异常错误的方法它不能进行错误恢复
如果你有一些初始化要完成可能有些不能在初始化程序实现因为出现了上面的限制之一这时你不得不将初始化代码随意放置了为了初始化类成员在static初始化块中放置初始化代码为了初始化实例成员就要在构造函数中放置初始化代码了
2.4实例和类成员
2.4.3 Static初始化块
下面再讲讲Static初始化块
下面举一个例子如图8所示
图8
errorStrings源代码必须在static初始化块中被初始化这是因为错误恢复必须在源代码没有被找到得时候才被执行同时errorStrings是一个类成员它不能在构造函数中被初始化在前面得例子中一一个static初始化块是以static关键字开头得并且JAVA代码是用大括号{}括起来的
一 个类可以有许多static初始化块它可以出现在类中任何地方系统保证static输出化块以及static初始化程序是按它们在源代码中的顺序被调用的
2.4实例和类成员
2.4.4 初始化实例成员
如果你想初始化一个实例变量而且不能在变量声明处来处理它那么就只能在构造函数中来为这个类初始化了假如errorStrings是一个实例变量而不是一个类变量你就可以使用以下的代码来初始化它
import java.util.ResourceBundle;
class Errors {
ResourceBundle errorStrings;
Errors() {
try {
errorStrings = ResourceBundle.
getBundle("ErrorStrings");
} catch (java.util.MissingResourceException e) {
// error recovery code here
}
}
}
现在代码是在构造函数中为类来初始化这个errorStrings的
有时类包含了许多构造函数并且每个构造函数允许调用者为新对象的不同实例变量提供初始化数值比如java.awt.Rectangle有以下的三个构造函数
Rectangle();
Rectangle(int width, int height);
Rectangle(int x, int y, int width, int height);
Rectangle()构造函数没有任何的参数所以它不能让用户大小或者原点和大小提供初始化数值而其它的两个构造函数它可以让用户设置初始数值
2.4实例和类成员
2.4.4 初始化实例成员
然而所有的实例变量原点和大小都必须初始化在这个例子中类经常有一个构造函数来完成所有的初始化其它的构造函数调用这个构造函数并且提供给它参数或者缺省数值比如下面是以上所说的三个构造函数它们初始化如下
Rectangle() {
this(0,0,0,0);
}
Rectangle(int width, int height) {
this(0,0,width,height);
}
Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
JAVA语言支持实例初始化块你可以放心使用它这里建议使用构造函数来初始化主要有以下三个原因
1. 所有的初始化代码处在一个地方这样使得代码更容易维护和阅读
2. 缺省值可以清除地知道
3. 构造函数广泛被JAVA程序设计人员所熟悉包括相对新的JAVA程序设计人员而实例初始化程序不能而且他可能导致其它JAVA程序设计员的困惑
2.4实例和类成员
2.4.5 对象和类
你可能会注意到对象和类看起来很相似在现实世界中类和对象之间的区别经常是让程序员困惑的源泉在现实世界中很明显类不能是它们描述的对象本身然而在软件中很困难来区分类和对象有部分原因是软件对象只是现实世界中的电子模型或者是抽象概念但是也因为对象通常有时是指类和实例
2.5什么是继承
一个类可以从它的父类继承状态和行为继承为组织和构造软件程序提供了一个强大的和自然的机理
总得说来对象是以类得形式来定义得你可能现在已经可以从它类知道许多对象了即使你如知道如果我告诉你它是一辆自行车你就会知道它有两个*和脚踏板等等面向对象系统就更深入一些了它允许类在其它类中定义比如山地自行车赛车以及串座双人自行车都是各种各样的自行车在面向对象技术中山地自行车赛车以及串座双人自行车都是自行车类的子类同样地自行车类是山地自行车赛车以及串座双人自行车的父类这个父子关系可以如图9所示
图9
每一个子例从父类中继承了状态山地自行车赛车以及串座双人自行车共享了这些状态速度等同样每一个子类继承类从父类的方法山地自行车赛车以及串座双人自行车共享了这些行为刹车改变脚踏速度等等
2.5什么是继承
然而子类不能受到父类提供的状态和行为的限制子类可以增加变量和方法到从父类继承而来的变量和方法比如串座双人自行车有两个座位这是它的父类没有的
子类同样可以重载继承的方法并且为这些方法提供特殊执行方法比如如果你有一
个山地自行车有额外的齿轮设置你就可以重载改变齿轮方法来使骑车者可以使用这些新的齿轮
你也不能受限于继承的一个层次继承树或者类的分级结构可以是很深方法和变量是逐级继承的总的来说在分级结构的越下方就有越多的行为
如果对象类处于分级结构的顶端那么每个类都是它的后代直接地或者是间接地一种类型的对象保留任何对象的一个引用比如类或者数组的一个实例对象提供了行为这些行为是运行在JAVA虚拟机所需要的比如所有类继承了对象的toString方法它返回了代表对象的字符串
下面说说我们为什么要使用继承它到底有哪些好处呢好处是有的
1. 子类提供了特殊的行为这是在父类中所没有的通过使用继承程序员可以多次重新使用在父类中的代码
2. 程序员可以执行父类称为抽象类来定义总的行为这个抽象的父类可以定义并且部分执行行为但是绝大多数的父类是未定义和未执行的其它的部分由程序员来实现特殊的子类
2.6什么是接口
接口是一个收集方法和常数表单的契约当类执行一个接口它就许诺声明在那个接口中执行所有的方法
接口是一个设备或者一个系统它是用于交互的无关的实体根据这个定义远程控制是一个在你和电视的接口而英语是两个人之间的接口强制在军事中的行为协议是不同等价人之间的接口在JAVA语言中接口是一个设备它是用来与其它对象交互的设备一个接口可能对一个协议是类似的实际上其它面向对象语言有接口的功能但它们调用它们的接口协议
自行车类和它的类分级结构定义了什么是自行车但是自行车在其它方面与现实世界交互作用例如仓库中的自行车可以由一个存货程序来管理一个存货程序不关心管理项目的哪一类只要项目提供某一信息比如价格和跟踪数字取代强迫类与其它无关项的关系存货程序建立了通讯的协议这个协议是由包含在接口中的常数和方法定义组成的这个存货清单接口将要定义但不执行方法来设置和得到零售价格指定跟踪数字等等
为了在存货清单程序中操作自行车类必须在执行接口的时候遵守这个协议当一个了执行一个接口的时候类遵守定义在接口中的所有方法因此自行车将为这些设置和获得零售价格并指定跟踪数值等等的方法提供执行
你可以使用接口来定义一个行为的协议这个行为可以有在类分级结构中任何类来执行接口的主要好处有一下几点
1. 不用人工强迫类关系在无关类中截获相似处
2. 声明想执行的一个或者更多类的方法
3. 在不暴露对象的类的前提下暴露对象的编程接口
2.7怎样将这些面向对象的概念转换为代码
这一小节将给你展现创建对象执行类发送消息创建一个父类以及执行一个接口的代码
以下是一个applet(applet是用JAVA编程语言编写的程序它可以运行在兼容JAVA平台的网络浏览器比如HotJava或者Netscape Navigator)的程序名为ClickMe如图10所示当你点击方框内任何地方一个红点就会出现
图10
提示上面的applet需要JDK1.1如果你使用老的不支持JDK1.1的浏览器你将不能运行这个applet相反你需要在一个1.1浏览器上来看这个网页比如在HotJavaJDK Applect浏览器(appletviewer)或者某个版本的Netscape Navigator和Internet Explorer
下面具体解释一下这个Applet
ClickMe Applet是一个相对简单的程序因此它的代码就短了多了但是如果你还没有太多的编程经验你可以发现这些代码也不是那么容易的我们不要求你马上理解程序中的每个问题并且这节教程也不是讲了十分详细的这里的目的示暴露一些源代码给你并且跟你刚才所学道的概念和技术联系上你将在以后的教程中学到更详细的内容
2.7怎样将这些面向对象的概念转换为代码
2.7.1ClickMe的源代码和Applet标签
为了编译这个applet你需要两个源文件ClickMe.java和Spot.java为了运行这个applet你需要利用这个applet标签来创建一个html文件
<applet code="ClickMe.class"width="300" height="150"></applet>其中ClickMe.java的源代码为import java.applet.Applet;import java.awt.*;import java.awt.event.*;public class ClickMe extends Applet implements MouseListener {private Spot spot = null;private static final int RADIUS = 7;public void init() {addMouseListener(this);} ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________ ________________________________
然后装载网页到浏览器或者appletviewer工具并且确保所有必要的文件都在相同的目录中 如图11所示
图11
2.7.2 ClickMe Applet中的对象
在这个applet中有许多对象两个最明显的是applet本身和红点
浏览器在包含applet的HTML代码中碰到applet标签的时候就创建了applet对象这个applet标签从创建applet对象的地方提供类的名字在本例子中这个类的名字为ClickMe
ClickME.applet将创建一个对象来在屏幕上画出点每次你在applet中点击鼠标的时候applet就将通过改变对象的x和y坐标来移动红点这个点不是自己画出来的它是applet画出的它是根据包含在点对象中的信息画出的
除了前面两个明显的对象另外还有一些看不见的对象呢有代表三种颜色黑白红的三个对象以及代表点击鼠标的用户动作的事件对象等等
2.7.3ClickMe Applet中的类
因为代表在屏幕上点的对象是很简单接下来让我们看看这个名为spot的类吧它声明了三个实例变量包括点半径的size包含点当前水平位置的x坐标以及包含点当前垂直位置的y坐标
public class Spot {
//实例变量
public int size;
public int x, y;
//构造函数
public Spot(int intSize) {
size = intSize;
x = -1;
y = -1;
}
}
另外类有一个构造函数它用来初始化由类创建的新对象构造函数跟类有相同的名字这个构造函数初始化所有三个对象的变量Size的初始化数值是在调用的时候座位参数提供的x和y变量都被设置为-1这里-1的目的是为了让点在开始的时候处于屏幕的外面即产生假不可视的效果
这个applet是在applet初始化的时候创建了一个新的点对象下面是applet类的相关代码
private Spot spot = null;
private static final int RADIUS = 7;
...
spot = new Spot(RADIUS);
第一行声明了一个名为spot的变量它是Spot数据类型并且初始化这个变量为NULL第二行声明了一个整型变量名为RADIUS它的值为7最后一行是创建一个对象New关键字为对象分配了内存空间Spot(RADIUS)调用了上面已经描述了的构造函数并且传递RADIUS数值这样点对象的size就被设置为7如图12所示的左图代表了Spot类而右边的是代表了spot对象
图12
2.7.4ClickMe Applet中的消息
就如所知道的对象A可以使用消息来请求对象B做一些事情一个消息有三个组成部分
1. 消息被寻址的对象
2. 要执行执行方法的名字
3. 方法需要的任何参数
在ClickMe applet种有以下两行这样的代码
g.setColor(Color.white);
g.fillRect(0, 0, getSize().width - 1, getSize().height - 1);
这两个消息都是从applet到名为g的对象其中g是一个图形对象它知道怎样在屏幕上简单画一些形状或者文本这个对象在浏览器指示applet来画的时候提供了applet上面代码的第一行设置颜色为白色第二行是填充一个指定大小的矩形区域它的颜色为白色如图13所示是一个消息的三个组成部分
图13
2.7.5 ClickMe Applet中的继承
为了在浏览器种运行对象必须是一个applet这就意味着对象必须是类的一个实例这个类是从由JAVA平台提供的Applet类派生而来的
ClickMe applet对象是一个ClickMe类的一个实例它是这样声明的
public class ClickMe extends Applet implements MouseListener {
...
}
上面的语句就产生了Applet的一个子类ClickMe继承了父类的许多功能包括初始化由浏览器来开始和结束在浏览器区域画图以及对接收到的鼠标事件注册除了有了这些功能ClickMe类还要实现以下的功能它的画图代码在paint的方法中初始化代码必须在init方法中等等
public void init() {
... // 这里加入ClickMe的初始化代码
}
public void paint(Graphics g) {
... // 这里加入ClickMe的画图代码
}
2.7.6 ClickMe Applet中的接口
ClickMe applet是通过在鼠标点击出显示一个红点来响应鼠标的点击事件如果对象想通知鼠标点击JAVA平台事件系统需要对象执行MouseListener接口这个对象必须同时作为鼠标监听器来注册
这个MouseListener接口声明了五种不同的志芋工每种方法是用在鼠标被点击的时候对不同鼠标事件的调用当鼠标被点击的时候或者当鼠标移动到applet外面的时候等等
下面是ClickMe applet完整的代码其中mousePressed是处理鼠标事件的
import java.applet.Applet;import java.awt.*;import java.awt.event.*;public class ClickMe extends Applet implements MouseListener {private Spot spot = null;private static final int RADIUS = 7;public void init() {addMouseListener(this);}public void paint(Graphics g) {// 画一个黑色边框和一个白色背景g.setColor(Color.white); ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________ ________________________________
2.8 面向对象概念的问题和练习
本节教程测试一下你对对象类消息等等的理解我们是通过做一些练习以及回答一些问题来进行的
2.8.1 问题
你可以使用API文档来回答这些问题
1. ClickMe applet使用Color.red来设置画图颜色为红色其它有什么颜色可以象这样来使用
2. 怎样设置颜色为紫色purple
2.8.2 练习
现在利用你从API文档中学到的知识来修改ClickMe applet为了编译这个程序你同样需要Spot.java文件具体修改如下
1. 修改这个applet来画一个绿色的方框而不是一个红点
2. 修改这个applet来用紫色显示你的名字而不是一个红点
第三章 语言基础
我们先看看一个具体例子给你们有个先入为主的感觉以下是一个BasicsDemo程序它的作用是从1加到10并显示结果
public class BasicsDemo {
public static void main(String[] args) {
int sum = 0;
for (int current = 1; current <= 10; current++) {
sum += current;
}
System.out.println("Sum = " + sum);
}
}
这个程序的输出为
Sum = 55
从上面的例子中我们可以看出即使是一个小的程序都要使用JAVA编程语言的许多传统特性其中包括变量操作符和流程控制语句以上的代码可能看起来有点复杂但是一旦你学习完本系列教程你就会发觉它实际上很简单本节教程将教给你一些JAVA编程语言的基础
语言基础
3.1 变量
你可以在程序中使用变量来容纳数据这一小节将数据类型怎样初始化变量以及怎样在代码块中引用变量实际上对象是存储它的状态在变量中的它的具体定义为变量是用标识符命名的数据项
你必须清除地为你想在程序中使用地每一个变量提供一个名字和类型这个变量的名字必须是一个合法的标识符以字母开头的一串Unicode字符你可以使用变量名来引用变量包含的数据这个变量的类型决定了什么类型的数值可以容纳以及什么的操作可以对它进行操作为了得到一个变量类型和名字你必须编写变量声明如下
type name
除了名字和类型你还要给变量一个作用域变量的作用域是由变量声明位置决定的
以下MaxVariablesDemo程序声明了八个不同类型的变量如下
public class MaxVariablesDemo {public static void main(String args[]) {// 整型数byte largestByte = Byte.MAX_VALUE;short largestShort = Short.MAX_VALUE;int largestInteger = Integer.MAX_VALUE;long largestLong = Long.MAX_VALUE;// 实型数float largestFloat = Float.MAX_VALUE;double largestDouble = Double.MAX_VALUE;// 其它的数据类型字符型和布尔型等等char aChar = 'S';boolean aBoolean = true; ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _______________________________________________________________________________________________________________________________ ________________________________
这个程序的输出为
The largest byte value is 127
The largest short value is 32767
The largest integer value is 2147483647
The largest long value is 9223372036854775807
The largest float value is 3.40282e+38
The largest double value is 1.79769e+308
The character S is upper case.
The value of aBoolean is true
下面的部分详细介绍了变量的各个方法包括数据类型名字作用域初始化以及final变量这个MaxVariablesDemo程序使用了你可能不熟悉并且不在本节提到的两项几个常数MAX_VALUE以及一个if-else语句每个MAX_VALUE常数是定义在由JAVA平台提供的其中一个数字类中它是最大的数值
3语言基础
语言基础
3.1.1 数据类型
每一个变量必须有一个数据类型一个变量的数据类型决定了变量能容纳的数值和操作符比如在MaxVariablesDemo程序中声明int largestInteger决定了largestInteger是一个整型数据类型int整型只能容纳整型数可以是正数也可以是负数你可以完成算术操作比如整型变量的加法等等
JAVA程序语言有两类的数据类型原始和引用一个原始类型的变量为它的类型包含了适当大小和格式的单一数值一个数字字符或者一个布尔型数值比如一个整型数值是一个32位数据
下面表格所有的JAVA支持的所有原始数据类型还给出了它们的大小和格式以及简短的描述MaxVariablesDemo程序为每一个原始类型声明了一个变量
关键字 |
描述 |
大小/格式 |
整型 | ||
byte |
字节长度整型 |
8位两个补码 |
Short |
短整型 |
16位两个补码 |
int |
整型 |
32位两个补码 |
long |
长整型 |
64位两个补码 |
实数 | ||
Float |
单精度浮点型 |
32位IEEE 754 |
Double |
双精度浮点型 |
64位IEEE 754 |
其它类型 | ||
Char |
单个字符 |
16位Unicode字符 |
boolean |
布尔型数值true或者false |
真或假 |
在其它语言中原始类型数据的格式和大小可能依靠于程序运行的平台相比之下Java程序语言可以指定原始数据类型的大小和格式因此你不必担心系统从属问题
你可以在你的代码中直接为原始变量设置数值比如如果你需要为一个整型变量设置为4你可以编写以下代码
int anInt = 4;
数字4就是一个整型数值下面是各种原始数值举例
数值 |
类型 |
178 |
Int |
8864L |
Long |
37.266 |
Double |
37.266D |
Double |
87.363F |
float |
26.77e3 |
double |
' c ' |
char |
true |
boolean |
false |
boolean |
总得说来没有小数点的数就是整型你可以通过在数字后面加一个'L' 或者'l'指定为一个长整型一般使用'L'而不使用'l'因为'l'很容易与数字'1'混起来有小数点的数为双精度类型你可以在数字后面放置'f' 或者 'F'来指定为实数而字符型数值可以是处在单引号中间的任何单一的Unicode字符两个布尔型数是true和false
数组类以及接口是引用的类型引用类型变量的数值跟原始类型的数值比较起来它是数值的一个引用或者是由变量代表的数值
一个引用称为一个指针或者在其它语言中称为内存地址JAVA编程语言象其它语言一样不支持地址的详细使用你可以使用变量的名字来取代
语言基础
3.1.1 数据类型
你可以在你的代码中直接为原始变量设置数值比如如果你需要为一个整型变量设置为4你可以编写以下代码
int anInt = 4;
数字4就是一个整型数值下面是各种原始数值举例
数值 |
类型 |
178 |
Int |
8864L |
Long |
37.266 |
Double |
37.266D |
Double |
87.363F |
float |
26.77e3 |
double |
' c ' |
char |
true |
boolean |
false |
boolean |
总得说来没有小数点的数就是整型你可以通过在数字后面加一个'L' 或者'l'指定为一个长整型一般使用'L'而不使用'l'因为'l'很容易与数字'1'混起来有小数点的数为双精度类型你可以在数字后面放置'f' 或者 'F'来指定为实数而字符型数值可以是处在单引号中间的任何单一的Unicode字符两个布尔型数是true和false
数组类以及接口是引用的类型引用类型变量的数值跟原始类型的数值比较起来它是数值的一个引用或者是由变量代表的数值
一个引用称为一个指针或者在其它语言中称为内存地址JAVA编程语言象其它语言一样不支持地址的详细使用你可以使用变量的名字来取代
3.1.2 变量名
程序是用变量名来引用变量数值的比如当显示largestByte变量的数值的时候MaxVariablesDemo程序就使用名字largestByte一个名字比如largesByte包含了一个单一的标识符被称为简单的名字即变量名在JAVA编程语言中对于变量名有下面的必须满足
1. 它必须是一个合法的标识符一个标识符是以字母开头的一串Unicode字符
2. 它必须不是一个关键字布尔型字符true或者false或者保留字NULL
3. 它必须在作用域中是唯一的在不同的作用域才允许存在相同名字的变量在一些条件下如果变量被定义在一个嵌套的代码块中它可能和其它变量共享相同的名字这点在以后的教程中会提到
这里有个约定变量名是以小写字母开头而类名是以一个大写字母开头的如果变量名包含了多个单词而每个单词要组合在一起则在每个单词的第一个字母大写比如IsVisible而下划线_可以处在变量的任何地方但是一般地它只用在常数中分离单词因为常数名都是用大写字母的利用下划线可以看得更清除
3.1.3 作用域
变量的作用域是一个程序的区域在上面变量可以通过它的名字来引用其次作用域也决定什么时候系统为变量创建和清除内存作用域只应用成员变量并决定是否变量可以从所在类的外部使用在程序中变量声明的位置建立它的作用域并且将它放置到以下四类之一
成员函数作用域
当地变量作用域
方法参数作用域
异常处理参数作用域
图14
如图14所示成员变量示类或者对象的成员它是在类中定义而不在任何方法或者构造函数中定义成员函数的作用域是类的完全定义但是当成员是使用在成员初始化表达式中的时候成员的定义需要在它使用之前出现在后面的教程中我们要再深入学习成员变量这里就不讲了
你可以在一个代码块中定义当地变量总的说来当地变量的作用域从它的声明扩展到了它被定义的代码块结束在MaxVariablesDemo中定义在主方法中的所有变量都是当地变量程序中的每一个变量的作用域从变量的定义扩展到了主方法的结束它在程序代码中是用右括号}来指示的
参数是方法或者构造函数的正式参数它们用于传递数值给方法和构造函数参数的作用域是整个方法或者构造函数
异常处理参数跟参数很相似差别在是前者是传递参数给异常处理而后者是传递给方法或者构造函数异常处理参数的作用域处在{和}之间的代码它紧跟着catch语句利用异常来处理错误向你说明了怎样编写一个带有参数的异常处理以下是一个代码例子
if (...) {
int i = 17;
...
}
System.out.println("The value of i = " + i); // 错误
最后的行不汇编因为当地变量I已经出了作用域i的作用域是处在{和}之间的代码块变量I在右括号}之后就不存在了改正的方法可以是讲变量的声明移到if语句块的外面或
者是将println方法调用移动到if语句块中
3.1.4 变量初始化
当地变量和成员变量可以利用一个赋值语句来初始化变量的数据类型必须与赋给它的数值的数据类型相匹配下面程序中的当地变量声明其初始化如下
// 整型
byte largestByte = Byte.MAX_VALUE;
short largestShort = Short.MAX_VALUE;
int largestInteger = Integer.MAX_VALUE;
long largestLong = Long.MAX_VALUE;
// 实数型
float largestFloat = Float.MAX_VALUE;
double largestDouble = Double.MAX_VALUE;
// 其它类型
char aChar = 'S';
boolean aBoolean = true;
参数和异常处理参数不能利用这种方法来初始化它的参数的数值只能通过来设置
3.1.5 final变量
你可以在任何作用域声明一个变量Final变量的数值不能在初始化之后进行改变这样的变量跟其它语言中的常量很相似
为了声明一个final变量你可以在类型之前的变量声明使用final关键字比如
final int aFinalVar = 0;
前面的语句声明了一个final变量并一起对它进行了初始化如果你在后面还想给aFinalVar赋其它的值的话就会导致一个编译错误在必要的时候你可以推迟对一个final本地变量进行初始化你可以先定义然后在之后再初始化如下
final int blankfinal;
. . .
blankfinal = 0;
已经声明了但是还没有初始化的final本地变量称为空白final同时一旦final本地变量被初始化它就不能再设置了并且之后的任何对blankfinal赋值的操作都将导致一个编译错误
3.1.5 变量的总结
当你声明了一个变量你就显性地设置了变量的名字和数据类型JAVA编程语言右两类的数据类型原始和引用原始数据的变量包含一个数值以下这张表显示了所有的原始数据类型以及它们的大小和格式
关键字 |
描述 |
大小/格式 |
整型 | ||
byte |
字节长度整型 |
8位两个补码 |
Short |
短整型 |
16位两个补码 |
int |
整型 |
32位两个补码 |
long |
长整型 |
64位两个补码 |
实数 | ||
Float |
单精度浮点型 |
32位IEEE 754 |
Double |
双精度浮点型 |
64位IEEE 754 |
其它类型 | ||
Char |
单个字符 |
16位Unicode字符 |
boolean |
布尔型数值true或者false |
真或假 |
变量声明的位置隐含地设置了变量的作用域它决定了代码的哪一部分可以通过变量名来调用这个变量具体有以下四种类型的作用域成员变量作用域本地变量作用域参数作用域以及异常处理参数作用域
你可以使用赋值操作符=来在声明的地方对变量进行初始化
你也可以将变量声明为finalFinal变量的数值在初始化之后不能再被改变
3.2 操作符
本节教程描述了怎执行各种操作比如算术和赋值操作
一个操作符利用一个两个或者三个运算对象来执行了一个函数只需要一个运算对象的操作符称为单元运算符例如++是一个单元操作符它是对运算对
象自增1需要两个运算对象的操作符号称为双元操纵符比如等于号=就是一个双元操作符它指定右边的运算对象给左边的运算对象最后三元操作符需要三个运算对象JAVA编程语言有一个三元运算符它是一个简要的if-else语句
单元操作符支持前缀或者后缀记号前缀记号是指操作符出现在它的运算对象之前例如
operator op //前缀记号
后缀记号是指运算对象出现在操作符之前例如
op operator //后缀记号
所有的双元操作符使用中缀记号即操作符出现在两个运算对象的中间
op1 operator op2 //中缀记号
三元操作符也是使用中缀记号例如
op1 ? op2 : op3 //中缀记号
操作除了执行一个操作还返回一个数值返回数值和它的类型依靠于操作符号和运算对象的类型比如算术操作符它完成基本的算术操作加减并且返回数值作为算术操作的结果由算术操作符返回的数据类型依靠于它的运算对象的类型如果你对两个整型数相加你就会得到一个整型数
我们可以将操作符分成以下几类
算术运算符
关系和条件运算符
移位和逻辑运算符
赋值运算符
其它的运算符
JAVA编程语言为所有的浮点型和整型数支持多种算术运算符这些运算符为+(加)-减*乘/除以及%(模)下面的表总结了双元算术运算符
运算符 |
使用 |
描述 |
+ |
op1 + op2 |
op1 加上op2 |
- |
op1 - op2 |
op1 减去op2 |
* |
op1 * op2 |
op1乘以op2 |
/ |
op1 / op2 |
op1 除以op2 |
% |
op1 % op2 |
op1 除以op2的余数 |
以下有一个例程ArithmeticDemo它定义了两个整型数和两个双精度的浮点数并且使用五种算术运算符来完成不同的运算操作这个程序同时使用了+符号来连接字符串程序如下
public class ArithmeticDemo {public static void main(String[] args) {//定义几个数int i = 37;int j = 42;double x = 27.475;double y = 7.22;System.out.println("变量数值...");System.out.println(" i = " + i);System.out.println(" j = " + j);System.out.println(" x = " + x);System.out.println(" y = " + y);//加System.out.println("加..."); ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________ ________________________________
这个程序得输出为
变量数值...
i = 37
j = 42
x = 27.475
y = 7.22
加...
i + j = 79
x + y = 34.695
减...
i - j = -5
x - y = 20.255
乘...
i * j = 1554
x * y = 198.37
除...
i / j = 0
x / y = 3.8054
计算余数...
i % j = 37
x % y = 5.815
混合类型...
j + y = 49.22
i * x = 1016.58
这里注意当一个整数和一个浮点数用为运算符来执行单一算术操作的时候结果为浮点型整型数是在操作之前转换为一个浮点型数的下面的表总结了根据运算对象的数据类型由算术操作符返回的数据类型它们是在操作执行之前进行数据转换的
结果的数据类型 |
运算数据类型 |
long |
任何一个运算对象都不是float或者doule型而且最少有一个运算对象为long |
int |
任何一个运算对象都不是float或者doule型而且还不能为long型 |
double |
最少有一个运算对象为double |
float |
最少有一个运算对象为float但不能是double型 |
除了双元的运算符+和-还有以下单元运算符
运算符 |
用法 |
描述 |
+ |
+op |
如果op是一个byteshort或者char型的op变成int型 |
- |
-op |
取op的相反数 |
另外两个简练的算术操作符为++和--++是完成自加1的作用而—是完成自减的作用不管是++还是—都可能出现在运算对象的前面前缀或者后面后缀但是它们的作用是不一样的前缀的格式为++op或--op它实现了在加/减之后才计算运算对象的数值而后缀的格式为op++或op--它实现了在加/减之前就计算运算对象的数值
下面的程序SortDemo中使用两次++和一个--如下
public class SortDemo {public static void main(String[] args) {int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076,2000, 8, 622, 127 };for (int i = arrayOfInts.length; --i >= 0; ) {for (int j = 0; j < i; j++) {if (arrayOfInts[j] > arrayOfInts[j+1]) {int temp = arrayOfInts[j];arrayOfInts[j] = arrayOfInts[j+1];arrayOfInts[j+1] = temp;}}}for (int i = 0; i < arrayOfInts.length; i++) {System.out.print(arrayOfInts[i] + " "); ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________________________________________
这个程序将10整型数值放置在一个数组arrayOfInts中这个程序使用了arrayOfInts.length来获得数组中的元素个数单个的元素可以由arrayOfInts[index]来访问这里index是一个整型数它指示数组中元素的位置这里一定要注意index的下标是从0开始的
这个程序的作用是输出从小到大的10个整数结果为
3 8 12 32 87 127 589 622 1076 2000
还是让我们好好看看这个SortDemo程序是怎样工作的下面是控制外部循环的语句
for (int i = arrayOfInts.length; --i >= 0; ) {
...
}
这个for语句是一个循环结构其中--i >= 0给出了循环的条件即--i大于等于0就继续循环一旦小于0就结束循环使用前缀--意味着最后的一次循环迭代是发生在当i等于0的时候如果我们改变改变一下使用后缀的那么循环迭代的最后一次发生在当i等于-1的时候这样就会发生错误因为数组的下标是从0开始的-1是一个无效的数组下标
程序中其它两个循环使用了后缀的++在两个例子中使用前缀或者后缀是没有问题的当其中一个运算符号返回的数值没有用到则约定使用后缀的
下面的表格总结自增/自减运算符
运算符 |
用法 |
描述 |
++ |
op++ |
自增1它是在自增之前计算op的数值的 |
++ |
++op |
自增1它是在自增之后计 |
算op的数值的 | ||
-- |
op-- |
自减1它是在自减之前计算op的数值的 |
-- |
--op |
自减1它是在自减之后计算op的数值的 |
3.2.2 关系与条件运算符
关系运算符是比较两个数值并决定它们的关系比如!=在如果两个运算对象不相等的情况下返回true以下这个表格总结了关系运算符
运算符 |
用法 |
在什么情况下返回true |
> |
op1 > op2 |
op1大于op2的时候 |
>= |
op1 >= op2 |
op1大于等于op2的时候 |
< |
op1 < op2 |
op1小于op2的时候 |
<= |
op1 <= op2 |
op1小于等于op2的时候 |
== |
op1 == op2 |
op1等于op2的时候 |
!= |
op1 != op2 |
op1不等于op2的时候 |
接下来介绍一个例子RelationalDemo
3.2.2 关系与条件运算符
下面是一个例子RelationalDemo它定义了三个整型数并且使用关系运算符来比较它们如下
public class RelationalDemo {public static void main(String[] args) {//定义几个数int i = 37;int j = 42;int k = 42;System.out.println("变量数值...");System.out.println(" i = " + i);System.out.println(" j = " + j);System.out.println(" k = " + k);//大于System.out.println("大于...");System.out.println(" i > j = " + (i > j));//false ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________ ________________________________
程序的输出为
变量数值...
i = 37
j = 42
k = 42
大于...
i > j = false
j > i = true
k > j = false
大于等于...
i >= j = false
j >= i = true
k >= j = true
小于...
i < j = true
j < i = false
k < j = false
小于等于...
i <= j = true
j <= i = false
k <= j = true
等于...
i == j = false
k == j = true
Not equal to...
i != j = true
k != j = false
3.2.2 关系与条件运算符
关系运算符经常用在条件运算符中来构造更复杂的判断表达式JAVA变成语言支持六种条件运算符五个双元运算符和一个单元运算符如下表所示
运算符 |
用法 |
什么情况返回true |
&& |
op1 && op2 |
op1 和 op2都是true有条件地计算op2 |
|| |
op1 || op2 |
op1 或者 op2是true有条件地计算op2 |
! |
! op |
op为false |
& |
op1 & op2 |
op1 和 op2都是true总是计算op1和op2 |
| |
op1 | op2 |
op1 或者 op2是true总是计算op1和op2 |
^ |
op1 ^ op2 |
如果op1和op2是不同的也就是说有其中一个运算对象是真的而不是两个都为真的时候 |
&&运算符可以完成条件AND的操作你可以使用两个不同的关系运算符和&&来决定是否两个关系都为true下面的一行代码使用了这个技术来决定是否数组的索引处在两个边界之间它决定了是否索引都大于等于0并且小于等于NUM_ENTRIES(它是在之前被定义为常数)
0 <= index && index < NUM_ENTRIES
这里注意在一些实例中第二个运算对象可能不用运算因为如果第一个运算对象是false则结果就一个是false因此不用在计算第二个运算对象了你看看以下的代码
(numChars < LIMIT) && (...)
&&运算符只有在两个运算对象都为true的时候才返回true因此如果numChars大于等于LIMIT的时候&&左边的运算对象就为false这时就不用在计算右边的运算对象就返回了数值false在这个例子中编译器将不会计算右边的运算对象如果右边运算对象有副效应的的话比如读一个流更新一个数值或者进行一个计算这个有重要的含义
当两边的运算对象都是boolean布尔型运算符号&跟&&执行相同运算但是&总是要计算两边的运算对象然后再在两个运算对象同为true的时候才返回true同样地当运算对象都为boolean布尔型的时候|执行与操作符号||一样的功能这个|运算符总是要计算两边的运算对象然后在最少有一边为true的时候才返回true当它们的运算对象为数字的时候& 和|是按位操作的
3.2.3 移位和逻辑运算符
移位运算符通过对第一个运算对象左移或者右移位来对数据执行位操作下面的这个表总结了JAVA编程语言中有效的移位运算符
运算符 |
用法 |
操作 |
>> |
op1 >> op2 |
将op1右移op2个位 |
<< |
op1 << op2 |
将op1左移op2个位 |
>>> |
op1 >>> op2 |
将op1右移op2个位无符号的 |
每一个运算符移动左边的运算对象的位数都是由右边的运算符给出的这个移位的方向取决于运算符本身比如下面的语句是实现将整数13右移1位的目的
13 >> 1;
13的二进制为1101.右移一位的结果为110即为十进制的6.左边的位用零来填充下面的表给出了JAVA编程语言提供的四种运算符来对它们的运算对象执行按位操作
运算符 |
用法 |
操作 |
& |
op1 & op2 |
按位与 |
| |
op1 | op2 |
按位或 |
^ |
op1 ^ op2 |
按位异或 |
~ |
~op2 |
按位求补 |
当它的运算对象为数字的时候&运算符为每一个运算对象的每位执行按位AND功能AND在运算对象的相应位为1的时候结果才为1其余情况结果都为0如下表所示
op1 |
op2 |
结果 |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
1 |
1 |
1 |
3.2.3 移位和逻辑运算符
假如你要对数13和12作AND操作比如13&12运算的结果为12因为12的二进制为1100遥13的二进制为1101具体运算过程如下所示
1101 //13
& 1100 //12
------
1100 //12
如果两个运算对象都为1AND的结果就为1或者结果就为0因此当你对两个运算对象执行AND操作的时候你可以看到左边的两位结果位1而右边两位的结果为0当两个操作对象都位数字的时候|操作符执行或操作而^执行异或操作或操作是说只要有一个运算对象为1结果就为1下面的表格给出了或操作的结果
op1 |
op2 |
结果 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
1 |
异或是指当运算对象不同时结果才为1或者结果为0下表给出了异或运算符的计算结果
op1 |
op2 |
结果 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
1 |
1 |
1 |
0 |
最后补运算符号是将运算对象的每一位倒置即如果原来的位位1结果就为0如果原来的位为0则结果为1
3.2.3 移位和逻辑运算符
通常按位操作对于管理boolean标识是很有用的假如程序中有几个boolean标识来指示各种组件的状态它是否可见它是否可以拖拉等等与其定义一个单独的boolean变量不如你定义单一的变量flags在flags的每一位都代表了这些标识的当前状态然后你可以使用位操作来设置或者得到每一个标识首先为程序设置常数来指示各种标识定义一个变量flags它的位要根据每个flag的当前状态来设置下面的代码将flags初始化为0意味着所有的标识的初始状态都位0 如下
static final int VISIBLE = 1;
static final int DRAGGABLE = 2;
static final int SELECTABLE = 4;
static final int EDITABLE = 8;
int flags = 0;
为了当有事物变得可见要设置"visible"标识你可以使用以下的语句
flags = flags | VISIBLE;
为了测试可见性你可以编写如下的语句
if ((flags & VISIBLE) == VISIBLE) {
...
}
下一页是完整的程序BitwiseDemo
3.2.3 移位和逻辑运算符
下面是完整的程序BitwiseDemo
public class BitwiseDemo {static final int VISIBLE = 1;static final int DRAGGABLE = 2;static final int SELECTABLE = 4;static final int EDITABLE = 8;public static void main(String[] args){int flags = 0;flags = flags | VISIBLE;flags = flags | DRAGGABLE;if ((flags & VISIBLE) == VISIBLE) {if ((flags & DRAGGABLE) == DRAGGABLE) {System.out.println("Flags are Visible and Draggable.");}} ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _ _________________________________________________________________________________________________________________________________________________________ _____ _______________________________________________________________________________________________________________________________ ________________________________
这个程序的输出为
Flags are Visible and Draggable.
Flags are now also Editable.
3.2.4 赋值运算符
你可以基本的赋值运算符=来为其它的赋值MaxVariablesDemo程序使用=来初始化所有的本地变量
// 整型
byte largestByte = Byte.MAX_VALUE;
short largestShort = Short.MAX_VALUE;
int largestInteger = Integer.MAX_VALUE;
long largestLong = Long.MAX_VALUE;
//实数
float largestFloat = Float.MAX_VALUE;
double largestDouble = Double.MAX_VALUE;
//其它类型
char aChar = 'S';
boolean aBoolean = true;
3.2.4 赋值运算符
JAVA编程语言同样提供了几个简洁的赋值运算符以进行算术移位或者按位操作或者赋值操作假如你想为一个变量增加一个数并将结果指定给该变量你可以这样做
i = i + 2;
当然你还可以这样来进行简化主要是用了+=运算符
i += 2;
上面的两行是等价的
下面的表列出了所有的赋值运算符和它们的等价形式
运算符 |
用法 |
等价形式 |
+= |
op1 += op2 |
op1 = op1 + op2 |
-= |
op1 -= op2 |
op1 = op1 - op2 |
*= |
op1 *= op2 |
op1 = op1 * op2 |
/= |
op1 /= op2 |
op1 = op1 / op2 |
%= |
op1 %= op2 |
op1 = op1 % op2 |
&= |
op1 &= op2 |
op1 = op1 & op2 |
|= |
op1 |= op2 |
op1 = op1 | op2 |
^= |
op1 ^= op2 |
op1 = op1 ^ op2 |
<<= |
op1 <<= op2 |
op1 = op1 << op2 |
>>= |
op1 >>= op2 |
op1 = op1 >> op2 |
>>>= |
op1 >>>= op2 |
Op1 = op1 >>> op2 |
3.2.5 其它的运算符
下面的表格给出了JAVA编程语言支持的其它运算符
运算符 |
描述 |
?: |
作用相当于if-else语句 |
[] |
用于声明数组创建数组以及访问数组元素 |
. |
用于访问对象实例或者类的类成员函数 |
( params ) |
意义一个以逗号分开的参数系列 |
( type ) |
将数值转换为一个的类型 |
new |
创建一个新的对象或者新的数组 |
instanceof |
决定是否第一个运算对象是第二个运算对象的一个实例 |
3.2.5 其它的运算符
下面的表格给出了JAVA编程语言支持的其它运算符
下面逐个介绍
(1)简化的if-else语句
这个?:运算符是一个条件运算符号它是一个简化的if-else语句
op1 ? op2 : op3
这个?:运算符号在op1为true的时候返回op2或者返回op3
(2) [ ] 运算符
你可以使用方括号来声明数组创建数组并且访问数组中一个特殊的元素以下是数组声明的一个例子
float[] arrayOfFloats = new float[10];
前面的代码声明了一个数组来容纳10个浮点型的数字以下给出访问第7个元素的方法
arrayOfFloats[6];
这里注意第7个元素的下标是6因为第一个元素的下标为0.
3.运算符
点运算符是访问对象实例或者类的类成员函数
4运算符
当声明或者调用一个方法的时候你可以在(和)之间列出方法的参数你可以利用()来指定一个空的参数列表
&S226; (type)运算符
这个运算符可以将数值转换为一个指定的类型
&S226; new运算符
你可以使用new运算符号来创建一个新对象或者一个新数组以下是一个从java.lang包中的Interger类创建新的整型对象
Integer anInteger = new Integer(10);
6instanceof运算符
instanceof操作符测试第一个运算对象是否是第二个运算对象的实例比如
op1 instanceof op2
这里op1必须是对象的名字而且op2必须是类的名字如果对象直接或者间接地来自类那么对象就被认为是类的一个实例
3.3 表达式语句和块
这一节教程讨论怎样将运算符和变量集中到表达式中去表达式是代码的构建块你同样还可以学到怎样构造语句和语句块
变量和运算符是程序的基本构造块你可以集中变量和运算符来组成表达式表达式是执行计算并返回数值的代码段通过使用大括号{和}你可以创建代码块
3.3.1表达式
表达式执行程序的工作跟其它事情一样表达式用于计算和为变量赋值并且帮助控制程序的执行流程表达式任务有两重利用表达式的元素来执行计算返回计算结果的数值
就象前面的章节讨论的运算符要返回一个数值因此运算符的使用就是一个表达式如下的程序
char aChar = 'S';
boolean aBoolean = true;
// 显示
System.out.println("The largest byte value is " + largestByte);
...
if (Character.isUpperCase(aChar)) {
...
}
3.3.1表达式
每一个条语句执行一个操作并且返回一个数值如下表所示
表达式 |
方式 |
返回数值 |
aChar = 'S' |
将字符'S'赋给字符变量aChar |
返回赋值以后的aChar值 |
"The largest byte value is " + largestByte |
连接字符串"The largest byte value is "和largestByte转换为字符串的数值 |
返回结果的字符串The largest byte value is 127 |
Character.isUpperCase(aChar) |
调用isUpperCase方法 |
返回方法的数值true |
表达式返回数值的数据类型依靠于使用在表达式中的元素这个表达式aChar = 'S'返回一个字符型因为赋值操作符返回相同数据类型操作符和aChar以及'S'都是字符型的数值从其它表达式可以看出表达式可以返回一个boolean数值字符串等等
只要表达式子需要的数据类型匹配其它的数据类型JAVA编程语言允许你将多个小的表达式构造复合的表达式下面是一个复合的表达式例子
x * y * z
在这个消息的例子中各个运算对象的先后顺序不是很重要因为乘法的结果跟顺序无关但是这对于其它表达式有时并不适合例如下面的表达式就跟顺序有关了它关系到是哪个先乘还是先加的问题
x + y / 100
你可以利用括号(和)来正确指定表达式比如为了避免上面表达式子的含糊性你可以这样编写
(x + y)/ 100 //推荐使用这种方法以养成良好的编程习惯
如果你没有在复合的表达式中指定各个运算的顺序那么运算的顺序将取决于表达式中的运算符的优先顺序即优先顺序高的先进行计算比如除法运算符的优先顺序高于加法运算符所以以下的两条语句是等价的
x + y / 100
x + (y / 100)
虽然它们是等价的还是推荐是推荐使用第二种在编写复合表达式的时候推荐你利用圆括号(和)来清楚指定计算的顺序这便于程序的阅读以及维护
3.3.1表达式
下面的表格给出了各种运算符的优先顺序前面的运算符优先顺序高于后面的运算符同一行的运算符其优先顺序相同
后缀运算符 |
[] . (params) expr++ expr-- |
单元运算符 |
++expr --expr +expr -expr ~ ! |
创建运算符 |
new (type)expr |
乘法运算符 |
* / % |
加法运算符 |
+ - |
移位运算符 |
<< >> >>> |
关系运算符 |
< > <= >= instanceof |
相等与不等 |
== != |
按位运算符 |
AND & |
按位运算符 |
OR ^ |
按位运算符 |
OR | |
逻辑运算符 |
AND && |
逻辑运算符 |
OR || |
条件运算符 |
? : |
赋值运算符 |
= += -= *= /= %= &= ^= |= <<= >>= >>>= |
当相同优先级的运算符出现在相同的表达式中所有的双元运算符的运算顺序是从左到右而赋值运算符是从右到左计算的
3.3.2 语句
语句粗略地等价于自然语言的句子一个语句组成了一个执行的完整单元以下表达式的类型可以通过用逗号;终止表达式来组成一条语句
1. 赋值语句
2. 任何使用++或者--的语句
3. 方法调用
4. 对象创建表达式
这些语句的种类称为表达式语句以下是表达式语句
aValue = 8933.234; //赋值语句
aValue++; //增量语句
System.out.println(aValue); //方法调用语句
Integer integerObject = new Integer(4); //对象创建语句
除了上面的这些表达式语句还有另外两种语句
1定义变量的声明语句以下给出声明变量的语句
double aValue = 8933.234; // 声明语句
2控制流程语句它控制了语句执行的先后顺序For循环语句和if语句都是控制流程语句的例子
3.3.3 块
块是一组处在{和}之间的零条或者多条语句它可以使用在程序的任何地方下面的例子给出了两个块每一个块中包含了一条语句
if (Character.isUpperCase(aChar)) {
System.out.println("The character " + aChar + " is upper case.");
} else {
System.out.println("The character " + aChar + " is lower case.");
}
3.4 控制流程语句
程序使用控制流程语句来有条件地执行语句循环语句或者跳转到程序中的其它部分执行语句这节教程介绍怎样利用比如if-else和while来控制你的程序流程
当你编写程序的时候你在文件中键如语句如果你没有使用控制流程语句编译器将
以从左到右从上到下的顺序执行这些语句你可以在你的程序中使用控制流程语句来有条件地执行语句来重复执行语句块地代码改变程序执行的顺序等等比如在下面的代码段中if语句就有条件执行了括号中的System.out.println这里的条件是Character.isUpperCase(aChar)的返回值
char c;
...
if (Character.isUpperCase(aChar)) {
System.out.println("The character " + aChar + " is upper case.");
}
JAVA编程语言提供了几个控制流程语句在下表给出了
语句 |
关键字 |
循环语句 |
while, do-while , for |
判断语句 |
if-else, switch-case |
异常处理 |
try-catch-finally, throw |
分支语句 |
break, continue, label:, return |
3.4 控制流程语句
控制流程语句的基本格式为
控制流程语句描述
{
语句参数
}
如果块中只有一条语句大括号{和}并不是必要的但是这里推荐使用{和}因为代码会更容易阅读避免在修改代码的时候发生错误
控制流程语句有
1. while和do-while语句
2. for语句
3. if-else语句
4. switch语句
5. 异常处理语句
6. 分支语句
这里注意虽然goto是一个保留字它在C语言中也是一个控制流程语句但是当前JAVA编程语言还不支持这个goto语句
好吧我们开始对各个控制流程语句逐个介绍
3.4.1 while和do-while语句
你可以使用while语句当条件保持为true的时候持续执行语句块While语句的通常语法为
while (expression) {
statement
}
首先while语句执行表达式它将返回一个boolean数true或者false如果表达式返回truewhile语句执行相应的语句While语句继续测试表达式并执行块代码直到表达式返回false
下面给出一个例程WhileDemo它使用while语句来浏览字符串的各个字符并复制字符串直到程序找到字符g为止
public class WhileDemo {
public static void main(String[] args) {
String copyFromMe = "Copy this string until you " +
"encounter the letter 'g'.";
StringBuffer copyToMe = new StringBuffer();
int i = 0;
char c = copyFromMe.charAt(i);
while (c != 'g') {
copyToMe.append(c);
c = copyFromMe.charAt(++i);
}
System.out.println(copyToMe);
}
}
最后一行打印出来的数值为Copy this strin.
3.4.2 for语句
for语句提供了一个简便的方法来进行循环For语句的语法如下
for (初始条件;终止条件;增量) {
语句
}
初始条件是初始化循环的表达式它在循环开始的时候就被执行一次而终止条件决定什么时候终止循环这个表达式在每次循环的过程都被计算当表达式计算结果为false的时候这个循环结束最后,增量是循环一次增加多少即步长的表达式所有的这些都是可选的实际上为了实现无限制的循环这三个表达式都可以省略
for ( ; ; ) { // 无限制的循环
...
}
下面是一个例程ForDemo它使用了一个for语句来打印一个数组的所有元素
public class ForDemo {
public static void main(String[] args) {
int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076,
2000, 8, 622, 127 };
for (int i = 0; i < arrayOfInts.length; i++) {
System.out.print(arrayOfInts[i] + " ");
}
System.out.println();
}
}
这个程序的输出为 32 87 3 589 12 1076 2000 8 622 127.
这里注意你可以在for循环的初始化语句中声明一个本地变量这个变量的作用域只是在for语句的块中它可以用在终止条件语句和增量表达式中如果控制for循环的变量没有用在循环的外部最好的方法是在初始化表达式中声明这个变量例如ijk等经常用来在for控制语句中在for循环的初始化表达式中声明它们来限制它们的生命周期以减少错误
3.4.3 if-else语句
if语句可以使程序根据一些条件有选择地执行语句举个例子假如你的程序根据boolean变量DEBUG的值来打印了调试信息如果DEBUG是true你的程序就打印出调试信息比如变量x的数值或者就不打印具体的程序如下
if (DEBUG) {
System.out.println("DEBUG: x = " + x);
}
上面是一个简单的if语句的例子总得说来if语句的语法格式为
if (表达式) {
语句参数
}
如果你想在if判断表达式为false的时候执行不同的语句你可以使用else语句举个另外的例子吧假如你的程序需要执行不同的工作主要是根据用户是点击OK键还是击警告窗口中的其它按钮你可以使用if和else语句来实现这个目的
. . .
// response is either OK or CANCEL depending
// on the button that the user pressed
. . .
if (response == OK) {
// 在这里添加执行OK动作的代码
} else {
// 在这里添加执行Cancel动作的代码
3.4.3 if-else语句
如果if部分为false则执行else块另外一种else语句的格式是else if它根据执行另外的表达式执行语句一个if语句可以跟着任意个else if语句但只能有一个else语句下面是一个例程IfElseDemo它根据测试分数的数值来指定一个等级90或者更高为A80或者更高为B等等
public class IfElseDemo {
public static void main(String[] args) {
int testscore = 76;
char grade;
if (testscore >= 90) {
grade = 'A';
} else if (testscore >= 80) {
grade = 'B';
} else if (testscore >= 70) {
grade = 'C';
} else if (testscore >= 60) {
grade = 'D';
} else {
grade = 'F';
}
System.out.println("Grade = " + grade);
}
}
程序的输出为
Grade = C
3.4.3 if-else语句
这时你可以注意到testscore的数值可以满足组合if条件76 >= 70 and 76 >= 60但是当系统处理组合if语句的时候一旦条件满足适当的语句就将被执行(grade = 'C';)并且不需要计算剩余的条件控制就跳出if语句JAVA编程语言支持运算符?:它是一个简化的if语句 下面再看看上面教程中的MaxVariablesDemo程序
if (Character.isUpperCase(aChar)) {
System.out.println("The character " + aChar + " is upper case.");
} else {
System.out.println("The character " + aChar + " is lower case.");
}
下面你可以利用?:运算符来重写这段代码
System.out.println("The character " + aChar + " is " +
(Character.isUpperCase(aChar) ? "upper" : "lower") +
"case.");
如果这个isUpperCase方法返回true这个?:运算符返回字符串"upper" 或则它就返回字符串"lower"
3.4.4 switch语句
使用switch语句可以根据一个整型表达式有条件地执行语句下面的例程SwitchDemo声明了一个整型变量month它的数值代表了月份这个程序显示了月份的名字主要是根据month的数值并且使用了switch语句
public class SwitchDemo {
public static void main(String[] args) {
int month = 8;
switch (month) {
case 1: System.out.println("January"); break;
case 2: System.out.println("February"); break;
case 3: System.out.println("March"); break;
case 4: System.out.println("April"); break;
case 5: System.out.println("May"); break;
case 6: System.out.println("June"); break;
case 7: System.out.println("July"); break;
case 8: System.out.println("August"); break;
case 9: System.out.println("September"); break;
case 10: System.out.println("October"); break;
case 11: System.out.println("November"); break;
case 12: System.out.println("December"); break;
}
}
}
这个switch语句计算它的表达式在这个例子中是计算month的数值然后计算适当的case 语句这样程序的输出为August
3.4.4 switch语句
当然你可以使用if语句来实现它
int month = 8;
if (month == 1) {
System.out.println("January");
} else if (month == 2) {
System.out.println("February");
}
. . .
决定使用if语句还是switch语句的关键主要是根据可读型以及其它因素if语句可以在更大范围或者条件来决定而switch只有根据单个整型变量来做决定同时提供给case语句的数值必须是单一的另外一点是switch语句在每个case之后有一个break语句每个break语句终止了swtich语句并且控制流程继续switch块之后的第一个语句break语句是必须的因为没有它case语句就会失效也就是说没有break语句控制流程按顺序执行case语句
下面的程序SwichDemo2将介绍为什么它是有效的
public class SwitchDemo2 {public static void main(String[] args) {int month = 2;int year = 2000;int numDays = 0;switch (month) {case 1:case 3:case 5:case 7:case 8:case 10:case 12:numDays = 31;break; ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _______________________________________________________________________________________________________________________________ ________________________________
这个程序的输出为
Number of Days = 29
最后的break语句是不需要的因为流程将跳出switch语句了但是我们推荐使用为
最后一个case语句使用一个break以防你需要还增加更多的case语句这就使得更容易修改代码以及减少错误
3.4.4 switch语句
一般地你可以使用break来在分支语句中终止循环最后你可以在switch最后使用缺省地语句来处理所有的上面没有处理的情况
int month = 8;
. . .
switch (month) {
case 1: System.out.println("January"); break;
case 2: System.out.println("February"); break;
case 3: System.out.println("March"); break;
case 4: System.out.println("April"); break;
case 5: System.out.println("May"); break;
case 6: System.out.println("June"); break;
case 7: System.out.println("July"); break;
case 8: System.out.println("August"); break;
case 9: System.out.println("September"); break;
case 10: System.out.println("October"); break;
case 11: System.out.println("November"); break;
case 12: System.out.println("December"); break;
default: System.out.println("Hey, that's not a valid month!"); break;
}
3.4.5 异常处理语句
JAVA编程语言提供一个异常机制用来帮助程序报告和处理错误当一个错误发生的时候程序就产生一个异常即正常的程序路程被中断并且系统缓建想找到一个异常处理一个能处理特殊类型错误的代码块这个异常福利可以视图恢复错误或者如果它决定了错误是不可修复的它提供一个较好退出程序的方法
有三条语句可以处理异常
1. try语句它可以在异常产生的地方识别语句块
2. catch语句它必须和try语句一起用它可是识别可以处理特定异常错误的语句块这个语句是在如果有一个特定的异常发生在try块中就被执行
3finally语句它必须跟try语句一起用它可以识别语句块这些语句块是在不管在try块中是否有错误发生被执行的
下面是这三个语句的语法格式
try {
语句参数
} catch (异常错误类型 名字) {
语句参数
} finally {
语句参数
}
上面简述了由JAVA编程语言提供的用来报告和处理错误的语句但是另外的因素和原因比如运行和检查异常之间的差异以及异常类的分级结构代表异常的不同种类在使用异常
机制的时候起着重要的作用
3.4.6 分支语句
JAVA编程语言支持下面的三种分支结构
break语句
continue语句
return语句
下面逐个介绍
1break语句
break语句有两种形式未标志的和标志的你在前面就已经看过了未标志的break语句一个未标志的break语句终止swtich语句控制流程马上转到switch语句下方的语句下面的例程BreakDemo它包含了一个for循环查找数组中特定的数值
public class BreakDemo {
public static void main(String[] args) {
int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076,
2000, 8, 622, 127 };
int searchfor = 12;
int i = 0;
boolean foundIt = false;
for ( ; i < arrayOfInts.length; i++) {
if (arrayOfInts[i] == searchfor) {
foundIt = true;
break;
}
}
if (foundIt) {
System.out.println("Found " + searchfor + " at index " + i);
} else {
System.out.println(searchfor + "not in the array");
}
}
}
当数值被找到的时候这个break语句终止了for循环控制流程就转到for语句的下面的语句继续执行这个程序的输出为 Found 12 at index 4
3.4.6 分支语句
未标志形式的break语句被用来终止内部的switchforwhile或者do-while而标志形式的break语句终止一个外部的语句它是通过在break语句中使用一个标志来实现的下面的例程BreakWithLabelDemo跟前面的例子有点相似只不过它是在一个两维数组中搜索一个数值利用两个嵌套的for循环遍历了整个数组当数值被找到了标志形式的break语句就终止标志的search语句这个search语句是在for循环外部的
public class BreakWithLabelDemo {
public static void main(String[] args) {
int[][] arrayOfInts = { { 32, 87, 3, 589 },
{ 12, 1076, 2000, 8 },
{ 622, 127, 77, 955 }
};
int searchfor = 12;
int i = 0;
int j = 0;
boolean foundIt = false;
search:
for ( ; i < arrayOfInts.length; i++) {
for (j = 0; j < arrayOfInts[i].length; j++) {
if (arrayOfInts[i][j] == searchfor) {
foundIt = true;
break search;
}
}
}
if (foundIt) {
System.out.println("Found " + searchfor + " at " + i + ", " + j);
} else {
System.out.println(searchfor + "not in the array");
}
}
}
这个程序的输出为
Found 12 at 1, 0
这个语法看起来可能有点费解break语句终止了标志语句它不能将控制流程转到这个标志处控制流程迅速将标志终止的后面的语句
3.4.6 分支语句
2continue语句
你可以使用continue语句来跳过当前的forwhile或者do-while循环未标志形式跳到内部循环体的末尾处并且计算控制循环的boolean表达式跳过循环的其它部分下面的例程ContinueDemo遍历字符串中的所有字母如果当前字母不是一个pcontiue语句就忽略循环的其它部分并且处理下一个字符如果它是一个p字母程序就对计数器增1再将p转换维大写字母
public class ContinueDemo {
public static void main(String[] args) {
StringBuffer searchMe = new StringBuffer(
"peter piper picked a peck of pickled peppers");
int max = searchMe.length();
int numPs = 0;
for (int i = 0; i < max; i++) {
//interested only in p's
if (searchMe.charAt(i) != 'p')
continue;
//process p's
numPs++;
searchMe.setCharAt(i, 'P');
}
System.out.println("Found " + numPs + " p's in the string.");
System.out.println(searchMe);
}
}
这个程序的输出为
Found 9 p's in the string.
Peter PiPer Picked a Peck of Pickled PePPers
3.4.6 分支语句
标志形式的continue语句将忽略外部给定标志的循环下面的例程ContinueWithLabelDemo它使用了一个嵌套的循环来搜索一个子字符串程序如下
public class ContinueWithLabelDemo {
public static void main(String[] args) {
String searchMe = "Look for a substring in me";
String substring = "sub";
boolean foundIt = false;
int max = searchMe.length() - substring.length();
test:
for (int i = 0; i <= max; i++) {
int n = substring.length();
int j = i;
int k = 0;
while (n-- != 0) {
if (searchMe.charAt(j++) != substring.charAt(k++)) {
continue test;
}
}
foundIt = true;
break test;
}
System.out.println(foundIt ? "Found it" : "Didn't find it");
}
}
这个程序的输出为Found it
3return 语句
最后讲讲分支结构的最后一个return语句你可以使用return 来退出当前的方法控制流程返回到调用方法的下一个语句这个return语句有两种形式一种是返回一个数值另外一种没有返回数值为了返回一个数值简单地可以将数值放置在return关键字后面即可例如
return ++count;
由return返回的数值类型必须匹配方法声明返回的数值类型当方法被声明voidreturn的使用就不返回一个数值
return
第四章 对象和简单数据对象
对象和简单数据对象
这节教程将开始讨论对象的生命周期包括怎样创建对象怎样使用它以及在不使用它的时候将它从系统中清除下面一个一个介绍
4.1 对象的生命周期
在这一小节中你可以学到怎样创建和使用任何类型的对象还讨论了当对象不再需要的时候系统怎样清除对象的
典型的JAVA程序创建对象对象之间的交互是通过发送消息来实现的通过这些对象的交互JAVA程序可以执行一个GUI运行一个动画或者通过网络发送和接收信息一旦对象已经完成了任务它就被作为无用信息被回收它的资源可以由其它对象回收利用
以下是一个小的例子程CreateObjectDemo它创建三个对象一个是Point对象和两个Rectange对象你需要这三个源程序才可以编译这个程序
public class CreateObjectDemo {
public static void main(String[] args) {
//创建一个Point对象和两个Rectangle对象
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
// 显示rect_one的宽高以及面积
System.out.println("Width of rect_one: " + rect_one.width);
System.out.println("Height of rect_one: " + rect_one.height);
System.out.println("Area of rect_one: " + rect_one.area());
// 设置rect_two的位置
rect_two.origin = origin_one;
// 显示rect_two的位置
System.out.println("X Position of rect_two: " + rect_two.origin.x);
System.out.println("Y Position of rect_two: " + rect_two.origin.y);
// 移动rect_two并且显示它的新位置
rect_two.move(40, 72);
System.out.println("X Position of rect_two: " + rect_two.origin.x);
System.out.println("Y Position of rect_two: " + rect_two.origin.y);
}
}
一旦创建了对象程序就可以操作对象并将它们有关的一些信息显示出来以下是这个程序的输出结果
Width of rect_one: 100
Height of rect_one: 200
Area of rect_one: 20000
X Position of rect_two: 23
Y Position of rect_two: 94
X Position of rect_two: 40
Y Position of rect_two: 72
这一节使用这个例子来在程序中描述对象的生命周期从这你可以学到怎样编写代码来创建使用对象以及系统怎样将它从内存中清除的
4.1 对象的生命周期
下面主要分成几部分来讨论
1. 创建对象
2. 使用对象
3. 清除没有使用的对象
4.1.1 创建对象
众所周知可以从类来创建对象下面的几条语句都是用来创建对象的它们都是来自上面程序CreateObjectDemo程序
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
上面第一条语句从Point类创建了一个对象而第二条和第三条语句是从Rectangle类众创建了对象但是每条语句都有三部分组成
1. 声明Point origin_oneRectangle rect_one以及Rectangle rect_two都是变量的声明它们的格式是类型后加变量名当你创建一个对象的时候你不必声明一个变量来引用它然而变量生命经常出现在创建对象代码的相同行上
2. 实例化new是JAVA运算符它可以创建新的对象并且为对象分配了内存空间
3. 初始化new运算符后跟着一个构造函数的调用比如 Point(23,94)就是一个Point类的构造函数的调用这个构造函数初始化了这个新对象
下页也对这几个部分逐个介绍
4.1.1.1 声明一个变量来引用对象
从前面的教程你应该知道了如何声明一个变量了你可以这样来编写
type name
其中type是数据类型而name是变量名
除了原始类型比如int和booleanJAVA平台还直接提供了类和接口也是数据类型这样为了声明一个变量来引用对象你可以使用类或者接口的名字作为变量的类型下面的例程使用了Point和Rectangle类作为类型来声明变量
Point origin_one = new Point(23, 94);
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
Rectangle rect_two = new Rectangle(50, 100);
声明没有创建新对象Point origin_one代码没有一个新的Point对象它只是声明一个变量orgin_one它将用来引用Point对象这个引用暂时是空的直到被赋值了一个空的引用就是一个NULL引用
4.1.1.2 实例化对象
为了创建一个对象你必须用new来实例化它New运算符是通过为新对象分配内存来实例化一个类的这个new运算符需要一个后缀参数即构造函数的一个调用构造函数
的名字提供了要初始化类的名字构造函数初始化了新的对象
New运算符号返回一个引用给它创建的对象的通常这个引用被赋值为适当类型的变量
4.1.1.3 初始化对象
以下是Point类的代码
public class Point {
public int x = 0;
public int y = 0;
//一个构造函数
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
这个类包含了一个构造函数你可以识别这个构造函数因为它跟类的名字是相同名字的它没有任何的返回类型这个在Point类中的构造函数有两个整型参数它是由代码(int x, int y)来定义的下面的整数23和94就是这个参数的数值
Point origin_one = new Point(23, 94);
4.1.1.3 初始化对象
下面是Rectangle类的代码它包含了四个构造函数
public class Rectangle {
public int width = 0;
public int height = 0;
public Point origin;
//四个构造函数
public Rectangle() {
origin = new Point(0, 0);
}
public Rectangle(Point p) {
origin = p;
}
public Rectangle(int w, int h) {
this(new Point(0, 0), w, h);
}
public Rectangle(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
//用于移动rectangle的方法
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
//用于计算矩形面积的方法
public int area() {
return width * height;
}
}
每一个构造函数可以让你为矩形的各个方法提供初始数值你可以设置矩形的原点宽度和高度如果一个类中有多个构造函数它们的名字都是相同的只是它们有不同类型的参数或者不同数目的参数JAVA平台可以根据参数的不同数目和类型类来区分构造函数当JAVA平台遇到的代码的时候它就调用在Rectangle类中的构造函数这个函数需要一个Point参数以及两个整型参数
Rectangle rect_one = new Rectangle(origin_one, 100, 200);
这个调用初始化了矩形的原点orgin_one代码也设置了矩形的宽度100和高度200
4.1.1.3 初始化对象
多个引用可以引用相同的对象下面的代码行调用了需要两个整型参数的构造函数它为宽度和高度提供了初始化数值如果你仔细看看这个代码你会发现它创建一个Point对象它的x和y数值被初始化为0
下面的Rectangle构造函数没有任何参数
Rectangle rect = new Rectangle();
如果一个类没有显性声明任何构造函数JAVA平台自动提供一个没有参数的构造函数这是一个缺省的构造函数它没有完成任何事情这样所有的类就至少有一个构造函数
4.1.2 使用对象
一旦你创建了一个对象你可能想使用它来做一些事情你可能从它得到一些信息或者想改变它的状态或者让它来完成一些动作对象允许你做以下两件事情
1. 操作或者检查它的变量
2. 调用它的方法
4.1.2.1 引用对象的变量
下面是引用对象变量的基本形式它是使用了有条件的名字即长名字
objectReference.variableName
当实例变量处在作用域内的时候你可以为实例变量使用一个简单的名字也就是说在对象类的代码中处在对象类外面的代码必须使用有条件的名字比如在CreateObjectDemo类中的代码处在类Rectangle类代码的外面所以为了引用Rectangle对象rect_one的originwidth和height变量CreateObjectDemo必须相应使用rect_one.originrect_one.width和rect_one.height这个程序使用了rect_one的width和height
System.out.println("Width of rect_one: " + rect_one.width);
System.out.println("Height of rect_one: " + rect_one.height);
如果直接使用在CreateObjectDemo类中的变量width和height那就将产生一个编译错误在后面程序还将使用类似的代码来显示关于rect_two的信息相同类型的对象将有相同实例变量的副本这样每一个Rectangle对象就都有变量originwidth和height了当你通过对象引用来访问实例变量的时候你就引用了特定对象的变量在CreateObjectDemo程序有两个对象rect_one和rect_two它们有不同的originwidth和height变量
对象的长文件名的第一部分是对象引用它必须是一个对象的引用这里你可以使用引用变量的名字或者你可以使用任何的表达式来返回一个对象引用重新调用这个new运算符可以返回一个对象的引用这样你可以使用从new返回的数值来访问一个新的对象变
量
int height = new Rectangle().height;
这个语句创建了一个新的Rectangle对象并且得到它的height(高度)从本质上讲这条语句计算了Rectangle缺省的高度这里注意在这条语句被执行后程序不再有创建了的Rectangle的引用因为程序不再在变量中存储这个引用对象就被取消引用而它的资源可以JAVA平台重新使用
4.1.2.2 关于变量访问
利用其它对象和类对对象变量直接的操作是不允许的因为有可能为变量设置的数值没有任何的意义比如我们使用前面教程中的Rectangle类你可以创建一个矩形它的width和height都是负的但是它是没有意义的
较好的做法是不采用直接对变量进行操作类提供一些方法其它的对象可以通过这些方法来检查或者改变变量这些方法要确保变量的数值是有意义的这样Rectangle类将提供setWidthsetHeightgetWidth以及getHeight方法来设置或者获得宽度和高度这些用于设置变量的方法将在调用者试图将width和height设置为负数的时候汇报一个错误使用方法而不使用直接变量访问的好处还有类可以改变变量的类型和名字来存储width和height而没有影响它的客户程序
但是在实际情况下有时允许对对象变量直接访问比如通过定义Point类和Rectangle类为public它们都允许对它们的成员变量*访问
JAVA编程语言提供了一个反问控制机制凭它类可以决定什么其它的类可以直接访问它的变量如果其它对象对类直接操作可能导致无意义的话类可以保护变量改变这些变量应该利用方法调用来控制如果类授权访问给它的变量你可以检查和改变这些变量而但不能造成不利的效果
4.1.2.2 调用对象的方法
同样你可以使用有限制的名字长名字来调用对象的方法有限制的名字的格式是在对象引用的后面加上点.再跟着方法的名字即对象引用.方法名字同样你还可以利用圆括号(和)来为方法提供参数如果方法不需要任何参数就留空它
objectReference.methodName(argumentList);
or
objectReference.methodName();
Rectangle类有两个方法area和move即计算矩形的面积和改变矩形的原点这里是CreateObjectDemo代码它调用这两个方法
System.out.println("Area of rect_one: " + rect_one.area());
...
rect_two.move(40, 72);
上面的第一条语句调用rect_one的area方法并显示结果第二条语句是移动rect_two因为move方法为对象的原点坐标x和y赋了新值其中objectReference 必须是一个对象的引用你可以使用一个变量名字而且你也可以使用任何表达式来返回对象的引用而new运算符返回一个对象的引用因此你可以使用从new返回的数值来调用一个新的对象方法
new Rectangle(100, 50).area()
表达式new Rectangle(100,50)返回一个对象引用它是引用一个Rectangle对象上面已经提到你可以使用点符号.来调用新的Rectangle的面积方法以计算新矩形的面积另外方法area也返回一个数值对于这些返回数值的方法你可以使用在表达式中使用方法调用你可以指定返回的数值给变量参见如下的例子
int areaOfRectangle = new Rectangle(100, 50).area();
这里要提醒一下在特定对象中调用一个方法跟发送一个信息给对象是相同的
4.1.2.3 关于方法访问
在Point和Rectangle类中的方法都被声明为public因此它们可以被任何其它的类所访问有时类需要限制访问它的方法比如一个类可能可能有一个方法只有它的子类才能调用它类可以在它用于控制访问它的变量的时候使用相同的机制来对它的方法进行控制访问
4.1.3 清除没有使用的对象
有些面向对象语言需要保持对所有对象的跟踪所以需要在对象不再使用的使用来将它从内存中清除管理内存是一个很沉闷的事情而且容易出错JAVA平台允许你创建任意个对象当然会受到系统的限制所以你也不必要老是要将它清除JAVA是在当对象不再使用的使用被清除的这个过程就是所谓的“垃圾收集”
当对象不再有引用的时候对象就会被清除即作为垃圾收集的对象保留在变量中的引用通常在变量超出作用域的时候被清除或者你可以通过设置变量为NULL来清除对象引用这里注意程序中同一个对象可以有多个引用对象的所有引用必须在对象被作为垃圾收集对象清除之前清除
下面讲讲垃圾收集器
JAVA有一个立即收集器它周期性地讲不再被引用的对象从内存中清除这个垃圾收集器是自动执行的虽然有时候你可能想通过调用系统类的gc方法来显性运行垃圾收集程序比如你可能想在创建大量垃圾代码之后或者在需要大量内存代码之前运行垃圾收集器垃圾收集器从内存中清除不再被引用的对象的机制已经被要到了VB.net和C中去了
最后介绍对象finalize方法
在一个对象得到垃圾收集之前垃圾收集器在对象自己调用对象的finalize方法之后给对象一个机会来从内存中清除掉这个过程就是所说的最后处理
绝大部分的程序员不得不关系这个finalize方法的执行在少数情况下程序员不得不执行finalize方法来释放资源
Finalize方法是一个对象类的成员函数它处在JAVA平台类分级结构的顶部而且是所有类的子类这个类重载了finalize方法来完成对象的最后处理工作
4.2字符和字符串
字符数据不管是单一字符或者一系列的字符可以利用java.lang三个类来储存和操作这三个类分别为CharacterString和StringBuffer
JAVA平台包含了三个类在处理字符数据的时候你可以使用这三个类
1. Character这个类的实例可以容纳单一的字符数值这个同样定义了简洁的方法来操作或者检查单一字符数据
2. String这个类用于处理由多个字符组成的不可变数据
3. StringBuffer这个类用于存储和操作由多个字符组成的可变数据
下面开始详细介绍字符
字符类型的对象包含了单一字符数值你可以在对象需要的时候使用一个字符对象取代原始字符类型比如当传递一个字符给改变数值的方法或者当放置一个字符到一个数据结构中比如一个矢量等等下面的例程序CharacterDemo创建了一些字符对象并显示一些信息
public class CharacterDemo {public static void main(String args[]) {Character a = new Character('a');Character a2 = new Character('a');Character b = new Character('b');int difference = a.compareTo(b);if (difference == 0) {System.out.println("a is equal to b.");} else if (difference < 0) {System.out.println("a is less than b.");} else if (difference > 0) {System.out.println("a is greater than b.");}System.out.println("a is "+ ((a.equals(a2)) ? "equal" : "not equal") ________ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ _ __ _____ ________ _______________________________________________________________________________________________________________________________ ________________________________ _______________________________________________________________________________________________________________________________ ________________________________
这个程序的数据结果为
a is less than b.
a is equal to a2.
The character a is lowercase.
4.2字符和字符串
CharacterDemo程序调用以下由Character类提供的构造函数和方法
Character(char)
Character类只是构造函数它创建了一个Character对象包含了由参数提供的数值一旦Character对象被创建它包含的数值就不能改变
compareTo(Character)
一个实例方法比较了由两个Character对象容纳的数值方法被调用的对象即例子中的a以及方法的参数即例子中的b这个方法返回一个整数表明在当前对象中的数值是否大于等于或者小于参数给定的数值如果字母的数值较大那么这个字母就较大
equals(Object)
一个实例方法它比较当前对象容纳的数值和由其它容纳的数值这个方法在两个对象容纳的数值相等的时候返回true
toString()
一个实例方法它将对象转换为字符串结果的字符串在长度上就是一个Character对象并且它包含了有这个Character对象容纳的数值
charValue()
一个实例方法它返回由Character对象容纳的数值作为原始字符数值
isUpperCase(char)
类方法决定是否原始字符数值为大写这个是许多Character类方法中的一个它可以检查或者操作字符数据
下面我们再谈谈于string(字符串)有关的几个问题
1. 为什么有两个String类String 和StringBuffer
2. 创建Strings 和StringBuffers
3. 存取器函数
4. 修改StringBuffers
5. 将对象转换为Strings
6. 将String转换为数字
7. Strings和JAVA编译器
下页接着一个一个加以细讲
1为什么有两个String类String 和StringBuffer
JAVA平台提供了两个类String和StringBuffer它们可以储存和操作字符串即包含多个字符的字符数据这个String类提供了数值不可改变的字符串比如如果你编写一个方法它需要字符串数据并且方法不能修改字符串而要将一个String对象传递给方法而这个StringBuffer类提供的字符串进行修改当你直到字符数据要改变的时候你就可以使用StringBuffer典型地你可以使用StringBuffers来动态构造字符数据比如从一个文件读文本数据因为Strings是常量所以它用起来比StringBuffers更有效并且可以共享因此在允许的情况下还是使用Strings
下面是一个例程StringsDemo它反转了字符串中的字符这个程序同时使用了String和StringBuffer
public class StringsDemo {
public static void main(String[] args) {
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
StringBuffer dest = new StringBuffer(len);
for (int i = (len - 1); i >= 0; i--) {
dest.append(palindrome.charAt(i));
}
System.out.println(dest.toString());
}
}
这个程序的输出为
doT saw I was toD
2创建Strings 和StringBuffers
字符串经常是由双引号括起的一系列字符组成比如当有以下的字符串JAVA平台就创建一个String对象它的数值为Goodluck
"Goodluck"
这个StringsDemo程序使用这个记述来创建字符串它是由palindrome变量引用的
String palindrome = "Dot saw I was Tod";
同样你可以利用其它方法来创建String对象使用new关键字和构造函数String类提供了几个构造函数它允许你提供字符串的初始值它们是使用不同的资源的比如字符的数组字节的数组或者一个StringBuffer
char[] helloArray = { 'h', 'e', 'l', 'l', 'o' };
helloString = new String(helloArray);
System.out.println(helloString);
上面的这段程序输出为hello
你必须使用new来创建一个StringBufferStringsDemo程序创建了SringBuffer它是由dest引用的这里使用了设置缓冲区容量的构造函数
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
StringBuffer dest = new StringBuffer(len);
这个代码创建了一个字符串的缓冲区这个缓冲区的容量等于被palindrome引用的字符串的长度这个确保了为dest只分配一个内存因为它只有包含被复制的字符串那么大的空间通过初始化字符串的缓冲区的容量你最小化必须分配的内存空间这就使得你的代码更加有效因为内存分配是相对高级的操作
3存取器函数
首先讲解一下获得String或者StringBuffer的长度
获得有关对象信息的方法称为存取器函数你可以用在字符串和字符串缓冲区的一个存取器方法是length方法它可以包含在字符串或者字符串缓冲区的字符的个数下面给出一个例程当这个程序执行后len就将等于17
String palindrome = "Dot saw I was Tod";
int len = palindrome.length();
除了lengthStringBuffer类还有一个方法叫做capacity它是返回为字符串缓冲区分配的空间大小而不是空间使用了多少比如字符串缓冲区的容量在StringsDemo例程中被dest引用时它是不能改变的虽然它的长度在每次的循环中增加1给出了在dest里面已经有9个字符之后的容量和长度
一个字符串缓冲区的长度是它包含的字符的个数一个字符串缓冲区的容量是被分配的字符空间的数量而String类没有capacity方法因为字符串不能改变
下面讲讲索引String或者String Buffer得到字符
你可以通过使用charAt存取器来在一个特定的位置索引一个字符串或者字符串缓冲区以得到字符串第一个字符的索引是0最后一个字符的索引是length()-1比如下面的代码在字符串中得到索引为9的字符
String anotherPalindrome = "Niagara. O roar again!";
char aChar = anotherPalindrome.charAt(9);
因为索引是从0开始的所以索引为9的字符为'O'使用charAt方法来在特定的索引下得到字符如图16所示给处了计算字符串最后一个字符的索引的方法你必须将length方法返回的数值减去1才得到最后一个字符的索引
3存取器函数
如果你想从一个字符串或者字符串缓冲区得到多个字符你可以使用substring方法这个substring方法有两个版本如下表所示
方法 |
描述 |
String substring(int) |
返回一个新的字符串它是这个字符串或者字符串缓冲区的一个子串第一个整数参数指定了第一个字符的索引第二个整数参数是最后字符-1的索引这个子串的长度就是第二个整数减去第一个整数如果第二个整数没有那么substring就扩展到整个字符串的结尾 |
String substring(int,int) |
下面的代码是得到字符串"Niagara. O roar again!"从索引为11到索引为15之间的子串结果得到单词"roar"
String anotherPalindrome = "Niagara. O roar again!";
String roar = anotherPalindrome.substring(11, 15);
使用substring方法来得到字符串或者字符串缓冲区的一部分这里一定要记住索引是从0开始的
4修改StringBuffers
reverseIt方法使用StringBuffer的append方法来增加一个字符到dest字符串末尾
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return dest.toString();
}
}
如果这个被增加的字符导致StringBuffer的大小超过当前的容量这个StringBuffer就会为它分配更多的内存因为内存分配示相对高级的操作所以你可以同时初始化StringBuffer的容量为较合理的大小来使你的代码更有效率比如reverseIt方法以初始的容量来构造StringBuffer等于源字符串的长度以只为dest分配一次内存
4修改StringBuffers
用在reverseIt的append方法只是一个StringBuffer方法的其中一种它在StringBuffer末尾增补了一个数据有几种append方法来增补各个类型的数据比如floatintboolean以及对象到StringBuffer末尾但是在增补操作之前数据都要先转换为字符串
下面讲讲插入字符
有时候你可能想插入数据到StringBuffer中间你可以使用StringBuffer的insert方法来实现以下的这个例子就是介绍如何将一个字符串插入到StringBuffer中去的
StringBuffer sb = new StringBuffer("Drink Java!");
sb.insert(6, "Hot ");
System.out.println(sb.toString());
这些代码将输出
Drink Hot Java!
利用StringBuffer的许多insert方法你可以在插入数据之前指定索引在上面的例子中"Hot "要插到"Java"的'J'之前因为索引是从0开始的所以'J'的索引为6为了插入数据到StringBuffer之前使用索引0.为了在StringBuffer的末尾插入数据你可以将所以等于StringBuffer的长度或者使用append方法来增补
下面讨论设置字符
另外一个有用的StringBuffer修改量是setCharAt它在StringBuffer的指定位置用参数列表中的指定字符来替换字符SetCharAt在你想重新使用StringBuffer的时候是很有用的
5将对象转换为Strings
首先介绍toString方法
经常需要将一个对象转换为String因为你需要将它传递给方法而这个方法只接收String数值前面我们使用reverseIt方法使用了StringBuffer的toString方法来将StringBuffer转换为String对象
class ReverseString {
public static String reverseIt(String source) {
int i, len = source.length();
StringBuffer dest = new StringBuffer(len);
for (i = (len - 1); i >= 0; i--) {
dest.append(source.charAt(i));
}
return dest.toString();
}
}
所有的类从对象类继承了toString并且在java.lang包的许多类重载了这个方法来提供一个很有意义的方法
下面说说valueOf方法
简单起见String类提供了一个类方法valueOf你可以使用valueOf来转换不同类型的变量为String比如为了打印pi的数值你可以这样来编写代码
System.out.println(String.valueOf(Math.PI));
6将String转换为数字
String类本身没有提供任何的方法来转换String为浮点型整型或者其它的数值类型但是"type wrapper"类的四种类型integerDoubleFloat和Long提供了一个类方法valueOf来将String转换为那个类型的对象举例如下
String piStr = "3.14159";
Float pi = Float.valueOf(piStr);
7Strings和JAVA编译器
JAVA编译器使用了String和StringBuffer类来处理字符串和字符串的连接
下面先讲讲Strings
在JAVA种你可以在双引号之间指定字符串如
"Hello World!"
你可以在任何使用String对象的地方使用字符串比如System.out.println接收一个String参数所以你可以在这里使用字符串了
System.out.println("Might I add that you look lovely today.");
同样你可以使用从字符串直接使用String方法比如
int len = "Goodbye Cruel World".length();
因为编译器自动为字符串创建了一个新的String对象你可以使用字符串来初始化一个String对象
String s = "Hola Mundo";
上面的这条语句等价于下面的语句
String s = new String("Hola Mundo");
最后讲讲字符串的连接以及+运算符
在JAVA种你可以使用+来连接字符串比如
String cat = "cat";
System.out.println("con" + cat + "enation");
这条语句看起来有点问题因为我们直到Strings是不能改变的但是在这条语句的背后编译器却使用了StringBuffers来执行字符串的连接上面的语句实际上编译为
String cat = "cat";
System.out.println(new StringBuffer().append("con").
append(cat).append("enation").toString());
同样你可以使用+运算符号来增不一个String
System.out.println("Java's Number " + 1);
在JAVA中在执行字符串连接操作之前编译器讲非String的数值如本例的整数1转化为一个String对象
4.3 数字
为了处理数值类型你可以使用Number类这个Number类是所有在JAVA平台中所有数字类的父类它的子类包括FloatInterger等等
本节教程将讨论java.lang和它的子类中的Number类特别地这节教程要讨论为什么你需要这些类并指出通用的方法和类变量以及向你介绍如何将实例转换为字符串
此外这节教程还讨论你需要的其它类来处理数字比如如果你需要以某个特殊格式来显示数字的时候你可以使用在java.test中的NumberFormat和DecimalFormat类来格式化它同时java.lang中的Math包含了类方法来执行数学函数这个类还有用于三角函数指数函数等等的方法
数字类包含
1. Number 这个抽象类Number是ByteDoubleFloatIntegerLong和Short的父类Number的子类必须提供将数值转换为bytedoublefloatintlong以及short的方法
2. Byte这个类是Number的子类它是用于字节数值的标准wrapper
3. Double这个Double类在对象中包装了原始类型doubleDouble类的对象包含单一类型double的对象另外这个类提供了几个用于将double转换为String或者将String转换为double的方法
4. Float 这个Float类在对象中包装了原始类型FloatFloat类的对象包含单一类型Float的对象另外这个类提供了几个用于将Float转换为String或者将String转换为Float的方法
5. Integer这个Integer类在对象中包装了原始类型IntegerInteger类的对象包含单一类型Integer的对象另外这个类提供了几个用于将Integer转换为String或者将String转换为Integer的方法
6. Long 这个Long类在对象中包装了原始类型LongLong类的对象包含单一类型Long的对象另外这个类提供了几个用于将Long转换为String或者将String转换为Long的方法
7. Short这个类是用于short数值的标准wrapper
4.3 数字
8. BigDecimal不可变的任意精度的有符号的十进制数字BigDecimal包含了任意精度的整型数和非负的32位的整型数BigDecimal 提供了用于基本算术运算比较格式转换等等的操作这个BigDecimal类给了用户对舍入行为的完全控制并且强迫用户为舍弃精度的操作显性指定一个舍入行为基于这个目的有八种舍入模式对BigDecimal有两种类型的操作比例/舍入操作和十进制点移动操作比例/舍入操作返回一个BigDecimal它的
数值大约或者精确地等于运算对象但是它的数值范围要指定也就是说它们可以对它的数值最小的影响来增加或者减少数字的精度十进制点移动操作movePointLeft和movePointRight返回一个由运算对象创建的BigDecima它是通过在指定方向和指定距离来移动十进制点的为了达到简短和清晰的目的伪代码用在了BigDecimal方法的描述中比如伪代码表达式i j就代表了“BigDecimal的数值是由BigDecimal i加上BigDecimal j的数值”而伪代码表达式i==j代表“当且仅当BigDecimal i的数值与BigDecimal j相同的时候才为true”其它的伪代码表达式也有类似的解释
BigInteger不可变任意精度的整型数BigInteger为所有的Java原始整型操作以及所有在java.lang.Math中的相关方法提供相似的操作另外BigInteger伪模数运算GCD计算位操作等等提供了运算算术运算的语义模仿了定义在JAVA语言规范中的Java的整型算术运算比如如果除数为0就会导致ArithmeticException算术异常等等所有在规范中的溢出都将被忽略因为BigIntegers将尽量适应运算结果的需要 而移位操作扩展了Java的移位操作它允许负方向的移位以负的位距来往右移就相当于往左移位而无符号的右移运算符>>>这里就不再使用 而按位逻辑操作跟Java的按位操作是相似的比较运算执行有符号的整型比较它跟Java的是相似的 模数算术运算提供计算余数执行乘幂等方法这些方法总是返回一个非零的结果介于0到-1之间为了简洁和清晰的目的伪代码也用在BigInteger方法中这个伪代码表达式(i j)代表了“等于BigInteger i加上BigInteger j的BigInteger数值”而伪代码表达式i==j代表了“当且仅当BigInteger i等于BigInteger j的时候才返回true”
4.3 数字
其它的数据类型
1. Boolean 这个Boolean类在对象中包装了原始类型BooleanBoolean类的对象包含单一类型double的对象另外这个类提供了几个用于将Boolean转换为String或者将String转换为Boolean的方法
2. Character 这个Character类在对象中包装了原始类型CharacterCharacter类的对象包含单一类型Character的对象另外这个类提供了几个用于将字符由小写转换为大写或者由大写转换为小写的方法
3. Void这个Void类是一个占位符类它保留对代表原始JAVA类void的引用
非基本算术类型Math
这个Math类包含了用于执行数值操作的方法比如初步的指数对数平反根以及三角函数不象StrictMath类中的有些数值函数这个Math类的所有函数不是定义为返回bit-for-bit相同的结果这允许更好的执行因为严格的再现情已经不需要了
缺省地许多Math函数简单代表了在StrictMath中的等价函数代码发生器支持使用特殊平台的本地库或者微处理器指令以提供性能更高的Math函数实行这样高性能的执行必须符合Math的规范
4.4 数组
在任何的编程语言中数组都是一个重要的数据结构数组是一个固定长度的结构它存储多个相同类型的数值你可以在数组中集合相同类型的数值数组直接被JAVA编程语言所支持所以没有一个数组类数组是一个Object类的一个不明显的扩展所以你可以指定一个数组给一个类型定义为Object的变量
JAVA平台集合了它的类到功能包中不是编写你自己的类你可以使用有平台提供的类这节教程中要讨论的绝大多数的类都是java.lang包的成员函数所有java.lang中的类对你的程序都是有效的
数组的长度在数组创建的时候就已经确定一旦创建以后数组就有了固定长度的结构.
数组元素就是数组中的一个数值可以通过数组中的位置来访问它
如果你想在一个结构中存储不同类型的数据或者如果你需要一个长度可以动态改变的结构可以使用Collection比如Vector而不用数组
本教程中将讨论以下的几个方法
1. 创建和使用数组合
2. 对象的数组
3. 数组的数组
4. 复制数组
4.4.1 创建和使用数组合
以下是个简单的程序ArrayDemo它创建了一个数组并放置了一些数值在上面然后进行显示
public class ArrayDemo {
public static void main(String[] args) {
int[] anArray; // 声明一个整型数组
anArray = new int[10]; // 创建一个整型数组
// 为每一个数组元素赋值并打印出来
for (int i = 0; i < anArray.length; i++) {
anArray[i] = i;
System.out.print(anArray[i] + " ");
}
System.out.println();
}
}
这节教程覆盖了以下这些内容
1. 声明一个变量来引用一个数组
2. 创建一个数组
3. 访问数组元素
4. 获得数组的大小
5. 数组初始化程序
4.4.1 创建和使用数组合
1声明一个变量来引用一个数组
以下的代码是声明一个数组变量
int[] anArray; // 声明整型的数组
象声明其它类型的变量一个数组声明有两个组件数组的类型和数组的名字数组的类型是这样来写的type[]其中type可以是floatbooleanObjectString等而[]代表了这是一个数组这里记住在数组中的所有元素都是相同类型的上面的例程中使用了int[]所以数组anArray就将容纳整型数据了以下是声明其它类型的数组
float[] anArrayOfFloats;
boolean[] anArrayOfBooleans;
Object[] anArrayOfObjects;
String[] anArrayOfStrings;
就象声明其它类型的变量声明数组变量也没有为数组元素分配任何内存所以必须在引用数组之前被数组赋值
4.4.1 创建和使用数组合
2创建一个数组
你可以使用JAVA的new运算符来创建一个数组下面的语句是为10个整型元素分配一个数组内存
anArray = new int[10]; // create an array of integers
总得说来当创建数组的时候你可以使用new操作符后面跟着数组元素的数据类型然后就是用方括号[和]括起来的元素的数目格式如下所示
new elementType[arraySize]
如果在上面的例程中new语句没有的话编译器将打印除一个如下的错误是的编译发生错误
ArrayDemo.java:4: Variable anArray may not have been initialized变量anArray 可能还没有初始化
3访问数组元素
既然已经给数组分配了内存我们来为数组元素赋值吧
for (int i = 0; i < anArray.length; i++) {
anArray[i] = i;
System.out.print(anArray[i] + " ");
}
这部分的代码给出了怎样引用一个数组元素或者示为它赋予一个数值或者访问数值在方括号之间的数值指示了要访问的元素的索引在JAVA中数组的索引是从0开始的并且以-1结束
4.4.1 创建和使用数组合
4获得数组的大小
为了获得数组的大小你可以使用下面的代码
arrayname.length
这里还提醒一下JAVA的新手可能会在length后面加一个圆括号(和)这样是否错误的因为length不是一个方法Length是由JAVA平台为所有数组提供的一个属性
在上面的例程中的for循环在anArray的每一个元素进行循环并给每个元素赋值这个for循环使用了anArray.length来决定什么时候终止循环
5数组初始化程序
JAVA编程语言为创建和初始化数组提供了简捷的语法一下是这个语法的例子
boolean[] answers = { true, false, true, true, false };
数组的length长度是由大括号{和}之间的数值的数目决定的
4.4.2 对象的数组
数组可以保留引用类型你可以用创建原始类型数组的方法来创建这样一个数组以下是一个小的程序ArrayOfStringsDemo,它创建了一个数组包含了三个String对象并且将这三个字符串以小写字母的形式打印出来
public class ArrayOfStringsDemo {
public static void main(String[] args) {
String[] anArray = { "String One", "String Two", "String Three" };
for (int i = 0; i < anArray.length; i++) {
System.out.println(anArray[i].toLowerCase());
}
}
}
这个程序中用单一的语句创建和操作了数组但是你可以创建一个数组而不用在里面放置任何元素这对于JAVA新手将是一个容易出错的地方假如有以下的代码
String[] anArray = new String[5];
一旦这条语句被执行了数组anArray就存在了并且有充足的空间来容纳5个String对象但是数组并不包含任何的字符串它是空的程序必须显性地创建字符串并将他们放置到数组中这本来示很显然的事情但是许多JAVA的新手会以为上面的语句已经创建了数组并创建了5和个空的字符串在里面了这样他们会象如下进行编写代码结果只会导致一个NullPointerException的异常错误
String[] anArray = new String[5];
for (int i = 0; i < anArray.length; i++) {
// 错误下面的行将导致一个运行错误
System.out.println(anArray[i].toLowerCase());
}
4.4.3 数组的数组
数组可以容纳数组以下的例程ArrayOfArraysDemo创建一个数组并使用一个初始化程序来包含四个子数组
public class ArrayOfArraysDemo {
public static void main(String[] args) {
String[][] cartoons =
{
{ "Flintstones", "Fred", "Wilma", "Pebbles", "Dino" },
{ "Rubbles", "Barney", "Betty", "Bam Bam" },
{ "Jetsons", "George", "Jane", "Elroy", "Judy", "Rosie", "Astro" },
{ "Scooby Doo Gang", "Scooby Doo", "Shaggy", "Velma", "Fred", "Daphne" }
};
for (int i = 0; i < cartoons.length; i++) {
System.out.print(cartoons[i][0] + ": ");
for (int j = 1; j < cartoons[i].length; j++) {
System.out.print(cartoons[i][j] + " ");
}
System.out.println();
}
}
}
这里注意所有的子数组都有不同的长度子数组的名字是否cartoons[0]和cartoons[1]等等
4.4.3 数组的数组
跟对象的数组一样你必须显性地在数组中创建子数组因此如果你不使用初始化程序的话你必须编写一些的代码
public class ArrayOfArraysDemo2 {
public static void main(String[] args) {
int[][] aMatrix = new int[4][];
for (int i = 0; i < aMatrix.length; i++) {
aMatrix[i] = new int[5]; //create sub-array
for (int j = 0; j < aMatrix[i].length; j++) {
aMatrix[i][j] = i + j;
}
}
//打印
for (int i = 0; i < aMatrix.length; i++) {
for (int j = 0; j < aMatrix[i].length; j++) {
System.out.print(aMatrix[i][j] + " ");
}
System.out.println();
}
}
}
你必须在创建数组的时候为主数组指定长度你可以让子数组的长度未指定直到创建了它们
4.4.4 复制数组
使用系统的arraycopy方法来有效地从一个数组复制数据到另外一个数组中去这个arraycopy方法需要5个参数
public static
void arraycopy(Object source,
int srcIndex,
Object dest,
int destIndex,
int length
其中两个Object参数指定了从哪个数组复制以及要复制到哪个数组三个整型参数指示了每个源数组和目标数组的开始位置以及要复制的元素的数目
4.4.4 复制数组
下面的程序ArrayCopyDemo使用了arraycopy方法来从copyForm数组复制元素到copyTo数组
public class ArrayCopyDemo {
public static void main(String[] args) {
char[] copyFrom = { 'd', 'e', 'c', 'a', 'f', 'f', 'e',
'i', 'n', 'a', 't', 'e', 'd' };
char[] copyTo = new char[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
System.out.println(new String(copyTo));
}
}
程序中调用arraycopy方法开始复制所以为2的源数组元素因为数组的索引是从0开始的所以复制是从第3个元素开始的即从'c'开始Arraycopy方法将复制的元素复制到目标数组中目标数组的索引是从0开始的即复制到目标数组的第一个元素开始这个程序一共复制了7个元素'c' 'a 'f' 'f' 'e' 'i'和'n'实际上arraycopy方法是从"decaffeinated"复制"caffein"
这里注意目标数组必须在调用arraycopy之间分配内存而且这个内存空间必须足够大以容纳被复制的数据
第五章 类和继承
在前面的教程面向对象编程概念的内容中你学习到了面向对象的概念现在我们来学习如何将这些概念用在JAVA中在这部分教程中将介绍
1. 创建类
2. 管理继承
3. 执行嵌套类
5.1 创建类
这节中将介绍一个更大的类Stack的完整描述并且描述一个从它继承而来的对象的生命周期的类的所有组件首先介绍构造函数然后介绍成员变量和方法
既然我们前面已经介绍了如何创建和使用对象以及怎样将对象从内存中清除现在该是我们介绍怎样编写类的时候了这一节中将通过一个执行LIFO后入先出堆栈的小例子来介绍类的组件
堆栈的执行使用了另外的对象即Vector来存储它的元素Vector是一个对象的数组在需要空间的时候它为新对象分配内存空间这个Stack类是通过使用Vector来存储它的元素的但是它对Vector限定了LIFO也就是说你可以只增加元素并且从堆栈的顶部移出元素
5.1.1 类的声明
组成类执行的两个主要组件类声明和类实体类声明定义了类的名字以及其它的属性为Stack类的类声明是相对地简单它指示了类为public并且它地名字为Stack
类声明可能的组件以及描述了它们的目的必须的组件是class关键字所有其它的组件是可选的如果你没有声明这些可选的组件JAVA编译器就将给出缺省值没有执行接
口的对象的nonpublicnonabstractnofinal子类
下面对每个类的组件给出更详细的介绍
public 这个public关键字声明了类可以在其它任何的类中使用
abstract 声明了这个类不能被实例化
Final声明了类不能被继承即没有子类了
class NameOfClass 关键字class指示编译器类的声明以及类的名字是NameOfClass
extends Super 这个extends 子句认为Super是类的父类因此在类的分级结构中插入了类
implements Interfaces 为了声明类可执行一个或多个接口可以使用关键字implement并且在其后面给出由类执行的接口的名字的列表它们是以逗号分隔的
5.1.2 类实体
类实体是跟在类声明的后面它是嵌入在大括号{和}中间的类实体包含了所有实例变量和类变量的声明另外类实体还包含了所有实例方法和类方法的声明
5.1.3 为类提供构造函数
类可能包含一个或者多个的构造函数它提供了从类创建的对象的初始化
所有的JAVA类都有构造函数它用来对新的对象进行初始化构造函数与类的名字是相同的比如Stack类的构造函数的名字为Stack而Rectangle类的构造函数的名字为RectangleThread类的构造函数的名字为Thread下面给出Stack类的构造函数
public Stack() {
items = new Vector(10);
}
JAVA支持对构造函数的重载这样一个类就可以有多个构造函数所有的构造函数的名字都是相同的下面是定义在Stack的另外的构造函数这个构造函数是根据它的参数来初始化堆栈的大小
public Stack(int initialSize) {
items = new Vector(initialSize);
}
从上面可以看出两个构造函数都有相同的名字但是它们有不同的参数列表编译器会根据参数列表的数目以及类型了区分这些构造函数的典型地构造函数使用它的参数来初始化新的对象状态当创建对象的时候选择构造函数要看看它的参数是否最好反映了你想初始化的新对象
5.1.3 为类提供构造函数
根据你传递给构造函数参数的数目和类型编译器可以决定要使用哪个构造函数下面的代码编译器是认识的它是使用了单一的整型参数
new Stack(10);
相似地当你编写下面代码的时候编译器选择了没有参数的构造函数或者缺省的构造函数
new Stack();
当你编写自己的类的时候你不必为它提供构造函数系统会自动提供缺省的构造函数这个缺省的构造函数不会完成任何事情因此如果你想进行一些初始化的时候你就要为类编写一些构造函数了
下面是Thread的子类的构造函数它执行动画设置了一些缺省的数值比如帧速度图片的数目然后装载图片
class AnimationThread extends Thread {
int framesPerSecond;
int numImages;
Image[] images;
AnimationThread(int fps, int num) {
super("AnimationThread");
this.framesPerSecond = fps;
this.numImages = num;
this.images = new Image[numImages];
for (int i = 0; i <= numImages; i++) {
. . .
// 以下转载图片
. . .
}
}
. . .
}
5.1.3 为类提供构造函数
实际上构造函数的实体跟方法的实体是相似的也就是说它包含了局部变量声明循环以及其它的语句但是AnimationThread构造函数中的第二行你在方法的构造函数是不会看到的
super("AnimationThread");
这一行调用了一个由父类AnimationThread提供的构造函数Thread这个特定的Thread构造函数有一个String参数它用来设置Thread的名字经常构造函数想利用编写在父类中的初始化代码实际上有些类必须调用它们的父类构造函数来使对象正确完成任务
父类的构造函数必须是子类构造函数的第一条语句对象必须首先执行高层次的初始化你可以在构造函数生命处通过使用一个访问的指定来指定什么其它对象可以创建类的实例访问的指示如下
private没有其它类可以实例化这个类这个类可能包含public类方法这些方法可以构造一个对象并返回但是其它的类不行
protected 只有子类和在相同包中的类可以创建它的实例
public 任何类都可以创建它的实例
没有说明的时候只有在相同包中的类可以创建它的实例
构造函数提供了一个初始化新对象的方法初始化实例和类成员提供了对初始化类和由类的新对象进行初始化的方法
5.1.4 声明成员变量
类的状态是由它的成员变量给出的你可以在类的实体中生命一个类的成员变量典型地你可以在声明它的方法之前声明类的变量虽然这不不是必要的
classDeclaration {
member variable declarations
method declarations
}
这里注意为了声明变量类的成员声明必须在类实体中而不是在方法的实体中在方法的实体中声明的变量都是局部变量
5.1.5 执行方法
众所周知对象的行为是由它的方法来执行的其它对象通过调用它的方法来访问对象在这一小节中我们将一起来为JAVA类编写方法在JAVA众你可以在类的实体中定义类的方法用于执行一些行为典型地你可以在类实体中的变量之后声明类的方法虽然这也不是必须的
给出了Stack的push方法的代码这个方法是一个进栈操作它将一个Object作为参数放置到堆栈的顶部然后返回它
就象一个类一样方法也有两个主要部分方法声明和方法实体方法声明了方法的所有属性比如访问等级返回类型方法名字以及参数
方法实体中有实现方法行为的代码它包含了执行方法的JAVA指令
下面介绍一下方法的声明
一个方法的声明包括方法的名字以及返回的类型指定了由方法返回的数据类型
returnType methodName() {
. . .
}
这个方法声明是非常基本的方法有许多其它的属性比如参数访问控制等等
那怎样将信息传递给方法呢
可能在绝大多数情况下使用的可选方法声明的组件是方法的参数类似于其它编程语言的函数JAVA方法接收调用者传递来的参数这些参数就提供了从方法作用域外部的信息给方法
5.1.6 控制对类成员的访问
当我们声明JAVA类成员的时候我们可以允许或者不允许其它类型的对象通过访问指示来访问这些成员
其中一个是类可以保护它们的成员变量和方法受其它对象的访问也许你会问这很重要吗是的假如你编写一个类它是对数据进行查询而这个数据库包含了公司的各种秘密信息所以就有必须进行保护
在JAVA中你可以在声明类变量和方法的时候使用访问指示来保护它们JAVA语言支持对成员变量和方法的四个访问等级private protected public以及留着不指定的等级
下面的表给出来每一种访问指示的访问等级
访问指示 |
类 |
子类 |
包 |
所有 |
|||||
Private |
X |
||||||||
Protected |
X |
X* |
X |
||||||
Public |
X |
X |
X |
X |
|||||
Package |
X |
X |
|||||||
第一列给出了是否类本身可以访问它的成员从上表可以知道类总是可以访问它自己的成员第二列给出是否类的子类可以访问它的成员第三列给出是否在相同包中的类可以访问成员第四列给出是否所有的类可以访问成员
这里注意在Protected/子类交叉的地方有一个'*'这个是特殊的访问情况后面的教程
会给出详细的介绍
5.1.6 控制对类成员的访问
下面对各种访问等级详细说明
1. Private
绝大多数代用限制性的访问等级是privatePrivate成员只能被它所定义的类所访问如果外部访问这个变量就将导致前后矛盾的状态或者如果private的方法被外部类所调用就会使得运行的程序或者对象的状态处于不良的状态Private成员就象一些不可告人的秘密
为了声明一个private成员只需在声明的时候加入private关键字即可下面的类包含了一个private成员变量和一个Private方法
class Alpha {
private int iamprivate;
private void privateMethod() {
System.out.println("privateMethod");
}
}
这个Alpha类型的对象可以检查或者修改imaprivate变量以及可以调用privateMethod方法但是其它类型的对象就不行比如以下的Beta类就不能访问iamprivate变量或者调用privateMethod因为Beta不是Alpha类型的
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iamprivate = 10; // 非法
a.privateMethod(); // 非法
}
}
5.1.6 控制对类成员的访问
1. Private
当一个类试图访问一个它不能访问的成员变量的时候编译器就会打印出错误信息如下病拒绝继续编译程序
Beta.java:9: Variable iamprivate in class Alpha not
accessible from class Beta. //在Alpha类中的imaprivate变量不能从Beta类中进行访问
a.iamprivate = 10; // 非法
^
1 error //一个错误
同时如果你的程序试图访问一个不能访问的方法就将导致如下的编译器错误
Beta.java:12: No method matching privateMethod()
found in class Alpha. //在Alapha类中没有匹配的方法privateMethod()
a.privateMethod(); // 非法
1 error //一个错误
JAVA的新手可能会问是否一个Alpha对象可以访问另外一个Alpha对象的private成员
下面给出具体例子来进行解释假如Alpha类包含了一个实例方法它比较当前的Alpha对象(this)以及另外一个对象的iamprivate变量
class Alpha {
private int iamprivate;
boolean isEqualTo(Alpha anotherAlpha) {
if (this.iamprivate == anotherAlpha.iamprivate)
return true;
else
return false;
}
}
结果是相当的合法所以相同类型的对象可以访问其它的private成员这是因为访问限制只是在类别层次类的所有实例而不是在对象层次类的特定实例上
5.1.6 控制对类成员的访问
2Protected
protected允许类本身子类以及在相同包中的类访问这个成员在允许类的子类访问而杜绝其它不相关的类的访问的时候可以使用protected访问等级Protected成员就象家庭秘密家里人知道无所谓但是就不让外人知道不是有“家臭不可外扬”一说吗虽然这里不是什么“家臭”但它是受protected的成员
为了声明一个protected成员使用关键字protected首先让我们看看protected是怎样影响在相同包内的类的假如上面的那个Alpha类现在被定义在一个package包Greek内它有一个protected成员变量和一个protected方法
package Greek;
public class Alpha {
protected int iamprotected;
protected void protectedMethod() {
System.out.println("protectedMethod");
}
}
现在假设类Gamma也声明为Greek包的一个成员不是Alpha的子类Gamma类可以合法访问Alpha对象的iamprotected成员变量并且可以合法调用它的protectedMethod
package Greek;
class Gamma {
void accessMethod() {
Alpha a = new Alpha();
a.iamprotected = 10; // 合法
a.protectedMethod(); // 合法
}
}
5.1.6 控制对类成员的访问
2Protected
下面我们来研究一下protected是怎样影响Alpha的子类的访问的
首先介绍一个新的类Delta它是来由Alpha继承而来的但是它处在不同的包中即Latin这个Delta类可以访问iamproted和protectedMethod不仅可以访问Delta类的对象而且可以访问它的子类Delta类不能访问Alpha类型的对象中的iamprotected或者protectedMethod在下面代码中的accessMethod试图访问在Alpha类型对象中的imaprotected成员变量它是不合法的而访问Delta类型对象则是合法的相似地accessMethod 试图调用Alpha对象的protectedMethod也是合法的
package Latin;
import Greek.*;
class Delta extends Alpha {
void accessMethod(Alpha a, Delta d) {
a.iamprotected = 10; // 非法
d.iamprotected = 10; // 合法
a.protectedMethod(); // 非法
d.protectedMethod(); // 合法
}
}
综上所述如果一个类是里面包含protected成员的类的子类或者它们处在同一个包中那么这个类就可以访问protected成员了
5.1.6 控制对类成员的访问
3Public
最简单的访问指示是public在任何类中任何包中都可以访问了的public成员只有在外部对象使用的时候不会产生不良的结果的时候才声明为public成员为了声明一个public成员你可以使用public关键字如下
package Greek;
public class Alpha {
public int iampublic;
public void publicMethod() {
System.out.println("publicMethod");
}
}
现在让我们重新编写Beta类再将它放置到不同的包中并且要确保它跟Alpha毫无关系
package Roman;
import Greek.*;
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iampublic = 10; // legal
a.publicMethod(); // legal
}
}
从上面的代码段你可以看出Beta可以合法地检查和修改在Alpha类中地iampublic变
量并且可以合法地调用publicMethod
4Package
如果你不显性设置成员访问其它级别的成员的时候使用package访问级这个访问级允许在相同包中的类访问成员访问的级别是假设在相同包中的类是互相信任的朋友比如以下修改过的Alpha类声明了一个单一包访问的成员变量和方法Alpha处在Greek包中
package Greek;
class Alpha {
int iampackage;
void packageMethod() {
System.out.println("packageMethod");
}
}
这个Alpha类同时可以访问iampackage和packageMethod另外所有定义在和Alpha相同的类也可以访问iampackage和packageMethod假如Alpha和Beta都是定义为Greek包的一部分
package Greek;
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iampackage = 10; // 合法
a.packageMethod(); // 合法
}
}
如上所述Beta可以合法访问iampackage和packageMethod
5.1.7 理解实例和类成员
Java类可以包括两种类型的成员实例成员和类成员这一小节将向你介绍如何生命这两种成员以及如何使用它们
你可以如下来声明一个成员变量如Myclass中的aFloat
class MyClass {
float aFloat;
}
你声明了一个实例变量每次你创建类的实例的时候系统就会为这个实例创建一个类实例的副本你可以对象中访问对象的实例变量
实例变量跟类变量是有区别的系统为每个类分配类变量而不管类创建的实例有多少当第一次调用类的时候系统为类变量分配内存所有的实例共享了类的类变量的相同副本你可以通过一个实例或者类本身来访问类变量
方法是相似的类有实例方法和类方法实例方法是对当前对象实例变量进行操作的而且访问类变量另外一个方面类方法不能访问定义在类中的实例变量除非它们创建了一个新的对象以及通过对象访问它们同时类方法可以在类中被调用你不必一个实例来调用一个类方法
缺省地除非其它指定定义在类中地成员是实例成员定义在下面的类有一个实例变量整型数x以及两个实例方法x和setX是让其它对象来设置和查询x的数值的
class AnIntegerNamedX {
int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
每次你从类实例化新对象的时候你就得到了类实例变量的一个新副本这些副本跟新对象是联系在一起的因此每次你实例化一个新AnIntegerNamedX对象的时候你就得到了一个和AnintegerNamedX对象有联系的x的新副本
5.1.7 理解实例和类成员
类的所有实例共享了一个实例方法的相同实现AnIntegerNamedX的所有实例共享了x和setX的相同实现这里注意两个方法x和setX都引用了对象的实例变量x但是你可能会问如果AnIntegerNamedX的所有实例共享了x和setX的相同实现这不会模棱两可吗答案当然是不是在实例方法中实例变量的名字是引用了当前对象的实例变量因此在x和setX中x就等价于这个x不会产生模棱两可的情况
AnIntegerNamedX外部的对象如果想访问x必须通过AnIntegerNamedX的一个特定实例来实现假如下面的代码段是在其它对象的方法中它包含了两种不同AnIntegerNamedX类型的对象并且设置x为不同的数值然后显示出来
. . .
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
. . .
这里值得注意代码使用setX来设置myX的x数值而直接赋值给anotherX.x不管用哪种方法代码是操作了两种不同的x的副本一个是包含在myX对象中另外一个是包含在anotherX对象中它们的输出为
myX.x = 1
anotherX.x = 2
上面的例子说明了类AnIntegerNamedX的每一个实例都有它自己的实例变量x的副本并且每个x都有不同的数值
当声明一个成员变量的时候你可以指定变量是一个类变量而不是一个实例变量相似地你可以指定一个方法为类方法而不是一个实例方法第一次调用
变量定义的类的时候系统就会为类变量创建一个副本类的所有实例共享了类变量的相同副本类方法只能操作类变量它们不能访问定义在类中的实例变量
5.1.7 理解实例和类成员
为了指定成员变量是一个类变量你可以使用static关键字比如让我们来修改以下AnIntegerNamedX类让x变量成为一个类变量
class AnIntegerNamedX {
static int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
结果输出为
myX.x = 2
anotherX.x = 2
结果输出相同是因为x现在是一个类变量了因此就只有一个变量的副本了它被AnIntegerNamedX的所有实例所共享包括myX和anotherX当你在任一个实例中调用setX的时候你就改变了AnIntergerNamedX所有实例的数值
当定义一个方法的时候你可以指定方法为类方法而不是实例方法类方法只能操作类变量而不能访问定义在类中的实例变量
5.1.7 理解实例和类成员
为了指定方法为一个类方法你可以在方法声明的地方使用static关键字现在我们改变一下AnIntegerNamedX类比如它的成员变量x为实例变量以及它的两个方法为类方法
class AnIntegerNamedX {
int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
当我们编译这个修改过的AnInterNamedX的时候编译器就给出以下的错误
AnIntegerNamedX.java:4: Can't make a static reference to
nonstatic variable x in class AnIntegerNamedX.
return x;
^
出现这个错误的原因是类方法不能访问实例变量除非方法首先创建AnIntergerNamedX的实例并且通过它来访问变量
下面再修改AnIntegerNamedX使x变量为一个类变量
class AnIntegerNamedX {
static int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
现在类就可以编译了它是在创建AnIntegerNamedX两个实例之前设置了它们的x数值并打印出来
myX.x = 2
anotherX.x = 2
实例成员和类成员之间的另外不同点是类成员可以在类本身中访问你不必实例化一个类来访问类成员下面再对上面的代码进行修改
. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = " + AnIntegerNamedX.x());
. . .
这里注意你不再需要创建myX和anotherX你可以直接从AnIntegerNamedX中设置x并检索x而你不能这样来处理实例成员你可以从对象中只调用实例方法你可以从类的实例或者从类本身访问类变量和方法
5.1.7.1初始化实例和类成员
你可以使用static初始化程序和实例初始化程序在你在类很中声明它们的时候来为类和实例成员提供初始数值
class BedAndBreakfast {
static final int MAX_CAPACITY = 10;
boolean full = false;
}
但是这种初始化的形式有以下的限制
1. 初始化程序只可以执行初始化这可以通过赋值语句来表达
2. 初始化程序不能调用任何导致异常的方法
3. 如果初始化程序调用一个产生异常的方法就不能进行错误恢复了
如果你有些初始化不能在初始化程序中完成因为上面的这些限制你就不得不在其它地方放置初始化代码为了初始化类成员在一个static初始化块中放置初始化代码为了初始化实例成员就在构造函数中放置初始化代码
5.1.7.2 使用static初始化块
下面是一个static初始化块的例子这里注意errorStrings资源束必须在static初始化块中进行初始化这是因为错误恢复必须在束备有找到的时候才进行同时errorString是一个类成员它对于构造函数中的初始化没有什么用如前面的例子所述的一个static初始化块是以static关键字开始的它是一个利用大括号{和}嵌入的普通JAVA代码
类可以有许多初始化块并且可以出现在类实体的任何地方系统保证static初始化块和static初始化程序按出现在源代码中的顺序被调用
5.1.7.3 初始化块实例变量
如果你想初始化实例变量并且不能在初始化块中进行初始化的时候你可以来类的构造函数中进行初始化假如erroeStrings束是一个实例变量而不是一个类变量你可以使用下面的代码来进行初始化
import java.util.ResourceBundle;
class Errors {
ResourceBundle errorStrings;
Errors() {
try {
errorStrings = ResourceBundle.
getBundle("ErrorStrings");
} catch (java.util.MissingResourceException e) {
// 这里是错误恢复代码
}
}
}
初始化errorStrings的代码现在处在类的构造函数中
5.1.7.3 初始化块实例变量
有时候类包含了许多构造函数并且每个构造函数允许调用者为新对象的不同实例变量提供不同的初始数值比如java.awt.Rectangle就有三个构造函数
Rectangle();
Rectangle(int width, int height);
Rectangle(int x, int y, int width, int height);
没有参数的构造函数没有让调用者提供初始数值而另外的两个构造函数让用户设置初始数值大小与原点然而所有的实例变量都必须初始化在本例子中构造函数完成了这些初始化工作比如以上的这三个构造函数是这样初始化的
Rectangle() {
this(0,0,0,0);
}
Rectangle(int width, int height) {
this(0,0,width,height);
}
Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
Java语言支持实例初始化块但是它不能用在无记录类不能声明构造函数中使用构造函数比较好的原因有
1. 所有的初始化代码放置在一起这样使得代码更容易维护和阅读
2. 缺省值可以显性地处理
程序员广泛使用构造函数即使JAVA的新手也是一样而实例初始化程序可能导致有些程序员对代码的阅读感到困难
5.2 管理继承
回顾前面的教程extends子句声明了你的类是其它类的子类你可以为你的类指定为父类JAVA不支持多个类支持即使你从你的声明中去掉extends子句你的类也可以为父类所以在JAVA中的每个类都有一个或者只有一个直接的父类
如图27所示是一个最顶端的类所有的类都是从这个类继承而来的这个Object类是定义在java.lang
这个Object类定义和执行了在JAVA系统需要的所有类的行为它是所有类的父类
5.3 执行嵌套类
JAVA编程语言可以让你在另外一个类中定义一个一个类即嵌套类内部的内就是典型的嵌套类
JAVA可以让你定义一个类作为另外一个类的成员比如
class EnclosingClass{
. . .
class ANestedClass {
. . .
}
}
你可以使用嵌套类来放映并且强制两个类之间的关系当嵌套类只有在嵌入以后才有意义的时候或者当它依靠于嵌入类的函数的实话你应该在另外一个类定义一个类即使用嵌套类比如文本鼠标只有在特定文本组件中的时候才有意义
作为圈入类的一个成员嵌套类有一个特权它可以毫无限制地访问圈入类地成员即使它们定义为private但是这个特权不是真正特殊的它跟private以及其它访问指示是一致的访问指示限制了对圈入类外部成员的访问嵌套类是处在圈入类中的这样它就可以访问圈入类的成员
象其它成员一样嵌套类可以声明为static一个static嵌套类称为static嵌套类非static嵌套类称为内部类下面给出例子
class EnclosingClass{
. . .
static class AStaticNestedClass {
. . .
}
class InnerClass {
. . .
}
}
5.3 执行嵌套类
当利用static方法和变量的时候一个static嵌套类就跟圈入类联系在一起了跟类成员一样static圈套类不能直接引用定义在圈入类中的实例变量或者方法只有通过对象的引用来使用它们当利用实例方法和变量的时候内部类就跟圈入类的实例联系在一起并且可以直接访问对象的实例变量和方法同时因为内部类跟实例联系在一起它不能定义
任何的static成员
为了帮助区分嵌入类和内部类这里建议用以下的方法来思考嵌入类是在两个类之间放映语法关系也就是说一个类的代码出现在另外一个类的代码中相比之下内部类是放映两个类实例之间的关系举例如下
class EnclosingClass {
. . .
class InnerClass {
. . .
}
}
你可以会在JAVA API中遇到这两个类并需要使用它但是绝大多数你编写的嵌套类将是内部类
第六章 接口和包
这节教程讲讲两个JavaTM编程语言特性来帮助你管理类之间的关系首先你将学到怎样编写和使用对象之间的通讯接口协议然后你会学到怎样捆绑类和接口到一个包
6.1 创建接口
在这节教程中你将看到一个执行接口的例子而且可以阅读到一些关于接口的内容它们是为了什么为什么要编写接口怎样编写接口等等
JAVA编程语言支持接口你可以使用接口来定义行为的协议这些行为可以在类分级结构中的任何类中被执行
6.1.1什么是接口
这一小节定义了什么是接口给出了一个接口的例子以及怎样使用它并且讨论为什么你需要在程序中使用接口
接口定义了行为的协议这些行为可以在类分级结构中的任何类中被执行接口定义了许多方法但是没有执行它们类履行接口协议来执行所有定义在接口中的方法因此适合某些行为因为接口是简单的未执行的系列以及一些抽象的方法 你可能会思考究竟接口于抽象类 有什么区别 知道了它们的区别是相当重要的 它们之间有以下的区别
1. 接口不能执行任何的方法而抽象类可以
2. 类可以执行许多接口但只有一个父类
3. 接口不是类分级结构的一部分而没有联系的类可以执行相同的接口
下面我们还是举个例子
6.1.1什么是接口
假如你已经编写了一个类这个类可以注意股票的价格这个类允许其它的类来注册以知道什么什么特定的股票的价格改变了首先编写StockMonitor类它可以执行一个方法来让其它对象的注册以得到通知
public class StockMonitor {
public void watchStock(StockWatcher watcher,
String tickerSymbol, double delta) {
...
}
}
这个方法的第一个参数未StockWatcher对象StockWatcher是一个接口的名字它的代码将在后面的教程中给出这个界面声明了一个方法valueChanged要被通知股票改变的对象必须试执行接口和valueChanged方法的类的实例其它两个参数提供了股票的符号以观察改变的数目当StockMonitor类检测到一个感兴趣的变化它就会调用watcher的valueChanged方法
WatchStock方法要通过第一个参数的数据类型确保所有注册对象执行valueChanged方法如果StockMonitor已经使用了一个类名作为数据类型就要强制它的用户的类关系因为类只可以有一个父类所以这也限制了什么类型的数据可以使用这个服务通过使用接口注册对象类可以是Applet或者Thread等等比如它允许类分级结构中的任何类使用这个服务
6.1.2 定义接口
定义一个接口跟创建一个新类是相似的接口定义需要两个组件接口定义和接口实体
interfaceDeclaration {
interfaceBody
}
interfaceDeclaration声明了各种关于接口的属性比如它的名字和是否扩展其它的接口这个interfaceBody包含了在接口中常量和方法声明
如图29所示给出了接口定义有两个组件接口声明和接口实体接口声明定义了各种关于接口的属性比如它的名字和是否扩展其它的属性接口实体包含了常数和用于接口的方法声明
StockWatcher接口和接口定义的结构为
public interface StockWatcher {
final String
sunTicker = "SUNW";
final String oracleTicker = "ORCL";
final String ciscoTicker = "CSCO";
void valueChanged(String tickerSymbol, double newValue);
}
接口定义了三个常量它们是watchable股票的股票行情自动收集器的符号这个接口也定义了valueChanged方法但是没有执行它执行这个接口的类为方法提供了执行
6.1.2 定义接口
下面讲讲界面的声明
在接口定义中需要两个元素interface关键字和接口的名字Public指示了接口可以在任何的包中任何的类中使用如果你没有指定接口为public那么接口就只能在定义接口的包中类使用了
接口定义可以有另外一个组件superinterfaces系列一个接口可以扩展另外的接口这跟类可以扩展一样但是类只能扩展一个另外的类而接口可以扩展任意个接口Superinterfaces系列椒以逗号分隔的所有接口这些接口可以由新的接口扩展
这时候你也许会问那接口实体怎么说呢别急下面就会解释了
接口实体为所有包含在接口中的方法包含了方法声明在接口中的方法声明可以紧跟着
一个逗号因为接口不为定义在它上面的方法提供执行所有定义在接口中的方法可以隐含地为public和abstact
接口可以包含常量s声明以及方法声明所有定义在接口中的常量可以是public static和final定义在接口中的成员声明不允许使用一些声明修饰语比如你不能在接口中的成员声明中使用transientvolatile或者synchronized同样你不能在声明接口的成员的时候使用private和protected修饰语
6.1.3 执行接口
为了使用接口你要编写执行接口的类如果一个类可以执行一个接口那么这个类就提供了执行定义在接口中的所有方法的方法
一个接口定义了行为的协议一个类可以根据定义在接口中的协议来执行接口为了声明一个类执行一个接口要包括一条执行语句在类的声明中你的类可以执行多个接口因为JAVA平台支持接口的多个继承因此可以在implements后面列出由类执行的接口系列这些接口是以逗号分隔的
以下是一个applet的部分例子它执行StockWatcher接口
public class StockApplet extends Applet implements StockWatcher {
...
public void valueChanged(String tickerSymbol, double newValue) {
if (tickerSymbol.equals(sunTicker)) {
...
} else if (tickerSymbol.equals(oracleTicker)) {
...
} else if (tickerSymbol.equals(ciscoTicker)) {
...
}
}
}
这里注意这个类引用了定义在StockWatcher.sunTicker的常量如oracleTicker等等执行接口的类继承了定义在接口中的常量因此这些类可以使用简单的名字来引用常量你可以象下面的语句使其它任何类使用接口常量
StockWatcher.sunTicker
从本质上讲当类执行一个接口的时候就签定了一个契约所有的类必须执行所有定义在接口以及它的superinterfaces中的方法以及类必须定义为abstract这个方法的签名名字和在类中参数类型的数目必须匹配方法的签名StockApplet执行SockWatcher接口因此applet提供了valueChanged方法这个方法公开地更新了applets的显示或者使用这个信息
6.1.4 作为一种类型来使用接口
当你定义一个新的接口的时候从本质上讲你定义了一个新的引用数据类型你可以在你使用其它类型的名字比如变量声明方法参数等等的地方使用接口名字回忆一下我们前面在StockMonitor类中的watchStock方法中的第一个参数的数据类型为StockWatcher
public class StockMonitor {
public void watchStock(StockWatcher watcher,
String tickerSymbol, double delta) {
...
}
}
只有执行接口的类的实例可以赋值为一个引用变量它的类型为接口名字因此只有执行StockWatcher接口的类的实例可以注册以得到股票数值改变的通知
6.1.5 接口不能发展
如果你将接口传给其它的程序员接口有个限制你应该注意接口不能发展下面对此进行解释
假如你想怎样一个函数到StockWatcher比如你想增加一个汇报当前股票价格的方法而不管数值是否被改变了
public interface StockWatcher {
final String sunTicker = "SUNW";
final String oracleTicker = "ORCL";
final String ciscoTicker = "CSCO";
void valueChanged(String tickerSymbol, double newValue);
void currentValue(String tickerSymbol, double newValue);
}
但是如果你做了这个改变的话执行老版本的StockWatcher接口的所有类都将中断因为它们不能执行这个接口了接口不能发展这是所有程序员要知道的为了达到以上增加一个方法的目的你可以创建更多的界面比如你可以创建一个
StockWatcher的subinterface子接口StockTracker:
public interface StockTracker extends StockWatcher {
void currentValue(String tickerSymbol, double newValue);
}
6.2 创建和使用包
本节教程将描述怎样捆绑你的类到包中并且教你怎样使用包中的类
为了使得类更容易地被发现和使用以及避免名字冲突控制访问程序员要捆绑相关的类和接口到包中类和接口都是JAVA平台的一部分它们都是各
种由函数捆绑类的包的成员基本类是在java.lang中而用于阅读和书写的类在java.io中等等你可以放置你的类和接口到包中
让我们看看以下的类并检查为什么你想将它们放置到包中你可以编写一系列的图形对象的类比如circlesrectangleslines和points你同样可以编写接口Draggable它是在用户拖动鼠标的时候可以移动这些图形对象
//在Graphics.java中的文件
public abstract class Graphic {
. . .
}
//在 Circle.java 的文件
public class Circle extends Graphic implements Draggable {
. . .
}
//在 Rectangle.java 中的文件
public class Rectangle extends Graphic implements Draggable {
. . .
}
//在Draggable.java 中的文件
public interface Draggable {
. . .
}
那为什么你要将这些类和接口捆绑到一个包中呢理由如下
1. 所有的程序员可以容易地决定哪些类和接口是相关的
2. 所有的程序员知道哪里可以找到与图形相关函数的类和接口
3. 类的名字不会与其它包中的名字冲突因为每个包创建了一个新的名空间
4. 你可以允许在包的类有无限制的访问以及包外的类的有限制访问
本文转自kenty博客园博客,原文链接http://www.cnblogs.com/kentyshang/archive/2006/12/21/599016.html如需转载请自行联系原作者
kenty