抽象类和接口在我们的代码中,生活中息息相关,与上图所示,操纵复杂,密切相关,那么问题来了,何谓抽象类,何谓接口?
带着这层薄纱,慢慢揭开这层薄纱;也许在古代,新婚之夜,透过这层薄纱,你看到的或者是惊喜,或许是惊悚,不要怕,无论是惊悚还是惊喜,她都会伴你一生。
曾几何时?你还会在面试当中与面试官对答如流的解释抽象类和接口吗?
面试官:解释一下抽象类和接口的区别?
me:
1、抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
2、抽象类要被子类继承,接口要被类实现。
3、接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
4、接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
5、抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
6、抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果
7、抽象类里可以没有抽象方法
8、如果一个类里有抽象方法,那么这个类只能是抽象类
扫描二维码关注公众号,回复: 2743592 查看本文章9、抽象方法要被实现,所以不能是静态的,也不能是私有的。
10、接口可继承接口,并可多继承接口,但类只能单根继承。
好吧,对答如流,可能是背会了,而且很熟,我们不仅仅是为了会背,如果过一段时间不忘记,能够根深蒂固,那只有融入到我们的思维中,形成我们自己的语言,不仅仅是文字的阐述,更是以图的形式存在,记忆在右脑当中,那么下面让我们探讨一下,在现实与代码中他们的作用。
我看网上有个例子很好,特此举例(适配器的概念):
没有使用适配器的时候,实现接口的类要实现接口的所有方法,即便只是对其中一个方法进行了实现
建立一个抽象类实现接口(适配器概念),再用抽象类的子类重写想要实现的方法
tips:对于接口,你需要继承接口里边所有的实现,而建立一个抽象类实现接口,那么可以用一个普通类实现这个抽象类就可以实现你需要的方法,方便了许多。
下面就是一个抽象类和接口引发的血案:
涉及到
1)掌握抽象类和接口的实例化操作。
2)掌握模板设计的作用。
3)掌握工厂设计模式的作用。
4)掌握代理设计模式的作用。
5)掌握适配器模式的作用。
6)掌握抽象类与接口的使用区别。
具体内容呢?
1、 在java中,可以通过对象的多态性,为抽象类和接口实例化,这样再使用抽象类和接口的时候就可以调用本子类中所覆写过的方法。
之所以抽象类和接口不能直接实例化,是因为其内部包含了抽象方法,抽象方法本身是未实现的方法,所以无法调用。
通过对象多态性可以发现,子类发生了向上转型关系之后,所调用的全部方法,都是被覆写过的方法。如下:
abstract class A{ // 定义抽象类A
public abstract void print() ; // 定义抽象方法print()
};
class B extends A { // 定义子类,继承抽象类
public void print(){ // 覆写抽象方法
System.out.println("Hello World!") ;
}
};
public class AbstractCaseDemo01{
public static void main(String args[]){
A a = new B() ; // 通过子类为抽象类实例化,向上转型。
a.print() ;
}
};
运行结果:
Hello World!
可以继续利用此概念,为接口实例化。
package com.zhijin;
interface A{ // 定义抽象类A
public abstract void print() ; // 定义抽象方法print()
};
class B implements A { // 定义子类,继承抽象类
public void print(){ // 覆写抽象方法
System.out.println("Hello World!!!") ;
}
};
public class ThisDemo06{
public static void main(String args[]){
A a = new B() ; // 通过子类为抽象类实例化
a.print() ;
}
};
所以,如果想要使用抽象类和接口,则只能按照以上操作完成。
2、抽象类的实际使用场景–设计模式(模板设计)
super : 动物
子类:猫、狗
abstract class Animal{
private String name ; // 定义name属性
private int age ; // 定义age属性
public Animal(String name,int age){
this.name = name ;
this.age = age ;
}
public String getName(){
return this.name ;
}
public int getAge(){
return this.age ;
}
public void say(){ // 人说话是一个具体的功能
System.out.println(this.getContent()) ; // 输出内容
}
public abstract String getContent() ; // 说话的内容由子类决定
};
class Cat extends Animal{
private String call;
public Cat(String name,int age,String call){
super(name,age) ; // 调用父类中的构造方法
this.call= call ;
}
public String getContent(){
return " 姓名:" + super.getName() +
";年龄:" + super.getAge() +
";叫声:" + this.call;
}
};
class Dog extends Animal{
private String call ;
public Dog (String name,int age,String call){
super(name,age) ; // 调用父类中的构造方法
this.call= call;
}
public String getContent(){
return "姓名:" + super.getName() +
";年龄:" + super.getAge() +
";叫声:" + this.call ;
}
};
public class AbstractCaseDemo02{
public static void main(String args[]){
Animal per1 = null ; // 声明Animal 对象
Animal per2 = null ; // 声明Animal 对象
per1 = new Cat("猫儿",2,"喵儿") ;
per2 = new Dog("小狗",10,"wangwang") ;
per1.call() ;
per2.call() ;
}
};
运行结果:
姓名:张三;年龄:2;喵儿
姓名:李四;年龄:10;wangwang
tips:抽象类就相当于一个模板,现实的模板:
3、接口的实际应用–制定标准
比如说“U盘和打印机都可以插在电脑上使用,因为他们都实现了USB标准的接口”
interface USB{ // 定义了USB接口
public void start() ; // USB设备开始工作
public void stop() ; // USB设备结束工作
}
class Computer{
public static void plugin(USB usb){ // 电脑上可以插入USB设备,向上转型,这里就相当于一个接口,只有符合这个接口的标准的类的对象(即只有继承这个接口的类的对象),才能被这个方法调用。
usb.start() ;
System.out.println("=========== USB 设备工作 ========") ;
usb.stop() ;
}
};
class Flash implements USB{
public void start(){ // 覆写方法
System.out.println("U盘开始工作。") ;
}
public void stop(){ // 覆写方法
System.out.println("U盘停止工作。") ;
}
};
class Print implements USB{
public void start(){ // 覆写方法
System.out.println("打印机开始工作。") ;
}
public void stop(){ // 覆写方法
System.out.println("打印机停止工作。") ;
}
};
public class InterfaceCaseDemo02{
public static void main(String args[]){
Computer.plugin(new Flash()) ;
Computer.plugin(new Print()) ;
}
};
运行结果:
U盘开始工作。
=========== USB 设备工作 ========
U盘停止工作。
打印机开始工作。
=========== USB 设备工作 ========
打印机停止工作。
4、工厂设计模式–接口应用
interface Fruit{ // 定义一个水果接口
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){
System.out.println("** 吃苹果。") ;
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子。") ;
}
};
public class InterfaceCaseDemo03{
public static void main(String args[]){
Fruit f = new Apple() ; // 实例化接口
f.eat() ;
}
};
运行结果:
吃苹果。
看起来上边的代码没有什么大问题,但是扩展性呢?
JVM工作原理:程序-》JVM(相当于客户端)-》操作系统。
问题的解决:客户端通过过渡端,得到特定子类的接口实例,返回接口实例给客户端,接口实例调用接口中的方法。
此时就涉及到了工厂模式
interface Fruit{ // 定义一个水果接口
public void eat() ; // 吃水果
}
class Apple implements Fruit{
public void eat(){
System.out.println("** 吃苹果。") ;
}
};
class Orange implements Fruit{
public void eat(){
System.out.println("** 吃橘子。") ;
}
};
class Factory{ // 定义工厂类
public static Fruit getInstance(String className){ //注意这里的方法是static修饰的,因为在主方法中是Factory调用
Fruit f = null ;
if("apple".equals(className)){ // 判断是否要的是苹果的子类
f = new Apple() ;
}
if("orange".equals(className)){ // 判断是否要的是橘子的子类
f = new Orange() ;
}
return f ;
}
};
public class InterfaceCaseDemo04{
public static void main(String args[]){
Fruit f = Factory.getInstance(”apple“) ; // 实例化接口
f.eat() ;
}
};
运行结果:
**吃苹果
5、代理模式–接口应用
这里说的代理模式有很多种,讨债公司代理要讨债的人,江歌案中(日本律师代理江母),下面我们讲一下我们经常使用的代理服务器;
interface Network{
public void browse() ; // 浏览
}
class Real implements Network{ //真实上网操作类
public void browse(){
System.out.println("上网浏览信息") ;
}
};
class Proxy implements Network{ //代理类。
private Network network ; // 代理对象
public Proxy(Network network){ //初始化,把真实对象传给代理对象,向上转型操作。
this.network = network ;
}
public void check(){
System.out.println("检查用户是否合法。") ;
}
public void browse(){
this.check() ;
this.network.browse() ; // 调用真实的主题操作,这里调用的是真实类里的对象。
}
};
public class ProxyDemo{
public static void main(String args[]){
Network net = null ;
net = new Proxy(new Real()) ;// 指定代理操作,这里两次向上转型操作,第一次向上是实例化代理类,
第二次向上转型是括号中,把真实类对象传入,以便在代理类中调用真实类中的方法。
net.browse() ; // 客户只关心上网浏览一个操作
}
};
运行结果:
检查用户是否合法
上网浏览信息
5、适配器模式–上边刚开始的案例
在Java中,一个子类实现一个接口,则肯定在子类中复写此接口中全部抽象方法,那么这就造成提供的抽象方法过多,而实际不需要,那么我们会选择一个中间过渡(也就是适配器模式)
interface Window{ // 定义Window接口,表示窗口操作
public void open() ; // 打开
public void close() ; // 关闭
public void activated() ; // 窗口活动
public void iconified() ; // 窗口最小化
public void deiconified();// 窗口恢复大小
}
abstract class WindowAdapter implements Window{
public void open(){} ; // 打开
public void close(){} ; // 关闭
public void activated(){} ; // 窗口活动
public void iconified(){} ; // 窗口最小化
public void deiconified(){};// 窗口恢复大小
};
class WindowImpl extends WindowAdapter{
public void open(){
System.out.println("窗口打开。") ;
}
public void close(){
System.out.println("窗口关闭。") ;
}
};
public class AdapterDemo{
public static void main(String args[]){
Window win = new WindowImpl() ;
win.open() ;
win.close() ;
}
};
运行结果:
窗口打开。
窗口关闭。
6、内部类的扩展
abstract class A{ // 定义抽象类
public abstract void printA() ; // 抽象方法
interface B{ // 定义内部接口
public void printB() ; // 定义抽象方法
}
};
class X extends A{ // 继承抽象类
public void printA(){
System.out.println("HELLO --> A") ;
}
class Y implements B{ // 定义内部类实现内部接口
public void printB(){
System.out.println("HELLO --> B") ;
}
};
};
public class InnerExtDemo01{
public static void main(String args[]){
A.B b = new X().new Y() ; //参考前面内部类的知识,实现内部类实例的方法:外部类.内部类 内部类对象= 外部类对象.new 内部类
b.printB() ;
}
};
运行结果:
HELLO-->B
含有内部接口的抽象类的子类,必须也要定义内部类继承该接口。
interface A{ // 定义接口
public void printA() ; // 抽象方法
abstract class B{ // 定义内部抽象类
public abstract void printB() ; // 定义抽象方法
}
};
class X implements A{ // 实现接口
public void printA(){
System.out.println("HELLO --> A") ;
}
class Y extends B{ // 继承抽象类
public void printB(){
System.out.println("HELLO --> B") ;
}
};
};
public class InnerExtDemo02{
public static void main(String args[]){
A.B b = new X().new Y() ;
b.printB() ;
}
};
含有内部抽象类的接口,对于继承他的子类来说,除了要覆写接口中的抽象方法,还要专门定义一个内部类继承抽象类,并覆写全部抽象类。
从开发中,一般不推荐,看起来这些代码有点混乱。
总结:
Tips:在开发中,一个类永远不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口,如果两个类同时可以使用的话,优先使用接口,避免单继承的局限性。
1)抽象类和接口类的实例化,通过多态性(向上转型,向下转型)。
2)抽象类表示一个模板,接口表示一个标准。
3)常见的设计模式:模板设计,工厂设计,代理设计,适配器设计。