java类、抽象类、接口、继承和对象解析

转载 2007年09月30日 09:54:00
这不是什么教材,笔者有时会在论坛上瞧瞧,看到不少初学者问到很多问题,这些问题是java程序员应该懂得的,而一般书上不会讲到或者一笔带过的知识。因 此斗胆涂鸦一篇文章,把想说的在这里一口气说完。这也是本人第一次写技术性的文章,文笔不畅之外,还请各位见谅。


首先讲清楚类和对象的区别。


类 是广泛的概念,表示一个有共同性质的群体,而对象指的是具体的一个实实在在的东西。例如,“人”是一个类,它可以表示地球上所有的人;而“张三”、“李 四”、“爱因斯坦”等则是一个个的对象,或者说它们是“人”这个类的一个个实例。在 Java 中,我们可以定义类,然后创建类的对 象。


例如:
// 声明一个类“Human”
class Human{
private String name;
public String getName(){
return name;
}
public void setName(String value){
this.name = value;
}
//......
}


创建一个类:
Human human = new Human();





其次,很多人对对象和对象的引用认识模糊
引用是程序操作对象的句柄,相当于C和C++中的指针。
前面说了,对象是一个实实在在的东西,比如前面的代码:
Human human = new Human();
程 序执行到这里之后,java虚拟机将会在内存中创建一个 Human 对象,并将这个对象的引用赋给  human 变量。这里有两步,首先是创建 Human 对象,然后把创建的对象的引用赋给  human 变量。
如果声明了一个对象的引用,但没有将对象赋值给它,则这个引用指向了空的对象,或者说引用了不存在的对象。这时如果想通过这个引用访问对象,则会抛出空指针异常,例如:
Human human;
//......
human.setName("张三");





下面重点谈一谈类、抽象类、接口和继承之间的关系
不少细心的初学者在论坛上问类似这样的问题:
1、接口不实现方法,但我却在程序中可以调用接口的方法,这是为什么?比如 java.sql 包中的 Connection、Statement、ResultSet 等都是接口,怎么可以调用 它们的方法呢?
2、 抽象类不能实例化,但是jdk中却有很多抽象类的对象,这是为什么?比如 System.in 是一个  InputStream 类型对象,但 InputStream 是抽象类,怎么可以得到它的对象呢?


不管怎么样,大家应该明白一点:不管是抽象类中的抽象方法,还是接口中定义的方法,都是需要被调用的,否则这些方法定义出来就没有意义了。


可能有很多书上没有提到,或者提到了而读者没有注意到这一点:
一个子类如果继承了它的基类,则表示这个类也是其基类的一种类型,这个子类的一个对象是子类类型,并且同时也是其基类的一个对象,它也具有基其类的类型;一个类如果实现了一个接口,则表示这个类的一个对象也是这个接口的一个对象。


可能这样说不太好懂,又是子类、基类、类型、接口什么的,容易搞混。其实举个现实的例子你就会觉得其实很简单:
如果“人”是一个基类,则“男人”是“人”的一个子类。如果“张三”是一个“男人”,也就是说“张三”是“男人”的一个对象,那么显然“张三”也是“人”这个基类的一个对象。


明 白了这一点,就容易理解为什么我们可以得到抽象类的对象了:原来我们得到的抽象类的对象其实是它的已经实现了抽象方法的子类或子孙类的一个对象,但我们拿 它当它的抽象类的基类来用。比如“人”这个类,每个人都会“悲伤”,男人悲伤的时候抽烟、喝酒,女人悲伤的时候哭泣、流泪。由于不同的子类在“悲伤”时所 进行的动作不一样,因此这个动作(方法)在基类中不好实现,但基类中又需要有这个方法,因此,“人”这个类就可以定义一个抽象方法“悲伤”,由其子类“男 人”和“女人”来实现“悲伤”这个方法。但是调用者只把男人和女人的对象当作其基类“人”的一个对象,调用它的“悲伤”方法。
读者可以去体验一下 jdk 的抽象类 java.lang.Process :
Runtime runtime = Rumtime.getRuntime();
Process process = rumtime.exec("notepad.exe");
Class cls = process.getClass();
System.out.println(cls.getName());
这 时会打印出 process 类的名字,如果在 Windows 下它会是一个类似于  *Win32* 的名字,它是 Process 的一个子类。因为 process 类用于管理打开 的进程,而在不同的操作系统上都有不同的实现,因此它把方法定义为 Process 的抽象方法,而具体的操作只能由对应在不同操作 系统下的子实现。


下面来谈接口,我们知道接口只定义了一些方法,而没有实现这些方法。而其实,接口是一个规范,它规定了实现这个接口所要做的事情,或者说规定了实现接口的类必须具备的能力(也就是方法)。
那么我们可以这样对比:
某种类型的驾驶执照,规定了拿到这个驾照的人必须能够“开小汽车”和“开公共汽车”。那么我们认为这个驾照是一个接口,它规定了实现它的类所必须有的能力。
我们可以定义一个类 Driver,继承自 Human,然后实现“驾照持有者”这个接口:
public interface DriverHolder{
public void driverCar();
public void driverBus();
}
public class Driver extends Human implements DriverHolder{
public void driverCar(){
// ......
}
public void driverBus(){
// ......
}
}


这样一来,一个“Driver”对象,它同时也是一个 DrivreHolder 对象。即一个司机(Driver)同时是一个驾照执持有者对象。在程序中我们可以这样:
DriverHolder driverholder = new Driver();
driverholder.driverCar();


这样我们就解释了为什么“接口没有实现方法,却可以得到接口类的对象”的问题。


但 是这样一来,肯定有人会问:为什么要定义一个接口呢,为什么不直接把这个方法定义到 Driver 类中去,然后  Driver driver = new Driver(); 一样可以调用它的  driverCar(); 和 driverBus() 方法,这样做岂不是方便得多?


这是因为 java是单继承的,它只能继承于一个类,这样它的类型就只限于其基类或者基类的基类。但是java可以实现多个接口,这样它就可以有很多个接口的类型。 就象一个人,它继承自“脊椎动物”这个类,而“脊椎动物”又继承自“动物”这个类,因此“张三”是个人,他是一个“脊椎动物”,当然他也是一个“动物”。 但他可以继承很多个接口,比如拿驾驶执照之后,他就是“驾照持有者”类型,他也可以拿英语六级证书,这样他就是一个六级证书持有者等等。


明白这一点之后,我们来看一看 java 的事件机制。
java.awt.Button  类有一个 addActionListener(ActionListener l);方法。这个方法传入的是一个接口类型: ActionListerner,在实际中,我们需要实现 ActionListener 接口,并且把实现这个接口的类的对象引用 作为参数传入。这样,Button对象就得到了一个 ActionListener 对象,它知道这个  ActionListener 对象有一个 actionPerformed 方法,或者说它有处理  Action 事件的能力,当 Action 事件发生时,它就可以调用这个对象的  actionPerformed 方法。


比如一般我们会这样做:
public class TestButton extends Frame implements ActionListener{
private Button btn1 = new Button();
//......

public TestButton(){
btn.addActionListener(this);
this.add(btn);
}

public void actionPerformed(ActionEvent e){

}
}


现 在我们假设 ActionListener 不是接口,而是一个类。那么我们只能继承  ActionListener 类,并且重写 actionPerformed 方法。但是java是单继承的,如果类继 承了 ActionListener 类,那么它就不能继承其它的类(Frame 类)了,而不从  Frame 类继承的话,又怎么创建窗体,怎么把 Button 放到窗体中去呢?


其实接口不完全是为了解决 java 的单继承问题,它在某种程度上可以达到调用和实现细节的分离。
比 如说,中国的民用电规范是一个接口:平均电压220V、50Hz、Sin 交流电,水力发电厂、火力发电厂、核电厂,还有小型的柴油发电机如果 按这个规范发电,则表示它们实现了这个民用电源的接口;冰箱、电视、洗衣机等家用电器使用这些电源,表示它们在调用这个接口。在这里,家用电器不管电从哪 里来,只要它符合民用电源的规范就好,电源也不管它发的电用于什么工作,只管提供电源。


再回过头来看看  Button 的事件机制。要知道,Button 要保证所有的 action 事件发生时,程序员都可以在他 的代码中处理它,图书馆管理系统、收银系统、进销存等等等等等等。而接口就可以做到这一点:找一个类实现  ActionListener 接口,并且让 Button 得到这个类的对象的引用( 调用  addActionListener 方法),从而当Action事件发生时,button 创建一个包含了事件信息的对象 (ActionEvent),然后调用这个接口对象的方法,到底怎么处理这次事件,这就是实现接口的类的事情了。在这里,Button 丝毫不 了解 actionPerformed 方法中到底干了什么事情,也不应该知道。Button 与具体的业务逻辑完全分离 开了,它可以应用到所有的场合。  

相关文章推荐

Java基础(面向对象三——继承、final关键字、抽象类、接口)

一、继承 继承:继承是面向对象的三大特点之一,当多个类中存在相同的属相和行为时,可以把这些共有的内容抽取到单独一个类中,那么多个类无需在定义这些属性和行为,只需要使用extends关键字继承抽取出来...
  • zzamk
  • zzamk
  • 2015年03月10日 10:19
  • 759

java面向对象浅析--抽象类、接口与多继承

高分好文! 原文地址:http://blog.csdn.net/hwalan/article/details/1341065 有时候,我们可能想要构造一个很抽象的父类对象,它可能仅仅代表一个分类或...
  • GS_008
  • GS_008
  • 2016年03月22日 01:41
  • 234

java面向对象浅析系列2——抽象类、接口与多继承

有时候,我们可能想要构造一个很抽象的父类对象,它可能仅仅代表一个分类或抽象概念,它的实例没有任何意义,因此不希望它能被实例化。例如:有一个父类“水果(Fruit)”,它有几个子类“苹果(Apple)”...

Java基础07天 学习笔记_面向对象(继承,final关键字,抽象类,接口)

001 002 继承概述 寻找对象直接的关系,并提取共性,单独进行描述。 classPerson { String name; int age; }   classStude...
  • fafrifa
  • fafrifa
  • 2014年09月01日 14:04
  • 257

黑马程序员 Java基础 面向对象:继承、final关键字、抽象类、模版方法模式、接口、多态、Object类

---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------...

java 中什么时候用抽象类,什么时候用 接口(面向对象的继承与多态)

抽象类:强调的是把共同(共有、相同)的属性方法, 抽象出来,统一写在一个地方(他们的实现代码是一样的),方便维护。 接口: 抽象的是行为 - 同一种行为的不同实现方式。当多个对象都拥有相同的行为,但是...

Java基础--面向对象(继承、抽象类、接口)

面向对象:继承概念:  将对象共性描述提取出来,单独进行描述,形成一个类,这个类被称之为父类,其他的类如果有这个类相同的特征且还有自己的特有功能则用extends(关键字)进行关联父类即可,这种方式称...

黑马程序员_java的面向对象(对第七课继承..抽象类..接口及fianl应用总结)

------- android培训、java培训、期待与您交流! ----------   通过今天对java语言面向对象的学习,我来给自己的学习内容做下简单直白的总结: 首先,我来总结下面向对...

Java基础之面向对象(三)--继承、抽象类、接口、final修饰符、模版方法模式

继承: /* 将学生和工人的共性描述提取出来,单独进行描述, 只要让学生和工人与单独描述的这个类有关系,就可以了。 继承: 1,提高了代码的复用性。 2,让类与类之间产生了关系。有了这个关系,...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java类、抽象类、接口、继承和对象解析
举报原因:
原因补充:

(最多只允许输入30个字)