面相对象入门介绍

本文介绍了Java面向对象编程的核心概念,包括封装、继承、多态、类的组成(如包、成员变量、构造方法、方法重载、静态内容),以及Lombok工具、继承与多态的深入解析,还有Java的接口、函数式接口和流式处理Stream的相关知识。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象

是人类解决问题的一种方式、主要采用封装、继承、多态、接口等手段实现、可以降低应用之间耦合度!
构成面向对象的三大基石 是 封装、继承、多态!

包 package

包 是用来管理 代码的 文件夹,
包名是标识符、必须遵循Java标识符命名规范。
多级包 以 . 分割、且 包名全小写。
包名 不能 为 java (sun公司为了区分SDK, 不允许开发人员使用 java 作为包名) 。
包名往往使用 企业 域名 倒序 作为 包的名称,例如 www.qikux.com, 那么包名可以命名为 com.qikux 。
.com : 企业的标识, .cn : 国家级域名 , .org: 开源组织 , .edu (教育机构)。
包是类的重要组成部分、一个类的完整名字是 包名.类名

  • java.lang : 是JDK的基础包、该包下面的类 在使用的时候,不需要做导入操作。但 java.lang.xxx 下的类使用的话需要导入!
  • java.util : 是JDK官方提供的工具包、里面包含大量的工具(赋值)类,例如 Scanner, Arrays, Math 这些类都在该包
  • java.io : 是 JDK官方提供的和 流 相关的包
  • java.net : 是 JDK官方提供的 和 网络编程 相关的包
  • java.sql : 是 JDK官方提供的 和 数据库操作 相关的包
  • java.time : 是 JDK官方提供的 和 新版 日期 操作 相关的包

类 class

类是抽象的、是自然界中具有相同特征和行为的事物的统称~
在类中 使用 属性 来描述 类的特征。属性是定义在类中的变量~

对象

万物皆为对象!
类是对象的模板,对象是类的具体实现!一个类型可以拥有无数个对象

创建对象

创建对象 使用 new 关键字

成员变量

成员变量 就是 定义在类中的属性。
成员变量 如果在声明的时候没有给值,则创建完对象会自动赋默认值。
成员变量的作用范围 是整个类

构造方法

作用: 创建对象,构造方法通过 new 关键字来调用
特点: 1. 方法名和类名完全相同 , 2. 没有返回值(不能写 void)
当一个类中没有提供任何的构造方法的时候,JVM 才会自动提供一个 无参公开的构造方法。
有参构造方法 可以 给 属性 赋值

方法重载

在一个类中,拥有多个 具有 方法名相同的 方法、这些方法名相同的方法 就被称为 方法的重载!
方法的重载可以让一个类提供的功能多样化~

方法重载的原则

  1. 方法名必须相同
  2. 参数列表不同 (个数不同、 或者 类型不同)

this 关键字

this 代表当前调用者

  • this 调用 属性
  • this 调用 方法
  • this 调用 构造方法

一个类中可能会定义多个构造方法、可以在构造方法中使用 this 调用 其他构造方法。
this在调用构造方法的时候,必须作为 构造方法的第一个语句!

代码块

类中可以直接定义代码块, 代码块的职责是做对象的初始化工作~, 在构造方法之前执行

对象的创建过程

  1. 递归的加载 父类中的属性、代码块 和 成员方法
  2. 分配空间
  3. 自上而下 依次 加载 类中的 属性 和 代码块、成员方法 (此时的代码块不执行代码、只检查语法)
  4. 自上而下 执行代码块 和 检查 内存中 的属性 是否有值 、如果 没有值 ,设置默认值
  5. 调用构造方法、完成对象的创建!

权限修饰符

  • public (公开的)

  • protected (受保护的)

  • default / friendly (缺省的/友好的)

  • private (私有的)

修饰符的使用位置

修饰符内部类属性方法
public
protected×
default
private×

修饰符的权限范围

修饰符同包子类本类其他
public
protected×
default××
private×××

封装

将 自然界中某种类型的 特征 和 行为 定义到一个类中, 并将属性私有化,公开方法。

Object类中定义的方法

Object类是Java语言提供的一个表示对象的类。
Object 类是 Java语言中 所有类 的 父类,Object类中定义的公开、受保护方法,所有的子类 都可以使用 ~

  • toString()

将一个对象 使用 字符串的形式表示 , 默认采用 类名 + 地址 形式表示
打印一个非空对象,默认会自动 toString() 方法~

  • getClass()

获取一个对象的类型(类对象),类对象 表示的是一个类型的对象,类型的对象使用 Class 类表示

  • hashCode()

获取一个对象的 hash (采用 hash算法计算得到的) 值, 返回一个 int

  • equals()

用来比较两个对象内容是否相等, 但 Object 提供的 equals 默认比较的是两个对象的地址是否相等

  • Objects.equals(a, b) : 比较 两个对象 a, b 内容是否相等,解决 空指针问题

类的组成

  • 代码块
  • 构造方法
  • 属性(成员变量)
  • 成员方法

类加载

当 JVM 第一次 读取一个类的时候,将类的字节码文件内容读取到内存的过程被称为 类加载 ,
类加载的产物 是 类对象。 一个类有且只有一个类对象,所以类对象 可以用 == 进行比较

static 修饰符

static 关键字修饰的内容 和 类 有直接关系。

  • 静态代码块

给一个类做初始化工作的,发生在 类加载的阶段 ,一个类只会被JVM 加载一次

  • 静态属性

静态属性 对 该类型的所有 对象是共享的、静态属性 通过 类名 进行调用,不推荐使用对象 调用

  • 静态方法

静态方法 由 类名 进行调用, 不推荐 使用对象调用。静态方法没有多态~

类加载的过程

类加载的过程 其实就是加载 static 修饰的所有信息。

  1. 递归的加载父类中的 静态属性、静态代码块 、静态方法
  2. 分配空间
  3. 自上而下 加载 静态属性 和 静态代码块、静态方法(只加载、不执行)
  4. 自上而下 初始化属性 和 执行 静态代码块

在静态上下文中不能使用this关键字

内部类

  • 成员内部类

在 类里面定义的类 被称为 成员内部类

  • 静态内部类

在 类里面定义的静态类 被称为 静态内部类

  • 局部内部类

在方法中 声明的 类、被称为 局部内部类

  • 匿名内部类

是一个特殊的 局部内部类!

建造者模式

  1. 在 类中 添加一个 公开静态内部类 Builder
  2. 在 内部类中,维护一个 外部类的 对象
  3. 在 内部类中,提供一个 build() 方法、用来返回 外部类的对象
  4. 在 内部类中,添加 外部类属性 相关的方法、负责给 外部类的 对象 的属性 赋值, 方法返回 内部类对象
  5. 在 外部类中 添加一个 静态方法 builder() , 返回一个 内部类对象

Lombok

是一个第三方工具类、非官方自带的。在使用前,需要将该依赖包下载并导入到项目中

  • 下载: https://projectlombok.org/downloads/lombok.jar
  • 将 jar 添加到工程中

在项目根下,新建一个 文件夹 lib (项目 -> 右键 -> new -> Directory)
将 lombok.jar 拷贝到 该 新建的文件夹下

  • 将 lib 文件夹作为 jar 库

选中 lib 文件夹 右键 -> add as Lib…

Lombok 常见的注解

  • @Getter

用来生成 属性的 get方法, 如果写在类上,那么代表 给 所有的属性(不包含静态属性和常量属性)生成 get方法.
该注解 还可以写在 某一个具体的属性上, 代表 只给 这个属性 生成 get 方法

  • @Setter

用来生成 属性的 set 方法 、用法参考 @Getter

  • @ToString

用来生成 toString() 方法

  • @EqualsAndHashCode

用来生成 equals 和 hashCode 两个方法

  • @Data

作用是 @Getter, @Setter, @ToString, @EqualsAndHashCode 四个注解的整合 ,该注解是一个复合注解
也可以和 @RequiredArgsConstructor 注解整合,但如果 使用了 @Builder 注解,则 @RequiredArgsConstructor无效

  • @NoArgsConstructor

用来生成无参构造

  • @AllArgsConstructor

用来生成包含所有属性的构造方法, 生成的构造方法参数顺序 和 属性定义的顺序 相关

  • @RequiredArgsConstructor

配合 @NonNull 注解 来生成 指定参数的构造方法、 @NonNull 是标记在属性上的注解,标记该属性必传

  • @Builder

快速给类添加一个建造者、该建造者 会使用 类的 带有全部参数的构造方法,所以必须确保该类提供了包含全部参数的构造方法

通常在 Pojo类上,提供 @Data, @Builder, @NoArgsConstructor, @AllArgsConstructor 四个注解即可

继承

继承体现了程序的可扩展性

一个类可以通过 extends 关键字 继承 另一个类,让类与类之间形成 父子关系 , 子类 可以继承 父类的属性和方法。
父类 用来抽取所有子类的共性,子类负责定义 特性。在子类中可以直接调用 能被继承的 父类中定义的 属性和方法。

Java中的继承采用单继承、形成树状结构、Object就是所有Java中类型的父类

super

当前调用者的父类对象

  • super 调用 父类 属性
  • super 调用 父类 方法
  • super 调用 父类 构造方法

构造方法默认的第一行是super(),通常会被省略~

方法的重写(覆盖)

当父类提供的方法无法满足子类的需求、那么子类可以重写父类的方法,这种现象被称为 方法的重写。
重写的前提是 父类的方法子类能够被继承!

方法重写的原则 CTRL + O (快速重写方法)
  1. 方法的修饰符 和 父类 保持相同 或者 比父类提供的修饰符 权限更大
  2. 方法的返回值类型、方法名、参数列表 和 父类保持一致
  3. 异常的抛出 和 父类保持 相同 或者 比父类 更窄

在重写的方法上,可以添加一个@Override注解、用来检查方法是否符合重写的要求、如果不符合,代码编译失败

final 修饰符

  • 属性

一旦一个属性被 final 修饰,那么这个属性就是一个常量。
final 如果修饰 基本数据类型, 代表 变量 指向的 值 不可改变!
final 如果修饰 引用数据类型, 代表 变量 指想的 地址 不可改变 !
如果一个属性 被 final 修饰 , 必须赋初值,且只能赋值一次。

final修饰的属性 赋值的位置有三个地方 a) 声明的时候赋值, b) 在代码块中赋值, c) 在构造方法中赋值
常量推荐使用全大写、多个单词使用下划线分割

  • 方法

一个方法如果被final修饰,那么表示该方法是一个最终方法, 不允许被子类重写

一个类如果被final修饰,那么表示该类是一个最终类、不允许被其他类继承。
JDK内置了大量的 final修饰的类,例如 String , Math , 包装类 …

  • 局部变量

final 修饰局部变量,可以不需要赋初值,在使用前 赋值 即可!

多态

将一个子类对象 赋值给 父类的引用,调用父类中提供的方法, 显示的是子类的效果,这种现象称为 多态。
多态的实现手段 是 方法的重写。

密封类 sealed

JDK17 新特性, 允许指定的类 继承 目标类, 其他类均不能继承 ~
密封类 在 定义的时候,必须明确指明哪些类可以继承类。
密封类 要求允许的子类 必须是 final修饰的类 或者 子类也是一个密封类。

抽象类 abstract

当一个类中 提供的方法的实现,所有的子类 都无法直接使用,那么该方法的具体实现就没有任何的意义,
此时可以将方法的实现去掉,形成一个抽象方法(没有实现体的方法)。抽象方法使用 abstract关键字修饰。
如果一个类中 有抽象方法,那么这个类也必须是一个抽象类!
抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类

抽象类的特点

  1. 使用 abstract 修饰类
  2. 方法中可以包含抽象方法
  3. 有构造方法但不能创建对象

抽象方法的特点

  1. 使用 abstract 修饰方法
  2. 没有方法实现体
  3. 子类 必须重写 父类的 抽象方法 或者 子类也是一个抽象类。

abstract 和 final 能同时修饰 类吗?

不能,abstract修饰的类不能被创建对象,需要子类来构建对象,final 修饰的类不允许有子类,二者冲突

abstract 和 final 能同时修饰 方法吗?

不能,abstract修饰的方法是抽象方法,要求子类必须重写、而 final修饰的方法不允许被任何子类重写,二者冲突

abstract 和 static 能同时修饰 方法吗?

不能,abstract 修饰的方法是抽象方法、要求子类必须重写,重写是多态的实现手段,而 static 修饰的方法不能重写且没有多态。

abstract 和 static 能同时修饰 内部类吗?

可以

static 和 final 能同时 修饰 方法 吗?

可以

接口 interface

是一种标准,一种规范!
降低程序耦合度、进行解耦合操作。

  • 使用 interface 修饰
  • 接口中所有的属性 都是 公开静态常量
  • 接口中所有的方法都是公开抽象方法(JDK7)
  • 接口与类之间采用多实现,关键字 implements
  • 接口与接口之间采用多继承, 关键字 extends
  • 接口中没有构造方法

JDK官方内置接口

  • Comparable : 两个对象比较大小的接口, 让某个类型实现该接口,那么该类型的对象就拥有了比较的能力

  • Comparator : 是一个比较器,可以用来比较两个对象的大小

  • Comparator.naturalOrder(): 返回一个比较器,按照对象的默认比较规则进行比较

  • Comparator.reverseOrder() 返回一个比较器, 比较规则 是和 默认规则 相反
    上述两个方法要求 比较的对象 所在的类必须实现 Comparable 接口

  • Objects.compare(obj1, obj2, Comparator) : 用来比较两个对象大小

  • Comparator.nullsFirst(Comparator)/Comparator.nullsLast(Comparator) : 如果 比较器在 比较 两个对象大小的时候,其中某个对象可能存在 null, 建议 将比较器进行 空值处理

  • Iterable : 可迭代的, 能够让一个类 拥有可迭代(遍历)的能力

  • Iterator : 是一个迭代器,负责迭代数据 , 该类中由 hasNext()方法,负责判断是否有下一个数据, next() 方法,获取下一个数据,并移动指针

一个类一旦 实现了Iterable接口,那么这个类就拥有 可迭代的能力,该类会重写 Iterable接口中的 iterator方法,返回一个迭代器。
由迭代器负责如何迭代数据。 同时 该类 拥有 增强 for 循环的能力

实现了 Iterable 接口的类 拥有的遍历方式
public class VarIntArray implements Iterable<Integer>{

    private int[] array = {10 ,2 , 3, 5, 10, 34} ;
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            // 记录 迭代的数据个数
            private int index = 0 ;
            /**
             * 判断是否有下一个数据
             */
            public boolean hasNext() {
                return index < array.length;
            }

            /**
             * 获取下一个数据
             */
            public Integer next() {
                return array[index++];
            }
        };
    }
}

  • 迭代器遍历
Iterator<T> it = xxx.iterator(); 

while(it.hasNext()) {
    T t = it.next(); 
    ....
}

  • 增强 for循环遍历数据

一个类只要实现 Iterable接口,那么就可以使用 增强for循环遍历数据

  • forEach(Consumer)方法遍历数据

一个类只要实现 Iterable 接口,那么就可以使用 forEach() 遍历数据, 并消费每一个迭代的数据。

匿名内部类

快速实现一个类的子类的一种有效手段 , 是一种实现 动态类定义的方式

语法:

类 变量名 =  new 类() {
     
} ;

JDK 8 中的接口

  • 允许定义公开静态方法

  • 新增了 默认方法

JDK 9 中的接口

  • 允许定义私有的成员方法

接口和抽象类的区别

  1. 接口使用 interface 修饰, 抽象类使用 abstract 修饰
  2. 接口和类采用 多实现、接口和接口采用 多继承, 而 抽象类 采用单继承
  3. 接口中没有构造方法、抽象类中有构造方法,但不能创建对象
  4. 接口中的属性都是 公开静态常量, 而抽象类中的属性没有要求
  5. 接口中的方法在JDK7版本中 都是公开抽象方法,JDK8版本中允许定义公开静态方法、默认方法,JDK9版本中允许定义私有方法。抽象类方法没有要求

函数式 接口

一个接口中 包含的 有且只有一个抽象方法 (不包含Object父类中声明的方法),那么这样的接口就被称为函数式接口~
函数式接口的声明上,可以添加一个 @FunctionalInterface 注解 ,用来进行 函数式接口的语法检查~

函数式接口的子类对象的创建方式
  • lambda 表达式

  • 方法引用

lambda 表达式

作用: 快速构建一个 函数式接口的 子类对象

语法:

(参数列表) -> { 方法具体实现 } 

(参数列表) : 函数式接口中的 抽象方法的参数列表, 参数列表中的数据类型可以省略,如果只有一个参数,可以省略 () ;
{方法具体实现} : 子类重写 接口中的抽象方法的具体实现代码, 如果实现体中有且只有一行代码,可以省略 {} ,
在省略 {} 之后,如果这个语句包含 return ,则 必须省略 return

方法引用

作用: 快速构建一个 函数式接口的 子类对象。
方法引用 指的是 使用一个已存在的方法的具体实现 来 作为 接口中 抽象方法的实现。
和 lambda 表达式作用是相同的,lambda表达式是由调用者自己去实现对应的方法,而方法的引用是 用用 官方/第三方 写好的方法实现的效果。

使用方法引用 只需要找到 和 函数式接口中抽象方法参数相同,且返回值类型相同的方法 即可引用,

  • 引用 静态方法

  • 引用 成员方法
    a) : 用对象引用方法

b) : 用类 引用方法 --> 当用一个类去引用一个成员方法的时候,我们可以认为此时类中的成员方法 第一个参数 是 类 的对象

class Animal:

    def eat(self):

anl =  new Animal();

anl.eat();

Animal.eat(anl);
  • 引用 构造方法

函数式接口的种类

  • 消费型接口 Consumer , BiConsumer

接口中的抽象方法 有参数、但无返回值 !
消费的具体行为(怎么使用这个参数),不由接口来确定,有接口的实现类来确定。

  • 生产型接口 Supplier

接口中的抽象方法 没有参数、但有返回值 !
生产者 只负责返回数据, 不关心数据的使用情况~

  • 功能型接口 Function, BiFunction

接口中的抽象方法 既有参数 又有返回值 (即是生产者又是消费者)

  • 断言型接口 Predicate , BiPredicate

接口中的抽象方法 有参数、且返回值是 boolean , 它是一个特殊的 功能型接口

  • 任务型接口 Runnable

流式处理 Stream

主要负责处理数据 、Java中的Stream流式处理采用单项数据流。
流由三部分组成 开始流、中间流、终止流~
流在处理的数据的过程中,采用一个原则,不影响原数据!

开始流

如何将数据以流的形式表示!

  • 将数组以 流的形式表示
// 使用 数组工具类 将一个数组转成 流 
Arrays.stream(array) 

// 使用 Stream 流 的 of 方法 将数组转成 流 

Stream.of(val...) ;

中间流

中间流是主要处理数据的流!
中间流 返回的结果 仍旧是一个流,每次返回一个新的流。
中间流 只有在调用 终止流的时候才会 执行,

  • filter(Predicate predicate) : 根据条件过滤数据,返回满足条件的数据, 数据不变,个数会减少
  • map(Function function) : 实现数据的映射、最终数据的个数不会改变
  • distinct() : 去重、
  • skip(n) : 跳过 n 个数据, 和 limit(maxSize) 配合实现数据分页查询
  • limit(maxSize) : 限制最大的数据个数
  • sorted() : 进行排序
  • sorted(comparator) : 传入 比较器 进行排序
  • takeWhile(predicate) : 从流中的第一个元素开始过滤满足条件的元素,一旦不满足条件,立即结束
  • dropWhile(predicate) : 从流中的第一个元素开始删除满足条件的元素,一旦不满足条件,立即结束,用法和 takeWhile刚好相反
  • peek(consumer) : 用来遍历 流中的数据,并消费数据,用法和 forEach类似,但peek是中间流,forEach是终止流
终止流

是用来结束整个流

  • forEach(Consumer consumer) : 遍历流中的数据 并消费数据

  • reduce(BiFunction<T, U, R> function) : 对流中的数据进行统计

reduce方法中的功能性函数 会执行 n - 1 次 (n 代表流中数据的个数)

T : 默认第一次进入 reduce方法代表 流中的第一个数据 , 代表每次 reduce 统计的结果 作为 下一次进入 reduce 时候 T 的初始值。
U : 代表 流中的 从第二个数据之后的每一个数据

  • reduce(init, BiFunction<T, U, R> function) : 对流中的数据进行统计

reduce方法中的功能性函数 会执行 n 次

init : 初始值作为 T 的默认值, 也决定了整个 reduce 的结果类型
T : 默认第一次进入 reduce 代表的是 init的值 。 代表每次 reduce 统计的结果 作为 下一次进入 reduce 时候 T 的初始值。
U : 代表 流中的 从第1个数据之后的每一个数据

  • U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator combiner);

  • count() : 返回流中数据的个数

  • max(Comparator)/ min(Comparator) : 获取流中的最大/小值

  • allMatch(Predicate) : 判断流中的数据是否全部符合条件

  • anyMatch(Predicate) : 判断流中的数据是否有任意一个符合条件

  • findFirst() : 获取流中第一个数据

  • findAny() : 应用于多线程环境下的并发操作

  • toArray() : 将流中的数据以数组的形式表示, 返回一个 Object数组

  • toArray(Integer[]::new) : 返回一个指定类型的数组

  • toArray(len -> new Integer[len]) : 该用法和 toArray(Integer[]::new) 用法完全相同

  • collect() : 收集流中的数据、并组装返回一个新的指定格式的数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值