1-corejava

前言

  • 笔记: corejava

  • 整理人: vanse(lc)

  • 时间: 2023/7/19

基本语法

概念

术语

  • sdk 软件开发包(函数库) 比如支付sdk 监控skd 云存储sdk
  • jdk 开发工具包 提供了java运行的基类 System.out.println(“hello”) 开发者
    • jdk包含jre
  • jre 运行环境 使用者
  • api 第三方平台开发接口 我们写好接口给其他人 路径对应的功能代码/weather
  • api doucment api文档 方法 类 参数的含义

jdk目录

  • bin 二进制文件
  • jre 运行时类库
    • lib/rt.jar jdk源码编译的字节码 System.class
  • src.zip jdk源码 System.java

java程序

  • 编写源代码 vim Hello.java

    • ```java
      public class Hello{
      public static void main(String[] args){
          System.out.println("hello world")
      }
      
      }
      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

      - 编译源代码(java) javac Hello.java

      - ==运行字节码(class) java Hello==



      > 加上包的类

      - 1班tom briup.Hello.java

      - 2班tom vanse.Hello.java

      - javac -d 目录 Hello.java 编译期间自动根据包创建文件夹

      - java com.briup.Tom

      ```java
      // 定义包
      package com.briup; // ;结束
      public class Tom{
      public static void main(String[] args){
      System.out.println("hello world")
      }
      }
      --------------------------------------------------------------------------------
      package com.vanse;
      public class Tom{
      public static void main(String[] args){
      System.out.println("hello world")
      }
      }

源码和字节码分离

  1. mkdir src bin

  2. touch src/Hello.java

  3. vim src/Hello.java

    1
    2
    3
    4
    5
    6
    package com.briup;
    public class Hello{
    public static void main(String[] args){
    System.out.println("hello world")
    }
    }
  4. javac -d bin/ src/Hello.java

    • -d 指定字节码生成的位置
  5. java -cp bin/ com.briup.Hello

    • -cp classpath 指定字节码加载的位置

注释

  • 单行注释 //

  • 多行注释 /** /

  • 文档注释 /** */ javadoc

    • sts/eclipse默认的编码(window) GBK

    • 修改为编码UTF-8 将来所有编码统一,不然会乱码

      image-20230721100629160

标识符

标识符 限制

  • 组成 _ $ 数字 字母
  • 数字不能开头
  • 不能是关键字

标识符 约定

  • 常量: 全部大写 多个字母_分割 public static int STUDENT_NUM
  • 变量: int a = studentNum;
  • 方法名: getName(){}
  • 类名: class Student 大写字母开头
    • StudentManager

类型|变量

类型

万物都可为类型 基本类(使用频率高 虚拟机做了优化)

数据类型 字节数量 2^10=1024 范围
byte 1 -2^7 - 2^7-1
short 2 32767
int 4 2147483647
long 8 -2^63-2^63-1
float 4
double 8
boolean 1 true/false
char 2 (unicode) 0-2^16-1 65535

整型 一个整数不指定类型默认使用int int a = 10

==最小的存储单位 字节byte 1个字母 1个字节==

==最小的传输单位 比特 bit 1 -> 0000 0001==

一个字节 = 8个比特位

计算器存储容量的单位,常见的还有KB、M、G、T,换算过程如下:

  • 1KB (Kilobyte 千字节) = 1024B,其中1024=2^10 ( 2 的10次方)
  • 1MB (Megabyte 兆字节 简称“兆”) = 1024KB
  • 1GB (Gigabyte 吉字节 又称“千兆”) = 1024MB
  • 1TB (Trillionbyte 万亿字节 太字节) = 1024GB

进制的由来 二进制0 1 符合计算规则 八进制 16进制简化二进制

负数如何表示二进制 第一个比特位当作符号

补充

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
byte 1个字节 8个比特

计算机运算 符号位当作运算符号
正数 原 反 补 一样
1 0000 0001
负数 第一位是符号位 1
原码(十进制的表示):第一位是符号位 1 000 0001
反码:除符号位取反0-1 1 111 1110
补码(计算):反码+1 1 111 1111
计算机 1+(-1) 使用补码做计算
1 原码 0000 0001
-1 原码 1000 0001
1000 0010 -2

1 反码 0000 0001
-1 反码 1111 1110
1111 1111
原码: 1000 0000 == -0
0已经能代表0这个值
-0 可以多替换一个值 -128


1 补码 0000 0001
-1 补码 1111 1111
10000 0000 = 0

byte : 1个字节 8个bit
-128 - 127 -2^7 2^7-1
最大值 0111 1111 1000 0000 - 1 = 128-1
最小值 1111 1111 -127-1 -0的位置 -128
-127 - 1
1111 1111 1000 0000 1000 0001
1000 0001 1111 1110 1111 1111
110000 000
1000 0000

short: 2 16
2^15-1 32768-1 32767

int: 32

2^31-1 214748 3647


char 2个字节 0 - 65535 1111 1111 1111 1111
无符号类型 0 - 2^16-1

浮点型 默认double类型

  • float float a = 3.14f
  • double double b = 3.14

字符型 char a 0-65535 (java使用的unicode编码)

0110 0001 ===97 == 0141 === 0x61 ===='a'

相关编码

  • ASCII 美国 127个字符 都能在计算机对应
    • 127 -> 256 西欧
  • GB2312 简体中文
  • GBK 中文编码 window/sts/eclipse
  • unicode 万国码(java字符类型使用的编码)
    • unicode-2
    • unicode-4
  • ==UTF-8==
    • 统一编码
    • 自动伸缩

特殊字符

  • \t 制表符
  • \n\r 回车换行
  • \ 转义字符
  • “%d %s”

布尔型

true/false 1true 0false

类型转换

  • 隐式转换 低精度->高精度

  • 强制转换 高精度->低精度 (==注意: 可能会丢失精度==)

    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
    package com.briup.identify;
    /**
    * 类型转换
    * 小->大 隐式转换
    * @author vanse
    *
    */
    public class Test3 {
    public static void main(String[] args) {
    // 自动转换 (隐式转换)
    byte a = 10;
    int b = a;
    // 强制转换(显示转换)
    short c = (short) b;
    System.out.println(b);
    System.out.println(c);


    int d = 128;
    byte e = (byte) d;
    // 00000.....10000000 取反+1
    // 10000000 补码 -0 -128
    // 01111111
    // 10000000
    System.out.println(e);

    int d1 = 130; // 128+2
    // 0000..... 1000 0010
    // 1000 0010 补码
    // 1111 1101
    // 1111 1110 原码 -126
    byte e1 = (byte) d1;
    System.out.println(e1);
    }
    }

特殊情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.briup.identify;
/**
* 特殊情况1 如果类型右边的值 在左边的范围类 允许
* 特殊情况2 如果类型参与了运算,会自动提升为最高的类型
*/
public class Test4 {
public static void main(String[] args) {
// 如果类型右边的值 在左边的范围类 允许
// 默认数字是int
byte a = 127;
short b = 32767;

// 特殊情况2 如果类型参与了运算,会自动提升为最高的类型
byte c = 10;
byte d = 11;
int e = c+d; // 右边是int
int f = 10;
float g = 11.0f;
float h = f+g;
}
}

变量

变量: 类型+变量名+值

  • 基本类型变量
  • 引用类型变量
    • 类类型
    • 数组 类类型 + []
    • 接口

常量

  • 宏观量 System.out.println(100)
  • final修饰的常量 public static final int A = 100

操作符

  • 算术 + - * / %(取模)
    • / 取整
    • ==++/– 自增自减 顺序== (不是在操作数栈中计算的,是直接在插槽中计算)
      • ++a 先+1,再用a
      • a++ 先用a 再+1
      • 注意;的结束,不管是前面还是后面 都要+1
    • + 拼接字符串
  • 赋值 = += -=
    • += 默认含类型转换
  • 等值 >= == <= !=
  • 逻辑
    • & 都为true | 有一个为true
    • && || 短路 只看前面的表达式结果
  • 位运算符
    • & 全为1 才为1
    • | 有一个1 为1
    • ^ 相同为0 不同为1
    • ~ 0-1 1-0
  • 移位运算
    • >> 有符号右移
    • << 左移
    • >>> 无符号右移
  • 三目运算
    • 表达式?a:b
优先级运算符结合性
1( ) [ ]  .从左到右
2!  ~  ++  –从右到左
3*  /  %从左到右
4+  -从左到右
5<<  >>  >>>从左到右
6<  <=  >  >=  instanceof从左到右
7==  !=从左到右
8&从左到右
9^从左到右
10|从左到右
11&&从左到右
12||从左到右
13? :从左到右
14=  +=  -=  *=  /=  %=  &=  |=  ^=  ~=  <<=  >>=  >>>=从右到左
15从右到左

流程控制

流程

  • 顺序结构
  • 分支结构
    • if else 冗余
    • switch case 有局限性 byte char short int String(hashcode)
  • 循环结构 重复执行某一段逻辑
    • for
    • while
    • do while 先执行一次循环体
1
2
3
for(  1.初始值;2.结束条件 ;4.迭代 ){
3.循环体
}

控制

  • break 退出==当前==分支/循环结构
    • 如果想退出任意循环 + label标记
  • continue 退出本次(continue之后代码的不执行),继续下一次
  • return 结束整个方法 会影响循环外的代码
  • 通过控制循环条件打破

数组

一维

使用场景: 一次存储多个数据 (容器)

定义: 一块连续的内存空间

定义方式: 类型 + []

  • int + [] = int[] 该容器可以存多个int类型 [1,2,3,4]
  • Student + [] = Student[] 该容器可以存多个Student类型 [new Student(),new Student(),s,s2]

赋值 引用类型 new + 类型[5]

注意 赋值的时候需要指定数组的容量

注意 引用类型变量的值 是一块地址值 不是基本值

注意 默认数组有初始值,和数组存储的类型有关系

数组元素

  • 通过 数组变量[下标] 可以取出数组元素
  • 通过 数组变量[下标] 可以设置数组元素的值

注意: 数组下标 0-数组的长度-1

数组属性 length属性

  • 可通过该属性循环操作

  • ```java
    package com.briup.array;
    /*

    通过数组的长度 做循环操作
    

    */
    public class Test4 {

    public static void main(String[] args) {
        // 1.声明并定义数组 容量是5
        int[] arr = new int[5];
        // 2.覆盖数组的默认值
            // 数组的属性length  获取数组的长度
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i+1; // arr[0]  arr[1]
        }
        // 3.取出数组的值
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        
        
    }
    

    }

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


    **数组异常**

    - 空指针异常
    - 引用类型没有赋予地址的话,默认是空地址(null),当用空地址调用方法
    - 数组越界异常 下标 <0 > =lenth

    **补充: 增强for循环**

    ```java
    // 增强for (语法糖) 没有索引,直接取
    for (int value : arr) {
    System.out.println(value);
    }
    // 反编译效果
    for (int i = 0; i < arr.length; i++) {
    int value = arr[i];
    System.out.println(value);
    }

java.lang.Arrays 操作数组的工具类

  • 排序 sort()
  • 查找 binarySearch();
  • 打印 toString();
  • 复制 copyOf();
    • System.arrayCopy();

可变参 : 本质是一个数组 接受多值和数组以及null

1
public void hello(int... args){}

二维

二维数组 int [][] arr = new int[row][col] row代表行(有几个一维数组) col代表列(各一维数组的长度)

1
2
3
4
5
6
// 遍历凡是
for(int i = 0; i< arr.length; i++){
for(intj = 0; j< arr[i].length; j++){

}
}

面向对象

定义

类和对象

  • 类: 一套模板(抽象) class类名
    • 静态行为 成员变量
    • 动态行为 成员方法
  • 对象: 根据模板创建的具体个例(具体)
    • new 类名()
  • 引用(变量)

成员

成员变量

成员变量和局部变量的区别

  • 代码位置 成员: 方法外 类中
  • 初始化 成员:有初始值
  • 生命周期 (debug) 成员:随对象
  • 内存位置 成员: 堆

注意: 如果局部变量和成员变量同名,走局部变量(就近原则)

成员方法

  • 成员方法

    • public 修饰符

    • int 返回值 没有就写void

    • 方法名

    • 参数 局部变量

      • 注意: java的参数传递只有值传递(拷贝值)
        • 基本类型 值为具体数值
        • 引用类型 值为引用地址
          • 具体内容 不是值
    • 异常声明 throws FileNotException

    • 方法重载 参数的个数 顺序 类型不一致才能构成重载

      1
      2
      3
      public int hello(int b) {
      return 0
      }
    • 方法递归(方法调用自己)

      • ```java
        // 斐波那契 第7项13
        // 1 1 2 3 5 8  y=y(x) + y(x-1)
        public static int getN(int n) {
            if(n == 1 || n == 2 ) {
                return 1;
            }// 6 
            return getN(n-1) + getN(n-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
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
        54
        55
        56
        57
        58
        59
        60
        61
        62
        63
        64
        65
        66
        67
        68
        69
        70
        71
        72
        73
        74
        75
        76
        77
        78
        79





        ### 构造方法

        - 特点: 1. 没有返回值也不写void 2.方法名称和类名一样

        - java创建对象 通过new 本质就是调用构造方法

        - 可以重载 可以加参数



        ### 成员代码块

        > 初始化普通成员变量 前提是(先执行完父类的初始化 声明 代码块 构造)

        - 声明时赋值 int a = 1;
        - **代码块赋值 { a =2} 会在构造器之前执行**
        - **构造器赋值**

        **如果同时出现** 归为方法init()

        1. 代码块和声明时赋值有先后关系
        2. 构造器在后面执行



        ### 成员内部类

        > 使用场景: 当类和类的关系密切时(内部类可以方便获取外部类的成员),可以使用内部类



        > 成员内部类

        - 成员内部类可以直接获取外部类的任意成员
        - 外部类成员可以**通过内部类对象**访问内部类的任意成员

        **成员内部类和外部类的区别**

        - ==成员内部类不能拥有静态成员==
        - 静态成员所在的内部类是外部类的普通成员,需要先创建外部类类对象才能开辟内存

        - 内部类可以加修饰符 private static protected



        > 静态内部类

        - 静态内部类可以直接访问外部类的成员

        - 静态内部类的成员方法不能访问外部类的普通成员

        - 外部类可以访问静态内部类的所有成员

        - 如果是静态成员 用静态内部类.成员
        - 如果是普通成员 用静态内部类实例.成员

        - **静态内部类可以定义静态成员**





        > 局部内部类(了解即可)

        - 局部内部类只能在外部类定义的方法中使用。
        - 局部变量为final修饰



        > ==**匿名内部类(局部内部类的简化写法)**====使用场景: 不想手动创建子类/实现类==

        ```java
        Thread t = new Thread(){}
        Runnable r = new Runnable(){}

特性

封装

使用场景: 成员修饰符(属性) private

  • 修饰字段
  • 修饰方法
  • 修饰构造器

一般属性私有,并提供对应的set/get方法

如果构造器私有,其他类无法创建该类对象

修饰符 本类 本包 不同包的子类 不同包的无关类
private 私有
default
protected 子类
public 公有

继承

使用场景: 将共性的代码抽取为父类 通过关键字extends 继承父类可获取父类的非私有成员

注意

  1. java中是单继承,不能直接继承多个类,但是可以间接继承
  2. 子类只能继承父类的非私有成员,但是可以通过继承父类的公有方法访问
  3. Object是所有引用类型的父类
  4. 子类构造器执行之前,会先隐式调用父类无参构造器(super)
  5. 构造方法不能继承

方法重写: 在子父类关系下,如果父类方法不满足子类特性,可重写父类的方法,将来调用的是子类重写的方法

重载 参数

  • 参数个数 类型 顺序

重写 一大两小 参数,方法名一致

  • 重写后的方法 修饰符 >= 父类方法
  • 重写后的方法 返回值 <= 父类方法
    • 基本类型 =
    • 引用类型 <
  • 重写后的方法 异常 <= 父类方法

注意

  • 私有方法不可重写(不报错)
  • 静态方法不可重写(不报错)
  • final修饰的方法不可重写(报错)

多态

使用场景: 通常将父类/接口放在参数位置,复用代码

1
2
3
4
5
6
父类  变量 = new 子类()
接口 变量 = new 实现类()
成员变量 编译看父类 运行看父类
成员方法 编译看父类 运行看子类

向下转型注意事项: instanceof

关键字

static

使用场景: 将对象范围提升为类范围

  1. static修饰的成员 属于类 所有对象共享 不属于对象
  2. 存储在方法区的静态中
  3. 可以直接使用类名调用
  4. 静态区内存优先于对象创建
  5. 静态不能访问非静态

修饰成员变量 静态成员变量

  • static不能修饰局部变量

修饰成员方法 静态成员方法

  • 问题1: 成员方法存储位置(方法区) 静态方法存储位置(方法区的静态区)
  • 问题2: 为什么静态方法不能访问普通成员变量
  • 问题3: 为什么静态方法没有this的局部变量
    • 静态方法不能访问普通成员变量
    • 局部变量不能加static

修饰代码块 静态代码块 static{} 用于做类的初始化擦欧总

  • 局部代码块 方法内
  • 成员代码块 在构造器之前执行, 构造器执行几次,代码块执行几次
  • 静态代码块 类加载一次,代码块执行一次

类初始化 cinit

  • 声明时赋值 static int a = 1;
  • 静态代码块赋值 static { a = 2}

如果同时出现,谁在前面谁先执行

main函数最后执行

类什么时候加载 (懒加载)

什么时候用 什么时候加载类

类什么时候初始化(静态代码块什么时候执行)

  • new
  • 第一次使用了该类静态成员
  • 调用父类的静态成员,只会初始化父类

类什么情况不会初始化

  • 类型+[] 数组类型不会初始化
  • 静态常量 编译期间确定

补充: 类的加载过程

  • 加载
  • 链接
    • 校验 字节码检验
    • 准备 类成员开辟空间并赋初始值
    • 解析 符号引用解析
  • 初始化 cinit
    • 静态赋值以及静态代码块执行

this

this 指带将要创建的对象 (非静态方法 默认含有this的局部变量)

  • 区分成员变量和局部变量 指代的就是当前对象
  • 调用其他成员方法
  • 调用其他构造器 只能放第一行

super

使用场景: 调用父类的成员

  • 区分子类成员变量和父类成员变量
  • 调用父类的成员方法
  • 调用父类构造器 只能放第一行

==当调用其他构造器的时候,this()和super()不能同时使用==

final

使用场景: 限制类 方法 变量

  • final-修饰类 该类不可被继承
  • final-修饰方法 该方法不可被重写
  • final-修饰变量 ->常量 不能二次赋值 (显示初始化赋值)
    • 如果是基本类型变量 不能直接二次赋值
    • 如果是引用类型变量 不能再指向新的地址,但是改原地址的内容

abstract

使用场景: 当父类的方法没有实际含义,需要被重写时,就定义为抽象方法

特点

  • 类上加了abstract关键字即抽象类
  • 含有抽象方法的类必须定义为抽象类
  • 抽象方法一定要被子类重写,除非子类也是抽象类
  • 抽象类不能实例化(new),但是有构造器(给子类使用 初始化父类成员)
  • 抽象类可以同时存在普通方法和抽象方法

普通类和抽象类

  • 抽象类: abstract
  • 抽象类: 多了抽象方法
  • 抽象类: 不能实例化

注意: abstract不能和final同时使用

interface

使用场景: 定义方法规范

特征

  • 用interface修饰
  • 成员变量都是 public static final
  • 成员方法都是 public abstract

接口和抽象类

  • 关键字不一样

  • 接口 方法只能是抽象方法

    • jdk8的新特性后可以有默认方法和静态方法实现

    • ```java
      default void show() {

      }
      static void hello() {

      }

      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



      - 接口变量都是静态常量

      - 接口多实现(多继承) 类单继承

      - 接口和接口 是多继承
      - 类和接口是多实现
      - 类和类 是单继承

      - 接口没有构造器









      # 枚举

      > 使用场景: 如果该类的对象个数明确,建议使用枚举

      - 该类默认使用final修饰,继承了Enum类
      - 写的**字符串**对象将来就是 static final 枚举类对象
      - 构造器私有
      - 方法
      - Gender[] values() 返回该类的所有对象
      - Gender valueOf("");
      - getName(); 获取枚举对象名称
      - getOrderinal() 获取枚举对象所在位置

      ```java
      public enum Gender{
      MAN,WOMAN; // 必须放第一行,如果后面有代码,加上分号做分割
      }


      // 获取枚举对象的方式
      Color blue = Color.blue;
      Color blue2 = Color.valueOf("blue");
      Color[] values = Color.values();
      Color blue3 = values[2];
      System.out.println(blue == blue2);

系统基类

注意: java.lang下的类不需要通过import导入

基类 含义
java.lang.System 系统类 输入输出流
java.lang.Math 数学 绝对值 正余弦 平方 开方 Π
java.util.Scanner 扫描类
java.util.Random 随机数类
java.util.Arrays 操作数组的类
java.lang.String 字符串类型
java.lang.Integer 包装类

包装类

使用: 当基本数据类型无法满足条件

基本类型 包装类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

基本类型->包装类型

  • new Integer(基本类型)

  • Integer.valueOf(基本类型) == 自动装箱: Integer a = 1;

    • 默认会自动缓存[-128到127]的地址 (IntegerCache)

包装类型->基本类型

  • new Integer().intValue()
  • 自动拆箱: int b = a;

==基本类型和包装类型比较,只比较值. Integer重写equals也只比较值==

Object

equals: 使用场景: 当不想比较地址时

  • ==和equals的区别

  • == 是操作符, 基本类型和引用类型都能使用

    • 基本类型比较值
    • 引用类型比较地址
  • equals是方法 引用类型才能用,默认比较地址

    • 将来可以重写比较逻辑

hashcode() 可以理解为 地址经过hash 得到我们打印的值(hashcode)

使用场景: 利用hash特性快速比较对象

  • 对象相同,hashcode相同
  • hashcode相同,对象不一定相同
  • hashcode不同,对象一定不同

==equals和hashcode的关系==

  • equals相同,hashcode相同
  • hashcode相同,equals不一定相同
  • hashcode不同,equals一定不同

先比较hashcode,筛选一部分对象,再用equals比较

image-20230804104244280

getClass 返回运行时的类,该方法无法重写

使用场景: 判断运行时的类型是否为同一个

toString 默认打印对象的全类名+@+对象hashcode的16进制字符串

使用场景: 查看对象日志

String

创建方式

  • String a = new String(“a”) 堆
    • 字符串常量池
    • 堆 默认指向的地址
  • String a “hello” 字符串常量池(堆/方法区)

String是不可变字符串,为什么? 由final字符数组构成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestString {
public static void main(String[] args) {
String s = "hello"; // new String("hello");
setS(s);
System.out.println(s); // hello
}
public static void setS(String s) {
s = "world!!!";
}
}
原因:
final class String
private final char[] value;
未提供修改字符内容的方法

StringBuilder 拼接字符 可变的字符数组 (原内容修改字符串)

  • new出来的字符串通过+拼接,默认使用StringBuilder拼接的

问题: 以下创建了几个对象

  • String b = “b”; 创建了几个对象

    • 字符串常量池
  • String a = new String(“a”); 创建了几个对象

    • a 字符串常量池
    • new String(“a”) 默认指向堆中
  • String ab = new String(“a”) + new String(“b”)

    • 字符串常量a
    • 字符串常量b
    • 堆 new String(“a”)
    • 堆 new String(“b”)
    • new StringBuilder();
    • new StringBuilder().toString() new String(“ab”)

**a.intern方法 **

  • 如果a已经存在于字符串常量池中,那么a不会放入,但是返回字符串常量池的引用
  • 如果a不存在于字符串常量池中,那么a会放入(a指向字符串常量池),而且返回字符串常量池的引用

集合

结构

集合的结构

  • 数据结构 (集合的特性) 栈 顺序表 树 队列
  • 集合接口(方法规范) MyStack
    • push(Object value)
    • pop()
    • peek();
    • show();
  • 集合实现类(数组实现 链表实现)
    • ArrayStack
    • LinkedStack

Collection

单列容器(Collection) 广泛的概念, 提供通用的方法,具体的容器接口会继承该结构

  • 通用方法 大小,清除内容,是否包含,添加,删除…
  • 遍历方式
    • 数组遍历
    • 增强for循环
    • 迭代器遍历 extends Iterable

List

List接口 具体容器 继承Collection 接口 实现类 动态数组/顺序表(数据结构)

特点

  • 存取有序
  • 带索引(不是数组索引)
  • 元素重复

实现类

  • ==ArrayList 本质是数组实现==
    • 增删慢 查找快
    • 扩容1.5倍
    • 线程不安全,效率快
  • LinkedList 本质是双向链表
    • 增删快 查找慢
  • Vector(Stack的父类) 本质是数组
    • 线程安全(锁),效率慢
    • 扩容2倍

Set

特性

  • 存取无序
  • 自动去重(hashcode和equals)
  • 无索引

实现类 HashSet 底层使用哈希表

image-20230809141254981

特性

  • 存null
  • 查找效率高
  • 存取无序
  • 去重
  • 无索引

实现类 TreeSet -> SortedSet->Set

  • 排序
    • 自然排序 Comparable Pojo实现该接口
      • this.compareTo(Object o)
        • 第一次只有一个值 this == o
        • 第二次this是新值 o是老值
          • ==新值的位置取决于返回值==
            • 返回正数 新值放后面
            • 返回负数 新值放前面
    • 比较器排序 Comparator 构造器加上该参数
      • compareTo(Object o1,Object o2)
        • 第一次只有一个值 o1 == o2
        • 第二次o1是新值 o2是老值
  • 去重(根据Comparable.compareTo返回值 == 0)
  • 无索引
  • 不能存null

实现类 LinkedHashSet

  • 存取有序
  • 去重
  • 无索引
  • 可以存null

Map

双列容器(Map)

特性

  • 键值对
  • 特性和set相似 (主要说的是key)Set借助于Map实现
    • 不能重复
    • 存取无序
    • 无索引

遍历方式

  • keySet 取出所有key,然后取value
  • entrySet 取出所有的Entry(键值结构)

实现类 HashMap -》 HashSet

特征

  • 存取无序
  • 键不能重复,值能重复
  • ==键值可以为空==
  • 线程不安全

实现类HashTable 用法和HashMap一致

  • 存取无序
  • 键不能重复,值能重复
  • 线程安全
  • ==键值对都不能为空==

实现类 TreeMap

  • 排序

    • 自然排序 Comparable
    • 比较器排序 Comparator
  • 键不能重复,值能重复

  • ==键不能为空==

linkedHashmap

  • 存取有序
  • 键值可以为空

泛型

使用场景: 代码复用,解决安全性问题

特征

  • 类型参数化 <>
  • 如果不加泛型,默认容器存储的类型是Object

泛型种类

  • 泛型类
  • 泛型接口
  • ==泛型方法==
    • 泛型声明在方法前(和泛型类没有关系)
    • 参数的泛型和返回的泛型一致

==注意: 泛型之间没有子父类关系,但是可以用?充当多态==

通配符 ?

  • ?是泛型的父类
  • 但是只能遍历 不能添加,除了null

泛型边界 解决?接受类型宽泛的问题

  • 泛型上限 <? extends 具体类型>
    • ?可以用子类型或者实现类
  • 泛型下限 <? super 具体类型>
    • ?可以用父类型或者接口类型

泛型擦除 编译成功后,是没有泛型的

  • 泛型不能构成重载
  • 泛型擦除后统一为Object

注解

每个注解有特殊含义,将来编译器能识别

使用场景:自定义注解来判断方法权限

定义注解

  • 每个注解上都有元注解 (修饰注解的注解)
    • @Target 注解的使用范围 (默认是所有范围)
      • METHOD
      • CLASS
    • @Retention 注解的保留策略 (默认是CLASS)
      • SOURCE
      • CLASS
      • ==RUNTIME==
1
2
3
4
5
6
7
8
@Target({ElementType.METHOD,ElementType.TYPE}) // 元注解: 注解应用的位置
@Retention(RetentionPolicy.SOURCE) // 元注解: 保留策略
@interface Role{
// 属性 @Role(name="guest");
public String name() default "";
// 如果属性的名字是value,可以省略@Role("admin")
public String value() default "";
}

异常

==学习目标==

  • 异常使用场景
  • 异常种类
  • 异常传播
  • 异常处理
    • 抛出
    • 捕获(try catch finally)
  • 断言(了解)

异常体系

Throwable

  • Exception
  • error

异常种类

Exception

  • 编译时异常 继承Exception
    • FileNotFoundException
  • 运行时异常 继承RuntimeExcetipn
    • NullPointException
    • ClassCastException
    • ArthemticException
    • ArrayIndexOutofBoundsException
    • NumberFormatException

异常传播

异常如果没有人处理,会一直向调用者传递,直到jvm,打印信息到控制台

异常处理

  • 创建并抛出异常 throw
    • 自动抛出
      • 运行时异常
      • 编译时异常
        • 通过throws先声明异常(具有传播行为)
          • 抛出者只能声明
          • 调用者可以声明也可以处理
    • 手动抛出
      • 运行时异常
      • 编译时异常
        • 通过throws先声明异常
  • 捕获异常 try catch [finally] (确保程序正常运行)
    • try 中的代码可能会被捕获
    • catch
      • 参数的异常类型是比对捕获的类型
      • 代码块是检测到异常后的行为,可不写
    • finally 代码最终执行

补充

  • try中有return

    • finally没有,方法寄存了return中的值,增量无用
    • finally有return, 覆盖try的值
  • try或者catch中有throw

    • 先要执行finally,再回去执行throw的代码
    • 如果finally有return,会吞掉异常

==总之:finally中不要写return==

断言 assert

线程

使用场景:多线程可以解决资源耗时 提升效率

==学习目标==

  • 理解进程和线程
  • 线程的调度策略
  • ==线程的创建方式==
  • 守护线程 优先级 线程组(了解)
  • ==线程状态(笔试面试掌握)==
  • ==线程安全和线程通信==
  • 死锁

概述

进程和线程

进程指一个内存中运行的应用程序,它是系统运行程序的基本单位。

线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中 至少有一个线程

JVM支持多线程(如垃圾回收器)

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
package com.briup.thread;

public class Test01_JVM {
public static void main(String[] args) throws InterruptedException {
for (int i = 1; i < 1000000; i++) {
new Test(i);
}
// 通知垃圾回收器 需要回收对象(不一定回收)
System.gc();

// 当前线程睡眠3秒 此时不占用资源
Thread.sleep(300000);
System.err.println("main end...");
}
}

//main线程休眠3s
class Test {
int n;

public Test(int n) {
this.n = n;
}

//当GC进行垃圾回收指定对象的时候,对象的finalize方法会被自动
@Override
protected void finalize() throws Throwable {
System.out.println("Test被销毁, n: " + n);
super.finalize();
}
}

使用jsconsole查看线程情况

image-20230815103506854

线程调度策略(并发 单核)

  • 多线程共享同一个cpu的 此时是轮流获取cpu
  • 哪个线程获取了cpu,此时才能运行,没有获取cpu的,就等待
    • 获取到cpu的这段时间,叫时间片
    • 时间片不会被某线程独享,会随机切换分配
  • 线程调度策略(分配时间片方案)
    • 轮流
    • 抢占(jvm)
      • 线程等级高的优先获取时间片,如果等级一样,随机分配

内存分配

  • 每个线程拥有各自的栈空间 互相独立
  • 线程共享堆和方法区

创建

线程的创建方式 (线程的执行顺序不可控)

  • 创建Thread/Thread子类

  • 通过Runnable创建Thread (复用run方法)

  • 通过Callable创建有返回值的线程

    • call方法虽然有返回值,但是会导致阻塞
  • 通过线程池创建

种类

线程的种类(了解)

  • 前台线程
    • 如果存在前台线程,jvm不会关闭
    • 默认开启的线程是前台线程,可以手动设置为守护线程
  • 守护线程

方法

  • 静态方法
    • currentThread
  • 普通方法
    • run 线程的执行逻辑 如果直接调用,不是新线程 是main线程
    • start 开启线程,会自动调用run
    • set/getName() 设置、获取线程名字
    • setPriority(10) 设置线程优先级
    • setDaemon(true) 守护线程
    • stop() 停止正在运行的线程(不建议使用 有安全隐患)

==触发线程状态转换的方法==

  • sleep(时间) == TimeUnit.SECONDS.sleep(10);

    • 当前线程阻塞睡眠(timed_waitting),10秒后会自动进入可运行状态(runnable)
    • 期间不获取时间片,无法运行
    • 在匿名内部类中不能抛出异常,只能捕获异常
    • 单位是毫秒
  • join() t1.join

    • 当前线程阻塞无限等待(waitting),等插入的线程执行完进入可运行状态(runnable)
  • join(时间) t1.join 类似sleep

    • 当前线程阻塞有限等待(timed_waitting),等时间过后完进入可运行状态(runnable)
  • wait()

  • notify();

状态

==线程状态==

  • 正常状态 NEW -> RUNNBALE(Runnable(就绪)/Running(运行)) -> TERMINED
  • 阻塞状态
    • timed_waitting 有限等待
    • waitting 无限等待
    • blocked 锁阻塞

状态变化

  • NEW

    • 创建线程时
  • RUNNABLE

    • start-》就绪 时间片-》运行
    • sleep时间结束回到就绪状态
    • join插入的线程执行完
    • join时间过后
    • 处于锁阻塞的线程抢到锁后
    • notify/notify();
  • TIMED_WAITTING

    • sleep(时间)
    • join(时间)
  • WAITTING

    • join(); 本质就是调用wait() t1.join()
    • wait();
  • BLOCKED

    • 线程没抢到锁
  • TERMINATED

    • run方法结束

安全

线程安全问题 (多线程 并发 共享资源,可能导致读的预期效果不一致)

解决思路: 控制临界资源只能同时被一个线程访问

==安全和效率==

  • 线程安全 效率低 StringBuffer Vector
  • 线程不安全 效率高 StringBuilder ArrayList

兼顾安全和效率(并发专题 无锁机制 cas)

  • 通过工具类将线程不安全的转为线程安全

    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
    package com.briup.safe;

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

    /**
    * StringBuilder 拼接字符串
    * @author vanse
    *
    */
    public class TestStringBuilder {
    public static void main(String[] args) {
    StringBuffer sb1 = new StringBuffer("a");
    // 链式编程 在字符数组自身改变
    StringBuilder sb = new StringBuilder("a");
    String finalS = sb.append("b").append("c").toString();
    System.out.println(finalS);

    // s+= "bc"; 字符数组没有提供设置内容的方法

    List<String> list = new ArrayList<>();
    // 线程安全的ArrayList
    List<String> synchronizedList = Collections.synchronizedList(list);



    }
    }

具体方案 锁机制

  • synchronized jdk提供的默认关键字,可对临界区上锁,保护资源
    • 修饰代码块 同步(原子)代码块
      • 用对象充当锁
      • 用类充当锁
    • 修饰方法 同步方法 只要保证同一个锁对象即可
      • 如果是普通方法 锁对象是当前对象this
      • 如果是静态方法 锁对象是当前类

==注意: 多个线程必须使用同一把锁对象==

通信

线程通信 (解决线程之间以某种规则运行的问题)

生产消费队列模型(mq)

通过等待唤醒机制 Object的方法wait和notify

  • wait() 当前线程进入无限等待状态,除非调用同一个锁对象的唤醒方法
    • 会释放锁
    • 可能存在虚假唤醒的问题
  • notify() 将等待池中的某个线程唤醒
  • notfifyAll() 唤醒等待池中的所有线程唤醒

这两个方法都需要在同步资源中使用,并且需要用锁对象调用

案例1: 线程1-5 线程2 11-15 线程1 6-10 线程2 16-20

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.briup.communication;

/**
* 线程通信1
*
* @author vanse
*
*/
public class Test1 {
static Object mutex = new Object();

public static void main(String[] args) {
Thread t1 = new Thread("t1 ") {
// 1-10
public void run() {
// 此时锁范围可以放大,因为wait等待时会释放锁
synchronized (mutex) {
for (int i = 1; i <= 10; i++) {
System.out.println(this.getName() + i);
if (i == 5) {
// 该线程进入无限等待 除非有人唤醒
// 该方法必须在同步中使用
// 该方法必须是同一个锁对象调用
try {
// 1. 1 2 3 4 5
mutex.notify();
mutex.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
mutex.notify();
}
};
};
Thread t2 = new Thread(" t2 ") {
// 10-20
public void run() {
synchronized (mutex) {
for (int i = 11; i <= 20; i++) {
System.out.println(this.getName() + i);
if (i == 15) {
try {
// 2. 15-20
// 等待之前应该把t1唤醒
mutex.notify();
mutex.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
mutex.notify();
}
};
};
t2.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.start();
}
}

案例2 小明和小红有一个共同账户,小明一直存钱,存完之后小红可以一直取钱

  • 小红(没钱等小明存完再取)
    • 没钱wait
    • 取完后唤醒小明存钱
  • 小明 (账户<1000 存)
    • 余额>1000 wait
    • 每次存完钱 唤醒小红
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.briup.communication;

import java.util.Random;

/**
* 双线程 通信取钱 无需轮流
* @author vanse
*/
public class Test2 {
public static void main(String[] args) {
Account a = new Account();
Random random = new Random();
Thread t1 =new Thread("小明") {
// 存钱
public void run() {
while(true) {
a.deposit(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
Thread t2 =new Thread("小红") {
// 取钱
public void run() {
while(true) {
a.withdraw(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
t1.start();
t2.start();
}
// 共同账户
static class Account{
public int balance; // 余额
// 存钱
public synchronized void deposit(int money) {
try {
while(balance > 1000) {
wait(); // 当余额>1000 等小红取钱
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 存钱逻辑(被小红唤醒了)
balance += money;
String name = Thread.currentThread().getName();
System.out.printf("%s存了%d,余额为%d\n",name,money,balance);
notify(); // 通知小红(有可能在等待)
}
// 取钱
public synchronized void withdraw(int money) {
try {
// 可能有虚假唤醒的问题
while(money >= balance) {
wait(); // 取的钱不能大于余额 | 没有钱 等小明存钱
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 取钱逻辑(被小明唤醒了)
balance -= money;
String name = Thread.currentThread().getName();
System.out.printf("%s取了%d,余额为%d\n",name,money,balance);
notify(); // 通知小明(有可能在等待)
}
}
}
/**
* 效果:
* 小明存了 10
* 小明存了 20
* 小明存了 980(此时余额>1000 不会继续存了,等小红取)
* 小红取了800 (如果继续取的钱大于余额,不能再取了,等小明存)
* 小明存了 100
*/



案例3 小明和小红和小绿有一个共同账户,小明一直存钱,存完之后小红和小绿可以一直取钱

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.briup.communication;

import java.util.Random;

/**
* 双线程 通信取钱 无需轮流
* @author vanse
*/
public class Test2 {
public static void main(String[] args) {
Account a = new Account();
Random random = new Random();
Thread t1 =new Thread("小明") {
// 存钱
public void run() {
while(true) {
a.deposit(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
Thread t2 =new Thread("小红") {
// 取钱
public void run() {
while(true) {
a.withdraw(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
Thread t3 =new Thread("小绿") {
// 取钱
public void run() {
while(true) {
a.withdraw(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
t1.start();
t2.start();
t3.start();
}
// 共同账户
static class Account{
public int balance; // 余额
// 存钱
public synchronized void deposit(int money) {
try {
while(balance > 1000) {
wait(); // 当余额>1000 等小红取钱
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 存钱逻辑(被小红唤醒了)
balance += money;
String name = Thread.currentThread().getName();
System.out.printf("%s存了%d,余额为%d\n",name,money,balance);
notifyAll(); // 通知小红(有可能在等待)
}
// 取钱
public synchronized void withdraw(int money) {
try {
// 可能有虚假唤醒的问题
while(money >= balance) {
wait(); // 取的钱不能大于余额 | 没有钱 等小明存钱
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 取钱逻辑(被小明唤醒了)
balance -= money;
String name = Thread.currentThread().getName();
System.out.printf("%s取了%d,余额为%d\n",name,money,balance);
notifyAll(); // 通知小明(有可能在等待)
}
}
}
/**
* 效果:
* 小明存了 10
* 小明存了 20
* 小明存了 980(此时余额>1000 不会继续存了,等小红取)
* 小红取了800 (如果继续取的钱大于余额,不能再取了,等小明存)
* 小明存了 100
*/



案例3: 小明和小红依次存取钱

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.briup.communication;

import java.util.Random;

/**
* 双线程 通信取钱 轮流
* @author vanse
*/
public class Test3 {
public static void main(String[] args) {
Account a = new Account();
Random random = new Random();
Thread t1 =new Thread("小明") {
// 存钱
public void run() {
while(true) {
a.deposit(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
Thread t2 =new Thread("小红") {
// 取钱
public void run() {
while(true) {
a.withdraw(random.nextInt(1000));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
};
t1.start();
t2.start();
}
// 共同账户
static class Account{
public int balance; // 余额
boolean flag; // 默认false 是否存过
// 存钱
public synchronized void deposit(int money) {
try {
while(flag) { // 不会执行
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 存钱逻辑(被小红唤醒了)
balance += money;
String name = Thread.currentThread().getName();
System.out.printf("%s存了%d,余额为%d\n",name,money,balance);
notify(); // 通知小红(有可能在等待)
flag = true; // 存过

}
// 取钱
public synchronized void withdraw(int money) {
try {
// 可能有虚假唤醒的问题
while(!flag) { // false
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
// 取钱逻辑(被小明唤醒了)
if(money < balance) {
balance -= money;
String name = Thread.currentThread().getName();
System.out.printf(" %s取了%d,余额为%d\n",name,money,balance);
notify(); // 通知小明(有可能在等待)
flag = false; // 取过
}
}
}
}
/**
* 效果:
* 小明存了 10
* 小明存了 20
* 小明存了 980(此时余额>1000 不会继续存了,等小红取)
* 小红取了800 (如果继续取的钱大于余额,不能再取了,等小明存)
* 小明存了 100
*/



案例4: 小明和小红和小绿依次存取钱,小明存(2000),小红取(1000),小绿取(1000)

标识:1:小明 2:小红 3:小绿

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.briup.communication;

public class Test4 {
public static void main(String[] args) {
Account a = new Account();
// 三个线程 各自独立
Thread t1 = new Thread("小明") {
public void run() {
while (true) {
synchronized (a) {
while (a.flag != 1) { //
try {
a.wait();
} catch (InterruptedException e) {
e.printStackTrace();
} // 等待
}
// 存钱 a.flag == 1 需要存钱
a.balance += 2000;
System.out.printf("%s存了2000,余额为%d\n", Thread.currentThread().getName(), a.balance);
a.flag = 2; // 指定小红线程取钱
a.notifyAll();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
};
Thread t2 = new Thread("小红") {
public void run() {
while (true) {
synchronized (a) {
// 如果flag不是2
while (a.flag != 2) {
try {
a.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 小红取钱
a.balance -= 1000;
System.out.printf("%s取了1000,余额为%d\n", Thread.currentThread().getName(), a.balance);
a.flag = 3; // 指定小红线程取钱
a.notifyAll();
}
}
};
};
Thread t3 = new Thread("小绿") {
public void run() {
while (true) {
synchronized (a) {
// 如果flag不是3
while (a.flag != 3) {
try {
a.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 小红取钱
a.balance -= 1000;
System.out.printf("%s取了1000,余额为%d\n", Thread.currentThread().getName(), a.balance);
a.flag = 1; // 指定小红线程取钱
a.notifyAll();
}
}
};
};
t1.start();
t2.start();
t3.start();
}

static class Account {
int balance; // 0
int flag = 1; // 1:存 2 3
}
}

死锁

BIO

==学习目标==

  • File使用
  • IO流(BIO blocked io 阻塞io)

FIle

文件的创建方式

  • new File(“”)
  • new File(“父目录”,”子文件”)
  • new File(父目录文件,”子文件”)

==注意1:windows是\ unix是/==

==注意2: 相对和绝对路径==

==注意3:File是代表目录和文件,真实路径字符串是否存在不影响file的创建,不会空对象==

文件的方法

操作 方法
是否存在 exists()
文件名字 getName()
查看文件绝对路径 getAbsolutePath()
是否文件 isFile()
是否目录 isDirectory()
文件大小 length()
列举目录 listFiles()
创建文件 createNewFile()
创建文件夹 mkdirs()
删除文件 delete()

补充 递归遍历指定文件

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
47
48
package com.briup.io;

import java.io.File;
import java.io.FileFilter;

public class Test1 {
public static void main(String[] args) {
String path = "/Users/vanse/Desktop/jd2312/a";
// 该文件夹下的txt结尾的文件 绝对路径 ||
// File dir = new File(path);
// 筛选后的数据 根据返回值true
// File[] filterFiles = dir.listFiles(new FileFilter() {
// // 如果返回true,将筛选的file放入返回的数组中
// @Override
// public boolean accept(File file) {
// String name = file.getName();
// return name.endsWith("txt") && file.isFile() ; // true 筛选成功
// }
// });
// for (int i = 0; i < filterFiles.length; i++) {
// System.out.println(filterFiles[i].getAbsolutePath());
// }
// a目录 下所有的文件
File dir = new File(path);

// 递归
filterFile(dir);
}
private static void filterFile(File dir) {
// 根文件项是目录
if (dir.isDirectory()) {
// 拿出根文件的二级文件项
File[] listFiles = dir.listFiles();
// for结束,递归会退出
for (int i = 0; i < listFiles.length; i++) {
File file = listFiles[i];
// 如果是文件 打印
if (file.isFile() && file.getName().endsWith("txt")) {
System.out.println(file.getAbsolutePath());
} else {
// 如果是目录 b 将b当成新的根目录
filterFile(file);
}
}
}
}
}

IO流

节点流

Inputstream / Outputstrem 文件上传/下载、在线视频

分类

  • 从流向 王文文拿到我的u盘 IOTest.java
    • 输入流 读文件 读u盘里的内容
    • 输出流 写文件 将u盘里的内容写到磁盘中
  • 从类型
    • 字节流 可以包含文字 图片 视频 音频
    • 字符流 包含文字(中)

流特点

  • 字符/字节
  • 输入/输出
  • 流向

步骤

  • 声明
  • 创建
  • 使用
  • 关闭

字节流 Inputstream / Outputstream 可读写文本 图片 音频等任意文件

==文件字节流 (目的:文件中读写)==

  • FileInputStream 文件字节输入流
    • int read(); 每次读一个字节,返回字节对应的ASCII码 a -> 97
    • int read(byte[] arr) 每次读arr.length个长度的字节,返回本次读到的字节数
    • int read(byte[] arr,0,len) 不常用 从数组的指定位置存储,存多少个字节
  • FileOutputStream 文件字节输出流
    • new FileOutputStream(文件,true) 该文件自动创建 true代表文件追加
    • void write() 每次写一个字节的ASCII码,实际写的是数据 97->a
    • void write(byte[] arr) 将arr数组的数据写到文件中
    • void write(byte[] arr,0,len) 写指定长度的数组
    • void flush() 将缓冲区的流刷新

==注意:中文在windows(默认gbk)占2个字节,在linux(默认linux)占3个字节==

==字节数组流 目的:字节数组中读写==

  • ByteArrayInputStream 字节数组输入流
    • 从内部的数组 读数据
    • read
  • ByteArrayOutputStream 字节数组输出流
    • 写数据到内部的数组(默认32个长度)中
    • write
    • flush

字符流 Reader/Writer 只能读写文本文件 读写中文不乱码

文件字符流

  • FileReader 文件字符输入流
    • int read()
  • FileWriter 文件字符输出流
    • wirte(char c)
    • wirte(char[] arr)
    • write(char[] arr,int offset,int len)
    • wirte(String str)
    • write(String,int offset,int len)

包装流

  • 标准输入输出字节流

    • 标准输出流 PrintStream ,可包装字节流

      • write() / 配合flush()方法
      • 写的是整数值,但是实际写的是对应字节数据 97 a
      • println() 该方法可以打印字节和字符
        • println() 会原样输出
        • 本质上调用write方法,并且换行(和操作系统相关)
    • 标准输入流 BufferedInputStream 可包装字节流

      • read() 会导致阻塞,需要等待输入
      • 回车换行 也会被输出
        • idea用的linux的分隔符10 \n
        • sts用的windows 13 10 \r\n
  • ==缓冲字节流 提升效率,内部有缓冲区(数组 1024*8)==

    • 缓冲字节输入流 BufferedInputStream

      • 额外支持方法 mark()和reset() 标记和重置标记,当流有缓冲区时才能调用该方法

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        public class Test2 {
        public static void main(String[] args) {
        try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream("img.png"));) {
        fis.mark(Integer.MAX_VALUE); // 标记大小要足够大 不然重置报错
        for (int i = 1; i <= 5; i++) {
        try (BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("img_copy" + i + ".png"))) {
        int len;
        while ((len = fis.read()) != -1) {
        fos.write(len);
        }
        fis.reset();
        }
        }
        } catch (Exception e) {
        e.printStackTrace();
        }
        }
        }
    • 缓冲字节输出流 BufferedOutputstream

  • ==缓冲字符流 提升效率,并且有增强方法==

    • 缓冲字符输入流 BufferedReader
      • ==readLine() 每次读一行 如果是空行(回车),忽略不读(空字符会读的)==
      • ready() 判断是否到文件末尾
    • 缓冲字符输出流 BufferedWriter
      • newLine() 换行
      • write(String s) 写字符串
  • 字符转换流 1.把字节转换为字符 2.指定编码去解析

    • 用UTF-8编码读GBK编码 编码和解码不一致则会乱码

    • ```java
      InputStreamReader isr = new InputStreamReader(fis,StandardCharsets.UTF_8);) {
      InputStreamReader isr = new InputStreamReader(fis,Charset.forName(“gbk”)

      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

      - 数据流DataInputStream(了解即可)

      - 提供了各种基本数据类型的读写

      - **对象流** 用于读写对象

      - ObjectInputStream 对象输入流

      - Object readObject();将对象字节重新变为对象
      - **反序列化不调用构造器 (不是只有new构造器才能创建对象)**
      - 反序列化的地址和原地址不一样

      - ObjectOutStream 对象输出流

      - 对象需要实现序列化接口Serializable

      - writeObject() 写的是对象的字节

      - 如果要写多对象,写容器即可,容器的元素会一起写入

      - **transient** 可以控制个别字段不序列化

      - **如果在序列化过程有字段的添加、修改 此时要确保序列化id一致**

      ```java
      private static final long serialVersionUID = 1L;

      **private** **static** **final** **long** **serialVersionUID** = 5104848660142885299L;

==特殊的包装流:PrintWriter() 打印流==

  • 同时包装字节流和字符流
  • 有println方法(会多输出一行,但是回车不计入正文)
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
package com.briup.wrapper.buffer;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
/**
* 额外方法 每次读一行
* @author vanse
*
*/
public class TestBufferCharIO3 {
public static void main(String[] args) {
try (
// 文本 每次读一行
BufferedReader br = new BufferedReader(
new FileReader("a.txt"));
PrintWriter pw = new PrintWriter(
new FileWriter("b.txt"));
) {
// 使用流
String line = null;
// 每读一行
while( (line = br.readLine()) != null ) {
// 写一行
pw.println(line);
}
pw.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}

==特殊: RandomAccessFile 任意访问流不属于4种基本类流子类==

  • 能读能写

  • 反复定位 seek()

    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
    47
    48
    49
    50
    51
    package com.briup.io;

    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.RandomAccessFile;

    /**
    * 任意访问流 能读能写 反复定位
    * 将a.txt文件内容:hello world123,
    * 使用随机访问流进行内容替换,完成后文件
    * 内容:hello briup123
    *
    * @author vanse
    *
    */
    public class TestRandomAccessIo {
    public static void main(String[] args) {
    // hello world123
    // hello briup123
    // 1.创建任意访问流
    RandomAccessFile ra = null;
    try {
    ra = new RandomAccessFile("a.txt", "rw");
    // 2.定位到需要修改的位置 从0开始
    // 读写文件会使指针变动
    // System.out.println(ra.getFilePointer());
    ra.seek(6);
    ra.write("briup".getBytes()); // 11
    System.out.println(ra.getFilePointer());
    ra.seek(0);
    // 3.重新读文件
    int len = -1;
    while( (len = ra.read()) != -1) {
    // ra.write(len);
    System.out.write(len);
    System.out.flush();
    }

    } catch (Exception e) {
    e.printStackTrace();
    }finally {
    if(ra != null) {
    try {
    ra.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

work

作业1 如果现在有一款只能试用10次的软件,超过10次之后就需要提醒用户购买正版软件。(程序运行一次,

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
47
48
49
package com.briup.homework.io;


import java.io.*;

/**
* @Auther: vanse(lc))
* @Date: 2023/8/22-08-22-22:36
* 编程实现:如果现在有一款只能试用10次的软件,超过10次之后就需要提醒用户购买正版软件。(程序运行一次,
* 使用次数就要减一次)
*/
public class Test3 {
public static void main(String[] args) {
File file = new File("a.txt");
if (!file.exists()) {
try {
file.createNewFile();
} catch (Exception e) {
e.printStackTrace();
}
}
try (FileInputStream fis = new FileInputStream(file)) {
byte[] arr = new byte[2];
int len = fis.read(arr);
System.out.println("len = " + len);
int intCont = 0;
try {
String strCount = new String(arr, 0, len);
intCont = Integer.parseInt(strCount);
System.out.println("strCount = " + strCount);
} catch (NumberFormatException e) {
e.printStackTrace();
} finally {
System.out.println("intCont = " + intCont);
if (intCont > 5) {
System.out.println("使用次数已到");
System.exit(-1);
}
try(PrintStream fos = new PrintStream(new FileOutputStream(file));) {
fos.print(++intCont); // 使用了1次
fos.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

作业2: 复制10张图片,命名img_copy_i_.png

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
package com.briup.homework.io;

import java.io.*;

/**
* @Auther: vanse(lc))
* @Date: 2023/8/22-08-22-22:08
* @Description:com.briup.homework.io
*/
public class Test2 {
public static void main(String[] args) {
try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream("img.png"));) {
fis.mark(Integer.MAX_VALUE);
for (int i = 1; i <= 5; i++) {
try (BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream("img_copy" + i + ".png"))) {
int len;
while ((len = fis.read()) != -1) {
fos.write(len);
}
fis.reset();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

作业3 筛选指定格式文件

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
package com.briup.homework.io;

import java.io.File;
import java.io.FileFilter;

/**
* @Auther: vanse(lc))
* @Date: 2023/8/22-08-22-21:09
* @Description:com.briup.homework.io
*/
public class Test1 {
public static void main(String[] args) {
String path = "/Users/vanse/Desktop/jd2312/a";
File dir = new File(path);
filterFile(dir);
}

private static void filterFile(File dir) {
if(dir.isDirectory()){ // a
File[] files = dir.listFiles(); // a b b.txt c.txt
for (File file : files) {
if(file.isFile() && file.getName().endsWith("txt")){
System.out.println("file.getAbsolutePath() = " + file.getAbsolutePath());
}else{
filterFile(file);
}
}
}
}
}

作业4 实现将一个文件从GBK编码转换为UTF-8编码

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
package com.briup.homework.io;

import java.io.*;

/**
* @Auther: vanse(lc))
* @Date: 2023/8/22-08-22-23:20
* @Description:改变文件编码
*/
public class Test4 {
public static void main(String[] args) {
File file = new File("a.txt");
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "gbk"))) {
String s = br.readLine();
System.out.println("s = " + s);
try(PrintWriter pw = new PrintWriter(file, "utf-8");){
pw.write(s);
pw.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

案例1 a.txt中有字符串hello !!! 将其变成hello World !!!(tips: 使用RandomAccessFile)

案例2 使用2个线程复制图片

  • 将图片字节数分为2半,每个线程控制一半数据读写 (保证线程安全 | 使用任意访问流)

网络

计算机通信三要素

  • ip
    • 公网ip 47.92.65.103 ip上的资源所有人都能访问
      • 阿里云ip 腾讯云ip 搬瓦工
    • 内网ip 指定局域网下的网段可以访问
    • 本机ip 127.0.0.1 -> localhost
      • c:windows/system32/drivers/etc/hosts
  • 端口
    • 计算机上的具体程序
  • 协议
    • 互联网通信规范 http file ftp

TCP

CS

  • 服务器 ServerSocket
  • 客户端 Socket

基本通信

服务端

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
package com.briup.tcp.cs.demo1;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 通信连接-服务端
* @author vanse
*
*/
public class MyServerSocket {
public static void main(String[] args) {
// 1.创建服务端 默认ip是127.0.0.1/localhost
// 当前代码在哪个操作系统上,都叫本机
// 端口随意指定 不冲突即可
try {
ServerSocket ss = new ServerSocket(8888);
// 2.服务器一般不关闭 一直等待客户端连接
System.out.println("服务器创建,等待连接");
// 本次连接的客户端
Socket accept = ss.accept(); // 等待客户端连接,客户端如果没有链接,就阻塞
System.out.println(accept);
} catch (Exception e) {
e.printStackTrace();
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.briup.tcp.cs.demo1;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
* 通信链接-客户端
* @author vanse
*
*/
public class MyClientSocket {
public static void main(String[] args) {
// 2.创建客户端
try {
Socket socket = new Socket("127.0.0.1",8888);
System.out.println(socket);
System.out.println("连接成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}

基本连接优化

服务端

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
package com.briup.tcp.cs.demo2;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket {
public static void main(String[] args) {
ServerSocket ss = null;
try {
// 1.创建服务器
ss = new ServerSocket(8888);
// 2.一直等待客户端连接
while(true) {
Socket accept = ss.accept();
System.out.println("接受请求:"+ accept);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.briup.tcp.cs.demo2;

import java.io.IOException;
import java.net.Socket;

public class MyClientSocket {
public static void main(String[] args) {
Socket socket = null;
try {
socket = new Socket("127.0.0.1",8888);
} catch (Exception e) {
e.printStackTrace();
}finally {
if(socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

客户端向服务端请求数据,服务端接收数据

服务端

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
package com.briup.tcp.cs.demo2;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServerSocket {
public static void main(String[] args) {
ServerSocket ss = null;
InputStream is = null;
try {
// 1.创建服务器
ss = new ServerSocket(8888);
Socket accept = ss.accept();
System.out.println("接受请求:" + accept);
// 4.接受来自客户端的数据
is = accept.getInputStream();
byte[] arr = new byte[1024];
is.read(arr);
System.out.println("读到数据: " + new String(arr));
// 5.
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (ss != null) {
ss.close();
}
} catch (Exception e2) {
}
}
}
}

客户端

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
package com.briup.tcp.cs.demo3;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class MyClientSocket {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
// 2.创建客户端
socket = new Socket("127.0.0.1", 8888);
// 3.客户端发送数据 通过套接字的流
// 写数据到服务端
os = socket.getOutputStream();
os.write("周杰伦".getBytes());
os.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (os != null) {
os.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e2) {
}
}

}
}

客户端向服务端请求数据,服务端响应数据

服务端

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
47
48
49
50
51
52
53
package com.briup.tcp.cs.demo4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* socket不报错 不出效果
* 通信失败 可能是换行符
* write println
* @author vanse
*
*/
public class MyServerSocket {
public static <L> void main(String[] args) {
try(
// 1.创建服务器
ServerSocket ss = new ServerSocket(10086);
Socket socket = ss.accept();
// 每次写一行
PrintWriter pw = new PrintWriter(socket.getOutputStream());
// 每次读一行
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
) {
// 4.接受客户端数据
String lineFromClient = null;
Scanner sc = new Scanner(System.in);
// 服务端可以一直接收
while((lineFromClient = br.readLine()) != null) {
System.out.println("客户端:" + lineFromClient);

// 5.一直响应数据给客户端
// scanner in
String lineFromConsole = sc.nextLine();
pw.println(lineFromConsole);
pw.flush();
}
// 不要写循环外


} catch (IOException e) {
e.printStackTrace();
}

}
}

客户端

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
package com.briup.tcp.cs.demo4;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class MyClientSocket {
public static void main(String[] args) {
try (
// 2.创建客户端
Socket socket = new Socket("localhost", 10086);
// 每次写一行
PrintWriter pw = new PrintWriter(socket.getOutputStream());
// 每次读一行
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));) {

// 3.向服务端发送数据 System.in nextLine()
Scanner sc = new Scanner(System.in);
// 客户端可以一直写数据给服务端
while(true) {
String lineFromConsole = sc.nextLine();
pw.println(lineFromConsole); // 写一行
pw.flush();
// 6.接收服务器的响应
System.out.println(br.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

客户端和服务端通信

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
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 客户端发给服务端
* 服务端返回客户端数据
* @author vanse
*
*/
public class MyServetSocket {
static final int port = 9999;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(port);
Socket socket = ss.accept();
// 2.接收客户端数据
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String lineFormClient = br.readLine();
// 3.响应数据
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println(lineFormClient);
pw.flush();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public class MyClientSocket {
public static void main(String[] args) throws Exception{
Socket s = new Socket("localhost",MyServetSocket.port);
// 1.发送数据 hello world
// 1.1 写一行 PrintWrite
PrintWriter pw = new PrintWriter(s.getOutputStream());
pw.println("hello word");
pw.flush();
// 4.接收服务器数据
BufferedReader br = new BufferedReader(
new InputStreamReader(
s.getInputStream()));
String lineFormServer = br.readLine();
System.out.println(lineFormServer);
}
}

==服务端通信一次后终止,需要循环==

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
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 客户端发给服务端
* 服务端返回客户端数据
* @author vanse
*
*/
public class MyServetSocket {
static final int port = 9999;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(port);
while(true) { // 先写死循环
Socket socket = ss.accept();
// 2.接收客户端数据
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String lineFormClient = br.readLine();
// 3.响应数据
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println(lineFormClient);
pw.flush();
}
}
}

==问题:客户端只能发送一次,需要一直从控制台发送==

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
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 客户端发给服务端
* 服务端返回客户端数据
* @author vanse
*
*/
public class MyServetSocket {
static final int port = 9999;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(port);
while(true) { // 先写死循环
Socket socket = ss.accept();
// 2.接收客户端数据
BufferedReader br = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
String lineFormClient;
while( (lineFormClient = br.readLine()) != null) {
// 3.响应数据
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.println(lineFormClient);
pw.flush(); // 写完要刷新
};

}
}
}
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
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class MyClientSocket {
public static void main(String[] args) throws Exception{
Socket s = new Socket("localhost",MyServetSocket.port);
// 1.发送数据 hello world
// 1.1 写一行 PrintWrite
PrintWriter pw = new PrintWriter(s.getOutputStream());
Scanner sc = new Scanner(System.in);
while(true) {
// 1.2 从控制台一直输入
String lineFromConsole = sc.nextLine();
pw.println(lineFromConsole);
pw.flush(); // 写完要刷新

// 4.接收服务器数据
BufferedReader br = new BufferedReader(
new InputStreamReader(
s.getInputStream()));
String lineFormServer = br.readLine();
System.out.println(lineFormServer);
}

}
}

==问题:一个服务端只能同时响应一个客户端bio 服务端要用多线程==

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 客户端发给服务端 服务端返回客户端数据
*
* @author vanse
*
*/
public class MyServetSocket {
static final int port = 9999;

public static void main(String[] args){
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
while (true) { // 先写死循环
Socket socket = ss.accept();
// System.out.println(socket);
// 每接受一个客户端,就开启新线程
new ServetThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

class ServetThread extends Thread {
// 每个线程都有一个socket(套接字流,来源接受)
private Socket socket;

public ServetThread(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
BufferedReader br = null;
PrintWriter pw = null;
try {
// 每个线程服务一个socket
// 2.接收客户端数据
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String lineFormClient;
// 该循环一直在进行,除非第一个客户端结束
while ((lineFormClient = br.readLine()) != null) {
// 3.响应数据
pw = new PrintWriter(socket.getOutputStream());
pw.println(lineFormClient);
pw.flush(); // 写完要刷新 一次连接 频繁使用ps对象
// pw.close();
}
} catch (Exception e) {
}finally {
try {
if(pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e2) {
}
}
}
}

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
47
48
49
50
51
52
53
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class MyClientSocket {
public static void main(String[] args){
Socket s = null;
PrintWriter pw = null;
BufferedReader br = null;
Scanner sc = null;
try {
s = new Socket("localhost", MyServetSocket.port);
// 1.发送数据 hello world
// 1.1 写一行 PrintWrite
pw = new PrintWriter(s.getOutputStream());
sc = new Scanner(System.in);
while (true) {
// 1.2 从控制台一直输入
String lineFromConsole = sc.nextLine();
pw.println(lineFromConsole);
pw.flush(); // 写完要刷新

// 4.接收服务器数据
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String lineFormServer = br.readLine();
System.out.println(lineFormServer);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
if (s != null) {
s.close();
}
if (sc != null) {
sc.close();
}
} catch (Exception e2) {
}
}

}
}

image-20230824152901647

聊天室

服务端

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package com.briup.net.tcp.demo2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
* 客户端发给服务端 服务端返回客户端数据
*
* @author vanse
*
*/
public class MyServetSocket {
// 集合(线程安全)保存所有的连接
static final int port = 9999;

public static void main(String[] args){
ServerSocket ss = null;
try {
ss = new ServerSocket(port);
while (true) { // 先写死循环
Socket socket = ss.accept();
// System.out.println(socket);
// 每接受一个客户端,就开启新线程
// 放到集合中
new ServetThread(socket).start();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(ss != null) {
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

class ServetThread extends Thread {
// 每个线程都有一个socket(套接字流,来源接受)
private Socket socket;

public ServetThread(Socket socket) {
this.socket = socket;
}

@Override
public void run() {
BufferedReader br = null;
PrintWriter pw = null;
try {
// 每个线程服务一个socket
// 2.接收客户端数据
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String lineFormClient;
// 该循环一直在进行,除非第一个客户端结束
while ((lineFormClient = br.readLine()) != null) {
// 3.响应数据 响应给除了了当前socket的所有socket
pw = new PrintWriter(socket.getOutputStream());
pw.println(lineFormClient);
pw.flush(); // 写完要刷新 一次连接 频繁使用ps对象
// pw.close();
}
} catch (Exception e) {
}finally {
try {
if(pw != null) {
pw.close();
}
if (br != null) {
br.close();
}
if (socket != null) {
socket.close();
}
} catch (Exception e2) {
}
}
}
}


客户端改造

  • 一个线程读来自服务器的广播数据
  • 一个线程录入数据(阻塞)

案例:传输图片 有隐藏问题

BS

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
47
package com.briup.net.tcp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

public class TestBS {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9999);
while(true) {
Socket s = ss.accept(); // 客户端是浏览器
System.out.println(s);
BufferedReader br =
new BufferedReader(
new InputStreamReader(s.getInputStream()));


// GET(请求方式) /(请求资源) HTTP/1.1(超文本传输协议)

// 请求协议
// 请求行 GET / HTTP/1.1
// 请求头 格式 Content-Type 数据类型
// 请求数据 周杰伦
String readLine = br.readLine();
System.out.println(readLine);

// 响应协议
// 响应行
// 响应头
// 响应数据
PrintWriter pw = new PrintWriter(s.getOutputStream());
// 响应行
pw.println("HTTP/1.1 200 OK");
// 响应头 (普通文本/html/json)
pw.println("Content-Type: text/plain;charset=UTF-8");
pw.println(); // 空行间隔数据
// html样式的标签
pw.println("hello world"); // 响应数据
pw.flush();
// 浏览器如果以为服务器没写完
s.shutdownOutput(); // 只关闭socket的输出流
s.close(); // 关闭socket
}
}
}

UDP

反射

类加载

类什么时候加载

什么时候用 什么时候加载,不一定会初始化

类加载过程

  • 加载 -> 方法区
  • 链接
    • 验证
    • 准备
      • 静态变量分配空间并赋初始值
    • 解析
  • 初始化(类的初始化 cinit)
    • 静态代码块执行一定程度上代表着类的初始化

类什么时候初始化

  • 第一次使用类的静态成员时(非final)

谁加载类 (类加载器) 类加载的关系不是子父类 是组合

  • 引导类加载器 BootClassLoader
    • C++ 实现,获取不到实现类
    • 加载rt.jar 基类
  • 拓展类加载器 ExtClassLoader
    • Java
    • jre/ext/ jar
  • 系统类加载(应用类加载器) AppClassLoader
    • Java
    • 自己写的类以及使用第三方类
    • CLASSPATH = .
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.briup.classloader;
// 查看三种类加载器
public class ShowClassLoader {
public static void main(String[] args) {
// 获取加载当前类的类加载器
// ClassLoader systemClassLoader =
// ShowClassLoader.class.getClassLoader();
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println("系统类加载器: " + systemClassLoader);

// 类加载器之间是组合关系 不是子父类关系
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println("拓展类加载器: " + extClassLoader);

ClassLoader bootClassLoader = extClassLoader.getParent();
System.out.println("引导类加载器:" + bootClassLoader);
}
}

双亲委任机制 保证类加载安全

  1. 应用类加载器先判断该类是否被加载过,如果没有,交给”父”类加载器 拓展类加载器
  2. 拓展类加载器先判断该类是否被加载过,如果没有,交给”父”类加载器 引导类加载器
  3. 引导类加载器先判断自己是否能够加载该类
    1. 能,自己加载 结束了
    2. 不能,交给”子”类加载器加载 拓展类加载器
      1. 自己是否能够加载该类
        1. 能 自己加载 结束了
        2. 不能 交给”子”类加载器加载 应用类加载器
          1. 自己是否能够加载该类
            1. 能 自己加载
            2. 抛出异常 ClassNotFoundException

使用类加载器加载文件 推荐使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.briup.classloader;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class LoaderFile {
public static void main(String[] args) throws Exception {
// new FileInputStream("src/a.txt");


// ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader classLoader = LoaderFile.class.getClassLoader();
// 将a.txt放到bin目录下
InputStream is = classLoader.getResourceAsStream("a.txt");
System.out.println(is);
int read = is.read();
System.out.println(read);
}
}

==注意: 只加载bin/out目录下的文件,如果文件放在src下,编译器可能会自动将文件放到bin/out==

反射

CLASS

Class的含义

Class 类是一个重要的核心类,它用于表示一个类或接口的运行时 信息。每个类在Java虚拟机中都有一个对应的 Class 对象,可以通过该对象获 取类的构造函数、方法、属性等信息,并且可以进行实例化对象、方法调用和数 据成员访问等操作。

image-20230825120312035

1
2
3
4
5
6
7
Student s = new Student();
// 左边是类 右边是对象
Class<Student> c = Student.class;
Filed
Method
Consturcotr
Annotation

Class对象的获取方式

  • 类名.class
  • 对象.getClass()
  • ==Class.forName(“完整类名”) 运行时才加载类==
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
package com.briup.reflect;
/**
* 获取Class对象
* 同一个对象(类只加载一次,运行时区域一个)
*/
public class TestGetClass {
public static void main(String[] args) throws ClassNotFoundException {
// 1.类名class
Class<?> c = A.class;
// 2.对象.getClass()
A a = new A();
Class<?> c2 = a.getClass();
System.out.println(c == c2);
// 3.Class.forName("带包类名"); 建议
Class<?> c3 = Class.forName("com.briup.reflect.A");
System.out.println(c3 == c2);
}

}
class A{
int age;
public void hello() {

}
}

注意: 同一个对象(类只加载一次,运行时区域一个)

获取类info

动态获取类本身信息

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
package com.briup.reflect;

import java.lang.reflect.Modifier;

/**
* 获取Class对象 同一个对象(类只加载一次,运行时区域一个)
*/
public class TestGetClassInfo{
public static void main(String[] args) throws ClassNotFoundException {
// 1.获取Class对象
Class<?> a = Class.forName("com.briup.reflect.TestGetClassInfo$A");
Class a1 = int.class;
Class a2 = Integer.class;
// 2.获取该类的信息
// System.out.println("原始类型: " + a1.isPrimitive());
// System.out.println("原始类型: " + a2.isPrimitive());
System.out.println("基本类型: " + a.isPrimitive());
System.out.println("类完整名: " + a.getName());
System.out.println("类简写名: " + a.getSimpleName());
System.out.println("类修饰符: " + Modifier.toString(a.getModifiers()));
System.out.println("是否为父类|接口: " + a.isAssignableFrom(B.class));
System.out.println("父类: " + a.getSuperclass());
}

class B {
}

// default是不写
private static class A {
int age;

public void hello() {
}
}
}

获取类成员

动态获取类成员 Class

  • Filed 属性/字段

    • getFileds(); 拿父类和本类的公有修饰字段
    • getFiled(String name); 拿指定公有字段
    • getDeclaredFileds() 拿本类所有修饰符字段,不能获取父类的任意字段
    • getDeclaredFiled(String name) 拿指定所有修饰符字段
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private static void getFiled(Class<?> a) {
    // Field[] fields = a.getFields(); // 只能获取本类公有字段
    // for (Field field : fields) {
    // System.out.println(field);
    // }
    System.out.println("---declared----start");
    Field[] declaredFields = a.getDeclaredFields(); // 可以获取本类所有
    StringBuilder sb = new StringBuilder();
    for (Field field : declaredFields) {
    // public int id 修饰符 类型(不要包)名字
    sb.delete(0, sb.length()); // 清除上一次的内容
    System.out.println(sb
    .append(Modifier.toString(field.getModifiers())+" ")
    .append(field.getType().getSimpleName() + " ")
    .append(field.getName()) + " ");
    }
    System.out.println("---declared----end");

    // Field ageField = a.getDeclaredField("age"); // 获取指定字段
    // System.out.println(ageField);
    }
  • Method 方法

    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
    private static void getMethod(Class<?> a) throws NoSuchMethodException {
    System.out.println("start---");
    Method[] declaredMethods = a.getDeclaredMethods();
    StringBuilder sb = new StringBuilder();
    for (Method method : declaredMethods) {
    sb.delete(0, sb.length());
    // public static int setAge(int age,int id){}
    StringBuilder s1 = sb
    .append(Modifier.toString(method.getModifiers()) + " ")
    .append(method.getReturnType().getSimpleName() + " ")
    .append(method.getName());
    // 遍历参数数组 s2
    Parameter[] parameters = method.getParameters();
    s1.append("(");
    for (Parameter param : parameters) {
    s1.append(param.getType().getSimpleName() + " ");
    s1.append(param.getName() + " ");
    }
    s1.append(")");
    System.out.println(sb);
    }
    System.out.println("end....");
    Method declaredMethod = a.getDeclaredMethod("hello", int.class,String.class);
    System.out.println(declaredMethod);
    }
  • Constructor 构造器

    1
    2
    3
    4
    5
    6
    private static void getConstuctor(Class<?> a) {
    Constructor<?>[] dcs = a.getDeclaredConstructors();
    for (Constructor<?> constructor : dcs) {
    System.out.println(constructor);
    }
    }
  • Annotation 注解

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    boolean flag = a.isAnnotationPresent(MyAnno.class);
    if(flag) {
    MyAnno annotation = a.getAnnotation(MyAnno.class);
    String value = annotation.value();
    System.out.println(value);
    }

    @Target(value = {ElementType.TYPE,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnno{
    String value() default "";
    }

操作类成员

动态操作类成员Class

  • 操作字段 设置值

    • 私有值可以更改
    • 静态值可以更改
    • 常量值可以更改
    • 静态常量不可以更改
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    private static void operatorMethod(Class<?> a)
    throws InstantiationException, IllegalAccessException, NoSuchFieldException {
    Object stu = a.newInstance();
    Field nameField = a.getDeclaredField("name");
    // stu.name= "tom";
    nameField.set(stu, "briup");
    System.out.println(nameField.get(stu)); // stu.name
    Field aField = a.getDeclaredField("a");
    aField.setAccessible(true); // 私有字段
    aField.set(stu, 2);
    System.out.println(aField.get(stu));
    }
  • 操作方法 调用方法

    1
    2
    3
    4
    5
    6
    Object stu = a.newInstance();
    Method methodHello = a.getDeclaredMethod("hello", int.class,String.class);
    methodHello.setAccessible(true);
    // stu.hello(1,"briup"); 调用方法
    methodHello.invoke(stu, 2,"briup111");
    System.out.println(stu);
  • 操作构造器 调用构造器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    private static void operatorConstructor(Class<?> a)
    throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    // Constructor<?> constructor = a.getDeclaredConstructor();
    // // student 如果是私有构造器 设置可访问
    // constructor.setAccessible(true);
    // Student stu = (Student) constructor.newInstance();
    // System.out.println(stu);
    // 简化写法
    // Object newInstance = a.newInstance();
    // System.out.println(newInstance);
    Constructor<?> constructor = a.getDeclaredConstructor(int.class,String.class,int.class,char.class);
    // student 如果是私有构造器 设置可访问
    constructor.setAccessible(true);
    Student stu = (Student) constructor.newInstance(1,"tom",18,'男');
    System.out.println(stu);
    }

案例1

1
2
3
4
5
6
Student s = new Student(1,"tom",18);  get/set
// 复制一份地址不一样的,内容一样的对象
1.泛型 + 反射
2.clone();
public <T> T copy(T t){
}

案例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
1.在浏览器第一个窗口访问地址  多线程 + 网络 + io
localhost:9999/hello.txt 文件内容需要io读取
2.在浏览器第一个窗口访问地址
localhost:9999/hello?role=admin
调用hello方法并将返回值返回给浏览器
该方法有权限注解(管理员 游客 vip) 只有管理员身份
该三个值在文件 role.txt中 -> 集合
3.如果访问的/hello2 没有这个资源
返回异常信息 异常+枚举
资源找不到
权限不足
自定义异常{
code 200 403 权限不足 404 无资源
msg
时间戳
}
枚举异常对象
4.服务器一直能接受客户端
- admin - guest - normal
@Role(admin,normal) -> role.txt
public String hello(){
}

@interface Role{
String[] values;
}

class CustomerException{
String msg;
int code;
long time;
}

补充

数据结构

底层的数据结构

  • 数组 增删慢 查找快

  • 链表 增删快 查找慢

由底层数据结构构成的新数据结构

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.briup.structor.stack;

/**
* 栈接口: 先进后出
*
* @author vanse
*
*/
public interface MyStack<T>{
// 压栈
void push(T t);
// 弹栈
T pop();
// 栈顶数据
T peek();
// 遍历栈
void show();
// 容量
int size();
}

数组实现

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.briup.structor.stack;

import java.lang.reflect.Array;
import java.util.Arrays;

/*
size
3 4
2 3
1 2
0 1
*/
public class MyArrayStack<T> implements MyStack<T> {
// 组装数组
private Object[] elementData;
private int size;
private static final int ELEMENT_CAPACITY = 10;

// 初始化栈 默认容量为10
public MyArrayStack() {
this(ELEMENT_CAPACITY);
}

// 初始化栈 容量可自定义
public MyArrayStack(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
elementData = new Object[initialCapacity];
}

@Override
public void push(T t) {
if (size == elementData.length - 1) {
elementData = Arrays.copyOf(elementData, elementData.length << 1);
}
elementData[size++] = t;
}

@Override
public T pop() {
if (size == 0) {
return null;
}
Object temp = elementData[--size];
elementData[size] = null;
return (T) temp;
}

@Override
public T peek() {
return size == 0 ? null : (T) elementData[size - 1];
}

@Override
public void show() {
StringBuilder sb = new StringBuilder("[");
for (int i = size - 1; i >= 0; i--) {
if (i == 0) {
sb.append(elementData[i]);
break;
}
sb.append(elementData[i] + ",");

}
System.out.println(sb.append("]"));
}

@Override
public int size() {
return size;
}
}


链表实现

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.briup.structor.stack;

public class MyLinkedStack<T> implements MyStack<T> {
private static class MyNode<T> {
public T data;
public MyNode<T> nextNode;

public MyNode(T data, MyNode next) {
this.data = data;
this.nextNode = next;
}
}

private MyNode<T> head;
// 栈中元素个数
private int size;

public MyLinkedStack() {
head = new MyNode<T>(null, null);
}

// 组装链表
@Override
public void push(T t) {
MyNode oldNode = head.nextNode;
// 新节点指向老节点
MyNode node = new MyNode(t, oldNode);
// 头节点指向新节点
head.nextNode = node;
size++;
}

@Override
public T pop() {
MyNode<T> topNode = head.nextNode; // 第一个元素
if (topNode == null) {
return null; // 空栈
}
// 头节点指向新的栈顶节点(原栈顶的下一个节点)
head.nextNode = topNode.nextNode;
size--;
return topNode.data;
}

@Override
public T peek() {
if (head.nextNode == null) {
return null;
}
return head.nextNode.data;
}

@Override
public void show() {
StringBuilder sb = new StringBuilder("[");
MyNode h = head;
while (h.nextNode != null) {
MyNode nextNode = h.nextNode; // 取出节点数据
if(nextNode.nextNode == null){
sb.append(nextNode.data+"]");
break;
}
sb.append(nextNode.data+",");
h = nextNode; //一直循环下一个节点
}
System.out.println("sb = " + sb);
}

@Override
public int size() {
return size;
}

}

设计模式

单例模式

chatgpt

先安装油猴插件

image-20230810104302678

再安装chatgpt脚本

image-20230810104412211

快捷键

  • ctrl + shift + + 字体放大
  • alt + / 提示
  • ctrl + / 单行注释
  • ctrl + shfit+ / 多行注释
  • ctrl + alt + 下方向 复制
  • alt + 上下方向 移动位置
  • alt+ 左右方向 上一步下一步代码
  • ctrl + shift + f 格式化代码
  • alt+shift+r 重构类名
  • ctrl+1 快速接收变量
  • ctrl + n 快速创建 class package project
  • ctrl + d 快速删除一行
  • ctrl + shift + t 查找类型
  • alt + shfit + m 抽取方法
  • ctro + shift +o 一次导包
  • alt + shfit + s
  • ctrl + t 查看实现类
  • alt+shift+z 环绕代码(try catch synchronized)
  • alt+enter 文件属性

0-软件

VM

双击软件进行安装

image-20230717100951186 image-20230717101011776 image-20230717101031634

注意安装路径

image-20230717101108254 image-20230717101136381 image-20230717101205181 image-20230717101221681 image-20230717101237291

输入注册码后(以下任选一个),即可使用

1
2
3
ZF3R0-FHED2-M80TY-8QYGC-NPKYF
YF390-0HF8P-M81RQ-2DXQE-M2UT6
ZF71R-DMX85-08DQY-8YMNC-PPHV8

image-20230827164546795

idea

介绍

【1】intelliJ IDEA就是Java的IDE。
【2】市场占有率竹节攀升,超过了Eclipse。
【3】JetBrains公司介绍:
JetBrains是一家捷克的软件开发公司,该公司位于捷克的布拉格,并在俄罗斯的圣彼得堡及美国麻州波士顿都设有办公室,该公司最为人所熟知的产品是Java编程语言开发撰写时所用的集成开发环境:IntelliJ IDEA。它产品,比如:

  • WebStorm:用于开发JavaScript、HTML5、CS3等前端技术;
  • PyCharm:用于开发python(python语言热度排行榜排名第一,在人工智能大数据领域应用)
  • PhpStorm:用于开发PHP
  • RubyMine:用于开发Ruby/Rails
  • AppCode:用于开发Objective-C/Swift,替换xcode的
  • CLion:用于开发C/C++
  • DataGrip:用于开发数据库和SQL
  • Rider:用于开发.NET
  • GoLand:用于开发Go(区块链主流开发语言就是Go语言)

安装

官网下载 [idea 2023.2](https://www.jetbrains.com/zh-cn/idea/download/?section=windows 作者:安安说前端哦 https://www.bilibili.com/read/cv25328397 出处:bilibili)后,双击打开安装

image-20230827150733584

image-20230827150808675

image-20230827150838741

image-20230827150853488

image-20230827151054334

使用脚本和密钥注册使用

运行第一个脚本 uninstall-all-user

image-20230827151503092

image-20230827151530951

运行第二个脚本 install-all-user

image-20230827151609925

打开idea

image-20230827151829341

image-20230827151932050

image-20230827152023664

复制以下激活码

1
6G5NXCPJZB-eyJsaWNlbnNlSWQiOiI2RzVOWENQSlpCIiwibGljZW5zZWVOYW1lIjoic2lnbnVwIHNjb290ZXIiLCJhc3NpZ25lZU5hbWUiOiIiLCJhc3NpZ25lZUVtYWlsIjoiIiwibGljZW5zZVJlc3RyaWN0aW9uIjoiIiwiY2hlY2tDb25jdXJyZW50VXNlIjpmYWxzZSwicHJvZHVjdHMiOlt7ImNvZGUiOiJQU0kiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBEQiIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiSUkiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOmZhbHNlfSx7ImNvZGUiOiJQUEMiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBHTyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFNXIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQV1MiLCJmYWxsYmFja0RhdGUiOiIyMDI1LTA4LTAxIiwicGFpZFVwVG8iOiIyMDI1LTA4LTAxIiwiZXh0ZW5kZWQiOnRydWV9LHsiY29kZSI6IlBQUyIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX0seyJjb2RlIjoiUFJCIiwiZmFsbGJhY2tEYXRlIjoiMjAyNS0wOC0wMSIsInBhaWRVcFRvIjoiMjAyNS0wOC0wMSIsImV4dGVuZGVkIjp0cnVlfSx7ImNvZGUiOiJQQ1dNUCIsImZhbGxiYWNrRGF0ZSI6IjIwMjUtMDgtMDEiLCJwYWlkVXBUbyI6IjIwMjUtMDgtMDEiLCJleHRlbmRlZCI6dHJ1ZX1dLCJtZXRhZGF0YSI6IjAxMjAyMjA5MDJQU0FOMDAwMDA1IiwiaGFzaCI6IlRSSUFMOi0xMDc4MzkwNTY4IiwiZ3JhY2VQZXJpb2REYXlzIjo3LCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-SnRVlQQR1/9nxZ2AXsQ0seYwU5OjaiUMXrnQIIdNRvykzqQ0Q+vjXlmO7iAUwhwlsyfoMrLuvmLYwoD7fV8Mpz9Gs2gsTR8DfSHuAdvZlFENlIuFoIqyO8BneM9paD0yLxiqxy/WWuOqW6c1v9ubbfdT6z9UnzSUjPKlsjXfq9J2gcDALrv9E0RPTOZqKfnsg7PF0wNQ0/d00dy1k3zI+zJyTRpDxkCaGgijlY/LZ/wqd/kRfcbQuRzdJ/JXa3nj26rACqykKXaBH5thuvkTyySOpZwZMJVJyW7B7ro/hkFCljZug3K+bTw5VwySzJtDcQ9tDYuu0zSAeXrcv2qrOg==-MIIETDCCAjSgAwIBAgIBDTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTIwMTAxOTA5MDU1M1oXDTIyMTAyMTA5MDU1M1owHzEdMBsGA1UEAwwUcHJvZDJ5LWZyb20tMjAyMDEwMTkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCUlaUFc1wf+CfY9wzFWEL2euKQ5nswqb57V8QZG7d7RoR6rwYUIXseTOAFq210oMEe++LCjzKDuqwDfsyhgDNTgZBPAaC4vUU2oy+XR+Fq8nBixWIsH668HeOnRK6RRhsr0rJzRB95aZ3EAPzBuQ2qPaNGm17pAX0Rd6MPRgjp75IWwI9eA6aMEdPQEVN7uyOtM5zSsjoj79Lbu1fjShOnQZuJcsV8tqnayeFkNzv2LTOlofU/Tbx502Ro073gGjoeRzNvrynAP03pL486P3KCAyiNPhDs2z8/COMrxRlZW5mfzo0xsK0dQGNH3UoG/9RVwHG4eS8LFpMTR9oetHZBAgMBAAGjgZkwgZYwCQYDVR0TBAIwADAdBgNVHQ4EFgQUJNoRIpb1hUHAk0foMSNM9MCEAv8wSAYDVR0jBEEwP4AUo562SGdCEjZBvW3gubSgUouX8bOhHKQaMBgxFjAUBgNVBAMMDUpldFByb2ZpbGUgQ0GCCQDSbLGDsoN54TATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQELBQADggIBABqRoNGxAQct9dQUFK8xqhiZaYPd30TlmCmSAaGJ0eBpvkVeqA2jGYhAQRqFiAlFC63JKvWvRZO1iRuWCEfUMkdqQ9VQPXziE/BlsOIgrL6RlJfuFcEZ8TK3syIfIGQZNCxYhLLUuet2HE6LJYPQ5c0jH4kDooRpcVZ4rBxNwddpctUO2te9UU5/FjhioZQsPvd92qOTsV+8Cyl2fvNhNKD1Uu9ff5AkVIQn4JU23ozdB/R5oUlebwaTE6WZNBs+TA/qPj+5/we9NH71WRB0hqUoLI2AKKyiPw++FtN4Su1vsdDlrAzDj9ILjpjJKA1ImuVcG329/WTYIKysZ1CWK3zATg9BeCUPAV1pQy8ToXOq+RSYen6winZ2OO93eyHv2Iw5kbn1dqfBw1BuTE29V2FJKicJSu8iEOpfoafwJISXmz1wnnWL3V/0NxTulfWsXugOoLfv0ZIBP1xH9kmf22jjQ2JiHhQZP7ZDsreRrOeIQ/c4yR8IQvMLfC0WKQqrHu5ZzXTH4NO3CwGWSlTY74kE91zXB5mwWAx1jig+UXYc2w4RkVhy0//lOmVya/PEepuuTTI4+UJwC7qbVlh5zfhj8oTNUXgN0AOc+Q0/WFPl1aw5VV/VrO8FCoB15lFVlpKaQ1Yh+DVU8ke+rt9Th0BCHXe0uZOEmH0nOnH/0onD

image-20230827152111980

image-20230827152342137

使用

创建项目

image-20230827152409517

image-20230827152942311

image-20230827153153116

默认

image-20230827154532473

注意1: 在Eclipse中我们有Workspace(工作空间)和Project(工程)的概念,在IDEA中只有Project(工程)和Module(模块)的概念。

IDEA官网说明:
An Eclipse workspace is similar to a project in IntelliJ IDEA An Eclipse project maps to a module in IntelliJ IDEA

  • Eclipse中 workspace 相当于IDEA中的Project
  • Eclipse中Project 相当于IDEA中的Module

在IntelliJ IDEA中Project(工程)是最顶级的级别,次级别是Module(模块)。

一个Project下可以有多个Module。

注意2: 从Eclipse 转过来的人总是下意识地要在同一个窗口管理n个项目,这在Intellj IDEA是无法做到的。Intellj IDEA提供的解决方案是打开多个项目实例,即打开多个项目窗口。即:一个Project 打开一个Window窗口。

注意3: out目录 存放的是编译后的字节码文件

配置

进入设置

image-20230827154958523

UI 默认是新UI,过于简化和紧凑,可设置旧UI

image-20230827155117581

设置后需要重启idea才能生效

image-20230827155320177

主题: 默认是暗黑主题,可设置白色背景或者下载第三方主题

image-20230827155353029

滚轮改变字体大小

image-20230827155509314

方法分割符

image-20230827155847817

image-20230827155900574

自动导包和优化包

image-20230827160004560

提示代码忽略大小写

image-20230827160402057

同时打开的代码标签数量

image-20230827160714450

注释颜色

image-20230827160953151

image-20230827161125162

自动添加类上的文档注释(只对新建类有效)

image-20230827161601312

1
2
3
4
5
/**
*@Auther: vanse(lc))
*@Date: ${DATE}-${MONTH}-${DAY}-${TIME}
*@Description:${PACKAGE_NAME}
*/

方法注释

image-20230827162433976

统一编码

image-20230827162603817

自动编译

image-20230827162749995

添加序列化id

image-20230827163051650

快捷键

image-20230827163312736

代码模板

模板 说明
psvm 生成main方法
sout 生成打印语句
soutv 输出就近变量
soutp 输出参数
soutm 输出方法名称
soutf 格式化输出
soutc 方法引用
fori for循环
itar 普通for循环
ritar 反转for循环
iter 增强for循环
list.for 集合for循环
itli 遍历list
itm 遍历map
itit 遍历迭代器
ifn if null
inn if not null
psf public static final
psfi public static final int
psfs public static final String
prsf private static final

插件

  • lombok 简化toString/setter/getter等和slf4j

  • Mybatis Log Plugin mybatis运行sql日志

  • jrebel 热部署(收费)

  • MybatisCodeHelperPro(收费)

  • mybatisX mybatis生成工具

  • RestfulToolKit result风格生成

  • Zoolytic zookeeper连接

  • Rainbow Brackets 彩虹括号

  • Maven Helper maven增强工具

  • .ignore git忽略

  • Background Image Plus 背景图片设置

    • stackoverflow 报错查找网站
  • Translation 翻译插件

  • **Atom Material Icons **图标

  • jclasslib bytecode view

  • https://plugins.jetbrains.com/plugin/15727-xcode-theme

  • Java Visualizer 栈可视化

    • idea memory view

mysql

卸载

mysql如果安装过,需要完全清除

  • 停止mysql服务

    image-20230831235802227

  • 删除安装目录和data目录和注册表 (建议使用geek)

    image-20230831235908908

    image-20230901000044962

    注册表

    image-20230901000137861

image-20230901000232350

下载

官方下载

img

这里让我们登账号,忽略,直接下载

imgMySQL如果是

安装包安装, 可以图形化界面自主配置
如果是压缩包解压, 可以配置 配置文件, 可以解压安装到指定的路径.

安装

双击安装包

img

img

我们选择自定义安装, 因为有些应用我们可能用不到

img

选择要安装的服务, 并点击箭头 将其添加到右边

img

如果到这里安装出现这个 Check Requirements

说明你的系统缺少 MySQL需要的C++库, 安装即可

点击 execute

img

img

改好了就NEXT

img

execute安装等待它安装完毕

img

img

点击 NEXT

配置

img

这个界面默认即可, 不要动它, 点击下一步

img

官方推荐第一种, 此处选择第二种 否则终端无法登录

image-20230901000852737

image-20230901000954273

image-20230901001017172

选最后一个,继续

img

接着继续安装

img

登录

image-20230901001154596

image-20230901001208672

查看编码 show variables like 'character%';

image-20230901001301447

如果编码不是utf-8,需要额外设置

image-20230901001351461

参考如下

image-20230901103003587

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
[client]
port=3306
default-character-set = utf8mb4

[mysql]
no-beep
default-character-set = utf8mb4

[mysqld]
character_set_server = utf8mb4
default-time_zone='+8:00'
port=3306
datadir=C:/ProgramData/MySQL/MySQL Server 8.0\Data
authentication_policy=mysql_native_password,,
default-storage-engine=INNODB
sql-mode="ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION"
log-output=FILE
general-log=0
general_log_file="LAPTOP-F4UKAGJR.log"
slow-query-log=1
slow_query_log_file="LAPTOP-F4UKAGJR-slow.log"
long_query_time=10
log-error="LAPTOP-F4UKAGJR.err"
log-bin="LAPTOP-F4UKAGJR-bin"
server-id=1
lower_case_table_names=1
secure-file-priv="C:/ProgramData/MySQL/MySQL Server 8.0/Uploads"
max_connections=151
table_open_cache=4000
temptable_max_ram=1G
tmp_table_size=184M
internal_tmp_mem_storage_engine=TempTable
myisam_max_sort_file_size=2146435072
myisam_sort_buffer_size=357M
key_buffer_size=8M
read_buffer_size=128K
read_rnd_buffer_size=256K
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=16M
innodb_buffer_pool_size=128M
innodb_redo_log_capacity=100M
innodb_thread_concurrency=25
innodb_autoextend_increment=64
innodb_buffer_pool_instances=8
innodb_concurrency_tickets=5000
innodb_old_blocks_time=1000
innodb_stats_on_metadata=0
innodb_file_per_table=1
innodb_checksum_algorithm=0
flush_time=0
join_buffer_size=256K
max_allowed_packet=64M
max_connect_errors=100
open_files_limit=8161
sort_buffer_size=256K
binlog_row_event_max_size=8K
sync_source_info=10000
sync_relay_log=10000
sync_relay_log_info=10000
loose_mysqlx_port=33060

然后重启mysql服务,再登录查看编码

  • Copyrights © 2015-2023 vanse
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信