JAVA面向对象三大特征:封装、继承、多态
自动类型转换:类型范围小的变量,可以直接赋值给类型范围大的变量。

在表达式中,小范围类型的变量会自动转换成当前较大范围的类型再运算。表达式的最终结果类型由表达式中的最高类型决定。

Note:在表达式中,byte,short,char 是直接转换成 int 类型参与运算。
当需要把大范围变量赋值给小范围变量时,需要使用强制类型转换。
Note:
数组的静态初始化写法:
// 完整写法
int[] ages = new int[]{
1, 2, 3};
double[] agess = new double[]{
1.0, 2.0, 3.0};
// 简化写法
int[] ages = {
1, 2, 3};
double[] agess = {
1.0, 2.0, 3.0};
数组变量名中存储的是数组的地址,属于 引用类型:

直接通过索引访问即可:

访问数组长度:数组名称.length()
定义数组类型的时候,只定义数组的长度,之后再存入数据。

Java 中的内存分配(先看这三个):
.class 字节码文件加载时进入的内存。


方法在没有被调用时,在 方法区 的字节码中存放。
方法在被调用的时候,需要进入到 栈内存 中运行。

在传递实参给方法的形参时,并不是传输实参变量本身,而是传输实参变量中存储的值,这称为 值传递。
() 中所包含的参数。
引用类型变量传递的是一个地址值。

基本类型与引用类型参数传递的异同:
- 都是值传递;
- 基本类型传输的是数据值;
- 引用类型传输的是地址值。
同一个类 中,出现多个方法名称相同,但是形参列表是不同的,那么这些方法就是 重载方法。
形参列表不同:形参的 个数、类型、顺序 不同,不关心形参的名称。
优点:对于相似功能的业务场景:可读性好,方法名称相同提示是同一类型的功能,通过形参不同实现功能差异化的选择。
可变参数:
数据类型...参数名称
可变参数在方法内部其实就是一个数组。

先定义类,再通过类 new 出对象。

Q:类和对象是什么?
A:类是共同特征的描述;对象是真实存在的具体案例。
注意:一个 class 文件中,只能有一个类使用 public 修饰,并且该类名称需要作为文件名称。



垃圾回收机制:
当堆内存中的 类对象 或 数组对象,没有被任何变量引用时,就会判定为内存中的“垃圾”。
Java 存在自动垃圾回收器,会定期进行清理。
构造器的作用:用于初始化一个雷的对象,并返回对象的地址。

构造器的分类:

注意:如果写了一个有参构造器,那么需要定义无参构造器,否则无法使用无参构造器。
this 关键字的作用:出现在成员方法、构造器中代表当前对象的地址,用于访问当前对象的成员变量、成员方法。
在构造器中:

在成员方法中:

封装就是 合理隐藏、合理暴露。
封装的实现步骤:
private 关键字修饰进行隐藏,private 修饰后该成员变量就 只能在当前类中访问。public 修饰的公开的 getter 和 setter 方法,暴露其取值与赋值。优点:
JavaBean:可以理解为实体类,其对象可以用于在程序中封装数据。
标准 JavaBean 必须满足如下要求:
private 修饰;setXXX() 和 getXXX();
static 是什么?
static 是 静态 的意思,可修饰成员变量与成员方法;static 修饰成员变量之后称为 静态成员变量(类变量),修饰方法之后称为 静态方法(类方法);static 修饰的成员变量表示 该成员变量在内存中只存储一份,可以被 共享访问、修改。成员变量可以分为两类:
【static静态成员变量内存图解】
注意:在
User.class加载到方法区时,静态成员变量onLineNumber也同步加载到堆内存,随后才是main方法进入栈内存。

成员方法的分类:
【static静态成员方法内存图解】
注意:方法还是在方法区中,而不会放在堆中。

重点注意:
this 关键字的。类中都是一些 静态方法,每个方法都是以完成一个 共用的功能 为目的,这个类用来给系统开发人员共同使用的。
由于工具类无需创建对象,建议将工具类的构造器进行私有。
代码块是类的 5 大成分之一(成员变量、构造器,方法,代码块,内部类),定义在类中方法外。
在 Java 类下,使用 { } 括起来的代码被称为 代码块 。


继承的好处:提高代码的复用性、增强类的扩展性
继承的设计规范:子类们相同特征(共性属性、共性方法)放在父类中定义,子类独有的属性和方法定义在自己类中。
【继承的内存图解】
无论是父类空间还是子类空间,对外都是一个子类对象。

继承的特点:
为什么不支持多继承?
继承后成员的访问特点:就近原则。那此时如果一定想在子类中使用父类的怎么办?
可以使用 super 关键字,指定访问父类的成员。
在继承体系中,子类出现了和父类中一模一样的方法声明,则称子类这个方法是 重写 的方法。

重要!!@override 重写校验注解:

子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己。





权限修饰符:用来控制一个成员能够被访问的范围。
可以修饰 成员变量、方法、构造器、内部类,不同权限修饰符修饰的成员能够被访问的范围将受到限制。

自己定义成员(方法,成员变量,构造器等)一般需要满足如下要求:
final 关键字可修饰类、方法、变量:

常量是使用了public static final 修饰的 成员变量,必须有初始化值,而且执行的过程中其值不能被改变。
常量名的命名规范:英文单词全部大写,多个单词下划线连接起来。
常量的作用:通常用来记录系统的配置数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzQN5pDh-1668166963695)(/Users/mac/Library/Application%20Support/typora-user-images/image-20221107220201446.png)]
枚举是 Java 中的一种特殊类型。作用:做 信息标志 和 信息分类。


在 Java 中,abstract 可修饰类、成员方法,使其变为 抽象类 或 抽象方法。

抽象类的使用场景:
注意:
abstract 修饰变量、代码块、构造器补充:
final与abstract是 互斥关系

接口是用来被类 实现(implements) 的,实现接口的类称为 实现类。实现类可以理解成所谓的子类。

重点注意:
- 类与类:单继承
- 类与接口:多实现
- 接口与接口:多继承
JDK8后的接口类新增方法:



注意事项:
多态:同类型的对象,执行同一个行为,会表现出不同的行为特征。
父类 对象名称 = new 子类构造器();
Animal a = new Dog();
接口 对象名称 = new 实现类构造器();
多态中成员访问特点:
多态只是强调 行为 的多态!

通过强制类型转换可以解决多态的弊端,即可调用子类独有方法。

内部类就是定义在一个类里面的类,里面的类可以理解成寄生,外部类可以理解成宿主。


Q:静态内部类中是否可以直接访问外部类的静态成员?
A:可以,外部类的静态成员只有一份,并且可以被共享访问。
Q:静态内部类中是否可以直接访问外部类的实例成员?
A:不可以的,外部类的实例成员必须用外部类对象访问。
成员内部类:无 static 修饰,属于 外部类对象。

Q:成员内部类中是否可以直接访问外部类的静态成员?
A:可以,外部类 的静态成员只有一份可以被共享访问。
Q:成员内部类的实例方法中是否可以直接访问外部类的实例成员?
A:可以,因为必须 先有外部类对象,才能有成员内部类对象,所以可以直接访问外部类对象的实例成员。

局部内部类放在方法、代码块、构造器等执行体中。
局部内部类的类文件名为: 外部类$N内部类.class。
匿名内部类,本质上是一个没有名字的局部内部类,定义在方法中、代码块等。

包装类是 8 种基本数据类型对应的引用类型。
| 基本数据类型 | 引用数据类型 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| char | Character |
| float | Float |
| double | Double |
| boolean | Boolean |
为什么需要提供包装类?
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量。
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量。


正则表达式爬去信息:

Lambda 表达式是 JDK 8 开始后的一种新语法形式。
作用:简化函数式接口匿名内部类的代码写法。
Lambda 只能简化接口中只有一个抽象方法的匿名内部类形式(函数式接口)。
@FunctionalInterface
interface Swimming{
void swim();
}



集合和数组都是容器。
不适合元素的个数和类型不确定的业务场景,更不适合做需要增删数据操作。
集合非常适合元素个数不能确定,且需要做元素的增删操作的场景。
Collection 单列集合,每个元素(数据)只包含一个值。
Map 双列集合,每个元素包含两个值(键值对)。


集合都是支持范型支持,可以在编译阶段约束集合只能操作某种数据类型。
注意:集合和泛型都 只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是 对象。

Collection 是 单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。


迭代器 在 Java 中代表的是 Iterator,迭代器是集合的专用遍历方式。

如果使用迭代器取元素越界,会出现
NoSuchElementException异常。
既可以遍历集合,也可以遍历数组。

需要注意的是:修改第三方变量的值不会影响到集合中的元素。

有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可重复
ArrayList 底层是基于 数组 实现的:根据索引定位元素快,增删相对慢;
LinkedList 底层基于 双链表 实现的:查询元素慢,增删首尾元素非常快。

// 多态风格,经典代码
List<String> list = new ArrayList<>();
list.add("java");
list.add("java2");
list.remove(1);
list.get(1);
list.set(1, "aaa");
list 集合的遍历方式:
Iterator<String> it = list.iterator();
while(it.hasNext()){
String ele = it.next();
}
for 循环for(String ele : list){
sout(ele);
}
lists.forEach(s -> {
sout(s)
})
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
由于有大量的首、尾操作,可以使用
LinkedList实现队列和栈。
| 方法名称 | 说明 |
|---|---|
| public void addFirst(E e) | 在该列表开头插入指定的元素 |
| public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
| public E getFirst() | 返回此列表中的第一个元素 |
| public E getLast() | 返回此列表中的最后一个元素 |
| public E removeFirst() | 从此列表中删除并返回第一个元素 |
| public E removeLast() | 从此列表中删除并返回最后一个元素 |
特点:
- 无序:存取顺序不一致(但不是随机无序)
- 不重复:可以去重
- 无索引:没有带索引的方法,不能用for循环变量,也不能通过索引取元素
相关实现类:
HashSet 的底层是采取 哈希表 存储数据的。哈希表是一种对增删查改数据性能都比较好的结构。
哈希表的组成:



哈希表的详细流程:
- 创建一个默认长度 16,默认加载因为 0.75 的数组,数组名 table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为 null,如果是 null 直接存入,如果位置不为 null,表示有元素,则调用equals 方法比较属性值,如果一样,则不存,如果不一样,则存入数组
- 当数组存满到 16*0.75=12 时,就自动扩容,每次扩容原先的两倍

Object.hash()参数一样,则生成的哈希值就一样。
特点:有序、无重复、无索引。这里的有序是指保证存储和取出的元素顺序一致。
原理: 底层数据结构仍然是哈希表,但每个元素又额外地多了一个 双链表的机制记录存储顺序。

特点:可排序、无重复、无索引。
可排序:按照元素的大小默认升序(小到大)排列。
TreeSet集合底层是基于 红黑树的数据结构 实现排序的,增删查改性能都比较好。
注意:TreeSet一定是要排序的,可以将元素按照制定规则排序。
数字默认从小到大,字母按照ASCII码从小到大排。

如何自定义排序规则呢?

注意:如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。
Map集合:


特点:

Map是双列集合的祖宗接口,它的功能是全部双列集合都可以继承使用的。

先 keySet() 再 get()。





HashMap的特点和底层原理:


当我们从集合中找出某个元素并删除的时候可能出现一种 并发修改异常 问题。


所以,foreach最好只用来查询,不要进行修改,同样lambda也不行。

哪些遍历存在问题?
- 迭代器遍历集合且直接用集合删除元素的时候可能出现。
- 增强for循环遍历集合且直接用集合删除元素的时候可能出现。
那种遍历且删除元素不出现问题?
- 迭代器遍历集合但是用 迭代器自己的删除方法 操作可以解决。
- 使用for循环遍历并删除元素不会存在这个问题(需要从后往前遍历)。
泛型:是 JDK5 中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>; 注意:泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
泛型的原理:把出现泛型变量的地方全部替换成传输的真实数据类型。
优点:

泛型方法:定义方法时同时定义了泛型的方法就是泛型方法。
泛型方法的格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}


使用了泛型定义的接口就是泛型接口。
泛型接口的格式:修饰符 interface 接口名称<泛型变量>{}
作用:泛型接口可以让实现类选择当前功能需要操作的数据类型。



go 方法参数改成 ArrayList 也不行!需要改成这样!!!

不可变集合:不可被修改的集合。
**集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变。**否则报错。
什么时候需要不可变集合?

List<Double> lists = List.of(69.5, 99.1);
在 Java 8 中,得益于 Lambda 所带来的函数式编程, 引入了一个全新的 Stream 流概念。
目的:用于简化集合和数组操作的API。
Stream流式思想的核心:
Stream流的三类方法:

Collection<String> list = new ArrayList<String>;
Stream<String> s = list.stream();
Map<String, Integer> map = new HashMap<>();
// 键流
Stream<String> keyStream = map.keySet().stream();
// 值流
Stream<String> valueStream = map.valueSet().stream();
// 键值对流
Stream<Map.Entry<String, Integer>> = map.entrySet().stream();
String[] names = {
"1", "2"};
Stream<String> nameStream = Arrays.stream(names);
//或
Stream<String> nameStream2 = Stream.of(names);

list.stream().filter(s -> s.startWith("张")).forEach(s -> sout(s));

收集Stream流:就是把 Stream 流 操作后的结果数据转回到集合或者数组中去。
Stream流:方便操作集合/数组的手段。
集合/数组