Yu's Blog

do something!

gn 和 ninja

ninja

  • ninjia是一个致力于速度的小型编译系统工具 (类似于Make编译工具)

gn

  • gn 即 Generate ninjia ,顾名思义就是用来生成ninjia编译文件的工具

gn与ninjia的关系

可以理解为make与cmake之间的关系,ninjia于GUN make构建工具类比对应关系如下:

1
2
3
4
5
6
7
8
9
ninjia工具      Make GUN工具                                    功能作用

gn <----------> cmake -------------> 生成构建文件

xxx.gn <----------> CMakeList --------------> 描述构建所需的编译文件

xxx.ninjia <----------> Makefile ---------------> 描述代码的具体编译步骤

ninjia <----------> Make ----------------> 编译代码

Usage

Java

Java 概述

环境介绍

  • JVM(java virtual machine)
    • 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令、管理数据、内存、寄存器,包含在 JDK 中。
    • 对于不同的平台,有不同的虚拟机。
    • Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。
  • JDK(java development kit, java 开发工具包)
    • JDK = JRE + java 开发工具(java, javac, javadoc, javap 等)
    • JDK 是提供给 java 开发人员使用的,其中包含了 java 的开发工具,也包括了 JRE。
  • JRE(java runtime environment, java 运行时环境)
    • JRE = JVM + java 核心类库
    • 如果想要运行一个开发好的 java 程序,计算机只需要安装 JRE 即可。
  • test.java(源文件) –> 编译 javac –> test.class(字节码文件) –> 运行 java –> JVM

注意事项和细节说明

  • java 应用程序的执行入口是 main() 方法,有固定的书写格式: public static void main(String [] args)
  • 一个源文件中最多只能包含一个 public 类(其他类的个数不限制, 也可以将 main 方法写在非 public 类中,然后制定运行非 public 类),同时文件名必须按该类名命名。
  • 编译后,每一个类都对应一个 .class 文件。

变量

数据类型

  • 每一种数据都定义了明确的数据类型,在内存中分配了不同大小的内存空间。

基本数据类型

数值型
整数类型
  • java 没有无符号数
  • byte(1字节)
  • short(2字节)
  • int(4字节): java 的整型常量默认是 int 类型,声明 long 类型常量加 l / L。
  • long(8字节)
浮点类型
  • float(4字节): java 的浮点型常量默认是 double 类型,声明 float 类型常量加 f / F。
  • double(4字节)
字符型
  • char(2字节)
    • 字符类型可以直接存放一个数字(char 的本质是一个整数,在输出时,是 unicode 码对应的字符)
    • 是可以用于运算的,相当于一个整数
字符编码表
  • ASCII: 一个字节表示
  • Unicode: 固定大小的编码,使用两个字节来表示字符,字母和汉字统一都占用两个字节。(兼容 ASCII 码)
  • utf-8: 大小可变的编码(可以使用 1-6 个字节表示一个符号),字母使用 1 字节,汉字使用 3 字节。
  • gbk: 大小可变的编码,字母使用 1 字节,汉字使用 2 字节。
布尔型
  • boolean(1字节): 存放 true / false
    • 不可以 0 或非 0 的整数代替 false 和 true,这点和 C 语言不同。
自动类型转换
  • 赋值或运算时,精度小的类型自动转换为精度大的数据类型。
  • char -> int -> long -> float -> double
  • byte -> short -> int -> long -> float -> double
  • 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
  • 把精度大的数据类型赋值给精度小的数据类型时会报错,反之会进行自动类型转换。
    • int a = 1.1; // error
  • (byte, short) 和 char 之间不会相互自动转换。
  • 当把具体数赋给 byte 时,先判断该数是否在 byte 范围内,如果是就可以
    • byte b = 1; // yes
    • int a = 1; byte b = a; // error
  • byte, short, char 三者之间可以运算,在运算时首先转换为 int 类型。(即使只使用 byte 进行运算也会先转为 int)
    • byte a = 1; byte b = a + a; // error
  • boolean 不参与类型转换。
强制类型转换
  • 自动类型转化的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时加上强制转换符 (),可能造成精度降低或溢出。
    • int n = (int)1.9;
基本数据类型和 String 类型的转换
  • 基本数据类型 -> String: 基本类型的值 + “”
    • int a = 100; String s = a + “”;
  • String -> 基本数据类型: 通过基本类型的包装类调用 parseXX 方法
    • String s = “123”; int n = Integer.parseInt(s);
    • 如果格式不正确,就会抛出异常。(编译能过,运行报错)
      • String s = “hello”; int n = Integer.parseInt(s);

引用数据类

类(class)
接口(interface)
数组([])

运算符

算术运算符

加法

  • 当左右两边都是数值类型时,做加法运算。
  • 当左右两边有一方为字符串时,做拼接运算。(从左到右运算)
    • “1” + 1 输出 “11”
    • 100 + 3 + “hello” 输出 “103hello”
    • “hello” + 100 + 3 输出 “helle1003”

除法

  • System.out.println(10 / 4); // 2
  • System.out.println(10.0 / 4); // 2.5
  • System.out.println(10 / 4.0); // 2.5
  • double d = 10 / 4; // 2.0

取模

  • a % b = a - a / b * b;
    • 10 % 3; // 1
    • -10 % 3; // -1
    • 10 % -3; // 1
    • -10 % -3; // -1

赋值运算符

基本赋值运算符

  • int a = 10;

复合赋值运算符

  • a += b;
  • 也会进行类型转换
    1
    2
    3
    byte a = 1;
    a += 2; // 等价于 a = (byte)(a + 2);
    // a = a + 2; // 这样写是错的

关系运算符(比较运算符)

  • ==, !=, <, >, <=, >=, instanceof

逻辑运算符

  • 用于连接多个条件,最终的结果也是一个 boolean 值。
  • &&, ||, !
  • &, |, ^
  • & 和 && 作用相同,| 和 || 作用相同,只不过 && 和 || 第一个条件能得出结果时,后面的条件不会执行;而 & 和 | 会执行所有条件。

位运算符

  • (>>, <<) 算术移位
  • (>>>) 逻辑右移(没有逻辑左移)

三元运算符

基本语法

  • 条件表达式? 表达式1: 表达式2;

控制结构

顺序控制

分支控制(同 C++)

  • switch(表达式)中表达式的返回值必须是: byte, short, int, char, enum, String; case 子句中的值必须是常量或者常量表达式

循环控制

  • for: 同 C++
  • while: 同 C++

break

continue

return

数组、排序和查找

数组

  • 数组可以存放多个同一类型的数据。
  • 数组也是一种数据类型,是引用类型。

初始化

动态初始化1
1
int[] a = new int[3];
动态初始化2(先声明后定义)
1
2
int[] a;
a = new int[3];
静态初始化
1
int[] a = {1, 2, 3};
  • java 中数组元素都是在堆中,不论是静态还是动态初始化。

默认值

  • int/short/byte/long: 0
  • float/double: 0.0
  • char: \u0000
  • boolean: false
  • String: null

赋值

  • 数组在默认情况下是引用传递,赋的值是地址,赋值方式为引用传递(是一个地址)
    1
    2
    int[] arr1 = {1, 2, 3};
    int[] arr2 = arr1; // arr2 的变化会影响 arr1

二维数组

1
2
3
4
int [][] arr = new int[2][3];
int[][] arr = {{1, 2, 3}, {4, 5, 6}};
System.out.println(arr.length); // 2
System.out.println(arr[0].length); // 3

排序

查找

面向对象编程(基础)

类与对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Hello{
public static void main(String [] args) {
Cat cat1 = new Cat();
cat1.name = "yzy";
cat1.age = 2;
cat1.color = "white";
System.out.println(cat1.name);
}
}

class Cat{
String name;
int age;
String color;
}

java 内存结构分析

  • 栈: 一般存放基本数据类型(局部变量)
  • 堆: 存放对象(Cat cat, 数组等)
  • 方法区: 常量池(常量,比如字符串),类加载信息

成员方法

1
2
3
4
5
6
7
8
class Cat{
String name;
int age;
String color;
public void speak() {
System.out.println("miao~");
}
}

成员方法传参机制

  • 对于基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参。
  • 对于引用数据类型,传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参。

重载 overload

  • 方法名: 必须相同
  • 形参列表: 必须不同(形参类型或个数或顺序,至少有一样不同,参数名无要求)
  • 返回类型: 无要求

可变传参

  • java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
  • 可变参数的实参可以为 0 个或任意多个
  • 可变参数的实参可以为数组
  • 可变参数的本质就是数组
  • 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  • 一个形参列表中只能有一个可变参数
    1
    2
    3
    4
    5
    6
    // 1. int... 表示接受的是可变参数,类型是int,即可以接收多个int(0-多)
    // 2. 使用可变参数时,可以当做数组来使用,即 nums 可以当作数组
    public int sum(int... nums) {
    System.out.println("可变参数的长度:" + nums.length);
    return 0;
    }

作用域

  • 全局变量: 也就是属性,作用域为整个类体。(属性在定义时,可以直接赋值)
  • 全局变量可以不赋值,直接使用,因为有默认值
  • 局部变量必须赋值后,才能使用,因为没有默认值
  • 全局变量和局部变量可以重名,使用时遵循就近原则
  • 全局变量可以加修饰符

构造器(构造函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Hello{
public static void main(String [] args) {
Cat cat1 = new Cat("yzy", 2, "white");
cat1.print();
}
}

class Cat{
String name;
int age;
String color;
public Cat(String _name, int _age, String _color) {
name = _name;
age = _age;
color = _color;
}
public void print() {
System.out.println("name: " + name + " age: " + age + " color: " + color);
}
}
  • 构造器的修饰符可以默认
  • 构造器没有返回值
  • 方法名和类名必须一样
  • 参数列表和成员方法一样的规则
  • 构造器的调用由系统完成

构造器使用细节

  • 一个类可以定义多个不同的构造器,即构造器重载
  • 构造器是完成对象的初始化,并不是创建对象
  • 如果没有定义构造器,系统会给类生成一个默认无参构造器
  • 一旦定义了构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显示的定义一下

对象创建的流程分析

    1. 加载类信息(方法区,只会加载一次)
    1. 在堆中分配空间
    1. 完成对象的初始化
    • 3.1 默认初始化
    • 3.2 显示初始化
    • 3.3 构造器初始化
  • 返回对象的地址

this

1
2
3
4
5
6
7
8
9
10
class Cat{
String name;
int age;
String color;
public Cat(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
}
  • this 关键字可以用来访问本类的属性、方法、构造器
  • this 用于区分当前类的属性和局部变量
  • 访问成员方法的语法: this.方法名(参数列表)
  • 访问构造器语法: this(参数列表); 只能在构造器中使用(即只能在构造器中访问另一个构造器,且必须放在构造器的第一条语句)
  • this 不能在类定义的外部使用,只能在类定义的方法中使用

面向对象编程(中级)

  • 包的本质实际上就是创建不同的文件夹/目录来保存类文件
  • 三大作用
    • 区分相同名字的类
    • 当类很多时,可以很好地管理类
    • 控制访问范围

包基本语法

  • package 包名;
  • package 的作用是声明当前类所在的包,需要放在类的最上面,一个类最多只能有一句 package
  • import 指令放在 package 的下面,在类定义的前面,可以有多句且没有顺序要求

命名

  • com.公司名.项目名.业务模块名

常用的包

  • java.lang.*: lang 包是基本包,默认引入,不需要再引入
  • java.util.*: util 包,系统提供的工具包,工具类。(使用 Scanner)
  • java.net.*: 网络包,网络开发
  • java.awt.*: 做 java 的界面开发,GUI

引入包

  • import java.util.Scanner; // 只会引入 java.util 包下的 Scanner
  • import java.util.*; // 将 java.util 包下的所有类都引入

访问修饰符

  • 用于控制方法和属性的访问权限
  • 只有默认级别和 public 才能修饰类
  • public > protected > 默认 > private

public

  • 对外公开

protected

  • 对子类和同一个包的类公开

默认级别

  • 没有修饰符号,向同一个包的类公开

private

  • 只有类本身可以访问,不对外公开

封装

  • 把抽象出来的数据和对数据的操作封装在一起

继承

1
2
3
4
5
class Father {
}

class Son extends Father {
}
  • 子类继承了父类所有的属性和方法,非私有的属性和方法可以直接访问,但是私有属性不能在子类直接访问,要通过公共的方法去访问
  • 子类必须调用父类的构造器,完成父类的初始化。(super() 默认调用父类的无参构造器)
  • 当创建子类时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
  • 如果希望指定去调用父类的某个构造器,则显式的调用一下: super(父类构造器参数列表)
  • super 在使用时,必须放在构造器的第一行(super 只能在构造器中使用)
  • super() 和 this() 都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
  • java 所有类都是 Object 类的子类,Object 类是所有类的基类
  • 父类构造器的调用不限于直接父类,将一直往上追溯直到 Object 类
  • 子类最多只能继承一个父类(直接继承),即 java 中是单继承机制
  • 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

Super

  • 用于访问父类的属性、方法和构造器
  • 不能访问父类的私有属性及私有方法
  • 当子类中有和父类中的属性和方法重名时,为了访问父类的属性和方法,必须通过 super。如果没有重名,使用 super、this、直接访问是一样的效果
  • super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员;如果多个基类(上级类)中都有同名成员,使用 super 访问遵循就近原则

override

  • 子类有一个方法和父类的某个方法的名称、返回类型、参数都一样,那么就说子类的这个方法覆盖了父类的方法
    • 子类的方法的参数、方法名称,要和父类方法的参数、方法名称完全一样
    • 子类方法的返回类型和父类方法返回类型一样,或者是父类返回类型的子类
    • 子类方法不能缩小父类方法的访问权限,可以扩大父类的访问权限

多态

  • 方法或对象具有多种形态,是建立在封装和继承基础之上的。
  • 一个的编译类型和运行类型可以不一致。
  • 编译类型在定义类型时就确定了,不能改变。
  • 运行类型是可以变换的。
  • 编译类型看定义时 = 号的左边,运行类型看 = 号的右边。
    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
    public class Hello{
    public static void main(String [] args) {
    Father fs = new Son(); // fs 编译类型是 Father, 运行类型是 Son
    fs.say(); // Son!
    fs = new Girl(); // fs 编译类型是 Father, 运行类型是 Gril
    fs.say(); // Gril!
    }
    }

    class Father {
    public void say() {
    System.out.println("Father!");
    }
    }

    class Son extends Father {
    @Override
    public void say() {
    System.out.println("Son!");
    }
    }

    class Girl extends Father {
    @Override
    public void say() {
    System.out.println("Girl!");
    }
    }

向上转型

  • 父类的引用指向了子类的对象
  • 可以调用父类的所有成员(需遵循访问权限),但是不能调用子类的特有成员(因为在编译阶段,能调用哪些成员,是由编译类型决定的),最终的运行效果看子类的具体实现(即调用方法时,按照从子类开始查找方法,然后调用)

向下转型

  • 子类类型 引用名 = (子类类型)父类引用;
  • 只能强转父类的引用,不能强转父类的对象
  • 要求父类的引用必须指向的是当前目标类型的对象
  • 当向下转型后,可以调用子类类型中的所有成员

属性重写

  • 属性没有重写一说,属性的值看编译类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Hello{
    public static void main(String [] args) {
    Father fs = new Son(); // fs 编译类型是 Father, 运行类型是 Son
    System.out.println(fs.cnt); // 1, 成员变量的访问看编译类型
    }
    }

    class Father {
    int cnt = 1;
    }

    class Son extends Father {
    int cnt = 2;
    }

instanceof

  • 用于判断对象的运行类型是否为 xx 类型或 xx 类型的子类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Hello{
    public static void main(String [] args) {
    Son s = new Son();
    System.out.println(s instanceof Son); // true
    System.out.println(s instanceof Father);//true
    Father fs = (Father)s;
    System.out.println(fs instanceof Father);//true
    System.out.println(fs instanceof Father);//true
    }
    }

    class Father {
    int cnt = 1;
    }

    class Son extends Father {
    int cnt = 2;
    }

动态绑定机制

  • 当调用对象方法的时,该方法会和该对象的内存地址 / 运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
    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
    33
    public class Hello{
    public static void main(String [] args) {
    A a = new B();
    System.out.println(a.sum()); // 40
    System.out.println(a.sum1()); // 30
    }
    }

    class A {
    public int i = 10;
    public int sum() {
    return getI() + 10;
    }
    public int sum1() {
    return i + 10;
    }
    public int getI() {
    return i;
    }
    }

    class B extends A {
    public int i = 20;
    public int sum() {
    return i + 20;
    }
    public int sum1() {
    return i + 10;
    }
    public int getI() {
    return i;
    }
    }
    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
    33
    public class Hello{
    public static void main(String [] args) {
    A a = new B();
    System.out.println(a.sum()); // 30
    System.out.println(a.sum1()); // 20
    }
    }

    class A {
    public int i = 10;
    public int sum() {
    return getI() + 10;
    }
    public int sum1() {
    return i + 10;
    }
    public int getI() {
    return i;
    }
    }

    class B extends A {
    public int i = 20;
    // public int sum() {
    // return i + 20;
    // }
    // public int sum1() {
    // return i + 10;
    // }
    public int getI() {
    return i;
    }
    }

多态参数

  • 方法定义的形参类型为父类类型,实参类型允许为子类类型

Object 类详解

== 运算符

  • 既可以判断基本类型,也可以判断引用类型
    • 判断基本类型,判断的是值是否相等
    • 判断引用类型,判断的是地址是否相等,即判断是不是同一个对象

equals 方法

  • Object 类中的方法,只能判断引用类型,默认判断的是地址是否相等,子类中往往会重写该方法,用于判断内容是否相等

hashCode 方法

  • 提高具有哈希结构的容器的效率
  • 两个引用,如果指向同一个对象,则哈希值肯定是一样的
  • 两个引用,如果指向的是不同对象,则哈希值是不一样的(有可能出现地址冲突而导致哈希值相同)
  • 一般是通过将该对象的内部地址转换为一个整数来实现的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Hello{
    public static void main(String [] args) {
    A a1 = new A();
    A a2 = new A();
    A a3 = a1;
    System.out.println(a1.hashCode() == a2.hashCode()); // false
    System.out.println(a1.hashCode() == a3.hashCode()); // true
    }
    }

    class A {
    }

toString 方法

  • 默认返回 全类名 + @ + 哈希值的十六机制
    1
    2
    Object o = new Object();
    System.out.println(o.toString()); // java.lang.Object@7181ae3f
  • 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式
  • 直接输出一个对象时,toString 方法会被默认的调用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Hello{
    public static void main(String [] args) {
    A a = new A();
    System.out.println(a); // A{name='yzy'}
    }
    }

    class A {
    String name = "yzy";
    @Override
    public String toString() {
    return "A{" +
    "name='" + name + '\'' +
    '}';
    }
    }

finalize 方法(析构函数)(deprecated)

  • 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用该方法
  • 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作

面向对象编程(高级)

类变量和类方法

类变量(静态类属性)

  • 同一个类所有对象共享
  • 在类加载的时候就生成(没有创建对象实例,也能使用类变量)
  • 生命周期随类的加载开始,随着类的消亡而销毁
  • 访问: 类名.类变量名 / 对象名.类变量名 (满足访问修饰符的访问权限和范围)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Hello{
    public static void main(String [] args) {
    System.out.println(A.cnt);
    A a = new A();
    System.out.println(a.cnt);
    }
    }

    class A {
    public static int cnt = 0;
    }

类方法(静态方法)

  • 访问: 类名.类方法名 / 对象名.类方法名 (满足访问修饰符的访问权限和范围)
  • 当方法中不涉及到任何和对象相关的成员,则可以设计成类方法
  • 和普通方法一样都是随着类的加载而加载,将结构信息存储在方法区
  • 类方法中无 this 参数
  • 类方法只能访问类变量和类方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Hello{
    public static void main(String [] args) {
    A.test();
    A a = new A();
    a.test();
    }
    }

    class A {
    public static void test() {
    System.out.println("A.test()");
    }
    }

理解 main 方法语法

  • main 方法是虚拟机调用的
  • java 虚拟机需要调用类的 main 方法,所以 main 方法的访问权限必须是 public
  • java 虚拟机在执行 main 方法时不必创建对象,所以 main 方法必须是 static
  • main 方法接收 String 类型的数组参数,该数组中保存执行 java 命令时传递给所执行的类的参数
  • java 执行的程序 参数1 参数2 参数3 …

代码块

  • 又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过 {} 包围起来
  • 和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显示调用,而是加载类或创建对象时隐式调用
  • 基本语法
    1
    2
    3
    4
    5
    [修饰符]{
    代码
    };
    修饰符可选,要写的话,只能写 static
    使用 static 修饰的叫静态代码块,不修饰的叫普通代码块
  • static 代码块,作用就是对类进行初始化,随着类的加载而执行,并且只会执行一次;若是普通代码块,每创建一个对象都执行
  • 静态代码块只能调用静态成员(属性和方法),普通代码块可以调用任意成员
  • 使用场景: 如果多个构造器中都有重复语句,可以抽取到代码块中,提高代码的重用
  • 不管调用哪个构造器创建对象,都会先调用代码块的内容
  • 代码块调用顺序优先于构造器
  • 构造器的最前面其实隐含了 super() 和普通代码块的调用(先 super 加载父类)

类加载时机

  • 创建对象实例时
  • 创建子类对象实例时,父类也会被加载
  • 使用类的静态成员(属性或方法)时

创建对象时类的调用顺序

  • 调用静态代码块和静态属性初始化(这俩初始化优先级是一样的,按定义的顺序调用)
  • 调用 super()、普通代码块和普通属性的初始化(这俩优先级也一样)
  • 调用构造方法

创建子类对象时类的调用顺序

  • 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  • 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
  • 父类的 super()、普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  • 父类的构造方法
  • 子类的 super()、普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
  • 子类的构造方法

单例设计模式

步骤

  • 构造器私有化
  • 类的内部创建对象
  • 向外暴露一个静态的公共方法

饿汉式

  • 未使用对象也会在类加载时创建
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class SingleTon {
    private static SingleTon tmp = new SingleTon();
    private SingleTon() {

    }
    public static SingleTon getInstance() {
    return tmp;
    }
    }

懒汉式

  • 目前懒汉式存在线程安全问题
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class SingleTon {
    private static SingleTon tmp;
    private SingleTon() {

    }
    public static SingleTon getInstance() {
    if (tmp == null) {
    tmp = new SingleTon();
    }
    return tmp;
    }
    }

final 关键字

  • 可以修饰类、属性、方法和局部变量
  • final 修饰的属性又叫常量,一般用大写命名
  • final 修饰的属性在定义时必须赋初值,并且以后不能修改,赋值可以加在如下位置之一
    • 定义时
    • 在构造器中
      1
      2
      3
      4
      5
      6
      class A {
      final int CNT;
      public A() {
      CNT = 0;
      }
      }
    • 在代码块中
      1
      2
      3
      4
      5
      6
      class A {
      final int CNT;
      {
      CNT = 0;
      }
      }
  • 如果 final 修饰的属性是静态的,则初始化的位置只能是定义时或者在静态代码块中,不能在构造器中赋值
  • final 类不能继承,但是可以实例化
  • 如果类不是 final 类,但是含有 final 方法,则该方法虽然不能重写,但是可以被继承
  • 如果一个类已经是 final 类了,就没有必要再将方法修饰成 final 方法
  • final 不能修饰构造方法
  • final 和 static 往往搭配使用,效率更高,这样不会导致类加载(单独使用 static 会加载类)
  • 包装类(Integer,Double,Float,Boolean等都是 final 类),String 也是 final 类

使用场景

  • 当不希望类被继承时
    1
    2
    final class A {}
    class B extends A {} // This should cause a compile-time error
  • 当不希望父类的某个方法被子类覆盖 / 重写时
    1
    2
    3
    4
    5
    6
    7
    class A {
    public final void test() {}
    }
    class B extends A {
    @Override
    public void test() {} // compile error
    }
  • 当不希望类的某个属性被修改时
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Hello{
    public static void main(String [] args) {
    A a = new A();
    a.cnt = 1; // compile error
    }
    }

    class A {
    public final int CNT = 0;
    }
  • 当不希望某个局部变量被修改时
    1
    2
    3
    4
    5
    6
    class A {
    public void test() {
    final int CNT = 0;
    cnt = 1; // error
    }
    }

抽象类

  • 当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
  • 当一个类中存在抽象方法时,需要将该类声明为 abstract 类
    1
    2
    3
    abstract class A {
    abstract public void test();
    }
  • 抽象类不能被实例化
  • 抽象类不一定要包含 abstract 方法,
  • 一旦类包含了 abstract 方法,则这个类必须声明为 abstract 类
  • abstract 只能修饰类和方法,不能修饰属性和其他的
  • 抽象类可以有任意成员
  • 抽象方法不能有主体,即不能实现
  • 如果一个类继承了抽象类,则该类必须实现抽象类的所有抽象方法,除非该类也声明为 abstract 类
  • 抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的

模板设计模式

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
abstract class Template {
public abstract void job();
public void calculateTime() {
long start = System.currentTimeMillis();
job();
long end = System.currentTimeMillis();
System.out.println("Time taken: " + (end - start) + " ms");
}
}

class jabA extends Template {
@Override
public void job() {
long sum = 0;
for (int i = 1; i <= 1000000; i++) {
sum += i;
}
System.out.println("Sum: " + sum);
}
}

class jabB extends Template {
@Override
public void job() {
long product = 1;
for (int i = 1; i <= 20; i++) {
product *= i;
}
System.out.println("Product: " + product);
}
}

接口

  • 接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来
    1
    2
    3
    4
    5
    6
    7
    8
    9
    interface 接口名 {
    // 属性
    // 方法
    }
    class 类名 implements 接口 {
    // 自己属性
    // 自己方法
    // 必须实现的接口的抽象方法
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    interface Ainterface {
    // 属性
    public int n = 0;
    // 方法
    // 在接口中,抽象方法,可以省略abstract关键字
    public void test();
    // 在 jdk8 之后,接口中允许定义默认方法
    default public void test1() {
    System.out.println("test1");
    }
    // 在 jdk8 之后,接口中允许定义静态方法
    static public void test2() {
    System.out.println("test1");
    }
    }

    class A implements Ainterface {
    // 实现接口中的所有抽象方法
    public void test() {
    System.out.println("实现接口中的抽象方法");
    }
    }
  • 接口不能被实例化
  • 接口中所有的方法是 public 方法,接口中的抽象方法,可以不用 abstract 修饰
  • 一个普通类实现接口,就必须将该接口的所有方法都实现
  • 抽象类实现接口,可以不用实现接口的方法
  • 一个类同时可以实现多个接口
  • 接口中的属性,只能是 final 的,而且是 public static final 修饰的
  • 接口中属性的访问形式: 接口名.属性名
  • 接口不能继承其他类,但是可以继承多个别的接口
  • 接口的修饰符只能是 public 和默认

接口 vs 继承

  • 接口是对 java 单继承机制的补充
  • 接口比继承更加灵活,继承是满足 is-a 关系,而接口只需满足 like-a 关系
  • 接口在一定程度上实现代码解耦(接口规范性 + 动态绑定机制)
接口和继承解决的问题不同
  • 继承的价值主要在于: 解决代码的复用性和可维护性
  • 接口的价值主要在于: 设计好各种规范,让其他类去实现这些方法

接口多态特性

  • 接口引用可以指向实现了接口类的对象
    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
    33
    public class Hello{
    public static void main(String [] args) {
    // 接口类型的变量可以指向实现了接口的对象实例
    Ainterface ifa = new A();
    Ainterface ifb = new B();
    A ifaa = new A();
    Test.func(ifa); // A
    Test.func(ifb); // B
    Test.func(ifaa);// A
    }
    }

    class Test {
    public static void func(Ainterface inf) {
    inf.test();
    }
    }

    interface Ainterface {
    public void test();
    }

    class A implements Ainterface {
    public void test() {
    System.out.println("A");
    }
    }

    class B implements Ainterface {
    public void test() {
    System.out.println("B");
    }
    }
  • 接口多态数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class Hello{
    public static void main(String [] args) {
    Ainterface infs[] = new Ainterface[2];
    infs[0] = new A();
    infs[1] = new B();
    }
    }

    interface Ainterface {
    public void test();
    }

    class A implements Ainterface {
    public void test() {
    System.out.println("A");
    }
    }

    class B implements Ainterface {
    public void test() {
    System.out.println("B");
    }
    }
  • 接口多态传递(接口与接口之间继承)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Hello{
    public static void main(String [] args) {
    Ainterface a = new A();
    Binterface b = new A();
    }
    }

    interface Ainterface {
    public void test();
    }

    interface Binterface extends Ainterface {
    public void test();
    }

    class A implements Binterface {
    public void test() {
    System.out.println("A");
    }
    }

内部类

  • 一个类的内部又完整的嵌套了另一个类结构
  • 内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系

定义在外部类局部位置上

局部内部类(有类名)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Outer {
private int x = 10;
public void func() {
// 局部内部类是定义在外部类的局部位置,通常是在方法内
// 不能添加访问修饰符,但是可以使用final修饰(不让下面的内部类继承)
// 作用域: 仅仅在定义它的方法或代码块内有效
// 外部其他类不能访问此类
class Inner {
// 如果局部内部类的成员与外部类的成员重名,默认遵循就近原则,如果想访问外部类的成员,可以使用 外部类名.this.成员名
private int x = 20;
public void print() {
// 可以访问外部类的成员,包括私有成员
System.out.println("x = " + Outer.this.x); // 10
System.out.println("x = " + x); // 20
}
}
// 外部类可以创建局部内部类的对象并调用其方法
Inner i = new Inner();
i.print();
}
}
匿名内部类(没有类名)
  • 本质是类,同时还是一个对象(jdk 底层会分配类名)
    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
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    interface IA {
    public void test();
    }

    class Father {
    public String name;
    public Father(String name) {
    this.name = name;
    }
    public void test() {
    System.out.println("father test");
    }
    }

    class Outer {
    private int x = 10;
    public void func() {
    // a 的编译类型是 IA,运行类型是匿名内部类
    // 原理是编译器在编译时会生成一个类文件,类名为 Outer$1.class
    IA a = new IA() {
    @Override
    public void test() {
    System.out.println("inner IA test");
    }
    };
    a.test();

    // f 编译类型是 Father,运行类型是匿名内部类
    // 原理同上,编译器会生成一个类文件,类名为 Outer$2.class
    Father f = new Father("yzy") {
    @Override
    public void test() {
    System.out.println("inner Father test");
    }
    };
    f.test();

    // 也可以不使用变量,直接调用匿名内部类的方法
    new Father("yzy") {
    @Override
    public void test() {
    System.out.println("inner Father test");
    }
    }.test();
    }
    }

定义在外部类的成员位置上

成员内部类(没用 static 修饰)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Outer {
private int x = 10;
class Inner {
public void test() {
System.out.println("x = " + x);
}
}
public void func() {
Inner i = new Inner();
i.test();
}
public Inner getInner() {
return new Inner();
}
}
  • 可以添加任意的访问修饰符(因为是外部类成员)
  • 外部其他类也能使用成员内部类
    1
    2
    3
    4
    5
    6
    class Test {
    public void test() {
    Outer.Inner i = new Outer().new Inner();
    i.test();
    }
    }

静态内部类(使用 static 修饰)

  • 可以直接访问外部类的所有静态成员
  • 可以添加任意的访问修饰符

枚举和注解

自定义类实现枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Season {
private String name;
private String desc;
// 1. 将构造方法私有化
// 2. 去掉 set 方法
// 3. 在类内部直接创建固定的对象
// 4. 提供公共的静态常量
public static final Season SPRING = new Season("春天", "春天是万物复苏的季节");
public static final Season SUMMER = new Season("夏天", "夏天是炎热的季节");
public static final Season AUTUMN = new Season("秋天", "秋天是丰收的季节");
public static final Season WINTER = new Season("冬天", "冬天是寒冷的季节");
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
public String getName() {
return name;
}
}

enum 关键字实现枚举

  • 当使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类
  • 如果使用无参创建枚举对象,则实参列表和小括号都可以省略
  • 当有多个枚举对象时,使用逗号间隔,最后一个分号结尾
  • 枚举对象必须放在枚举类的行首
  • 使用了 enum 关键字之后,就不能继承其他类,因为 enum 会隐式继承 Enum,而 java 是单继承机制
  • 枚举类和普通类一样可以实现接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    enum Gender {
    BOY, GIRL;
    }

    enum Season {
    SPRING("Spring", "The season of new beginnings"),
    SUMMER("Summer", "The warmest season"),
    AUTUMN("Autumn", "The season of harvest"),
    WINTER("Winter", "The coldest season");
    private String name;
    private String desc;
    private Season(String name, String desc) {
    this.name = name;
    this.desc = desc;
    }
    public String getName() {
    return name;
    }
    public String getDesc() {
    return desc;
    }
    }

Enum 成员方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Hello{
public static void main(String [] args) {
// name()
System.out.println(Gender.BOY.name()); // BOY
// ordinal() 输出枚举常量的序数
System.out.println(Gender.BOY.ordinal()); // 0
// values() 含有所有枚举常量的数组
Gender[] values = Gender.values();
for (Gender gender : values) {
System.out.println(gender.name());
}
// valueOf() 根据名称获取枚举常量
Gender gender = Gender.valueOf("BOY");
System.out.println(gender.name()); // BOY
// compareTo() 比较两个枚举常量的序数,输出减法
System.out.println(Gender.BOY.compareTo(Gender.GIRL));
}
}

enum Gender {
BOY, GIRL;
}

JDK 内置的基本注解类型

注解的理解

  • 注解也被称为元数据,用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息
  • 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息

基础注解

  • Override: 表示指定重写父类的方法,如果父类没有该方法,则会报错
  • Deprecated: 表示某个程序元素已过时,可以修饰方法、类、字段、包、参数等
  • SuppressWarnings: 抑制编译警告

元注解: 对注解进行注解

  • Retention: 指定注解的范围,三种(SOURCE、CLASS、RUNTIME)
  • Target: 指定注解可以在哪些地方使用
  • Documented: 指定该注解是否会在 javadoc 中体现
  • Inherited: 子类会继承父类注解

异常

常见的异常

  • NullPointerException
  • ArithmeticException
  • ArrayIndexOutOfBoundsException
  • ClassCastException
  • NumberFormatException

异常处理方式

  • 没有显式处理异常,默认是 throws

try-catch-finally

throws

1
2
3
4
5
6
7
class Test {
public void test() throws Exception {
System.out.println("Hello, World!");
}
}

throw new MyException("_");
  • 子类重写父类方法时,对于抛出异常的规定: 所抛出的异常类型要么和父类抛出的异常一样,要么为父类抛出异常的子类

异常处理分类

编译时异常

运行时异常

自定义异常

1
2
3
4
5
class MyException extends Exception {
public MyException(String message) {
super(message);
}
}

throw 和 throws 对比

  • throws: 异常处理的一种方式,在方法声明处使用,后面跟异常类型
  • throw: 手动生成异常对象的关键字,在方法体中使用,后面跟异常对象

常用类

包装类

  • 针对八种基本数据类型相应的引用类型–包装类
基本类型 包装类
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
  • 常用方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Hello{
    public static void main(String [] args) {
    int a = 1;
    Integer integer = a;
    int b = integer;
    System.out.println(b);
    String str1 = integer + "";
    String str2 = integer.toString();
    String str3 = String.valueOf(integer);
    string str4 = "12345";
    Integer c = Integer.parseInt(str4);
    Integer c = new Integer(str4);
    }
    }

String

  • 实现了 Serializable 和 Comparable(可以串行化和比较大小)
  • String 是 final 类
  • 有属性 private final char value[]; 用于字符串内容(final 表示指向的地址不能修改,类似于 C++ 中 char* const)

创建剖析

直接赋值
  • String str = “yzy”;
  • 先从常量池查看是否有 “yzy” 数据空间,如果有,直接指向;如果没有则重新创建,然后指向。最终指向的是常量池的空间地址。
调用构造器
  • String str = new String(“yzy”);
  • 先在堆中创建空间,里面维护了 value 属性,指向常量池的 “yzy” 空间。如果常量池没有 “yzy”,重新创建,如果有,直接通过 value 指向。最终指向的是堆中的空间地址。
常用方法

StringBuffer

  • String 保存的是字符串常量,里面的值不能更改,每次 String 类的更新实际上就是更改地址,效率较低 // private final char value[];
  • StringBuffer 保存的是字符串常量,里面的值可以更改,每次的更新实际上可以更新内容,不用每次更新地址,效率较高

StringBuilder

  • 一般用在单线程

Math

  • 包含用于执行基本数学运算的方法

Arrays

  • 包含了一系列的静态方法,用于管理或操作数组(比如排序和搜索)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import java.util.Arrays;

    public class Hello{
    public static void main(String [] args) {
    Integer[] integers = {1,2,3};
    System.out.println(Arrays.toString(integers));
    Arrays.sort(integers);
    }
    }

System

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Arrays;

public class Hello{
public static void main(String [] args) {
// 数组拷贝
int[] src = {1,2,3};
int[] dest = new int[3];
System.arraycopy(src, 0, dest, 0, src.length);

// 距离 1970.1.1 的毫秒数
System.out.println(System.currentTimeMillis());

// gc
System.gc();

// 退出
System.exit(0);
}
}

BigInteger、BigDecimal

BigInteger

  • 加减乘除需要使用对应的方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import java.math.BigInteger;

    public class Hello{
    public static void main(String [] args) {
    BigInteger b1 = new BigInteger("123456789012345678901234567890");
    System.out.println(b1);
    BigInteger b2 = b1.add(new BigInteger("987654321098765432109876543210"));
    BigInteger b3 = b1.multiply(new BigInteger("2"));
    BigInteger b4 = b2.subtract(b3);
    BigInteger b5 = b4.divide(new BigInteger("3"));
    }
    }

BigDecimal

  • 同 BigIntger
  • 使用除法时可能除不尽,可以设置参数保留解读

Date、Calender、LocalDate

Date

  • 精确到毫秒
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import java.util.Date;
    import java.text.SimpleDateFormat;

    public class Hello{
    public static void main(String [] args) {
    Date d1 = new Date();
    System.out.println(d1); // Sat Aug 23 00:01:43 CST 2025
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String formattedDate = sdf.format(d1);
    System.out.println(formattedDate); // 2025-08-23 00:01:43
    }
    }

Calender

1
2
3
4
5
6
7
8
9
import java.util.Calendar;

public class Hello{
public static void main(String [] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
System.out.println(calendar.get(Calendar.YEAR));
}
}

LocalDate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Hello{
public static void main(String [] args) {
LocalDate today = LocalDate.now();
System.out.println(today);
System.out.println(today.getYear());
System.out.println(today.getMonthValue());
System.out.println(today.getDayOfMonth());
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formattedDate = today.format(dtf);
System.out.println(formattedDate);
}
}

集合

集合框架体系

Collection

Collection 公共常用方法

  • add
  • remove
  • contains: 查看元素是否存在
  • size
  • isEmpty
  • clear
  • addAll: 添加一个 Collection
  • containsAll
  • removeAll
    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
    import java.util.ArrayList;

    public class Hello{
    public static void main(String [] args) {
    ArrayList list = new ArrayList();
    list.add("Hello, World!");
    list.add(123); // list.add(new Integer(123));
    list.add(true);
    System.out.println(list);
    list.remove(0); // 删除索引为0的元素
    list.remove(true); // 删除值为 true 的元素
    System.out.println(list);
    System.out.println(list.contains("yzy"));
    System.out.println(list.size());
    System.out.println(list.isEmpty());
    list.clear();
    System.out.println(list);
    ArrayList list2 = new ArrayList();
    list2.add("haha");
    list.addAll(list2);
    System.out.println(list);
    list.removeAll(list2);
    System.out.println(list);
    }
    }

迭代器遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.ArrayList;
import java.util.Iterator;

public class Hello{
public static void main(String [] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");

Iterator it = list.iterator();
while (it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
}
}

增强 for

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.util.ArrayList;

public class Hello{
public static void main(String [] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");

for (Object obj : list) {
System.out.println((String)obj);
}
}
}

List

  • List 集合类中元素有序、可重复
  • 每个元素都有其顺序索引
List 接口方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;

public class Hello{
public static void main(String [] args) {
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");
System.out.println(list.get(0));
System.out.println(list.get(1));
list.add(1, "Java");
System.out.println(list.indexOf("Hello"));
System.out.println(list.lastIndexOf("Hello"));
list.remove(0);
list.set(1, "Programming");
System.out.println(list);
}
}
ArrayList
  • 可以放 null 值
  • 底层使用数组实现的
  • 基本等同于 Vector
  • 线程不安全的
  • 维护了一个 Object 类的数组
  • 当创建 ArrayList 对象时,如果使用的是无参构造器,则初始容量为 0,第一次添加,则扩容为 10,若需要再次扩容,则扩容为 1.5 倍
  • 若使用的是指定大小的构造器,则初始容量为指定大小,若在扩容,还是扩 1.5 倍
Vector
  • 底层也是数组
  • 线程安全的
LinkedList
  • 实现了双向链表和双向队列特点
  • 可以添加任意元素,包括 null
  • 线程不安全

Set

  • 无序、没有索引
  • 不允许有重复元素
  • 可以使用迭代器、增强 for
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import java.util.HashSet;

    public class Hello{
    public static void main(String [] args) {
    HashSet set = new HashSet();
    set.add("Hello");
    set.add("World");
    set.add("Hello");
    set.add(null);
    System.out.println(set);
    }
    }
HashSet
  • HashSet 实际上是一个 HashMap
LinkedHashSet
  • LinkedHashSet 实际上是一个 LinkedHashMap,底层维护了一个数组 + 双向链表
  • 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序
TreeSet
  • 使用无参构造器仍然是无序的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import java.util.TreeSet;
    import java.util.Comparator;

    public class Hello{
    public static void main(String [] args) {
    TreeSet ts = new TreeSet(new MyComparator());
    ts.add("B");
    ts.add("A");
    ts.add("C");
    System.out.println(ts);
    }
    }

    class MyComparator implements Comparator {
    public int compare(Object obj1, Object obj2) {
    return -((String)obj1).compareTo((String)obj2);
    }
    }

Map

  • Map 与 Collection 并列存在

常用方法

  • put
  • remove
  • get
  • size
  • isEmpty
  • clear
  • containKey

遍历方式

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
33
34
35
36
37
38
39
import java.util.HashMap;
import java.util.Set;
import java.util.Iterator;

public class Hello{
public static void main(String [] args) {
HashMap map = new HashMap();
map.put("key1", "value1");
map.put("key2", "value2");
System.out.println(map);

// 1
Set keys = map.keySet();
for (Object key : keys) {
System.out.println(key + ": " + map.get(key));
}

// 2
Iterator it = keys.iterator();
while (it.hasNext()) {
Object key = it.next();
System.out.println(key + ": " + map.get(key));
}

// 3
Set entries = map.entrySet();
for (Object entryObj : entries) {
HashMap.Entry entry = (HashMap.Entry) entryObj;
System.out.println(entry.getKey() + ": " + entry.getValue());
}

// 4
Iterator entryIt = entries.iterator();
while (entryIt.hasNext()) {
HashMap.Entry entry = (HashMap.Entry) entryIt.next();
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}

HashMap

  • 底层是数组 + 链表 + 红黑树
  • 线程不安全

HashTable

  • key 和 value 都不能为 null
  • 使用方法基本上和 HashMap 一样
  • 线程安全

LinkedHashMap

TreeMap

Properties

  • 继承自 HashTable 类并且实现了 Map 接口
  • 使用特点和 HashTable 类似
  • 用于从 xxx.propertise 文件中,加载数据到 Properties 类对象

Collections

  • 一个操作 Set、List 和 Map 等集合的工具类
  • 提供了一系列静态的方法对集合元素进行排序、查询和修改等操作

泛型

  • 编译时检查添加元素的类型,提高了安全性
  • 减少了类型转化的次数
    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
    33
    import java.util.ArrayList;

    public class Hello{
    public static void main(String [] args) {
    ArrayList <Dog> dogs = new ArrayList<Dog>();
    dogs.add(new Dog("Buddy", 3));
    dogs.add(new Dog("Max", 5));
    // dogs.add(new Cat("Whiskers", 2)); // This line will cause a compile-time error
    for (Dog dog : dogs) {
    System.out.println(dog.name + " is " + dog.age + " years old.");
    }
    }
    }

    class Dog {
    String name;
    int age;

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

    class Cat {
    String name;
    int age;

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

泛型语法

  • 给泛型指向数据类型时,要求是引用类型,不能是基本数据类型
    1
    2
    ArrayList<int> list = new ArrayList<int>();     // error
    ArrayList<String> list = new ArrayList<String>(); // yes
  • 在给泛型指定具体类型后,可以传入该类型或者其子类类型
  • 省略语法
    1
    2
    ArrayList<String> list = new ArrayList<>();   // 编译器会自动推断
    ArrayList list = new ArrayList(); // 不指定类型时,默认是 Object

自定义泛型

  • 普通成员可以使用泛型
  • 使用泛型的数组,不能初始化
  • 静态方法中不能使用类的泛型(因为静态是和类相关的,在类加载时,对象还没有创建,所以,如果静态方法或属性使用了泛型,JVM就无法完成初始化),但是能够定义和使用自己的泛型,成为静态泛型方法
  • 泛型类的类型,是在创建对象时确定的
  • 如果在创建对象时,没有指定类型,默认为 Object

泛型类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Hello{
public static void main(String [] args) {
Animal<String> dog = new Animal<>("Buddy");
}
}

class Animal<T> {
private T name;

public Animal(T name) {
this.name = name;
}

public T getName() {
return name;
}

public void setName(T name) {
this.name = name;
}
}

泛型接口

1
2
3
interface Animal<T> {

}

泛型方法

  • 修饰符 <T, R…> 返回类型 方法名(参数列表) {}
  • 可以定义在普通类中,也可以定义在泛型类中

泛型继承和通配符

  • 泛型没有继承性
    1
    ArrayList<Object> list = new ArrayList<String>();   // error
  • : 支持任意泛型
  • : 支持 A 类以及 A 类的子类,规定了泛型的上限
  • : 支持 A 类以及 A 类的父类,不限于直接父类,规定了泛型的下限

线程(基础)

线程使用

  • main 线程结束后,其他线程不会被动结束,还会继续执行

继承 Thread 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Hello{
public static void main(String [] args) {
Test t = new Test();
t.start();
}
}

// 当一个类继承了 Thread 类,该类就可以作为一个线程类使用
class Test extends Thread {
@Override
public void run() {
System.out.println("Hello, World!");
}
}

实现 Runnable 接口

  • 避免单继承的限制
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Hello{
    public static void main(String [] args) {
    Thread t = new Thread(new Test(), "My Runnable");
    t.start();
    }
    }
    class Test implements Runnable {
    public void run() {
    System.out.println("Hello, World!");
    }
    }

线程方法

  • setName: 设置线程名字
  • getName
  • start: 底层会创建新的线程,并调用 run
  • run: run 就是一个简单的方法调用,不会启动新线程
  • setPriority: 设置线程优先级
  • getPriority
  • sleep: 线程的静态方法,使当前线程休眠
  • interrupt: 中断线程,但并没有真正结束线程。一般用于中断正在休眠的线程(唤醒?)
  • yield: 让出 cpu,让其他线程执行(不一定礼让成功,看操作系统)
  • join: 线程的插队,一旦插队成功,则肯定先执行完插入的线程所有的任务

用户线程

  • 也叫工作线程,当线程的任务执行完或通知方式结束

守护线程

  • setDaemon(true): 一般是为工作线程服务的,当所有的工作线程结束,守护线程自动结束

线程生命周期

  • Thead.State
    • NEW: 尚未启动的线程处于此状态
    • RUNNABLE: 在 java 虚拟机中执行的线程处于此状态
      • READY
      • RUNNING
    • BLOCKED: 被阻塞等待监视器锁定的线程处于此状态
    • WAITING: 正在等待另一个线程执行特定动作的线程处于此状态
    • TIMED_WAITING: 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
    • TERMINATED: 已退出的线程处于此状态

Synchronized

同步代码块

1
2
3
synchronized (对象) {   // 得到对象的锁才能操作同步代码
// 需要被同步的代码
}

同步方法

1
2
3
public synchronized void func() {   // 在同一时刻,只能有一个线程执行该方法
// 需要被同步的代码
}

互斥锁

  • 每个对象都对应一个可称为互斥锁的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
  • 关键字 synchronized 来与对象的互斥锁联系,当某个对象被 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问
  • 同步方法(非静态的)的所可以是 this,也可以是其他对象(任意对象都可以)
  • 同步方法(静态的)的锁为当前类本身(默认锁对象: 当前类.class)

死锁

IO 流

文件

概念

  • 文件在程序中是以流的形式来操作的
  • 输入流: 内存 –> 文件
  • 输出流: 文件 –> 内存

常用操作

创建文件
  • new File(String pathname) // 根据路径构建一个 File 对象
  • new File(File parent, String child) // 根据父目录文件 + 子路径构建
  • new File(String parent, String child) // 根据父目录 + 子路径构建
  • createNewFile: 创建新文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import java.io.File;

    public class Hello{
    public static void main(String [] args) {
    File f = new File("test.txt");
    try {
    f.createNewFile();
    }
    catch (Exception e) {
    System.out.println(e);
    }
    }
    }
获取文件信息
  • getName
  • getAbsolutePath
  • getParent
  • length
  • exits
  • isFile
  • isDirectory
目录操作和文件删除
  • mkdir: 创建一级目录
  • mkdirs: 创建多级目录
  • delete: 删除空目录或文件j

IO 流原理及流的分类

  • 按操作数据单位不同: 字节流(InputStream, OutpuStream)、字符流(Reader, Writer)
  • 按数据流的流向不同: 输入流、输出流
  • 按流的角色不同: 节点流、处理流 / 包装流

节点流和处理流

输入流

InputStream()

FileInputStream
BufferedInputStream
ObjectInputStream

Reader

FileReader
BufferedReader
InputStreamReader

输出流

OutputStream

FileOutputReader
BufferedOutputReader
ObjectOutputReader

Writer

FileWriter
BufferedWriter
OutputStreamWriter

Properties 类

反射

  • 通过外部文件配置,在不修改源码的情况下来控制程序
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import java.lang.reflect.Method;

    public class Hello{
    public static void main(String [] args) {
    String className = "Cat";
    String methodName = "hi";
    try {
    Class<?> cls = Class.forName(className);
    Object obj = cls.getDeclaredConstructor().newInstance();
    Method method = cls.getMethod(methodName);
    method.invoke(obj);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    class Cat {
    public void hi() {
    System.out.println("Meow");
    }
    }

反射机制

  • 反射机制允许程序在执行期间借助于 Reflection API 取得任何类的内部信息(比如成员变量、构造器、成员方法等),并能操作对象的属性及方法
  • 加载完类之后,在堆中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。
  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时得到任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理

反射相关类

  • java.lang.Class: 代表一个类,Class 对象表示某个类加载后在堆中的对象
  • java.lang.reflect.Method: 代表类的方法
  • java.lang.reflect.Field: 代表类的成员变量
  • java.lang.reflect.Constructor: 代表类的构造方法

Class类

  • Class 也是类,因此也继承 Object 类
  • Class 类对象不是 new 出来的,而是系统创建的
  • 对于某个类的 Class 类对象,在内存中只有一份,因为类只加载一次
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成
  • 通过 Class 可以完整地得到一个类的完整结构,通过一系列 API
  • Class 对象是存放在堆中的
  • 类的字节码二进制数据是放在方法区的,有的地方称为类的元数据

反射获取类的结构信息

Class

Field

Method

Constructor

访问属性

访问方法

哪些类有 Class 对象

  • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • interface
  • 数组
  • enum
  • annotation(注解)
  • 基本数据类型
  • void

类加载

  • 静态加载: 编译时加载相关的类,如果没有则报错,依赖性太强
  • 动态加载: 运行时加载需要的类,如果运行时不用该类则不报错,降低了依赖性

类加载时机

  • 当创建类时 new
  • 当子类被加载时
  • 调用类中的静态成员
  • 反射(动态加载)

类加载流程(线程安全)

  1. java 源码
  2. 字节码文件
  3. 加载: 将类的 class 文件读入内存,并为之创建一个 java.lang.Class 对象,此过程由类加载器完成
  4. 连接: 将类的二进制数据合并到 JRE 中
    • 验证: 对文件的安全进行校验
    • 准备: 对静态变量进行默认初始化(0, null, false…)并分配内存
    • 解析: 把符号引用转成直接引用
  5. 初始化: JVM 负责对类进行初始化,这里主要指静态成员

类加载后内存布局

  • 方法区: 类的字节码二进制
  • 堆区: 类的 Class 对象

反射调用性能优化

MySQL

安装

1
2
3
4
5
6
7
8
9
sudo pacman -S mysql
sudo mysqld --initialize --user=mysql --basedir=/usr --datadir=/var/lib/mysql
sudo systemctl start mysqld.service
sudo systemctl enable mysqld.service
sudo systemctl status mysqld.service
mysql -uroot -p
alter user 'root'@'localhost' identified by '123456';
quit
mysql -h 主机地址 -P 端口 -u 用户名 -p密码

数据库

创建

1
2
3
CREATE DATABASE test;   # 默认 utf8 和 utf8_general_ci
CREATE DATABASE test CHARACTER SET utf8; # 指定字符集 utf8
CREATE DATABASE test CHARACTER SET utf8 collate utf8_bin; # 指定字符集,校对规则(utf8_bin 区分大小写, utf8_general_ci 不区分大小写)

查看、删除数据库

1
2
3
SHOW DATABASES; # 显示数据库语句
SHOW DATABASE db_name; # 显示数据库创建语句
DROP DATABASE db_test; # 数据库删除语句

备份恢复数据库

备份数据库
1
2
3
4
5
# 在 shell 中执行
# 备份数据库
mysqldump -u 用户名 -p密码 -B 数据库1 数据库2 ... 数据库n > 文件名.sql
# 备份数据库的表
mysqldump -u 用户名 -p密码 数据库 表1 表2 ... 表n > 文件名.sql
恢复数据库
1
2
# 在 mysql 命令行执行
source 文件名.sql

创建

1
2
3
4
create table table_name (
id INT,
name VARCHAR(255)
)

删除

修改

MySQL 数据类型

数值类型

  • 整型
    • tinyint
    • smallint
    • mediumint
    • int
    • bigint
  • 小数类型
    • float
    • double
    • decimal

文本类型(字符串类型)

  • char
  • varchar
  • text
  • longtext

二进制数据类型

  • blob
  • longblob

日期类型

  • data(年月日)
  • time(时分秒)
  • datatime(年月日时分秒)
  • timestamp(时间戳)
  • year

CRUD

Insert

Update

Delete

Select

单表
多表

函数

统计函数

时间日期

字符串函数

数学函数

流程控制

内连接

外连接

约束

not null

primary key

unique

foreign key

check

自增长

索引

事务

linux tools

fzf

  • 运行 fzf 就会递归列出当前文件夹下的所有文件,随后可以在弹出的框中输入字符来过滤(ctrl + j / ctrl + k 上下选择),回车后选择的文件就会被打印到标准输出。
  • vim $(fzf): 这样写就会把 fzf 的输出当作 vim 的参数。
  • fzf –preview “预览工具”

FastAPI

类型提示

1
2
3
4
5
def get_full_name(first_name: str, last_name: str):
full_name = first_name.title() + " " + last_name.title()
return full_name

print(get_full_name("john", "doe"))
  • 有类型提示时,能够使用该类型的自动补全
  • 还能检查是否有语法错误

常见类型

1
2
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_d, item_e

嵌套类型

  • 有些容器数据结构可以包含其他的值,比如 dict、list、set 和 tuple。它们内部的值也会拥有自己的类型,可以使用 Python 的 typing 标准库来声明这些类型以及子类型。

列表

1
2
3
4
5
from typing import List

def process_items(items: List[str]):
for item in items:
print(item)

元组和集合

1
2
3
4
from typing import Set, Tuple

def process_items(items_t: Tuple[int, int, str], items_s: Set[bytes]):
return items_t, items_s

字典

1
2
3
4
5
6
from typing import Dict

def process_items(prices: Dict[str, float]):
for item_name, item_price in prices.items():
print(item_name)
print(item_price)

类作为类型

1
2
3
4
5
6
class Person:
def __init__(self, name: str):
self.name = name

def get_person_name(one_person: Person):
return one_person.name

Pydantic 模型

  • Pydantic 是一个用来执行数据校验的 Python 库。你可以将数据的”结构”声明为具有属性的类。每个属性都拥有类型。接着用一些值来创建这个类的实例,这些值会被校验,并被转换为适当的类型(在需要的情况下),返回一个包含所有数据的对象。将获得这个对象的所有编辑器支持。

基本操作

1
2
3
4
5
6
7
from fastapi import FastAPI # 导入 FastAPI

app = FastAPI() # 创建一个 app 实例

@app.get("/") # 编写一个路径操作装饰器,如 @app.get("/")
async def root(): # 定义一个路径操作函数
return {"message": "Hello World"}

路径参数

  • FastAPI 支持使用 Python 字符串格式化语法声明路径参数(变量):

    1
    2
    3
    @app.get("/items/{item_id}")  # 这段代码把路径参数 item_id 的值传递给路径函数的参数 item_id
    async def read_item(item_id):
    return {"item_id": item_id}
  • 声明路径参数的类型

    1
    2
    3
    @app.get("/items/{item_id}")
    async def read_item(item_id: int): # 使用 Python 标准类型注解,声明路径操作函数中路径参数的类型
    return {"item_id": item_id}
  • 数据转换:FastAPI 通过类型声明自动解析请求中的数据

  • 数据校验:FastAPI 使用 Python 类型声明实现了数据校验

顺序很重要

  • 路径操作是按顺序依次运行的,前面声明的可能会覆盖后声明的

查询参数

  • 声明的参数不是路径参数时,路径操作函数会把该参数自动解释为查询参数

请求体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
return item

linux 三剑客

小技巧

1
2
3
4
# 命令行展开
echo {1..100} # 1,2,3,...,100
echo {1..100..2} # 1,,3,...
echo {a..z} # a,b,...,z

正则表达式

  • 正则工作时以行为单位
  • linux 仅受三剑客(sed, awk, grep)支持,其他命令无法使用。(通配符是大部分普通命令都支持的)

基本正则表达式

功能

  • 匹配字符
  • 匹配次数
  • 位置锚定

符号

  • ^: 尖角号,用于模式的最左侧,如 “^oldboy”,匹配以 oldboy 单词开头的行。
  • $: 美元符,用于模式的最右侧,如 “oldboy$”,匹配以 oldboy 单词结尾的行。
  • ^$: 组合符,表示空行。
  • .: 匹配任意一个且只有一个字符,不能匹配空行。
  • : 转义字符,让特殊含义的字符现出原形,还原本意,例如 . 代表小数点。
  • *: 匹配前一个字符(连续出现)0次或1次以上,重复0次代表空,即匹配所有内容。
  • .*: 组合符,匹配所有内容。
  • ^.*: 组合符,匹配任意多个字符开头的内容。
  • .*$: 组合符,匹配任意多个字符结尾的内容。
  • [^abc]: 匹配除了 ^ 后面的任意字符,^ 表示对abc取反

扩展正则表达式

  • 扩展正则必须用 grep -E 才能生效。
  • +: 匹配前一个字符1次或多次。
  • [符号集]+: 匹配括号内的字符1次或多次。
  • ?: 匹配前一个字符0次或多次。
  • |: 表示或者,同时过滤多个字符串。
  • (): 分组过滤,被括起来的内容表示一个整体。
  • a{n,m}: 匹配前一个字符最少n次,最多m次。
  • a{n,}: 匹配前一个字符最少n次。
  • a{n}: 匹配前一个字符正好n次。
  • a{,m}: 匹配前一个字符最多m次。

grep(Global search Regular expression and Print out the line)

  • 文本搜索工具,根据用户指定的模式(过滤条件),对目标文本进行匹配检查,打印匹配的行。

语法

1
2
3
4
5
6
7
8
9
10
grep [option] [pattern] file
命令 参数 匹配模式 文件数据
-i: ignorance, 忽略字符的大小写
-o: 仅显示匹配到的字符串本身
-v: --invert-match, 显示不能被模式匹配到的行
-E: 支持使用拓展的正则表达式元字符
-q: --quit, --silent, 静默模式, 即不输出任何信息
-n: 显示行号
--color=auto: 为 grep 过滤结果添加颜色
-c: 统计行数

小技巧

1
2
# 过滤空行
grep '^$' filename -n -v

sed(stream editor)

  • 对文件或数据流进行加工处理, 是操作、过滤和转换文本内容的强大工具
  • 常用功能包括结合正则表达式对文件实现快速增删改查,其中查询的功能中最常用的两大功能是过滤(过滤指定字符串)、取行(取出指定行)

语法

  • sed [选项] [sed 内置命令字符] [输入文件]
  • 选项:
    1
    2
    3
    4
    -n: 取消默认 sed 的输出,常与 sed 内置命令 p 一起用
    -i: 直接将修改结果写入文件,不用 -i,sed 修改的是内存数据
    -e: 多次编辑,不需要管道符了
    -r: 支持正则表达式
  • 内置命令字符,用于对文件进行不同的操作功能,如对文件增删改查
    1
    2
    3
    4
    5
    a: append,对文件追加,在指定行后面添加一行/多行文本
    d: delete,删除匹配行
    i: insert,表示插入文本,在指定行前添加一行/多行文本
    p: print,打印匹配行的内容,通常 p 与 -n 一起用
    s/正则/替换内容/g: 匹配正则内容,然后替换内容(支持正则),结尾 g 代表全局匹配
  • sed 匹配范围
    1
    2
    3
    4
    5
    空地址: 全文处理
    单地址: 指定文件某一行
    /pattern/: 被模式匹配到的每一行
    范围区间: 10,20 十到二十行; 10,+5 第十行以及向下五行;
    步长: 1~2 表示 1,3,5,7,9,奇数行, 2~2 两个步长,表示 2,4,6,8,10,偶数行

awk(格式化之后再输出文本)

  • 有强大的文本格式化的能力

语法

  • awk [option] ‘pattern[action]’ file …
  • awk 也是按行处理的,根据用户指定的分隔符工作
  • awk 默认以空格为分隔符,且多个空格也识别为一个空格
    1
    2
    3
    4
    5
    6
    awk '{print} $0' # 打印所有内容
    # $0 表示一整行
    # $1 表示第一列信息
    # $2 表示第二列信息
    # $NF 表示当前分割后的最后一列
    # $(NF-1) 表示倒数第二列

内置变量

  • $n: 指定分隔符后,当前记录的第 n 个字段
  • $0: 完整的输入记录
  • FS: 输入字段分隔符,默认是空格
  • OFS: 输出字段分隔符,默认是空格
  • NF(Number of fields): 分隔后,当前行一共有多少个字段
  • NR(Number of records): 当前记录数,行数

自定义输出内容

  • awk 必须外层单引号,内层双引号
  • 内置变量都不得添加双引号,否则会识别为文本,尽量别加引号
    1
    awk '{print "第一列",$1,"第二列",$2}' file

参数

  • -F: 指定分割字段符
    1
    2
    3
    awk -F ',' 'print $0' file
    awk -v FS=',' 'print $0' file
    awk -v FS=',' -v OFS='--------' 'print $1,$2' file
  • -v: 定义或修改一个 awk 内部的变量
  • -f: 从脚本文件中读取 awk 命令

支持 printf 格式化输出

1
awk '{printf "%s\n", $0}' file

模式(也叫条件)

  • BEGIN 和 END (处理文本之前和之后的动作)
    1
    2
    awk 'BEGIN{print "开始:"}{print $0}' file
    awk '{print $0}END{print "结束!"}' file
  • 使用正则
    1
    awk '/正则表达式/{print $0}' file

https(http over SecureSocket Layer)

  • http + ssl/tls = https

安全性高

  • 加密传输(ssl/tls)
  • 身份认证
    • 证书:由第三方可信机构颁发,有一对公私钥,想要证明身份时,用私钥加密证书发给对方,对方可以拿给第三方可信机构证明身份。
  • 保证完整性
  • 不可否认/抵赖

工作流程

  • 建立 tcp 连接
  • client 发送请求
  • server 返回公钥证书(server 同时拥有公钥和私钥; 证书能够代表 server 的身份)
  • client 验证证书
  • client 生成对称密钥,用公钥加密后发给 server
  • server 用私钥解密,得到对称密钥
  • c/s 双方使用对称密钥
    • 加密明文并发送
    • 解密密文得到明文

Playwright

install

1
2
3
pip install --upgrade pip
pip install playwright
playwright install / playwright install chromium

note

  • 使用 page.wait_for_timeout(5000) 进行 wait
  • Playwright 的 API 不是线程安全的。若在多线程环境中使用 Playwright,应该为每个线程创建一个 Playwright 实例。
  • 默认情况下,Playwright 会自动关闭对话框,因此您无需处理它们(alert()、confirm()、prompt())。但是,您可以在触发对话框的操作发生之前注册一个对话框处理程序,以选择 dialog.accept() 或 dialog.dismiss() 它。

css 基础: 层叠样式表 (Cascading Style Sheets)

编写位置

行内样式

  • 写在标签的 style 属性中,(又称:内联样式)
    1
    <h1 style="color:red;font-size:60px;">hello</h1>

内部样式

  • 写在html 页面内部,将所有的 CSS 代码提取出来,单独放在