**
抽象类和接口
**
抽象类是一个泛泛的类。举个例子,现在有一个图形,并不告诉你是什么类型的图,然后让你求这个图形的面积。由于给的范围太不具体太抽象,导致我们没办法来求出这个图形的面积。这个图形就可以成为一个抽象的类。一个图形抽象类定义如下:
public abstract class Graphic {
public abstract void caculateAera(double l,double w);//计算面积肯定需要两个参数
//一个抽象方法,只有函数头,因为具体怎么实现不确定
}
在class前加上abstract关键字,一个抽象类定义就完成啦。上面的抽象类有一个抽象方法来计算面积,但是没有方法体,这是因为我们并不知道如何计算面积。一个抽象方法只能在抽象类中定义,但是具体的方法也可以在抽象类中定义。(比如计算周长,我们不需要知道是什么图形,只需要知道所有边的长度也可以计算周长)
一个抽象类不可以创建对象
public static void main(String[] args) {
//Graphic g=new Graphic();错误写法,抽象类不可以创建对象,必须用一个子类来继承抽象父类
}
现在我们被告知,该图形是一个三角形和矩形,我们现在就可以通过继承一个抽象类的方式来具体化Graphic这个抽象类。
Triangle类:
public class Triangle extends Graphic {
@Override
public void caculateAera(double l,double w) //必须覆盖重写父类中所有的抽象方法,不然报错
{
System.out.println(l*w/2);
}
Rectangle类:
public class Rectangle extends Graphic {
@Override
public void caculateAera(double l,double w) //必须覆盖重写父类中所有的抽象方法,不然报错
{
System.out.println(l*w);
}
}
以上是通过继承来实现对抽象类的具体化,一个抽象类的子类还可以是一个抽象类,但是一个具体的类(非抽象)的子类就不可以是一个抽象类了。
接口
现在我们看一个和抽象类非常像的东西叫接口。
接口是引用数据类型,是多个类的公共规范。从Java9开始,一个接口可以包含:
1.常量
2.抽象方法
3.默认方法(Java8以后的版本包含)
4.静态方法(Java8以后的版本包含)
5.私有方法(Java9以后的版本包含)
同时接口不可以有构造方法和静态代码块。
下面是我定义的一个接口MyInterfaceAbstract,我把上面接口可以包含的常量和四大方法都写进去,一些需要注意的地方写在了注释里:
public interface MyInterfaceAbstract {
public static final int NUM=10;//前三个关键字可以不写,但是就算不写,系统也是默认这三个,不可以改变。若定义的时候不赋值默认为0,一样不可改变。
//常量的名称要全大写
public abstract void method1();
public void method2();
abstract void method3();
void method4();
//以上四个都是抽象方法,两个关键字都可以省略
public default void methodDefault()
{
System.out.println("这是一个默认方法AAA");
methodPrivate();
}
//默认方法可被接口实现类对象直接调用,也可以在实现类中覆盖重写
public static void methodStatic()
{
System.out.println("这是一个静态方法");
methodPrivate2();
}
//静态方法不可以被接口的实现类对象直接调用,直接通过接口名称来调用
/*
问题描述:默认方法与静态方法有许多重复的代码,产生了重复代码问题
解决:
1.普通私有方法:解决默认方法的重复代码问题
2.静态私有方法:解决静态方法的重复代码问题
私有方法不可以被实现类使用
*/
private void methodPrivate()
{
System.out.println("这是默认方法里面的重复代码");
}
private static void methodPrivate2()
{
System.out.println("这是静态方法里面的重复代码");
}
}
上面说了接口和抽象类非常像,我们同样也需要一个与具体类相似的东西来具体化接口,我们把这样的类称作实现类。
但是为了引出实现类的某些注意事项,我们在定义一个简单的接口MyInterfaceAbstractB
public interface MyInterfaceAbstractB {
public abstract void method1();
public default void methodDefault()
{
System.out.println("这是一个默认方法BBB");
}
}
这个接口里面非常简单,只有一个抽象方法method1与默认方法methodDefault。实现类与一般的子类不同的是前者可以实现多个接口,下面是一个实现类
public class MyInterfaceAbstractImpl implements
MyInterfaceAbstract,MyInterfaceAbstractB{
//一个实现类可以implement多个接口,这一点一父类子类不同
@Override
public void method1()
{
System.out.println("这是第一个抽象方法");
}// MyInterfaceAbstract与MyInterfaceAbstractB中都有method1,但只需要覆盖重写一次
@Override
public void method2()
{
System.out.println("这是第二个抽象方法");
}
@Override
public void method3()
{
System.out.println("这是第三个抽象方法");
}
@Override
public void method4()
{
System.out.println("这是第四个抽象方法");
}
@Override
public void methodDefault()
{
System.out.println("这是重写的默认方法");
}//MyInterfaceAbstract与MyInterfaceAbstractB中都有一个相同名称的默认方法,但是方法提不一样,这样就必须对该默认方法重写
}
实现类同样也需要覆盖重写所实现接口的所有抽象方法,但是像method1这样在两个接口都有的抽象方法(函数头一样)就只需要覆盖重写一次。
像methodDefault这样在MyInterfaceAbstract中和MyInterfaceAbstractB中都给出了定义,函数头一样但是方法体不同,这就产生了默认方法冲突。对于冲突的默认方法,我们也必须在实现类中进行覆盖重写。
同时一个实现类也可以有一个父类,而且父类方法优先(这里主要介绍接口没有让上面的实现类有一个父类)。
下面我们在main函数里创建一个MyInterfaceAbstractImpl对象调用一下四种类型的方法和常量
public static void main(String[] args) {
MyInterfaceAbstractImpl impl=new MyInterfaceAbstractImpl();//创建一个实现类的对象,不能创建接口的对象
impl.method1();
impl.method2();
impl.method3();
impl.method4();
//调用覆盖的抽象方法
impl.methodDefault();//调用默认方法
MyInterfaceAbstract.methodStatic();//调用静态方法
System.out.println(MyInterfaceAbstract.NUM);//使用接口中的常量
}
结果
这是第二个抽象方法
这是第三个抽象方法
这是第四个抽象方法
这是重写的默认方法
这是一个静态方法
这是静态方法里面的重复代码
10
还有一点接口的小知识,当一个接口继承(这里不是实现类)别的接口时,由于默认方法的冲突,该接口同样需要覆盖重写该冲突了的默认函数。
public interface MyInterfaceAbstractC extends MyInterfaceAbstract,MyInterfaceAbstractB {
@Override
public default void methodDefault()
{
MyInterfaceAbstract.super.methodDefault();//调用MyInterfaceAbstract的默认函数,格式与之前有差别
}
}
最后说一下类和接口的区别:
1.类和类之间是单继承的,一个类只能有一个直接父类
2. 类和接口之间是多实现的,一个类可以实现多个接口
3.接口和接口之间是多继承的
你可能觉得接口和抽象类非常的相似,从而产生了迷惑,这里我挂一个我在csdn上看到的大佬的连接,里面详细的介绍了二者区别,我就不在这班门弄斧了。
链接: link.