第八天(坚持)
抽象类
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)); } }