第6章 面向对象编程基础
面向对象的编程(Object-Oriented Programming,OOP)以对象为基本单元来进行代码划分,组织程序代码。ActionScript 3.0是一个完全标准的面向对象编程语言。面向对象编程在Flash 5已经开始支持,但语法不是业界传统的编程语言格式,因此在Flash 5和Flash MX中编写面向对象的编程非常不方便。ActionScript 2.0在面向对象的编程上有很大的进步,但是由于它的使用并不完全符合标准,存在很多的问题。ActionScript 3.0的推出基本解决了ActionScript 2.0中存在的问题,并有了很多的改进,而且相对于其他的OOP语言,ActionScript 3.0更简单易学。
6.1 面向对象编程技术概述
面向对象编程中最重要也最难以理解的概念就是“对象”。对象,指的是具有某种特定功能的程序代码。
对象(Object)具体可以指一件事、一个实体、一个名词,一个具有自己特定标识的东西。比如汽车、人、房子、桌子、植物、支票、雨衣等都是对象。对象就是一种客观存在,可能有时你觉得虚无缥缈,但是却的的确确存在着。
任何一个对象都有其属性。以人为例,人有身高、体重、性别、血型、年龄等,这些都反映了人作为一个社会存在所共有的特性。把这些特性反映到编程语言中,这些特性就是属性,用来反映某一个对象的共有特点。
任何一个对象都有其方法。还是以人为例,人要吃饭、睡觉、走路、工作等,这同样反映了人作为一个社会群体都要做的事情,是人的共有方法。存在这些共有方法,人才是一个活生生的人。人活在世上,都要做而且能做一些事情。把这些能做的事情反映到程序语言中,这些事情就是对象的方法。对象正是利用这些方法去做它能够做的事情。
属性和方法构成了对象,或者说属性和方法是对象的本质组成部分。有了对象和方法,对象才能称为一个真正的对象。
面向对象编程的基本表现就是把对象的属性和方法包装成类。类是面向对象编程的核心内容。
在ActionScript 3.0中,Adobe公司已经设计好很多用于实现不同功能的类,这些是ActionScript编程语言的基础,习惯上称为核心类。常用的ActionScript 3.0核心类包括:用于处理显示对象的Display(显示对象)类;用于处理数组的Array(数组)类;用于处理数字和数学的Math类,用于处理绘制效果的Shape(绘制)类等。这些内容将在第三篇“ActionScript 3.0核心类”中详细地讲解。
除了Adobe公司所提供的这些核心类之外,可以使用这些核心类所提供的方法,开发属于自己的类。面向对象编程的主要工作就是利用自定义的类实现新的工作和方法,为自己的项目开发服务。
下面还是以人为例,给出一个简单类(Class)的例子。
//定义包 package { //创建类 public class Person { //定义属性 public var personname:String; private var age:int; public var birth:int; //构造函数 public function Person(username:String,ages:int) { //利用构造函数,初始化属性变量 personname=username; age=ages; } //定义一个方法 public function getbirth() { //获取当前时间 var date:Date=new Date; 获取当前的年份并减去年纪 birth=date.getFullYear() - age; //返回出生年份 return birth; } } }
在类中给人(Person)设置了3个属性personname(人的姓名)、age(年龄)、birth(出生年份),设置了一个方法getbirth(取得出生年份)。
把该段代码保存在Person.as文件中,然后新建一个Person.fla文件,和Person.as保存在同一个目录下。下面建立一个人的实例(建立实例的方法,请参见6.2.3小节的内容),并通过getbirth()方法来取得此人的出生年份。代码如下所示:
//建立人的实例 var p:Person=new Person("张小姐",20); //通过getbirth()方法取得出生年份 trace(p.getbirth());//输出:1988
6.2 类
对象是抽象的概念,要想把抽象的对象变为具体可用的实例,必须使用类。使用类来存储对象可保存的数据类型及对象可表现的行为信息。要在应用程序开发中使用对象,就必须要准备好一个类,这个过程就好像制作好一个元件并把它放在库中一样,随时可以拿出来使用。本节从类的基本概念着手,逐步介绍类的定义方法和类的使用方法。
6.2.1 类概述
类就是一群对象所共有的特性和行为。
早在ActionScript 1.0中,程序员使用原型(Prototype)扩展的方法,来创建继承或者将自定义的属性和方法添加到对象中,这是类在Flash中的初步应用。在ActionScript 2.0中,通过使用class和extends等关键字,正式添加了对类的支持。ActionScript 3.0不但继续支持ActionScript 2.0中引入的关键字,而且还添加了一些新功能,如通过protected和internal属性增强了访问控制,通过final和override关键字增强了对继承的控制。
下面来简单了解一下ActionScript 3.0中类的构成。具体的类结构及实现方法,请参见6.2.2小节的内容。
❑包块和类的名称。
❑构造函数。
❑属性:包括实例属性和静态属性。
❑方法:包括实例方法和静态方法。
6.2.2 创建自定义的类
创建一个自定义类的操作步骤如下:
1)建立一个准备保存类文件的目录,即为一个包(package)。比如在计算机中有个目录“F:\Test”。
2)启动Adobe Flash CS3,新建一个ActionScript文件,文件名为要创建的类的名字。比如要创建的类的名称为Sample,那么保存的文件名称也要为Sample。注意一定要保存在刚才建立的目录中。
3)在文件的开头写入package关键字和package包的路径。如package Test{},其中Test就是保存类文件的目录名称。
4)若需要引入其他的类,则需要在package后面的大括号后插入新行,使用import语句加入其他类的包路径和名称。比如import flash.geom.point。若不需要,则此步骤可以省略。
5)在新的一行写入class关键字和类的名字,如class Sample{}。
6)在class后面的大括号内写入对类定义的内容,包括构造函数、属性和方法。
注意 如果package关键字后面没有声明包路径,即默认包路径为当前的项目目录。
下面用代码的形式来显示创建出来的类的结构和一般格式:
package 包路径{ import *.*.*//引入要使用的类 class 类名称{ //属性 private var 属性名称:数据类型 public var ... //构造函数 public function 类名称{ } //隐式获取/设置方法 public function get 属性():返回类型{ return 属性 } //公共方法 public function 方法名称(){ } //私有方法 private function 方法名称(){ } } }
下面仍以人为例,建立一个完整的Person类,代码如下所示:
//创建包 package { //创建类 public class Person { //定义属性 public var personname:String; private var age:int; public var birth:int; //构造函数 public function Person(username:String,ages:int) { //在构造函数中初始化变量 personname=username; age=ages; } //隐式获取 public function getpersonname():String { return personname; } //隐式设置 public function setpersonname(username:String) { personname=username; } //创建做事情的方法 public function dosomething() { trace("做点什么事情呢?"); } //创建获得出生年份的方法 private function getbirth() { var date:Date=new Date; birth=date.getFullYear() - age; return birth; } } }
说明
完整的类由多个部分组成,在具体的使用过程中,需要根据具体的需要来设计。
6.2.3 创建类的实例
类是为了使用而创建的,要使用创建好的类,必须通过类的实例来访问。要创建类的实例,需要执行下面的步骤。
1)使用import关键字导入所需的类文件。其用法格式如下所示:
import 类路径.类名称;
格式说明如下:
❑import:载入类关键字,必需。用于指明将导入接下来的类。
❑类路径:类包的路径,也就是定义类中package关键字后的路径。
❑类名称:要导入的类的名称。
具体示例代码如下所示:
import flash.display.Sprite;
2)使用new关键字加上类的构造函数。其用法格式如下:
var 类引用名称:类名称=new 类名称构造函数();
格式说明如下:
❑var:变量定义关键字,必需。
❑类引用名称:用作引用类的实例的命名,必需参数。
❑类名称:说明引用变量的类型,可选参数。推荐选择,有助于编译时检测。
❑new:关键字,必需。用于指明将创建一个该类的新实例。
❑类构造函数名称:指明要导入的类的名称,可根据类的定义加入所需参数。
具体示例代码如下所示。
var sp:Sprite=new Sprite();
6.2.4 包块和类
在ActionScript 3.0中,包路径是一个独立的模块,单独用一个包块来包含类,不再作为类定义的一部分。定义包块使用package关键字,其用法格式如下所示:
package 包块路径{ //类体 }
用法示例代码如下:
package com.lzxt.display{
//类体
}
包块路径为该类存储位置的路径,定义之后,该类一定要存储在该路径的目录下,否则编译器就会报错。
ActionScript 3.0中所有的类都需要定义到一个包块内。若不需要存储在包路径下,只需使用package关键字,而不用添加包块路径,此时为默认的项目目录路径。例如下面的示例代码也是正确的:
package {
//类体
}
在ActionScript 3.0中,类和包路径分离,类位于包块之内。其定义格式如下所示:
public class 类名称 { //类体 }
格式说明如下:
❑public:类命名控制,用于指明类的可使用范围。
❑class:定义类关键字,指明将要定义一个新类。注意class的首字母是小写而不是大写。
❑类名称:要定义的类的名称,使用大写字母开头。
具体示例代码如下:
public class Rect {
//类体
}
6.2.5 包的导入
在ActionScript 3.0中,要使用某一个类文件,就需要先导入这个类文件所在的包,也就是要先指明要使用的类所在的位置。
通常情况下,包的导入有以下3种情形:
1)明确知道要导入哪个包,直接导入单个的包。
例如说要创建一个绘制对象,那么只需导入Display包中的Shape包即可。代码如下所示:
import flash.display.Shape;
2)不知道具体要导入的类,使用通配符导入整个包。
例如需要一个文本的控制类,但是并不知道该类的具体名称,就可以使用“*”通配符进行匹配,一次导入包内的所有类。具体使用代码如下所示:
import flash.text.*
3)要使用同一包内的类文件,则无须导入。
如果现在有多个类位于计算机中的同一个目录下,则这些类在互相使用的时候,不需要导入,直接使用即可。
比如说现在com.lzxt.display包内有一个DrawCircle类,若同时该包还有一个类DrawSample,则在DrawSample类中使用DrawCircle时,无须导入,直接使用即可。
在com.lzxt.display包内新建一个DrawSample类文件,此类文件使用DrawCircle类构造了一个绘制圆形实例,则使用的DrawCircle类就不必事先导入。DrawSample类的代码如下所示:
package com.lzxt.display{ import flash.display.Sprite; public class DrawSample extends Sprite { public function DrawSample() { //生成CircleSprite对象 var red:DrawCircle=new DrawCircle(40,0xff0000); red.x=100; red.y=100; //把这个对象加入显示列表 addChild(red); } } }
要使用的DrawCircle类文件在同一包内,代码如下所示:
package com.lzxt.display{ import flash.display.Sprite; import flash.text.TextField; public class DrawCircle extends Sprite { //声明属性 private var _color:uint; private var _radius:Number; //构造函数 public function DrawCircle(radius:Number=50,color:uint=0x00ff00) { //初始化属性 _radius=radius; _color=color; drawcircle(); } //私有方法,用于绘制显示对象 private function drawcircle():void { //下面的三行用于绘制一个圆形 graphics.beginFill(_color); graphics.drawCircle(0,0,_radius); graphics.endFill(); } } }
说明
同一包内的类就是指位于同一个文件目录下的类,这些类可以互相调用而不用导入。
6.2.6 构造函数
构造函数是一个特殊的函数,其创建目的是为了在创建对象的同时初始化对象,即为对象中的变量赋初始值。
在ActionScript 3.0编程中,创建的类可以定义构造函数,也可以不定义构造函数。如果没有在类中定义构造函数,那么编译时编译器会自动生成一个默认的构造函数,这个默认的构造函数为空。构造函数可以有参数,通过参数传递实现初始化对象操作。
下面的示例列出两种常用的构造函数代码:
//空构造函数 public function Sample(){ } //有参数的构造函数 public function Sample(x:String){ //初始化对象属性 }
构造函数与其他的函数不同,在定义时也有一定的差别。具体差别如下:
❑构造函数必须和类名完全相同,首字母必须大写。
❑构造函数只能使用public访问控制,不能使用其他命名空间。
❑不能在构造函数中使用return语句返回值,但可以使用return语句控制程序的流程。
❑构造函数不能声明返回类型。
下面的示例构建一个完整类的构造函数,代码如下所示:
package com.lzxt.classes{ public class Sample { //定义一个变量_str private var _str:String; //构造函数 public function Sample(str:String) { //输出 trace("这是一个构造函数"); //初始化变量_str _str=str; trace(_str); } } }
下面使用该构造函数创建一个对象,并利用其构造函数向对象传递一个参数。示例代码如下所示:
//导入类 import com.lzxt.classes.Sample; //创建一个Sample对象 var sp:Sample=new Sample("进入了AS 3.0世界");
程序运行的结果如下所示:
这是一个构造函数 进入了AS 3.0世界
6.2.7 声明和访问类的属性
在编程语言中,使用属性来指明对象的特征和对象所包含的数据信息以及信息的数据类型。在定义类的过程中,需要通过属性来实现对象特征的描述和对象信息数据类型的说明。比如在创建一个关于人的类的过程中,就需要说明人这一对象的“性别”特征,需要说明人的“年龄”这一数据的数据类型是一个数字等。本小节将介绍声明和访问类属性的方法。
在ActionScript 3.0的类体中,一般在class语句之后声明属性。类的属性分两种情况:实例属性和静态属性。实例属性必须通过创建该类的实例才能访问,而静态属性则不需要创建类实例就能够访问。
声明实例属性的语法格式如下所示:
var 属性名称:属性类型; var 属性名称:属性类型=值; public var 属性名称:属性类型=值;
用法示例代码如下:
var _x:Number; var name:String="lzxt"; public var birthday:Date; public var age:Number=30;
声明实例属性需要注意下面几点:
❑声明实例属性可以直接给属性赋值,但不能把表达式赋值给属性。
❑var关键字之前可以添加访问控制标识。若没有使用访问控制,默认为internal。其他的标识还有:public、private、protect。
❑实例实现只有在包内可以访问,包外不能访问。
ActionScript 3.0中,访问控制符有4个,其主要控制范围如下:
❑internal(默认):对同一包中的引用可见。
❑private:对同一类中的引用可见。
❑protected:对同一类及派生类中的引用可见。
❑public:对所有位置的引用可见。
要访问实例属性,需先创建该类的实例,然后使用“.”运算符来访问。
下面的示例先创建一个类,然后创建该类的实例并访问类的属性。代码如下所示:
package com.lzxt.classes{ //创建类 public class ClassSample { //创建一公共属性,并设置其默认值 public var username:String="浪子啸天"; //定义一公共属性,设置其数据类型为数值型 public var age:int; //构造函数,并通过构造函数传递参数 public function ClassSample(ages:int) { age=ages; } } }
下面先创建一个ClassSample对象的实例,并访问其属性。示例代码如下所示:
//导入类 import com.lzxt.classes.ClassSample; //创建ClassSample类的一实例 var mysample:ClassSample=new ClassSample(30); //输出ClassSample对象实例的username和age属性,获取相关信息 trace(mysample.username); trace(mysample.age);
程序运行的结果如下所示:
浪子啸天
30
使用实例属性,需要先创建类的实例,然后才能访问其属性。静态属性则不同,它不用创建类的实例就可以访问。
静态属性的定义格式如下所示:
static var 属性名称:属性类型; static var 属性名称:属性类型=值; public static var 属性名称:属性类型; public static var 属性名称:属性类型=值;
要声明一个常量,则需要使用const关键字,而且需要在声明时就赋值。其具体格式如下:
static const 属性名称:属性类型=值;
可以直接使用静态属性名称来访问静态属性。若要在外部访问类的属性,可使用类名加“.”运算符来实现。
下面的示例先创建一个类,然后从外部进行访问。代码如下所示:
package com.lzxt.classes{ //创建类 public class StaticClass { //创建静态属性 public static var username:String="浪子啸天"; public static var age:int=30; //创建构造函数并传递参数 public function StaticClass(ages:int) { age=ages; } } }
下面的代码可以实现访问静态属性,示例代码如下所示:
//导入类 import com.lzxt.classes.StaticClass; //通过访问静态属性访问属性,获取信息 trace(StaticClass.username); trace(StaticClass.age);
程序运行的结果如下所示:
浪子啸天
30
6.2.8 声明和访问类的方法
在编程语言中,使用方法来构建对象的行为,即对象可以完成的操作。在编程过程中,通过对象的方法,告诉对象可以做什么事情,怎么做。比如在创建一个关于人的类的过程中,就需要知道人能够干什么事情,这就是人这一对象的方法。比如人张口说话、举手等行为,都需要通过方法来表示。
在ActionScript 3.0中,声明类实例方法的格式和上面函数的格式类似,格式如下所示:
function 方法名称(参数...):返回类型{ //方法内容 }
和访问类属性一样,访问类实例方法,也可以使用实例名称加“.”运算符来实现。
下面的示例先创建一个类,然后创建该类的实例并访问类的方法。代码如下所示:
package com.lzxt.classes{ //创建类 public class ClassMeth { //构造函数 public function ClassMeth() { } //创建该类的方法,在访问此方法时,将执行一个输出操作 public function SampleMeth() { trace("这是一个实例方法"); } } }
访问代码如下所示:
import com.lzxt.classes.ClassMeth; //创建ClassMeth对象的一个实例 var me:ClassMeth=new ClassMeth(); 访问其SampleMeth()方法 me.SampleMeth();//输出:这是一个实例方法
和静态属性一样,也可以创建静态方法,用于在不创建类实例的前提下访问类方法。Meth类中定义的方法,基本都是静态方法,均可以通过类直接访问。
声明静态方法的格式如下所示:
static function(参数...):返回类型{ //方法内容代码 } public static function(参数...):返回类型{ //方法内容代码 }
要访问静态方法,同样可以使用类名称加“.”运算符来访问。
下面的示例创建一个类,并从外部访问其静态方法。代码如下所示:
package com.lzxt.classes{ public class StaticMeth { public function StaticMeth() { } //使用static关键字定义一个静态方法 public static function SampleMeth() { trace("这是一个静态方法"); } } }
访问代码如下所示:
import com.lzxt.classes.StaticMeth; //不用创建实例,直接访问该方法 StaticMeth.SampleMeth();//输出:这是一个静态方法
此外,在ActionScript 3.0中仍然支持使用get和set方法隐式地获取和设置类的属性。其用法格式分别如下:
//获取属性 访问控制符 function get 方法名(参数:参数类型):返回类型{ //控制代码 return 访问属性 } //设置属性 访问控制符 function set 方法名(参数:参数类型):void{ //控制代码 访问属性=参数 }
设置后的隐式获取和设置,可以使用实例名称加“.”运算符来访问。
下面的示例创建一个简单的类Person,然后使用get和set方法访问与设置其属性。代码如下所示:
package com.lzxt.classes{ public class Person { //定义私有属性 private var _name:String="浪子啸天"; //隐式获取属性 public function get name():String { return _name; } //隐式设置属性 public function set name(username:String):void { _name=username; } } }
访问代码如下所示:
import com.lzxt.classes.Person; //创建对象实例 var ps:Person=new Person(); //实现隐式获取ps实例的属性 trace(ps.name); //实现隐式设置ps实例的属性 ps.name="lzxt"; //输出 trace(ps.name);
程序运行的结果如下所示:
浪子啸天
lzxt
6.2.9 类的使用
在ActionScript 3.0中,创建好的类一般有3种使用方法:作为文档类进行文档类绑定;作为库中元件的类进行绑定;作为外部类使用import关键字导入。下面结合实例具体介绍这3种用法。
1. 文档类
文档类是ActionScript 3.0的新特色,该特色是为实现代码与文档分离而设计的。文档类是继承自Sprite或者MovieClip类的子类(继承的方法及要求,请参见6.4节的内容),它将作为SWF文件的主类而存在。在执行SWF文件时,此文件的构造函数将被首先调用,因此该类将成为该SWF中程序的入口,任何需要做的事情都可以写在上面。
文档类和普通类的区别在于,文档类必须是继承自Sprite类或者MovieClip类的子类。
通过使用文档类,把原来需要加在时间轴关键帧上的代码放在外部的文档中,在文档属性中进行绑定,实现文档与代码分离。
下面的实例使用文档类在舞台上添加一个显示圆形对象,具体操作步骤如下:
1)打开脚本窗口,新建一个DocSample类。其代码如下所示:
//定义包路径 package com.lzxt.classes{ //导入包 import flash.display.Shape; import flash.display.Sprite; //定义类 public class DocSample extends Sprite { //定义属性 public var sp:Shape; //构造函数 public function DocSample() { //创建对象 sp=new Shape(); sp.graphics.beginFill(0xff0000); sp.graphics.drawCircle(0,0,150); sp.graphics.endFill(); sp.x=225; sp.y=200; //加入场景 addChild(sp); } } }
2)新建一个Flash文件(ActionScript 3.0),保存文件到com同级的目录下。
3)打开“属性”面板,在“文档类”文本框中输入类路径:com.lzxt.classes.DocSample,实现文档类绑定。注意只要输入类名称即可,不要加.as。完成后如图6.1所示。
图6.1 文档类绑定
至此,文档类绑定完成。按下【Ctrl+Enter】组合键,编译文件。完成后的效果如图6.2所示。
图6.2 文档类示例效果
注意 在ActionScript 3.0面向对象编程的要求下,本书后面的示例尽可能使用文档类使文档和代码分离。所提及的文档类的使用方法与此相同,以后不再重复。
2. 与库文件绑定
与fla文件库中的影片剪辑进行绑定,每个影片剪辑只能绑定一个类。
下面的实例通过使用库文件绑定,实现一个上升水泡的效果。具体操作过程如下:
1)新建一个Flash文件(ActionScript 3.0),保存文件为addMovieClip。
2)单击【插入】|【新建元件】命令,打开“创建新元件”窗口,在“名称”文本框中输入影片剪辑的名称“水泡”,单击【确定】按钮,创建一个新的影片剪辑。
3)使用创作工具创作一个水泡的形状效果。如图6.3所示。
图6.3 水泡效果图
4)单击【文件】|【新建】命令,打开“新建文档”窗口,选择“ActionScript文件”选项,单击【确定】按钮,新建一个脚本文件,保存为H2O,路径和addMovieClip相同。
5)在脚本窗口输入如下代码:
//包 package { //导入包 import flash.display.MovieClip; import flash.events.*; //定义类 public class H2O extends MovieClip { //初始化速度 var speedx:Number=0; var speedy:Number=0; //构造函数 public function H2O() { speedx=.5 * Math.random() - 0.5; speedy=5 * Math.random(); //注册事件侦听 this.addEventListener(Event.ENTER_FRAME,Mot); } //定义事件函数 function Mot(e:Event) { //控制位置变化 this.x+= speedx; this.y-= speedy; //运动出舞台之后初始化 if (this.y < 0) { init(); } } //初始化位置 function init() { this.y=400; this.x=Math.random() * 550; } } }
6)回到addMovieClip.fla文件,打开“库”面板,右击“水泡”影片剪辑,选择“链接”选项,打开“链接属性”对话框。如图6.4所示。
图6.4 链接属性
7)在“类”文本框中输入H2O,基类保持不变。单击【确定】按钮。
8)打开“动作”面板,输入如下代码:
//自定义显示函数 function DisplayH2O() { //使用循环载入多个显示对象 for(var i:int = 0; i <100; i++){ //载入对象 var h2o:H2O = new H2O(); //加入舞台 this.addChild(h2o); //控制初始位置和属性 h2o.x = Math.random()*550; h2o.y = Math.random()*300+400; h2o.alpha = .2+Math.random()*.5; var scale:Number = .3+Math.random(); h2o.scaleX = h2o.scaleY = scale; } } //执行函数 DisplayH2O();
至此,此动画效果制作完成,完成后的效果如图6.5所示。
图6.5 绑定库元件动画效果图
3. import导入类
此方法在前面的创建类实例一节已经介绍了实例,此处不再赘述。
6.2.10 包外类
在ActionScript 3.0中,一个类文件中可以定义多个类,但是包块内只能定义一个类,此为主类。主类的名称一定要和类文件的名称相同,这个类是供外部使用的。在包块之外,还可以定义多个类,这些类的名称和类文件的名称不同,而且只有当前类中的成员可以访问。这种类,有些资料称其为包外类,有些资料称其为助手类。在这里使用包外类这一名称。
包外类的格式如下所示:
package 包路径{ class 主类{ //主类内容 } } class 包外类1{ //包外类内容 } class 包外类2{ //包外类内容 }
下面的示例使用包外类,具体代码如下所示:
//创建包 package com.lzxt.display{ import flash.display.Sprite; //创建主类 public class Draw extends Sprite { //构造函数 public function Draw() { //使用包外类,通过创建该类的实例来创建对象 var red:DrawCircle=new DrawCircle(40,0xff0000); red.x=100; red.y=100; addChild(red); } } } //包外类 import flash.display.Sprite; import flash.text.TextField; //创建一个绘制圆形的类 class DrawCircle extends Sprite { private var _color:uint; private var _radius:Number; public function DrawCircle(radius:Number=50,color:uint=0x00ff00) { _radius=radius; _color=color; drawcircle(); } private function drawcircle():void { graphics.beginFill(_color); graphics.drawCircle(0,0,_radius); graphics.endFill(); } }
包外类在使用的过程中,需要注意以下几点:
❑包外类只对当前类文件可见,故不能使用public关键字定义。
❑包外类需要重新导入要使用的包,包块内导入的无效。
❑包外类的名字如果和包内的类重复,优先使用包外类。
6.3 接口
接口可以看做是对象之间相互联系的纽带,起桥梁的作用。通过接口中方法声明的集合,能够使互不相干的对象之间相互通信。
接口和类既存在一些类似的地方,也有一定的区别。
❑接口仅仅声明方法而不提供实现的方法,要实现方法,需要在该接口的类中声明和实现。
❑接口方法的实现无任何属性,但在接口类的定义中,实现的方法必须为public。
❑可通过extends语句由接口继承实现多个接口,也可以通过implements语句由类继承实现。
❑接口是一个定义类方法组的数据类型,可作为类型注释。
6.3.1 接口的定义
定义接口的方法和定义类相似,不过需要使用interface关键字来实现。
接口可以包含实例方法、静态方法、get和set方法。
接口也需要保存为as文件,文件名必须和接口名完全相同。
定义接口的语法格式如下所示:
package 包路径{ 访问控制 接口名称{ function 实例方法名(参数:参数类型):返回类型; static function 静态方法名(参数:参数类型):返回类型; function get 方法名():返回类型; function set 方法名(参数:参数类型):void; } }
接口定义示例代码如下所示:
//包 package { //定义接口IPerson public interface IPerson { //定义方法 function say():void; function walk():void; } }
接口定义中需要注意以下几点:
❑接口的定义需要使用interface关键字。
❑接口方法的定义不能包含访问控制关键字。
❑接口中只能定义方法,不能定义属性。
❑接口的名称通常用大写字母I开头。
6.3.2 在类中实现接口
接口只是负责定义一些方法,这些方法要实现,则必须使用接口类来完成。接口类要使用implements关键字来实现。一个类可以实现多个接口,多个接口之间使用“,”分开。同样一个接口可以由多个子类来继承。
下面的示例代码继承6.3.1小节中的IPerson接口,并创建子类Man。代码如下所示:
package { //定义类Man,继承自接口IPerson public class Man implements IPerson { //定义类方法walk public function walk():void { trace("男人走路有力量!!"); } //定义类方法say public function say():void { trace("男人说话直截了当!"); } } }
使用接口创建类的使用示例代码如下所示:
package { import flash.display.Sprite; public class DoMan extends Sprite { public function DoMan() { //创建Man类的实例 var man:Man=new Man; man.walk(); man.say(); /* 男人走路有力量!! 男人说话直截了当! */ } } }
继承接口实现子类的方法时要注意以下几方面:
❑从接口中继承的方法必须使用public关键字控制。
❑从接口中继承方法的参数的数量、数据类型及访问类型也必须相同。
❑参数的名称和参数的默认值可以与接口中不同。
6.4 继承
继承(Inheritance)是面向对象技术的一个重要概念,也是面向对象技术的一个显著特点。一个对象通过继承可以使用另一个对象的属性和方法。准确来说,继承的类具有被继承类的属性和方法。被继承的类,称为基类或者超类,也可以称为父类;继承出来的类,称为扩展类或者子类。
6.4.1 继承的定义
类的继承要使用extends关键字来实现。其语法格式如下所示:
package{ class 子类名称 extends 父类名称{ } }
下面的示例创建一个新类Man,它继承自Person类,这样Man类就是子类,而Person类就是父类。用法示例代码如下:
package{ class Man extends Person{ } }
6.4.2 属性和方法的继承
子类可以继承父类中的大部分的属性和方法,但是父类中使用“private”访问控制符定义的属性和方法不能被继承。
下面的示例新建一个PC类作为父类,该类有两个实例属性name和age,有两个实例方法walk()和say()。
创建父类PC,代码如下所示:
package com.lzxt{ //构建类 public class PC { //构建属性 public var name:String="浪子啸天"; public var age:int=30; //构建方法 public function walk():void { trace("人可以走路!"); } public function say():void { trace("欢迎你来到as3世界"); } } }
创建子类Man,代码如下所示:
package com.lzxt{
//继承父类
public class Man extends PC{
}
}
下面使用文档类创建子类Man对象的一个实例,并访问父类PC的属性和方法,代码如下所示:
package { import flash.display.Sprite; import com.lzxt.Man; //文档类 public class SampleIt extends Sprite { //构造函数 public function SampleIt() { //构建实例对象 var man:Man=new Man(); //获取属性 trace(man.name); trace(man.age); //获取方法 man.say(); man.walk(); } } }
程序运行后的结果如下所示:
浪子啸天 30 人可以走路! 欢迎你来到as3世界
说明
输出的结果表明,子类的对象继承了父类的方法和属性。
6.4.3 重写(override)
通过继承,子类可以继承父类的属性和方法。但有些时候只需要使用父类中的方法名称,而需改变其内容,这时就需要利用重写来修改其方法内容。
在ActionScript 3.0中,只能重写实例的方法,不能重写实例的属性。另外,父类中方法的访问控制符为final的方法也不能进行重写。
下面在6.4.2小节的子类Man中重写父类PC的方法walk(),代码如下所示:
package com.lzxt{ //继承父类 public class Man extends PC { public override function walk():void { trace("这是重写过的走路方法"); } } }
继续使用文档类SampleIt,编译后的输出结果如下:
浪子啸天 30 欢迎你来到as3世界 这是重写过的走路方法
说明
输出的结果表明,已经覆盖了父类的方法walk()。
使用override方法重写父类的方法,需要注意下面几点:
❑一旦使用了override关键字,重写方法的方法父类中一定要存在。
❑不能重写final关键字定义的方法。
❑重写的方法必须与父类方法的参数和返回类型声明完全一致。它们参数的数量必须相同,而且每个参数的类型也要相同。若父类方法的参数是可选的,则重写的函数该参数也必须是可选的。
❑父类和子类这两个函数的访问控制符和命名空间属性也必须相同。
6.5 AS 3.0命名空间
在ActionScript 3.0中引入了命名空间的概念。使用命名空间,可以控制标识符的可见性,可以管理各个属性和方法的可见性。不管命名空间是在内部还是外部,都可以应用于代码。ActionScript 3.0中有4个访问控制符,分别为:public、private、internal和protected。这4个访问控制符就是一种命名空间,同样,也可以定义自己的命名空间。本节就来介绍一下自定义命名空间和应用命名空间的方法。
6.5.1 命名空间的定义
命名空间常见的情况有两种:一是将命名空间定义到一个独立的as文件中,该命名空间能够使不同的包进行访问;二是将命名空间定义在类体中,该命名空间仅在本类中进行访问。
1. 将命名空间定义在一个独立的as文件中
下面的示例定义一个lzxt命名空间,代码如下所示:
package{
//定义命名空间
public namespace lzxt = "http://www.flacs.cn/";
}
说明
从示例可以看出,定义一个命名空间需要使用namespace关键字。
2. 将命名空间定义在类体中
定义在类体中的命名空间也使用namespace关键字,命名空间之前可以使用访问控制符。下面创建一个示例,代码如下所示:
package { class SampleSpace { //定义命名空间 namespace flasc //获取命名空间方法 public function getNameSpace():Namespace { return flasc; } //使用自定义命名空间控制方法 flasc function welcome():void { trace("欢迎使用自定义命名空间"); } } }
说明
在类体中定义命名空间,是一种很好的处理方式。
6.5.2 使用命名空间
若在类体中定义命名空间,则可以像访问控制符一样使用,在定义的属性或者方法之前加上命名空间即可。如下面的代码:
package { class SampleSpace { //定义命名空间 namespace lzxt lzxt var name:String="浪子啸天"; //使用自定义命名空间控制方法 lzxt function welcome():void { trace("欢迎使用自定义命名空间"); } } }
若要使用独立的命名空间,则需要使用use关键字,代码如下所示:
命名空间类:
package{
//定义命名空间
public namespace lzxt = "http://www.flacs.cn/";
}
使用命名空间类:
package{ //打开命名空间lzxt use namespace lzxt; public class Samplelzxt{ //用命名空间lzxt定义doSth 方法 lzxt function sampleMeth():void{ trace("这是一个自定义命名空间的函数"); } } }
文档类:
package { import flash.display.MovieClip; //使用命名空间 use namespace lzxt; public class TestNameSpace extends MovieClip { public function TestNameSpace() { //构建实例 var sam:Samplelzxt=new Samplelzxt(); //执行方法 sam.sampleMeth(); //输出:这是一个自定义命名空间的函数 } } }
命名空间主要可以用于处理以下问题:
❑避免命名冲突。
❑更加灵活高效地实现访问控制。
❑实现不公开的API接口。
6.6 练习题
1. 关于访问控制符,描述不正确的是( )。
A. 类可以用public private final来声明
B. 使用final声明的方法,不可以被继承
C. 使用internal声明的方法,只能被同一包访问
D. 使用protected声明的方法,不能被其他类访问,只能由自己和子类访问
2. 关于override描述正确的是( )。
A. 使用override可以重写父类public声明的属性
B. 使用override可以重写父类protected声明的方法
C. 方法被重写时,参数数量需一致,参数类型可以不同
D. final声明的方法也可以被重写
3. 简述接口和类的区别及什么时候需要使用接口。
4. 关于AS3的接口,描述正确的是( )。
A. 一个类可以继承多个接口
B. 接口中可以定义方法和属性
C. 接口中定义的方法可以有public protected描述访问权限
D. 继承接口的类,可以不去实现接口中的方法
5. 用文档类和库文件绑定的方式实现以下程序。
定义一个类—人(Person),实现方法—行走run()和站立stand(),run()让人以10px/s移动,定义男人Man继承Person,重写run(),以20px/s移动。在文档类里,创建Person和Man的两个对象添加到舞台,并调用它们的run()。