Nrich's blog Nrich's blog
首页
  • Java
  • Golang
  • 深度学习
  • Git
  • Linux
  • DataStructure
  • CloudNative
  • Redis
  • MySQL
  • 路由劫持
GitHub (opens new window)

Nrich

小聪明
首页
  • Java
  • Golang
  • 深度学习
  • Git
  • Linux
  • DataStructure
  • CloudNative
  • Redis
  • MySQL
  • 路由劫持
GitHub (opens new window)
  • Java

    • 黑马Java入门基础-学习笔记

      • Java 基础语法
      • 数组
      • 方法
      • 面向对象基础
      • 常用API(String、ArrayList)
      • 面向对象进阶1(static、单例、代码块、继承)
      • 面向对象进阶2(包、权限修饰符、抽象类、接口)
      • 面向对象进阶3(多态、内部类、常用API)
      • 面向对象进阶4(常用日期API、正则、Arrays类、Lambda)
      • 面向对象进阶5(集合体系)
      • 面向对象进阶6(集合体系之Map)
      • 面向对象进阶-补充(可变参数)
    • 面向对象
      • 1. 模板方法模式
        • 1.1 抽象类
        • 1.2 模板方法
      • 2. 接口
        • 2.1 基本知识
        • 2.2 接口新增的方法
        • 2.3 使用接口的注意事项(面试中涉及)
      • 3. 多态
        • (1) 什么是多态
        • (2) 多态的常见形式
        • (3) 多态中成员访问特点
        • (4) 多态的前提
        • (5) 优势
        • (6) 多态下引用数据类型的类型转换
      • 4. 内部类
        • 4.1 静态内部类
        • 4.2 成员内部类(非静态内部类)
        • 4.3 局部内部类
        • 4.4 *匿名内部类
        • (1) 概述
        • (2) 常见使用形式
    • 常用API
    • 并发、并行、异步、同步(暂存)
    • 《Spring Boot 进阶-郑天民》

  • GoLang

  • 开发
  • Java
Nrich
2022-03-28
目录

面向对象

# 1. 模板方法模式

# 1.1 抽象类

如果一个类中的某个方法的具体实现不能确定,就可以申明成abstract修饰的抽象方法(不能写方法体了),这个类必须用abstract修饰,被称为抽象类。

image-20220328164447274

抽象的使用总结与注意事项:

  • 抽象类可以理解成类的不完整设计图,是用来被子类继承的。
  • 一个类如果继承了抽象类,那么这个类必须重写完抽象类的全部抽象方法,否则这个类也必须定义成抽象类。
  • 抽象类没有创建对象的能力。
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

# 1.2 模板方法

模板方法模式实现步骤:

  • 把功能定义成一个所谓的模板方法,放在抽象类中,模板方法中只定义通用且能确定的代码。
  • 模板方法中不能决定的功能定义成抽象方法让具体子类去实现。

提示

模板方法我们是建议使用final修饰的,模板方法是给子类直接使用的,不是让子类重写的, 一旦子类重写了模板方法就失效了。

例如,中学生和小学生写《我的爸爸》这篇作文,他俩除了文章主体部分不同,别的地方都一样,此时,可以定义一个抽象类,用一个公共的模板方法来实现相同的部分,不同的部分在这个抽象类中声明一个抽象方法,让子类在此处来实现他们分别的功能。如下图代码所示:

image-20220328170749055

# 2. 接口

# 2.1 基本知识

接口体现了一种规范,约束类一定要干啥事。规范一定是公开的。

JDK 8 之前接口中只能有抽象方法和常量。

  • 关键字:interface

  • 接口的实现:implements 接口1,接口2,...

几种关系:

  • 类和类的关系:单继承。
  • 类和接口的关系:多实现。
  • 接口和接口的关系:多继承,一个接口可以同时继承多个接口。

接口多继承的作用: 规范合并,整合多个接口为同一个接口,便于子类实现。(子类只需要implements一个接口就可以了,不用很累赘地实现很多个接口)

# 2.2 接口新增的方法

JDK8开始后新增了那些方法 ?

  • 默认方法:default修饰,实现类对象调用。
  • 静态方法:static修饰,必须用当前接口名调用。不能用子类调用静态方法。
  • 私有方法:private修饰,jdk9开始才有的,只能在接口内部被调用。
  • 他们都会默认被public修饰。

注意

JDK8新增的3种方法我们自己在开发中很少使用,通常是Java源码涉及到的,我们需要理解、识别语法、明白调用关系即可。

提示

切换模块的编译版本,选择JDK版本:File -> Project Structure -> Modules ->language level

image-20220328195212510

# 2.3 使用接口的注意事项(面试中涉及)

比较偏的一些语法,实际开发中通常不用,只有在一些面试中可能会涉及。

  1. 接口不能创建对象

  2. 一个类实现多个接口,多个接口中有同样的静态方法不冲突。

  3. 一个类继承了父类,同时又实现了接口,父类中和接口中有同名方法,默认用父类的。

    继承只能在实现之前写,即先有亲爸再有干爹。接口为干爹,继承为亲爸。 image-20220328200737543

  4. 一个类实现了多个接口,多个接口中存在同名的默认方法,不冲突,这个类重写该方法即可。

  5. 一个接口继承多个接口,是没有问题的,即使多个接口中有重名的方法,但如果多个接口中存在规范冲突则不能多继承。

# 3. 多态

# (1) 什么是多态

同类型的对象,执行同一个行为,会表现出不同的行为特征。

父类的引用类型指向子类的构造,向上造型,即为多态。

# (2) 多态的常见形式

  • 父类类型 对象名称 = new 子类构造器;
  • 接口 对象名称 = new 实现类构造器;
Animal a = new Dog();
a.run(); // 方法调用:编译看左,运行看右
System.out.println(a.name); // 方法调用:编译看左,运行也看左,动物名称

Animal a1 = new Dog();
a1.run();
System.out.println(a1.name); // 动物名称
1
2
3
4
5
6
7

# (3) 多态中成员访问特点

  • 方法调用:编译看左,运行看右。=> 因此多态下不能使用子类的独有功能。
  • 变量调用:编译看左,运行也看左。(多态侧重行为多态)

因此上述代码的输出结果都是动物名称。

# (4) 多态的前提

有继承/实现关系;有父类引用指向子类对象;有方法重写。

# (5) 优势

  • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。
Animal a = new Dog(); // 后续业务更改时只需要修改此处new的Dog即可
a.run(); // 后续业务行为随对象而变,后续代码无需修改
1
2
  • 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的一切子类对象,体现出多态的扩展性与便利。
    public static void go(Animal a){
        System.out.println("开始。。。");
        a.run();
        System.out.println("结束。。。");
    }
// 这个函数可以传上面代码中的a, 也可以传a1
1
2
3
4
5
6

# (6) 多态下引用数据类型的类型转换

  • 自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。

    // 自动类型转换
    Animal a = new Dog();
    a.run();
    //a.lookDoor(); // 多态下无法调用子类独有功能
    
    1
    2
    3
    4
  • 强制类型转换吗(从父到子)

    • 此时必须进行强制类型转换:子类 对象变量 = (子类)父类类型的变量

      // 强制类型转换:可以实现调用子类独有功能的
      Dog d = (Dog) a;
      d.lookDoor();
      
      1
      2
      3
    • 作用:可以解决多态下的劣势,可以实现调用子类独有的功能。

    • 注意: 如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException

建议

建议强制转换前,先判断变量指向对象的真实类型,再强制类型转换。

变量名 instanceof 真实类型 判断关键字左边的变量指向的对象的真实类型,是否是右边的类型或者是其子类类型,是则返回true

以上判断是很有必要的,因为很有可能在方法内部看不到这个对象到底是啥类型。如下代码所示,不知道传进来的 Animal 到底是狗还是乌龟:

    public static void go(Animal a){
        System.out.println("预备~~~");
        a.run();
        // 独有功能
        if(a instanceof Tortoise){
            Tortoise t = (Tortoise) a;
            t.layEggs();
        }else if(a instanceof Dog){
            Dog d1 = (Dog) a;
            d1.lookDoor();
        }
        System.out.println("结束~~~~");
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4. 内部类

内部类就是定义在一个类里面的类

# 4.1 静态内部类

有static修饰,属于外部类本身。它的特点和使用与普通类是完全一样的,类有的成分它都有,只是位置在别人里面而已。

public class Outer{
    // 静态成员内部类
    public static class Inner{
    }
}
1
2
3
4
5

静态内部类创建对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类构造器; 范例:Outer.Inner in = new Outer.Inner();

这种语法比较少用,一般还是分开定义两种类的。

# 4.2 成员内部类(非静态内部类)

无static修饰,属于外部类的对象。JDK16之前,成员内部类中不能定义静态成员,JDK 16开始也可以定义静态成员了。

public class Outer {
    // 成员内部类
    public class Inner {
        
    }
}
1
2
3
4
5
6

成员内部类创建对象的格式:格式:外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器(); 范例:Outer.Inner in = new Outer().new Inner();

(先有外部对象,然后才能创建成员内部类的对象)

关于static

static 修饰的是表示属于类的,而不是属于对象的。若有1000个对象,那么属于对象的部分也是有1000个,而 static 修饰的部分只有一个,因为只有一个这样的类。所以对象里面是不存在静态的。

成员内部类比静态内部类更常用,因为他有对象创建的先后次序。企业内部,必须先有车这个对象,再有发动机对象。

注意

在成员内部类中访问所在外部类的对象,格式为:外部类名.this

# 4.3 局部内部类

鸡肋语法,了解即可

# 4.4 *匿名内部类

# (1) 概述

本质上是一个没有名字的局部内部类,定义在方法中、代码块中、等。

作用:方便创建子类对象,最终目的为了简化代码编写。

点击展开代码
/**
      目标:学习匿名内部类的形式和特点。
 */
public class Test {
    public static void main(String[] args) {
        Animal a = new Animal(){
            @Override
            public void run() {
                System.out.println("老虎跑的块~~~");
            }
        };
        a.run();
    }
}

//class Tiger extends Animal{
//    @Override
//    public void run() {
//        System.out.println("老虎跑的块~~~");
//    }
//}

abstract class Animal{
    public abstract void run();
}
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

上面这段代码中虽然 Animal 是个抽象类,但是在 Test 中也可以用 Animal 来创建一个匿名内部类。匿名内部类写出来就会产生一个匿名内部类的对象。匿名内部类的对象类型相当于是当前new的那个的类型的子类类型。即产生的匿名内部类相当于注释掉的 Tiger 子类对象。

匿名内部类也会产生 Class 文件。

# (2) 常见使用形式

有了匿名内部类之后,可以不是必须要定义另外的Class去实现相应的接口了,可以直接在 new 的时候定义匿名内部类出来进行接口的实现。

具体的一个例子
/**
    目标:掌握匿名内部类的使用形式(语法)
 */
public class Test2 {
    public static void main(String[] args) {
        Swimming s = new Swimming() {
            @Override
            public void swim() {
                System.out.println("学生快乐的自由泳🏊‍");
            }
        };
        go(s);

        System.out.println("--------------");

        Swimming s1 = new Swimming() {
            @Override
            public void swim() {
                System.out.println("老师泳🏊的贼快~~~~~");
            }
        };
        go(s1);

        go(new Swimming() {
            @Override
            public void swim() {
                System.out.println("运动员泳🏊的贼快~~~~~");
            }
        });

        System.out.println("--------------");

    /**
       学生 老师 运动员可以一起参加游泳比赛
     */
    public static void go(Swimming s){
        System.out.println("开始。。。");
        s.swim();
        System.out.println("结束。。。");
    }
}


interface Swimming{
    void swim();
}
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

经常用匿名内部类方法的入参形式,如下图:

image-20220330142330956

啥时候用匿名内部类

开发中不是我们主动去定义匿名内部类的,而是别人需要我们写或者我们可以写的时候才会使用。能写的时候才要写,可遇不可求。

编辑 (opens new window)
上次更新: 2022/03/30, 06:54:33
面向对象进阶-补充(可变参数)
常用API

← 面向对象进阶-补充(可变参数) 常用API→

最近更新
01
YAML、Pod、Job、CronJob、ConfigMap、Secret
06-06
02
Kubernetes 的安装与基本架构
06-04
03
初识容器
05-30
更多文章>
Theme by Vdoing | Copyright © 2022-2023 Nrich | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式