Contents
  1. 1. 抽象类
    1. 1.1. 多态
  2. 2. 接口
    1. 2.1. 匿名内部类与Lambda表达式
  3. 3. 泛型
    1. 3.1. 泛型类
    2. 3.2. 泛型接口
    3. 3.3. 泛型方法
    4. 3.4. 常用的数据结构
      1. 3.4.1. 集合(Collection)
        1. 3.4.1.1. 列表(List)
        2. 3.4.1.2. 集合(Set)
        3. 3.4.1.3. 队列(Queue)
        4. 3.4.1.4. 栈(Stack)
      2. 3.4.2. 映射(Map)
    5. 3.5. 一个例子
  • 参考内容
  • 目标读者:

    了解 Java 基本数据类型,权限修饰符,继承,封装,静态。
    掌握 Java 输出、for、if 等基本语法。
    熟练应用函数,了解函数重载。

    抽象类

    • 关键字 abstract

      1
      2
      3
      4
      // 用abstract关键字修饰的类就是抽象类。
      public abstract class People {
      //...
      }
    • 抽象类和普通类的区别:

      1. 抽象类不能被实例化

      2. 抽象类的子类必须实现它的抽象方法,除非这个子类也是抽象类。

      3. 抽象类可以拥有抽象方法。普通的类不能拥有抽象方法。

        抽象方法:

        abstract关键字修饰的方法就是抽象方法。

        抽象方法需要被子类实现,即重写(Override)。如果子类也是抽象类,那么可以不重写抽象方法,或者重写其中几个。

        1
        2
        3
        4
        5
        6
        public abstract class People {
        // 将会说出的一段话。e.g 我是杨晨
        protected abstract String words();
        // 做出一个动作。e.g 露出开心的笑容
        protected abstract String action();
        }

    这是一个例子:

    • 需求:

    ​ 我们需要定义老师和学生。他们都是,都拥有年龄和名字。每个人都可以做一件事:先做一个动作,然后再说一句话。每个人的动作可以不同,说的话也可以不同

    老师需要教一门课程,在老师说话时需要说出自己教的课程名称。

    ​ 对学生没有特殊要求。

    • 定义抽象类People

      ​ 根据需求,可以将老师和同学共同的部分抽象出来,组成抽象类。这样功能相同的代码只需要写一次,老师和学生只要继承People即可。

      ​ 使用抽象类时,如果需求发生变化,我们想去掉年龄,增加性别、学历、户籍等信息,只需要修改People一个类即可。如果不使用继承需要修改StudentTeacher两个类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package classroom;

    public abstract class People {
    protected int age;
    protected String name;
    protected String words;
    protected String express;

    public People(int age, String name, String words, String express) {
    this.age = age;
    this.name = name;
    this.words = words;
    this.express = express;
    }

    // 每个人可以做些什么
    public final void doSomething() {
    String express = action();
    String words = words();
    int len = Math.max(express.length(), words.length()) * 2;
    StringBuilder sb = new StringBuilder();
    String line;
    for (int i = 0; i < len / 2; i++) {
    sb.append("=");
    }
    sb.append(name).append("的分割线");
    for (int i = 0; i < len / 2; i++) {
    sb.append("=");
    }

    line = sb.toString();
    System.out.println(line);
    System.out.println(express);
    System.out.print("然后说:\"");
    System.out.print(words);
    System.out.println("。\"");
    System.out.println(line);
    }

    // 将会说出的一段话。e.g 我是杨晨
    protected abstract String words();

    // 做出一个动作。e.g 露出开心的笑容
    protected abstract String action();

    }

    • Teacher类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package classroom;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.BiFunction;

    public class Teacher extends People {
    protected String subject;
    private final Map<String, Integer> answer2count = new HashMap<>();

    public Teacher(int age, String name, String words, String expression) {
    this(age, name, words, expression, "高数");
    }

    public Teacher(int age, String name, String words, String expression, String subject) {
    super(age, name, words, expression);
    this.subject = subject;
    }

    @Override
    protected String words() {
    return words + "。今天我给大家讲一讲《" +
    subject +
    "》";
    }

    @Override
    protected String action() {
    return name + "老师" +
    express + "。";
    }

    }

    • Student类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package classroom;

    public class Student extends People {

    public Student(int age, String name, String words, String expression) {
    super(age, name, words, expression);
    }

    @Override
    protected String words() {
    return words;
    }

    @Override
    protected String action() {
    return name + "同学" +
    express + "。";
    }

    }

    • Main 主程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import java.util.ArrayList;
    import java.util.List;

    public class Main {
    public static void main(String[] args) {
    People mbg = new Teacher(69, "马保国",
    "现在的年轻人不讲武德", "捂着红肿的眼睛",
    "武德");
    People yc = new Teacher(19, "杨晨",
    "我女妆真好看", "露出开心的笑容",
    "女妆的一百零八种技巧");
    People zk = new Student(21, "郑开",
    "杨晨学姐太强力", "拍了拍杨晨");
    List<People> peoples = new ArrayList<>();
    peoples.add(mbg);
    peoples.add(yc);
    peoples.add(zk);
    for (People people : peoples) {
    people.doSomething();
    }
    }
    }

    • 输出结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    =====================马保国的分割线=====================
    马保国老师捂着红肿的眼睛。
    然后说:"年轻人不讲武德。今天我给大家讲一讲《武德》。"
    =====================马保国的分割线=====================
    ============================杨晨的分割线============================
    杨晨老师露出开心的笑容。
    然后说:"我女妆真好看。今天我给大家讲一讲《女妆的一百零八种技巧》。"
    ============================杨晨的分割线============================
    ==========郑开的分割线==========
    郑开同学拍了拍杨晨。
    然后说:"杨晨学姐太强力。"
    ==========郑开的分割线==========

    Process finished with exit code 0

    多态

    多态存在的三个必要条件

    • 继承
    • 重写
    • 父类引用指向子类对象

    多态的优点

    1. 消除类型之间的耦合关系

    2. 可替换性

    3. 可扩充性

    4. 接口性

    5. 灵活性

    6. 简化性

    多态是同一个行为具有多个不同表现形式或形态的能力。如:老师和学生都可以doSomething,但是他们do的方式不一样。虽然mbgyczk都是People类型的变量,都调用了同一个方法,但是却有不同的表现形式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Main {
    public static void main(String[] args) {
    People mbg = new Teacher(69, "马保国",
    "现在的年轻人不讲武德", "捂着红肿的眼睛",
    "武德");
    People yc = new Teacher(19, "杨晨",
    "我女妆真好看", "露出开心的笑容",
    "女妆的一百零八种技巧");
    People zk = new Student(21, "郑开",
    "杨晨学姐太强了", "拍了拍杨晨");
    List<People> peoples = new ArrayList<>();
    peoples.add(mbg);
    peoples.add(yc);
    peoples.add(zk);
    for (People people : peoples) {
    // 尽管有的人是老师,有的人是同学,但我们并不在乎。
    // 因为多态,所以我们可以使用相同的方式,来调用不同的函数。
    people.doSomething();
    }
    }
    }

    接口

    ​ 关键字interfaceimplements

    ​ 众所周知,Java的类只能单继承,无论继承的是普通类还是抽象类。但是在庞大的程序中,多继承是不可避免的。为了实现这个功能,Java中引入了接口(Interface)。

    那么问题来了,既然还是要有多继承,为什么不直接设计成可以继承多个类呢?

    在C++里,类是可以多继承的,但正是因为它的多继承,让程序员们遇到了很多问题。如菱形继承问题:B继承了A,C继承了A,而D同时继承了B和C,这时D中会出现两个同名的父类A,在使用A时会带来一些问题。

    一篇解释这个问题的博客

    • 接口中可以定义函数和变量

      1. 接口默认函数是public abstract的。而且接口中的函数只能是public的。
      1
      2
      3
      4
      public interface Answerable {
      //去掉这里的public abstract 也是一样的效果
      public abstract void answerQuestion(String answer);
      }
      1. 接口中默认的变量是public static final的。
      1
      2
      3
      4
      public interface QAndA extends Answerable,CanAsk {
      //去掉这里的public static final 也是一样的效果
      public static final String test = "这是一个test";
      }
    • 接口可以被类用实现,一个类可以实现多个接口。implments

      1
      2
      3
      4
      //Answerable.java
      public interface Answerable {
      public abstract void answerQuestion(String answer);
      }
      1
      2
      3
      4
      //CanAsk.java
      public interface CanAsk {
      void askQuestion(Answerable answerable, String question);
      }
      1
      2
      3
      4
      5
      6
      //People.java
      public abstract class People implements CanAsk, Answerable {
      //...
      //因为是抽象类,所以即使不实现接口中定义的函数也可以。

      }
    • 接口可以继承多个接口。extends

      1
      2
      3
      4
      public interface QAndA extends Answerable,CanAsk {
      //QAndA接口中有在Answerable定义的抽象方法 answerQuestion和CanAsk中定义的抽象方法askQuetion
      // 可以定义新的方法
      }

    有了接口也就解决了之前说的多继承的问题。即使不同的接口有同名的函数,因为都没有实现,所以也就不会有菱形继承的问题。

    • 接口与抽象类:

      1. staticdefault

        在jdk1.8之后,增加了这两个特性。在接口中可以使用staticdefault来修饰方法,当使用这两个关键字来修饰函数时,函数就不能被abstract修饰了。

        接口中可以定义静态函数,使用方式和类没有区别。需要在接口中定义函数实现。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        public interface InterfaceA {
        /**
        * 静态方法
        */
        static void showStatic() {
        System.out.println("InterfaceA++showStatic");
        }
        }

        1
        2
        3
        4
        5
        public class Test {
        public static void main(String[] args) {
        InterfaceA.show();
        }
        }
        1
        2
        # 结果
        InterfaceA++showStatic

        在类中,default是默认的访问修饰符,不需要写出。但是同时default也是Java的关键字,在接口中使用它修饰方法,表示该方法需要一个默认的实现。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        public interface InterfaceA {

        /**
        * 静态方法
        */
        static void showStatic() {
        System.out.println("InterfaceA++showStatic");
        }

        /**
        * 默认方法
        */
        default void showDefault() {
        System.out.println("InterfaceA ++showDefault");
        }

        }

        public class InterfaceAImpl implements InterfaceA{


        }
        1
        2
        3
        4
        5
        6
        7
        public class Test {
        public static void main(String[] args) {
        InterfaceA.showStatic();
        new InterfaceAImpl().showDefault();
        }
        }

        1
        2
        3
        # 结果
        InterfaceA++showStatic
        InterfaceA ++showDefault

        子类可以重写接口的default函数

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        public class InterfaceAImpl implements InterfaceA
        /**
        *
        * 跟接口default方法一致,但不能再加default修饰符
        */
        @Override
        public void showDefault(){
        System.out.println("InterfaceAImpl++ defaultShow");
        }
        }

        既然在jdk1.8之后,接口可以有默认的函数实现了,那之前的菱形继承的问题,不就又出现了吗。在Java中提供了解决方案,只要出现了冲突,那么子类就必须要重写这个函数。

      2. 他们都不可以被实例化

      3. 一个类可以实现多个接口,但只能继承一个类。

    匿名内部类与Lambda表达式

    • 匿名内部类

      在类的内部,我们依旧可以定义类。这样的类叫做内部类。其中有一种特殊的内部类叫做匿名内部类

    1
    2
    3
    4
    5
    6
    7
    8
    //定义一个接口
    package anoymous;

    public interface Animal {
    void speak();
    void eat();

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package anoymous;

    public class AnonymousMain {
    //内部类
    static class AnimalImpl implements Animal{
    @Override
    public void speak() {

    }
    @Override
    public void eat() {

    }
    }
    public static void main(String[] args) {
    Animal animalImpl = new AnimalImpl();
    // 匿名内部类,匿名内部类实际上也创建了一个新的类,我们需要实现接口中的抽象函数。但是匿名内部类只能实例化这一次。
    // 除了接口,抽象类也可以通过匿名内部类的方式创建实例。
    Animal anonymousAnimal = new Animal() {
    @Override
    public void speak() {

    }

    @Override
    public void eat() {

    }
    };
    }
    }
    • Lambda表达式

      当接口只有一个函数时,可以用Lambda表达式替换匿名内部类。

      1
      2
      3
      4
      5
      package anoymous;

      public interface SingleFunctionInterface {
      void function(int a,int b);
      }
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      package anoymous;

      public class AnonymousMain {
      public static void main(String[] args) {
      // 匿名内部类
      SingleFunctionInterface singleFunctionInterface =
      new SingleFunctionInterface() {
      @Override
      public int function(int a, int b) {
      System.out.println(a + b);
      return a + b;
      }
      };
      // Lambda表达式
      // ()括号中的参数对应接口中函数的参数列表。
      // ->剪头后{}花括号内的内容为实现的方法体
      SingleFunctionInterface singleFunctionInterface2 =
      (a, b) -> {
      System.out.println(a + b);
      return a + b;
      };
      }
      }

    泛型

    Java泛型(generics)是JDK 5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许程序员在编译时监测非法的类型(编译后与泛型相关的信息会被擦掉,即类型擦除)。使用泛型机制编写的程序代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性。泛型对于集合类尤其有用,例如,ArrayList就是一个无处不在的集合类。

    泛型的本质是参数化类型,也就是所操作的数据类型被指定为一个参数。

    我们为什么需要泛型?

    假如我们需要一个类在使用上和数组的使用几乎无异,但是它可以自动扩展长度.

    当添加的元素数量超过数组的长度时,这个类会新建一个长度为原本长度2倍的数组来替换原本的数组.

    这时我们需要创建一个类,它拥有一个数组类型的成员变量,像这样.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package generic;

    public class StringsClass {
    private String[] data;

    public StringsClass(String[] data) {
    this.data = data;
    }

    public String[] getData() {
    return data;
    }
    }

    但是数组创建时我们可以任意指定一种类型.如果我们需要整数类型,那就要这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package generic;

    public class IntegersClass {
    private Integer[] data;

    public IntegersClass(Integer[] data) {
    this.data = data;
    }

    public Integer[] getData() {
    return data;
    }
    }

    这样不仅代码会高度重复,而且我每自定义一个新的类型,都需要再写一个类,再重复一遍代码。

    那不如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package generic;

    public class ObjectsClass {
    private Object[] data;

    public ObjectsClass(Object[] data) {
    this.data = data;
    }

    public Object[] getData() {
    return data;
    }
    }

    因为Java中,所有类的父类都是Object,这样不管你要什么类型都可以了。我们只需要ObjectsClass一个类即可维护所有类型的数组。

    但是这样使用时需要强制类型转换,而且在使用时我也无法得知,某个ObjectsClass类的实例到底存了什么类型的变量。data变量很可能存了不同类型的变量,在实际使用时依旧存在很多问题。

    所以我们需要一种机制来解决这个问题,它就是泛型(generics

    泛型类

    在类名后增加<T>,就是泛型类.其中T是泛型变量。我们也可以指定多个泛型变量,而且可以任意命名(必须符合java变量的命名规范),比如:<T,K,M,O>,<FIRST,SECOND>。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package generic;
    public class GenericClass<T> {
    private T[] data;

    public GenericClass(T[] data) {
    this.data = data;
    }

    public T[] getData() {
    return data;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package generic;

    public class GenericMain {
    public static void main(String[] args) {
    Integer[] integers = {1, 2, 3, 4, 5};
    //<>我们通过这个尖括号来指定类型。我们可以认为它是类的参数,在使用这个类的时候我们需要传入参数,当传入Integer类型时,GenericClass中的T就是Integer。
    GenericClass<Integer> integerGenericClass = new GenericClass<>(integers);
    String[] strings = {"a","b","ccc","defg"};
    GenericClass<String> stringGenericClass = new GenericClass<>(strings);
    // 如果不指定类型T 就是Object类型,但是不推荐这样使用。
    GenericClass genericClass = new GenericClass(integers);
    }
    }

    泛型接口

    定义泛型接口的方式和定义泛型类很相近。

    1
    2
    3
    4
    5
    6
    7
    package generic;

    public interface Generator<T> {

    public T next();

    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //继承了泛型接口,但是是普通类
    public class FruitGenerator implements Generator<String> {

    @Override
    public String next() {
    return "Fruit";
    }

    }
    //继承了泛型接口,但是是泛型类
    public class FruitGenerator<T> implements Generator<T> {

    private T next;

    public FruitGenerator(T next) {
    this.next = next;
    }

    @Override
    public T next() {
    return next;
    }

    public static void main(String[] args){
    FruitGenerator<String> fruit = new FruitGenerator<>("Fruit");
    System.out.println(fruit.next);
    }

    }

    泛型方法

    定义泛型方法时,尖括号和类型变量需要放在修饰符和返回类型之间。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package generic;

    public class GenericMain {
    public static void main(String[] args) {
    Integer[] integers = {1, 2, 3, 4, 5};
    GenericClass<Integer> integerGenericClass = new GenericClass<>(integers);
    String[] strings = {"a", "b", "ccc", "defg"};
    GenericClass<String> stringGenericClass = new GenericClass<>(strings);
    GenericClass genericClass = new GenericClass(integers);
    print(integerGenericClass.getData());
    print(stringGenericClass.getData());
    }
    // 在返回类型前指定泛型<T>
    public static <T> void print(T[] ts) {
    for (T t : ts) {
    System.out.print(t + " ");
    }
    System.out.println();
    }
    }

    常用的数据结构

    集合(Collection)

    Collection是Java中的一个接口,它定义了容器应有的方法,如添加元素、删除元素、遍历元素、清空等等。它有很多不同的实现。容器要容纳某一种类型的元素,所以在使用容器时需要用到泛型。

    • 常用方法

      Collection为所有容器定义了元素操作,常用的有:

      1. add(E e)方法添加元素

      2. remove()方法删除元素

      3. clear()方法清空容器

    Collection有很多不同的实现:

    列表(List)

    ​ 数组相似。但List是一个接口,不能被实例化,需要使用ArrayListLinkedList等。

    集合(Set)

    ​ 集合中不能有重复的元素。同样Set也是一个接口,需要使用HashSetSortedSet等。

    队列(Queue)

    ​ 类似列表,但是对容器的操作方式进行了限制,只能将新元素放在最后,只能拿出最早放进去的元素。具有先进先出的性质,像排队一样。Queue也是一个接口,需要使用ArrayQueue等。

    栈(Stack)

    ​ 类似队列,但是只能将新元素放在前面,且只能拿出最新放进去的元素。具有后进先出的性质。

    映射(Map)

    Map是一个接口,但Map不是Collection的子接口。

    Map中存储的是键值对<KEY,VALUE>,我们可以通过键拿到对应的值,即键和值构成了映射关系。

    Map是接口,我们可以用HashMap来实例化。

    • 常用的方法

      Map实例可以通过:

      1. V put(K k, V v)向map中存入一个键值对,K为键,V为值,返回之前这个键对应的值。
      2. V remove(Object key)根据键移除map中的键值对。
      3. V get(Object o)根据键查找值。
      4. Set<K> keySet()获取map的键的集合
      5. Collection<V> values()获取map的值的集合
      6. Set<Map.Entry<K, V>> entrySet()获取键值对集合
      7. V replace(K key, V value)将key对应的值替换为参数value

    一个例子

    ​ 在之前People的demo基础上,增加一些代码。我们要定义一间教室,教室里有很多人People,人要可以问问题,还可以回答问题,人们可以在教室里进行讨论。我的帮手小明决定帮我完成Classroom类的定义,而我去完善人的定义。

    ​ 但是现在,我们的“人”并不会问问题,也不会回答问题,小明也不知道我将会用什么样的方式实现这个功能。为了能够尽快开始工作,我们协商了两个接口AnswerableCanAsk,我们不需要修改什么代码,只要让People类实现这两个接口就定义好了”人”需要怎么问问题,怎么回答问题。

    1
    2
    3
    4
    5
    package classroom;

    public interface Answerable {
    public abstract void answerQuestion(String answer);
    }
    1
    2
    3
    4
    5
    package classroom;

    public interface CanAsk {
    void askQuestion(Answerable answerable, String question);
    }
    1
    2
    3
    public abstract class People implements CanAsk, Answerable {
    //...
    }

    ​ 此时,我和小明就可以各自开始干各自的活。他写他的Classroom,我去完善TeacherStudent。而AnswerableCanAsk以及People在我们完工之前不能私自进行任何改动,这三块代码已经成为了我和小明任务对接的接口,它成为了一个不能轻易改变的协议,因为我们在完成各自的任务时都需要使用到这三份代码。

    三天后,任务完成了,即使这三天我们没有进行任何沟通,代码也合并得十分完美,Perfect!

    完成后的代码:

    • Classroom.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      package classroom;

      import java.util.*;

      public class Classroom {
      // 人不能有重复,所以使用set。重写了people类的equals 和 hashcode函数,集合才可以更合理地查重
      private Set<People> peoples = new HashSet<>();
      private String name;
      // 记录题目到答案的映射。存储<题目,答案>键值对。
      private static Map<String, String> question2answer = new HashMap<>();

      public Classroom(String name) {
      this.name = name;
      //添加默认题库
      question2answer.put("1+1=?", "2");
      question2answer.put("杨晨学姐美不美?", "美!");
      }

      public String getName() {
      return name;
      }

      /**
      * 添加问题
      * @param question 问题
      * @param answer 答案
      * @return 教室
      */
      public Classroom addQA(String question,String answer){
      question2answer.put(question,answer);
      return this;
      }

      /**
      * 给教室添加人
      * @param people 人
      * @return 教室
      */
      public Classroom addPeople(People people) {
      peoples.add(people);
      return this;
      }

      public void addPeople(List<People> peoples) {
      this.peoples.addAll(peoples);
      }

      public void addPeople(People... peoples) {
      this.peoples.addAll(Arrays.asList(peoples));
      }

      /**
      * 提供答案查询功能
      * @param question
      * @return
      */
      public static String getAnswer(String question) {
      return question2answer.get(question);
      }

      /**
      * 开始讨论
      */
      public void discuss() {
      int askIndex, answerIndex, questionIndex;
      // 将人和问题转成数组
      People[] peopleArray = new People[peoples.size()];
      String[] questionArray = new String[question2answer.keySet().size()];
      question2answer.keySet().toArray(questionArray);
      peoples.toArray(peopleArray);
      for (int k = 0; k < 5; k++) {
      //通过Math.random取随机数得到随机的索引值。
      askIndex = (int) (Math.random() * peopleArray.length);
      answerIndex = (int) (Math.random() * peopleArray.length);
      while (answerIndex == askIndex) {
      answerIndex = (int) (Math.random() * peopleArray.length);
      }
      questionIndex = (int) (Math.random() * questionArray.length);
      //随机选择一个人随机问另一个人随机一个问题
      peopleArray[askIndex].askQuestion(peopleArray[answerIndex], questionArray[questionIndex]);
      }
      }
      }
    • People.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      package classroom;

      import java.util.Objects;

      public abstract class People implements CanAsk, Answerable {
      protected int age;
      protected String name;
      protected String words;
      protected String action;

      public People(int age, String name, String words, String action) {
      this.age = age;
      this.name = name;
      this.words = words;
      this.action = action;
      }

      // 每个人可以做些什么
      public final void doSomething() {
      String action = action();
      String words = words();
      int len = Math.max(action.length(), words.length()) * 2;
      StringBuilder sb = new StringBuilder();
      String line;
      for (int i = 0; i < len / 2; i++) {
      sb.append("=");
      }
      sb.append(name).append("的分割线");
      for (int i = 0; i < len / 2; i++) {
      sb.append("=");
      }

      line = sb.toString();
      System.out.println(line);
      System.out.println(action);
      System.out.print("然后说:\"");
      System.out.print(words);
      System.out.println("。\"");
      System.out.println(line);
      }

      // 将会说出的一段话。e.g 我是杨晨
      protected abstract String words();

      // 做出一个动作。e.g 露出开心的笑容
      protected abstract String action();

      //为了Set可以更好的判断people是否相同,重写Object类的equals和hashCode函数
      @Override
      public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      People people = (People) o;
      return age == people.age &&
      Objects.equals(name, people.name) &&
      Objects.equals(words, people.words) &&
      Objects.equals(action, people.action);
      }

      @Override
      public int hashCode() {
      return Objects.hash(age, name, words, action);
      }
      }
    • Student.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      package classroom;

      public class Student extends People {

      public Student(int age, String name, String words, String expression) {
      super(age, name, words, expression);
      }

      @Override
      protected String words() {
      return words;
      }

      @Override
      protected String action() {
      return name + "同学" +
      action + "。";
      }

      @Override
      public void answerQuestion(String answer) {
      System.out.println("A--Student " + name + " :");
      if(answer==null){
      System.out.println("我也不知道");
      return;
      }
      double random = Math.random();
      //假设学生有50%的概率知道答案。
      if (random >= 0.5) {
      System.out.println("答案我知道,是:" + answer);
      } else {
      System.out.println("答案是...我也不知道");
      }
      }

      @Override
      public void askQuestion(Answerable answerable, String question) {
      System.out.println("Q--Student " + name + " :");
      System.out.println("我想问个问题:" + question);
      //通知那个人(answerable)该回答问题了。
      answerable.answerQuestion(Classroom.getAnswer(question));
      }
      }
    • Teacher.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      package classroom;

      import java.util.HashMap;
      import java.util.Map;
      import java.util.function.BiFunction;

      public class Teacher extends People {
      protected String subject;
      private final Map<String, Integer> answer2count = new HashMap<>();

      public Teacher(int age, String name, String words, String expression) {
      this(age, name, words, expression, "高数");
      }

      public Teacher(int age, String name, String words, String expression, String subject) {
      super(age, name, words, expression);
      this.subject = subject;
      }

      @Override
      protected String words() {
      return words + "。今天我给大家讲一讲《" +
      subject +
      "》";
      }

      @Override
      protected String action() {
      return name + "老师" +
      action + "。";
      }

      @Override
      public void answerQuestion(String answer) {
      System.out.println("A--Teacher " + name + " :");
      if (answer == null) {
      System.out.println("我也不知道");
      return;
      }
      Integer count = answer2count.get(answer);
      if (count != null && count > 1) {
      System.out.println("这道题我已经讲过" + count + "次了!");
      }
      System.out.println("这道题的答案是:" + answer);
      answer2count.merge(answer, 1,
      //匿名内部类
      new BiFunction<Integer, Integer, Integer>() {
      @Override
      public Integer apply(Integer integer, Integer integer2) {
      return integer + integer2;
      }
      });
      //Lambda表达式
      // answer2count.merge(answer, 1,
      // (integer, integer2) -> integer + integer2);
      //使用相同的函数代替Lambda表达式
      // answer2count.merge(answer, 1,
      // Integer::sum);


      }

      @Override
      public void askQuestion(Answerable answerable, String question) {
      System.out.println("Q--Teacher " + name + " :");
      System.out.println("问题是:" + question);
      //通知那个人(answerable)该回答问题了。
      answerable.answerQuestion(Classroom.getAnswer(question));
      }
      }
    • Main.java

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      import classroom.*;

      import java.util.ArrayList;
      import java.util.List;

      public class Main {
      public static void main(String[] args) {
      People mbg = new Teacher(69, "马保国",
      "年轻人不讲武德", "捂着红肿的眼睛",
      "武德");
      People yc = new Teacher(19, "杨晨",
      "我女妆真好看", "露出开心的笑容",
      "女妆的一百零八种技巧");
      People zk = new Student(21, "郑开",
      "杨晨学姐太强了", "拍了拍杨晨");
      People robot = new People(0, "1024", "你们肯定猜不到我是机器人", "喝了一口机油") {
      @Override
      protected String words() {
      return words;
      }

      @Override
      protected String action() {
      return name + "机器人" + action + "。";
      }

      @Override
      public void answerQuestion(String answer) {
      System.out.println("A--Robot " + name + " :");
      if (answer == null) {
      System.err.println("机器故障:NullPointerException");
      return;
      }
      System.out.println("答案是:" + answer);
      }

      @Override
      public void askQuestion(Answerable answerable, String question) {
      System.out.println("Q--Robot " + name + " :");
      question = "你知道我的名字吗?";
      System.out.println("问你个问题:" + question);
      answerable.answerQuestion(Classroom.getAnswer(question));
      }
      };
      List<People> peoples = new ArrayList<>();
      peoples.add(mbg);
      peoples.add(yc);
      peoples.add(zk);
      peoples.add(robot);
      for (People people : peoples) {
      people.doSomething();
      }
      Classroom classroom = new Classroom("twt");
      classroom.addPeople(peoples);
      classroom.discuss();

      }
      }
    • 运行结果

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      =====================马保国的分割线=====================
      马保国老师捂着红肿的眼睛。
      然后说:"年轻人不讲武德。今天我给大家讲一讲《武德》。"
      =====================马保国的分割线=====================
      ============================杨晨的分割线============================
      杨晨老师露出开心的笑容。
      然后说:"我女妆真好看。今天我给大家讲一讲《女妆的一百零八种技巧》。"
      ============================杨晨的分割线============================
      ==========郑开的分割线==========
      郑开同学拍了拍杨晨。
      然后说:"杨晨学姐太强了。"
      ==========郑开的分割线==========
      ==============1024的分割线==============
      1024机器人喝了一口机油。
      然后说:"你们肯定猜不到我是机器人。"
      ==============1024的分割线==============
      Q--Student 郑开 :
      我想问个问题:1+1=?
      A--Teacher 杨晨 :
      这道题的答案是:2
      Q--Teacher 马保国 :
      问题是:1+1=?
      A--Teacher 杨晨 :
      这道题的答案是:2
      Q--Student 郑开 :
      我想问个问题:杨晨学姐美不美?
      A--Robot 1024 :
      答案是:美!
      Q--Teacher 马保国 :
      问题是:杨晨学姐美不美?
      A--Robot 1024 :
      答案是:美!
      Q--Teacher 马保国 :
      问题是:1+1=?
      A--Student 郑开 :
      答案是...我也不知道

      Process finished with exit code 0

    参考内容

    Java泛型

    JAVA常见容器

    demo代码仓库网址

    Contents
    1. 1. 抽象类
      1. 1.1. 多态
    2. 2. 接口
      1. 2.1. 匿名内部类与Lambda表达式
    3. 3. 泛型
      1. 3.1. 泛型类
      2. 3.2. 泛型接口
      3. 3.3. 泛型方法
      4. 3.4. 常用的数据结构
        1. 3.4.1. 集合(Collection)
          1. 3.4.1.1. 列表(List)
          2. 3.4.1.2. 集合(Set)
          3. 3.4.1.3. 队列(Queue)
          4. 3.4.1.4. 栈(Stack)
        2. 3.4.2. 映射(Map)
      5. 3.5. 一个例子
  • 参考内容