≈佐

—— 有恒则成。

统计

最新评论

多态与抽象类

 

多态是java中一个比较重要的概念,在认识和理解它之前先熟悉一下下面的几个概念.

1.成员变量的隐藏和方法的重写

成员变量的隐藏:子类可以隐藏继承的成员变量,对于子类可以从父类继承成员变量,只要子类中定义的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量.但不提倡这种做法,子类总是自己定义变量而隐藏父类的变量,这样会浪费很多空间.

方法的重写:子类可以隐藏已继承的方法,子类通过方法重写来隐藏继承的方法.具体是指,子类中定义一个方法,并且这个方法的名字,返回值类型,参数个数和类型与父类继承的方法完全相同.
注意:方法的重写与方法的重载要分清楚,回顾一下方法的重载的定义为方法名必须相同,参数的类型和个数必须不同.

用下面的程序好好体会一下方法的重写(还有重载)

 

class Chengji
{
        
float f(float x,float y)
        {
                
return x*y;
         }
}
class Xiangjia extends Chengji
{
         
float f(float x,float y) //方法的重写,方法的名字,返回值类型,参数个数和类型与父类完全相同
        {
                
return x+y ;
        }
        
//float f(float,x,int y) {return x-y;}错误的语句,在方法名相同的情况下既不属于"重写"也不属于"重载"
        
//float f(int x,int y,int z){z=x-y; return z;}方法的重写,琢磨一下定义就知道了
}
public class Example
{
        
public static void main(String args[])
        {
                Xiangjia sum;
                sum
=new Xiangjia();
                
float c=sum.f(4,6);//调用的是子类里的f(),因为子类重写了父类的f()
                
//c=sum.f(1,2,3); 这个则是调用了重载的函数;当然具体调用那个要根据实参的个数和类型来判断
                System.out.println(c);
        }

 

如果没有重写父类的方法,那使用父类中的方法时就正常使用,看看下面这个混合使用的例子

class Area
{
        
float f(float r )
        {
                
return 3.14159f*r*r;
        }
        
float g(float x,float y)
        {
                
return x+y;
        }
}
class Circle extends Area
{
        
float f(float r)
        {
                
return 3.14159f*2.0f*r;//重写
        }
}
public class Example4_17
{
        
public static void main(String args[])
        {
                Circle yuan;
                yuan
=new Circle();
                
float length=yuan.f(5.0f);
                
float sum=yuan.g(232.645f,418.567f); //父类中的方法正常使用
                System.out.println(length);
                System.out.println(sum);
        }
}

注意:重写父类的方法时,不可以降低方法的访问权限.下面的例子中,子类重写父类的方法f,该方法在父类中的访问权限是protected

级别,子类重写时不允许级别低于protected级别.

 

 

class Chengji
{
        
protected float f(float x,float y)
        {
                
return x*y;
        }
}
class Xiangjia extends Chengji
{
        
float f(float x,float y)//错误,friendly的权限低于protected
        {return x+y;}
}
class Xiangjian extends Chengji
{
        
public float f(float x,float y)//正确,public的权限高于protected
        {return x-y;}
}

 

子类重写了父类中的方法后也有办法访问被隐藏的父类的方法的,这就要引入super关键字了

1).使用关键字super调用父类的构造方法
子类不继承父类的构造方法,因此,子类如果想使用父类的构造方法,必须在子类的构造方法中使用,并且必须使用关键字super来表示

,而且super必须是子类构造方法中的头一条语句.例如:

 

 

class Student
{
        
int number;
        String name;
        Student()
        {
        }
        Student(
int number,String name)//父类的构造方法
        {
                
this.number=number;
                
this.name=name;
                System.out.println(
"I am "+name+ "my number is "+number);
        }
}
class Univer_Student extends Student
{
        
boolean 婚否;
        Univer_Student(
int number,String name,boolean b) //子类的构造方法
        {
                
super(number,name);//子类构造方法中的第一条语句
                婚否=b;
                System.out.println(
"婚否="+婚否);
        }
}
public class Example4_23
{
        
public static void main(String args[])
        {
                Univer_Student zhang
=new Univer_Student(9901,"和晓林",false);
        }

 

注意:如果在子类的构造方法中,没有使用关键字super调用父类的某个构造方法,那么默认有super();语句,即调用父类不带参数的构

造方法.
如果类里定义一个或多个构造方法,那么java不提供默认的构造方法(不带参数的构造方法),因此,当在父类中定义多个构造方法时,

应当包括一个不带参数的构造方法,以防子类省略super时出现错误.


2).使用关键字super操作被隐藏的成员变量和方法

如果在子类中想使用被子类隐藏了的父类的成员变量或方法就可以使用关键字super.比如super.x,super.play(),就是被子类隐藏的

父类的成员变量x和方法play().例如

 

 

class Sum
{
        
int n;
        
float f()
        {
                
float sum=0;
                
for(int i=1;i<=n;i++)
                        sum
=sum+i;
                
return sum;
        }
}
class Average extends Sum
{
        
int n;
        
float f()
        {
                
float c;
                
super.n=n;//调用被隐藏的父类中的变量n
                c=super.f();//调用父类中被隐藏的方法f()
                return c/n;
        }
        
float g()
        {
                
float c;
                c
=super.f();//调用父类中被隐藏的方法f()
                return c/2;
        }
}
public class Example
{
        
public static void main(String args[])
        {
                Average aver
=new Average();
                aver.n
=100;
                
float result_1=aver.f();
                
float result_2=aver.g();
                System.out.println(
"result_1="+result_1);
                System.out.println(
"result_2="+result_2);
        }

 

2.对象的上转型对象(具体用途在抽象类中体现出来)
我们经常说"老虎是哺乳动物","狗是哺乳动物"等.若哺乳类是老虎类的父类,这样说当然正确,但当说老虎是哺乳动物时,老虎讲失掉老虎独有的属性和功能.下面就介绍对象的上转型对象.
假设B类是A类的子类或间接子类,当用子类B创建一个对象,并把这个对象的引用放到A类的对象中时,如
A a;
a=new B();

A a;
B b=new B();
a=b;
那么,称这个A类对象a是子类对象b的上转型对象(好比说:"老虎是哺乳动物").对象的上转型型对象的实体是子类负责创建的,但上转型对象会失去原来的一些功能.

上转型对象具有如下特点:
1)上转型对象不能操作子类新增的成员变量(失掉了这部分属性),不能使用子类新增的方法(失掉了一些功能).
2)上转型对象可以操作子类继承或隐藏的成员变量,也可以使用子类继承的或重写的方法.
3)上转型对象操作子类继承或重写的方法时,就时通知对应的子类对象去调用这些方法.因此,如果子类重写了父类的某个方法后,对象的上转型对象调用这个方法时,一定是调用了这个重写的方法.
4)可以讲对象的上转型对象再强制转换到一个子类的对象,这时,该子类对象又具备了子类的所有属性和功能.

注意:a.不要将父类创建的对象和子类对象的上转型对象混淆;b.不可以将父类创建的对象的引用赋值给子类声明的对象(不能说:"哺乳动物是老虎")

体会下面的例子来掌握上转型对象的概念

class 类人猿
{
        
private int n=100;
        
void crySpeak(String s)
        {
                System.out.println(s);
        }
}
class People extends 类人猿
{
        
void computer(int a,int b)
        {
                
int c=a*b;
                System.out.println(c);
        }
        
void crySpeak(String s)
        {
                System.out.println(
"**"+s+"**"); 
        }
}
class Example
{
        
public static void main(String args[])
        {
                类人猿 monkey
=new People(); //把子类创建的对象赋给父类创建的对象monkey,则monkey现在就是一个上转型对象
                 monkey.crySpeak("I love this game");//monkey可以调用子类中继承或重写的方法
                People people=(People)monkey; //把上转型对象强制转换为子类的对象,赋值子类创建的对象       
                people.computer(10,10);
        }

3.final 关键字

final可以修饰类,成员变量,方法中的参数.如变量被其修饰后就相当于C++中常量的概念,不可以再对其更改
final类不可以被继承,即不能有子类,如

A就是一个final类.有时候出于安全性的考虑,将一些类修饰为final类.例如:java提供的String类,它对于编辑器和解释器的正常运行有很重要的作用,对它不能轻易改变.因此它被修饰为final类.

如果一个方法被修饰为final方法,则这个方法不可以被重写;
如果一个成员变量被修饰为final的,就是常量,常量必须赋初始值,而且不能再发生变化;
如果方法的参数被修饰为final的,该参数的值不能被改变.

体会下面的例子:

class A
{
        
final double PI=3.1415926;//变量PI为常量不可再被更改
        public double getArea(final double r)//参数r被初始化后不可以被更改
        {
                
//r=20;错误,r不可以被修改
                 return PI*r*r;
        }
}
public class
{
        
public static void main(String args[])
        {
                A a
=new A();
                System.out.println(
"面积:"+a.getArea(100));
        }
}

 

4.准备工作都做完了,下面让我们开始正式学习多态性
我们经常说:"哺乳动物有很多叫声",比如,"汪汪","喵喵","嚎","吼"等,这就是叫声的多态.
当一个类有很多子类时,并且这些子类都重写了父类中的某个方法.那么当把子类创建的对象的引用放到一个父类的对象中时,就得到

了该对象的一个上转型对象,那么这个上转的对象在调用这个方法时就可能具有多种形态,因为不同的子类在重写父类的方法时可能

产生不同的行为,比如,狗类的上转型对象调用"叫声"方法时产生的行为是"汪汪",而猫类的上转型对象调用"叫声"的方法时,产生的行为是"喵喵",等等.

多态性就是指父类的某个方法被其子类重写时,可以各自产生自己的功能行为,下面的例子展示了多态性.


 

class 动物
{
        
void cry()
        {
        }
}
class 狗 extends 动物
{
        
void cry()//方法的重写
        {
                System.out.println(
"汪汪..");
        }
}
class 猫 extends 动物
{
        
void cry()//方法的重写
        {
                System.out.println(
"喵喵..");
        }
}
class Example4_20
{
        
public static void main(String args[])
        {
                动物 dongwu;
                dongwu
=new 狗();
                dongwu.cry();
                dongwu
=new 猫();
                dongwu.cry();
        }
}

 

5.抽象(abstract)类
用关键字abstract修饰的类称为抽象(abstract)类.如
abstract class A
{//...}

abstract类的几个特点:
1)abstract类中可以有abstract方法
与普通的类相比,abstract类可以有abstract方法.对于abstract方法,只允许声明,不允许实现,而且不允许使用final修饰abstract方法.下面的A类中的min()方法就是abstract方法

abstract class A 
{
abstract int min(int x,int y);
int max(int x,int y) 

return x>y?x:y;
}
}

注意:abstract类中也可以没有abstract方法

2)对于abstract类,不能使用new运算符创建该类的对象,需产生其子类,由子类创建对象,如果一个类是abstract类的子类,它必须具体实现abstract方法,这就是为什么不允许使用final修饰abstract方法的原因.体会一下下面的例子:

 

abstract class A
{
        
abstract int min(int x,int y);
        
int max(int x,int y)
        {
                
return x>y?x:y;
        }
}
class B extends A
{
        
int min(int x,int y) //子类中具体实现了min()的功能
        {
                
return x<y?x:y;
        }
}
public class Example
{
        
public static void main(String args[])
        {
                A a;
                B b
=new B();
                
int max=b.max(12,34);
                
int min=b.min(12,34);
                System.out.println(
"max="+max+" min="+min);
                a
=b;
                max
=a.max(12,34);
                System.out.println(
"max="+max);
            }
}

 

一个abstract类只关心它的子类是否具有某种功能,并不关心功能的具体行为,功能的具体行为由子类负责实现,抽象类中的抽象方法可以强制子类必须给出这些方法的具体实现.理解下面的例子来充分学习抽象类.


 

abstract class 图形
{
        
public abstract double 求面积();//相当于给子类们发出了求面积的具体表现命令
}
class 梯形 extends 图形
{
        
double a,b,h;
        梯形(
double a,double b,double h)
        {
                
this.a=a;
                
this.b=b;
                
this.h=h;
        }
        
public double 求面积()//子类梯形类实现了求面积功能
        {
                
return((1/2.0)*(a+b)*h);
        }
}
class 圆形 extends 图形
{
        
double r;
        圆形(
double r)
        {
                
this.r=r;
        }
        
public double 求面积()//子类圆形类实现了求面积功能
        {
                
return(3.14*r*r);
        }
}
class 堆
{
        图形 底;
//只是声明
        double 高;
        堆(图形 底,
double 高)//调用时候就可以用上转型对象来解释为什么了
        {
                
this.底=底;
                
this.高=高;
        }
        
void 换底(图形 底)
        {
                
this.底=底;
        }
        
public double 求体积()
        {
                
return (底.求面积()*高)/3.0;
        }
}
public class Example
{
        
public static void main(String args[])
        {
                堆 zui;
                图形 tuxing;
                tuxing
=new 梯形(2.0,7.0,10.7);
                System.out.println(
"梯形的面积"+tuxing.求面积());
                zui
=new 堆(tuxing,30);
                System.out.println(
"梯形底的堆的体积"+zui.求体积());
                tuxing
=new 圆形(10);
                System.out.println(
"半径是10的圆的面积"+tuxing.求面积());
                zui.换底(tuxing);
                System.out.println(
"圆形底的堆的体积"+zui.求体积());
        }
}

posted on 2008-09-22 23:13 ≈佐 阅读(298) 评论(0)  编辑  收藏 所属分类: J2SE 知识