最近发现一位博客大神chenssy,他写的博客总结的很好,我以后会用他的博客进行知识复习与巩固,特此说明,希望他不要介意。
要想明白抽象类与接口首先要知道二者之间有什么联系。
抽象类与接口是相互补充的。
一个类只能单继承,不能多继承,接口使得继承变得更加完善(接口是对继承的补充)。
抽象类与实现类之间是is-a继承关系,接口与实现类之间是like-a实现关系。(设计层面)
例子:Door
我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:
抽象类:
abstract class Door{
abstract void open();
abstract void close();
}
接口
interface Door{
void open();
void close();
}
但是现在如果我们需要门具有报警的功能,那么该如何实现呢?
abstract class Door{
abstract void open();
abstract void close();
}
interface Alarm{
void alarm();
}
class AlarmDoor extends Door implements Alarm{
void open(){}
void close(){}
void alarm(){}
}
AlarmDoor它是一个门,这表明他的继承关系,他还有报警功能,因为已经有了继承关系,所以报警功能只能是接口来实现了。这个例子我用的篇幅较少,因为我认为这个例子理解起来不是很难,如果大家看不明白可以看看其他人的详细解释。我在这里主要想表达的是:分清is-a还是like-a可以让程序设计起来更清晰,另外也可以看出接口使得继承更加完善。
抽象类的特点:
1. .抽象类不能被实例化,实例化的工作应该交由它的子类来完成。
首先我们说明一下实例化有几种方法:第一、new是最常见的;第二、Class.forName(“”).newInstance()/getInstance()方法;第三、clone()方法;第四、IO流的反序列化。
我们遇到的与抽象类实例化挨边的方法就是前两种,第一种大伙都知道是不可以的,第二种很多人都有疑惑。
这里我们有个疑问:在jdk中发现有好多抽象类都被实例化了,例如calendar类,FormatDate类
calender源代码如下:
public static Calendar getInstance()
{
Calendar cal = createCalendar(TimeZone.getDefaultRef(), Locale.getDefault());
cal.sharedZone = true;
return cal;
}
private static Calendar createCalendar(TimeZone zone,
Locale aLocale)
{
// If the specified locale is a Thai locale, returns a BuddhistCalendar
// instance.
if ("th".equals(aLocale.getLanguage())
&& ("TH".equals(aLocale.getCountry()))) {
return new sun.util.BuddhistCalendar(zone, aLocale);
} else if ("JP".equals(aLocale.getVariant())
&& "JP".equals(aLocale.getCountry())
&& "ja".equals(aLocale.getLanguage())) {
return new JapaneseImperialCalendar(zone, aLocale);
}
“把父类calendar的引用cal指向了子类.GregorianCalendar的对象”
不过按照具体情况calendar的应用ca还可能指向GregorianCalendar子类的对象
我们发现创建的不是抽象类的实例。也就是说之前提到的实例化抽象类不成立!
3. 只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。
4. 抽象类中可以包含抽象的方法,当然也可以不包含抽象方法。
这条要和上一条一起理解,有抽象方法的类肯定是抽象类,抽象类中不一定有抽象方法。
6. abstract不能与final并列修饰同一个类。
如果某个类用final修改,表明该类是最终类,它不希望也不允许其他类继承它。有关final的知识点我放在最后面,大家相互学习。
7. abstract 不能与private、static、final或native并列修饰同一个方法。
static与private不能修饰同一方法原因:
首先明白abstract方法需要重写(不是继承)才有意义!
如果一个抽象类(含有抽象方法的类就是抽象类)的某一个抽象方法定义为private那么这个方法对子类来说是拥有却不可见的(private权限修饰符的原因),即使你把那个private方法写出来了,你新写出来的方法也不属于重写,它应当算是子类特有的方法,因为子类压根看不到父类的private方法尽管它确实存在(子类确实继承了父类的private方法)。
父类的private方法子类能继承但是却对子类不可见所以没有重写。
static与abstract不能修饰同一方法原因:
你要调用一个别的类中的方法,就2种方式,如果这个是静态方法,直接类名.方法名()调用,如果不是静态方法,就得先生成这个类的实例,再通过实例名.方法名()调用(当然,前提是public的,其它关键字要考虑是否在包中或者是否子类等等,pirvate肯定是不行的),另一方面,一个类如果包含一个抽象方法,这必然是个抽象类,而抽象类是不能实例化的,它必须通过被继承来实现其中的抽象方法。本来static不需要子类就能用,可是加上abstract之后却需要子类实现才能用。
final与abstract不能修饰同一方法原因:
final修饰的方法不可修改,abstract修饰的方法如果不重写那么就没有意义。
接口的特点:
Interface的方所有法访问权限自动被声明为public且仅能是public。
接口中变量会自动变为为public static final。
接口中不存在实现的方法(所有方法都是抽象的)。
不能使用new操作符实例化一个接口,但可以声明一个接口变量,该变量必须引用(refer to)一个实现该接口的类的对象。(向上转型可以)
在实现多接口的时候一定要避免方法名的重复。
说完联系我们来说说他俩的区别
主要是设计理念不同,is-a和like-a的关系。
再说说他俩的共同点
从设计角度两者都需要子类对其进行重写。
且二者无法实例化。