最新Java基础系列课程--Day08-面向对象高级(二)https://developer.aliyun.com/article/1423505
4.3 接口的案例
各位同学,关于接口的特点以及接口的好处我们都已经学习完了。接下来我们做一个案例,先来看一下案例需求.
首先我们写一个学生类,用来描述学生的相关信息
public class Student { private String name; private char sex; private double score; public Student() { } public Student(String name, char 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 char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public double getScore() { return score; } public void setScore(double score) { this.score = score; } }
接着,写一个StudentOperator接口,表示学生信息管理系统的两个功能。
public interface StudentOperator { void printAllInfo(ArrayList<Student> students); void printAverageScore(ArrayList<Student> students); }
然后,写一个StudentOperator接口的实现类StudentOperatorImpl1,采用第1套方案对业务进行实现。
public class StudentOperatorImpl1 implements StudentOperator{ @Override public void printAllInfo(ArrayList<Student> students) { System.out.println("----------全班全部学生信息如下--------------"); for (int i = 0; i < students.size(); i++) { Student s = students.get(i); System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore()); } System.out.println("-----------------------------------------"); } @Override public void printAverageScore(ArrayList<Student> students) { double allScore = 0.0; for (int i = 0; i < students.size(); i++) { Student s = students.get(i); allScore += s.getScore(); } System.out.println("平均分:" + (allScore) / students.size()); } }
接着,再写一个StudentOperator接口的实现类StudentOperatorImpl2,采用第2套方案对业务进行实现。
public class StudentOperatorImpl2 implements StudentOperator{ @Override public void printAllInfo(ArrayList<Student> students) { System.out.println("----------全班全部学生信息如下--------------"); int count1 = 0; int count2 = 0; for (int i = 0; i < students.size(); i++) { Student s = students.get(i); System.out.println("姓名:" + s.getName() + ", 性别:" + s.getSex() + ", 成绩:" + s.getScore()); if(s.getSex() == '男'){ count1++; }else { count2 ++; } } System.out.println("男生人数是:" + count1 + ", 女士人数是:" + count2); System.out.println("班级总人数是:" + students.size()); System.out.println("-----------------------------------------"); } @Override public void printAverageScore(ArrayList<Student> students) { double allScore = 0.0; double max = students.get(0).getScore(); double min = students.get(0).getScore(); for (int i = 0; i < students.size(); i++) { Student s = students.get(i); if(s.getScore() > max) max = s.getScore(); if(s.getScore() < min) min = s.getScore(); allScore += s.getScore(); } System.out.println("学生的最高分是:" + max); System.out.println("学生的最低分是:" + min); System.out.println("平均分:" + (allScore - max - min) / (students.size() - 2)); } }
再写一个班级管理类ClassManager,在班级管理类中使用StudentOperator的实现类StudentOperatorImpl1对学生进行操作
public class ClassManager { private ArrayList<Student> students = new ArrayList<>(); private StudentOperator studentOperator = new StudentOperatorImpl1(); public ClassManager(){ students.add(new Student("迪丽热巴", '女', 99)); students.add(new Student("古力娜扎", '女', 100)); students.add(new Student("马尔扎哈", '男', 80)); students.add(new Student("卡尔扎巴", '男', 60)); } // 打印全班全部学生的信息 public void printInfo(){ studentOperator.printAllInfo(students); } // 打印全班全部学生的平均分 public void printScore(){ studentOperator.printAverageScore(students); } }
最后,再写一个测试类Test,在测试类中使用ClassMananger完成班级学生信息的管理。
public class Test { public static void main(String[] args) { // 目标:完成班级学生信息管理的案例。 ClassManager clazz = new ClassManager(); clazz.printInfo(); clazz.printScore(); } }
注意:如果想切换班级管理系统的业务功能,随时可以将StudentOperatorImpl1切换为StudentOperatorImpl2。自己试试
4.4 接口JDK8的新特性
各位同学,对于接口最常见的特性我们都学习完了。随着JDK版本的升级,在JDK8版本以后接口中能够定义的成员也做了一些更新,从JDK8开始,接口中新增的三种方法形式。
我们看一下这三种方法分别有什么特点?
public interface A { /** * 1、默认方法:必须使用default修饰,默认会被public修饰 * 实例方法:对象的方法,必须使用实现类的对象来访问。 */ default void test1(){ System.out.println("===默认方法=="); test2(); } /** * 2、私有方法:必须使用private修饰。(JDK 9开始才支持的) * 实例方法:对象的方法。 */ private void test2(){ System.out.println("===私有方法=="); } /** * 3、静态方法:必须使用static修饰,默认会被public修饰 */ static void test3(){ System.out.println("==静态方法=="); } void test4(); void test5(); default void test6(){ } }
接下来我们写一个B类,实现A接口。B类作为A接口的实现类,只需要重写抽象方法就尅了,对于默认方法不需要子类重写。代码如下:
public class B implements A{ @Override public void test4() { } @Override public void test5() { } }
最后,写一个测试类,观察接口中的三种方法,是如何调用的
public class Test { public static void main(String[] args) { // 目标:掌握接口新增的三种方法形式 B b = new B(); b.test1(); //默认方法使用对象调用 // b.test2(); //A接口中的私有方法,B类调用不了 A.test3(); //静态方法,使用接口名调用 } }
综上所述:JDK8对接口新增的特性,有利于对程序进行扩展。
4.5 接口的其他细节
最后,给同学们介绍一下使用接口的其他细节,或者说注意事项:
- 一个接口可以继承多个接口
public class Test { public static void main(String[] args) { // 目标:理解接口的多继承。 } } interface A{ void test1(); } interface B{ void test2(); } interface C{} //比如:D接口继承C、B、A interface D extends C, B, A{ } //E类在实现D接口时,必须重写D接口、以及其父类中的所有抽象方法。 class E implements D{ @Override public void test1() { } @Override public void test2() { } }
接口除了上面的多继承特点之外,在多实现、继承和实现并存时,有可能出现方法名冲突的问题,需要了解怎么解决(仅仅只是了解一下,实际上工作中几乎不会出现这种情况)
1.一个接口继承多个接口,如果多个接口中存在相同的方法声明,则此时不支持多继承 2.一个类实现多个接口,如果多个接口中存在相同的方法声明,则此时不支持多实现 3.一个类继承了父类,又同时实现了接口,父类中和接口中有同名的默认方法,实现类会有限使用父类的方法 4.一个类实现类多个接口,多个接口中有同名的默认方法,则这个类必须重写该方法。
综上所述:一个接口可以继承多个接口,接口同时也可以被类实现。
五、枚举
定义枚举类型的语法:
[修饰符] enum 枚举类型名 { 枚举成员 方法 }
枚举类型名:有两层含义,一是作为枚举名使用;二是表示枚举成员的数据类型,正因为如此,枚举成员也称为枚举实例或枚举对象。
5.1 不包含方法的枚举类
每个枚举类型的成员都可以看作是Enum类的实例,这些枚举成员默认被final public static修饰。当访问枚举类型的成员时,直接使用枚举名调用枚举成员即可,即“枚举名.枚举成员”。当然如果不想使用这种形式取得枚举类的对象,也可使用Enum类定义的valueOf()方法通过“枚举名.valueOf()”的形式进行调用来获取枚举的对象。
示例:此时枚举中的成员相当于平时定义的常量
public enum Signal { // 定义一个枚举类型 GREEN,YELLOW,RED }
public class TrafficLight { Signal color = Signal.RED; public void change() { switch(color) { case RED: color = Signal.GREEN; break; case YELLOW: color = Signal.RED; break; case GREEN: color = Signal.YELLOW; break; } } }
Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可。
所有枚举实例都可以调用 Enum 类的方法,常用方法如表 1 所示。
方法名称 | 描述 |
values() | 以数组形式返回枚举类型的所有成员 |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
ordinal() | 获取枚举成员的索引位置 |
示例:
通过调用枚举类型实例的 values( ) 方法
可以将枚举的所有成员以数组形式返回,也可以通过该方法获取枚举类型的成员。
下面的示例创建一个包含 3 个成员的枚举类型 Signal,然后调用 values() 方法输出这些成员。
enum Signal { // 定义一个枚举类型 GREEN,YELLOW,RED; } public static void main(String[] args) { for(int i = 0;i < Signal.values().length;i++) { System.out.println("枚举成员:"+Signal.values()[i]); } }
输出结果如下:
枚举成员:GREEN 枚举成员:YELLOW 枚举成员:RED
示例:
创建一个示例,调用valueOf() 方法
获取枚举的一个成员,再调用 compareTo() 方法进行比较,并输出结果。具体实现代码如下:
public class TestEnum { public enum Sex { // 定义一个枚举 male,female; } public static void main(String[] args) { compare(Sex.valueOf("male")); // 比较 } public static void compare(Sex s) { for(int i = 0;i < Sex.values().length;i++) { System.out.println(s + "与" + Sex.values()[i] + "的比较结果是:" + s.compareTo(Sex.values()[i])); } } }
上述代码中使用 Sex.valueOf(“male”) 取出枚举成员 male 对应的值,再将该值与其他枚举成员进行比较。最终输出结果如下:
male与male的比较结果是:0 male与female的比较结果是:-1
示例:
通过调用枚举类型实例的ordinal() 方法
可以获取一个成员在枚举中的索引位置。下面的示例创建一个包含 3 个成员的枚举类型 Signal,然后调用 ordinal() 方法输出成员及对应索引位置。
具体实现代码如下:
public class TestEnum1 { enum Signal { // 定义一个枚举类型 GREEN,YELLOW,RED; } public static void main(String[] args) { for(int i = 0;i < Signal.values().length;i++) { System.out.println("索引" + Signal.values()[i].ordinal()+",值:" + Signal.values()[i]); } } }
输出结果如下:
索引0,值:GREEN 索引1,值:YELLOW 索引2,值:RED
5.2 包含方法的枚举类
因为枚举也是一种类,所以它具有与其他类几乎相同的特性,因此可以定义枚举的属性、构造方法以及方法。但是,枚举的构造方法只是在构造枚举实例值时被调用。每一个枚举实例值都是枚举的一个对象,因此创建每个枚举实例时都需要调用该构造方法。
Java 为枚举类型提供了一些内置的方法,同时枚举常量也可以有自己的方法。此时要注意必须在枚举实例的最后一个成员后添加分号,而且必须先定义枚举实例。
示例:
下面的代码创建了一个枚举类型 WeekDay,而且在该类型中添加了自定义的方法
enum WeekDay { Mon("Monday"),Tue("Tuesday"),Wed("Wednesday"),Thu("Thursday"),Fri("Friday"),Sat("Saturday"),Sun("Sunday"); // 以上是枚举的成员,必须先定义,而且使用分号结束 private final String day; private WeekDay(String day) { this.day = day; } public static void printDay(int i) { switch(i) { case 1: System.out.println(WeekDay.Mon); break; case 2: System.out.println(WeekDay.Tue); break; case 3: System.out.println(WeekDay.Wed); break; case 4: System.out.println(WeekDay.Thu); break; case 5: System.out.println(WeekDay.Fri); break; case 6: System.out.println(WeekDay.Sat); break; case 7: System.out.println(WeekDay.Sun); break; default: System.out.println("wrong number!"); } } public String getDay() { return day; } }
上面代码创建了 WeekDay 枚举类型,下面遍历该枚举中的所有成员,并调用 printDay() 方法。示例代码如下:
public static void main(String[] args) { for(WeekDay day : WeekDay.values()) { System.out.println(day+"====>" + day.getDay()); } WeekDay.printDay(5); }
输了结果:
Mon====>Monday Tue====>Tuesday Wed====>Wednesday Thu====>Thursday Fri====>Friday Sat====>Saturday Sun====>Sunday Fri