第三章 Java面向对象程序设计 3.1 Java的类和对象 3.2 域和方法 3.3 访问控制符 3.4 继承 3.5 方法的继承、覆盖与重载 3.6 上转型对象 3.7 接口 3.8 包
3.1 Java的类和对象 类 对象 类是Java语言的最基本概念,是组成Java程序的基本要素 类封装了该类对象的变量和方法。 对象 对象是类的实例化,对象的创建是通过对象构造方法来实现的; 我们可以生成多个对象,通过消息传递来进行交互,最终完成复杂的任务; 消息传递是指激活指定的某个对象的方法,以改变它的状态或使其产生一定的动作。
类和对象 现实生活中的对象 计算机中 的对象的原型 class Car { int color_number; int door_number; int speed; void brake() { … } void speedUp() {…}; void slowDown() { … } } 现实生活中的对象 计算机中 的对象的原型
类和对象 什么是类? 类是描述对象的“基本原型”,它定义一种对象所能拥有的数据和能完成的操作,在面向对象的程序设计中,类是程序的基本单元。 程序中的对象 是类的一个实例,是一个软件单元,它由一组结构化的数据和在其上的一组操作构成。 variables methods
类和对象 变量:即指对象的所知道的状态 方法:指对象的功能单元。 什么是消息? 软件对象通过相互间传递消息来相互作用和通信 一个消息由三部分组成: 1. 接受消息的对象 2. 要完成方法的名字 3. 方法需要的参数 对象B 对象 A message
3.1.1 系统定义的类 Java程序设计就是定义类的过程,它分为两大类: 系统定义的类,即Java类库中的类; 用户程序自定义的类。
常用包(一) 1. java.lang包 2.java.io包 该包是Java语言的核心类库,包含了运行Java程序必不可少的系统类,如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等。 每个Java程序运行时,系统都会自动引入java.lang包,所以这个包的加载是缺省的。 2.java.io包 该包是Java语言的标准输入/输出类库,包含了实现Java程序与操作系统、用户界面以及其它Java程序做数据交换所使用的类。 如基本输入/输出流、文件输入/输出流、过滤输入/输出流、管道输入/输出流、随机输入/输出流等。
常用包(二) 3.java.util包 4.java.awt包 该包包含了Java语言中的一些低级的使用工具,如处理时间的Date类、处理变长数组的Vector类,实现栈和散列表的Stack类和HashTable类等。 4.java.awt包 该包是Java语言用来构建图形用户界面(GUI)的类库,它包括了许多界面元素和资源,主要提供三方面的支持: 低级绘图操作,如Graphics类等; 图形界面组件和布局管理,如Checkbox类、Container类、LayoutManager接口等; 界面用户交互控制和事件响应,如Event类。
常用包(三) 5.java.awt.image包 该包用来处理和操纵来自网上的图片的Java工具类库。 6.java.awt.peer包 它是程序代码和平台之间的中介,将不同的平台包裹、隐藏起来,使之在用户面前呈现相同的面貌; 该包是实现Java语言跨平台特性的手段之一。
常用包(四) 7.java.applet包 该包是用来实现运行于Internet浏览器中的Java Applet的工具类库,它仅包含少量的几个接口和一个非常有用的类:java.applet.Applet。 8.java.net包 该包是Java语言用来实现网络功能的类库,主要包括: 底层的网络通信,如Socket类、ServerSocket类; 编写用户自己的Telnet、FTP、邮件服务等实现网上服务的类; 用于访问Internet上资源和进行CGI网关调用的类,如URL等。
常用包(五) 9.java.corba包和java.corba.orb包 这两个包将CORBA嵌入到Java环境中,使Java程序可以存取、调用CORBA对象,并与之共同工作。 10.java.lang.reflect包 该包提供用于反射对象工具,允许程序监视一个正在运行的对象并获得它的构造函数、方法和属性。 11.java.rmi包、java.rmi.registry包和java.rmi.server包 实现远程方法调用,可在远程计算机上创建对象,在本地计算机上使用该对象。
常用包(六) 12.java.security包、java.security.acl包和java.security.interfaces包 13. java.util.zip包 用来实现文件压缩功能。 14.java.awt.datatransfer包 提供了处理数据传输的工具类,包括剪贴板、字符串发送器等。
常用包(七) 15.java.sql包 16.java.swing包 是实现JDBC(Java database connection)的类库。利用它可使Java程序具有访问不同种类数据库的能力,如Oracle、Informix、Sybase、SQL Server、DB2等。 16.java.swing包 Swing 提供许多新的组件,让你容易地建立精致的图形用户界面。它改进旧的组件,并增加许多组件,像是内部框架(internal frames),树,表格,和文字编辑器等等 Swing 组件比 AWT 组件提供更标准化的跨平台运作机制,使我们朝 "write once, run anywhere" 更接近一步。
3.1.2 用户程序自定义类(一) 类的一般格式是: classHead { classBody } 一个类的定义包含有两个部分的内容: { classBody } 一个类的定义包含有两个部分的内容: classHead类头的声明 classBody类体的定义 类体又由属性(域)和方法组成。
3.1.2 用户程序自定义类(二) 类头使用关键字class标志类定义的开始,class后面跟着类名; 类体用一对大括号括起,包括属性(域)和方法两大部分: 其中域对应类的静态属性 方法对应类的行为和操作。 ▼
3.1.3 创建对象与定义构造函数(一) 1.创建对象 创建对象的一般格式为: 类名 新建对象名 = new 构造函数() 例如: PhoneCard myCard = new PhoneCard(); 创建对象与声明基本数据类型的变量类似,赋值号右边的new是为新建对象开辟内存空间的算符。 与变量相比,对象占用的内存空间要大得多,对象是以类为模板创建的具体实例。
3.1.3 创建对象与定义构造函数 (二) 以PhoneCard类为例,它定义了五个域和三个方法,它的对象myCard的内存空间保存的域和方法分别为 myCard.cardNumber myCard.password,myCard.balance myCard.connectNumber,myCard.connnected,myCard.performConnection(),myCard.getBalance() myCard.performDial()。 要访问或调用一个对象的域或方法,首先访问该对象,然后用算符“.”连接该对象的域或方法。 例如:myCard.balance = 50;
3.1.3 创建对象与定义构造函数 (三) 2.构造函数 构造函数是与类同名的方法,创建对象的语句用new算符开辟了新建对象的内存空间后,将调用构造函数初始化这个新建对象。 构造函数是类的一种特殊方法,其特殊性如下: 构造函数的方法名与类名相同。 构造函数没有返回值。 构造函数的作用是对类的对象初始化。 在创建一个类的新对象的同时,系统会自动调用该类的构造函数。
3.1.3 创建对象与定义构造函数 (四) 例如:定义PhoneCard类的构造函数。 然后就可以用如下的语句初始化PhoneCard对象: PhoneCard(long cn, int pw, double b, String s){ cardNumber = cn; password = pw; if(b>0) balance = b; else System.exit(1); connectNumeber = s; connected = false; } 然后就可以用如下的语句初始化PhoneCard对象: PhoneCard myCard = new PhoneCard(20010922, 342323, 50, “200”); ▼
3.1.4 类的修饰符(一) Java程序定义类时,可在class之前增加若干类的修饰符来限定所定义类的特性。 类的修饰符分为 * 访问控制符 * 非访问控制符 有些修饰符可用来修饰类中的域或方法,本节讨论类的非访问控制符。
3.1.4 类的修饰符(二) 1.抽象类 凡是用abstract修饰符修饰的类被称为抽象类,抽象类就是没有具体对象的概念类。 例如:“鸟”是一个抽象类,它可以派生出若干子类如“鸽子”、“燕子”、“麻雀”、“天鹅”等,“鸟”仅仅作为一个抽象的概念存在,它代表所有鸟的共同属性,任何一只具体的鸟都是由“鸟”经过特殊化形成的某个子类的对象。
3.1.4 类的修饰符(三) 例子: 电话卡有很多类型:磁卡、IC卡、IP卡、200卡、300卡、校园201卡。 同时它们也拥有一些共同的特性,如每张卡都有剩余的金额,都有通话的功能,因此,可以定义一种抽象的电话卡类。
3.1.4 类的修饰符(四) 电话卡类 IC卡 磁卡 无卡号类 有卡号类 IP卡 校园201卡 200卡
3.1.4 类的修饰符(五) 2.最终类 如果一个类被final修饰,说明该类不可以有子类了。 被定义为final的类通常是一些有固定作用、用来完成某种标准功能的类,如Java系统定义好的用来实现网络功能的InetAddress、Socket等类都是final类。
3.2 域和方法 域 方法 域是类和对象的静态属性,它可以是基本数据类型的变量,也可以是其它类(系统类或用户自定义类)的对象。 因此,定义域的操作就是说明变量或创建对象的操作。 方法 方法是类的动态属性,标志了类所具有的功能和操作,用来把类和对象的数据封装在一起。 Java的方法与其它语言中的函数或过程类似,是一段完成某种功能的程序段。
3.2.1 域 (一) 1.静态域 用static修饰的域就是静态域。 静态域最本质的特点是:它们是类的域,不属于任何一个类的具体对象,它不保存在某个对象的内存区域中,而是保存在类的内存区域的公共存储单元。 换句话说,对该类的具体对象而言,静态域是一个公共的存储单元。任何一个类的对象访问它,得到的都是相同的值;任何一个类的对象修改它,都是对同一内存单元进行操作。
3.2.1 域 (二) 下面的程序片段定义了两个静态域: class PhoneCard200 { static String connetcNumber = “200”; //所有的200电话卡的接入号码都是“200” static double addtoryFee; //所有的200电话卡的电话附加费相同 long cardNumber; int password; boolean connected; double balance; … } ▼
3.2.1 域 (三) 2.静态初始化器 静态初始化器是由static引导的一对大括号括起来的语句组。它的作用与类的构造函数相似,都是用来完成初始化的工作,但两者也有根本性的不同: 构造函数是对每个新创建的对象初始化,而静态初始化器是对类本身进行初始化。 构造函数是在用new运算符产生新对象时由系统自动执行的,而静态初始化器则是在它所属的类加载入内存时由系统调用执行的。 不同于构造函数,静态初始化器不是方法,没有方法名、参数列表。 ▼
3.2.1 域 (四) 3.最终域 程序中经常需要定义各种类型的常量,并为它们取一个类似于变量的标识符名字,这样就可以在程序中用这个名字来引用常量。 final就是用来修饰常量的修饰符,一个类的域如果被声明为final,则它的取值在程序的整个执行过程中都不会改变。 例如: static final String connectNumber = “200”;
3.2.1 域 (五) 4.易失域 如果一个域被volatile修饰,说明该域可能同时被几个线程所控制和修改,即该域不仅仅为当前程序所掌控,还有其它的程序操作来影响和改变该域。 通常,volatile用来修饰接受外部输入的域,如表示当前时间的域,可由系统的后台线程随时修改。
3.2.2 方法 (一) 方法由方法头和方法体组成,其一般格式如下: 其中形式参数列表的格式为: 修饰符1 修饰符2 …… 返回值类型 方法名(形式参数列表)throws[异常列表] { 方法体各语句; } 其中形式参数列表的格式为: 形参类型1 形参名1,形参类型2 形参名2,……
3.2.2 方法 (二) 1.抽象方法 修饰符abstract修饰的抽象方法是一种仅有方法头,而没有具体的方法体和操作实现的方法。 例如:在抽象类PhoneCard中定义一个抽象方法:abstract void performDial(); 使用抽象方法可使所有PhoneCard类的子类对外呈现一个统一的接口,至于方法体的具体实现,则留到当前类的不同子类的类定义中完成。 注意:所有的抽象方法,都必须存在于抽象类之中。若一个抽象类的子类不是抽象类,则它必须为父类中的所有抽象方法书写方法体。 ▼
3.2.2 方法 (三) 2.静态方法 用static修饰的方法是属于整个类的方法,它的特点如下所示: static方法是属于整个类的方法,它在内存中的代码将随着类的定义而分配和装载,不被任何对象专有;非static方法是属于某个对象的方法,在对象创建时在内存中拥有自己的专用代码段。 static不能操作属于某个对象的成员变量,而只能处理属于整个类的成员变量,即:static方法只能处理static域。
3.2.2 方法 (四) 例如:在PhoneCard200中需要修改附加费,可以定义一个setAdditory()的静态方法: static void setAdditory(double newAdd) { if(newAdd>0) additoryFee = newAdd; }
3.2.2 方法 (五) 3.最终方法 用final修饰的方法是功能和内部语句不能被更改的最终方法。 在面向对象程序设计中,子类可把从父类那里继承来的某个方法改写,形成域父类方法同名,解决的问题类似,但具体实现和功能不全一致的新的类方法,这个过程称为重写(覆写overriding) 。 如果类方法被final修饰了,则该类的子类就不能在重新定义与此方法同名的自己的方法,只能使用从父类继承来的方法。这样,可以防止子类对父类的关键方法的错误的重定义,保证了程序的安全性和正确性。 注意:所有被private修饰的方法,以及包含在final类中的方法,都被缺省认为是final的。
3.2.2 方法 (六) 4.本地方法 native修饰符一般用来声明用其它语言书写的方法。 由于native方法是以非Java字节码的二进制代码形式嵌入Java程序的,所以整个Java程序的跨平台性能将受到限制或破坏,因此使用这类方法时应特别谨慎。
3.2.2 方法 (七) 5.同步方法 synchronized修饰符主要用于多线程共存的程序中的协调和同步。 如果synchronized修饰的是类的方法(static方法),那么在被调用前将对应当前类的对象加锁。 如果synchronized修饰的是一个对象的方法(非static方法),则在被调用前将当前对象加锁。
3.3 访问控制符 访问控制符是一组限定类、域或方法是否可以被程序里的其它部分访问和调用的修饰符。 类的访问符只有public。 域和方法的访问控制符有三个: public private protected
3.3.1公共访问控制符public(一) 1.类 Java的类是通过包来组织,处于同一包中的类可以不需任何说明而方便的互相访问和引用,而对于处于不同包中的类,缺省的它们是互相之间不可见的。 但是,当一个类被声明为public时,它就具有了被其它包中类访问的可能性,只要这些包中的类在程序中使用import语句引入public类,就可访问它了。 一个类作为整体可见,并不能代表类中的域和方法也一定可见,除非它们也被声明为public。 C++中没有包的概念,所以没有类的修饰符。
3.3.1公共访问控制符public(二) 2.方法 被定义为public的方法是这个类对外的接口部分,程序的其它部分通过调用public方法达到与当前类交换信息、甚至影响当前类的目的。 如果一个类希望作为公共工具供其它的类和程序使用,则应该把类和方法定义为public。 Java程序中如果有多个类,又有public修饰符,则主类必须是public类,且只能有一个public类。
3.3.1公共访问控制符public(三) 3.域 用public修饰的域被称为公共域,如果一个公共域属于一个公共类,则它可被所有其它类所引用。 public修饰符会造成安全性和数据封装性下降,所以一般应减少public域的使用。
3.3.2缺省访问控制符 假如一个类没有访问控制符,缺省的认为该 类只能被同一个包中的类访问,这种访问特 性又称包访问性。 同理,类内的域和方法如果没有访问控制符, 也说明它们具有包访问性,可以被同一个包 中的其它类所访问和调用。
3.3.3私有访问控制符 (一) 用private修饰的域或方法只能被该类自身所访问和修改,而不能被任何其它类(包括该类的子类)来获取和引用。 private修饰符提供了最高的保护级别。 例如:在200电话卡类PhoneCard200中,电话卡的密码password不能允许其它类或对象随意查看或修改,所以这个域可以声明为私有的: private int password; 注意:在其它类中即使使用PhoneCard200.password也不能访问该域。
3.3.3私有访问控制符 (二) 当其它类希望获取或修改私有成员时,需要借助于类的方法来实现。 比如可以在类PhoneCard200中 定义方法setPassword()来修改密码; 从而把password完全包裹保护起来。 定义方法getPassword()来获取密码; 同时,为保证只有具备一定权限才能查看或修改密码,可在getPassword()方法和setPassword()方法中做必要的安全性检查,满足了一定条件才能获得或修改变量password的数值,从而保证了私有数据的私有性。
3.3.4保护访问控制符protected 用protected修饰的域和方法可以被三种类引用: 该类自身; 与它在同一个包中的其它类; 在其它包中的该类的子类。 使用protected修饰符的主要作用是允许包中的类和其它包中的子类来访问父类的特定属性。
修饰符使用需要注意的问题 abstract不能与final并列修饰同一个类。 abstract不能与private、static、final或native并列修饰同一个方法。 abstract类中不能有private的成员(包括域和方法)。 abstract方法必须在abstract类中。 static方法中不能处理非static属性。
3.4 继承(一) 当一个类拥有另一个类的数据和操作时,就称这两个类之间具有继承关系,被继承的类称为父类或超类,继承的类称为子类。 3.4 继承(一) 当一个类拥有另一个类的数据和操作时,就称这两个类之间具有继承关系,被继承的类称为父类或超类,继承的类称为子类。 一个父类可以同时拥有多个子类,该父类实际上是所有子类的公共域和公共方法的集合,而子类是父类的特殊化,可对公共域和方法在功能、内涵方面加以扩展和延伸。
3.4 继承(二) 面向对象的继承特性中,还有一个关于单继承和多继承的概念。 3.4 继承(二) 面向对象的继承特性中,还有一个关于单继承和多继承的概念。 单继承是指任何类都只有一个父类。 多继承是指一个类可以有一个以上的父类,它静态数据和操作从所有这些父类中继承的。 Java只支持单重继承,但支持界面,一个类可以实现多个界面。利用界面可以得到多继承的优点,又没有多继承混乱、复杂的问题。
3.4.1派生子类 (一) Java中的继承是通过extends关键字来实现的(C++通过public和private)。 如果子类和父类不在同一个包中,它可以从父类那里继承protected、public的的域和方法作为自己的成员 例如:class 子类名 extends 父类名
3.4.1派生子类 (二) C++中定义子类的方法如下: class 子类名: public 父类名 class 子类名: private 父类名 父类 派生子类 公有派生 public protected private 不能访问 私有派生
3.4.2域的继承与隐藏 (一) 1.域的继承 子类可以继承父类的所有非私有域。 ▼ 下面以电话卡类来说明域的继承: PhoneCard类: 子类可以继承父类的所有非私有域。 ▼ 下面以电话卡类来说明域的继承: PhoneCard类: double balance; None_Number_PhoneCard类: double balance; //继承自父类PhoneCardString PhoneSetType;
3.4.2域的继承与隐藏 (二) Number_PhoneCard类: double balance; //继承自父类PhoneCard long cardNumber; int password; String connectNumber; boolean connected ; MagCard类: double balance; //继承自父类No_Number_PhoneCard String PhoneSetType; String usefulArea;
3.4.2域的继承与隐藏 (三) IC_Card类: double balance; //继承自父类No_Number_PhoneCard String PhoneSetType;
3.4.2域的继承与隐藏 (四) IP_Card类: double balance;//继承自父类Number_PhoneCard long cardNumber;//继承自父Number_PhoneCard int password;//继承自父类Number_PhoneCardString connectNumber;//继承自父类Number_PhoneCard boolean connected;//继承自父类Number_PhoneCard Date expireDate;
3.4.2域的继承与隐藏 (五) D200_Card类: double balance; //继承自父类Number_PhoneCard long cardNumber;//继承自父Number_PhoneCard int password;//继承自父类Number_PhoneCardString connectNumber; //继承自父类Number_PhoneCard boolean connected;//继承自父类Number_PhoneCard double additoryFee;
3.4.2域的继承与隐藏 (六) 2.域的隐藏 子类重新定义一个从父类那里继承来的域变量完全相同的变量,称为域的隐藏。 自父类继承的方法操纵父类被隐藏的域。 例如,把例3.4.1中对200电话卡子类的定义修改为:
3.4.2域的继承与隐藏 (七) class P200_Card extends Number_PhoneCard { double additoryFee; double balance; //父类已定义过 boolean performDial() { if(balance>(0.3+additory)) { balance-=(0.3+additory); return true; } else return false; } }
3.4.2域的继承与隐藏 (八) 上面的类定义中,增加定义了与从父类继承来的balance变量完全相同的变量,这样,P200_Card类的域变为: double balance; //继承自父类Number_PhoneCard double balance; // P200_Card类自己定义的域 long cardNumber; //继承自父类Number_PhoneCard int password; //继承自父类Number_PhoneCard String connectNumber; //继承自父类Number_PhoneCard boolean connected; //继承自父类Number_PhoneCard double additoryFee;
3.4.3 null、this与super Java系统默认每个类缺省地都具有三个域:null、this与super,所以在任意类中都可以不加说明而直接使用它们。
1.null (一) null表示“空”变量,用于指代某一对象,但是,这个对象是不存在相应的实例的。 例如:Point pNull = null; 它创建的是Point类的变量pNull,但不能创建相应的对象。即pNull是不属于任何实例对象的,相当是个“空壳”。 在方法需要用对象作为参数时,可以用null来代替。 例如:drawPoint(null);
2.this (一) this表示的是当前对象本身,更准确地说, this代表当前对象的一个引用。 对象的引用可以理解为对象的另一个名字, 通过引用可以顺利地访问到对象,包括访 问、修改对象的域、调用对象的方法。 ▼
2.this (二) 在上例的构造方法中,用this来区分参数(不带this)和成员变量(带this)。 在参数名与成员名出现相同时,Java是按参数优先并用相同的名字隐藏了成员变量,所以要指向成员变量,必须通过this明确当前对象中的成员。 使用this会增加代码的清晰度,减少基于名字相关的错误。 也可以使用this调用当前对象的方法,这只有在方法名有歧义和增加代码清晰度时才是必要的。
3.super 子类对父类的同名成员变量和方法,分别实行隐藏和覆盖。但是,有时需要在子类中访问父类的变量,父类的方法,Java提供的super就可以实现这访问。它的使用有三种情况: 访问被隐藏的父类成员变量;如: super.VariableName 调用父类中被覆盖的方法;如: super.MethodName 调用父类中的构造方法;如: super([paramList]) ▼
3.5 多态性(polymorphism) 多态性是面向对象程序设计的又一特殊性。 面向过程的语言编程的主要工作是编写一系列过程或函数,这些过程和函数各自对应一定的功能,它们之间不能重名,否则在调用时,就会产生歧异和错误。 而在面向对象的程序设计中,有时却需要利用这样的“重名”现象来提高程序的抽象度和简洁性。 3.5.1方法的继承 ▼
3.5.2方法的覆盖 子类可以定义与父类同名的方法,实现对父类方法的覆盖(Overriding)。 方法的覆盖与域的隐藏的区别为: 子类隐藏父类的域只是使之不可见,父类的同名域在子类对象中仍然占有自己独立的内存空间; 而子类方法对父类同名方法的覆盖将清除父类方法占用的内存,从而使父类方法在子类对象中不存在。
3.5.3 重载方法的重载(overload) 是实现多态技术的重要手段。 与方法的覆盖不同,重载不是子类对父类同名方法的重新定义,而是类对自身已有的同名方法的重新定义。 由于重载发生在一个类里,不能用类名来区分不同的方法,所以采用不同的形式参数列表,包括形式参数的个数、类型、顺序的不同,来区分重载的方法。 ▼
3.5.4 构造函数的继承与重载 (一) 1.构造函数的继承 子类可以继承父类的构造函数,构造函数的继承遵循以下的原则: 子类无条件地继承父类的不含参数的构造函数。 若子类没有定义自己的构造函数,它将继承父类无参数的构造函数作为自己的构造函数。 若子类定义了自己的构造函数,它先执行继承自父类的无参数构造函数,在执行自己的构造函数。 对父类含参数的构造函数,子类可以通过在定义自己的构造函数中使用super关键字来调用它,但这个调用语句必须是子类构造函数的第一个可执行语句。
3.5.4 构造函数的继承与重载 (二) 2.构造函数的重载 构造函数的重载是指同一个类中存在若干个具有不同参数列表的构造函数,创建该类对象的语句会自动根据给出的实际参数的数目、类型和顺序来确定调用那个构造函数来完成对新对象的初始化工作。 一个类的若干个构造函数之间可以互相调用,当一个构造函数调用另一个时,可以使用this关键字,但该调用语句应是整个构造函数的第一个可执行语句。 ▼
3.6 上转型对象 假设A类是B类的父类,当我们用子类创建一个对象,而这个对象的引用放到父类的对象中时。 A a; a = new B( ); 或 A a; B b = new B( ); a = b; 称这个父类对象a是子类对象的上转型对象。 例如:“老虎是哺乳动物”,哺乳类是老虎类的父类,但这样说将失掉老虎独有的属性。
3.6 上转型对象 上转型对象不能操作子类新增的域和方法。 上转型对象可以操作子类继承或重写的域和方法。 如果子类重写了父类的某个方法,上转型对象调用该方法时,是调用的重写方法。
3.7 接口(一) 接口(interface)也称为界面,在其声明语法上有些类似于类。 实际上,完全可以把接口理解为一种特殊的类,一种由常量和抽象方法组成的特殊类。 在Java中,出于简化程序结构的考虑,不支持类间的多重继承而只支持单重继承,即一个类至多只能有一个直接父类。 接口是用来实现类间多重继承功能的结构。
3.7 接口(二) 然而,接口的实现功能比多重继承更强。 接口把方法的定义和类的层次区分开来,通过它可以在运行时动态地定位所调用的方法; 同时,也可以实现“多重继承”,且一个类可以实现多个接口。 正是这些机制使得接口提供了比多重继承更简单,更灵活,而且更强健的功能。
3.7.1 接口的声明(一) 接口的定义格式为: interfaceDeclaration { interfaceBody } 其中:
3.7.1 接口的声明(二) 1. 接口声明部分 [public] interface 接口名 [extends 父接口1,父接口2,…] { ... }
3.7.1 接口的声明(三) 其中: public指明任意类均可以使用这个接口。在缺省情况下,只有与该接口定义在同一个包中的类才可以访问这个接口。 extends子句与类声明中的extends子句基本相同,不同的是一个接口可以有多个父接口,用逗号隔开,而一个类只能有一个父类。子接口继承父接口中所有常量和方法。
3.7.1 接口的声明(四) 2. 接口体的定义 接口体中包括常量定义和方法定义,其格式如下所示: type constantName = Value; returnType methodName([paramList]); 在接口中定义的常量可以被用来实现该接口的多个类共享,与C语言中的const定义常量是相似的。 在接口中定义的常量必须是pubilc static final,这是系统默认的规定,所以常量也可以没有任何修饰符。
3.7.1 接口的声明(五) 接口中只有方法声明,而无方法实现,所以,方法定义没有方法体,且用分号作为结束。 接口中声明的方法必须是public abstract。如果方法体是用其他语言书写的,则该接口方法可以用native修饰符修饰。 另外,如果在子接口中定义了和父接口同名的常量或相同的方法,则父接口中的常量被隐藏,方法被覆盖。
3.7.1 接口的声明(六) 例: interface Collection { int MAX_NUM=100; void add (Object objAdd); void delete (Object objDelet); Object find (Object objFind); int currentCount(); } 接口定义中声明了一个常量和四个方法。这个接口可以由队列、堆栈、链表等来实现。
3.7.2 接口的实现(一) 接口的声明仅仅给出了抽象方法,要具体地实现接口所规定的功能,则需某个类为接口中的抽象方法定义实在的方法体,这就称为接口的实现。 在类的声明中,用implements子句表示一个类将要实现某个接口,在类体中可以引用接口中定义的常量,而且必须实现接口中定义的所有方法。一个类可以实现多个接口,在implements子句中用逗号分隔。 ▼
3.7.2 接口的实现(二) 注意: 在类中实现接口所定义的方法时,方法的声明必须与接口中所定义的完全一致。 在类中实现接口所定义的方法时,必须显式地使用public修饰符,否则将被系统警告为缩小了接口中定义的方法的访问控制范围。 抽象类可以不实现接口的抽象方法,而非抽象类必须实现接口中的所有方法。
3.7.3 接口的回调 可以把实现某一接口类创建的对象的引用赋给该接口声明的接口变量,那么接口变量就可以调用被类实现的接口中的方法。 ▼
3.7.4 接口做参数 //InterPara.java源文件 interface SpeakHello { void speakHello(); } class Chinese implements SpeakHello public void speakHello() System.out.println("中国人的习惯用语:你好,吃饭了吗?");
class English implements SpeakHello { public void speakHello() System.out.println("英国人的习惯用语:你好,天气不错!"); } class KindHello public void lookHello(SpeakHello hello) hello.speakHello();
public class InterPara { public static void main(String args[]) KindHello kindHello=new KindHello(); kindHello.lookHello(new Chinese()); kindHello.lookHello(new English()); }
3.8 包(package) 利用Java语言开发实际系统时,可以利用“包”来管理类。包是松散的类的集合,为了方便编程和管理,通常把需要在一起工作的类放在一个包里。 为Java创建一个源码文件的时候,它通常叫做一个“编辑单元”,每个编辑单元必须有一个以java结尾的名字。 在编辑单元的内部,可以有一个与文件同名的public主类(也可以是非public主类),但只能有一个public类。
3.8.1 创建包 (一) 缺省情况下,系统为每个.java源文件创建一个无名包,该文件中定义的所有类都隶属于该包,但由于该包无名字,所以不能被其它包引用。为了解决这个问题,就要创建有名包。 用package关键字创建包,而且该语句是.java源文件的第一个语句。 package 包名; 例如: package cardClass; package cardSystem.cardClass;
3.8.1 创建包 (二) 实际上,创建包就是在当前目录下创建一个子目录,以便存放这个包中包含的所有类的.class文件。 上面的第二个语句中的“.”代表了目录分割符。即包中的类放在当前目录下的CardSystem子目录下的CardClass子目录下。
3.8.2 包的引用 (一) 缺省的情况下,一个类只能引用同一个包中的类,如果需要使用其它包中的public类,可使用如下的方法。 1.使用包名、类名前缀 对于同一个包中的其它类,只需在要使用的属性或方法名前加上类名作为前缀,对于其它包中的类,则需要在类名前缀的前面再加上包名前缀。 例如: CardClass.P200_Card my200 = new CardClass.P200_Card(); System.out.println(my200.toString());
3.8.2 包的引用 (二) 2.import语句 加前缀的方法使用起来非常麻烦,可以使用import语句加载需要使用的类或包。 例如:加载类 import CardClass.P200_Card; P200_Card my200 = new CardClass.P200_Card(); 例如:加载包 import CardClass.*; import java.awt.*;
CLASSPATH的设置 环境变量CLASSPATH类似于DOS下的PATH,它指明了所有缺省的类字节码文件路径。 CLASSPATH= .; C:\Program Files\Java\jre1.5.0\lib\rt.jar; D:\java_class; 例子:我们有一个类是chen.Rose,编译好后怎么存放呢? 我们可以在d:盘的java_class目录下建立一个chen子目录,然后把Rose.class文件复制到该子目录下: d:\java_class\chen\Rose.class 若需引用:import chen.Rose; 或 import chen.*;
CLASSPATH的设置 一些公司把它们的一组类往往打包发行,把这个包压缩成Jar或Zip,设置的方法如下: set classpath="d:\java_class; d:\oracle\ora.jar" 你只需把文件名包含到classpath中去即可正确地引用该压缩包中的类了。 对于jdk1.3以上的版本,引用java.*,sun.*等系统包不需要设置classpath。
综合习题一 根据要求编程实现复数类ComplexNumber。 1)复数类ComplexNumber的属性 realPart:实部,imageinPart:虚部。 2)复数类ComplexNumber的方法 ComplexNumber():构造函数,将实部和虚部置零。 ComplexNumber(double r,double I):构造函数,将实部和虚部分别置为r和i。 getRealPart() :获得复数对象的实部。 getImageinPart() ;获得复数对象的虚部。 complexAdd(ComplexNumber c) ;当前复数对象与形式参数复数对象相加,所得结果为复数对象,返回调用者。 complexMulti((ComplexNumber c) ;当前复数对象与形式参数复数对象相乘,所得结果为复数对象,返回调用者。 toString() ;把当前复数对象,以a+bi的字符串形式组合起来,a为实部,b为虚部。
综合习题二 要求把习题一的类打包到comp中。 实现类ImpComplex,使用包comp中的方法,既要求import该包。