抽象类、接口、枚举

第八天(坚持)

抽象类

1.什么是抽象类,作用特点。

抽象类是面向对象编程中一种特殊的类,它不能被实例化,主要用于作为其他类的基类(父类)。抽象类的主要作用是定义公共结构和行为规范 ,同时强制子类实现特定的功能,常用于将部分代码抽象到抽象类中。比较典型的就是模板方法模式。

抽象类中可以补定义抽象方法,但抽象方法一定在抽象类中。

注解(抽象类的特点)

1)由abstract修饰的方法为抽象方法,抽象方法没有方法体,需要使用分号结尾

2)若类中包含抽象方法,那么该类必须使用关键字abstract声明成抽象类,final不能修饰抽象类

3)抽象类里,可以没有抽象方法

4)抽象类里可以提供构造器,但是不能实例化,没有意义。

5)一个类继承了抽象类,那么必须重写里面的所有抽象方法,除非该子类也声明为抽象类。

接口

接口中不考虑Java8中default方法情况下,只能定义抽象类。接口主要用于制定规范,一般在实际开发中,我们会先把接口暴露给外部,然后在业务代码中实现接口。

接口特点

  • 使用interface定义
  • 可以提供成员变量,默认提供public static final进行修饰

  • 可以提供成员方法,默认使用public abtract进行修饰

  • 接口中不能存在构造器,接口不能实例化,

  • 与继承不同,一个类可以实现多个接口。接口间使用逗号分开。

  • 使用关键字implements进行实现,必须实现接口中的所有抽象方法

  • 若一个类中没有全部实现接口中的抽象方法,那么该类需要使用abstract声明成抽象类

  • 接口之间可以存在继承关系,即一个接口通过关键字extends可以继承另一个(或多个)接口。

  • 子接口继承了父接口中的所有抽象方法


接口和抽象类的区别

1.方法定义:接口和抽象类,最明显的区别就是接口只是定义了一些方法而已,在不考虑Java8中default方法情况下,接口中只有抽象方法,是没有实现的代码的。(Java8中可以有默认方法)

2.修饰符:抽象类中的修饰符可以有public、protected和private和<default>这些修饰符,而接口中默认修饰符是public。不可以使用其它修饰符。(接口中,如果定义了成员变量,还必须要初始化)

3.构造器:抽象类可以有构造器必须私有。接口不能有构造器。

4.接口可以被实现,抽象类可以被继承。一个类可以实现多个接口,一个接口可以继承多个其他接口但,一个抽象类只能继承一个抽象类。

5.职责不同:接口和抽象类的职责不一样。接口主要用于制定规范,一般在实际开发中,我们会先把接口暴露给外部,然后在业务代码中实现接口。抽象类的主要作用是定义公共结构和行为规范,同时强制子类实现特定的功能,常用于将部分代码抽象到抽象类中。比较典型的就是模板方法模式。


枚举

在Java中,枚举 是一种特殊的引用数据类型 ,是一个被命名的整型常数的集合,用于声明一组带标识符的常数,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

枚举特点

  • 使用enum关键字定义一个枚举,默认会继承java.lang.Enum类,而且是一个final类,因此不能再继承其他类
  • 必须在枚举类的第一行声明枚举类对象。有多个枚举对象时,使用逗号隔开,最后一个用分号结尾
  • 可以提供构造器,必须是私有的,如果构造器有形参,定义对象时必须显式调用构造器

  • 如果使用无参构造器创建枚举对象,则定义对象时,小括号可以省略

  • 可以提供私有的属性(建议,如不外部可以修该)

复制代码
public enum Year{
  //1. 枚举的第一行,必须是该枚举的对象,即你要表达的离散值。多个离散值使用逗号隔开,最后一个值分号结束。
  JANUARY(1),
  FEBRUARY(2),
  MARCH(3),
  APRIL(4),
  MAY(5),
  JUNE(6),
  JULY(7),
  AUGUST(8),
  SEPTEMBER(9),
  OCTOBER(10),
  NOVEMBER(11),
  DECEMBER(12),
  AAAA; // 如果调用无参构造器,小括号可以省略
  //2. 枚举可以提供私有属性
  private int number;
  //3. 可以提供构造器,但是必须是私有的,  实际上枚举的离散值就是调用构造器产生的,
  //   调用构造器的格式:   离散值(有参传参)
  private Year(int number){
  this.number = number;
  }
  //4. 构造器可以重载
  private Year(){}
  // 5. 可以给私有的成员变量,提供get/set方法, 不建议提供set方法
  public int getNumber(){
  return number;
  }
​
​
}

带有value的枚举

复制代码
package com.rs.shuatiwang.model.enums;
​
import cn.hutool.core.util.ObjectUtil;
​
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
​
public enum AppTypeEnum {
​
    SCORE("得分类", 0),
    TEST("测评类", 1);
​
    private final String text;
​
    private final int value;
​
    AppTypeEnum(String text, int value) {
        this.text = text;
        this.value = value;
    }
​
    /**
     * 根据 value 获取枚举
     *
     * @param value
     * @return
     */
    public static AppTypeEnum getEnumByValue(Integer value) {
        if (ObjectUtil.isEmpty(value)) {
            return null;
        }
        for (AppTypeEnum anEnum : AppTypeEnum.values()) {
            if (anEnum.value == value) {
                return anEnum;
            }
        }
        return null;
    }
​
    /**
     * 获取值列表
     *
     * @return
     */
    public static List<Integer> getValues() {
        return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList());
    }
​
    public int getValue() {
        return value;
    }
​
    public String getText() {
        return text;
    }
}

内部类

定义在一个类内部的类,就是内部类。内部类可以分为:成员内部类、静态内部类、局部内部类、匿名内部类。

成员内部类(了解)

定义在一个类的内部,与这个类的成员(属性、方法)平级,并且没有用static修饰的类。

1、访问权限可以是任意的权限,类似于一个类中的成员。

2、实例化的过程,需要先实例化外部类对象,再使用外部类对象进行内部类的实例化

3、内部类编译后,也会生成.class字节码文件。格式:外部类$内部类.class

复制代码
/**
 * 成员内部类
 */
public class Program {
    public static void main(String[] args) {
        // 1. 实例化一个外部类的对象
        Outer outer = new Outer();
        outer.name = "outer";
        // 2. 通过外部类的对象,实例化内部类对象
        Outer.Inner inner = outer.new Inner();
        inner.name = "inner";
        inner.show("hello");
    }
}
​
/**
 * 外部类
 */
class Outer {
    public String name;
    public int age1;
    // 这个类,由于是书写在一个类的内部,因此这个类被称为--内部类
    // 这个类,由于是写在Outer类中,和类中的属性、方法平级,可以称为是一个类的成员。
    // 并且,这个类没有使用 static 修饰,这样的类,被称为 -- 成员内部类
    class Inner {
        Inner() {
            System.out.println("实例化了一个Inner的对象");
        }
        public String name;
        public int age2;
​
        public void show(int age3) {
            System.out.println("参数age3: " + age3);
            System.out.println("内部类属性age2: " + age2);
            System.out.println("外部类属性age1: " + age1);
        }
​
        public void show(String name) {
            System.out.println("参数name: " + name);
            System.out.println("内部类属性name: " + this.name);
            System.out.println("外部类属性name: " + Outer.this.name);
        }
    }
}

静态内部类(了解)

定义在一个类的内部,与这个类的成员(属性、方法)平级,并且使用static修饰的类。

1、访问权限可以是任意的权限,类似于一个类中的成员。

2、实例化的过程中,直接使用 new实例化一个外部类 .内部类对象即可。

3、内部类编译后,也会生成.class字节码文件。格式:外部类$内部类 .class

复制代码
/**
 * 静态内部类
 */
public class Program {
    public static void main(String[] args) {
        // 1. 实例化静态内部类对象的时候,不需要借助外部类对象的。
        // Outer.Inner inner = new Outer.Inner();
        // 2. 如果已经事先导包了,还可以直接进行实例化
        Inner inner = new Inner();
​
    }
}
​
class Outer {
    public String name;
    // 因为这个类,书写与Outer类内,并且是用static修饰的类
    // 这样的类,被称为 -- 静态内部类
    static class Inner {
        Inner() {
            System.out.println("实例化了一个Inner对象");
        }
​
        public String name;
​
        public void show(String name) {
            System.out.println("参数name: " + name);
            System.out.println("内部类属性name: " + this.name);
            System.out.println("外部类属性name,此时无法访问" Outer.this.name);
        }
    }
}

局部内部类(了解)

定义在某一个代码段中的中。

1、没有访问权限修饰符。

2、在当前方法中,直接实例化即可

3、内部类编译后,也会生成.class字节码文件。格式:外部类$序号内部类 .class

复制代码
public class Program {
    public static void main(String[] args) {
        int a;
        // 写在某一个局部代码段中(例如:方法中)
        // 这个类,只能在当前的方法中使用
        class Inner {
​
        }
        Inner inner = new Inner();
        test();
    }
​
    static void test() {
        class Inner {
​
        }
    }
}

4.4 匿名内部类(重点)以及数组自定义排序Comparable接口

匿名内部类是Java中一种特殊的内部类,它没有显式的类名,通常用于简化代码实现一次性使用的类

复制代码
public class Teacher implements Comparable<Teacher> {
    private int age;
    public Teacher(int age) {
        this.age = age;
    }
public int getAge() {
    return age;
}
​
@Override
public String toString() {
    return "Teacher{" +
            "age=" + age +
            '}';
}
​
@Override
public int compareTo(Teacher o) {
    return this.age - o.age;
}
}
复制代码
import java.util.Arrays;
import java.util.Comparator;
​
public class TeacherTest {
    public static void main(String[] args) {
        //定义一个Teacher类型的数组,存储4个Teacher对象
        Teacher[] ts = new Teacher[4];
        for (int i = 0; i < ts.length; i++) {
            ts[i] = new Teacher((int)(Math.random()*10)+30);
        }
        //排序:调用数组工具类的排序方法
        Arrays.sort(ts);
        System.out.println(Arrays.toString(ts));
​
        //想要在不改变Teacher源码的情况下,降序
        /*
         * 匿名内部类:
         * 1.  写在方法中。
         * 2.  因为只在该处使用一次,因此没有必要定义出来该类的类结构。
         * 3.  匿名内部类一般都是向上造型这种多态写法,父类一般都是抽象类或者是接口。
         * 4.  语法结构:
         *      父类型名  变量名 = new 父类型名(){
         *         //要重写的方法
         *      };
         */
        Comparator c = new Comparator<Teacher>(){
            public int compare(Teacher o1, Teacher o2) {
                return o2.getAge()-o1.getAge();
            }
        };
        Arrays.sort(ts, c);
        System.out.println(Arrays.toString(ts));
    }
​
}