阶段二JavaSE进阶阶段之多态、关键字、抽象类 2.3
文章目录
- 一、面向对象三大特征之多态
- 1.1 多态的概述
- 1.2 多态的好处
- 1.3 多态的注意事项
- 二、final关键字
- 2.1 final修饰符的特点
- 2.2 常量
- 三、 抽象类
- 3.1 什么是抽象类
- 3.2 抽象类的场景和好处
- 3.3 模板方法设计模式
- 总结
详细的java学习路径和教程请看我写的另一篇java小白到大牛的快速直通车
一、面向对象三大特征之多态
1.1 多态的概述
多态是在继承、实现情况下的一种现象,表现为:对象多态、行为多态。
比如:Teacher和Student都是People的子类,代码可以写成下面的样子
1.2 多态的好处
在多态形式下,右边的代码是解耦合的,更便于扩展和维护。
怎么理解这句话呢?比如刚开始p1指向Student对象,run方法执行的就是Student对象的业务;假如p1指向Teaacher对象 ,run方法执行的自然是Teaacher对象的业务
定义方法时,使用父类类型作为形参,可以接收一切子类对象,扩展行更强,更便利。
public class Test2 {public static void main(String[] args) {Teacher t = new Teacher();go(t);Student s = new Student();go(s);}//参数People p既可以接收Student对象,也能接收Teacher对象。public static void go(People p){ //People p = new Teacher/StudentSystem.out.println("开始------------------------");p.run();System.out.println("结束------------------------");}
}
1.3 多态的注意事项
虽然多态形式下有一些好处,但是也有一些弊端。在多态形式下,不能调用子类特有的方法,比如在Teacher类中多了一个teach方法,在Student类中多了一个study方法,这两个方法在多态形式下是不能直接调用的。
此时我们需要对p对象进行强转
如果类型转换错了,就会出现类型转换异常ClassCastException,比如把Teacher类型转换成了Student类型.
所以多态形式下不能直接调用子类特有方法,但是转型后是可以调用的。这里所说的转型就是把父类变量转换为子类类型。并且为了稳妥起见,防止转换类型出错,我们最好加上判断,格式如下:
//如果p接收的是子类对象
if(父类变量 instance 子类){//则可以将p转换为子类类型子类 变量名 = (子类)父类变量;
}
二、final关键字
接下来我们学习一个在面向对象编程中偶尔会用到的一个关键字叫final,也是为后面学习抽象类和接口做准备的。
2.1 final修饰符的特点
我们先来认识一下final的特点,final关键字是最终的意思,可以修饰类、修饰方法、修饰变量。
- final修饰类:该类称为最终类,特点是不能被继承
- final修饰方法:该方法称之为最终方法,特点是不能被重写。
- final修饰基本类型变量:该变量只能被赋值一次。
- final修饰引用类型变量:该变量的地址不可被改变,但地址所指向的内容是可以被改变的
接下来我们分别演示一下,先看final修饰类的特点
再来演示一下final修饰方法的特点
再演示一下final修饰变量的特点
- 情况一
- 情况二
- 情况三
2.2 常量
在Java中被 static final 修饰的成员变量,称之为常量。
常量通常被用于记录系统的配置信息
接下来我们用代码来演示一下
public class Constant {//常量: 定义一个常量表示学校名称//为了方便在其他类中被访问所以一般还会加上public修饰符//常量命名规范:建议都采用大写字母命名,多个单词之前有_隔开public static final String SCHOOL_NAME = "清华大学";
}
public class FinalDemo2 {public static void main(String[] args) {//由于常量是static的所以,在使用时直接用类名就可以调用System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);System.out.println(Constant.SCHOOL_NAME);}
}
那么当我们想把展示信息从清华大学改成北京大学是,只需要修改Constant中的常量就可以了,这样让我们的代码可读性会提升,也方便我们后期的维护
在程序编译后,常量会“宏替换”,出现常量的地方,全都会被替换为其记住的字面量。把代码反编译后,其实代码是下面的样子
public class FinalDemo2 {public static void main(String[] args) {System.out.println("清华大学");System.out.println("清华大学");System.out.println("清华大学");System.out.println("清华大学");System.out.println("清华大学");System.out.println("清华大学");System.out.println("清华大学");}
}
三、 抽象类
3.1 什么是抽象类
抽象类顾名思义,是一种没有具体的行为或属性的类,在Java中有一个关键字叫abstract,它就是抽象的意思,它可以修饰类也可以修饰方法。
- 被abstract修饰的类,就是抽象类
- 被abstract修饰的方法,就是抽象方法(不允许有方法体)
接下来用代码来演示一下抽象类和抽象方法
//abstract修饰类,这个类就是抽象类
public abstract class A{//abstract修饰方法,这个方法就是抽象方法public abstract void test();
}
抽象类的注意事项:
- 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
- 类该有的成员(成员变量、方法、构造器)抽象类都可以有
// 抽象类
public abstract class A {//成员变量private String name;static String schoolName;//构造方法public A(){}//抽象方法public abstract void test();//实例方法public String getName() {return name;}public void setName(String name) {this.name = name;}
}
- 抽象类不能被创建对象,仅作为一种特殊的父类,让子类继承并实现
//B类继承抽象A类,必须复写test方法
public class B extends A {@Overridepublic void test() {}
}
- 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类
3.2 抽象类的场景和好处
现在有如下的需求:
某宠物店,需要管理猫和狗的数据。
猫的数据有:名字;行为是:喵喵喵的叫~
狗的数据有:名字;行为是:汪汪汪的叫~
分析需求发现,该案例中猫和狗都有名字这个属性,也都有叫这个行为,所以我们可以将共性的内容抽取成一个父类,Animal类,但是由于猫和狗叫的声音不一样,于是我们在Animal类中将叫的行为写成抽象的。代码如下
public abstract class Animal {private String name;//动物叫的行为:不具体,是抽象的public abstract void shout();public String getName() {return name;}public void setName(String name) {this.name = name;}
}
接着写一个Animal的子类,Dog类。代码如下
public class Dog extends Animal{public void shout(){System.out.println(getName() + "汪汪汪的叫~~");}
}
然后,再写一个Animal的子类,Cat类。代码如下
public class Cat extends Animal{public void shout(){System.out.println(getName() + "喵喵喵的叫~~");}
}
最后,再写一个测试类,Test类。
public class Test2 {public static void main(String[] args) {// 利用多态来创建对象Animal a = new Dog();a.cry(); //这时执行的是Dog类的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{@Overridepublic void doSing() {System.out.println("我是一只小小小小鸟,想要飞就能飞的高~~~");}
}
接着,再写一个B类继承C类,也复写doSing()方法,代码如下
public class B extends C{@Overridepublic void doSing() {System.out.println("我们一起学猫叫,喵喵喵喵喵喵喵~~");}
}
最后,再写一个测试类Test
public class Test {public static void main(String[] args) {// 目标:搞清楚模板方法设计模式能解决什么问题,以及怎么写。B b = new B();b.sing();}
}
综上所述:模板方法模式解决了多个子类中有相同代码的问题。具体实现步骤如下
第1步:定义一个抽象类,把子类中相同的代码写成一个模板方法。
第2步:把模板方法中不能确定的代码写成抽象方法,并在模板方法中调用。
第3步:子类继承抽象类,只需要父类抽象方法就可以了。
总结
以上就是今天要讲的内容,本文介绍了多态、final、抽象类的介绍,不同场景下的使用和相关的注意事项。