Java 基础

Time: 2024-11-02 Saturday 15:34:01
Author: Jackasher

Java 基础

必须要记录一下,不然我发现全部要忘完,我至少要知道我学了什么吧

抽象方法的应用

image-20241024143937749

内部类

内部类会被编译成 AB的类,如果还嵌套就是AB 的类,如果还嵌套就是 AB$C

image-20241024151317531

内部类会有外部类的引用, 反编译后B 里面会有 A 的成员变量,但是静态类的内部类是没有的,

什么时候使用内部类

当我们希望一个类可以访问另一个类的所有属性和方法时,就可以使用, 虽然我也想不到这个的使用场景…

匿名内部类

匿名内部类的字节码文件是 A$1

image-20241024155227530

接口

image-20241024155732849

image-20241024155847326

太厉害了这老师,讲接口的变量命名规范时, 指出 JDK 的错误, 于是有了以下的代码

1
2
3
4
5
6
7
public final static Color red       = new Color(255, 0, 0);

/**
* The color red. In the default sRGB space.
* @since 1.4
*/
public final static Color RED = red;

流的三种分类

image-20241024161957219

其实就是字符和字节的输入输出流,大致就是这四种,所有的都是继承改体系`

![image-20241024162146397](/Users/leojackasher/Library/Mobile Documents/com~apple~CloudDocs/Markdown/Java 基础.assets/image-20241024162146397.png)

Writer 是用 Write把信息输出到缓冲流,

如果是 Reader.read(),那么返回的是读到内容(数字), 如果是 Reader.read(char[] c),则返回的是读到的个数, 内容在 char[]里面, Writer.write(),是把内容写到缓冲区里面, 可以放入 char[]数组, 也可以是数字和字符串

image-20241024192021402

总结来说,直接读写,返回的是读取的内容, 到末尾变 0, 如果有缓存数组, 则会把数据读到数组, 返回读到的个数,字符串可以转换为字节数组,中文操作系统默认是 GBK,java 默认是 unicode,

image-20241024201729404

字符流读取时,有的字符是一个字节有的是两个,Reader 怎么知道的

因为编码特点, 如 GBK 的最高位只有汉字是 1, 其他的是 0,所以可以区分,windows10 系统会有一个问题,就是存储猫字符时, 因为开头是 110.就自动就用 utf-8 来存了

image-20241025132104285

字符流的存储

java 使用 unicode 来存的, 存硬盘前需要先查 unicode 再查 GBK 转化

![image-20241025140458838](/Users/leojackasher/Library/Mobile Documents/com~apple~CloudDocs/Markdown/Java 基础.assets/image-20241025140458838.png)

image-20241025140244969

字节流

咋们发现 Reader.read()是只能传 char 的,而 InputStream 是传 byte 的,

1
2
3
4
5
FileReader fileReader = new FileReader("/Users/leojackasher/Downloads/资料 解密JVM/代码/jvm/src/cn/itcast/jvm/t3/candy/a.txt");
int i;
while ((i = fileReader.read()) != -1) {
System.out.print((char) i);
}

Reader.read() 方法返回的是一个 int 类型的数字,表示读取的字符的 Unicode 码点。具体来说,这个返回值有以下含义:

  1. 字符的 Unicode 码点
    • 如果成功读取到一个字符,read() 方法返回该字符的 Unicode 码点,范围在 0 到 65535 之间。
    • 例如,字符 ‘A’ 的 Unicode 码点是 65,字符 ‘汉’ 的 Unicode 码点是 27721。
  2. 返回值 -1
    • 如果到达文件的末尾(EOF),read() 方法返回 -1,表示没有更多字符可以读取。
  3. 读入字符和字节的区别
    • Reader 是字符流,因此它处理的是字符,而不是字节。即使内部实现可能会处理字节,read() 方法最终返回的是字符的 Unicode 码点。

InputStream.read() 方法在 Java 中返回的是一个 int 类型的数字,表示读取的字节值。具体来说,它的返回值有以下含义:

  1. 字节值
    • 当成功读取到一个字节时,read() 方法返回该字节的值,范围在 0 到 255 之间。这个值是字节在 8 位中表示的无符号整数。
    • 例如,字节值 65 对应的字符是 ‘A’。
  2. 返回值 -1
    • 如果到达文件的末尾(EOF),read() 方法返回 -1,表示没有更多字节可以读取。
  3. 读入字节和字符的区别
    • InputStream 是字节流,因此它处理的是原始字节,而不是字符。如果要将字节转换为字符,需要使用字符编码进行转换。

为什么不能用字符流读视频图片等文件

因为字符流有一定读取的规则, 而视频图片的二进制有许多无法被解码的数据, 找不到码表就会被解析为?或者方框,当再进行操作时, 这些?就会被解析为 unicode 的数字码点 63, 其实如果字符流就是会对编码的规则进行读取,列如utf-8的汉字就会直接读取三个字节, 然后将其装换为 unicode 的码点, 而音频文件,有可能恰好有符合这些规则的数据,被转为码点,导致数据被改变

重点就在这个解码的过程,如果字符编码和解码正常是可以用字符流的, 重点是编码时会丢失数据

写入时默认会覆盖原文件,可以采用这种方式

1
writer.write("Bye", 0, 3);

为什么快速的 io 操作会非常消耗性能且慢,机械硬盘的指针会在两个方向来回快速动

![image-20241025170433488](/Users/leojackasher/Library/Mobile Documents/com~apple~CloudDocs/Markdown/Java 基础.assets/image-20241025170433488.png)

DateStream(字节过滤流)

就是一次性可以读很多个字节

InputStream.read() 方法返回的是 int 而不是 byte。这样做的原因:

-1 表示文件末尾

  • InputStream.read() 的返回值范围是 -1255,其中 -1 专门用来表示“没有更多字节可以读取”。
  • 如果返回类型是 byte(范围 -128127),-1 就会和读取到的数据冲突,因为实际数据也可能包含 -1

InputSreameReader

这是把字节流转换为字符流,传入字节流和码表就可以

补码

计算机里面只存补码,正数开头是 0,负数开头是 1,负数补码第一位还是 1,计算出来的数据 也是补码

异常

异常分为检查型异常和运行时异常 ,运行时异常会自动转移

线程

线程有两种启动方式,一种是继承 Thread 后, A.start(),一种是实现 runnable 接口,然后 Thread.start( a ),

线程状态

image-20241027123836681

1.新建状态

就是刚创建还没有运行时的状态

2.可运行状态

就是加入线程了, 可以去抢 cpu 了

线程如何停止

采用 interrupted 来为线程设置信号量

image-20241027123622365

为什么要用枚举

为了可读性, Thread.MAX_PROITY 比单单一个数字 1 的可读性好太多了

模版方法和策略模式

突然发现这个线程的开启不就是这两个方法吗, 实现 Runnable 放入 Thread 就是策略模式, 继承 Thread 然后直接调用 start 方法不就是标准的模版方法吗?

代码一定是按照顺序执行的吗?

不是, 会打乱, 但是会保证结果一样

对象锁到底是什么

其实就是一个标记,在字节码层面,有个地方标记,01 就是没锁,10 就是重量锁之类的,

对象头是 JVM 中每个对象都包含的基本部分,包含了管理对象的元数据:

  • Mark Word:用于存储锁状态、对象的哈希码、GC 标记等信息。根据锁的状态(如无锁、偏向锁、轻量级锁、重量级锁),Mark Word 会动态改变其内容。
  • Class Pointer(类指针):指向该对象所属的类的元数据,帮助 JVM 知道该对象的类型,并访问该类型的字段和方法信息。
  • 数组长度(仅适用于数组对象):对于数组对象,头部会额外包含数组的长度信息。

image-20241028115515546

image-20241028121113025

这样的代码也是线程不安全的,问题的根本在于 wait()会释放当前的锁, ,所以这个synchronized 就失效了,没有原子性了,wait 方法 必须在持有对象锁的情况下调用,否则会抛出 IllegalMonitorStateException 异常。这是因为 wait方法的设计要求线程首先获得对象的锁,以便能够协调线程的等待和唤醒操作,为什么要拿到锁,为了释放锁,不然你在那一 wait 就卡死了,

解决方式是判断改成 while

image-20241028162851079

方法锁

在方法前面加 synchronized 就是相当于 synchronized(this){},把整个方法体包起来了

静态锁

静态锁理论上无法直接加 synchronized,通常我们调用方法时,其实实例对象会被传进去,才会有 this,因此可以对象上锁,可是静态调用没有这个对象

在 Java 中,对象调用方法 a.f() 的底层实现确实会将 a 传入 f() 方法。这个过程称为“隐式传递”,且 a 对象在方法内部通过 this 关键字来引用。

具体细节

当调用 a.f() 时:

  1. 隐式传参:编译器在编译时会把 a 作为第一个参数传递给 f() 方法。方法 f() 的参数列表和内容不变,但 a 在方法中作为 this 对象出现。
  2. 字节码实现:在字节码中,Java 使用 invokevirtualinvokespecial 等指令来调用方法。编译时会生成代码,将调用对象(a)作为第一个参数压入栈顶。
  3. 方法签名转换:例如,假设方法 f()void f(int x)。编译后的字节码可以理解为 f(a, x),其中 a 是传入的隐式参数(即 this),在方法中表示当前对象实例。

静态锁,锁的是字节码,字节码也是对象

守护线程

在创建线程的时候指定

StringBuilder 和 StringBuffer

这两个都会直接操作字符串的空间

image-20241028202302379

区别是一个线程安全,一个不安全,StringBuilder 是线程不安全的

![image-20241028202543707](/Users/leojackasher/Library/Mobile Documents/com~apple~CloudDocs/Markdown/Java 基础.assets/image-20241028202543707.png)

线程池

image-20241029154120014

集合

集合的家族

image-20241029160208579

Collection 接口的常用方法

​ 1. add(E e):向集合中添加元素。

​ 2. addAll(Collection<? extends E> c):将指定集合中的所有元素添加到当前集合。

​ 3. clear():清空集合中的所有元素。

​ 4. contains(Object o):判断集合中是否包含指定的元素。

​ 5. containsAll(Collection<?> c):判断集合中是否包含指定集合中的所有元素。

​ 6. isEmpty():判断集合是否为空。

​ 7. iterator():返回一个用于遍历集合的迭代器。

​ 8. remove(Object o):从集合中删除指定的元素。

​ 9. removeAll(Collection<?> c):从集合中删除指定集合包含的所有元素。

​ 10. retainAll(Collection<?> c):保留集合中与指定集合的交集的元素。

​ 11. size():返回集合中元素的个数。

​ 12. toArray():将集合转换为一个数组。

​ 13. toArray(T[] a):将集合中的元素存储到指定类型的数组中。

ArrayList 的方法

ArrayList 实现了 List 接口,继承了 Collection 中的所有方法,并且还提供了以下特定方法:

​ 1. get(int index):返回指定索引处的元素。

​ 2. set(int index, E element):用指定元素替换指定索引处的元素。

​ 3. add(int index, E element):在指定位置插入指定元素。

​ 4. remove(int index):移除指定索引处的元素。

​ 5. indexOf(Object o):返回指定元素首次出现的索引。

​ 6. lastIndexOf(Object o):返回指定元素最后出现的索引。

​ 7. subList(int fromIndex, int toIndex):返回列表中指定范围的视图。

subList 会更改原来的集合

image-20241029183911356

迭代器的 next()其实就是取下一个元素

image-20241029184233508

为什么要用迭代器, 明明有了循环通过下标获取

因为只有 ArrayList 是数组, 可以通过下标,其他的只能通过迭代器

ArrayList 查询原理

ArrayList不是线程安全的

image-20241029185634797

因为要先找,找到 i/2 的内存地址,这个过程很慢, 但是加的过程其实是很快的,list.add()会从 0 到下表点,而 LIstiterator 就可以解决,

image-20241029190600161

自己设计一个栈

image-20241029194232329

Set

HashSet无论有多少数据,花费寻找时间都是一样的

image-20241030132110144

HashMap就是一个LinkedList的数组,存数据时,先计算HashCode,然后看有没有桶位,没有的话就进行对比, 桶位里面有没有重复的值

image-20241030151144073

TreeSet

TreeSet可以直接排序,字符串是比第一个字母

HashSet的实现

image-20241030200548129

对象的比较

![image-20241030160028661](/Users/leojackasher/Library/Mobile Documents/com~apple~CloudDocs/Markdown/Java 基础.assets/image-20241030160028661.png)

假设有一个Person类,包含nameage属性,并希望按年龄从小到大排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

public class Person implements Comparable<Person> {
private String name;
private int age;

public Person(String name, int age) {
this.name = name;
this.age = age;
}

// 实现 compareTo 方法
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}

@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + '}';
}

// Getters
public String getName() {
return name;
}

public int getAge() {
return age;
}
}

在这里,compareTo方法根据age进行比较:

  • 若当前对象age小于参数对象age,则返回负数;
  • 若相等,返回0;
  • 若大于,返回正数。

使用示例

可以使用Collections.sortPerson对象的列表进行排序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));

Collections.sort(people); // 使用 compareTo 方法进行排序

System.out.println(people); // 输出按年龄排序的结果
}
}

这就是面对扩展开发,修改关闭,TreeSet的作者留了,Comparator的接口,

image-20241030161545606

例如按照字符串长度比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.sql.Connection;
import java.util.*;

/**
* @author Jackasher
* @version 1.0
* @className two
* @since 1.0
**/
public class two {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
String a = "awerwe";
String b = "bwe";
String c = "awee";
String d = "ae";

treeSet.add(a);
treeSet.add(b);
treeSet.add(c);
treeSet.add(d);

System.out.println(treeSet);


}
}

为什么要使用迭代器

image-20241030211057200

集合工具类

这个是只读集合

1
List list = Arrays.asList(1,2,3,4)

可以这样做

1
List list = new ArrayList(Arrays.asList(1,2,3,4))

如果要交换集合的两个值?

1
Collection.swap(list, index_1, index_2)

For加强

只有实现了Iterator的类才可以使用For加强循环,原来迭代器要实现hasNext和next的方法

image-20241030220359613

可变参数

可变参数就是类型后面加… 本质就是数组,可变参数必须放在最后面,因为不然有歧义,放前面,不知道第几个参数是第二个行参

反射

image-20241031131254011

image-20241031135514792

反射可以无视private

image-20241031135816304

image-20241031140812787

注解

用@interface定义注解

1
2
3
4
5
6
7
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name(); // 必填属性
int age() default 18; // 带默认值的属性
}

套接字


Java 基础
http://example.com/2024/11/02/Java 基础/
作者
Jack Asher
发布于
2024年11月2日
许可协议