面向对象和类 | 青训营笔记

88 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 11 天

面向对象概述

面向过程或者说传统的程序设计通过设计一系列的算法来求解问题,“算法 + 数据结构 = 程序”。面向过程思想通过将问题分解为一个个的过程,然后每个过程去操作全局变量,最终达到问题的求解。

面向对象相对于面向过程思想更加抽象,面向对象的程序是由一个个对象组成的,一个对象包含用户公开的特定的功能方法和隐藏的功能实现,根本上说,只要对象能够完成特定的功能,就没必要关心它的具体实现

举个例子,实现一个简单的浏览器。实现一个简单的浏览器,利用面向过程方法论需要2000+的过程和全局变量来实现;面向对象方法则需要100+类和每个类20个左右的方法实现。乍一看体量差不多,但是实际开发过程中,如果发生了错误,在2000+的过程中寻找错误位置和在一个类中寻找20多个方法的难度不是一个数量级的。

类和对象

类:构造对象的模板。对象:通过模板创建出来的实例。

public class 类名 {
	成员变量/静态变量/静态初始化块
	成员方法/静态方法      创建对象
	构造器/初始化块      ----------> 类名 变量名 = new 类名();
	内部类
}

上面的伪代码就可以说明类和变量的关系,类中包含了各种各样的模板,方法,变量,代码块和内部类。而对象创建出来以后就需要被调用或者说被使用。

使用对象,主要有两种类型:调用成员方法,使用成员变量。

Person p = new Person();
p.age = 12;					// 使用成员变量,为成员变量赋值
p.sleep();					// 调用成员方法

对象三个重要的特性:行为(behavior)、状态(state)、标识(identifier)。行为 - 成员方法、状态 - 成员变量、标识 - 唯一ID。

对象的状态在实践中,通常只能通过类的成员方法来实现,否则会破坏一个对象的封装性。

注意事项:

  1. 类要使用大驼峰的命名规则来命名
  2. 一个 java 文件只有一个 public 类,而且文件名和这个 public 类的名称保持一致
  3. 成员变量的完整定义格式:权限修饰符 数据类型 变量名称 = 初始化值; 如果没有初始化值,那就是默认值

类之间的关系

类之间的关系主要有:

  • 依赖(uses-a)
  • 聚合(has-a)
  • 继承(is-a)

在实际时间过程中,对于类之间的关系如何命名,根据经验,可以通过当前对象和相关对象的生命周期来判断。

  • 依赖:一个类的方法操作或者使用某个对象,那么这个类就依赖于另一个类。生命周期来看,这两个对象的生命周期之间没有关系,只存在比较弱的关系。
  • 聚合:一个类的对象和另一个类的对象之间存在包含关系,这就是聚合。从生命周期来看,对象中包含的生命周期和这个对象没有强关系称之为聚合。这区别于组合,组合关系中对象和它包含的对象有一致的生命周期,是比聚合更加强的关系。
  • 继承:一个类和它派生子类之间的关系,派生子类一般对父类进行了重写,或者对方法进行了扩展。生命周期来看,这个对象有多重身份也就是这条继承链上的所有身份。

封装性

封装,其实就是让一个类将其成员进行隐藏,对象代表什么,就封装相应的数据,并且提供数据相关的行为。

权限修饰符 private 就可以起到一定的封装效果。private 关键字可以修饰成员,被 private 修饰的成员只能在本类中使用,其他类包括派生类都不可以使用。

面向对象的封装性的关键在于:绝对不能让类中的方法直接访问其他类的实例字段

遮蔽(shadow)

遮蔽(shadowing)就是局部变量和全局变量同名的时候,按照这个名称使用变量优先会使用局部变量。

public class Person {
    private int age;
    public void setAge(int age) {
        System.out.println(age);		// 方法参数
        System.out.println(this.age);	// 成员变量
    }
}

为了避免这个问题,就需要 superthis 关键字来区别这个变量。

初始化块和静态初始化块

*初始化块(initialization block)*是 Java 中初始化对象的一种机制,Java 代码中可以有多个初始化块。

  • 初始化块会按照类声明顺序执行代码块内容
  • 初始化块中无法访问,定义在初始化块之前的字段

类似地,也会存在*静态初始化块(static initialization block)*用于初始化比较复杂的静态字段。

public class Block {
    private long timestamp;
    {
        LocalDateTime date = LocalDateTime.now();
        timestamp = date
            .toInstant(ZoneOffset.of("+8"))
            .toEpochMilli();
        // 无法访问声明之后的字段
        // year = date.getYear();
    }
    private int year;
    { year = LocalDateTime.now().getYear(); }
    
    // 静态初始化块
    private static int random;
    static {  random = new Random().nextInt(); }
}

对象的初始化

关于对象的初始化,在JVM中有详细解释,下面是一个非常简单的过程

  1. 类加载:加载类
  2. 内存中开辟空间
  3. 构造方法调用过程