面向对象
是人类解决问题的一种方式、主要采用封装、继承、多态、接口等手段实现、可以降低应用之间耦合度!
构成面向对象的三大基石 是 封装、继承、多态!
包 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 才会自动提供一个 无参公开的构造方法。
有参构造方法 可以 给 属性 赋值
方法重载
在一个类中,拥有多个 具有 方法名相同的 方法、这些方法名相同的方法 就被称为 方法的重载!
方法的重载可以让一个类提供的功能多样化~
方法重载的原则
- 方法名必须相同
- 参数列表不同 (个数不同、 或者 类型不同)
this 关键字
this 代表当前调用者
- this 调用 属性
- this 调用 方法
- this 调用 构造方法
一个类中可能会定义多个构造方法、可以在构造方法中使用 this 调用 其他构造方法。
this在调用构造方法的时候,必须作为 构造方法的第一个语句!
代码块
类中可以直接定义代码块, 代码块的职责是做对象的初始化工作~, 在构造方法之前执行
对象的创建过程
- 递归的加载 父类中的属性、代码块 和 成员方法
- 分配空间
- 自上而下 依次 加载 类中的 属性 和 代码块、成员方法 (此时的代码块不执行代码、只检查语法)
- 自上而下 执行代码块 和 检查 内存中 的属性 是否有值 、如果 没有值 ,设置默认值
- 调用构造方法、完成对象的创建!
权限修饰符
-
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 修饰的所有信息。
- 递归的加载父类中的 静态属性、静态代码块 、静态方法
- 分配空间
- 自上而下 加载 静态属性 和 静态代码块、静态方法(只加载、不执行)
- 自上而下 初始化属性 和 执行 静态代码块
在静态上下文中不能使用this关键字
内部类
- 成员内部类
在 类里面定义的类 被称为 成员内部类
- 静态内部类
在 类里面定义的静态类 被称为 静态内部类
- 局部内部类
在方法中 声明的 类、被称为 局部内部类
- 匿名内部类
是一个特殊的 局部内部类!
建造者模式
- 在 类中 添加一个 公开静态内部类 Builder
- 在 内部类中,维护一个 外部类的 对象
- 在 内部类中,提供一个 build() 方法、用来返回 外部类的对象
- 在 内部类中,添加 外部类属性 相关的方法、负责给 外部类的 对象 的属性 赋值, 方法返回 内部类对象
- 在 外部类中 添加一个 静态方法 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 (快速重写方法)
- 方法的修饰符 和 父类 保持相同 或者 比父类提供的修饰符 权限更大
- 方法的返回值类型、方法名、参数列表 和 父类保持一致
- 异常的抛出 和 父类保持 相同 或者 比父类 更窄
在重写的方法上,可以添加一个@Override注解、用来检查方法是否符合重写的要求、如果不符合,代码编译失败
final 修饰符
- 属性
一旦一个属性被 final 修饰,那么这个属性就是一个常量。
final 如果修饰 基本数据类型, 代表 变量 指向的 值 不可改变!
final 如果修饰 引用数据类型, 代表 变量 指想的 地址 不可改变 !
如果一个属性 被 final 修饰 , 必须赋初值,且只能赋值一次。
final修饰的属性 赋值的位置有三个地方 a) 声明的时候赋值, b) 在代码块中赋值, c) 在构造方法中赋值
常量推荐使用全大写、多个单词使用下划线分割
- 方法
一个方法如果被final修饰,那么表示该方法是一个最终方法, 不允许被子类重写
- 类
一个类如果被final修饰,那么表示该类是一个最终类、不允许被其他类继承。
JDK内置了大量的 final修饰的类,例如 String , Math , 包装类 …
- 局部变量
final 修饰局部变量,可以不需要赋初值,在使用前 赋值 即可!
多态
将一个子类对象 赋值给 父类的引用,调用父类中提供的方法, 显示的是子类的效果,这种现象称为 多态。
多态的实现手段 是 方法的重写。
密封类 sealed
JDK17 新特性, 允许指定的类 继承 目标类, 其他类均不能继承 ~
密封类 在 定义的时候,必须明确指明哪些类可以继承类。
密封类 要求允许的子类 必须是 final修饰的类 或者 子类也是一个密封类。
抽象类 abstract
当一个类中 提供的方法的实现,所有的子类 都无法直接使用,那么该方法的具体实现就没有任何的意义,
此时可以将方法的实现去掉,形成一个抽象方法(没有实现体的方法)。抽象方法使用 abstract关键字修饰。
如果一个类中 有抽象方法,那么这个类也必须是一个抽象类!
抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
抽象类的特点
- 使用 abstract 修饰类
- 方法中可以包含抽象方法
- 有构造方法但不能创建对象
抽象方法的特点
- 使用 abstract 修饰方法
- 没有方法实现体
- 子类 必须重写 父类的 抽象方法 或者 子类也是一个抽象类。
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 中的接口
- 允许定义私有的成员方法
接口和抽象类的区别
- 接口使用 interface 修饰, 抽象类使用 abstract 修饰
- 接口和类采用 多实现、接口和接口采用 多继承, 而 抽象类 采用单继承
- 接口中没有构造方法、抽象类中有构造方法,但不能创建对象
- 接口中的属性都是 公开静态常量, 而抽象类中的属性没有要求
- 接口中的方法在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() : 收集流中的数据、并组装返回一个新的指定格式的数据