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 文件属性

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

请我喝杯咖啡吧~

支付宝
微信