最新Java基础系列课程--Day08-面向对象高级(一)https://developer.aliyun.com/article/1423504
3.2 抽象类的好处
接下来我们用一个案例来说一下抽象类的应用场景和好处。需求如下图所示
分析需求发现,该案例中猫和狗都有名字这个属性,也都有叫这个行为,所以我们可以将共性的内容抽取成一个父类,Animal类,但是由于猫和狗叫的声音不一样,于是我们在Animal类中将叫的行为写成抽象的。代码如下
public abstract class Animal { private String name; //动物叫的行为:不具体,是抽象的 public abstract void cry(); public String getName() { return name; } public void setName(String name) { this.name = name; } }
接着写一个Animal的子类,Dog类。代码如下
public class Dog extends Animal{ public void cry(){ System.out.println(getName() + "汪汪汪的叫~~"); } }
然后,再写一个Animal的子类,Cat类。代码如下
public class Cat extends Animal{ public void cry(){ System.out.println(getName() + "喵喵喵的叫~~"); } }
最后,再写一个测试类,Test类。
public class Test2 { public static void main(String[] args) { // 目标:掌握抽象类的使用场景和好处. Animal a = new Dog(); a.cry(); //这时执行的是Dog类的cry方法 } }
再学一招,假设现在系统有需要加一个Pig类,也有叫的行为,这时候也很容易原有功能扩展。只需要让Pig类继承Animal,复写cry方法就行。
public class Pig extends Animal{ @Override public void cry() { System.out.println(getName() + "嚯嚯嚯~~~"); } }
此时,创建对象时,让Animal接收Pig,就可以执行Pig的cry方法
public class Test2 { public static void main(String[] args) { // 目标:掌握抽象类的使用场景和好处. Animal a = new Pig(); a.cry(); //这时执行的是Pig类的cry方法 } }
综上所述,我们总结一下抽象类的使用场景和好处
1.用抽象类可以把父类中相同的代码,包括方法声明都抽取到父类,这样能更好的支持多态,一提高代码的灵活性。 2.反过来用,我们不知道系统未来具体的业务实现时,我们可以先定义抽象类,将来让子类去实现,以方便系统的扩展。
3.3 模板方法模式
学习完抽象类的语法之后,接下来,我们学习一种利用抽象类实现的一种设计模式。先解释下一什么是设计模式?设计模式是解决某一类问题的最优方案。
设计模式在一些源码中经常会出现,还有以后面试的时候偶尔也会被问到,所以在合适的机会,就会给同学们介绍一下设计模式的知识。
那模板方法设计模式解决什么问题呢?模板方法模式主要解决方法中存在重复代码的问题
比如A类和B类都有sing()方法,sing()方法的开头和结尾都是一样的,只是中间一段内容不一样。此时A类和B类的sing()方法中就存在一些相同的代码。
怎么解决上面的重复代码问题呢? 我们可以写一个抽象类C类,在C类中写一个doSing()的抽象方法。再写一个sing()方法,代码如下:
// 模板方法设计模式 public abstract class C { // 模板方法 public final void sing(){ System.out.println("唱一首你喜欢的歌:"); doSing(); System.out.println("唱完了!"); } public abstract void doSing(); }
然后,写一个A类继承C类,复写doSing()方法,代码如下
public class A extends C{ @Override public void doSing() { System.out.println("我是一只小小小小鸟,想要飞就能飞的高~~~"); } }
接着,再写一个B类继承C类,也复写doSing()方法,代码如下
public class B extends C{ @Override public void doSing() { System.out.println("我们一起学猫叫,喵喵喵喵喵喵喵~~"); } }
最后,再写一个测试类Test
public class Test { public static void main(String[] args) { // 目标:搞清楚模板方法设计模式能解决什么问题,以及怎么写。 B b = new B(); b.sing(); } }
综上所述:模板方法模式解决了多个子类中有相同代码的问题。具体实现步骤如下
第1步:定义一个抽象类,把子类中相同的代码写成一个模板方法。 第2步:把模板方法中不能确定的代码写成抽象方法,并在模板方法中调用。 第3步:子类继承抽象类,只需要父类抽象方法就可以了。
四、接口
同学们,接下来我们学习一个比抽象类抽象得更加彻底的一种特殊结构,叫做接口。在学习接口是什么之前,有一些事情需要给大家交代一下:Java已经发展了20多年了,在发展的过程中不同JDK版本的接口也有一些变化,所以我们在学习接口时,先以老版本为基础,学习完老版本接口的特性之后,再顺带着了解一些新版本接口的特性就可以了。
4.1 认识接口
我们先来认识一下接口?Java提供了一个关键字interface,用这个关键字来定义接口这种特殊结构。格式如下
public interface 接口名{ //成员变量(常量) //成员方法(抽象方法) }
按照接口的格式,我们定义一个接口看看
public interface A{ //这里public static final可以加,可以不加。 public static final String SCHOOL_NAME = "黑马程序员"; //这里的public abstract可以加,可以不加。 public abstract void test(); }
写好A接口之后,在写一个测试类,用一下
public class Test{ public static void main(String[] args){ //打印A接口中的常量 System.out.println(A.SCHOOL_NAME); //接口是不能创建对象的 A a = new A(); } }
我们发现定义好接口之后,是不能创建对象的。那接口到底什么使用呢?需要我注意下面两点
- 接口是用来被类实现(implements)的,我们称之为实现类。
- 一个类是可以实现多个接口的(接口可以理解成干爹),类实现接口必须重写所有接口的全部抽象方法,否则这个类也必须是抽象类
比如,再定义一个B接口,里面有两个方法testb1(),testb2()
public interface B { void testb1(); void testb2(); }
接着,再定义一个C接口,里面有两个方法testc1(), testc2()
public interface C { void testc1(); void testc2(); }
然后,再写一个实现类D,同时实现B接口和C接口,此时就需要复写四个方法,如下代码
// 实现类 public class D implements B, C{ @Override public void testb1() { } @Override public void testb2() { } @Override public void testc1() { } @Override public void testc2() { } }
最后,定义一个测试类Test
public class Test { public static void main(String[] args) { // 目标:认识接口。 System.out.println(A.SCHOOL_NAME); // A a = new A(); D d = new D(); } }
4.2 接口的好处
同学们,刚刚上面我们学习了什么是接口,以及接口的基本特点。那使用接口到底有什么好处呢?主要有下面的两点
- 弥补了类单继承的不足,一个类同时可以实现多个接口。
- 让程序可以面向接口编程,这样程序员可以灵活方便的切换各种业务实现。
我们看一个案例演示,假设有一个Studnet学生类,还有一个Driver司机的接口,还有一个Singer歌手的接口。
现在要写一个A类,想让他既是学生,偶然也是司机能够开车,偶尔也是歌手能够唱歌。那我们代码就可以这样设计,如下:
class Student{ } interface Driver{ void drive(); } interface Singer{ void sing(); } //A类是Student的子类,同时也实现了Dirver接口和Singer接口 class A extends Student implements Driver, Singer{ @Override public void drive() { } @Override public void sing() { } } public class Test { public static void main(String[] args) { //想唱歌的时候,A类对象就表现为Singer类型 Singer s = new A(); s.sing(); //想开车的时候,A类对象就表现为Driver类型 Driver d = new A(); d.drive(); } }
综上所述:接口弥补了单继承的不足,同时可以轻松实现在多种业务场景之间的切换。
最新Java基础系列课程--Day08-面向对象高级(三)https://developer.aliyun.com/article/1423506