java面向对象四大特征(下)

来源:互联网 发布:nfs网络文件系统 编辑:程序博客网 时间:2024/04/28 04:47

上一篇中介绍完了java面向对象四大特征中的封装、继承,本文继续说剩下的2个特征:抽象和多态。

抽象

当多个类中出现相同功能,但功能内容不一样时,可以向上抽取功能定义,但不抽取功能内容,由各个子类去实现各自的功能主体。这样的功能就是抽象方法,java中抽象由抽象类和抽象方法来体现。

抽象类和抽象方法都用abstract修饰,抽象方法只有方法声明,没有方法体,直接以;结束,且用abstract修饰。abstract关键字不能和final、private、static这3个关键字共存。

抽象类的特点
  • 抽象方法必须在抽象类中;
  • 抽象方法和抽象类都必须被abstract关键在字修饰;
  • 抽象类不可以用new创建对象,因为调用抽象方法没有意义,抽象类中可以有构造函数;
  • 抽象类的方法要被使用,必须由子类复写完所有的抽象方法后,建立子类对象调用;如果子类只复写了部分抽象方法,那子类还是一个抽象类。
  • 特殊情况:抽象类中可以不定义抽象方法,这样做仅仅是不让类建立对象。

abstract 关键字,和哪些关键字不能共存。
final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。
private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。而抽象方法出现的就是需要被复写。
static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。可是抽象方法运行没意义。

下面的代码中演示了抽象类和抽象方法的使用:

/*需求: 获取一段程序运行的时间。原理:获取程序开始和结束的时间,并相减即可。*/abstract class GetTime{public final void getTime(){long start=System.currentTimeMillis();//获取当前时间runcode();long end=System.currentTimeMillis();System.out.println("程序运行 "+(end-start)+"毫秒");}public abstract void runcode();}class SubTime extends GetTime{public void runcode(){for(int i=0;i<400;i++){System.out.println(i);}}}public class TemplateDemo{public static void main(String[] args) {SubTime st=new SubTime();st.getTime();}}

这个程序同时也展示了一种程序设计模式:模板方法模式--在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,那么这时就将不确定的部分暴露出去,由该类的子类去完成。
上面的程序展示了模板方法模式,但是并不是说模板方法模式中暴露出去的部分必须是抽象的,抽象方法只是模板方法模板的一种实现方式。

当抽象类的方法都是抽象的,那么该类可以通过接口的形式来表示,interface用于定义接口。

接口定义格式特点:

  • 接口中常见定义:常量,抽象方法
  • 接口中的成员都有固定修饰符
    常量:public static final
    方法:public abstract
    即使在写程序时接口中成员没有使用上面的修饰符,成员的实际修饰符也是如此,系统会自动补上正确的修饰。

接口注意事项:

  • 接口不能创建对象,因为有抽象方法;
  • 接口需要子类实现,子类中将接口中的抽象方法全部覆盖后,子类才可以实例化,否则子类是一个抽象类。
  • 接口可以被子类多实现,弥补了java中不支持多继承的缺憾,是多继承的转换形式。
    多个接口中即使出现了一模一样的函数,但是因为都没有函数体,所以函数怎么实现完全由子类定义,所以函数调用时不会像多继承时出现无法识别该调用哪个父类方法的问题,而是可以明确的运行子类方法;多实现时多个接口中也不能出现仅返回值不同的同名函数,这样会导致子类实现接口后类中有多个返回值不同的同名函数。
  • 一个类在继承一个类的同时还可以实现多个接口
  • 接口与接口之间可以继承,而且可以多继承。

接口多实现及接口继承接口的例子:

interface A{void methodA();}interface B{void methodB();}interface C extends A,B //接口间可以多继承,C接口中有methodA和methodB方法{void methodC();}interface D{void methodD();}class E{void methodE(){System.out.println("Fu E");}}class F extends E implements C,D//继承一个类的同时实现2个接口, F类中共有5个方法{public void methodA(){System.out.println("interface A");}public void methodB(){System.out.println("interface B");}public void methodC(){System.out.println("interface C");}public void methodD(){System.out.println("interface D");}void methodE(){System.out.println("Zi E");}}public class Interfacedemo{public static void main(String[] args) {}}

接口在设计上的特点:

  • 接口是对外暴露的规则
  • 接口是程序的扩展功能;
  • 接口的出现降低了耦合度。

子父类继承时,子父类时is a的所属关系;子类实现接口时,子类与接口间的关系是like a关系,接口扩展了子类的功能,但接口功能可能并不是子类对象体系的必备功能,与子类并没有直接的所属关系。如下面这个例子:

interface Fly //Fly作为一个扩展功能接口 {void fly(); } class Animal //动物类,动物都会吃 { public void eat(){ System.out.println("动物都会吃东西"); } } class LaoYing extends Animal implements Fly{//LaoYing是Animal中的一种,实现Fly接口只是LaoYing的扩展功能 public void fly(){ System.out.println("老鹰在天空中高高的飞翔");// } public void eat(){ System.out.println("老鹰吃肉"); } public void Zhuaxiaoji(){ //老鹰的特有功能 System.out.println("老鹰抓小鸡"); } }
子类继承父类或实现接口,引发了java面向对象的最后一个特征:多态
多态
某一类事物的多种存在形态
多态的体现:父类的引用指向了自己的子类对象。或者说,父类的引用也可以接收自己的子类对象,如Animal a=new Cat()。
多态时的向下转型:父类引用指向子类对象,如果想要调用子类的特有方法,需要强制将父类的引用,转成子类类型,向下转型;父类对象不能转换子类类型。
多态的前提:必须是类与类之间有关系,要么继承,要么实现,通常还有一个前提:存在覆盖
多态的好处:多态的出现大大提高了程序的扩展性.
多态的弊端:提高了扩展性,但是只能使用父类引用访问父类中的成员。

多态中成员的特点(多态使用的注意事项)
多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
在运行期间:参阅对象所属的类中是否有调用的方法
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边。

多态的一个基本演示及应用:

/* 电脑运行实例电脑运行基于主板,电脑要能上网、视频等还需要网卡、声卡等设备运行,这些设备可作为主板的扩展功能来实现,由主板来操作其启动和终止*/interface PCI{ //主板管理网卡、声卡等设备的统一接口,所有外接设备都需要遵守此接口规范public void open();public void close();}class MainBoard{public void run(){System.out.println("MainBoard run!");}public void usePCI(PCI p){ //函数调用时,PCI p=new NetCard(),接口型引用指向自己的子类对象if(p!=null){p.open();p.close();}}}class NetCard implements PCI{public void open(){System.out.println("NetCard open");}public void close(){System.out.println("NetCard close");}}class SoundCard implements PCI{ //新来的外接设备只需实现PCI接口,就可以被主板操作,运行起来public void open(){System.out.println("SoundCard open");}public void close(){System.out.println("SoundCard close");}}public class DuoTaiDemo {public static void main(String[] args) {MainBoard mb=new MainBoard();mb.run();mb.usePCI(new NetCard());mb.usePCI(new SoundCard());}}

下面再记录与多态有关的2个比较有意思且容易把人绕晕的程序:

class Fu{        String s = "fu";        void show(){                System.out.println(s);                System.out.println(this.toString());/*f.show()执行时,此处打印的也是Zi对象,因为代码中没有new Fu对象,内存中就没有Fu对象,只有Zi对象*/        }}class Zi extends Fu{ //Zi类中没有重写show()方法        String s = "zi";}public class DuoTaiDemo2 {        public static void main(String[] args) {                Fu f = new Zi();                f.show();/*因为子类中没有复写show()方法,在子类方法区中找不到该方法,从而调用Fu类中的show(),show()方法中使用的成员变量s就近取值,从而也是父类的。父类中的一个方法只有在父类中定义而在子类中没有重写的情况下,才能被父类类型的引用调用*/                Zi z = new Zi();                z.show();/*还是调用的父类的show()方法*/        }}

运行结果是:

fuZi1@7f0b8d03fu<a target=_blank href="mailto:Zi1@4f57011e" target="_blank">Zi1@4f57011e</a>

另一个程序是:

public class DuoTaiDemo4{ public static void main(String[] args) { Fu a = new Zi();                 a.b();     }}  class Fu {  public void a() {  System.out.println("fu---a");      }  public void b() {  System.out.println("fu---b");                  a();      }  }  class Zi extends Fu{  public void a(){ System.out.println("zi----a");      }  public void b(){ System.out.println("zi----b");  super.b(); /*此处执行的是Fu类的b()函数,这里明确指明是调用Fu类方法区的b()函数,而在父类的b()方法中再调用a()方法时,执行的是Zi类对象中的a()方法,因为Zi类复写了a()方法,此时自动通过this引用变量可以找到子类对象的a()方法*/ System.out.println(super.toString());/*此处打印的是子对象信息,DuoTaiDemo4类的主程序中就new了一个Zi对象,没有new Fu对象,所以内存中只有一个Zi对象*/ }  } 运行结果是:zi----bfu---bzi----aZi@7f0b8d03





0 0
原创粉丝点击