面向对象

面向对象

概述

  • 历史:

    • 面向对象编程object-oriented *programming: oop)取代 面向过程编程(结构化)

      注:面向对象是一种思想而不是技术Java 是面向对象的语言但是不代表不能按照面向过程的思想写代码,C 是面向过程的语言同样不代表不能按照面向对象的思想写代码

    • ==程序 = 算法 + 数据结构==(Pascal 语言的设计者 Niklaus Wirth 提出)

      (1) 面向过程的编程思想中认为 算法 是优先级最高的元素:程序员需要先明确如何操作数据之后才去定义具体的数据结构

      (2) 面向对象的编程思想中认为 数据结构 是优先级最高的元素

    • 面向对象编程更利于大型项目的开发,面向过程编程相对于适合小型项目的开发

    • 定义:用于创建具体实例或者对象的 模板
    • 基本组成:(1) 类变量 (static 修饰的成员变量) (2) 成员变量(实例变量)(3) 构造方法 (4) 方法
    • 特性:(1) ==封装== (2) ==继承== (3) ==多态==
    • 关系:(1) 泛化 (2) 实现 (3) 依赖 (4) 组合 (5) 关联 (6) 聚合
  • 对象

组成

特性

封装

  • 定义:合并数据和行为并创建新的数据类型,将接口和实现分离,实现细节隐藏

继承

  • 定义:采用关键字 extends 实现的类与类之间的关系

  • 特点:

  • this & super

    • 定义:两者都指向子类对象内存区域,前者用于引用子类的属性,后者用于引用父类的属性

      Java 核心技术 卷一》:“super 不是一个对象的引用,例如,不能将值 super 赋给另一个对象变量,它只是一个指示编译器调用超类方法的特殊关键字”

      注:从核心技术这句话中也可以看出,子类创建时并没有在内存中创建父类对象,也证明两个引用确实都是指向的子类的内存区域

    • 特点:

      • 两者都无法在静态代码块、静态方法中使用
      • 两者都无法调用静态变量、静态方法
      • super() 调用父类构造器方法会被虚拟机 ==默认调用==
      • super() 调用父类构造器方法 ==只能够放在子类构造器中第一行==,不可以放在其他任何方法中
向上转型 & 向下转型
重写 & 重载

多态

  • 定义:某个对象引用可以指向多种不同的实际类型的现象(==多态又被称为后期绑定、动态绑定、运行时绑定==)

  • 体现:重载可以体现方法的多态性 、重写和向上转型可以体现类的多态性;成员变量无法体现多态性

    /*向上转型体现多态*/
    Father father = new Son();
    /*方法重写*/
    public void play(){ // 父类中的方法
    System.out.println("Father Play...");
    }
    public void play(){
    System.out.println("Son Play..."); // 子类中的方法:重写
    }
    public static void test(Father father){
    father.play(); // 最后调用的一定是子类重写的方法而不是父类的方法,重写就在这里体现了多态性:编译器是如何知道最后调用的是子类的实现呢?明明这里是父类的引用
    }
    public static void main(String[] args){
    test(new Son()); // 方法参数给定的类型是父类,但是传入的参数却是子类:发生向上转型
    }
    /*方法重载*/

    /*成员变量无法体现多态性*/
    Father father = new Son(); // 假定父类和子类中都存在变量 field
    System.out.println(father.field); // 调用的一定是父类自身的那个变量而一定不会是子类
  • 解释:

  • 特点:

关系

  • 泛化(Generalization

    • 定义:从特殊到一般抽象出更加通用的类

    • 细节:

      (1) 泛化和继承是从 不同的角度描述的同一种关系:泛化是从特殊到一般,继承是从一般到特殊

      (2) 实际实现时通常 不推荐使用 泛化/继承 这种高耦合的关系

    fd91eda2fa5279281a7ccb866d9b95c6.png
  • 实现(Realization

    • 定义:类实现接口、抽象类中的抽象方法

    • 细节:接口中的所有方法都必须实现,抽象类中则只需要实现抽象类方法

      d22a707d17c9b052e4232c89bce66b4e.png
  • 聚合(Aggregation

  • 组合(Composition

  • 关联(Association

    • 定义:类与类之间的联系,联系是强依赖的,具有长期性的
    • 分类:
      • 按照方向分类:(1) 双向关联 (2) 单向关联 (3) 自关联
      • 按照联系重数分类:(1) 一对一 (2) 一对多 (3) 多对多
    • 细节:
      • 实际实现只要其他类出现在当前类中都算作是关联关系
      • 关联关系可以和其他的关系叠加
  • 依赖(Dependency

    • 定义:类与类之间的联系,联系是偶然性的,非常弱

    • 细节:实际实现时在方法的参数中使用到另外一个类的引用

      class ClassA{
      public void move(ClassB classb){ // 仅仅只是在方法中使用到其他类的引用不是长期性的
      ...
      }
      }
      bc309d4393a01b790e5774fde51189e2.png

访问权限

包可见性

public

private

protected

  • 定义:被修饰的属性可以被 ==子类== 或者 ==同一个包== 下的类直接访问
  • 细节:
    • protected 不可以修饰普通类;可以修饰内部类
    • 构造器访问权限为受保护的:该类只可以在隶属于同一个包下的类创建对象,子类是不可以创建父类对象的,仅可以借助 super 调用
private protected friendly public
范围 ==类内部可访问== ==子类及其同一个包下的类== ==同一个包下的类== ==任何类==
修饰 变量、方法、内部类 变量、方法、内部类 变量、方法、任何类 变量、方法、任何类(唯一)
继承 可以 可以 可以 可以
构造器 不可以直接创建(单例模式) 同一个包中类可以直接创建 同一个包中类可以直接创建 可以随意创建

关键字

  • 前提:仅列出和类相关的常用关键字,并发和其他相关的关键字不在此列出

static

静态变量

静态方法

静态代码块

  • 定义:采用关键字 static 修饰的代码块

    static {
    InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
    Properties properties = new Properties();
    ...
    }
  • 特点:

  • 细节:

    • 提高程序性能

      解释:多次调用方法中反复初始化内容不变的对象,可以把对象的初始化过程放入静态代码块中减少反复初始的过程

      boolean isBornBoomer() {														// 每次调用这个方法都会创建对象
      Date startDate = Date.valueOf("1946"); // 创建的对象具有内容还完全一样
      Date endDate = Date.valueOf("1964"); // 最好抽取出来成为静态代码块
      return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
      }
    • 多个静态代码块的执行顺序是按照 ==从上至下== 的顺序执行的:包括 main 方法在内

      public class Test {
      static{
      System.out.println("test static 1"); // 第一个执行的静态代码块
      }
      public static void main(String[] args) {
      // 第二个执行的静态代码块:虽然什么内容都没有但是依然会执行
      }
      static{
      System.out.println("test static 2"); // 第三个执行的静态代码块
      }
      }

静态内部类

  • 定义:采用关键字 static 修饰的内部类

  • 特点:

    • 静态内部类可以直接在外部通过 new 创建对象:内部类只能够借助外部类后再利用 new 创建对象

      public static void main(String[] args){
      MyTest myTest = new MyTest(); // 外部类
      InnerClass innerClass = myTest.new InnerClass(); // 内部类只能够借助外部类创建对象
      StaticInnerClass staticInnerClass = new StaticInnerClass(); // 静态内部类直接创建对象
      }

静态导包

初始化顺序

  • 初始化优先级排序:

    • 父类静态代码块、静态变量
    • 子类静态代码块、静态变量
    • 父类实例变量、普通代码块
    • 父类构造方法
    • 子类实例变量、普通代码块
    • 子类构造方法
  • 细节:

    • 静态修饰的内容的初始化顺序取决于声明的顺序;实例变量和普通代码初始化顺序也取决于声明的顺讯
    • 如果静态变量是引用类型:那么将会 完整地初始化 引用的对象,而不是仅执行静态代码块、静态变量
  • 例子:尝试判断一下下面所有的语句执行顺序

    class Father{
    {
    System.out.println("父类代码块初始化...");
    }

    static {
    System.out.println("父类静态代码块初始化...");
    }

    public Father()
    {
    System.out.println("父类初始化...");
    }
    }

    class Brother{
    public Brother()
    {
    System.out.println("兄弟类被初始化...");
    }
    }

    class Son extends Father{

    private static Brother brother = new Brother();

    {
    System.out.println("子类代码块初始化");
    }

    static {
    System.out.println("子类静态代码块初始化...");
    }

    public Son() {
    System.out.println("子类初始化...");
    }

    }

final

不可变变量

  • 定义:采用关键字 final 修饰的变量

  • 特点:不可以对被修饰的变量进行 任何的修改(常量值)

    • 如果不可变变量是 ==基本数据类型==:不可以进行任何修改

    • 如果不可变变量是 ==引用类型==:引用不可以再指向其他的对象,对象本身是可以修改的

      final int constant = 0;
      final MyObject constantRef = new MyObject();/* constantRef 不可以指向其他对象了,但是依然可以调用类中的方法对对象进行修改 */
  • 分类: 根据常量赋值的不同时期进行区分

    • 编译时常量:定义常量时赋予其固定值

      注:编译时常量在 编译阶段设置默认值,在连接过程的准备阶段赋值JVM 相关)

      final int constant = 0; // 编译时常量
    • 运行时常量:定义常量时 调用方法赋值 或者 构造方法 中赋值

      (1) 等待方法被调用或者对象被初始化时才可以赋值

      (2) 利用构造器初始化常量值,该常量也被称为空白 final

      final int number = new Random().nextInt(); // 等待随机方法被调用时才会真正为常量赋值:不会在类加载阶段就赋值,而是等到运行时才会

      class MyObject{
      private final int constant;
      public MyObject(){
      constant = 120;
      }
      }
    • 细节:

      (1) 无论常量在什么时候赋值,在使用常量之前 必须被初始化并且赋值,否则编译报错

      final int consVal; // 这种情况是不被编译器允许的:必须赋值

      (2) 常量值既不属于 当前 类也不属于对象,属于运行该类方法的主类JVM 相关)

      class ConstantClass
      {
      // 注意:这里是公共属性
      public final static constantValue = "Hello World";
      }

      public class NotInitializationClass
      {
      public static void main(String[] args)
      {
      // 引用类的常量值
      System.out.println(Constant.constantValue);
      // 编译阶段通过常量传播优化,常量值并没有存储在 ConstantClass 的常量池中,而是存储在 NotInitializationClass 类中
      }
      }
  • 静态常量:

    • 定义:static final 共同修饰的变量

    • 特点:普通常量值属于每个对象,静态常量仅属于类 / 每个对象的普通常量值都可以不同,每个对象的静态常量值一定相同

      原因:静态变量在类加载阶段就已经被初始化完成了,但是对象还没有初始化所以所有的对象的拥有的静态常量一定相同(即使采用 random() 方法)

  • 访问权限:

    • private 修饰的常量:仅可以在类的内部使用或者外部利用公共方法调用
    • public 修饰的常量:这种常见于工具类中,便于其他的类使用(这种更加常见)
  • 细节:编译时静态常量的命名方式必须采用 全部字母大写下划线隔开 的方式

    final static int CONSTANT_VALUE = 10;

不可变方法

  • 定义:采用关键字 final 修饰的方法

  • 特点:

  • 细节:

    • private 方法被隐式地指定为 final 方法

      解释:子类中定义的方法和父类中的一个 private 方法签名相同,那么子类方法不是重写父类方法,而是在子类中定义了一个新的方法

    • 方法中的形式参数也可以采用 final 修饰,相当于传入一个常量:常用于向匿名内部类传递参数

不可变类

如果某个类被 fianl 修饰,但是我们依然需要 使用 这个类中的所有方法,那么应该怎么实现?

重排序

Object

  • 定义:==所有类的父类==:无论是自定义类还是库中提供的类都会继承它

  • 工具类:Objects

  • 方法

    • equals()

      (1) 采用等号比较对象是否相同:实际比较的是两个对象的地址是否相同,大多数情况都是不相同的,一般需要重写

      (2) 调用方法的对象可能为空:Objects 中提供了 equals 方法用于比较两个对象,两个对象都可以为空

      public boolean equals(Object obj) {
      return (this == obj);
      }
      /*Objects 提供的方法 */
      public static boolean equals(Object a, Object b) {
      return (a == b) || (a != null && a.equals(b));
      }
    • hashCode

      (1) 计算哈希值的方法是本地方法:各种哈希表和类基本都是重写了这个方法

      (2) 重写 hashCode() 方法时通常需要重写 equals(),否则很容易带来不一致的问题(详情见哈希表)

      (3) 等价的两个对象散列值一定相同,但是散列值相同的两个对象不一定等价

      public native int hashCode(); 		
    • notify() & notifyAll() & wait()

      public final native void notify(); 	// 唤醒线程池中的某个线程

      public final native void notifyAll(); // 唤醒线程池中的所有线程

      public final void wait() throws InterruptedException { // 让某个线程进入线程池等待
      wait(0);
      }
    • clone

      protected native Object clone() throws CloneNotSupportedException;

枚举

Author: Fuyusakaiori
Link: http://example.com/2021/09/12/java/面向对象/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.