抽象类和接口

抽象类

定义

某个父类只是知道子类应该包含怎么样的方法,但是无法准确知道子类如何实现这些方法。
抽象方法的定义:通过 abstract 关键字来修饰的类称为抽象类

特点

  • 抽象方法定义在抽象类中,也可以存在于接口中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
  • 抽象方法不可以有方法体
  • 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
  • 抽象方法(其前有abstract修饰)不能用private、static、synchronized、final、native访问修饰符修饰。
  • 抽象类不能实例化,因为有抽象方法未实现
  • 抽象类可以含有静态代码块以及静态方法
  • 抽象类中可以定义普通属性
  • 抽象类可以被继承,子类可以是抽象的,也可以非抽象的
  • 抽象类不一定非要被继承可以通过调用静态方法直接使用
  • abstract不能与final并列修饰同一个类
  • 子类中的抽象方法不能与父类的抽象方法同名。因为父类的抽象方法必须在子类中全部实现,如果子类中存在同样的抽象方法,则没有实现,子类还是一个抽象类。

 

抽象类与普通类的区别

  • 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
  • 抽象类不能用来创建对象;
  • 如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类,abstract不能与final并列修饰同一个类
  • 子类中的抽象方法不能与父类的抽象方法同名。因为父类的抽象方法必须在子类中全部实现,如果子类中存在同样的抽象方法,则没有实现,子类还是一个抽象类。
  • 抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、final、native访问修饰符修饰。

最后一条的原因:

抽象方法没有方法体,是用来被继承的,所以不能用private,final修饰;

static修饰的方法可以通过类名来访问该方法(即该方法的方法体),抽象方法用static修饰没有意义;

使用synchronized关键字是为该方法加一个锁。而如果该关键字修饰的方法是static方法。则使用的锁就是class变量的锁。如果是修饰类方法。则用this变量锁。但是抽象类不能实例化对象,因为该方法不是在该抽象类中实现的。是在其子类实现的。所以,锁应该归其子类所有,所以。抽象方法也就不能用synchronized关键字修饰了;

native,这个东西本身就和abstract冲突,他们都是方法的声明,只是一个把方法实现移交给子类,另一个是移交给本地操作系统。如果同时出现,就相当于即把实现移交给子类,又把实现移交给本地操作系统,那到底谁来实现具体方法呢?

抽象类的体现-模板模式

模板模式设计:抽象类作为多个子类的通用模板,子类在抽象类的基础上进行拓展,但是子类在总体上大致保留抽象类的行为方式;编写一个抽象父类,该父类提供了多个子类的通用方法,并把一个或多个抽象方法留给子类去实现,这就是模板设计模式。
模板模式应用的简单规则:
1.抽象父类可以只定义需要使用的某些方法,其余留给子类去实现;
2.父类提供的方法只是定义了一个通用算法,其实现必须依赖子类的辅助;
如果父类的方法不想被子类覆写,那么可以在前面加上 final 关键字修饰
代码演示:

package JBTest;


//模板模式

//抽象类中包含很多的抽象方法,子类必须去覆写!
abstract class Method {
	abstract double mul();// 返回值类型如果是void的话,下面报错,因为没有返回值,无法引用!

	abstract double divid();

	void show() {
		System.out.println("面积是:" + mul());// 周长
		System.out.println("面积是:" + divid());// 面积}
	}
}

	class Square extends Method {
		double d;

		public Square(double d) {
			super();
			this.d = d;
		}

		@Override
		double mul() {
			return d * d;
		}

		@Override
		double divid() {
			return 4 * d;
		}
	}

	class Cirle extends Method {
		double r;

		public Cirle(double r) {
			super();
			this.r = r;
		}

		@Override
		double mul() {
			return 2 * 3.14 * r;
		}

		@Override
		double divid() {
			return 3.14 * r * r;
		}
	}

public class Demo16 {
	public static void main(String[] args) {
		Square s = new Square(5);
		s.show();
		Cirle c = new Cirle(4);
		c.show();
	}
}

代码结果:

面积是:25.0
面积是:20.0
面积是:25.12
面积是:50.24

接口

例子:
生活中听说过的 USB 接口其实并不是我们所看到的那些插槽,而是那些插槽所遵循的一种规范;而我们看到的那些插槽是根据 USB 规范设计出来的实例而已,也就说插槽是 USB 的实例;对应不同型号的 USB 设备而言,他们各自的 USB 插槽都需要遵循一个规范,遵守这个规范就可以保证插入插槽的设备能与主板正常通信;对于同一种型号的主板上的多个 USB 插槽,他们有相同的数据交换方式,相同的实现细节,可认为他们都是同一个类的不同实例

接口只定义了类应当遵循的规范,却不关心这些类的内部数据和其方法内的实现细节.
接口只规定了这些类里必须提供的方法;从而分离了规范和实现.增强了系统的可拓展性和维护性;
使用接口的好处,拓展性,维护性更好,所以我们在开发中会经常用到接口(. 相当于定义了一种标准)

interface 定义

接口定义一种规范,规定一个类必须做什么,但它不管如何具体去做;
[修饰符] interface 接口名 extends 父接口 1,父接口 2....

特点

  • 没有构造方法,不能实例化;
  • 接口只能继承接口,不能继承类
  • 接口里没有普通方法,方法全是抽象的;
  • 接口里的方法默认修饰符是 public abstract;
  • 接口里的字段全是全局常量,默认修饰符是 public static final;
  • 接口里的成员包括(主要是前两个):全局常量 公共的抽象方法
  • 接口可以多继承,但是只能继承接口,不能继承类。格式:public class SubImpl extends Super implements IA,IB
  • 实现接口的方法必须是 public 类型
  • 接口与类之间的关系:实现关系或者说是继承关系

JDK8及以后,允许我们在接口中定义static方法和default方法。

在jdk8之前,interface之中可以定义变量和方法,变量必须是public static final的,方法必须是public abstract的。由于这些修饰符都是默认的,所以在JDK8之前,下面的写法都是等价的.

public interface JDK8BeforeInterface { 

    public static final int field1 = 0; 

    int field2 = 0; 

    public abstract void method1(int a) throws Exception; 

    void method2(int a) throws Exception; 

}

JDK8及以后,允许我们在接口中定义static方法和default方法。

public interface JDK8Interface { 


    // static修饰符定义静态方法 

    static void staticMethod() { 

        System.out.println("接口中的静态方法"); 

    } 


    // default修饰符定义默认方法 

    default void defaultMethod() { 

        System.out.println("接口中的默认方法"); 

    } 

}

 

再定义一个接口的实现类:

public class JDK8InterfaceImpl implements JDK8Interface { 

    //实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写 

}

静态方法,只能通过接口名调用,不可以通过实现类的类名或者实现类的对象调用default方法,只能通过接口实现类的对象来调用。

public class Main { 

    public static void main(String[] args) { 

        // static方法必须通过接口类调用 

        JDK8Interface.staticMethod(); 

 

        //default方法必须通过实现类的对象调用 

        new JDK8InterfaceImpl().defaultMethod(); 

    } 

}

当然如果接口中的默认方法不能满足某个实现类需要,那么实现类可以覆盖默认方法。

public class AnotherJDK8InterfaceImpl implements JDK8Interface { 

 

    // 签名跟接口default方法一致,但是不能再加default修饰符 

    <a href="/profile/992988" data-card-uid="992988" class="js-nc-card" target="_blank" style="color: #25bb9b">@Override 

    public void defaultMethod() { 

        System.out.println("接口实现类覆盖了接口中的default"); 

    } 

}

</a>

由于java支持一个实现类可以实现多个接口,如果多个接口中存在同样的static和default方法会怎么样呢?如果有两个接口中的静态方法一模一样,并且一个实现类同时实现了这两个接口,此时并不会产生错误,因为jdk8只能通过接口类调用接口中的静态方法,所以对编译器来说是可以区分的。但是如果两个接口中定义了一模一样的默认方法,并且一个实现类同时实现了这两个接口,那么必须在实现类中重写默认方法,否则编译失败。

public interface JDK8Interface1 { 

 

    // static修饰符定义静态方法 

    static void staticMethod() { 

        System.out.println("JDK8Interface1接口中的静态方法"); 

    } 

 

    // default修饰符定义默认方法 

    default void defaultMethod() { 

        System.out.println("JDK8Interface1接口中的默认方法"); 

    } 

 

}

public class JDK8InterfaceImpl implements JDK8Interface,JDK8Interface1 { 

 

    // 由于JDK8Interface和JDK8Interface1中default方法一样,所以这里必须覆盖 

     @Override 

    public void defaultMethod() { 

        System.out.println("接口实现类覆盖了接口中的default"); 

    } 

}

</a>

public class Main { 

    public static void main(String[] args) { 

        JDK8Interface.staticMethod(); 

        JDK8Interface1.staticMethod(); 

        new JDK8InterfaceImpl().defaultMethod(); 

    } 

}

面向接口编程之制定标准和简单工厂模式

制定一个标准,让别人去实现或者说满足它!
Eg:

interface USB{//定义 USB 标准
void useUSB();//USB 有使用 USB 的行为
}

简单工厂模式
构建一个工厂出来,在里面进行生产,用的时候直接拿。

好处:屏蔽不同子类实现的差异,提高代码的可拓展性和可维护性;

package JBTest;

//简单工厂模式
interface Phone {// 制定标准,都要实现send()方法
	public void send();
}

class Iphone implements Phone {
	@Override
	public void send() {
		System.out.println("Iphone手机在发短信");
	}
}

class AndroidPhone implements Phone {
	@Override
	public void send() {
		System.out.println("AndroidPhone手机在发短信");
	}
}

class MyPhone implements Phone {
	@Override
	public void send() {
		System.out.println("MyPhone手机在发短信");
	}
}

class Factory {
	public static void show(String type) {// 传入参数,根据不同的类型个性化定制
		if (type.equals("")) {// 为空的情况,不用往下执行
			System.out.println("对不起,类型为空!,请重新输入!");
			return;
		}
		Phone p = null;
		if ("Iphone".equals(type)) {// 判断类型
			p = new Iphone();
		} else if ("AndroidPhone".equals(type)) {
			p = new AndroidPhone();
		} else {
			p = new MyPhone();
		}
		p.send();
	}
}

public class FactoryDemo17 {
	public static void main(String[] args) {
		new Factory().show("Iphone");// 调用方法
		new Factory().show("AndroidPhone");
		new Factory().show("MyPhone");
		new Factory().show("YourPhone");
		new Factory().show("");
	}
}

输出:
Iphone手机在发短信
AndroidPhone手机在发短信
MyPhone手机在发短信
MyPhone手机在发短信
对不起,类型为空

面向接口编程之适配器模式

使用一个现成的类,但是它的接口不完全符合你的需求,我只想要它其中的一个方法,不想覆写其他的方法。
比如,窗体有变大,变小,关闭的行为,但是我现在只需要关闭行为;

package JBTest;

//适配器模式:只想用其中的某一个方法,用适配器作为中间的过渡
interface Windows {
	void max();

	void min();

	void close();
}

// 适配器模式,实现接口所有的方法,但是不写方法体!
class AdapterWindows implements Windows {
	@Override
	public void max() {
	}

	@Override
	public void min() {
	}

	@Override
	public void close() {
	}
}

class MyWindows extends AdapterWindows {
	// 覆写父类的方法
	public void close() {
		System.out.println("这个实现的是关闭功能!");
	}
}

public class Demo17 {
	public static void main(String[] args) {
		new MyWindows().close();
	}
}

抽象类和接口

相同点:

  • 都位于继承的顶端,用于被其他实现或继承;
  • 都不能实例化;
  • 都包含抽象方法,其子类都必须覆写这些抽象方法;

不同点:

1、设计层面

  • 接口体现的是一种行为规范 hava (鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们不能将鸟、飞机共用一个父类),类似整个系统的总纲,制订了系统各模块应该遵循的标准,因此接口不应该经常改变,一旦改变对整个系统是辐射性的。
  • 抽象类作为多个子类的共同父类 is-a(猫、狗可以抽象成一个动物类抽象类,具备叫的方法。)体现的是一种模板式设计,可以当作系统实现过程中的中间产品,已经实现了系统部分功能。
  • 抽象类是自底向上抽象(先知道子类才能抽象出父类)而来的
  • 接口是自顶向下设计(不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知)出来的。

2、使用不同

  • 接口只能包含抽象方法 public abstract,抽象类可以包含普通方法。
  • 接口里不能含有静态代码块以及静态方法,抽象类可以。是静态方法而不是抽象的静态方法
  • 接口只能定义静态常量属性public static final类型 不能定义普通属性,抽象类可以 。 接口不包含构造器,抽象类可以(不是用于创建对象而是让子类完成初始化)。
  • 接口多继承,抽象类单继承(只能有一个直接父类)。
  • java8中接口可以有default方法、static方法

二者的选用:
优先选用接口,尽量少用抽象类;需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;

门和警报的例子:

门都有open( )和close( )两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

abstract class Door {

    public abstract void open();

    public abstract void close();

}

interface Door {

    public abstract void open();

    public abstract void close();

}

但是现在如果我们需要门具有报警alarm( )的功能,那么该如何实现?下面提供两种思路:

  1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;

  2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的open( )和close( ),也许这个类根本就不具备open( )和close( )这两个功能,比如火灾报警器。

  从这里可以看出, Door的open() 、close()和alarm()根本就属于两个不同范畴内的行为,open()和close()属于门本身固有的行为特性,而alarm()属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含alarm()行为,Door设计为单独的一个抽象类,包含open和close两种行为。再设计一个报警门继承Door类和实现Alarm接口。

interface Alram {

    void alarm();

}

abstract class Door {

    void open();

    void close();

}

class AlarmDoor extends Door implements Alarm {

    void oepn() {

      //....

    }

    void close() {

      //....

    }

    void alarm() {

      //....

    }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值