第六章 深入面向对象的程序设计一、继承与重载二、接口三、包四、方法五、错误、异常及其处理一、继承与重载
1、类的继承
在 Java 中,类是一个层次结构,其中有一个被称为 Object的特殊超类,所有的类都直接或间接地继承 Object类
子类 继承 父类的属性和方法,同时也可以修改 父类的属性或 重载 父类的方法,以及在父类的基础上 添加 新的属性和方法。因此,
父类和子类 之间反映的是,一般与特殊,的关系子类的定义格式
[修饰符 ] 子类名 extends 父类名
{ 类体; }
,注意,
1、没有 extends,默认父类为 Object
2、只能有一个父类,即单继承
3、子类继承父类的全部成员
4、类继承具有传递性
class B
{
int b1 = 1;
public int b2 = 2;
protected int b3 = 3;
private int b4 = 4;
int getb4( ) { return b4; }
}
class A extends B
{
int a = 5;
int Sum( ) { return b1 + b2 + b3 + getb4( ) + a ; }
}
public class ClassInheritance
{
static public void main(String arg[ ])
{
B bb = new B( );
System.out.println("B,"+bb.b1+" "+bb.b2+
" "+bb.b3+" "+bb.getb4( ));
A aa = new A( );
System.out.println("A,"+aa.b1+" "+aa.b2+
" "+aa.b3+" "+aa.getb4( ) +
" "+aa.a + " " + aa.Sum( ) );
}
}
class B
{
int b1 = 1;
public int b2 = 2;
protected int b3 = 3;
private int b4 = 4;
int getb4( ) { return b4; }
void setb4( int i ) { b4 = i; }
}
class A extends B
{
int a = 5;
}
class C extends A
{
int c = 6;
void change( int increase )
{
b1 += increase;
b2 += increase;
b3 += increase;
setb4 ( getb4( ) +
increase );
a += increase;
c += increase;
}
}
public class CPP
{
static public void main(String arg[ ])
{
C cc = new C( );
System.out.println("C,"+cc.b1+" "+cc.b2+" "+
cc.b3+" "+cc.getb4( ) +" "+cc.a+" "+cc.c);
cc.change( 15 );
System.out.println("C,"+cc.b1+" "+cc.b2+" "+
cc.b3+" "+cc.getb4( ) +" "+cc.a+" "+cc.c);
}
}
子类与父类的属性与方法
子类继承父类的 所有内容,但父类中的
private部分不能直接访问
子类中新增加的属性和方法是对父类的 扩展
子类中定义的与父类同名的属性是对父类属性的 隐藏,同名的方法是对父类方法的 重载或覆盖类的成员覆盖
1、类成员覆盖是指在子类中新增加的成员变量或成员方法的名称与父类相同。
2、成员变量:在子类中,访问的是子类定义的成员变量,若要使用父类的成员,需要使用关键字 super 作为前缀;
3、成员方法:若参数表完全一样,欲使用父类中的成员函数,也需要使用 super 作为前缀

class A
{ int x = 1234;
void show( )
{ System.out.println("class A,"); }
}
class B extends A
{
double x = 567.89;
void show( )
{
super.show( );
System.out.println("class B,");
}
}
class C extends B
{
char x = 'c';
void showABC( )
{
System.out.println(super.x);
System.out.println(x);
super.show( );
show( );
}
void show( )
{
System.out.println("class
C,");
}
}
public class OverTest
{
static public void main(String arg[ ])
{
C cc = new C( );
cc.showABC( );
}
}
不能通过 super.super.x来引用隐藏在另一个基础类的基础类中的成员方法的重载,
类对自身已有的同名方法的重新定义。
通过参数列表来区分不同的方法。
参数列表的不同表现在:
参数类型不同、参数数目不同、顺序不同注:不能通过返回值的不同来区分同名的不同方法
2、子类的构造函数
1、子类继承父类无参构造函数
2、若子类无构造函数,则它将继承父类的无参构造函数作为自己的构造函数;若子类定义了构造函数,则在创建新对象时,先执行父类的无参构造函数,再执行自己的构造函数
3、对于父类中含参构造函数,子类可以在自己的构造函数中使用 super调用它。但必须是第一个可执行语句
class A
{ private int a;
A( ) { a = 0; }
A(int i) { a = i; }
int geta( ) { return a; }
}
class B extends A
{
double b1,b2;
B( ) { this(11.1,12.2); }
B(double d1,double d2) { b1 = d1; b2 = d2; }
B(int i,double d1,double d2) { super(i); b1 = d1; b2 = d2; }
B(int i,double d) { super(i); b1 = d; }
B(double d) { super(4); b2 =d; }
}
当一个构造函数需要调用另一个构造函数时应使用关键字 this
public class DCI
{
static public void main(String arg[ ])
{
B bb1 = new B( );
System.out.println(bb1.geta( )+" "+bb1.b1+" "+bb1.b2);
B bb2 = new B(2.2,3.3);
System.out.println(bb2.geta( )+" "+bb2.b1+" "+bb2.b2);
B bb3 = new B(4,5.5,6.66);
System.out.println(bb3.geta( )+" "+bb3.b1+" "+bb3.b2);
B bb4 = new B(7,8.8);
System.out.println(bb4.geta( )+" "+bb4.b1+" "+bb4.b2);
B bb5 = new B(99.9);
System.out.println(bb5.geta( )+" "+bb5.b1+" "+bb5.b2);
}
}
三、类对象之间的类型转换
1、子类对象转换为父类对象时,可以用显式或隐式实现,即子类对象可以直接向父类对象赋值;
2、父类对象向子类对象赋值时,必须使用显式转换,即强制类型转换。
class A
{
String s = "class,A";
}
class B extends A
{
String s = "class,B";
}
public class Type
{
static public void main(String arg[])
{
B b1,b2 = new B( );
A a1,a2;
a1 = (A)b2;
a2 = b2;
System.out.println(a1.s);
System.out.println(a2.s);
b1 = (B)a1;
System.out.println(b1.s);
}
}
使用 Object作为一个通用的超类还带来的影响:一方面,一个 Object类型的变量可以容纳任何类的对象,当你想编写一个方法来处理未知类型对象的时候这种结果会有用,你可以用一个 Object类型的变量作为一个方法的形式参数来接受一个对象,
然后在方法中包含一些代码指出这实际上是什么类型的对象二、接口交通工具陆上 海上 空中自行车卡车轮船 皮划艇 喷气机滑翔机交通工具类的层次结构交通工具陆上 海上 空中自行车卡车轮船 皮划艇 喷气机滑翔机实现了接口交通工具类的层次结构可燃油的接口可以被用来实现类间多继承结构。接口内部只能定义 public 的 抽象方法 和 静态的,
公有常量,因此所有的方法需要在子类中实现。
接口的定义格式
[修饰符 ] interface 接口名称 [extends 父接口名 ]
{//接口体
//常量域声明
[public] [static] [final] 域类型 域名 =常量值;
//抽象方法声明
[public] [abstract] [native] 返回值 方法名(参数列表) [throw 异常列表 ];
}
由于接口中定义的方法都是抽象、公有的,常量都是静态、公有的,所以 修饰符可以省略。
interface A
{
double g=9.8;
void show( );
}
class B implements A
{
public void show( )
{System.out.println(“g=“+g);}
}
public class InterfaceTest
{
public static void main(String
args[ ])
{
B b=new B( );
b.show( );
}
}
interface A
{
int a=1;
void showa( );
}
interface B extends A
{
int b=2;
void showb( );
}
interface C
{
int c=3;
void showc( );
}
interface D extends B,C
{
int d=4;
void showd( );
}
接口的继承举例
class E implements D
{ int e=5;
public void showa( ){System.out.println(“a=“+a);}
public void showb( ){System.out.println(“b=“+b);}
public void showc( ){System.out.println(“c=“+c);}
public void showd( ){System.out.println(“d=“+d);}
public void showe( ){System.out.println(“e=“+e);}
}
public class Interface_H_C
{
public static vois main(String args[ ])
{
E ee=new E( );
ee.showa( );
ee.showb( );
ee.showc( );
ee.showd( );
ee.showe( );
}
}
三、包包是一组相关的类和接口的集合。
将类和接口分装在不同的包中,可以避免重名类的冲突,限定包之间类的访问权限,更有效地管理众多的类和接口。
包的创建与使用
package pk1[.pk2[.pk3...]];
包是分层管理的,其含义类似于目录。
若使用一个包中类,只需在程序的第一行写上:
import 包名;
若需将类放在包中,只需在程序的第一行写上:
package 包名;
默认的包在默认情况下,系统会为每个,java
源文件创建一个无名包,该,java文件中定义的所有类都隶属于这个无名包
,它们之间可以相互引用。但是由于这个无名包是没有名字的,所以它不能被其他包所引用。
四、方法:
前面我们已讨论过类、接口、包、字段及方法。
本章,我们要继续针对方法作一较深入的探讨。
不过首先我们需陈述一个概念:就面向对象程序设计而言,对象对于成员字段及成员方法的存取概念是一样的,而用修饰符所控制的权限几乎是相同的。只不过字段代表的是一个存储数据或参考地址,而方法代表的是一段特别的程序代码 。
方法、函数、过程另外有一个容易搞混淆的概念就是:方法( method)与函数
( function)、过程( procedure)有何不同?在 C及 C++语言里,
函数与过程有点类似,这二者的写法都相同,都是一段程序代码。当程序调用函数或过程时,程序主权便会交给它们,等到其内的程序代码执行完毕,又将主权交回过程本身。但二者的不同在于:过程的回传值类型为 void,意思是不回传任何值,而函数有回传值,类型并不为 void。但是因着 Java是完全对象化的语言,不允许单独的过程与函数存在,因而在 Java中并没有使用函数与过程,而是使用方法。方法必须存在一类(对象)中 。
类中的成员一个类中包含了四种成员:类、接口、字段及方法,
所以我们称此四者为成员类、成员接口、成员字段与成员方法。而这些成员由超类、上层接口以及在类主体中声明而来的。但是构造函数却不是成员。
这四种成员的名称在一个类中可以用一样的,只要在同一种成员中的名称是唯一的。也就是说可以取某一名称的类与一个同名称的接口,其字段与方法也相同 。
方法的调用在程序中要如何调用已经声明的方法呢?这个要根据下列几种情况而有所不同 。
A.欲调用的方法是否有回传值
1.若方法有回传值,则将此方法的调用当作一个数值来处理。(数值类型与回传值的类型需一致 )
例,int x= avg(a,b,c);
有时可直接使用此方法的调用,而不用再另行设置一个变量来存储回传值例,System.out.println(avg(a,b,c));
2.若方法没有回传值,就可自行调用例,equipment();
调用方法的所在位置基本上 Java是面向对象的语言,对于方法的调用,
最标准的写法是以
〔 对象 〕,〔 方法 〕 的方式来调用。但有时也可简便的直接调用方法,不过这常常会容易搞混,因为有时要用 this,有时用 Super,现将方法调用的各种情形分类整理如下表 。
调用方法的所在位置是指:程序中调用一个方法的所在位置,通常是在 类方法 或 实例方法 中。而方法调用的写法就要看 被调用的方法所在位置 。被调用的方法有可能是在本身这个类中,也有可能是在超类或其他的类中 。
呼叫方法的所在位置类别方法 实例方法类别方法 A
直接呼叫,
〔类别方法 A 〕或
〔本类别实例〕,〔类别方法
A 〕
但不能用 this
直接呼叫:〔类别方法 A 〕

〔 this 〕,〔类别方法 A 〕
均可本类别实例方法 B
〔本类别实例〕,〔实例方法
B 〕
但不能用 this,也不能直接呼叫直接呼叫:〔实例方法 B 〕

〔 this 〕,〔实例方法 B 〕
均可类别方法 C
〔上类别实例〕,〔类别方法
C 〕

[ 其他对象实例 ],[ 类别方法 C]
〔上类别实例〕,〔类别方法
C 〕或
[ sup e r ],〔类别方法 C 〕

[ 其他对象实例 ],[ 类别方法
C]
被呼叫方法上类别

其他对象实例方法 D
〔上类别实例〕,〔实例方法
D 〕

[ 其他对象实例 ],[ 实例方法
D]
〔上类别实例〕,〔实例方法
D 〕或
[ sup e r ],〔实例方法 D 〕
均可

[ 其他对象实例 ],[ 实例方法
D]
上表的原则就是,
1,this与 super不能用在由 static修饰的类方法里。若放在其中,则会产生编译错误信息,
non-static variable this cannot be referenced from a static context
non-static variable super cannot be referenced from a static context
2.在类方法中可直接调用本身类方法,但不可直接调用实例方法 。
3.在实例方法中可直接调用本身类中的类方法与实例方法 。
4,this与 super能用在实例方法中。
5,〔 xx实例 〕,〔 xx方法 〕 的方式可用于任何情况里。
方法的参数传递在前面方法的声明格式里,有一个<参数行>。在方法声明<参数行>里的参数,
称为形式参数。等到方法真正要被调用时,
才被变量或其他数据所取代,而这些被称为实际参数。要调用一个方法,需提供实际参数,(当然参数是选择性的,并非绝对必须),而这些实际参数的类型与次序,
需完全与每一个相对应的形式参数吻合。
这就是所谓的“参数次序关系” 。 而方法的参数传递,依参数的类型,分为传值调用与传址调用两种 。
传值调用与传址调用若方法的参数之类型为原生类的话,称为传值调用。若方法的参数之类型为参考类型的话,
则称为传址调用。传值调用并不会改变所传之参数的真正数值,因所传的是参数的内容数值,
而传址调用因所传的参数是一个参考,便会改变其所指到的地址之数值 。
例,传值与传址调用
public class PassingParam
{
static class OneObject{
public String Y="a";
}
static void changeParam(int XX,OneObject oobject1){
XX=3;
oobject1.Y="A";
}
public static void main(String args[]){
int X=1;
OneObject oobject=new OneObject();
System.out.println("传递前的参数值,x="+X+"y="+oobject.Y);
changeParam(X,oobject);
System.out.println("传递后的参数值,x="+X+"y="+oobject.Y);
}
}
执行结果,
传递前的参数值,x=1y=a
传递后的参数值,x=1y=A
五、异常程序中的错误
,编译错误,
由于所编写的程序存在语法问题,未能通过由源代码到目标代码的编译过程而产生的错误。
它将由语言的编译系统负责检测和报告。
,运行错误,
在程序的运行过程中产生的错误。
异常、错误一支程序在执行时期可能会遇到一些非预期的或不正常的的错误。 Java的异常处理机制,便是提供便利又强大的执行时期错误处理功能,来处理这类的状况 。
在 Java里,将这些错误分作异常( Exception)与错误( Error)两大类。 Exception类描绘着一些由程序本身及环境所产生的错误。而 Error类则描绘一些较少会发生的内部系统错误。异常( Exception)可以被抓住并处理。但错误( Error)发生时,通常都不太能做什么,就只能通知使用者关闭程序 。
Java对异常的处理机制
Java使用异常对 Java程序给出一个统一和相对简单的 抛出 和 处理 错误的机制。如果一个方法本身能抛出异常,当所调用的方法出现异常时,调用者可以 捕获 异常使之得到处理;也可以 回避 异常,这时异常将在调用的堆栈中向下传递,直到被处理。
发现异常自行处理必须解决 自行解决或将其抛出。
发现异常不自行解决,将其抛出。
a b c d
异常类
Object
Throwable
Error Exception
AWTException IOException RuntimeException
Error 类对象由 Java 虚拟机生成并抛出;
Exception 类对象由应用程序处理或抛出。
Throwable类
一个 Java异常,指的是一个继承 Throwable类之类的实例。 Throwable类与其两个次类:
Exception与 Error均被放在 java.lang包中。而其再往下的次类,则是被放在不同的包中 。
Exception
Error
异常处理的方式异常处理的方式有两种。第一种是将可能发生异常的程序区块用 try…catch
的方式,将可能发生异常的程序区块包起来。若发生了异常,程序会依照所发生 异常的种类,而跳到 该异常种类的 catch区 去作异常处理
try…catch
try{
//可能会发生异常的程序区块;
}catch(异常类 1 异常参考名称 1){
异常处理程序 1
}catch(异常类 2 异常参考名称 2){
异常处理程序 2
}…
finally{
最终处理程序

1,try代码块中包含的是可能引起一个或者多个异常的代码,你希望捕获的会抛出异常的代码必须位于 try中
2,catch代码块包含着计划用于处理一个 try中可能抛出的某种特定类型异常的代码
3,finally代码块总是在方法结束之前执行,而不论是否在 try块中有任何异常抛出
throws与 throw
异常处理的第二种方式,是在方法的参数传递时,可能会发生错误,所以在方法的开头,可声明在此方法中用 throws的异常类。
然后在方法中,遇到异常发生条件成立时,用 throw将异常触发 。
< 方法名称>(<参数行>) 〔 throws<异常类 1>,<异常类 2
> … 〕

if(异常条件 1成立 )
throw new异常类 1();
if(异常条件 2成立 )
throw new异常类 2();


import java.io.*;
public class ExcepHandling{
static float c;
public static void main(String Args[]){
BufferedReader readin=new BufferedReader(new InputStreamReader(System.in));
try{
System.out.println("请任意输入一个被除数 (数字 ) ");
String input1=readin.readLine();//会产生 IOException
float a=Float.parseFloat(input1);//会产生 NumberFormatException
System.out.println("请任意输入一个非零的除数 ");
String input2=readin.readLine();//会产生 IOException
float b=Float.parseFloat(input2);//会产生 NumberFormatException
c=division(a,b);
}catch(IOException ioe){
System.out.println("系统输出入有问题 ");
System.out.println(ioe.getMessage());
System.out.println("程序无法处理即将中断 ");
System.exit(0);
}catch(NumberFormatException nfe){
System.out.println("您所输入的数值是,");
System.out.println(nfe.getMessage());
System.out.println("程序无法处理即将中断 ");
System.exit(0);}
例 1
catch(ArithmeticException ae){
System.out.println(ae.getMessage());
System.exit(0);
}finally{
System.out.println("两数相除的结果是 "+'\n'+c);
System.exit(0);
}
}
static float division(float arg1,float arg2)throws ArithmeticException{
if(arg2==0) throw new ArithmeticException("除数不能为 0,
否则结果是无限大 ");
float result;
result=arg1/arg2;
return result;
}
}
执行结果 1:
请任意输入一个被除数 (数字 )
6y
您所输入的数值是:
6y
程序无法处理即将中断执行结果 2:
请任意输入一个被除数 (数字 )
56
请任意输入一个非零的除数
0
除数不能为 0,否则结果是无限执行结果 3:
请任意输入一个被除数 (数字 )
45
请任意输入一个非零的除数
9
两数相除的结果是
5.0
NumberFormatException异常会由 valueOf()或者
decode()方法在表示整数的类中,即表示 Byte,short、
Integer,Long类型的类中抛出。这些类中的
parsexxx() 方法也会抛出这种异常。如果 String对象作为参数传递到一个转换方法时,传递过来的 String
对象包含非法字符,则会抛出这种异常
publicclassTryCatchFinally
{ publicstaticvoidmain(Stringargs[] )
{ Method(0); Method(1 ); Method(2); }
staticvoidMethod(inta )
{ System.out.println("调用方法 Method("+a+")");
try
{ if(a==0 )
{ System.out.println("\t没有异常产生,正常退出 。
");
return; }
else if( a==1 )
{ inti=0; intj=4/i;}
else if( a==2 )
{ intiArray[ ]=new int[4];iArray[4]=3;}
}
例 2
catch(ArithmeticExceptione )
{ System.out.println("\t捕获异常,"+e); }
catch(ArrayIndexOutOfBoundsException e )
{ System.out.println("\t捕获异常,"+e.getMessage()); }
catch( Exception e )
{ System.out.println(“\t产生的异常已被处理,
该 catch块不会被执行 。 "); }
finally
{ System.out.println("\tfinally块总是被执行 。 "); }
}
}
public class Exception
{ public staticvoid main( String args[] )
{ try
{ Method( 0 ); Method( 1 ); }
catch(NumberFormatException e )
{System.out.println("\t捕获异常,"+e);}
catch( ArrayIndexOutOfBoundsException e )
{System.out.println("\t捕获异常,"+e);}
finally
{System.out.println(“finally块总是会被执行,”);}
}
例 3
staticvoid Method( int i )
throws ArithmeticException,NumberFormatException
{ System.out.println("调用方法 Method("+i+")");
if(i==0 )
{ System.out.println(“\t没有异常事件 。,);
return; }
elseif( i==1 )
{ String str = "xyz";
int c;
c=Integer.parseInt(str);
}
}
}
抛出异常在捕获异常之前,必须有一段 Java代码生成一个异常对象并将它抛出。异常对象的生成和抛出可以有三种情况:
Java运行时系统;
JDK中某个类;
在程序中创建异常对象并抛出。
publicclassTestThrowException
{ static double sqrt(double i) throws IllegalArgumentException
{ if(i < 0) {
IllegalArgumentExceptione=newIllegalArgumentException
("Cannottakesquarerootofa negativenumber");
throw e;}
return(Math.sqrt(i));
}
publicstaticvoidmain(String[]args)
{ System.out.println("IllegalArgumentExceptionexample");
try { System.out.println(sqrt(-4));}
catch(IllegalArgumentException e)
{ System.out.println(e.getMessage()); }
}
}
public class TestMyException
{ staticvoid Method( int a ) throws MyException
{ System.out.println("调用方法 Method("+a+")");
if(a>10|a<0 ) throw new MyException(a);
System.out.println("\t正常退出 。 ");
}
public staticvoid main( String args[ ] )
{ try
{ Method( 1 ); Method( -6 );}
catch( MyException e )
{ System.out.println("\t捕获异常,"+e.toString()); }
}
}
classMyException extends Exception
{
private int detail;
MyException( int a )
{
detail= a;
}
public String toString( )
{
return"MyException "+detail;
}
}