Java从零开始学十八(抽象类和接口)
一、什么是抽象类和接口
抽象类、接口与类是一个层次的概念,是java中极其重要的概念。
抽象类是从多个类中抽象出来的公共模板,提供子类均具有的功能。
接口是从多个类中抽象出来的规范,体现的是规范和实现分离的原则,同时也有效的解决了java单重继承的缺陷
二、抽象类
2.1、抽象类与普通类的区别
- 抽象类的class前面有abstract关键字,而普通类没有
/* * 抽象类 */ public abstract class Person1 {} /* * 普通类 */ public class Person {}
- 抽象类中可以有抽象方法和普通方法,而普通类中只能有普通方法
- 普通类可以实例化使用new,而抽象类不能实例化
2.2、抽象方法与普通方法区别
- 抽象方法的前面有abstract关键字,而普通方法没有
- 抽象方法没有方法体,而普通方法有方法体
/* * 抽象方法 */ public abstract void show(); //没有方法体 /* * 普通方法 */ public void show(){ System.out.println("这是个普通方法"); } /* * 普通类中不能有抽象方法 */
2.3、完整对比2个类
package com.pb.demo3; /* * 普通类 */ public class Person { private String name; //名称 private int age ; //年龄 /* * 普通方法 */ public void show(){ System.out.println("这是个普通方法"); } /* * 普通类中不能有抽象方法 */ //setter/getter方法 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package com.pb.demo3; /* * 抽象类 */ public abstract class Person1 { private String name; //名称 private int age ; //年龄 /* * 抽象方法 */ public abstract void show(); //没有方法体 //抽象类中可以有普通方法 public void printSelf(){ System.out.println("这是个抽象类中的普通方法"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
2.4、如果抽象类被继承
- 如果子类不是抽象类,则必须重写父类抽象类的全部抽象方法。(普通方法选择重写或者不写)
package com.pb.demo3; /* * 学生类继承Person1抽象类 */ public class Student extends Person1 { //继承时,会强制要求重写父类的抽象方法 @Override public void show() { System.out.println("重写父类的抽象方法"); } }
2.5 、一些注意事项
- abstract不能与final关键字一起使用
- private 关键字不能修饰抽象方法
- abstract修饰的抽象方法没有方法体
2.6、图像对比
三、接口
3.1、接口定义
接口使用interface关键字来定义
简单的说,接口是一个不能实例化,且只能拥有抽象方法的类型
public interface 接口名称{ 抽象方法 }
public interface Person { }
package com.pb.demo4; public interface Person { /* * 定义2个抽象方法 */ public void run(); public void say(); }
3.2.为什么接口中的抽象方法不用使用abstract关键字?
因为接口中只能有抽象方法,所以不必专门使用abstract关键字来修饰,当然也可以加上不过完全没有必要
3.3、接口的抽象方法
接口的抽象方法只能由public来修饰
四、使用抽象类和接口
4.1、抽象类使用继承来实现
/* * 抽象类 */ public abstract class Person { /* * 普通方法 */ public void run(){ System.out.println("抽象类的普通方法! 走一走"); } /* * 抽象方法 */ public abstract void say(); }
/* * 继承抽象类 */ public class Student extends Person{ @Override public void say() { System.out.println("实现父类的抽象方法!说一说"); } }
public class Test { public static void main(String[] args) { //声明子类对象 Student stu=new Student(); //调用子类重写的方法 stu.say(); //调用父类的普通方法 stu.run(); } }
结果:
实现父类的抽象方法!说一说
抽象类的普通方法! 走一走
4.2、接口的使用
使用implements来实现接口,解决java中单继承的关系,接口可以是多个
package com.pb.demo4; public interface Person { /* * 定义2个抽象方法 */ public void run(); public void say(); }
package com.pb.demo4; /* * 实现接口Person */ public class PersonImp implements Person { /* * 必须重写接口的全部抽象方法 */ @Override public void run() { System.out.println("实现接口的走一走抽象方法"); } @Override public void say() { System.out.println("实现接口的说一说抽象方法"); } }
package com.pb.demo4; public class Test { public static void main(String[] args) { //声明对象 PersonImp p=new PersonImp(); //调用实现的方法 p.run(); p.say(); } }
结果:
实现接口的走一走抽象方法
实现接口的说一说抽象方法
4.3 、对比实现
五、抽象类和接口的简单区别
5.1、区别
- 抽象类使用abstract声明,接口使用interface声明
- 抽象类可以有抽象方法也可以有普通方法,接口只能有抽象方法
- 实现方式不同:抽象类使用继承extends来实现,接口使用implements来实现
5.2、相同点
- 抽象类和接口都不能实例化
5.3、图形对比
5.4、使用规则
- 抽象类和接口都不能实例化
- 抽象类与接口分别被继承与实现的场合,其子类必须实现其中的抽象方法
- 抽象类中的抽象方法访问修饰符不能为private,接口中的抽象方法访问修饰符必须为public
六、简单实现抽象类
package com.pb.demo5; /* * 鸟类抽象类 */ public abstract class Bird { /* * 飞的普通方法 */ public void fly(){ System.out.println("弹射飞!"); } /* * 攻击抽象方法 */ public abstract void attack(); }
package com.pb.demo5; /* * 火箭鸟 */ public class RocketBird extends Bird { /* * 重写攻击方法 */ @Override public void attack() { System.out.println("火箭鸟攻击方式:加速冲撞!"); } }
package com.pb.demo5; /* * 分裂鸟 */ public class SplittBird extends Bird { /* * 重写攻击方法 */ @Override public void attack() { System.out.println("分裂鸟攻击方式:分裂攻击!"); } }
package com.pb.demo5; public class Test { public static void main(String[] args) { /* * 多态的应用 */ //创建鸟类对象,实例化为分裂鸟对象 Bird splitbird=new SplittBird(); //创建鸟类对象,实例化为火箭鸟对象 Bird rocketbird=new RocketBird(); //分别调用鸟类的普通飞的方法 //分别调用子类重写后的叫方法 //分别调用子类重写后的攻击方法 splitbird.fly(); splitbird.attack(); rocketbird.fly(); rocketbird.attack(); } }
结果:
弹射飞! 分裂鸟攻击方式:分裂攻击! 弹射飞! 火箭鸟攻击方式:加速冲撞!
从上例子中可以看出:子类相同的方法,如:飞的方法 可以放在父类的抽象类中使用普通方法实现,实现代码的重用,而不同的部分,使用抽象方法,子类来实现自己的方法,简单来讲:抽象类就是抽取公共的部分在抽象来中来实现
如:完全没有相同的方法,但都有这此方法,该怎么实现呢?这时候就需要接口大神出场
七、简单接口实现
还是以上例为例,比如,连有叫的方法,但每种 鸟的叫声都不一样呢?有的会叫,有的不会叫,
package com.pb.demo5; /* * 叫的接口 */ public interface Shout { /* * 叫的方法 */ public void shout(); } package com.pb.demo5; public class AoShoutImpl implements Shout { @Override public void shout() { System.out.println("嗷嗷叫!"); } } package com.pb.demo5; public class ZhaShoutImpl implements Shout { @Override public void shout() { System.out.println("喳喳叫!"); } } package com.pb.demo5; public class NoCanShoutImpl implements Shout { @Override public void shout() { System.out.println("不会叫!"); } }
package com.pb.demo5; /* * 鸟类抽象类 */ public abstract class Bird { Shout shout; /* * 构造方法 */ public Bird(Shout shout) { super(); this.shout = shout; } /* * 叫的普通方法 */ public void shout(){ //调用自身叫的方法 shout.shout(); } /* * 飞的普通方法 */ public void fly(){ System.out.println("弹射飞!"); } /* * 抽象攻击方法 */ public abstract void attack(); }
package com.pb.demo5; /* * 炸弹鸟 */ public class Bombbird extends Bird { public Bombbird(Shout shout) { super(shout); } @Override public void attack() { System.out.println("炸弹鸟攻击方式:炸弹攻击"); } @Override public void shout() { System.out.println("炸弹鸟:喳喳叫!"); } } package com.pb.demo5; /* * 胖子鸟 */ public class Fatbird extends Bird { public Fatbird(Shout shout) { super(shout); } @Override public void attack() { System.out.println("胖子鸟攻击方式:下蛋攻击"); } @Override public void shout() { System.out.println("胖子鸟:不会叫!"); } } package com.pb.demo5; /* * 红色鸟 */ public class Redbird extends Bird { public Redbird(Shout shout) { super(shout); } @Override public void attack() { System.out.println("红色鸟攻击方式:普通攻击"); } @Override public void shout() { System.out.println("红色鸟:喳喳叫!"); } } package com.pb.demo5; import com.pb.demo5.Bird; /* * 火箭鸟 */ public class RocketBird extends Bird { public RocketBird(Shout shout) { super(shout); } /* * 重写攻击方法 */ @Override public void attack() { System.out.println("火箭鸟攻击方式:加速冲撞!"); } /* * 重写叫的方法 */ @Override public void shout() { System.out.println("火箭鸟:嗷嗷叫!"); } } package com.pb.demo5; /* * 分裂鸟 */ public class SplittBird extends Bird { public SplittBird(Shout shout) { super(shout); } /* * 重写攻击方法 */ @Override public void attack() { System.out.println("分裂鸟攻击方式:分裂攻击!"); } /* * 重写叫的方法 */ @Override public void shout() { System.out.println("分裂鸟:嗷嗷叫!"); } }
package com.pb.demo5; import com.pb.demo5.Bird; public class Test { public static void main(String[] args) { /* * 创建接口对象 */ Shout aoshout=new AoShoutImpl(); Shout zhashout=new ZhaShoutImpl(); Shout noshout=new NoCanShoutImpl(); /* * 多态的应用 */ //创建鸟类对象,实例化为分裂鸟对象 Bird splitbird=new SplittBird(aoshout); Bird rocketbird=new RocketBird(aoshout); Bird bomb=new Bombbird(zhashout); Bird redbird=new Redbird(zhashout); Bird fatbird=new Fatbird(noshout); splitbird.fly(); splitbird.attack(); splitbird.shout(); rocketbird.fly(); rocketbird.shout(); rocketbird.attack(); bomb.fly(); bomb.shout(); bomb.attack(); redbird.fly(); redbird.shout(); redbird.attack(); fatbird.fly(); fatbird.shout(); fatbird.attack(); } }
结果:
弹射飞! 分裂鸟攻击方式:分裂攻击! 分裂鸟:嗷嗷叫! 弹射飞! 火箭鸟:嗷嗷叫! 火箭鸟攻击方式:加速冲撞! 弹射飞! 炸弹鸟:喳喳叫! 炸弹鸟攻击方式:炸弹攻击 弹射飞! 红色鸟:喳喳叫! 红色鸟攻击方式:普通攻击 弹射飞! 胖子鸟:不会叫! 胖子鸟攻击方式:下蛋攻击
由上可以看出:抽象类方便代码复用,接口方便代码维护