简述JAVA的封装性

来源:互联网 发布:美工每天工作内容 编辑:程序博客网 时间:2024/04/30 04:13

JAVA的封装性

2008-04-22 10:50

类的封装性即不能让外面的类随意修改一个类的成员变量;

在定义一个类的成员(包括变量和方法),使用private关键字说明这个成员的访问权限,只能被这个类的其他成员方法调用,而不能被其他的类中的方法所调用;

为实现封装性,常将类的成员变量声明为private,再通过public的方法来对这个变量进行访问。对一个变量的操作,一般都有 读取和赋值操作,我们一般定义两个方法来实现这两种操作,即:getXxx()与setXxx();

一个类就是一个模块,我们应该让模块仅仅公开必须要让外界知道的内容,而隐藏其他的一切内容。再进行程序设计时,应尽量避免一个模块直接修改或操作另一个模块的数据,模块设计追求强内聚(许多功能尽量在类的内部独立完成,不让外面干预),弱耦合(提供给外部尽量少的方法调用)。

 

 

 

java学习总结-高级语言特性-封装,继承,多态2007年12月26日 星期三 下午 05:121.1. 封装 (encapsulation)
1.事物的内部实现细节隐藏起来
2.对外提供一致的公共的接口――间接访问隐藏数据
3.可维护性

1.2. 继承 (inherit)
1.2.1. JAVA继承特点
继承:父类的成员能否继承到子类?
子类能否访问到父类的成员

private:本类内部可以访问 不能继承到子类
(default):本类内部可以访问,同包其他类也可 以访问
能否继承到子类? 不一定

protected:本类内部可以访问,不同包的子类也可以访问, 同包其他类也可以访问
能继承到子类  
public:任何地方都可以访问 能继承到子类

从严 到宽

(override)覆盖的构成条件:
1、方法名:相同
2、参数表:相同(个数,类型)
3、访问限制符:相同或者更宽
4、返回值类型:相同 或者 子类返回的类型是父类返回的类型的子类
5、不能抛出比subclass(父类)更多的异常

对象的构造过程:
1.递归的构造父类对象
2.分配空间
3.初始化属性
4.调用本类的某一个构造方法,并用在方法的第一句。

super:调用父类的某一个构造方法
父类对象
不能出现在static方法内

多态:
1. 对象不变
2. 只能对对象调用编译时类型中定义的方法
3. 运行时,根据对象的运行时类型,找覆盖过的方法来调用(运行时动态类型判定)

强制类型转换 instanceof

屏蔽子类差异,利用父类共性做出通用编程

属性的遮盖(shadow)没有多态
方法的重载看参数的编译时类型

1.2.2. 父类(SuperClass)和 子类(SubClass)的关系
父类的非私有化属性(不同包的子类无法访问default修饰符)和方法可以默认继承到子类。
Class Son extends Father{
}
而如果父类中的私有方法被子类调用的话,则编译报错。
父类的构造方法子类不可以继承,更不存在覆盖的问题。
所以子类构造方法默认调用父类的无参构造方法。(所以养成写无参构造的习惯)
如果子类访问父类的有参构造方法,必须在子类构造方法第一行使用super(参数)
当构造一个对象的时候,系统先构造父类对象,再构造子类对象。
Public class BMWcar extends Car{
Public BMWcar(){
Super(int alength); //显式的调用父类的构造,默认调用无参构造
//所以父类没有无参构造的话,子类如果不加显示调用其他构造就会报错。这里的super是一个对父类的引用
}
}

1.2.3. 系统构造一个对象的顺序
1先为最里层类成员属性赋初值;
2再构造该类对象;
3返回外层,重复1(上一层类)、2步骤直到完成最外层类的构造。

最外层类

最里层基类

<!--[if !vml]-->最外层类[里层类]


注意:super() this() 不会同时出现
A(){
super();
}
A(int a){
this();
}
1.3. 多态(polymorphism)
多态:一个对象变量可以指向多种实际类型的现象。

1.3.1. 方法的覆盖(overridding)
当子类从父类继承一个无参方法,而又定义了一个同样的无参方法,则子类新写的方法覆盖父类的方法,称为覆盖。(注意返回值类型也必须相同,否则编译出错。)
如果方法参数表不同,则成重载。
特点:
1.对于方法的访问限制修饰词,子类方法要比父类的访问权限更高。
父类为public,那么子类为private则出现错误。
2.子类抛出的异常应该是父类抛出的异常或其子类。

1.3.2. 多态的分类
多态分两种:
1编译时多态:编译时动态重载;
2运行时多态:指一个对象可以具有多个类型,方法的覆盖
这样对于对象而言分为:
理解运行时多态:
Car c = new Bus();
Car编译时类型 编译时检查变量类型是否存在,是否有调用的方法
Bus运行时类型 实际运行是访问heep中的对象,调用实际的方法。
运行时多态是由运行时类型决定的
编译时多态是由编译时类型决定的
猫,小鸟,狗 都是动物,都可以安上动物的标签。
Interface Animal{}
Class Car implements Animal{}
Class Bird implements Animal{}
Class Dog implements Animal{}
方法中
Animal a = new Car();
Animal b = new Bird();
Animal c = new Dog();

*方法重载看的是参数的编译时类型

public class Animal{
public static void main(String[] args){

}
}


(1)   是覆盖吗?不能多态了
abstract class MyClass{
priavate void m();
}
class Sub extends MyClass(){
public void m();
}
(2) 错误的修饰符组合
abstract class MyClass{
priavate abstract void m();
}
class Sub extends MyClass(){
public void m();
}
(3) 5.0 新 非覆盖
abstract class MyClass{
private final void m();
}
class Sub extends MyClass(){
public void m();
}


1.3.3. 运行时多态的三原则
1.对象不变;(改变的是主观认识)
2.对于对象的调用只能限于编译时类型的方法,如调用运行时类型方法报错。
在上面的例子中:Animal a=new Dog();对象a的编译时类型为Animal,运行时类型为dog。
注意:编译时类型一定要为运行时类型的父类或者同类型。
对于语句:Dog d=(Dog)a。将d强制声明为a类型,此时d为Dog(),此时d就可以调用运行时类型。注意:a和d指向同一对象。
3.动态类型判定实际调用的方法。即它调用覆盖后的方法。
 

 


继承 封装 和多态2007年10月25日 星期四 20:43封装、继承、多态
比较抽象,只是一种概念,刚开始学的时候无需太过于深究,如果要你一上来学JAVA就理解这些东西,有点牵强,你所要做的就是至少看到代码知道这个用到的是什么。

封装的概念好比一辆汽车,你学开车的时候只需学会诸如踩油门、刹车,转方向盘即可,无需去了解它的发动机是如何发动。

继承,先说说我对类的理解,类起到的作用有:分类(你所用某个类创建的对象实际上该类的个案)和模板的作用,那么继承则起到了对类再次分类的作用,比如,有个类“动物”,“哺乳动物”继承“动物”,再往下“马”又继承了“哺乳动物”这个类。在这里,我们从下往上讲,首先,我们把某种东西划分出来,叫做“马”(当然还有“牛”,“鱼”等等),接着,我们发现,“马”,“羊”等还有很多共同的特点,于是,我们再次划分出了“哺乳动物”这个类,再次分类,我们则有了“动物”。但在实际开发中,我们一般是从上往下定义的,即先有了“动物”,再有“哺乳动物”,最后有“马”。

多态,正如上面朋友所说一重写,二重载。用汉字来打个比方,比如“开”这个字,在不同的时候各有的意思,比如“开门”,“开窗”,甚至有“开车”,“开饭”等,具有相同名称但操作不同。具体的实现我就不累述了。

说说接口吧,在JAVA不支持多继承的,实际上接口起到了类似多继承的作用,一个类只能继承另一个类(或抽象类)但可以实现多个接口。打个比方,“张三”,他是一个“人”,因此他继承了“人”;与此同时,他是一个“司机”,他的平时行为还有“开车”,很显然,这并不能从“人”这个类里继承下来。怎么办?JAVA里只支持单继承,这个时候,接口就起到了作用,它定义了“司机”这个接口,“张三”实现了它,因此,他会开车了。

 

 

 

 

 

 


演示java面向对象的核心特性(类的封装性,继承性和多态性)2007-08-13 17:18本例演示实例成员与类成员的差别,演示析构方法的设计和调用.声明的Person1类包含了实例成员变量,类成员变量,实例成员方法和类成员方法. 

 

public class Person1 { 
protected String name;               //姓名,实例成员变量,保护成员 
protected int age;                   //年龄 
protected static int count=0;        //人数,类成员变量,本类及子类对象计数 
public Person1(String name,int age) //构造方法 

this.set(name); 
this.set(age); 
count++;                      //人数增加1 

     public Person1(String name)                 //构造方法重载 
     { 
      this(name,0);                           //调用本类的构造方法 
     } 
     public Person1()                            //构造方法重载 
     { 
      this("姓名未知",0); 
     } 
     public Person1(Person1 p1)                  //构造方法重载 
     { 
      this(p1.name,p1.age); 
     } 
     public void finalize()                       //析构方法 
     { 
      System.out.println("释放对象(" + this.toString()+")");    //调用实例成员方法 
      this.count--; 
     } 
     public void set(String name)                 //设置成员变量值 
     { 
      if(name==null||name=="") 
      this.name="姓名未知"; 
      else 
      this.name=name; 
     } 
     public void set(int age) 
     { 
      if(age>0&&age<100) 
      this.age=age; 
      else 
      this.age=0; 
     } 
     public void set(String name,int age) 
     { 
      this.set(name); 
      this.set(age); 
     } 
     public void set(Person1 p1) 
     { 
      this.set(p1.name); 
      this.set(p1.age); 
     } 
     public String getName()                     //获得成员变量值 
     { 
      return this.name; 
     } 
     public int getAge() 
     { 
      return this.age; 
     } 
     public static void howMany()                //类成员方法,只能访问类成员变量 
     { 
      System.out.print("Person1.count="+count+"    "); 
     } 
     public String belongClassName() 
     { 
      String str="";                          //局部变量,不能使用修饰符 
      if(this instanceof Person1)             //判断当前对象是否属于Person1类 
      str="Person1"; 
      return str; 
     } 
     public String toString() 
     { 
        return this.name+","+this.age+"岁"; 
     } 
     public void print()                          //实例成员方法,可以访问类成员变量和实例成员变量 
     { 
      this.howMany();                          //通过对象调用类成员方法 
      System.out.println(this.belongClassName()+"类   ("+this.toString()+")"); 
                                              //通过对象调用实例成员方法 
     } 
     public int olderThen(Person1 p2)             //比较两个人的年龄 
     { 
      return this.age-p2.age; 
     } 
     public static void main(String args[])       //main方法也是类成员方法 
     { 
      Person1 p1=new Person1("李小明",21); 
      p1.print(); 
      Person1 p2=new Person1("王大伟",19); 
      p2.print(); 
      System.out.println(p1.getName()+" 比 "+p2.getName()+" 大 "+p1.olderThen(p2)+" 岁 "); 
                                              //通过对象调用实例成员方法 
      p1.finalize();                           //调用对象的析构方法 
      p1=null;                                 //p1为空对象 
      Person1.howMany();                       //通过类名调用类成员方法 
      System.out.println(); 
     } 
}


/* 
子类重定义父类成员:
下面声明Student2类重定义父类Person1的成员变量和成员方法演示类中方法的多态性,包括重载和覆盖.
*/
public class Student2 extends Person1 {
private String speciality;             //专业,子类私有成员变量
private static int count=0;             //人数,隐藏了父类的count
public Student2(String name,int age,String spec)
{
super(name,age);                    //调用父类的构造方法
this.speciality=spec;
this.count++;                      //子类对象计数
}
public Student2(String name,int age)
{
this(name,age,"未知");
}
public Student2(String name)
{
this(name,0,"未知");
}
public Student2()
{
this("姓名未知",0,"未知");
}
public Student2(Student2 s1)
{
this(s1.name,s1.age,s1.speciality);
}
public Student2(Person1 p1,String spec)
{
this(p1.name,p1.age,spec);
}
public void finalize()                         //覆盖父类的析构方法
{
super.finalize();                          //调用父类析构方法,父类人数减1
this.count--;                              //子类人数减1
}
public void setSpeciality(String spec)
{
if(spec==null||spec=="")
this.speciality="不知";
else
this.speciality=spec;
}
public void set(String name,int age,String spec)      // 重载父类同名成员方法
{
super.set(name,age);
this.setSpeciality(spec);
}
public String getSpeciality()
{
return this.speciality;
}
public static void howMany()    //分别统计父类和子类的人数,覆盖父类的同名类成员方法
{
Person1.howMany();                                   //调用父类的类成员方法
System.out.print("Student2.count="+count+"   ");     //显示子类的成员变量值
}
public String belongClassName()
{
String str=super.belongClassName();        //返回父类的类名
if(this instanceof Student2)
str+=",Student2";
return str;
}
public String toString()                           //覆盖父类的同名方法
{
return super.toString()+"."+speciality+"专业";        //调用父类同名方法
}
public void print()                              //覆盖父类的同名方法
{
this.howMany();
System.out.println(this.belongClassName()+"类   ("+this.toString()+")");
}
}

 

class Student2_ex {
public static void main(String args[])
{
Person1 p1=new Person1();     //父类对象
p1.set("李小明",21);           //调用父类成员方法
p1.print();
Student2 s1=new Student2();     //子类对象
s1.set("王大伟",19);             //调用父类成员方法
s1.print();
Student2 s2=new Student2();     //子类对象
s2.set("张三",18,"计算机");        //调用子类重载的成员方法
s2.print();

s1.finalize();                  //覆盖父类的析构方法
s1=null;
Student2.howMany();
System.out.println();
}

}

运行结果如下:


Person1.count=1    Person1类   (李小明,21岁)
Person1.count=2    Student2.count=1   Person1,Student2类   (王大伟,19岁.未知专业)
Person1.count=3    Student2.count=2   Person1,Student2类   (张三,18岁.计算机专业)
释放对象(王大伟,19岁.未知专业)
Person1.count=2    Student2.count=1
 

 


面向对象--多态2008-02-14 10:17一、什么是多态

面向对象程序设计中的另外一个重要概念是多态性。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。可以把一组对象放到一个数组中,然后调用它们的方法,在这种场合下,多态性作用就体现出来了,这些对象不必是相同类型的对象。当然,如果它们都继承自某个类,你可以把这些派生类,都放到一个数组中。如果这些对象都有同名方法,就可以调用每个对象的同名方法。

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。多态性通过派生类重载基类中的虚函数型方法来实现。

在面向对象的系统中,多态性是一个非常重要的概念,它允许客户对一个对象进行操作,由对象来完成一系列的动作,具体实现哪个动作、如何实现由系统负责解释。

“多态性”一词最早用于生物学,指同一种族的生物体具有相同的特性。在C#中,多态性的定义是:同一操作作用于不同的类的实例,不同的类将进行不同的解释,最后产生不同的执行结果。C#支持两种类型的多态性:

● 编译时的多态性

编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

● 运行时的多态性

运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。

编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

二、实现多态

多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。多态性允许对类的某个方法进行调用而无需考虑该方法所提供的特定实现。例如,可能有名为 Road 的类,它调用另一个类的 Drive 方法。这另一个类 Car 可能是 SportsCar 或 SmallCar,但二者都提供 Drive 方法。虽然 Drive 方法的实现因类的不同而异,但 Road 类仍可以调用它,并且它提供的结果可由 Road 类使用和解释。

可以用不同的方式实现组件中的多态性:

● 接口多态性。

● 继承多态性。

● 通过抽象类实现的多态性。

接口多态性

多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。接口本质上是类需要如何响应的定义。接口描述类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,但将这些成员的特定实现留给实现类去完成。

组件编程中的一项强大技术是能够在一个对象上实现多个接口。每个接口由一小部分紧密联系的方法、属性和事件组成。通过实现接口,组件可以为要求该接口的任何其他组件提供功能,而无需考虑其中所包含的特定功能。这使后续组件的版本得以包含不同的功能而不会干扰核心功能。其他开发人员最常使用的组件功能自然是组件类本身的成员。然而,包含大量成员的组件使用起来可能比较困难。可以考虑将组件的某些功能分解出来,作为私下实现的单独接口。

根据接口来定义功能的另一个好处是,可以通过定义和实现附加接口增量地将功能添加到组件中。优点包括:

1.简化了设计过程,因为组件开始时可以很小,具有最小功能;之后,组件继续提供最小功能,同时不断插入其他的功能,并通过实际使用那些功能来确定合适的功能。

2.简化了兼容性的维护,因为组件的新版本可以在添加新接口的同时继续提供现有接口。客户端应用程序的后续版本可以利用这些接口的优点。

通过继承实现的多态性

多个类可以从单个基类“继承”。通过继承,类在基类所在的同一实现中接收基类的所有方法、属性和事件。这样,便可根据需要来实现附加成员,而且可以重写基成员以提供不同的实现。请注意,继承类也可以实现接口,这两种技术不是互斥的。

C# 通过继承提供多态性。对于小规模开发任务而言,这是一个功能强大的机制,但对于大规模系统,通常证明会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的开发时间没有任何帮助。

何时使用继承驱动的多态性呢?使用继承首先是为了向现有基类添加功能。若从经过完全调试的基类框架开始,则程序员的工作效率将大大提高,方法可以增量地添加到基类而不中断版本。当应用程序设计包含多个相关类,而对于某些通用函数,这些相关类必须共享同样的实现时,您也可能希望使用继承。重叠功能可以在基类中实现,应用程序中使用的类可以从该基类中派生。抽象类合并继承和实现的功能,这在需要二者之一的元素时可能很有用。

通过抽象类实现的多态性

抽象类同时提供继承和接口的元素。抽象类本身不能实例化,它必须被继承。该类的部分或全部成员可能未实现,该实现由继承类提供。已实现的成员仍可被重写,并且继承类仍可以实现附加接口或其他功能。

抽象类提供继承和接口实现的功能。抽象类不能示例化,必须在继承类中实现。它可以包含已实现的方法和属性,但也可以包含未实现的过程,这些未实现过程必须在继承类中实现。这使您得以在类的某些方法中提供不变级功能,同时为其他过程保持灵活性选项打开。抽象类的另一个好处是:当要求组件的新版本时,可根据需要将附加方法添加到基类,但接口必须保持不变。

何时使用抽象类呢?当需要一组相关组件来包含一组具有相同功能的方法,但同时要求在其他方法实现中具有灵活性时,可以使用抽象类。当预料可能出现版本问题时,抽象类也具有价值,因为基类比较灵活并易于被修改。
示例:实现多态性的程序

using System ;

public class DrawingBase

{

public virtual void Draw( )

{


Console.WriteLine("I'm just a generic drawing object.") ;

}

}

public class Line : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("I'm a Line.") ; }

}

public class Circle : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("I'm a Circle.") ; }

}

public class Square : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("I'm a Square.") ; }

}

public class DrawDemo

{

public static int Main(string[] args)

{

DrawingBase [] dObj = new DrawingBase [4];

dObj[0] = new Line( ) ;

dObj[1] = new Circle( ) ;

dObj[2] = new Square( ) ;

dObj[3] = new DrawingBase( ) ;

foreach (DrawingBase drawObj in dObj)

drawObj.Draw( ) ;

return 0;

}

}

说明:上面程序演示了多态性的实现。在DrawDemo类中的Main( )方法中,创建了一个数组,数组元素是DrawingBase类的对象。该数组名为dObj,是由四个DrawingBase类型的对象组成。接下来,初始化dObj数组,由于Line,Circle和Square类都是DrawingBase类的派生类,所以这些类可以作为dObj数组元素的类型。如果C#没有这种功能,你得为每个类创建一个数组。继承的性质可以让派生对象当作基类成员一样用,这样就节省了编程工作量。 一旦数组初始化之后,接着是执行foreach循环,寻找数组中的每个元素。在每次循环中,dObj 数组的每个元素(对象)调用其Draw( )方法。多态性体现在:在运行时,各自调用每个对象的Draw( )方法。尽管dObj 数组中的引用对象类型是DrawingBase,这并不影响派生类重载DrawingBase类的虚方法Draw( )。 在dObj 数组中,通过指向DrawingBase基类的指针来调用派生类中的重载的Draw( )方法。

输出结果是:

I'm a Line.

I'm a Circle.

I'm a Square.

I'm just a generic drawing object.

在DrawDemo 程序中,调用了每个派生类的重载的Draw( )方法。 最后一行中,执行的是DrawingBase类的虚方法Draw( )。这是因为运行到最后,数组的第四个元素是DrawingBase类的对象。
三、虚方法

当类中的方法声明前加上了virtual 修饰符,我们称之为虚方法,反之为非虚。使用了virtual 修饰符后,不允许再有static, abstract, 或override 修
饰符。

示例1:带有虚方法的类

using System ;

public class DrawingBase

{

public virtual void Draw( )

{ Console.WriteLine("这是一个虚方法!") ; }

}

说明:这里定义了DrawingBase类。这是个可以让其他对象继承的基类。该类有一个名为Draw( )的方法。Draw( )方法带有一个virtual修饰符,该修饰符表明:该基类的派生类可以重载该方法。DrawingBase类的 Draw( )方法完成如下事情:输出语句"这是一个虚方法!"到控制台。

示例2:带有重载方法的派生类

using System ;

public class Line : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("画线.") ; }

}

public class Circle : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("画圆.") ; }

}

public class Square : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("画正方形.") ; }

}

说明:上面程序定义了三个类。这三个类都派生自DrawingBase类。每个类都有一个同名Draw( )方法,这些Draw( )方法中的每一个都有一个重载修饰符。重载修饰符可让该方法在运行时重载其基类的虚方法,实现这个功能的条件是:通过基类类型的指针变量来引用该类。

对于非虚的方法,无论被其所在类的实例调用,还是被这个类的派生类的实例调用,方法的执行方式不变。而对于虚方法,它的执行方式可以被派生类改变,这种改变是通过方法的重载来实现的。

下面的例子说明了虚方法与非虚方法的区别。

using System ;

class A

{

public void F( ) { Console.WriteLine("A.F") ; }

public virtual void G( ) { Console.WriteLine("A.G") ; }

}

class B: A

{

new public void F( ) { Console.WriteLine("B.F") ; }

public override void G( ) { Console.WriteLine("B.G") ; }

}

class Test

{

static void Main( )

{

B b = new B( ) ;

A a = b;

a.F( ) ;

b.F( ) ;

a.G( ) ;

b.G( ) ;

}

}

例子中,A 类提供了两个方法:非虚的F 和虚方法G 。类B 则提供了一个新的非虚的方法F, 从而覆盖了继承的F; 类B 同时还重载了继承的方法G 。那么输出应该是:A.F B.F B.G B.G

注意到本例中,方法a.G( ) 实际调用了B.G,而不是A.G,这是因为编译时值为A,但运行时值为B ,所以B 完成了对方法的实际调用。

四、接口多态性

多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。接口本质上是类需要如何响应的定义。接口描述类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,但将
这些成员的特定实现留给实现类去完成。

组件编程中的一项强大技术是能够在一个对象上实现多个接口。每个接口由一小部分紧密联系的方法、属性和事件组成。通过实现接口,组件可以为要求该接口的任何其他组件提供功能,而无需考虑其中所包含的特定功能。这使后续组件的版本得以包含不同的功能而不会干扰核心功能。

其他开发人员最常使用的组件功能自然是组件类本身的成员。然而,包含大量成员的组件使用起来可能比较困难。可以考虑将组件的某些功能分解出来,作为私下实现的单独接口。

根据接口来定义功能的另一个好处是,可以通过定义和实现附加接口增量地将功能添加到组件中。优点包括:

● 简化了设计过程,因为组件开始时可以很小,具有最小功能;之后,组件继续提供最小功能,同时不断插入其他的功能,并通过实际使用那些功能来确定合适的功能。

● 简化了兼容性的维护,因为组件的新版本可以在添加新接口的同时继续提供现有接口。客户端应用程序的后续版本可以利用这些接口的优点(如果这样做有意义)。

五、继承多态性

多个类可以从单个基类“继承”。通过继承,类在基类所在的同一实现中接收基类的所有方法、属性和事件。这样,便可根据需要来实现附加成员,而且可以重写基成员以提供不同的实现。请注意,继承类也可以实现接口,这两种技术不是互斥的。

C# 通过继承提供多态性。对于小规模开发任务而言,这是一个功能强大的机制,但对于大规模系统,通常证明会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的开发时间没有任何帮助。看下面的例子:

class B

{ public virtual void foo () {} }

class D : B

{

public override void foo () {}

}

//试图重载一个非虚的方法将会导致一个编译时错误,除非对该方法加上“new”关键字,//以指明该方法意欲隐藏父类的方法。

class N : D

{

public new void foo () {}

public static void Main() {

N n = new N ();

n.foo( ) ; // 调用N的foo

((D)n).foo( ) ; // 调用D的foo

((B)n).foo( ) ; // 调用D的foo

}

}

和C++、Java相比,C#的override关键字使得阅读源代码时可以清晰地看出哪些方法是重载的。不过,使用虚方法有利有弊。第一个有利点是:避免使用虚方法轻微的提高了执行速度。第二点是可以清楚地知道哪些方法会被重载。

我们看一个关于飞机描述的类。假设我们有一个描述飞机的基类。现在,我们要完成一个飞机控制系统,有一个全局的函数fly,它负责让传递给它的飞机起飞,那么,只需要这样:

using System ;

class plane

{

public virtual void fly(){} //起飞纯虚函数

public virtual void land() {} //着陆纯虚函数

public virtual string modal(){} //查寻型号纯虚函数

}

// 然后,我们从plane派生出两个子类,直升机(copter)和喷气式飞机(jet):

class copter:plane

{

private String fModal ;

public override void fly(){}

public override void land(){}

public override string modal(){}

}

class jet : plane

{

private String fModal ;

public override void fly(){}

public override void land(){}

public override string modal{}

}

就可以让所有传给它的飞机(plane的子类对象)正常起飞!不管是直升机还是喷气机,甚至是现在还不存在的,以后会增加的飞碟。因为,每个子类都已经定义了自己的起飞方式。

可以看到 plane.fly()函数接受参数的是 plane类对象引用,而实际传递给它的都是 plane的子类对象,多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。 很显然,parent = child; 就是多态的实质!因为直升机“是一种”飞机,喷气机也“是一种”飞机,因此,所有对飞机的操作,都可以对它们操作,此时,飞机类就作为一种接口。多态的本质就是将子类类型的指针赋值给父类类型的指针(在OP中是引用),只要这样的赋值发生了,多态也就产生了,因为实行了“向上映射”。
 

 

面向对象的三个基本特征-----封装、继承、多态(二)2007-10-30 16:24面向对象的三个基本特征-----封装、继承、多态(二)
概念讲解
泛化(Generalization)

 

图表 1 泛化

在上图中,空心的三角表示继承关系(类继承),在UML的术语中,这种关系被称为泛化(Generalization)。Person(人)是基类,Teacher(教师)、Student(学生)、Guest(来宾)是子类。

若在逻辑上B是A的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性。

例如,教师是人,Teacher 是Person的“一种”(a kind of )。那么类Teacher可以从类Person派生(继承)。

如果A是基类,B是A的派生类,那么B将继承A的数据和函数。

如果类A和类B毫不相关,不可以为了使B的功能更多些而让B继承A的功能和属性。

若在逻辑上B是A的“一种”(a kind of ),则允许B继承A的功能和属性。


聚合(组合)

 

图表 2 组合

若在逻辑上A是B的“一部分”(a part of),则不允许B从A派生,而是要用A和其它东西组合出B。

例如,眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以类Head应该由类Eye、Nose、Mouth、Ear组合而成,不是派生(继承)而成。


聚合的类型分为无、共享(聚合)、复合(组合)三类。


聚合(aggregation)

 


图表 3 共享

上面图中,有一个菱形(空心)表示聚合(aggregation)(聚合类型为共享),聚合的意义表示has-a关系。聚合是一种相对松散的关系,聚合类B不需要对被聚合的类A负责。


组合(composition)

 

图表 4 复合

这幅图与上面的唯一区别是菱形为实心的,它代表了一种更为坚固的关系——组合(composition)(聚合类型为复合)。组合表示的关系也是has-a,不过在这里,A的生命期受B控制。即A会随着B的创建而创建,随B的消亡而消亡。


依赖(Dependency)

 

图表 5 依赖

这里B与A的关系只是一种依赖(Dependency)
 

java初学者实践教程13-面向对象之多态2007-09-27 03:49上节课我们了解了比较重要的概念面向对象,和java的OOP有封装、继承、多态的特征。但是什么叫做多态,是很多初学者不容易理解的问题。对于继承来说,很容易理解因为你就看字面的意思就知道它是继承着父类的特性。多态字面不容易理解了。下面我们具体讲一下吧!

类之间的继承关系使子类具有父类的所有变量和方法,=> 父类所具有的方法也可以在它所有子类中使用,发给父类的消息也可以发送给子类 => 子类的对象也是父类的对象=>子类的对象既可以做本身的类型,也可以做父类的类型。 呵呵,上述推导公式好像绕口令似的。我们举个例子理解上述概念。举例:

public class 动物 //动物是父类

public class 猫 extends 动物 //猫是子类

动物的所有特性在猫中可以使用,发给动物的信息猫也能收到=>猫的对象new 猫();既可以作为本身的类型 猫 a=new 猫(); 也可以作为父类的类型 动物 b = new 猫();这样说理解了吗?如有疑问请访问我们的技术论坛。

如果大家明白了的话,我们就可以从上述公式推导出结论,所有的子类都可以作为父类的类型(同一种类型)来对待。像刚才那个动物有很多子类啊,可以有很多对象。动物 a=new 猫();动物 b=new 狗(); 动物 c=new 猪();。这样的将子类型的对象引用转换成父类型的对象引用,叫做上溯造型(upcasting)。

我们再来引伸一下,我们在数组那节课里讲了,数组存放的元素是相同类型的数据,但是上溯造型使得java允许创建不同类型对象的数组。例如:

Employee[] staff = new Employee[3];

staff[0] = new Manager();

staff[1] = new Secretary();

staff[2] = new Employee();

夷?这是怎么回事啊,数组里面不是相同类型吗?对啊,因为Sectetary和Manager是Employee的子类,所以也可以通过上溯造型变成Employee啊。以前我们还学到了所有对象都是从java.lang.Object 继承下来的。如果数组要是 Object型的话 Object[] obj=new Object[];那就是里面放什么对象都行了。因为什么对象都可以是Object型的。

实践:

// java中的多态

class Shape {

void draw() {}

void erase() {}

}

//圆形

class Circle extends Shape {

void draw() {

System.out.println("Circle.draw()");

}

void erase() {

System.out.println("Circle.erase()");

}

}

//正方形

class Square extends Shape {

void draw() {

System.out.println("Square.draw()");

}

void erase() {

System.out.println("Square.erase()");

}

}

//三角形

class Triangle extends Shape {

void draw() {

System.out.println("Triangle.draw()");

}

void erase() {

System.out.println("Triangle.erase()");

}

}

public class Shapes {

public static Shape randShape() {

switch((int)(Math.random() * 3)) {

default:

case 0: return new Circle();

case 1: return new Square();

case 2: return new Triangle();

}

}

public static void main(String[] args) {

Shape[] s = new Shape[9];

// 向数组里添加类型

for(int i = 0; i < s.length; i++)

s[i] = randShape();

// 用多态的方法调用

for(int i = 0; i < s.length; i++)

s[i].draw();

}

}

上述源码下载

Java的多态性,有什么意义呢?它的突出优点是使程序具有良好的扩展性。它通过继承,可以派生出任意多个新类型,或向基类增加更多方法时,无须修改原有对基础类进行处理的相关程序。就是扩展性好。

我们返回再看面向对象(专指OOP)的这三个特性封装、继承、多态三者的关系。没有封装就没有继承,没有继承就没有多态。
 
 

原创粉丝点击