通过学习本篇文章可以掌握如下知识
1、多态;
2、抽象类;
3、接口。
之前已经学过了继承,static等基础知识,这篇文章我们就开始深入了解面向对象多态、抽象类和接口的学习。
多态是在继承/实现情况下的一种现象,表现为:对象多态和行为多态。
指的是一个人可以有多个身份,例如可以是学生,老师和消防员
指的是不同身份的人都可以跑,但是跑的行为不同,这里的行为包括速度、姿态等等都可以称之为行为。对应到代码里面就是,run方法的实现逻辑不同。注意现实或者文字描述如何对应到代码。
代码验证
// 分类
public class People {
public void run(){
System.out.println("人可以跑");
}
}
class Student extends People{
@Override
public void run(){
System.out.println("学生可以跑的很快");
}
}
class Teacher extends People{
@Override
public void run(){
System.out.println("老师可以跑有点慢~");
}
}
还记得上一篇文章讲的方法的覆盖吗?方法能覆盖的前提是父类对象也有相同的方法。因此p1想要调用run方法的前提是Person类有run方法,否则会报错。
到此相信你应该对动态有一定的认识了吧。
那么仅仅认识这些还是不够的,下来讲对多态的一个全新的用法。
方法的入参是父类,则可以接收父类和子类对象
代码验证
public class Test {
public static void main(String[] args) {
People s1 = new Student();
People p1 = new People();
People t1 = new Student();
//方法的入参是父类,则可以接收父类和子类对象
go(p1);
go(s1);
go(t1);
}
public static void go( People p) {
p.run();
}
}
结果验证
在java中final是常量的意思,相对比较简单,其功能也比较用以验证。因此就直接将其功能结论记录如下。
总结
在java中存在一个关键词:abstract(抽象) 用来修饰类和成员方法。
被修饰的类叫做抽象类,被修饰方法叫做抽象方法。
特点:
- 抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类。
- 抽象类不能创建对象,一般来说抽象类往往作为父类。
- 一个类A继承抽象类,必须重写抽象类的全部抽象方法,否则类A也被定义为抽象类。
这些特点可以用代码验证,由于过于简单这里就不再阐述。
抽象类的场景和好处
之前学习到了多态,可以配合抽象类一起使用,
- 父类知道子类想要做某个行为,但是每个子类的实现情况不一样,因此父类可以定义为抽象类,交给子类重写方法,可以更方便的支持多态。
例如:
代码实现
学习完模板模式,你应该总结出,模板模式解决了什么问题,解决思路是怎么样的。
它为解决方法中存在重复代码的问题。
如何实现?
1、定义一个抽象类
2、在里面定义两个方法
一个是模板方法:把相同代码放里面去。
一个是抽象方法:具体实现交给子类完成。
场景1:写作文:
第一段是相同的
最后一段也是相同的
只有最后一段是不同的。
场景2:移动支付
支付前的校验
不同的支付方式(支付宝,微信,银行卡,xxx)
支付后的操作。
public abstract class People {
public void write(){
System.out.println("\t\t\t《看书》");
System.out.println("周六作业看书xxxxxx");
System.out.println(writeMain());
System.out.println("终于看完了");
}
public abstract String writeMain();
}
public class Student extends People{
@Override
public String writeMain() {
return "我是学生我爱看书xxxxx";
}
}
public class Teacher extends People{
@Override
public String writeMain() {
return "我是老师,我要看名著";
}
}
// 测试
public class Test {
public static void main(String[] args) {
People s1 = new Student();
s1.write();
System.out.println("===============");
People t1 = new Teacher();
t1.write();
}
}
测试结果
java提供了关键字interface,用这个关键字可以定义一个特殊的结构:接口
在jdk1.8之前,接口中只能定义成员变量和抽象方法
接口的好处:
1、弥补了类单继承的不足,一个类可以同时实现多个接口
2、让程序可以面向接口编程,这样程序更加灵活。(多态)
代码实现
public class Test {
public static void main(String[] args) {
}
}
class A extends Student implements Driver, Singer{
@Override
public void drive() {
System.out.println("我继承了student,并且会开车");
}
@Override
public void song() {
System.out.println("我继承了student,并且会唱歌");
}
}
class Student{
}
interface Driver{
void drive();
}
interface Singer{
void song();
}
总结
继承就像在说你是谁,你来自哪里哪一类。
接口就好像在说你会什么,有什么能力,你被贴了什么标签,例如歌手标签,
代码实现
// 测试类
public class Test {
public static void main(String[] args) {
ClassManage classManage = new ClassManage(new StudentOperatorImpl2());
ArrayList<Student> students = classManage.getStudents();
students.add(new Student("迪丽热巴",false,99));
students.add(new Student("古力娜扎",false,100));
students.add(new Student("马尔扎哈",true,95));
students.add(new Student("卡尔扎巴",true,92));
classManage.studentOperator.printAllInfo(classManage.getStudents());
classManage.studentOperator.printAverageScore(classManage.getStudents());
}
}
学生类
public class Student {
private String name;
private boolean sex;
private double score;
public Student() {
}
public Student(String name, boolean sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
班级管理类
public class ClassManage {
private ArrayList<Student> students;
StudentOperator studentOperator;
public ClassManage(StudentOperator operator) {
this.studentOperator = operator;
this.students = new ArrayList<>(10);
}
public void addStudent(Student student){
students.add(student);
}
public ArrayList<Student> getStudents() {
return students;
}
}
操作接口
public interface StudentOperator {
void printAllInfo(ArrayList<Student> students);
void printAverageScore(ArrayList<Student> students);
}
接口实现类1
public class StudentOperatorImpl1 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("--------全班全部学生信息如下----------");
students.forEach(student -> {
System.out.println("姓名:" + student.getName());
System.out.println("性别:" + student.isSex());
System.out.println("成绩:" + student.getScore());
});
}
@Override
public void printAverageScore(ArrayList<Student> students) {
System.out.println("-------全班学生平均成绩如下-----------");
double sum = 0.0;
for (Student student : students) {
sum += student.getScore();
}
System.out.println("全班的平均成绩为:" + sum / students.size());
}
}
接口实现类2
public class StudentOperatorImpl2 implements StudentOperator {
@Override
public void printAllInfo(ArrayList<Student> students) {
System.out.println("--------全班全部学生信息如下----------");
int male = 0;
int female = 0;
for (Student student : students) {
System.out.println("姓名:" + student.getName());
System.out.println("性别:" + student.isSex());
System.out.println("成绩:" + student.getScore());
if (student.isSex()) {
male++;
} else {
female++;
}
}
System.out.println("男生人数:" + male + ", 女生人数:" + female);
}
@Override
public void printAverageScore(ArrayList<Student> students) {
double max = 0.0;
double min = 101.0;
double sum = 0.0;
for (Student student : students) {
max = Math.max(max, student.getScore());
min = Math.min(min, student.getScore());
sum += student.getScore();
}
System.out.println("全班的平均成绩为:" + ((sum - max - min) / (students.size() - 2)));
}
}
为什么实现的这么复杂呢?因为这样设计的好处就在于可以仅仅改变下图这一个地方就可以改变不同的实现方案。
默认方法:如果接口只有默认方法,那么实现类就可以直接使用。
public class Test2 {
public static void main(String[] args) {
AImpl a1 = new AImpl();
a1.test1();
}
}
interface A{
// 新增的默认方法。
// 必须使用default 修饰
public default void test1(){
System.out.println("===默认方法===");
}
}
class AImpl implements A{
}
结果展示
私有方法
jdk9以后才有的新特性,私有方法和默认方法能够访问私有方法。
静态方法
必须使用static修饰,直接使用接口调用。
总结
有时候你会迷茫学这些干嘛?这些是java的基础特性,就是要背诵或者熟练使用的,就好像数学中懂了加法和乘法才会数学运算一样,因此需要熟练掌握。