面向对象(OOP) - 图2

Object Oriented Programming(OOP)
_
统一建模语言(Unified Modeling Language,UML)是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言,是非专利的第三代建模和规约语言。UML使用面向对象设计的的建模工具,但独立于任何具体程序设计语言

类图

类图是描述系统中的类,以及各个类之间的关系的静态视图。能够让我们在正确编写代码以前对系统有一个全面的认识。类图是一种模型类型,确切地说,是一种静态模型类型。类图表示类、接口和它们之间的协作关系。
面向对象(OOP) - 图3

类与类的关系

  • 关联
  • 依赖
  • 聚集
  • 泛化
  • 实现

    Java 修饰符

    Java语言提供了很多修饰符,主要分为以下两类:

  • 访问修饰符

  • 非访问修饰符

修饰符用来定义类、方法或者变量,通常放在语句的最前端。

访问控制修饰符

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

我们可以通过以下表来说明访问权限:
访问控制

修饰符 当前类 同一包内 子孙类(同一包) 子孙类(不同包) 其他包
public Y Y Y Y Y
protected Y Y Y Y/N(说明 N
default Y Y Y N N
private Y N N N N

访问控制和继承

请注意以下方法继承的规则:

  • 父类中声明为 public 的方法在子类中也必须为 public。
  • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
  • 父类中声明为 private 的方法,不能够被继承。

    非访问修饰符

    为了实现一些其他的功能,Java 也提供了许多非访问修饰符。

  • static 修饰符,用来修饰类方法和类变量。

  • final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
  • abstract 修饰符,用来创建抽象类和抽象方法。
  • synchronized 和 volatile 修饰符,主要用于线程的编程。

    static 修饰符

  • 静态变量:
    static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。

  • 静态方法:
    static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。

对类变量和方法的访问可以直接使用 classname.variablenameclassname.methodname 的方式访问。

final 修饰符

final 变量:
final 表示”最后的、最终的”含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
final 修饰符通常和 static 修饰符一起使用来创建类常量。
final 方法:
父类中的 final 方法可以被子类继承,但是不能被子类重写。
声明 final 方法的主要目的是防止该方法的内容被修改。
final 类
final 类不能被继承,没有类能够继承 final 类的任何特性。

abstract 修饰符

抽象类:
抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
一个类不能同时被 abstract 和 final 修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
抽象类可以包含抽象方法和非抽象方法。
抽象方法
抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
抽象方法不能被声明成 final 和 static。
任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,例如:public abstract sample();

synchronized 修饰符

synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符。

transient 修饰符

序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。

  1. public transient int limit = 55; // 不会持久化
  2. public int b; // 持久化

volatile 修饰符

volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是 null。

  1. public class MyRunnable implements Runnable
  2. {
  3. private volatile boolean active;
  4. public void run()
  5. {
  6. active = true;
  7. while (active) // 第一行
  8. {
  9. // 代码
  10. }
  11. }
  12. public void stop()
  13. {
  14. active = false; // 第二行
  15. }
  16. }

通常情况下,在一个线程调用 run() 方法(在 Runnable 开启的线程),在另一个线程调用 stop() 方法。 如果 第一行 中缓冲区的 active 值被使用,那么在 第二行 的 active 值为 false 时循环不会停止。
但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止。

继承 extends

继承概念

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。(创建分等级的类)

  1. 如果类 B 从类 A 派生,或者说类 B 扩展自类 A,或者说类 B 继承类 A,则称类 A 为”父类”,也称为超类、基类;
  2. 称类 B 为”子类”,也称为次类、扩展类、派生类。 ```java class 父类 { }

class 子类 extends 父类 { }

  1. <a name="WCDQq"></a>
  2. ## 为什么使用继承
  3. - 从已有的类派生出新的类,称为继承。
  4. - 在不同的类中也可能会有共同的特征和动作,可以把这些共同的特征和动作放在一个类中,让其它类共享。
  5. - 因此可以定义一个通用类,然后将其扩展为其它多个特定类,这些特定类继承通用类中的特征和动作。
  6. - 继承是 Java 中实现软件重用的重要手段,避免重复,易于维护,易于理解。
  7. - 运用继承,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码)
  8. <a name="4Oq5z"></a>
  9. ## 继承类型
  10. ![](https://cdn.nlark.com/yuque/0/2020/png/316618/1583750647590-bf4e1d18-8443-4e14-9a88-e7411b83138a.png#align=left&display=inline&height=600&margin=%5Bobject%20Object%5D&originHeight=600&originWidth=800&size=0&status=done&style=none&width=800)
  11. <a name="JhHcf"></a>
  12. ## 继承的特性
  13. - 子类拥有父类非 private 的属性、方法。<br />
  14. - 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。<br />
  15. - 子类可以用自己的方式实现父类的方法。<br />
  16. - Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。<br />
  17. - 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。<br />
  18. <a name="TPl5O"></a>
  19. ## 继承关键字
  20. 继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 **java.lang** 包中,所以不需要 **import**)祖先类。
  21. <a name="jWvAf"></a>
  22. ### extends关键字
  23. 在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
  24. <a name="zTdEU"></a>
  25. ### implements关键字
  26. 使用 implements 关键字可以**变相**的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
  27. ```java
  28. public interface A {
  29. public void eat();
  30. public void sleep();
  31. }
  32. public interface B {
  33. public void show();
  34. }
  35. public class C implements A,B {
  36. }

super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。(在构造函数里的super必须写在第一行)
this关键字:指向自己的引用。采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。

构造器

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

Java 转型问题

Java 转型问题其实并不复杂,只要记住一句话:父类引用指向子类对象。
什么叫父类引用指向子类对象,且听我慢慢道来。
从 2 个名词开始说起:向上转型(upcasting)向下转型(downcasting)
举个例子:有2个类,Father 是父类,Son 类继承自 Father。

  1. Father f1 = new Son(); // 这就叫 upcasting (向上转型)
  2. // 现在 f1 引用指向一个Son对象
  3. Son s1 = (Son)f1; // 这就叫 downcasting (向下转型)
  4. // 现在f1 还是指向 Son对象
  1. Father f2 = new Father();
  2. Son s2 = (Son)f2; // 出错,子类引用不能指向父类对象

总结:
1、父类引用指向子类对象,而子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转换吗,如:
Father f1 = new Son();
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转换,如:
f1 就是一个指向子类对象的父类引用。把f1赋给子类引用 s1 即 Son s1 = (Son)f1;
其中 f1 前面的(Son)必须加上,进行强制转换。

Java 重写(Override)与重载(Overload)

重写(Override)

  • 重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法。
  • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
  • 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。例如: 父类的一个方法申明了一个检查异常 IOException,但是在重写这个方法的时候不能抛出 Exception 异常,因为 Exception 是 IOException 的父类,只能抛出 IOException 的子类异常。

    方法的重写规则

  • 参数列表必须完全与被重写方法的相同。

  • 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  • 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  • 父类的成员方法只能被它的子类重写。
  • 声明为 final 的方法不能被重写。
  • 声明为 static 的方法不能被重写,但是能够被再次声明。
  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  • 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  • 构造方法不能被重写。
  • 如果不能继承一个方法,则不能重写这个方法。

    重载(Overload)

    重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理
    重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
    每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
    最常用的地方就是构造器的重载。
    重载规则:

  • 被重载的方法必须改变参数列表(参数个数或类型不一样);

  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。
  • 无法以返回值类型作为重载函数的区分标准。

    重写与重载之间的区别

    | 区别点 | 重载方法 | 重写方法 | | :—- | :—- | :—- | | 参数列表 | 必须修改 | 一定不能修改 | | 返回类型 | 可以修改 | 一定不能修改 | | 异常 | 可以修改 | 可以减少或删除,一定不能抛出新的或者更广的异常 | | 访问 | 可以修改 | 一定不能做更严格的限制(可以降低限制) |

面向对象(OOP) - 图4

Java 封装


在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。

封装的优点

    1. 良好的封装能够减少耦合。
    1. 类内部的结构可以自由修改。
    1. 可以对成员变量进行更精确的控制。
    1. 隐藏信息,实现细节。

      实现Java封装的步骤

      private 属性+getter和setter方法。
      通过getter和setter方法去访问类中私有成员变量
      1. public class User{
      2. //定义了一个关于名称的实例变量
      3. private String name;
      4. /**
      5. * 获取 name 的值
      6. */
      7. public String getName(){
      8. return this.name;
      9. }
      10. /**
      11. * 设置 name 的值
      12. */
      13. public void setName(String name){
      14. this.name = name;
      15. }
      16. }

      接口

      接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
      接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
      除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
      接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
      接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似。

      接口与类相似点:

  • 一个接口可以有多个方法。

  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  • 接口的字节码文件保存在 .class 结尾的文件中。
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

    接口与类的区别:

  • 接口不能用于实例化对象。

  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

    接口特性

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。

  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。(java 1.8版本以后接口中可以有方法体)
  • 接口可以多继承
  • 接口的方法声明必须是 public abstract 即便不写默认也是
  • 接口里面不能包含方法具体实现
  • 类实继承接口必须实现接口里申明的全部方法,除非该类是抽象类
  • 类里面可以声明 public static final 修饰的变量
  • 接口不能被实例化,但是可以被实现类创建

    抽象类和接口的区别

    1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    1. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    1. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    1. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

      :JDK 1.8 以后,接口里可以有静态方法和方法体了。

1、语法层面上的区别

  • 1)抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
  • 2)抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
  • 3)接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
  • 4)一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    2、设计层面上的区别

    1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。举个简单的例子,飞机和鸟是不同类的事物,但是它们都有一个共性,就是都会飞。那么在设计的时候,可以将飞机设计为一个类 Airplane,将鸟设计为一个类 Bird,但是不能将 飞行 这个特性也设计为类,因此它只是一个行为特性,并不是对一类事物的抽象描述。此时可以将 飞行 设计为一个接口Fly,包含方法fly( ),然后Airplane和Bird分别根据自己的需要实现Fly这个接口。然后至于有不同种类的飞机,比如战斗机、民用飞机等直接继承Airplane即可,对于鸟也是类似的,不同种类的鸟直接继承Bird类即可。从这里可以看出,继承是一个 “是不是”的关系,而 接口 实现则是 “有没有”的关系。如果一个类继承了某个抽象类,则子类必定是抽象类的种类,而接口实现则是有没有、具备不具备的关系,比如鸟是否能飞(或者是否具备飞行这个特点),能飞行则可以实现这个接口,不能飞行就不实现这个接口。
    2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。什么是模板式设计?最简单例子,大家都用过 ppt 里面的模板,如果用模板 A 设计了 ppt B 和 ppt C,ppt B 和 ppt C 公共的部分就是模板 A 了,如果它们的公共部分需要改动,则只需要改动模板 A 就可以了,不需要重新对 ppt B 和 ppt C 进行改动。而辐射式设计,比如某个电梯都装了某种报警器,一旦要更新报警器,就必须全部更新。也就是说对于抽象类,如果需要添加新的方法,可以直接在抽象类中添加具体的实现,子类可以不进行变更;而对于接口则不行,如果接口进行了变更,则所有实现这个接口的类都必须进行相应的改动。
    下面看一个网上流传最广泛的例子:门和警报的例子:门都有 open()close() 两个动作,此时我们可以定义通过抽象类和接口来定义这个抽象概念:

///////

  1. abstract class Door {
  2. public abstract void open();
  3. public abstract void close();
  4. }
  5. 或者:
  6. interface Door {
  7. public abstract void open();
  8. public abstract void close();
  9. }

但是现在如果我们需要门具有报警 的功能,那么该如何实现?下面提供两种思路:
1)将这三个功能都放在抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了报警功能,但是有的门并不一定具备报警功能;
2)将这三个功能都放在接口里面,需要用到报警功能的类就需要实现这个接口中的 open( ) 和 close( ),也许这个类根本就不具备 open( ) 和 close( ) 这两个功能,比如火灾报警器。
从这里可以看出, Door 的 open() 、close() 和 alarm() 根本就属于两个不同范畴内的行为,open() 和 close() 属于门本身固有的行为特性,而 alarm() 属于延伸的附加行为。因此最好的解决办法是单独将报警设计为一个接口,包含 alarm() 行为,Door 设计为单独的一个抽象类,包含 open 和 close 两种行为。再设计一个报警门继承 Door 类和实现 Alarm 接口。

/

  1. interface Alram {
  2. void alarm();
  3. }
  4. abstract class Door {
  5. void open();
  6. void close();
  7. }
  8. class AlarmDoor extends Door implements Alarm {
  9. void oepn() {
  10. //....
  11. }
  12. void close() {
  13. //....
  14. }
  15. void alarm() {
  16. //....
  17. }
  18. }

更多内容查看:深入理解 Java 的接口和抽象类

default关键字介绍

default是在java8中引入的关键字,也可称为Virtualextension methods——虚拟扩展方法。是指,在接口内部包含了一些默认的方法实现(也就是接口中可以包含方法体,这打破了Java之前版本对接口的语法限制),从而使得接口在进行扩展的时候,不会破坏与接口相关的实现类代码。

  • 实现类会继承接口中的default方法
  • 如果一个类同时实现接口A和B,接口A和B中有相同的default方法,这时,该类必须重写接口中的default方法
  • 如果子类继承父类,父类中有b方法,该子类同时实现的接口中也有b方法(被default修饰),那么子类会继承父类的b方法而不是继承接口中的b方法

    接口声明

  1. //语法格式
  2. [可见度] interface 接口名称 [extends 其他的接口名] {
  3. // 声明变量
  4. // 抽象方法
  5. }
  6. //example
  7. /* 文件名 : NameOfInterface.java */
  8. import java.lang.*;
  9. //引入包
  10. public interface NameOfInterface
  11. {
  12. //任何类型 final, static 字段
  13. //抽象方法
  14. }

接口有以下特性:

  • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
  • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
  • 接口中的方法都是公有的。

    接口的实现

    当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
    类使用implements关键字实现接口。在类声明中,Implements关键字放在class声明后面。
    实现一个接口的语法,可以使用这个公式:
  1. ...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...

重写接口中声明的方法时,需要注意以下规则:

  • 类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
  • 类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
  • 如果实现接口的类是抽象类,那么就没必要实现该接口的方法。

在实现接口的时候,也要注意一些规则:

  • 一个类可以同时实现多个接口。
  • 一个类只能继承一个类,但是能实现多个接口。
  • 一个接口能继承另一个接口,这和类之间的继承比较相似。

    接口的继承

    一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
    下面的Sports接口被Hockey和Football接口继承:
  1. // 文件名: Sports.java
  2. public interface Sports
  3. {
  4. public void setHomeTeam(String name);
  5. public void setVisitingTeam(String name);
  6. }
  7. // 文件名: Football.java
  8. public interface Football extends Sports
  9. {
  10. public void homeTeamScored(int points);
  11. public void visitingTeamScored(int points);
  12. public void endOfQuarter(int quarter);
  13. }
  14. // 文件名: Hockey.java
  15. public interface Hockey extends Sports
  16. {
  17. public void homeGoalScored();
  18. public void visitingGoalScored();
  19. public void endOfPeriod(int period);
  20. public void overtimePeriod(int ot);
  21. }

Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。
相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。

最常见的两个接口

  • Map
  • List

(都是具体的数据结构)

Map(映射)

面向对象(OOP) - 图5

  1. import java.util.Map;
  2. import java.util.HashMap;
  3. // key value 得是 Java 类型
  4. Map<key,value> map = new HashMap<>();//key,value的类型可以是任何的Java对象
  1. import java.util.Map;
  2. import java.util.HashMap;
  3. /**
  4. * MapTest
  5. */
  6. public class MapTest {
  7. public static void main(String[] args) {
  8. Map<String, String> map = new HashMap<String, String>();
  9. map.put("1", "value1");
  10. map.put("2", "value2");
  11. map.put("3", "value3");
  12. // 获取"3"对应的值
  13. String value = map.get("3");
  14. System.out.println(value);
  15. int size = map.size();
  16. System.out.println(size);
  17. //第一种:普遍使用,二次取值
  18. System.out.println("通过Map.keySet遍历key和value:");
  19. for (String key : map.keySet()) {
  20. System.out.println("key= "+ key + " and value= " + map.get(key));
  21. }
  22. //第二种
  23. System.out.println("通过Map.entrySet使用iterator遍历key和value:");
  24. Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
  25. while (it.hasNext()) {
  26. Map.Entry<String, String> entry = it.next();
  27. System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  28. }
  29. //第三种:推荐,尤其是容量大时
  30. System.out.println("通过Map.entrySet遍历key和value");
  31. for (Map.Entry<String, String> entry : map.entrySet()) {
  32. System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
  33. }
  34. //第四种
  35. System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
  36. for (String v : map.values()) {
  37. System.out.println("value= " + v);
  38. }
  39. }
  40. }
  • 如果遍历 hashMap() 时 entrySet() 方法是将 key 和 value 全部取出来,所以性能开销是可以预计的, 而 keySet() 方法进行遍历的时候是根据取出的 key 值去查询对应的 value 值, 所以如果 key 值是比较简单的结构(如 1,2,3…)的话性能消耗上是比 entrySet() 方法低, 但随着 key 值得复杂度提高 entrySet() 的优势就会显露出来。

  • 综合比较在只遍历 key 的时候使用 keySet(), 在只遍历 value 的是使用 values() 方法, 在遍历 key-value 的时候使用 entrySet() 是比较合理的选择。

  • 如果遍历 TreeMap 的时候, 不同于 HashMap 在遍历 ThreeMap 的 key-value 时候务必使用 entrySet() 它要远远高于其他两个的性能, 同样只遍历 key 的时候使用 keySet(), 在只遍历 value 的是使用 values() 方法对于 TreeMap 也同样适用。

    List

    List是ArrayList 的接口,用法和ArrayList没有太大区别 ```java import java.util.*;

public class Test{ public static void main(String[] args) { List list=new ArrayList(); list.add(“Hello”); list.add(“World”); list.add(“HAHAHAHA”); //第一种遍历方法使用 For-Each 遍历 List for (String str : list) { //也可以改写 for(int i=0;i<list.size();i++) 这种形式 System.out.println(str); }

  1. //第二种遍历,把链表变为数组相关的内容进行遍历
  2. String[] strArray=new String[list.size()];
  3. list.toArray(strArray);
  4. for(int i=0;i<strArray.length;i++) //这里也可以改写为 for(String str:strArray) 这种形式
  5. {
  6. System.out.println(strArray[i]);
  7. }
  8. //第三种遍历 使用迭代器进行相关遍历
  9. Iterator<String> ite=list.iterator();
  10. while(ite.hasNext())//判断下一个元素之后有值
  11. {
  12. System.out.println(ite.next());
  13. }

} } ``` 三种方法都是用来遍历ArrayList集合,第三种方法是采用迭代器的方法,该方法可以不用担心在遍历的过程中会超出集合的长度。