1、 抽象类

看我们以前示例中的Person、Student和Employee,从我们使用的角度来看主要对Student和Employee进行实例化,Person中主要包含了一些公共的属性和方法,而Person我们通常不会实例化,所以我们可以把它定义成抽象的:

  • 在java中采用abstract关键字定义的类就是抽象类,采用abstract关键字定义的方法就是抽象方法
  • 抽象的方法只需在抽象类中,提供声明,不需要实现
  • 如果一个类中含有抽象方法,那么这个类必须定义成抽象类
  • 如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被重写。如果在子类中不复写该抽象方法,那么必须将此类再次声明为抽象类
  • 抽象的类是不能实例化的,就像现实世界中人其实是抽象的,张三、李四才是具体的
  • 抽象类不能被final修饰(它是需要被继承的)
  • 抽象方法不能被final修饰,因为抽象方法就是被子类实现的

抽象类中可以包含方法实现,可以将一些公共的代码放到抽象类中,另外在抽象类中可以定义一些抽象的方法,这样就会存在一个约束,而子类必须实现我们定义的方法,如:teacher必须实现printInfo方法,Student也必须实现printInfo方法,方法名称不能修改,必须为printInfo,这样就能实现多态的机制,有了多态的机制,我们在运行期就可以动态的调用子类的方法。所以在运行期可以灵活的互换实现。

1646730907(1).png

1、采用abstract声明抽象类

  1. public class AbstractTest01 {
  2. public static void main(String[] args) {
  3. //不能实例化抽象类
  4. //抽象类是不存在,抽象类必须有子类继承
  5. Person p = new Person();
  6. //以下使用是正确的,因为我们new的是具体类
  7. Person p1 = new Employee();
  8. p1.setName("张三");
  9. System.out.println(p1.getName());
  10. }
  11. }
  12. //采用abstract定义抽象类
  13. //在抽象类中可以定义一些子类公共的方法或属性
  14. //这样子类就可以直接继承下来使用了,而不需要每个
  15. //子类重复定义
  16. abstract class Person {
  17. private String name;
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. public String getName() {
  22. return name;
  23. }
  24. //此方法各个子类都可以使用
  25. public void commonMethod1() {
  26. System.out.println("---------commonMethod1-------");
  27. }
  28. }
  29. class Employee extends Person {
  30. }
  31. class Student extends Person {
  32. }

2、抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现

  1. public class AbstractTest02 {
  2. public static void main(String[] args) {
  3. //Person p = new Employee();
  4. //Person p = new Student();
  5. //Person p = new Person();
  6. p.setName("张三");
  7. p.printInfo();
  8. }
  9. }
  10. abstract class Person {
  11. private String name;
  12. public void setName(String name) {
  13. this.name = name;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. //此方法各个子类都可以使用
  19. public void commonMethod1() {
  20. System.out.println("---------commonMethod1-------");
  21. }
  22. //public void printInfo() {
  23. // System.out.println("------Person.printInfo()--------");
  24. //}
  25. //采用abstract定义抽象方法
  26. //如果有一个方法为抽象的,那么此类必须为抽象的
  27. //如果一个类是抽象的,并不要求具有抽象的方法
  28. public abstract void printInfo();
  29. }
  30. class Employee extends Person {
  31. //必须实现抽象的方法
  32. public void printInfo() {
  33. System.out.println("Employee.printInfo()");
  34. }
  35. }
  36. class Student extends Person {
  37. //必须实现抽象的方法
  38. public void printInfo() {
  39. System.out.println("Student.printInfo()");
  40. }
  41. }

3、如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被覆盖。如果在子类中不覆盖该抽象方法,那么必须将此方法再次声明为抽象方法

  1. public class AbstractTest03 {
  2. public static void main(String[] args) {
  3. //此时不能再new Employee了
  4. Person p = new Employee();
  5. }
  6. }
  7. abstract class Person {
  8. private String name;
  9. public void setName(String name) {
  10. this.name = name;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. //此方法各个子类都可以使用
  16. public void commonMethod1() {
  17. System.out.println("---------commonMethod1-------");
  18. }
  19. //采用abstract定义抽象方法
  20. public abstract void printInfo();
  21. }
  22. abstract class Employee extends Person {
  23. //再次声明该方法为抽象的
  24. public abstract void printInfo();
  25. }
  26. class Student extends Person {
  27. //实现抽象的方法
  28. public void printInfo() {
  29. System.out.println("Student.printInfo()");
  30. }
  31. }

4、抽象类不能被final修饰

  1. public class AbstractTest04 {
  2. public static void main(String[] args) {
  3. }
  4. }
  5. //不能采用final修改抽象类
  6. //两个关键字是矛盾的
  7. final abstract class Person {
  8. private String name;
  9. public void setName(String name) {
  10. this.name = name;
  11. }
  12. public String getName() {
  13. return name;
  14. }
  15. //此方法各个子类都可以使用
  16. public void commonMethod1() {
  17. System.out.println("---------commonMethod1-------");
  18. }
  19. //采用abstract定义抽象方法
  20. public abstract void printInfo();
  21. }
  22. class Employee extends Person {
  23. //实现抽象的方法
  24. public void printInfo() {
  25. System.out.println("Student.printInfo()");
  26. }
  27. }
  28. class Student extends Person {
  29. //实现抽象的方法
  30. public void printInfo() {
  31. System.out.println("Student.printInfo()");
  32. }
  33. }

5、抽象方法不能被final修饰

  1. public class AbstractTest05 {
  2. public static void main(String[] args) {
  3. }
  4. }
  5. abstract class Person {
  6. private String name;
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public String getName() {
  11. return name;
  12. }
  13. //此方法各个子类都可以使用
  14. public void commonMethod1() {
  15. System.out.println("---------commonMethod1-------");
  16. }
  17. //不能采用final修饰抽象的方法
  18. //这两个关键字存在矛盾
  19. public final abstract void printInfo();
  20. }
  21. class Employee extends Person {
  22. //实现抽象的方法
  23. public void printInfo() {
  24. System.out.println("Student.printInfo()");
  25. }
  26. }
  27. class Student extends Person {
  28. //实现抽象的方法
  29. public void printInfo() {
  30. System.out.println("Student.printInfo()");
  31. }
  32. }

6、抽象类中可以没有抽象方法(参见1.16.1)

2、 接口(行为)

接口我们可以看作是抽象类的一种特殊情况,在接口中只能定义抽象的方法和常量
1) 在java中接口采用interface声明
2) 接口中的方法默认都是public abstract的,不能更改
3) 接口中的变量默认都是public static final类型的,不能更改,所以必须显示的初始化
4) 接口不能被实例化,接口中没有构造函数的概念
5) 接口之间可以继承,但接口之间不能实现
6) 接口中的方法只能通过类来实现,通过implements关键字
7) 如果一个类实现了接口,那么接口中所有的方法必须实现
8) 一类可以实现多个接口
9) 接口可以继承多个接口

1、接口中的方法默认都是public abstract的,不能更改

  1. public class InterfaceTest01 {
  2. public static void main(String[] args) {
  3. }
  4. }
  5. //采用interface定义接口
  6. //定义功能,没有实现
  7. //实现委托给类实现
  8. interface StudentManager {
  9. //正确,默认public abstract 等同public abstract void addStudent(int id, String name);
  10. public void addStudent(int id, String name);
  11. //正确
  12. //public abstract void addStudent(int id, String name);
  13. //正确,可以加入public修饰符,此种写法较多
  14. public void delStudent(int id);
  15. //正确,可以加入abstract,这种写法比较少
  16. public abstract void modifyStudent(int id, String name);
  17. //编译错误,因为接口就是让其他人实现
  18. //采用private就和接口原本的定义产生矛盾了
  19. private String findStudentById(int id);
  20. }

2、接口中的变量是public static final类型的,不能更改,所以必须显示的初始化

  1. public class InterfaceTest02 {
  2. public static void main(String[] args) {
  3. //不能修改,因为final的
  4. //StudentManager.YES = "abc";
  5. System.out.println(StudentManager.YES);
  6. }
  7. }
  8. interface StudentManager {
  9. //正确,默认加入public static final
  10. String YES = "yes";
  11. //正确, 开发中一般就按照下面的方式进行声明
  12. public static final String NO = "no";
  13. //错误,必须赋值,因为是final的
  14. //int ON;
  15. //错误,不能采用private声明
  16. private static final int OFF = -1;
  17. }

3、接口不能被实例化,接口中没有构造函数的概念

  1. public class InterfaceTest03 {
  2. public static void main(String[] args) {
  3. //接口是抽象类的一种特例,只能定义方法和变量,没有实现
  4. //所以不能实例化
  5. StudentManager studentManager = new StudentManager();
  6. }
  7. }
  8. interface StudentManager {
  9. public void addStudent(int id, String name);
  10. }

4、接口之间可以[多]继承,但接口之间不能实现

  1. public class InterfaceTest04 {
  2. public static void main(String[] args) {
  3. }
  4. }
  5. interface inter1 {
  6. public void method1();
  7. public void method2();
  8. }
  9. interface inter2 {
  10. public void method3();
  11. }
  12. //接口可以继承
  13. interface inter3 extends inter1 {
  14. public void method4();
  15. }
  16. //接口不能实现接口
  17. //接口只能被类实现
  18. interface inter4 implements inter2 {
  19. public void method3();
  20. }

5、如果一个类实现了接口,那么接口中所有的方法必须实现

  1. public class InterfaceTest05 {
  2. public static void main(String[] args) {
  3. //Iter1Impl实现了Inter1接口
  4. //所以它是Inter1类型的产品
  5. //所以可以赋值
  6. Inter1 iter1 = new Iter1Impl();
  7. iter1.method1();
  8. //Iter1Impl123实现了Inter1接口
  9. //所以它是Inter1类型的产品
  10. //所以可以赋值
  11. iter1 = new Iter1Impl123();
  12. iter1.method1();
  13. //可以直接采用Iter1Impl来声明类型
  14. //这种方式存在问题
  15. //不利于互换,因为面向具体编程了
  16. Iter1Impl iter1Impl = new Iter1Impl();
  17. iter1Impl.method1();
  18. //不能直接赋值给iter1Impl
  19. //因为Iter1Impl123不是Iter1Impl类型
  20. //iter1Impl = new Iter1Impl123();
  21. //iter1Impl.method1();
  22. }
  23. }
  24. //接口中的方法必须全部实现
  25. class Iter1Impl implements Inter1 {
  26. public void method1() {
  27. System.out.println("method1");
  28. }
  29. public void method2() {
  30. System.out.println("method2");
  31. }
  32. public void method3() {
  33. System.out.println("method3");
  34. }
  35. }
  36. class Iter1Impl123 implements Inter1 {
  37. public void method1() {
  38. System.out.println("method1_123");
  39. }
  40. public void method2() {
  41. System.out.println("method2_123");
  42. }
  43. public void method3() {
  44. System.out.println("method3_123");
  45. }
  46. }
  47. abstract class Iter1Impl456 implements Inter1 {
  48. public void method1() {
  49. System.out.println("method1_123");
  50. }
  51. public void method2() {
  52. System.out.println("method2_123");
  53. }
  54. //再次声明成抽象方法
  55. public abstract void method3();
  56. }
  57. //定义接口
  58. interface Inter1 {
  59. public void method1();
  60. public void method2();
  61. public void method3();
  62. }

6、一类可以实现多个接口

  1. public class InterfaceTest06 {
  2. public static void main(String[] args) {
  3. //可以采用Inter1定义
  4. Inter1 inter1 = new InterImpl();
  5. inter1.method1();
  6. //可以采用Inter1定义
  7. Inter2 inter2 = new InterImpl();
  8. inter2.method2();
  9. //可以采用Inter1定义
  10. Inter3 inter3 = new InterImpl();
  11. inter3.method3();
  12. }
  13. }
  14. //实现多个接口,采用逗号隔开
  15. //这样这个类就拥有了多种类型
  16. //等同于现实中的多继承
  17. //所以采用java中的接口可以实现多继承
  18. //把接口粒度划分细了,主要使功能定义的含义更明确
  19. //可以采用一个大的接口定义所有功能,替代多个小的接口,
  20. //但这样定义功能不明确,粒度太粗了
  21. class InterImpl implements Inter1, Inter2, Inter3 {
  22. public void method1() {
  23. System.out.println("----method1-------");
  24. }
  25. public void method2() {
  26. System.out.println("----method2-------");
  27. }
  28. public void method3() {
  29. System.out.println("----method3-------");
  30. }
  31. }
  32. interface Inter1 {
  33. public void method1();
  34. }
  35. interface Inter2 {
  36. public void method2();
  37. }
  38. interface Inter3 {
  39. public void method3();
  40. }
  41. /*
  42. interface Inter {
  43. public void method1();
  44. public void method2();
  45. public void method3();
  46. }
  47. */

7、一个接口可以继承多个几口

  1. //声明接口:interface
  2. interface A{
  3. public static final int CONST = 1;
  4. void a();//接口中的方法默认都是public abstract的
  5. }
  6. interface K{
  7. void k();
  8. }
  9. //接口可以继承多个接口
  10. interface B extends A,K{
  11. void b();
  12. int d();
  13. }

3、 接口的进一步应用

在java中接口其实描述了类需要做的事情,类要遵循接口的定义来做事,使用接口到底有什么本质的好处?可以归纳为两点:(提供了标准、规范)

  • 采用接口明确的声明了它所能提供的服务
  • 解决了Java单继承的问题(一个类可以实现多个接口,一个接口可以继承多个接口)
  • 实现了可接插性(重要)

示例:完成学生信息的增删改操作,系统要求适用于多个数据库,如:适用于Oracle和MySQL;
第一种方案,不使用接口,每个数据库实现一个类:
1647158855(1).png

  1. //Oracle的实现
  2. public class StudentOracleImpl {
  3. public void add(int id, String name) {
  4. System.out.println("StudentOracleImpl.add()");
  5. }
  6. public void del(int id) {
  7. System.out.println("StudentOracleImpl.del()");
  8. }
  9. public void modify(int id, String name) {
  10. System.out.println("StudentOracleImpl.modify()");
  11. }
  12. }

需求发生变化了,客户需要将数据移植Mysql上

  1. ////Mysql的实现
  2. public class StudentMysqlImpl {
  3. public void addStudent(int id, String name) {
  4. System.out.println("StudentMysqlImpl.addStudent()");
  5. }
  6. public void deleteStudent(int id) {
  7. System.out.println("StudentMysqlImpl.deleteStudent()");
  8. }
  9. public void udpateStudent(int id, String name) {
  10. System.out.println("StudentMysqlImpl.udpateStudent()");
  11. }
  12. }

调用以上两个类完成向Oracle数据库和Mysql数据存储数据

  1. public class StudentManager {
  2. public static void main(String[] args) {
  3. //对Oracle数据库的支持
  4. /*
  5. StudentOracleImpl studentOracleImpl = new StudentOracleImpl();
  6. studentOracleImpl.add(1, "张三");
  7. studentOracleImpl.del(1);
  8. studentOracleImpl.modify(1, "张三");
  9. */
  10. //需要支持Mysql数据库
  11. StudentMysqlImpl studentMysqlImpl = new StudentMysqlImpl();
  12. studentMysqlImpl.addStudent(1, "张三");
  13. studentMysqlImpl.deleteStudent(1);
  14. studentMysqlImpl.udpateStudent(1, "张三");
  15. }
  16. }

以上代码不能灵活的适应需求,当需求发生改变需要改动的代码量太大,这样可能会导致代码的冗余,另外可能会导致项目的失败,为什么会导致这个问题,在开发中没有考虑到程序的扩展性,就是一味的实现,这样做程序是不行的,所以大的项目比较追求程序扩展性,有了扩展性才可以更好的适应需求。

第二种方案,使用接口
UML,统一建模语言
1647158957(1).png

  1. public class Student4OracleImpl implements IStudent {
  2. public void add(int id, String name) {
  3. System.out.println("Student4OracleImpl.add()");
  4. }
  5. public void del(int id) {
  6. System.out.println("Student4OracleImpl.del()");
  7. }
  8. public void modify(int id, String name) {
  9. System.out.println("Student4OracleImpl.modify()");
  10. }
  11. }
  12. public class Student4MysqlImpl implements IStudent {
  13. public void add(int id, String name) {
  14. System.out.println("Student4MysqlImpl.add()");
  15. }
  16. public void del(int id) {
  17. System.out.println("Student4MysqlImpl.del()");
  18. }
  19. public void modify(int id, String name) {
  20. System.out.println("Student4MysqlImpl.modify()");
  21. }
  22. }
  23. public class StudentService {
  24. public static void main(String[] args) {
  25. /*
  26. IStudent istudent = new Student4OracleImpl();
  27. IStudent istudent = new Student4MysqlImpl();
  28. istudent.add(1, "张三");
  29. istudent.del(1);
  30. istudent.modify(1, "张三");
  31. */
  32. //IStudent istudent = new Student4OracleImpl();
  33. //IStudent istudent = new Student4MysqlImpl();
  34. //doCrud(istudent);
  35. //doCrud(new Student4OracleImpl());
  36. //doCrud(new Student4MysqlImpl());
  37. //doCrud(new Student4OracleImpl());
  38. doCrud(new Student4MysqlImpl());
  39. }
  40. //此种写法没有依赖具体的实现
  41. //而只依赖的抽象,就像你的手机电池一样:你的手机只依赖电池(电池是一个抽象的事物),
  42. //而不依赖某个厂家的电池(某个厂家的电池就是具体的事物了)
  43. //因为你依赖了抽象的事物,每个抽象的事物都有不同的实现
  44. //这样你就可以利用多态的机制完成动态绑定,进行互换,是程序具有较高的灵活
  45. //我们尽量遵循面向接口(抽象)编程,而不要面向实现编程
  46. public static void doCrud(IStudent istudent) {
  47. istudent.add(1, "张三");
  48. istudent.del(1);
  49. istudent.modify(1, "张三");
  50. }
  51. //以下写法不具有扩展性
  52. //因为它依赖了具体的实现
  53. //建议不要采用此种方法,此种方案是面向实现编程,就依赖于具体的东西了
  54. /*
  55. public static void doCrud(Student4OracleImpl istudent) {
  56. istudent.add(1, "张三");
  57. istudent.del(1);
  58. istudent.modify(1, "张三");
  59. }
  60. */
  61. }