Java SE

gosling 1900 sun->甲骨文

一,概述

1.1 开发场景

  • SSM

    Spring 轻量级容器框架

    SpringMVC 分层 web 开发框架

    MyBatis 持久化框架

  • Android

  • 大数据-hadoop

1.2 应用领域

  • JavaEE
  • 大数据
  • Android

1.3 技术体系

  • Java SE (Java Standard Edition) 标准版
  • Java EE (Java Enterprise Edition) 企业版
  • Java ME (Java Micro Edition) 小型版

1.4 特点

  1. oop 面向对象
  2. 健壮 (强类型机制,异常处理,垃圾自动收集)
  3. 跨平台 (.class文件 JVM)
  4. 解释性 (c++ 是编译性语言,Java编译后的代码需要解释器执行后才可以机器执行)

1.5 开发工具

  • notepad++
  • Sublime Text (推荐)
  • IDEA (推荐)
  • eclipse

后两种是集成 IDE 很智能,建议先 Sublime 再 IDEA

1.6 运行机制&过程

  1. Java 语言特性:跨平台

    因为:JVMimage-20220521191924351

  2. JVM (Java Virtual Machine)

    • JVM 是 Java 虚拟机,也就是一个虚拟的计算机,包含在 JDK 中
    • 不同操作系统有不同 JVM
    • JVM 实现了 一次编译,到处运行 有个.class 文件就中了

    示意图:image-20220521192220122

1.7 JDK&JRE

1.7.1 JDK

JDK(Java Development Kit) 是 Java 开发工具包

组成:JRE + Java 开发工具 (java,Javac,javadoc,javap…)

1.7.2 JRE

JRE(Java Runtime Environment) 是 Java 运行环境

组成:JVM + Java SE核心类库

如果只是想运行一个开发好的 Java 程序(.class 文件),计算机只需要安装 JRE 就行

1.7.3 安装 JDK

  • 作者的阿里云网盘有Windows的JDK8&JDK11 点这里

  • 官网

  • 声明:

    目前 JDK 有很多版本,但是用的多的还是 8,11 。这两个也是永久版,所以推荐这两个。

    傻瓜式安装,下一步就行。

    安装下载目录最好是非C盘,

    路径不要有中文或特殊符号尤其是空格

1.7.4 配置环境变量 path

  1. 为什么要配这个?

这时候 JDK 只能在安装目录用,在非安装目录用不了。比如你在桌面打开 DOW 命令行输入 java或者javac会出现下图image-20220521194807551

有这种情况就是需要配置环境变量 path

  1. 为什么会有这种情况?

    因为在执行当前的程序在当前目录不存在时,Windows 就会在系统已有的一个path的环境变量的指定目录下查找。如果还找不到就会出现上图场景

  2. 配置步骤

    image-20220521195350220

    配置教学博客

1.8 开发注意

  1. image-20220521201005503
  2. Java 转义符image-20220521201404789
  3. 新手易犯错误image-20220521201602888
  4. 设置电脑中文状态也输入英文符号点这里

1.9注释&代码规范

自己看人代码的时候骂别人”TM怎么不写注释这孙子!“

自己写代码的时候,“我这水平还用注释?注释-狗都不写”

  1. 还是劝大家养成写注释的好习惯,增强自己代码的可读性。这是一种格局。

  2. 开始编程前先写注释理清思路,后面按着注释写代码会轻松很多。

  3. 注释类型

    • 单行注释 // ~
    • 多行注释 /* ~ */
    • 文档注释 /** ~ */
  4. 注意:

    • 多行注释里面不允许俄罗斯套娃再整个多行注释

    • 文档注释一般是写在类或者方法前面,它是用javadoc 所解析,生成一套网页文件形式的说明文档

    • 文档注释语法:javadoc -d 文件夹名 -xx -yy 源文件.java

  5. 文档注释演示

    • image-20220521205118849

    • image-20220521205154449

    • image-20220521205345683

    • image-20220521205422646

  6. 代码规范image-20220521204411620

1.10 DOS命令(了解)

DOS(Disk Operating System) 磁盘操作系统。这个是 Windows的。在 Windows 上用的还真不多,一般都是图形页面直接点啊点。

有过 Linux 基础的朋友应该看着看轻松啦

  1. 路径

    • 绝对路径:你滴全名。从顶级目录开始定位形成的路径
    • 相对路径:你滴小名。从当前目录开始定位形成的路径
  2. 常用命令

    image-20220521210201095

1.11 IDE

IDE 叫集成开发环境,Java 中最著名的当属 IDEA ,Eclipse。

  • image-20220524092050355
  • image-20220524092155141

1.11.1 IDEA

1.11.1.1 工作界面

image-20220524092654606

1.11.1.2 设置
  • 字体和颜色主题image-20220524092804146
  • 字符编码:image-20220524092834521
1.11.1.3 文件对应项目

image-20220524093012753

1.11.1.4 快捷键

image-20220524094158020

二,变量

2.1 简介

变量要素:(权限) 类型 名称 值

咋个理解:变量就是一个酒店房间,然后变量名就是房间门牌号,变量值就是住在这个房间中的人,然后通过变量名去访问变量值就是送外卖按门牌号送到那个人手上,因为住在这个房间中的人是可以变的所以它叫变量,要是一个人一直住在一个房间,那他不就是定量了,对吧。

2.2 使用入门

  1. 声明

    int a;

  2. 赋值

    a = 1;

  3. 同时

    int a = 1;

  4. 注意

    image-20220521212110558

2.3 ‘+’ 使用

Java 中字符串的拼接用的是+,有些语言是用$,比如 Linux 中的 Shell 和 Android 的 Kotlin。其实吧后者更好用的,一会这儿一个加号那里一个很麻烦的。

image-20220521212247714

2.4 数据类型

前言:其实吧不同的数据类型就是对一个数据在内存中给它分配不同的内存大小(字节)

  1. 总览结构(很重要,必须记下来!)

    image-20220521213249265

  2. 整数类型

    • 顾名思义,拿来放整数的image-20220521213444590
  • 咱就是说int类型正那边最大能放1.5个中国人口(21.5亿)

  • 还有那个 byte 能容纳的最大整数怎么记?咱程序员大多是男生,所以~“拜托要爱妻”(byte 127)

  • 负数部分比正数部分数值多一点撒~那就"富人正好比你多亿点"

  • 整数的使用细节image-20220521214335916

  • 补充字符编码image-20220522111615580

  1. 浮点类型

    • 啥浮点不符点的,就是咱说的小数,以后看见浮点数就是小数。

    • image-20220522104330386

    • 浮点数就是小数点可以浮动,可以变来变去。代价是什么是你表示小数时后面要乘以10。小数点浮动10的幂就改变。(科学计数法)

    • 注意:浮点数是不能直接比较大小的,他们都是近似值,有精度缺失。

    • image-20220522105513138

  2. 字符类型

    • 字符就是单个的字母,汉字啥的,比如‘a’,‘你’ 这种。你要是有一串就得用字符串或者字符数组。

    • 记得用英文单引号引起来

    • 如果是直接赋值数字,它以为是unicode 码 会去找这个数字在 unicode 码中对应哪一个字符

    • image-20220522110616598

  3. 布尔类型

    boolean 就是只有 true & false 两个值,表示判断的,逻辑运算

2.5 自动/强制 类型转换

Java 程序在进行赋值或者运算时,精度小的自动转换成精度大的数据类型

2.5.1 数据类型精度排序

(背下来!很重要!)

image-20220522112227389

2.5.2 自动转换注意细节

image-20220522112410536

2.5.3 强制类型转换

精度低到高的会自动类型转换,那反过来呢?非要水往高处流,加个水泵对吧。

注意:image-20220522113610818

2.6 基础数据类型和 String 类型转换

2.6.1 基础 -> String

  1. 基本类型值 + “”
  2. 基本数据类型.toString

2.6.2 String ->基础

用 ‘基础数据类型的包装类.parseXX’ 方法

要确保 String 能装成有效的数据,别搁着 “hello” -> int

2.6.3 String -> char

char = String.charArt(x) 要字符串第 x 个字符

2.6.4 借图演示

image-20220522114152932

三,运算符

一般有对数据的运算赋值和比较等等

3.1 包含

  • 算术运算符
  • 赋值运算符
  • 关系(比较)运算符
  • 逻辑运算符
  • 位(二进制)运算符
  • 三元运算符

3.2 算术运算符

  1. 演示图image-20220522145957804
  2. 细节补充image-20220522150329679
  3. 一道相关面试题image-20220522160013390

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

关系运算符的结果(关系表达式)一般是 boolean 型,就是 true & false 任选其一

这个经常在 if 或者 循环结构中用

image-20220522160724908

提一句:一个等号是赋值,两个才是比较鸭!

3.4 逻辑运算符

用来连接多个条件,多个关系表达式,最终结果也是一个 boolean 值

3.4.1 一览表

image-20220522161437715

3.4.2 规则

image-20220522161505684

3.4.3 补充

  1. 逻辑与和短路与的区别:比如第一个结果是 false ,短路与直接短路,说不可能了这局,然后退出;但是逻辑与不管怎么样还会去看后面的那哥们。全部一一核实后才会结束。
  2. 正因如此,我们很少用单个的那个逻辑与或都是用短路的,为啥?效率高。

3.4.4 题目

  1. 韩老师这题目出的挺好image-20220522162505993
  2. 还有这道题,不愧是韩老师,我就全贴过来了image-20220522162717611

3.5 赋值运算符

将某个运算后的值赋给指定的变量

3.5.1 一览表image-20220522162920352

3.5.2 特点

image-20220522163042508

细节补充:image-20220522163126131

3.6 三元运算符

3.6.1 语法

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

3.6.2 运算规则

image-20220522163517140

3.6.3 细节

表达式1和表达式2要是可以赋给接受变量的类型,可以自动类型转换也是行的。

3.7 运算符优先级

  1. 只有赋值和单目运算符是从右向左来的,
  2. 一览表,上 > 下image-20220522164002709

3.8 标识符

标识符就是你给类,方法,变量起的名字

  1. 规则image-20220522164216445

    规则是必须遵守的哈

  2. 规范

    image-20220522164442245

    不是必要,但是能显得更加专业

  3. 什么是关键字?

    就是被 Java 语言赋予了特殊含义,有专门用途的英文单词

    关键词中所有字母小写撒

    看图(不用背,多用):image-20220522164735727

    image-20220522164802335

  4. 保留字

    日后可能会晋升关键字的一群大哥

    image-20220522165146506

3.9 键盘输入

是什么个逻辑呢?

就是 Java 有个包里面有个 Scanner 类是专门负责这个键盘读取的。所以咱的思路就是把资源引进来,然后实例化,用实例化的对象的对应方法来读。

  1. 导入包中的类

    import java.util.Scanner;

  2. 实例化这个类的一个对象

    Scanner myScanner = new Scanner(System.in);

  3. 用这个对象的对应方法来读

    int num = myscanner.nextInt();

闲谈:

为什么 new 的时候传给 Scanner 的构造器的参数是 System.in

System 是系统类,然后 in 是静态的,所以可以不 new 直接通过类名调用。然后 System.in 是 inputStream 中的标准输入流,用的方法是 read() ,默认是把键盘当作输入的对象。把这个作为参数传给 Scanner 就是构造出来的那个 Scanner 是从键盘读取信息的。此外的话还有System.out 是标准输出到控制台,System.erro 是错误输出

3.10 进制

这章我是菜鸡,不做过多评讲

3.10.1 介绍

  • 二进制:以 0B 结尾
  • 八进制:以 0 开头
  • 十六进制 以 0X 开头

不区分大小写,那个以什么结尾

3.10.2 十进制转 X 进制

规制:将它不停的除以 X 直到商为 0 ,然后把余数到过来就是对应二进制了。

其他转换补充:image-20220522185725164

3.10.3 源码,反码,补码

image-20220522190258234

3.10.4 位运算符

  • image-20220522190416245
  • image-20220522190453000
  • image-20220522190706499

四,控制结构

4.1 有哪几种?

  • 顺序控制
  • 分支控制
  • 循环控制

4.2 顺序控制

如同水履平地,注意变量的前向引用。也就是只能用前面有的东西。

4.3 分支控制

4.3.1 if-esle
  • 单分支 if
  • 双分支 if-else
  • 多分支 if-else if -… -else
4.3.1.1 单分支

image-20220522191712503

4.3.1.2 双分支

image-20220522191845273

image-20220522191902805

4.3.1.4 多分支

image-20220522192006414image-20220522192048320

4.3.2 嵌套分支

就是很多 if-else 混在一起,建议别超过3层,分内外层。不然跟个俄罗斯套娃一样谁看得懂鸭对吧

image-20220522192340405

4.3.3 switch
4.3.3.1 语法

image-20220522192517567

4.3.3.2 流程图

image-20220522192606877

4.3.3.3 细节

image-20220522192734751

说一下,上图中的 2 为什么表达式不能放浮点型这种小数呢?这个其实我前面有说到的。如果 uu 你细心记得的话。是 Java 中浮点数是不等直接判断相等的。

不记得的同学转到 2.4.3 复习一下

4.3.4 if&switch 怎么选

要是区间判断或者结果是 boolean 就用 if

要是具体判断的值不多,还是 byte ,short ,int,char ,String ,enum 就用 switch

4.4 循环控制

4.4.1 for
4.4.1.1 语法

image-20220522193827420

4.4.1.2 说明

image-20220522193948571

4.4.1.3 流程图

image-20220522194021801

4.4.1.4 注意

image-20220522194141952

4.4.2 while
4.4.2.1 语法

image-20220522194337050

4.4.2.2 流程图

image-20220522194405086

4.4.2.3 细节
  • 该说不说那个循环体中的 循环变量迭代 是我常丢的
  • image-20220522194536586
4.4.3 do…while
4.4.3.1 语法

image-20220522194640796

4.4.3.2 流程图

image-20220522194935451

4.4.3.3 说明
  • image-20220522194810287
  • 根据俺的经验这个大家主动用的不多,都记得 while 去了。其实在实际开发中,这个还是挺有用的,不然它怎么还存活至今,肯定是有他的独特作用滴。用多了,你就知道它的妙处了。
4.4.4 多重循环控制

其实跟上面那个嵌套 if 分支是一个道理,只不过判断换成了循环

  • image-20220522195537642
  • 要说起来,典型的用法有九九乘法表,金字塔,冒泡排序…

4.5 跳转控制

4.5.1 break
  1. 适用对象:switch for while do…while

  2. 作用:用来结束某个语句块的执行

  3. 演示图image-20220522195908685

  4. image-20220522200322501

4.5.2 continue
  1. 用途:结束本次循环,继续下一次循环

  2. 事宜演示图

    image-20220522200939043

  3. 也可以配合标签使用

4.5.3 return

用处:跳出所在方法

五,数组

5.1 介绍

一种引用数据类型,里面存放同一类型的数据

数组?一组数据

5.2

5.2.1 动态初始化

  1. image-20220522202437348
  2. image-20220522202609563
  3. 其实吧,咱还是中括号跟在数据类型后面的比较多,也就是 int[ ] a;

5.2.2 静态初始化

image-20220522202854189

5.2.3 细节

  1. image-20220522203343752
  2. 既然数组中可以放同一数据类型的引用类型,那是不是可以直接把类放到数组中呢?哎对了,一定程度上这是可以的。但是类是是抽象的没有实体,根据 同一个类实例化的对象就可以放在数组中。
  3. 数组它本身不也是引用类型嘛?所以数组元素也可以放数组,是不是打开了新世界的大门了?对这个叫高维数组。见得最多的就是二维数组。哈哈哈。其实数组数据就是对象(Object)

5.2.4 怎么理解 “数组就是对象” 这句话?

  1. 在 Java 数组就是对象

    证据:Object obj = new int[10];

    这是个很特殊的现象。你看数组是怎么产生的new 数据类型[]而对象呢?new 类名()。是不是有着异曲同工的蛛丝马迹。

  2. 数组的父类就是 Object,数组不是由某个类实例化过来的,而是直接由 JVM 指令创建的,这个直接创建对象的父类就是Object,所以数组可以调用Object中的所有方法,这也是论证依据。

  3. 数组不仅自己是对象,也是对象集合。它里面的元素不是可以是任何数据类型嘛?那引用数据类型也是数据类型,类就是一种引用类型,故数组中可以放类。但类是抽象的,所以只能放类的实例化的东西,什么?由类构成的对象!

  4. 其实数组刚诞生的时候就是 Object 的对象,他里面元素就是一个个的 Object ,是null。然后在指定了数据类型后,每个元素的初始值才变成了对应数据类型的初始值。比如制定了 new int[],开始元素都是null后来全是0,再然后才是你给初始化的那些数字。而且数组名里面的值还只是那些数字的地址,它从始至终都只是在引用罢了,这是不是和对象是很像的?

  5. 还有就是=。这个在基础数据类型是将值赋值给变量,但是到了引用类型是什么?是将一个对象给变量名参考引用,也就是说数组中=是引用是地址!比如你想完整拷贝一个数组,你直接 数组1 = 数组2行吗?是行不通的。要怎么办?要一个个拷,哪怕是System.arraycopy 原理还是一个个拷。所以如果你直接把一个数组同时=给A,B。如果 A 自己改了数组元素,他不仅会直接影响原数组,还会让 B 也跟着改变。

  6. 还有就是数组能直接知道里面有效元素的个数嘛?比如我这个数组容量是 100 ,其实里面只放了 10 个。除了在一个个统计一次是没办法直接得出的。.length获得的是数组的容量,不是元素个数。那.sizeof呢?这个对集合可以,但是数组没有。这是不是很奇怪。

  7. 还有数组最多只能是 255 维,是不是又看到了 C 的影子。

5.3 数组赋值机制图

image-20220522214805986

5.4 数组拷贝,反转,扩容

如果上文你看懂了,你就知道这些只不过都是开了一个新的数组,然后把旧的数组数据经过处理移植到新数组来然后 Java 的垃圾自动回收装置自动回收了旧的数组罢了。

5.5 数组排序

5.5.1 分类

  1. 内部排序:将需要处理的数据全加载到内部存储器中进行排序。(交换,选择,插入)
  2. 外部排序:数据量过大,就借助外部存储库来进行排序(合并,直接合并)

5.5.2 冒泡排序

  1. 解释:image-20220523090928344
  2. 思路:image-20220523091046410

5.6二维数组

咋个理解法?

我们以二维数组为例。不是说数组元素可以是任何数据类型吗?包括引用类型对吧,然后数组也是引用类型,那数组可以放在数组元素的位置上。

但是你用一维数组实现这个想法的时候,你会发现没法确定数据类型是什么?是[]嘛?[][] arr = new [][3]这样可以吗?它报错了。那可以是 Object 嘛?因为数组的父类就是 Object 对吧。你看下图证实了这是可以的。

1
2
3
4
5
6
7
8
9
10
11
Object[] test03 = new Object[2];
test03[0] = new int[]{1, 2, 3, 5, 6};
test03[1] = new int[]{4, 5, 6};
for (int i = 0; i < test03.length; i++) {
printArray((int[]) test03[i]);
}

public static void printArray(int[] arr) {
System.out.println(Arrays.toString(arr));
}
}

那还有别的办法嘛?有高维数组也可以。比如二维数组

二维数组就是存储一维数组的一维数组,啥意思就是它就是也是由一维数组组成的。就是你有没有这种疑惑,既然刚才我们用一维数组就一定程度上实现了数组元素放数组。那为啥还要整个二维数组,高维数组呢?

那我直接一个 Object 数组然后因为所有数据类型都是 Object 的子类,那我是不是可以随便放东西进去?你说这个有没有集合的影子?

那我们又回来,这个 Java 中的高维数组,二维数组存在意义是什么呢?他可不可以被替代?

我个人浅薄之见:高维数组/二维数组有自己存在的含义。发现没有,用 Object[] 这种办法实现的,每个元素是独立的,是封闭的,访问拿取难度是复杂的,需要强转。

然而二维数组虽然咱把它看成一维数组的合体容易理解,但是!它是不是像一个矩阵,它其中的元素独立程度是大于 Object[] 的。Object[] 是一行为一个封闭单位,而二维数组是一行中的一个小元素。

换言之,二维数组的数据粒度更小了,数据耦合性更低了,独立性更强了。

所以呢,这可以干些什么?我们可以利用他的每行每列中的每个元素都易于访问,去做单个数据规律性强的事情。而且它更加直观,访问更加方便。

所以说,存在还是有存在的意义的。

5.6.1 语法
  1. 动态初始化

    • image-20220523105909766

    • 这个是个很妙的做法,很高级下面的 2 就是实现这个的。image-20220523110310726

    • 动态初始化之变阶不确定列数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //演示二维数组动态初始化列数不确定
    //这个主要利用的就是
    int[][] test04 = new int[3][];//
    for (int i = 0; i < test04.length; i++) {
    test04[i] = new int[i+1];
    for (int j = 0; j < test04[i].length; j++) {
    test04[i][j] = j+1;
    }
    }
    for (int[] ints : test04) {
    printArray(ints);
    }

    public static void printArray(int[] arr){
    System.out.println(Arrays.toString(arr));
    }



  2. 静态初始化

    image-20220523111802540

5.6.2 内存图

image-20220523110151120

5.6.3 细节

image-20220523111958605

有个好玩的我也贴过来

1
2
3
//5.如何花样一行代码同时创建一维数组和二维数组
int[] a,b[];
//a 就是一维数组,b 就是二位数组

六,面向对象编程

6.0 三大特征

6.0.1 介绍

  • 封装
  • 继承
  • 多态

6.0.2 封装

6.0.2.1 介绍

image-20220524102717747

6.0.2.2 好处

image-20220524102804521

6.0.2.3 步骤

image-20220524102950827

注:其实也可以把带有业务逻辑的 set 放到 构造器中,这样构造器也会有验证过程,更加安全。

6.0.3 继承

6.0.3.1 需求描绘
  1. 当两个类有很多属性和方法是相同的,我们就可以把他们相同的提取出类做成一个父类表示他们的共性,然后让他们自己写自己的特性不需要再重新定义这些属性和方法,只需要声明继承这个父类即可。
  2. 好处:提高了代码复用性,扩展性,维护性,让你写的代码更接近人类思维。
6.0.3.2 示意图

image-20220524103812161

6.0.3.3 语法

image-20220524103918464

6.0.3.4 细节
  1. image-20220524105900441
  2. image-20220524105913314
6.0.3.5 本质
  • image-20220524110522983
  • image-20220524110609393
  • 贴个题目:image-20220524110812498

6.0.4 多态

6.0.4.1 是什么?
  1. 就是说方法或者对象具有多种形态,这个是建立在封装和继承上的

  2. 具体的体现形式:

    方法重载 - 传入不同的参数就会执行不同的方法

    寓意的是状态的多样性有着一对多的意味

6.0.4.2 对象多态
  1. image-20220524142624587

  2. 前提:两个对象存在继承关系

  3. 向上转型:image-20220524143011899

  4. 向下转型:image-20220524143035813

  5. 注意:属性没有重写之说,属性的值看编译类型

  6. instanOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或其子类型。

6.0.4.3 Java 的动态绑定机制
  1. 介绍:image-20220524144417135

  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
    package day02;

    /**
    * @author 李敏灿
    * @version 1.0
    */

    public class demo01{
    public static void main(String[] args) {
    //a 的编译类型 A, 运行类型 B
    A a = new B();//向上转型
    System.out.println(a.sum());//?40 -> 30
    System.out.println(a.sum1());//?30-> 20
    }
    }

    class A {//父类
    public int i = 10;

    //动态绑定机制:
    public int sum() {//父类 sum()
    return getI() + 10;//20 + 10

    }

    public int sum1() {//父类 sum1()
    return i + 10;//10 + 10
    }

    public int getI() {//父类 getI
    return i;
    }
    }

    class B extends A {//子类
    public int i = 20;

    // public int sum() {
    // return i + 20;
    // }
    public int getI() {//子类 getI()
    return i;
    }
    // public int sum1() {
    // return i + 10;
    // }
    }
6.0.4.4 应用
  1. 多态数组:数组定义为父类类型,里面实际保存子类类型
  2. 多态参数:方法定义的形参为父类类型,实参允许为子类类型

6.1 类与对象

6.1.1 类与对象的关系

  1. image-20220523114528305

  2. image-20220523115228243

  3. 怎么通俗的理解类和对象呢?举个例子。

    高富帅就是一个类,因为你又高又富又帅所以就是这个类的一个对象。

6.1.2 对象在内存中的存在形式

  • image-20220523115621335

  • 如何创建对象

image-20220523140107603

6.1.3 类和对象的内存分配机制

6.1.3.1 Java 内存的结构分析

image-20220523140551124

6.1.3.2 Java 创建对象的流程分析
  1. 在方法区中加载类的相关信息,属性和方法。这个阶段只会有一次
  2. 在堆中分配空间,进行初始化,分为默认初始化,显式初始化,构造器初始化。
  3. 将堆的地址赋给栈中的对象名,就指向对象
  4. 示意图:image-20220523191613378

6.2 类组成

6.2.1 属性

6.2.1.1 叫什么

从概念上它叫 属性,成员变量,field(字段)

咱统一叫属性

6.2.1.2 由什么组成

数据类型:包括基础数据类型,引用数据类型(数组,对象)

例如:image-20220523135732824

6.2.1.3 注意

image-20220523135852600

6.2.1.4 怎么访问属性

image-20220523140315280

6.2.2 方法

6.2.2.1 调用原理

image-20220523141312538

6.2.2.2 好处

image-20220523141533995

6.2.2.3 定义

image-20220523141616140

解释:

  1. 形参列表:表示成员方法输入
  2. image-20220523141709060
6.2.2.4 注意
  1. 调用带参数的方法,对应着参数列表传入相同类型或兼容类型的参数

  2. 实参和形参的类型要一致或兼容,个数,顺序必须一致

  3. 方法不能嵌套定义

  4. 一个方法最多有一个返回值。想多个用数组类引用的。

  5. 返回类型可以是任意类型,包括基本类型和引用类型

  6. 有返回值必有 return

  7. 命名最好见名知意

  8. public,protected,默认,private

  9. 返回数据类型image-20220523142524333

  10. 形参:image-20220523142703353

6.2.2.5 调用说明

image-20220523142805269

其实吧,也不一定非要对象名.方法名来调用。静态(static)方法就可以直接类名.方法名来用。🐶

同类中直接调用还要注意一个问题就是static问题。如果你在 public 类中的 main 方法中调用同样写在 public 类下面的方法。不是 static 方法调不来的。因为 main 是 static 的。

6.2.2.6 传参机制
6.2.2.6.1 基本数据类型

基础数据类型,传递的是值,是值得拷贝,形参得任何改变不影响实参

6.2.2.6.2 引用数据类型

引用数据类型传递的本质是地址,可以通过形参影响实参。

其实传的也是值,不过这次的值他是个地址。所以说本质上传的是地址。

6.2.2.7 递归

递归就是方法自己调用自己,这里是比较难的,不是有句话嘛:😵

人理解迭代,神理解递归。

  1. 贴个老师的示意图image-20220523144619330

  2. 递归重要规则image-20220523144716686

  3. 贴个题目吧image-20220523144851865

    还有个小老鼠找迷宫,汉诺塔,八皇后也都很难,都是用递归。

这章的难度不小,作者修行未果,水平不够,贴几张我老师的图,有兴趣的大佬感兴趣自行百度探索。

6.2.2.8重载
6.2.2.8.1 基本介绍

Java 中允许一个类中有多个同名方法的存在,只是要求形参列表不同。这就是重载!

6.2.2.8.2 好处
  • 减轻起名烦恼

  • 减轻记名烦恼

6.2.2.8.3 举例

image-20220523150007197

6.2.2.8.4 注意细节

说到底只用在乎两件事:

  1. 函数名一样
  2. 形参有区别

其他的什么,返回类型,修饰符都随意

“名相等,参不同,其他不影响” 清楚不

形参有区别又指:参数类型或者个数至少有一个不一样。

你说参数名?不影响

6.2.2.9 重写
6.2.2.9.1 介绍

就是子类中有和父类一摸一样的方法(细节下面说),只有方法体不一样。我们就说子类这个方法覆盖了父类的方法,造成了方法重写。

6.2.2.9.2 细节
  1. image-20220524113115405
6.2.2.10重写&重载辨析

image-20220524113235789

6.2.3 构造器

6.2.3.1 需求

就是咱想直接在创建对象的时候就直接指定这个对象的属性就发明了这个构造器

6.2.3.2语法
  • image-20220523184954378

  • image-20220523185035408

  • 那我提个问:构造器可以被 static final abstract 修饰嘛?为什么?

    1. static:JAVA中静态的东西都是属于类的,为类服务,构造函数是为了初始化对象,为对象服务。构造函数是用来生成实例,既然是实例就不是static的。这两者是相互矛盾的

    2. final:构造器是不允许被继承,不允许被重写的。而 final 的作用就是防止函数被重写,所以构造器自己就有这功能,就不必画蛇添足了。

    3. abstract:abstract 修饰的方法没有函数体,还要在子类中重写。然而在 new 对象的时候就会自动调用构造器,因此必须要有一个函数体(矛盾一)。此外构造器不能被重写。

6.2.3.3 基本介绍

image-20220523185151371

6.2.3.4 注意细节

image-20220523191418216

6.2.4 代码块

6.2.4.1 介绍

image-20220524193557050

6.2.4.2 语法

image-20220524193637420

6.2.4.3 好处/用法

image-20220524193721897

6.2.4.4 细节
  1. image-20220524194757367
  2. image-20220524194926160
  3. image-20220524195126523
  4. image-20220524195348406
  5. static 代码什么时候用:如果有些代码必须在项目启动的时候就执行,那么我们就可以使用静态代码块来实现,这种代码是主动执行的。

6.2.5 内部类

6.2.5.1 分类
  1. 局部位置:局部内部类(有类名),匿名内部类(没有类名)
  2. 成员位置:成员内部类(没有 static),静态内部类(有 static)
6.2.5.2 介绍

image-20220525145458411

6.2.5.3 语法

image-20220525145754625

6.2.5.4 局部内部类
  1. image-20220525150632634

  2. 上图第二点说明:

    ==局部内部类不能用 public,protected,private,static修饰可以用 final 修饰==

    为什么?

    因为局部内部类就是一个局部变量,而局部变量本身就是一个访问权限 的设定。 只能在局部调用,也就是说局部变量的生命周期在{}之中除了这个方法外界是没办法访问你这个变量,所以不需要用任何修饰符修饰,比如private ,public protected,等但是能加final,也不能加static,静态的关键词

    因为static只能修饰成员变量和成员方法,在局部变量中用static修饰,又不能直接被类调用,而static关键字就是不直接过对象而用类就可以直接调用的,所以局部变量前不能加static关键字

6.2.5.5 匿名内部类
6.2.5.6 介绍

注意啊,这也是一个对象。

image-20220525151046000

image-20220525163917158

6.2.5.7 插眼

具体?lambda?

6.2.5.6 成员内部类
6.2.5.7 介绍

image-20220525172710216

这里要特殊补一句的就是:这个成员内部类怎么得到它实例化的对象呢?需要自己在外部类中写一个公开的方法,来返回。

6.2.5.7 静态内部类
  • image-20220525175654007

  • 外部其他类怎么访问静态内部类image-20220525175812832

6.3可变参数

6.3.1 概念

就是 Java 中多个方法,名字相同,功能相同只是参数个数不同,就可以用封装成一个方法。用可变参数。

6.3.2 基础语法

image-20220523151553970

6.3.3 注意细节

image-20220523152146884

举例:

1
2
3
4
5
6
int[] test05 = {1,2,3};
printMixed("这里有这么几个数",test05);

public static void printMixed(String s1,int... i1){
System.out.println(s1+Arrays.toString(i1));
}

6.4 作用域

6.4.1 基本使用

image-20220523161034727

思考:为什么 Java 中全局变量有默认直接用,局部变量没有默认值必须赋值后才能用?

我个人觉得哈可能会有一下几点:

首先科普一下有哪几种变量,

类变量:在类内方法外,带有 static 修饰。是一个类共用的。存在于内存中的方法区,类一加载就会对它初始化。有默认值,可以不赋值。

属性(成员变量): 在类内方法外,没有 static 修饰。每个类实例化的对象各不相干,各不相同。存在于堆中,一实例化对象就会初始化它。有默认值,可以不赋值。

局部变量:在方法中,用方法才会加载,存在于栈中,没有默认值,局部变量可能是任意值,而不会自动初始化为某个值。(from 《Java 编程思想》)不可以不赋值就用,编译过不去。

  1. 存储位置。Java 中栈适合用于存储数据量少,但他查询存储速度比堆快,而堆可用于存储数据量大的,增删改查结点就比较麻烦,所以从数据量大小和访问频率来看这可能有点关系,毕竟局部变量是在用的时候直接进栈的,如果没有赋值,这个变量就没有初始值,也就没有办法操作,故局部变量要初始化。

  2. 赋值和取值的顺序差异。对于类变量和属性这种由类管辖的变量来说,他的赋值和取用顺序是不确定的。比如下面的代码:

    1
    2
    3
    4
    5
    6
    public class Test {  
    public String name;
    public void AAA() {
    System.out.println("AAA name=" + name);
    }
    }

    这里 name的赋值可以发生在 AAA 之前也可以发生在 AAA 之后,这是运行时决定的,是 JVM 来管的。javac编译的管不着的,也拿捏不住根本。

    就像你用这个Test类实例化了一个对象,你就只是想调用 AAA 方法你直接new Test().AAA(),这个顺序是不是就不是常规的顺序结构了。

    但是局部变量不一样,你看下面这个,编译器直接说必然是顺序结构,必须先定义赋值后访问。

    1
    2
    3
    4
    public  void AAA() {     
    String myname = "张三";
    System.out.println("AAA name=" + myname);
    }
    1. 目的和安全考虑。局部变量是拿来做运算的的,做运算如果不人为给定,逻辑不同。因此,为局部变量设置默认值不是一个好主意。否则,检查意外答案的原因非常困难且耗时。“我无法提出一个有效的理由使编译器将所有未初始化的变量初始化为0” gosling 如是说。

    2. 防止编程人员出错。在《Java 编程思想》中有提到这个,image-20220523164534842

      语言设计者觉得人是不可靠的,这样做是为了大家不容易出现错误,他觉得这是对程序员的一种约束限制。C++ 就没有这个问题。

    3. 其实还有…额,作者能力有限,就说到这。希望更有能力的看官朋友们在评论区补充。

6.4.2 注意事项

image-20220523175216545

6.5 关键字

6.5.1 this

6.5.1.1解释

JVM 给每个对象分配 this ,代表当前对象。

简单来说,那个来调用,this 就代表哪一个对象

6.5.1.2 注意
  • image-20220523193350999

  • image-20220523193359592

  • 其实吧,这个是用来处理函数中,形参和类中的全局变量一样的情况。你要是代码块中没有和类中的全局变量没有同名的,你用这个的次数很少的。但是有些函数和特殊地方还是有用。

6.5.2 super

6.5.2.1 介绍

super 代表父类的引用,用于访问父类的属性,方法,构造器。

6.5.2.2 语法

image-20220524111121563

6.5.2.3 注意细节
  1. image-20220524111304487
  2. 访问属性/方法的规则:image-20220524111339231
  3. image-20220524111452762

6.5.3 两者比较

image-20220524111704211

6.5.4 final

6.5.5 介绍

image-20220525102909066

6.5.6 细节
  1. image-20220525103358913

  2. image-20220525103751952

    1. 问:final 和 static 搭配不会使类加载的原理是什么?怎么理解?

      严格来说应该是当他们两个同时修饰基础数据类型或者 String 时可以这样。

      final 是在 运行时候加载的;static 是在 类信息加载时候初始化的。然而当 final 和 static 一起的时候,类加载的准备阶段就初始化了。

      具体过程:Java 中有个属性叫 ConstantValue,他是通知 JVM 自动给 static 变量赋值的。只有同时被 static 和 final 修饰才会挂上这个属性。Java 是解释性语言,在运行的时候有这个几个阶段:首先是写好的源码.java文件然后javac编译成.class字节码文件,然后再java运行在 JVM 上跑起来。跑的第一步就是 类加载的准备,然后再是类加载,再初始化,再进 main方法。

      而当你是 static final 双 buff 变量,在 javac编译的时候就会给你生成 ConstantValue 属性,然后在类加载的准备时机便会根据这个 ConstantValue 属性给变量设置相应的值。

      所以在 类加载前这些事就整完了,所以它两搭配的变量访问不会引起类加载。

      “老师我还有一个问题?为什么ConstantValue的属性值只限于基本类型和string?”

      因为从常量池中只能引用到基本类型和String类型的字面量

      其实还有原因……额我的能力有限就讲到这,欢迎各位更有实力的朋友在评论区补充。

6.6 包

6.6.1 作用

  1. 区分同名类
  2. 当类很多时方便管理类
  3. 控制访问范围

6.6.2 基本语法

image-20220524094502143

6.6.3 本质

本质就是个文件夹来存放类

6.6.4 命名

  1. 规则:只能包含字母,数字,下划线,小圆点。但是不可以数字开头,不可以是保留字或者关键字。
  2. 规范:一般是小写字母加小圆点。例如:com.公司名.项目名.业务模块名

6.6.5 常用包

image-20220524094946548

6.6.6 怎么引入包

image-20220524095023678

其实吧,推荐那种用什么类就专门引用这个包下面的一个类。

6.6.7 注意细节

image-20220524095211282

6.7 访问修饰符

主要加方法或属性前面,表示访问权限(范围)

6.7.1 基本介绍

image-20220524095451218

6.7.2 一览图 (很重要)

  • image-20220524095529921

  • 这里面难就难在中间两个(protected,默认)不同包的子类的处理上。

    比如 protected ,我问你是不是不同包就不能访问?不是的。比如有个不同包的子类,它是能访问的。

    又比如 默认 ,那我再问你:是不是所有子类都不能访问? 不是的,同包子类就是可以访问的。

    所以说我们怎么理解?总结一下

    1. 使用protected修饰后方法、变量在同一包中都为可见的(main函数中需先实例化);而在不同包中只有在子类中可见
    2. 默认修饰后,只要在本包内任何的都可见

6.7.3 注意细节

image-20220524102207367

6.8 Object 类详解

6.8.1 equals

  1. image-20220524150322381

  2. 注意:第五条的意思是,一般 equals 对于引用类型默认还是判断地址是否相等,但是 Integer String 给你重写好了,它两是判断内容是否相等的。

  3. 怎么重写:用方法重写,然后把每个内容比较一遍就可以知道等不等了。(一般来说在 IDEA 下直接打个 equals 回两下车就行,当然能自己理清逻辑写下也是极好的。)

6.8.2 hashCode

  1. 是什么

    《对象地址替代品》image-20220524151114437

  2. 小结image-20220524151148800

6.8.3 toString

  1. 介绍image-20220524151350960
  2. 注:
    • 重写 toString 方法,打印对象或拼接对象时都会自动调用该对象的 toString 方法
    • 当直接输出一个对象时,toString 会被默认的调用
  3. 当然了,一般我们都喜欢重写这个方法来打印对象属性,IDEA 中快捷键,或者你输入 toString 他就会给你补全了。

6.8.4 finalize

image-20220524151928634

这个在实际开发中很少用的哈。更多是为了应付面试。

6.9 类变量/类方法

6.9.1 类变量

6.9.1 介绍

image-20220524190531772

6.9.2 语法

image-20220524190614614

6.9.3 访问

image-20220524190758786

6.9.4 细节

image-20220524190959108

6.9.2 类方法

6.9.2.1 介绍

image-20220524191126390

6.9.2.2 调用

image-20220524191211622

6.9.2.3 适用场景

image-20220524191355049

所以说是把一些常用的工具类做成静态的,然后就可以不实例化对象直接用,就很方便,比如 Math 这些。

6.9.2.4 细节

image-20220524191956573

6.9.3 单例设计模式

6.9.3.1 设计模式是什么

image-20220525095122851

6.9.3.2 单例设计模式又是什么

image-20220525095302995

6.9.3.3 实现方式
  1. image-20220525095356381

  2. 饿汉式就是直接在开头那个 static 就直接给你 new 了对象

    懒汉式是在开头给你 static 时 只是声明对象名,在公开的返回对象的方法中判断要是没有 new 就给你 new。new 了就不给 new 了。

6.9.3.4 代码演示
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 day03;

/**
* @author 李敏烂
* @version 1.0
* 演示单例设计模式
*/

public class Singer {
public static void main(String[] args) {
Cat cat1 = Cat.getInstance();
Cat cat2 = Cat.getInstance();
System.out.println(cat1);
System.out.println(cat2);
compareSame(cat1, cat2);

Dog dog1 = Dog.getInstance();
Dog dog2 = Dog.getInstance();
System.out.println(dog1);
System.out.println(dog2);
compareSame(dog1, dog2);
}

//比较是否是同一对象的方法
public static void compareSame(Object o1, Object o2) {
if (o1 == o2) {
System.out.println("是一样的");
} else {
System.out.println("不是一样的");
}
}
}

//一,饿汉式
class Cat {
private String name;
//2.自己 在内部 new
static Cat cat1 = new Cat("小花");

//1.私有化构造器
private Cat(String name) {
this.name = name;
}

//3.对外公布一个得到猫对象的方法
public static Cat getInstance() {
return cat1;
}

//4.重写 toString 用于输出
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}

//一,懒汉式
class Dog {
private String name;
//2.自己 在内部 new
static Dog dog1;

//1.私有化构造器
private Dog(String name) {
this.name = name;
}

//3.对外公布一个得到猫对象的方法
public static Dog getInstance() {
if (dog1 == null) {
dog1 = new Dog("小白");
}
return dog1;
}

//4.重写 toString 用于输出
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
6.9.3.5 比较

image-20220525102738384

6.10 main 方法详解

6.10.1 说明image-20220524192714496

6.10.2 细节image-20220524192740454

6.10.3 IDEA 中怎么给 main 传参

  1. image-20220524193054259
  2. image-20220524193155253

6.11 抽象类

6.11.1 介绍

  1. image-20220525133143797

  2. image-20220525133242730

  3. image-20220525133435874

  4. image-20220525133456627

6.11.2 细节

image-20220525133554556

image-20220525134325590

image-20220525134437386

6.11.3 模板设计模式

6.11.3.1 介绍

image-20220525134817531

6.11.3.2适用

image-20220525134919103

6.12 接口

6.12.1 介绍

image-20220525140035344

6.12.2 场景

image-20220525140145362

6.12.3 注意

  1. image-20220525140247904

  2. image-20220525140340457

  3. 证明:

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

/**
* @author 李敏灿
* @version 1.0
*/

public class Demo {
public static void main(String[] args) {
//证明接口中的属性,是 public static final
System.out.println(IB.n1);//说明 n1 就是 static
//IB.n1 = 30;
//这里打开就出错说明 n1 是 final
}
}

interface IB {
//接口中的属性,只能是 final 的, 而且是 public static final 修饰符
int n1 = 10; //等价 public static final int n1 = 10;
void hi();
}
  1. 为什么 Java 中接口中的属性默认是 static 加 final?

    接口的地位相当于一个合同,一个模板,一种规范。

    static:是 static 是考虑未来子类中也会有 static 方法,要是不是 static 属性,那些子代被 static 修饰的就不能访问这个了。这样设计能最大限度发挥接口的属性的功能?

    final:如果这里的变量不是 final 的,并且接口中的方法都是 abstract 的, 那我想修改类就得实现类,这违背了接口的设计初衷。而且接口就是一种规范,后面子类都得按这个做事,怎么呢想改就改了。

6.12.4 怎么理解接口和抽象类

  1. image-20220525143440341
  2. image-20220525143627127

6.12.5 接口多态的体现

image-20220525143912324

也就是编译类型为接口的变量可以被赋予实现了这个接口的其他类的实现对象。

七,枚举&注解

7.1 枚举

7.1.1 介绍

image-20220525190819920

7.1.2 实现方式

image-20220525191016848

7.1.3 自定义枚举

  1. 原理image-20220525191057450

  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
    package day03;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示枚举
    */

    public class CustomEnumeration {
    public static void main(String[] args) {
    Season season1 =Season.FALL;
    System.out.println(season1);
    }
    }
    class Season{
    private String season;
    private String des;
    public static final Season SPRING = new Season("春天","万物复苏");
    public static final Season SUMMER = new Season("夏天","热烈火热");
    public static final Season FALL = new Season("秋天","天高气爽");
    public static final Season WINTER = new Season("冬天","白雪皑皑");

    private Season(String season, String des) {
    this.season = season;
    this.des = des;
    }

    public String getSeason() {
    return season;
    }

    public String getDes() {
    return des;
    }

    @Override
    public String toString() {
    return "Season{" +
    "season='" + season + '\'' +
    ", des='" + des + '\'' +
    '}';
    }
    }
  3. 关键

    image-20220525191515277

7.1.4 enum 关键字枚举

7.1.4.1 介绍
  1. image-20220525194615833

  2. image-20220525194626892

  3. image-20220525194706665

7.1.4.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
package day03;

/**
* @author 李敏灿
* @version 1.0
* 演示枚举
*/

public class CustomEnumeration {
public static void main(String[] args) {
Season1 season11 = Season1.SUMMER;
System.out.println(season11);
}
}

enum Season1{
SPRING("春天","暖"),SUMMER("夏天","热"),FALL("秋天","凉"),WINTER("冬天","冷");
private String season;
private String des;

Season1(String season, String des) {
this.season = season;
this.des = des;
}

@Override
public String toString() {
return "Season1{" +
"season='" + season + '\'' +
", des='" + des + '\'' +
'}';
}
}
7.1.4.3 Enum方法
  1. image-20220525203308302
7.1.4.4 enum 实现接口

image-20220525204645610

7.5 注解

7.5.1 理解

image-20220525205138272

7.5.2 常用注解

image-20220525205310649

7.5.3 Override

image-20220525205547957

7.5.4 Deprecated

image-20220525205647415

7.5.5 SuppressWarnings

  1. image-20220525205758808
  2. image-20220525205913268
  3. image-20220525205938155

7.5.6 元注解

7.5.6.1 介绍

解释修饰其他注解的注解

7.5.6.2 种类

image-20220525210123556

7.5.6.3 @Retention
  1. image-20220525210221943
  2. image-20220525210244698
7.5.6.4 @Target

image-20220525210443633

7.5.6.5 @Documented

image-20220525210820912

7.5.6.6 @Inherited

image-20220525210856225

八,异常

8.1 概念

在 Java 语言中,程序执行中发生的不正常情况称之为“异常”。(开发过程中的语法错误和逻辑错误不是异常)

8.2 异常体系图

8.2.1 图

image-20220525211707141

8.2.1 小结

image-20220525212422858

8.3 运行时异常

  1. image-20220525212522164

空指针:要用的对象是空的

数学运算:比如除以 0 这种

数组下标越界:数组下标小于 0 或者大于数组容量

类型转换:本来人家是 String 你非要转 double

数字格式错误:当应用程序试图将字符串转换成一种数值类型, 但该字符串不能转换为适当格式时, 抛出该异常 => 使用异常我们
可以确保输入是满足条件数字. (Integer.parseInt)

8.4 编译异常

8.4.1 介绍

image-20220525213123250

8.4.2 常见罗列

image-20220525213151402

8.5 异常处理

8.5.1 介绍

image-20220525213347728

8.5.2 方法

image-20220525213424939

8.5.3 示意图

  1. try-catchimage-20220525213506015
  2. throwsimage-20220525213546906

8.6 try-catch

8.6.1 说明

image-20220525213739066

8.6.2 注意

  1. image-20220525213815325
  2. image-20220525213839596
  3. image-20220525214018022

8.7 throws

8.7.1 介绍

image-20220525214122422

8.7.2 细节

image-20220525214208473

8.8 自定义异常

8.8.1 概念

image-20220525214318699

8.8.2 步骤

image-20220525214344039

8.8.3 演示

  1. 结果图:image-20220525215002263

  2. 代码:

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

    /**
    * @author 李敏灿
    * @version 1.0
    */

    public class ExceptionDemo {
    public static void main(String[] args) {
    if (!(1 < 0)) {
    throw new IntTest("这是不对的");
    }
    }
    }

    class IntTest extends RuntimeException {
    public IntTest(String message) {
    super(message);
    System.out.println(message);
    }
    }

8.9 throw&throws

image-20220525215148171

九,常用类

这章有很多设计底层的东西,比较难

9.1 包装类

9.1.1 分类

包装类就是针对八种数据类型的引用类型——包装类

如图:image-20220525230523053

9.1.2 包装类和基本数据类型的转换

  1. image-20220525230630939

  2. image-20220525230740395

  3. Integer 和 Character 常用方法

    image-20220525231256615

9.1.3 一个面试题

就是自动装包的原理是什么?

  1. 自动装包底层用的还是那个 Integer.valueOf().我们来看看他的源码是什么

    1
    2
    3
    4
    5
    6
    @HotSpotIntrinsicCandidate
    public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
    }

    从上面Integer valueOf()源码可以知道:当传入数字再一个范围内的话就是直接返回IntegerCache.cache这个数组中的元素,要是不在这个范围就是直接 new 了。那这个IntegerCache.cache又是个怎么样的数组呢?再追一次源码

    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
    private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
    // high value may be configured by property
    int h = 127;
    String integerCacheHighPropValue =
    VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
    if (integerCacheHighPropValue != null) {
    try {
    int i = parseInt(integerCacheHighPropValue);
    i = Math.max(i, 127);
    // Maximum array size is Integer.MAX_VALUE
    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
    } catch( NumberFormatException nfe) {
    // If the property cannot be parsed into an int, ignore it.
    }
    }
    high = h;

    cache = new Integer[(high - low) + 1];
    int j = low;
    for(int k = 0; k < cache.length; k++)
    cache[k] = new Integer(j++);

    // range [-128, 127] must be interned (JLS7 5.1.7)
    assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
    }

    大家看出来了吗?这个IntegerCache.cache其实就是一个依次从 -128~127 的一个 Integer 数组。所以我们能得出什么结论:==当自动装包的数字在 -128~127 中的时候其实返回来是从一个固定的数组中返回的,如果数字是同一个返回的就是同一个对象。如果出了这个范围他就会自己直接 new 一个。==

  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
    package day04;

    /**
    * @author 李敏灿
    * @version 1.0
    */

    public class PackagingClass01 {
    public static void main(String[] args) {
    //1.直接 new 的坑定不同
    Integer i1 = new Integer(1);
    Integer i2 = new Integer(1);
    System.out.println(i1 == i2);//false
    //2,自动装箱用的是 valueOf 方法。分情况
    //-128~127 是相等的,出了这个范围就不是
    Integer i3 = 1;
    Integer i4 = 1;
    System.out.println(i3 == i4);//true
    System.out.println(i1 == i3);//false
    Integer i5 = 127;
    Integer i6 = 127;
    System.out.println(i5 == i6);//true
    Integer i7 = -128;
    Integer i8 = -128;
    System.out.println(i7 == i8);//true
    Integer i9 = 128;
    Integer i10 = 128;
    System.out.println(i9 == i10);//false
    }
    }
  3. 还有一个细节就是==两边有一个基本数据类型比较的就是值是否相等。==比较的是什么前面的笔记说的很清楚了哈,不记得要回顾。演示代码:

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

    /**
    * @author 李敏灿
    * @version 1.0
    */

    public class PackagingClass02 {
    public static void main(String[] args) {
    Integer i = 128;
    Integer i2 = new Integer(128);
    int i1 = 128;
    System.out.println(i == i1);//true
    System.out.println(i2 == i1);//true

    }
    }

9.2 字符串

9.2.1 String

9.2.1.1 介绍

image-20220526101547553

这张图为 char[]怎么转String提供了一套有效的思路。

9.2.1.2 创建方式
  1. 直接赋值

    String s = "你好啊朋友";

  2. 调用构造器

    String s = new String("你好啊朋友");

9.2.1.3 两种创建区别
  1. image-20220526101953997

  2. 出道小题目:

    • image-20220526102123389

    • image-20220526102135360

    • image-20220526102358438

9.2.1.4 特性
  1. String 是一个 final 类,代表不可变的字符序列

  2. 字符串是不可变的。一个字符串对象一旦被分配,他的==内容==是不可变的。(==字符串指向可以变==)

  3. 补充:这个在其他语言有自己的表现形式,比如同样是做 Android 开发的另一门语言 Kotlin 中就是用val不可变变量来做这个。具体什么意思呢?就是我这个String s = "hello";一旦字符串赋给了字符串变量。字符串的==内容==就不可以变辣。不能说hello自己变成了Hello。但是s变量指向可以变,比如又指向了HaHaHa.这个过程创建了两个字符串对象,然后这个变量指向从第一个换成了第二个。字符串对象自己是没有变的。所以他这里的不可变是指==字符串对象的内容不可变,但是字符串变量的指向可变==。

    image-20220526103907153

  4. 出个题目:

    • image-20220526104031917
    • image-20220526104133346
    • image-20220526104206282
9.2.1.5 常用方法
  1. image-20220526105127743

  2. image-20220526105320489

    concat 是拼接字符串

    这些要用的时候直接查就可以了

9.2.2 StringBuffer

9.2.2.1 介绍

就是内容可以变的 String

image-20220526112334756

9.2.2.2 VS String
  1. 区别image-20220526112619571

  2. 怎么转换

    • String -> StringBuffer

      image-20220526112736052

    • StringBuffer->String

      image-20220526112826255

9.2.2.3 常见方法
  1. 增:append(加的东西)
  2. 删:delete(x,y) [x,y)的字符
  3. 改:replace(x,y,”AAA”) 用 AAA 替换 [x,y) 处的字符
  4. 查找第一出现索引 indexOf()
  5. 插入 insert(x,”AAA”) 把 AAA 插入在索引 x 处,原本的后移
  6. length 长度

问个小问题:String s = null;请问append 这个输出什么?

答案:“null”理由在源码。

9.2.3 StringBuilder

9.2.3.1 介绍

image-20220526113946999

9.2.4 三者

  1. 比较image-20220526114526298
  2. 选择image-20220526114610696

9.3 Math

9.3.1 介绍

这里面都是执行基本数学运算的方法,均为静态方法。需要就查下 API

9.3.2 方法图

  1. abs 绝对值
  2. pow 求幂
  3. ceil 向上取整
  4. floor 向下取整
  5. round 四舍五入
  6. sqrt 求开方
  7. random 求随机数 [0,1)
  8. max/min 最值

9.4 Arrays

9.4.1 介绍

这里也是一些静态方法,用于管理或操作数组

9.4.2 具体

image-20220526152728644

9.5 System

9.5.1 罗列

image-20220526153706254

9.6 大数

9.6.1 介绍

image-20220526153807499

9.6.2 常用方法

image-20220526153840413

9.7 日期

9.7.1 第一代日期 Date

  1. 一览图image-20220526154023605

  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
    package day04;

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示第一代日期
    */

    public class DateTimeDemo1 {
    public static void main(String[] args) throws ParseException {
    //1.Date
    //直接这样 new 是返回当前时间,外国格式
    Date date = new Date();
    System.out.println(date);
    //这样是用秒数去换算是什么时候
    Date date1 = new Date(989989);
    System.out.println(date1);

    //2.SimpleDateFormat
    //设置格式,注意字母是有规定的
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy 年 MM 月 dd 日 hh:mm:ss E");
    String s = simpleDateFormat.format(date);
    System.out.println("进行了格式化的时间\n"+s);//将原来的 Date 转了格式输出
    String s1 = "2018 年 09 月 23 日 09:23:09 周一";
    System.out.println(simpleDateFormat.parse(s1));//也可以把符合规范的 String 转为 Date
    }
    }

9.7.2 第二代日期 Calendar

9.7.2.1 介绍

image-20220526155547520

9.7.2.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
package day04;

import java.util.Calendar;

/**
* @author 李敏灿
* @version 1.0
* 演示第二代时间 Calender 日历
*/

public class DateTimeDemo2 {
public static void main(String[] args) {
//1. Calender 构造器是私有的
Calendar c1 = Calendar.getInstance();
System.out.println(c1);//打出来是奇怪东西
System.out.println("年:"+c1.get(Calendar.YEAR));//年:2022
//他的月份是从 0 开始的,所以要加一
System.out.println("月:"+(c1.get(Calendar.MONTH)+1));//月:5
System.out.println("日:"+c1.get(Calendar.DAY_OF_MONTH));//日:26
System.out.println("时:"+c1.get(Calendar.HOUR));//时:4
System.out.println("时:"+c1.get(Calendar.HOUR_OF_DAY));//时:16
System.out.println("分:"+c1.get(Calendar.MINUTE));//分:9
System.out.println("秒:"+c1.get(Calendar.SECOND));//秒:23
//是的他没有组合的,得自己拼起来
System.out.println("组合输出:"+c1.get(Calendar.YEAR)
+"-"
+(c1.get(Calendar.MONTH)+1)
+"-"
+c1.get(Calendar.DAY_OF_MONTH)
+" "
+c1.get(Calendar.HOUR_OF_DAY)
+":"
+c1.get(Calendar.MINUTE)
+":"
+c1.get(Calendar.SECOND)
);//组合输出:2022-5-26 16:9:23

}
}

9.7.3 第三代日期

9.7.3.1 出生背景

image-20220526163430514

9.7.3.2 介绍

image-20220526163602293

9.7.3.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
package day04;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;

/**
* @author 李敏灿
* @version 1.0
* 演示第三代日期
*/

public class DateTimeDemo3 {
public static void main(String[] args) {
//1. 使用 now() 返回当前日期时间
LocalDateTime now = LocalDateTime.now();//LocalDate.now()~LocalTime.now()
System.out.println(now);
//2. DateTimeFormatter 来格式化
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(now);
System.out.println("格式化过后的时间" + format);
//3.获取单个信息
System.out.println("年:" + now.getYear());//年:2022
System.out.println("月:" + now.getMonth());//月:MAY
System.out.println("月:" + now.getMonthValue());//月:5
System.out.println("日:" + now.getDayOfMonth());//日:5
System.out.println("时:" + now.getHour());//时:16
System.out.println("分:" + now.getMinute());//分:48
System.out.println("秒:" + now.getSecond());//秒:14
//4. 时间加减
LocalDateTime time1 = now.plusDays(20);
System.out.println("20 天后是" + time1);//20 天后是2022-06-15T16:52:17.709479300
LocalDateTime time2 = now.minusHours(10);
System.out.println("10个小时前是"+ time2);//10个小时前是2022-05-26T06:52:17.709479300
}
}
9.7.3.3 格式日期类

image-20220526165535502

9.7.4 时间戳

  1. image-20220526165627489

  2. package day04;
    
    import java.time.Instant;
    import java.util.Date;
    
    /**
     * @author 李敏灿
     * @version 1.0
     * 演示时间戳
     */
    
    public class DateTimeDemo4 {
        public static void main(String[] args) {
            //1.时间戳
            Instant now = Instant.now();
            System.out.println(now);
            //2.时间戳转 Date
            Date from = Date.from(now);
            //3.Date 转 Instant 
            Instant instant = from.toInstant();
        }
    }
    
    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
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179



    ## 十,集合

    ### 10.1 引入

    ![image-20220526170846404](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526170846404.png)

    ### 10.2 介绍

    ![image-20220526171005081](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526171005081.png)

    ### 10.3 框架

    1. ![image-20220526171041219](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526171041219.png)
    2. ![image-20220526171125147](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526171125147.png)

    ### 10.4 Collection

    #### 10.4.1 特点

    ![image-20220526171751067](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526171751067.png)

    #### 10.4.2 接口方法

    1. add 添加单个元素
    2. remove 删除指定元素
    3. contains 查找元素是否存在
    4. size 获取元素个数
    5. isEmpty 判断是否为空
    6. clear 清空
    7. addAll 添加多个元素
    8. containsAll 查找多个元素是否存在
    9. removeAll 删除多个元素

    #### 10.4.3 遍历

    ##### 10.4.3.1 迭代器 Iterator

    1. 基本介绍

    ![image-20220526172253496](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526172253496.png)

    2. 执行原理

    ![image-20220526172402666](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526172402666.png)

    3. 快捷键 `itit`。(显示所有快捷键:Ctrl + j)

    ##### 10.4.3.2 增强 for 循环

    ![image-20220526173546544](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526173546544.png)

    ### 10.5 List

    #### 10.5.1 基本介绍

    ![image-20220526174505304](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526174505304.png)

    #### 10.5.2 方法

    1. add 添加
    2. get 获取
    3. remove 移除
    4. indexOf 返回查找元素的位置
    5. lastIndexOf 上面一样不过是最后一个
    6. set 替换
    7. subList 返回某段位置的子集合

    ### 10.6 ArrayList

    1. 注意![image-20220526181246356](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526181246356.png)

    2. 底层源码

    - ![image-20220526181417539](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526181417539.png)

    - 示意图:![image-20220526192804650](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526192804650.png)

    ![image-20220526192824518](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526192824518.png)

    ### 10.7 Vector

    #### 10.7.1 介绍

    ![image-20220526192930493](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220526192930493.png)

    #### 10.7.2 源码

    这里的源码解析过于复杂,略过,后面有时间了再补起来。

    #### 10.7.3 比较

    ArrayList 和 Vector 比较

    ![image-20220527105631312](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527105631312.png)

    ### 10.8 LinkedList

    #### 10.8.1 说明

    ![image-20220527105739738](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527105739738.png)

    #### 10.8.2 底层操作

    ![image-20220527110003876](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527110003876.png)

    #### 10.8.3 源码解析和演示

    难度有点深后面再加

    #### 10.8.4 比较

    ![image-20220527111346938](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527111346938.png)

    ### 10.9 Set

    #### 10.9.1 介绍

    ![image-20220527111908678](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527111908678.png)

    没有索引就不能使用索引来获取

    ### 10.10 HashSet

    #### 10.10.1 介绍

    ![image-20220527112250780](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527112250780.png)

    #### 10.10.2 底层机制

    1. ![image-20220527113140512](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527113140512.png)

    2. ![image-20220527113231272](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527113231272.png)

    3. 以上的源码具体分析有点深,后面再研究吧。这里略过了。

    4. HashSet 扩容机制和转成红黑树机制

    ![image-20220527113729287](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527113729287.png)

    ### 10.11 LinkedHashSet

    #### 10.11.1 介绍

    ![image-20220527113917333](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527113917333.png)

    #### 10.11.2 底层机制

    ![image-20220527114009094](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527114009094.png)

    ### 10.12 Map

    #### 10.12.1 特点

    1. ![image-20220527115218079](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527115218079.png)

    2. 细节讨论:这里说一下,就是第二点。他说全得是引用类型。但是你会发现俺就用基础数据类型好像也没事也能用哈。如下图:

    ```java
    package CollectionsDemo1;

    import java.util.HashMap;

    /**
    * @author 李敏灿
    * @version 1.0
    * 验证 Map 中能不能放基础数据类型
    */

    public class demo01 {
    public static void main(String[] args) {
    HashMap map = new HashMap();
    map.put(1.1,1);
    System.out.println(map.get(1.1));//1

    }
    }
    为什么会有这种情况?难道是烂哥又说错了?不是啊 这个 Map 中真的只能放引用类型哈,这里之所以能过还能打出来。是因为他给你自动包装了:`int -> Integer,double -> Double`。这样就是引用类型了。你以为你用的是基础类型其实人家给你包成了引用类型哈。不信你看下图,我加上泛型指定后问题就暴露出来了。 ![image-20220527120217257](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220527120217257.png) 用基础类型的直接给你报错,换成包装类就好了。故得证 ==Map 中两个都得用引用类型==。
  3. image-20220527120906274

10.12.2 常用方法

  1. put 添加
  2. containKey 查看键是否存在
  3. 其他的重合了,不必多说,这个用的时候直接查就行。

10.12.3 遍历方法

这个有点特殊

  1. image-20220527121940966

一般来说咱都喜欢 2 和 4 .

  1. 代码演示:
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
package CollectionsDemo1;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* @author 李敏灿
* @version 1.0
* Map 遍历演示
*/

public class demo01 {
public static void main(String[] args) {
//准备工作
HashMap<String, Integer> map = new HashMap<>();
map.put("1", 1);
map.put("2", 2);
map.put("3", 3);
map.put("4", 4);
map.put("5", 5);
//开始遍历
//1. 直接用 key
System.out.println("=====key======");
Set<String> strings = map.keySet();
for (String string : strings) {
System.out.print(map.get(string)+" ");
}
System.out.println();
//2. 直接 value
System.out.println("=====value======");
Collection<Integer> values = map.values();
for (Integer value : values) {
System.out.print(value+" ");
}
System.out.println();
//3. 同时拿两个
System.out.println("=====entrySet======");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
System.out.print(entry.getKey()+"-");
System.out.print(entry.getValue()+" ");
}
}
}
  1. 运行截图:image-20220527134646301

10.13 HashMap

10.13.1 介绍

image-20220527152837826

10.13.2 底层

  1. image-20220527152926201

  2. image-20220527153953901

  3. 源码剖析

    这里暂时省略他的底层源码分析和扩容机制

10.14 Hashtable

  1. 介绍image-20220527154145374
  2. 对比image-20220527154410869

10.15 Properties

  1. 介绍image-20220527154446817

10.16 Collections 工具类

10.16.1 介绍

image-20220527155824811

10.16.2 方法

  1. 对于 Listimage-20220527155936656
  2. image-20220527160050304

10.17 开发中怎么选

image-20220527154951579

10.18 TreeMap&TreeSet

  1. TreeSet 好像可以自己传比较器进去让元素有顺序,默认无序
  2. 其他的后面补充

十一,泛型

11.1 背景

比如我们想给 ArrayList 中加一些 dog 对象,并遍历出来。我们会面临两个问题:1. 可能会误加其他类型的对象进去集合;2. 遍历的时候必须进行向下类型,影响效率。泛型就可以解决这个问题。

11.2 好处

image-20220527160807888

11.3 介绍

image-20220527161021178

就是让泛型成为一种数据类型,然后在此类中可以使用。和普通的类型地位相同,至于之歌泛型 E 到底是什么。由实例化时候直接指定。

11.4 语法

  1. 声明image-20220527161519321
  2. 实例化image-20220527161615033

11.5 注意

image-20220527162454738

实际开发中建议前面有后面没有,这是一种简写。

11.6 自定义泛型

  1. image-20220527162928835

    解释一下数组和静态那两条:

    数组初始化需要 new 但是还不确定具体类型是什么,所以只能声明不初始化

    静态这些东西都是类初始化的时候做的,然而泛型的具体确定要等到你实例化那个类,时间逻辑相悖。

  2. image-20220527163829542

11.7 继承和通配符

image-20220527165518983

第一个是不行的哈,泛型没有继承性

十二,多线程

12.1 相关概念

  1. 程序image-20220527171303280
  2. 进程image-20220527171341881
  3. 线程image-20220527171413727
  4. 单线程/多线程image-20220527171559465
  5. 并发/并行image-20220527171635532

12.2 线程

理解:image-20220528095910844

12.2.1 创建

image-20220527171756944

12.2.2 Thread

12.2.2.1 代码演示
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
package day05;

/**
* @author 李敏灿
* @version 1.0
* 演示继承 Thread
*/

public class Thread01 {
public static void main(String[] args) throws InterruptedException {
Cat cat = new Cat();
/*!!!这个下面你会发现 run 也能跑,start 也能跑。区别是:run只是单纯的调用这个类中的方法,还是 main 线程;start 是另外开辟了那个类的单独线程,是另外一个线程。怎么证明?用 run 是执行完这个 run 才会执行下面的。但是 start 这种多线程是两边线程同时交替执行的。*/
//cat.run();
cat.start();
System.out.println("主线程也在执行"+Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("主线程 i= "+i);
Thread.sleep(1000);
}
}
}

//这里这个 Cat 我们让它每隔一秒输出一句 “喵喵喵”
//这次我们用的是 继承 Thread,然后重写她的 run 方法,加上自己的业务逻辑
class Cat extends Thread {
int time = 0;
@Override
public void run() {
do {
try {
System.out.println("喵喵喵,我是小猫咪"+(++time)+"线程名为"+Thread.currentThread().getName());
//下面这个地方是让该线程休眠 1000 毫秒,也就是1 秒
sleep(1000);
//Thread.sleep(1000);
//这里你发现了可以直接 sleep 也可以 Thread.sleep 这个只能在非 main 线程中用,在这里没有 main ,他知道是休眠这个线程
} catch (InterruptedException e) {
e.printStackTrace();
}![image-20220527175539276](JavaSe.assets/image-20220527175539276.png)

}while (time < 10);//我就打印 10 次就退出
}
}
12.2.2.2 分析详解:
  1. sleep:这个在非主线程直接写sleep(毫秒数)是可以的,但是 main 中必须 Thread.sleep(毫秒数)

  2. run&start :这个直接 run() 还是在 main 线程中,只是单纯的调用了那个类中的那个方法。会堵塞在那个 run 方法中,执行完才会继续执行 main 中后面的东西;但是 start 是重新给 那个 Cat 类开了它自己的线程,然后 main 线程照样执行。所以控制台的效果就是他两轮流执行。

    用 run():image-20220527175546974

    用 start() :

    image-20220527175747890

  3. start() 原理是什么?

    start() -> start0() -> run()

    所以它实际最后还是调用了 run() 这个办法,区别就是前面调用了 start0() 这个方法,这个方法是本地方法,是 JVM 调用的,底层用 C/C++ 实现,是它给你开新线程的,开了新线程并不是立马运行的,开了只是给你生成了,准备了将新线程变成了可运行状态,具体什么时候跑起来,取决于 CPU,由这个老哥统一调度。

12.2.3 Runnable

12.2.3.1 说明

image-20220527194318089

也没啥子不同的就是要把那个继承了 Runnable 接口的类的对象用个 Thread 包起来然后再 start

代码:

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
package day06;

/**
* @author 李敏灿
* @version 1.0
* 用 Runnable 接口实现多线程
*/

public class demo1 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog);
thread.start();
System.out.println("你还好吗?朋友");
}
}

class Dog implements Runnable {

@Override
public void run() {
int count = 0;
do {
System.out.println("汪汪汪,俺是修狗"+ (++count));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}while (count < 10);
}
}

12.2.4 两者区别

image-20220528100026514

12.2.5 线程终止

image-20220528100341345

演示:

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
package day06;

/**
* @author 李敏灿
* @version 1.0
* 演示怎么通过通知来终止另外一个线程
*/

public class demo2 {
public static void main(String[] args) throws InterruptedException {
Duck duck = new Duck();
new Thread(duck).start();
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("你嘎什么噶");
if (i == 5){
duck.setSign(false);
}
}
}
}

class Duck implements Runnable {
boolean sign = true;

@Override
public void run() {
do {
System.out.println("嘎嘎嘎嘎,我是小鸭子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (sign);
}
//就是这里可以设置这个线程的循环
public void setSign(boolean sign) {
this.sign = sign;
}
}

12.2.6 常用方法

  1. image-20220528101645768

  2. image-20220528101828035

  3. image-20220528101915754

    演示一下怎么线程插队:

    上代码:

    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 day06;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示线程插队
    */

    public class demo3 {
    public static void main(String[] args) throws InterruptedException {
    Bird bird = new Bird();
    Thread thread1 = new Thread(bird);
    thread1.start();
    for (int i = 0; i < 20; i++) {
    System.out.println("我是 main 不许你嘻嘻哈哈");
    Thread.sleep(1000);
    if (i == 4) {
    thread1.join();
    }
    }
    }
    }

    class Bird implements Runnable {
    @Override
    public void run() {
    for (int i = 0; i < 20; i++) {
    System.out.println("叽叽喳喳哈哈哈哈");
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }
  4. 守护线程

    image-20220528103208143

12.3 生命周期

12.3.1 线程状态

image-20220528103458469

12.3.2 状态转换图

image-20220528103539682

12.4 线程同步

12.4.1 介绍

image-20220528105142062

12.4.2 具体方法

image-20220528105445060

12.4.3 原理

image-20220528105513750

12.4.4 代码演示

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
package day06;

/**
* @author 李敏灿
* @version 1.0
* 演示同步
*/

public class demo4 {
public static void main(String[] args) {

//1. 用接口
SellTicket01 sellTicket01 = new SellTicket01();
new Thread(sellTicket01).start();
new Thread(sellTicket01).start();
new Thread(sellTicket01).start();
//2. 用继承类
new SellTicket02().start();
new SellTicket02().start();
new SellTicket02().start();

}
}


// 用接口
class SellTicket01 implements Runnable {
private int ticketNum = 100;
private boolean loop = true;
Object object = new Object();

@Override
public void run() {
while (loop) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
sell();
}
}

public synchronized void sell() {
if (ticketNum <= 0) {
System.out.println("售票结束。。。");
loop = false;
return;
}

System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数=" + (--ticketNum));

}

}
//用继承类
//这个里面就有些需要用 static
class SellTicket02 extends Thread {
private static int ticketNum = 100;
private boolean loop = true;
static Object object = new Object();

@Override
public void run() {
while (loop) {
//这个休眠放在这里更加符合逻辑,是卖了一个票休息一下对吧
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
sell();
}
}

public void sell() {
synchronized (object) {
//其实还是这样放在里面比较保险
if (ticketNum <= 0) {
System.out.println("售票结束。。。");
loop = false;
return;
}

System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票" + "剩余票数=" + (--ticketNum));

}
}
}

12.5 锁

12.5.1 互斥锁

12.5.1.1 介绍
  1. image-20220528110005539

  2. image-20220528121910223

12.5.2 死锁

image-20220528155924141

12.5.3 释放锁

  1. 释放锁操作:image-20220528160519978
  2. 不会释放锁的操作:image-20220528160556497

十三,IO流

13.1 文件流

image-20220528200239502

13.2 文件操作

13.2.1 创建文件

  1. 概述image-20220528200518228

  2. 代码演示:

  3. package day06;
    
    import java.io.File;
    import java.io.IOException;
    
    /**
     * @author 李敏灿
     * @version 1.0
     * 文件创建
     */
    
    public class demo5 {
        public static void main(String[] args) throws IOException {
            //1. 直接全路径创建路径
            String filepath = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt";
            File file = new File(filepath);
            file.createNewFile();
            //2.父文件+子名字
            File file1 = new File("C:\\Users\\Micenlee李敏灿\\Desktop\\");
            String sonfilepath = "hello1.txt";
            File file2 = new File(file1, sonfilepath);
            file2.createNewFile();
            //3.父目录 + 子目录
            String fatherfilepath = "C:\\Users\\Micenlee李敏灿\\Desktop\\";
            String sonfilepath1 = "hello2.txt";
            File file3 = new File(fatherfilepath, sonfilepath1);
            file3.createNewFile();
        }
    }
    
    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

    成功截图:![image-20220528201900237](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220528201900237.png)

    #### 13.2.2 获取文件信息

    1. 介绍![image-20220528202017865](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220528202017865.png)

    2. 代码演示:

    3. ```java
    package day06;

    import java.io.File;
    import java.io.IOException;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示 文件信息查询
    */

    public class demo6 {
    public static void main(String[] args) throws IOException {
    //先创建文件对象
    File file = new File("C:\\Users\\Micenlee李敏灿\\Desktop\\news1.txt");
    //调用相应的方法, 得到对应信息
    file.createNewFile();
    System.out.println("文件名字=" + file.getName());//文件名字=news1.txt
    System.out.println("文件绝对路径=" + file.getAbsolutePath());//文件绝对路径=C:\Users\Micenlee李敏灿\Desktop\news1.txt
    System.out.println("文件父级目录=" + file.getParent());//文件父级目录=C:\Users\Micenlee李敏灿\Desktop
    System.out.println("文件大小(字节)=" + file.length());//文件大小(字节)=0
    System.out.println("文件是否存在=" + file.exists());//T
    System.out.println("是不是一个文件=" + file.isFile());//T
    System.out.println("是不是一个目录=" + file.isDirectory());//F
    }
    }

13.2.3 目录操作

image-20220528202607363

13.3 IO 流原理及流的分类

13.3.1 Java IO 流原理

image-20220528202745272

13.3.2 流的分类

image-20220528202918644

13.4 IO 流体系图

13.4.1 IO 流体系图

image-20220528203032767

13.4.2 文件 VS 流

image-20220528203123926

13.5 FileInputStream

字节输入流 文件 -> 程序

演示:

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
package day06;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/**
* @author 李敏灿
* @version 1.0
* FileInputStream 字节输入流
*/

public class demo7 {
public static void main(String[] args) throws IOException {
//1. 把文件目录写出来
String filePath = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt";
//2. 建立 FileInputStream
FileInputStream fis = new FileInputStream(filePath);
//做个这个读的效率更高
byte[] readDate = new byte[8];
int readLen = 0;
while ((readLen=fis.read(readDate))!=-1){//节点流都是这个 -1
System.out.print(new String(readDate,0,readLen));
}
//3.关闭资源
fis.close();

}
}

13.6 FileOutputStream

13.6.1 介绍

image-20220528205042612

13.6.2 代码演示

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

import java.io.FileOutputStream;
import java.io.IOException;

/**
* @author 李敏灿
* @version 1.0
* FileotputStream 字节输出流
*/

public class demo8 {
public static void main(String[] args) throws IOException {
String filePath = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt";
//注意这个地方如果没有也是可以的,但是没有默认是覆盖,加上这个是追加写入
FileOutputStream fos = new FileOutputStream(filePath,true);
String inputString = "\n My name is Jack";
fos.write(inputString.getBytes());
fos.close();
}
}

13.7 FileReader&FileWriter

13.7.1 介绍

image-20220528205936214

13.7.2 相关方法

  1. FileReader

    image-20220528210047224

  2. FileWriter

    image-20220528210227005

  3. 注意:这个 FileWriter 写完之后记得刷新

13.7.3 代码演示

这里演示的是利用 FileReader 从一个文件中读取出来然后用 FileWriter 追加带另外一个文件中去

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 day06;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
* @author 李敏灿
* @version 1.0
* FileReader&FileWriter
*/

public class demo9 {
public static void main(String[] args) throws IOException {
//1. 把两个流做起来
FileReader fr = new FileReader("C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt");
FileWriter fw = new FileWriter("C:\\Users\\Micenlee李敏灿\\Desktop\\hello1.txt",true);
char[] readArray = new char[8];
int readLen = 0;
while ((readLen = fr.read(readArray)) != -1) {
//2.这里是读了一点就去写一点
fw.write(readArray,0,readLen);
fw.flush();
}
//3.关闭相关流
fw.close();
fr.close();

}
}

13.8 节点流和处理流

13.8.1 介绍

image-20220528211844875

13.8.2 一览表

image-20220528211954764

13.8.3 区别与联系

image-20220528212428436

13.8.4 处理流功能

image-20220528212505175

13.8.5 处理流

13.8.5.1 BufferedReader&BufferedWriter

image-20220528212649184

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package day06;

import java.io.*;

/**
* @author 李敏灿
* @version 1.0
* Bufferedreader&BufferesWriter
* 来完成一个文章拷贝
*/

public class demo10 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\Micenlee李敏灿\\Desktop\\hello1.txt", true));
String readLine = null;
while ((readLine = br.readLine())!= null){
bw.write(readLine);
bw.newLine();//给换个行鸭
}
bw.close();
br.close();
}
}
13.8.5.2 BufferInputStream&BufferOutputStream
  1. 介绍: image-20220528214534414

    image-20220528214603263

    可以拿这两个便捷的拷贝音视频文件

  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
    package day07;

    import java.io.*;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示用处理流拷贝音视频文件
    */

    public class demo1 {
    public static void main(String[] args) {
    //1. 把路径,流准备好
    String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
    String toPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\BBB.mp4";
    //2. 提前声明流,方便 finally 中关闭
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    byte[] readDate = new byte[1024];
    int readLen = 0;
    try {
    //3. 流的初始化
    bis = new BufferedInputStream(new FileInputStream(fromPath));
    bos = new BufferedOutputStream(new FileOutputStream(toPath));
    while ((readLen = bis.read(readDate))!= -1){
    bos.write(readDate,0,readLen);
    bos.flush();
    }

    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    if (bos != null){
    try {
    bos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    if (bis!=null){
    try {
    bis.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

13.8.6 对象流

13.8.6.1 序列化和反序列化

image-20220529134829773

image-20220529134855593

13.8.6.2 对象流

image-20220529135039975

13.8.6.3 注意

image-20220529135331904

13.8.6.4 代码演示
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
package day07;

import java.io.*;

/**
* @author 李敏灿
* @version 1.0
* 序列化和反序列化
*/

public class demo3 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1. 准备工作
Dog dog = new Dog(19, "李敏烂");
String path = "C:\\Users\\Micenlee李敏灿\\Desktop\\aa.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(path));
//2.开写
oos.writeObject(dog);
oos.writeUTF("hello");
oos.writeInt(12);
oos.writeBoolean(true);
oos.flush();//知道这里为什么要加个 flush 不?
System.out.println(ois.readObject());//Dog{age=19, name='李敏烂'}

System.out.println(ois.readUTF());//hello
System.out.println(ois.readInt());//12
System.out.println(ois.readBoolean());//true
//3.记得关闭流
ois.close();
oos.close();

}
}

class Dog implements Serializable {
private int age;
private String name;

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

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

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

23行为什么要加一个 flush 的解释:

如果不加他就会报 EOFException image-20220529154840867

这个的原因还是上一篇说的 flush 原理,因为 Object 对象流还是处理流,有哪个缓存区机制,如果 23 行没有那个 flush 。你是 write 了然后就 read ,可能有东西还在缓存区没写进去,你就去读。不就是读到异常结尾了,就给你蹦出来一个 EOFException。如果你 write 完了,及时的 flush 了,或者把那个 close 提到你 read 之前他就解决了。

13.8.7 标准输入输出流

13.8.7.1 介绍

image-20220529155744689

13.8.8 转换流

13.8.8.1 背景

一般来说 Java 中默认读取文件字符是按 UTF8 来读的,当文件是这种类型的一点问题没有。但是如果读取的文件是 gbk 或者其他格式,再按默认的去读就会出乱码。如下图:

image-20220529161610906

image-20220529161640355

解决办法,用字节流读取可以指定读取的格式,然后用字符包装类可以高效读取。那用转换类中间弄一下就可以又解决中文乱码问题又可以高效读取了。这就是转换流存在的意义。

13.8.8.2 介绍

image-20220529162058244

13.8.8.3 代码演示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package day07;

import java.io.*;

/**
* @author 李敏灿
* @version 1.0
* 转换流
*/

public class demo5 {
public static void main(String[] args) throws IOException {
String path = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello1.txt";
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(path),"gbk"));
System.out.println(br.readLine());
br.close();

}
}

结果图:image-20220529162339134

13.8.9 打印流

  1. 介绍

    打印只有输出流,没有输入流。就 PrintStream 字节打印流和 PrintWriter 字符打印流。image-20220529162934483

  2. 代码演示:

    两个差不多,唯一的区别就是字节和字符。这里我们用字节打印流演示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package day07;

    import java.io.FileNotFoundException;
    import java.io.PrintStream;

    /**
    * @author 李敏灿
    * @version 1.0
    * 字节打印流
    * PrintStream
    */

    public class demo6 {
    public static void main(String[] args) throws FileNotFoundException {
    String path = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt";
    PrintStream printStream = new PrintStream(path);
    printStream.print("你还好吗朋友");//这里是向桌面的 hello.txt 输入
    printStream = System.out;//这个改变了字节打印流,成了向控制台输出
    System.out.println("我很好,谢谢你");

    printStream.close();//记得关闭刷新,不然写不进去哦
    }
    }

    结果图:image-20220529164913960

13.9 Propoties

13.9.1 引入

image-20220529170020002

13.9.2 介绍

image-20220529170107101

13.9.3 代码演示

  1. 写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    package day07;

    import java.io.FileWriter;
    import java.io.IOException;
    import java.util.Properties;

    /**
    * @author 李敏灿
    * @version 1.0
    * 写 Properties 配置文件
    */

    public class demo7 {
    public static void main(String[] args) throws IOException {
    String path = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.properties";
    Properties properties = new Properties();
    properties.setProperty("age","19");
    properties.setProperty("name","李敏烂");
    properties.setProperty("height","187");
    properties.store(new FileWriter(path),"我演示给大家看的一个实验");

    }
    }

    结果图:image-20220529171623014

  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
    package day07;

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Properties;

    /**
    * @author 李敏灿
    * @version 1.0
    * 读 properties
    */

    public class demo8 {
    public static void main(String[] args) throws IOException {
    String path = "C:\\Users\\Micenlee李敏灿\\Desktop\\hello.properties";
    Properties properties = new Properties();
    properties.load(new FileReader(path));
    properties.list(System.out);
    System.out.println(properties.getProperty("age"));
    System.out.println(properties.getProperty("name"));
    System.out.println(properties.getProperty("height"));

    }
    }

    结果图:image-20220529172049355

13.10 flush 详解

13.10.1 背景

我有个问题:

就是 Java 中 IO 流中的那个 flush 是干嘛的?原理是什么?什么时候用?什么时候可以不用?

13.10.2 原理

  1. 流的 writer 原理:Java 中 文件直接通过流 (Stream) 的方式传递,但是不同的流传递的方式还是有点不同的。比如现在通过 Java 程序去给桌面一个文件写东西进去:如果是节点流,他们 write 是直接淦目标中了;但是包装流 (Buffer 流) 他们就有个缓冲区,设计的初衷是提高读写效率,他们 write 先是写在缓冲区,满了才给写目标文件中去。

  2. flush 原理:这个 flush 是干啥的呢?就是如果我们直接调用它,他就会不管缓存区有没有满,都给他淦出缓存区,强行冲一遍。注意啊,这个赶出缓存不是直接写到物理地址硬盘上,而是赶出去让操作系统来写到文件上去。这个 flush 还是个空方法。

    下面是 flush 的源码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * Flushes this output stream and forces any buffered output bytes
    * to be written out. The general contract of <code>flush</code> is
    * that calling it is an indication that, if any bytes previously
    * written have been buffered by the implementation of the output
    * stream, such bytes should immediately be written to their
    * intended destination.
    * <p>
    * If the intended destination of this stream is an abstraction provided by
    * the underlying operating system, for example a file, then flushing the
    * stream guarantees only that bytes previously written to the stream are
    * passed to the operating system for writing; it does not guarantee that
    * they are actually written to a physical device such as a disk drive.
    * <p>
    * The <code>flush</code> method of <code>OutputStream</code> does nothing.
    * 中文翻译:
    刷新此输出流并强制写出任何缓冲的输出字节。 <code>flush<code> 的一般约定是调用它表示,如果先前写入的任何字节已被输出 流的实现缓冲,则应立即将这些字节写入其预期目的地。 <p> 如果此流的预期目的地是底层操作系统提供的抽象,例如文件,则刷新 流仅保证先前写入流的字节被传递给操作系统进行写入;它不能保证它们实际上被写入了物理设备,例如磁盘驱动器。 <p> <code>OutputStream<code> 的 <code>flush<code> 方法什么也不做。
    * @exception IOException if an I/O error occurs.
    */
    public void flush() throws IOException {
    }
  3. 缓冲流的 close 原理:它会先用一下 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
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    /**
    * Closes this output stream and releases any system resources
    * associated with the stream.
    * <p>
    * When not already closed, the {@code close} method of {@code
    * FilterOutputStream} calls its {@code flush} method, and then
    * calls the {@code close} method of its underlying output stream.
    *
    *中文翻译:
    关闭此输出流并释放与该流关联的所有系统资源。 <p> 当尚未关闭时,{@code FilterOutputStream} 的 {@code close} 方 法调用其 {@code flush} 方法,然后调用其底层输出流的 {@code close} 方法
    * @exception IOException if an I/O error occurs.
    * @see java.io.FilterOutputStream#flush()
    * @see java.io.FilterOutputStream#out
    */
    @Override
    public void close() throws IOException {
    if (closed) {
    return;
    }
    synchronized (closeLock) {
    if (closed) {
    return;
    }
    closed = true;
    }

    Throwable flushException = null;
    try {
    flush();
    } catch (Throwable e) {
    flushException = e;
    throw e;
    } finally {
    if (flushException == null) {
    out.close();
    } else {
    try {
    out.close();
    } catch (Throwable closeException) {
    // evaluate possible precedence of flushException over closeException
    if ((flushException instanceof ThreadDeath) &&
    !(closeException instanceof ThreadDeath)) {
    flushException.addSuppressed(closeException);
    throw (ThreadDeath) flushException;
    }

    if (flushException != closeException) {
    closeException.addSuppressed(flushException);
    }

    throw closeException;
    }
    }
    }
    }

13.10.3 使用场景

Java 中有缓冲区的是 BufferedOutput 流的那些,所以在这些流每次 write 后如果想立马把缓存区的东西写到文件,以防丢失就可以用一下这个

  1. 其实现实开发中,如果暂时不能 close ,那就每 write 一下就 flush 一下
  2. 如果一定会 close 流(比如写在 finally 中),就可以不写 flush。因为它 close 前给你 flush 了。

13.10.4 错误使用演示

你要是不肯定会 close 也不 write 后就是 flush ,你写的数据就会丢失一小部分。

演示:

  1. 不 close 也不及时 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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package day07;

import java.io.*;

/**
* @author 李敏灿
* @version 1.0
* 演示用处理流拷贝音视频文件
*/

public class demo1 {
public static void main(String[] args) {
//1. 把路径,流准备好
String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
String toPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\BBB.mp4";
//2. 提前声明流,方便 finally 中关闭
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
byte[] readDate = new byte[1024];
int readLen = 0;
try {
//3. 流的初始化
bis = new BufferedInputStream(new FileInputStream(fromPath));
bos = new BufferedOutputStream(new FileOutputStream(toPath),1024);
while ((readLen = bis.read(readDate))!= -1){
bos.write(readDate,0,readLen);
//bos.flush();
}

} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// if (bos != null){
// try {
// bos.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

结果图:

拷是拷成功了image-20220529114924889

但是你打开:

image-20220529115007246

  1. 只及时 flush 不 close -> 可以

    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 day07;

    import java.io.*;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示用处理流拷贝音视频文件
    */

    public class demo1 {
    public static void main(String[] args) {
    //1. 把路径,流准备好
    String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
    String toPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\BBB.mp4";
    //2. 提前声明流,方便 finally 中关闭
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    byte[] readDate = new byte[1024];
    int readLen = 0;
    try {
    //3. 流的初始化
    bis = new BufferedInputStream(new FileInputStream(fromPath));
    bos = new BufferedOutputStream(new FileOutputStream(toPath));
    while ((readLen = bis.read(readDate))!= -1){
    bos.write(readDate,0,readLen);
    bos.flush();
    }

    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    // if (bos != null){
    // try {
    // bos.close();
    // } catch (IOException e) {
    // e.printStackTrace();
    // }
    // }
    if (bis!=null){
    try {
    bis.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

    结果图:

    拷贝成功

    image-20220529115358943

    打开拷贝视频可以播放,没有损坏

    image-20220529115443818

  2. 不及时 flush 但最后一定 close 了 -> 可以的

    代码:

    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 day07;

    import java.io.*;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示用处理流拷贝音视频文件
    */

    public class demo1 {
    public static void main(String[] args) {
    //1. 把路径,流准备好
    String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
    String toPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\BBB.mp4";
    //2. 提前声明流,方便 finally 中关闭
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    byte[] readDate = new byte[1024];
    int readLen = 0;
    try {
    //3. 流的初始化
    bis = new BufferedInputStream(new FileInputStream(fromPath));
    bos = new BufferedOutputStream(new FileOutputStream(toPath));
    while ((readLen = bis.read(readDate))!= -1){
    bos.write(readDate,0,readLen);
    //bos.flush();
    }

    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    if (bos != null){
    try {
    bos.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    if (bis!=null){
    try {
    bis.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
    }
    }

    拷贝成功图:image-20220529115629945

    打开能播放图:image-20220529115651105

13.10.5 总结

所以大家在用 处理流写东西时,如果一定能 close 流,那没啥事。但是如果不能关,记得 write 一下 flush 一下,不然数据会丢失。

13.10.6 非要做

比如现在我们非要既不及时 flush 也不 close 还想写到文件怎么办?

  1. 把缓存区大小改为数据大小

    代码:

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

    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;

    public class demo2 {
    public static void main(String[] args) throws Exception {
    File file = new File("C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt");
    if (!file.exists()) {
    file.createNewFile();
    }
    FileOutputStream fos = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(fos,1000);//这里我把缓存区大小设置为 1 kb
    byte[] b = new byte[1024];
    bos.write(b);//这里我们写入 1 kb 的数据
    //bos.flush();
    }
    }

    结果图:是成功的,大小为我们刚写的 1 KBimage-20220529133850637

    这里为啥大小和占用空间不一样鸭?为什么占用空间是 4 KB。因为硬盘最小是以 4 K 为分配单位的。大小和占用空间不一样是很正常的,硬盘分区的最小是以4k为计算单位的。 就是说好像一个一个格子一样,这些格子比较大。 即使是一个文件写了一个字,它的大小不足1k,但是显示占用空间为4k。 所以特别是一些小文件很多的东西。

  2. 写入文件大于缓存区默认大小(8 KB)

    代码:

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

    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;

    public class demo2 {
    public static void main(String[] args) throws Exception {
    File file = new File("C:\\Users\\Micenlee李敏灿\\Desktop\\hello.txt");
    if (!file.exists()) {
    file.createNewFile();
    }
    FileOutputStream fos = new FileOutputStream(file);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    byte[] b = new byte[1024*8];
    bos.write(b);//这里我们写入 1 kb 的数据
    //bos.flush();
    }
    }

    结果图:大小为 8 KB

    image-20220529134413803

  3. 大家思考:那在音视频我们能不能这样呢?为什么能或者不能?

    答案:不能,会有丢失。原因俺也不知道,希望大家在评论区教我。

13.11 怎么关这种匿名流

题目描述:image-20220530084852822

  1. 在方法内部关闭。这样可以在调用这个办法结束就关闭这个匿名流,匿名流只能用一次。

    image-20220530090601609

  2. 依靠 full-gc 给你关。就是 Java 的垃圾自动装置,会在 gc 的时候给你关闭那些没有用过的流。一定程度上如果胆子大,可以就不关然后等着 JVM 给你调 full-gc 给你关。

  3. 用新版的 try-with-resouce 别老整这些匿名流。这个是最新的也是最优雅的关流的办法。

    演示代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package day07;

    import Utils.StreamUtils;

    import java.io.*;

    /**
    * @author 李敏灿
    * @version 1.0
    */
    @SuppressWarnings({"all"})
    public class demo14 {
    public static void main(String[] args) {
    String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
    try (
    BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(fromPath))
    //这里写流
    ) {
    //操作代码块
    } catch (IOException e) {
    }

    }
    }

    IDEA中在 new 资源流的时候后面加个.twr就可以出来了image-20220530091212144

    凡是在 try 后面小括号中的流,只要实现了 Closeable 接口的,都可以这样关,省心又方便。

  4. 《Effective Java》在第三版中也推荐使用try-with-resources语句替代try-finally语句。

    所以在处理必须关闭的资源时,使用try-with-resources语句替代try-finally语句。生成的代码更简洁,更清晰,并且生成的异常更有用。 try-with-resources语句在编写必须关闭资源的代码时会更容易,也不会出错,而使用try-finally语句实际上是不可能的。

    如此,推荐大家使用try-with-resources优雅地关闭资源!

十四,网络编程

14.1 网络

14.1.1 网络通信

image-20220529192446777

14.1.2 网络

image-20220529192539053

14.1.3 ip 地址

image-20220529192645291

14.1.4 域名

image-20220529192827564

14.1.5 网络通信协议

  1. image-20220529193050329
  2. 借用下我老师的图:image-20220529193240070

14.1.6 TCP 和 UDP

image-20220529193444268

14.2 InetAddress 类

14.2.1 方法

image-20220529193849936

代码演示:

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 day07;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
* @author 李敏灿
* @version 1.0
* 演示 InetAddress
*/

public class demo9 {
public static void main(String[] args) throws UnknownHostException {
//1. 获取计算机对象
//1) 直接获取本机
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);
//2)获取百度
InetAddress name = InetAddress.getByName("www.baidu.com");
System.out.println(name);
//3)通过名字获取
System.out.println(InetAddress.getByName("Mecenlee"));
//2.获取计算机名
String hostName = localHost.getHostName();
System.out.println(hostName);
//3.获取计算机 IP
String hostAddress = localHost.getHostAddress();
System.out.println(hostAddress);
}
}

14.3 Socket

14.3.1 介绍

image-20220529195603795

14.3.2 示意图

image-20220529195624603

14.4 TCP 网络通信编程

14.4.1 介绍

image-20220529195909850

14.4.2 代码演示

  1. 利用字节流来发条消息

    服务端:

    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
    package day07;

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

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示 TCP 网络通信
    * 服务端
    */
    /*
    介绍:
    用字节流编写一个服务端,一个客户端。服务端在 9999 端口监听
    客户端连接到服务器端,发送消息,然后退出。服务器接收到打印在控制台然后退出。
    */

    public class demo10 {
    public static void main(String[] args) throws IOException {
    //1.开服务端,开接口监听,形成一条数据通道
    ServerSocket serverSocket = new ServerSocket(9999);
    System.out.println("我是服务端,在 9999 端口监听,等待连接。。。");
    //2.接受客户端
    Socket socket = serverSocket.accept();
    System.out.println("服务端 socket ="+ socket.getClass());
    //3.读取客户端写入到数据通道的数据
    InputStream inputStream = socket.getInputStream();
    //4.IO 读取
    byte[] buf = new byte[1024];
    int readLen = 0;
    while ((readLen = inputStream.read(buf))!= -1){
    System.out.println(new String(buf,0,readLen));
    }
    //5.关闭所有流
    inputStream.close();
    socket.close();
    serverSocket.close();

    }
    }

    客户端:

    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
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.net.UnknownHostException;

    /**
    * @author 李敏灿
    * @version 1.0
    * 客户端
    */

    public class demo11 {
    public static void main(String[] args) throws IOException {
    //1. 连接服务端 (IP,端口)
    Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
    System.out.println("客户端 socket 返回"+socket.getClass());
    //2.连接上,得到对应的输出写入流
    OutputStream outputStream = socket.getOutputStream();
    //3.通过输出流,写数据到 数据通道
    outputStream.write("hello,server".getBytes());
    //4.关闭流
    socket.shutdownOutput();
    outputStream.close();
    socket.close();
    System.out.println("客户端退出");
    }
    }

    结果图:image-20220529203803639

    image-20220529203818623

  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
    package Utils;

    import java.io.*;

    /**
    * @author 李敏灿
    * @version 1.0
    * IO 流的工具类
    */

    public class StreamUtils {
    /**
    * 将输入流转换成 byte[]
    */
    public static byte[] streamToByteArray(InputStream is) throws IOException {
    //1.创建输出流对象
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int readLen = 0;
    while ((readLen = is.read(buf))!=-1){
    bos.write(buf,0,readLen);
    }
    byte[] array = bos.toByteArray();
    bos.close();
    return array;
    }
    /**
    * 将 InputStream 转换为 String
    */
    public static String streamToString(InputStream is) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder stringBuilder = new StringBuilder();
    String line;
    while ((line = reader.readLine())!=null){
    stringBuilder.append(line+"\r\n");
    }
    return stringBuilder.toString();
    }
    }

    服务端:

    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
    package day07;

    import Utils.StreamUtils;

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

    /**
    * @author 李敏灿
    * @version 1.0
    * 传图片对话网络编程
    * 服务端
    */

    public class demo12 {
    public static void main(String[] args) throws IOException {
    //1. 服务端在本机监听 8888 端口
    ServerSocket serverSocket = new ServerSocket(8888);
    System.out.println("服务端在 8888 端口监听。。。");
    //2.等待连接
    Socket socket = serverSocket.accept();
    //3.读取用户发送的数据
    BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
    byte[] bytes = StreamUtils.streamToByteArray(bis);
    //4.将得到的 byte 数组,写到指定路径
    String destPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\ccc.mp4";
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destPath));
    bos.write(bytes);
    bos.flush();
    //==========向客户端回复收到消息
    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    bw.write("收到视频,谢谢你");
    bw.flush();
    socket.shutdownOutput();//这个是设置写入结束标记.网络编程必加
    //关闭其他资源
    bw.close();
    bos.close();
    bis.close();
    socket.close();
    serverSocket.close();
    }
    }

    客户端:

    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
    package day07;

    import Utils.StreamUtils;

    import java.io.*;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.net.UnknownHostException;

    /**
    * @author 李敏灿
    * @version 1.0
    * 传图片对话网络编程
    * 客户端
    */

    public class demo13 {
    public static void main(String[] args) throws IOException {
    //1. 连接服务端
    Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
    //2.将文件读到 Java 程序
    String fromPath = "C:\\Users\\Micenlee李敏灿\\Desktop\\AAA.mp4";
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fromPath));
    byte[] bytes = StreamUtils.streamToByteArray(bis);
    //3.将数据打到通道中
    BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
    bos.write(bytes);
    bos.flush();
    socket.shutdownOutput();

    //====接受服务端的消息
    InputStream is = socket.getInputStream();
    String string = StreamUtils.streamToString(is);
    System.out.println(string);

    //关闭所有流
    is.close();
    bos.close();
    bis.close();
    socket.close();
    }
    }

    结果图:image-20220529211909390image-20220529211938632

14.4.3 总结

14.4.3.1 服务端写法
  1. 服务器开端口:

    ServerSocket serverSocket = new ServerSocket(端口);

  2. 等待连接:

    Socket socket = serverSocket.accept();

  3. 获取对应的输出/输入流:

    socket.getInputStream()

    socket.getOutputStream()

  4. 具体的 IO 流操作:

    记得如果是输出记得加上socket.shutdownOutput()

  5. 最后记得关闭所有流

14.4.3.2 客户端写法
  1. 连接服务端:

    Socket socket = new Socket(InetAddress.getLocalHost(), 8888)

  2. 还是获取对应的流

  3. 具体操作,还是如果是 write 记得 socket.shutdownOutput()

  4. 关闭流

14.5 netstat 指令

image-20220530094724279

14.6 UDP 网络通信编程

14.6.1 介绍

image-20220530095150606

14.6.2 流程

image-20220530095323214

14.6.3 代码

问题:image-20220530095420073

代码:

接收端:

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
package day08;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

/**
* @author 李敏灿
* @version 1.0
* UDP 接收端
*/

public class UDPReceiverA {
public static void main(String[] args) {
try (
//1.创建一个 DatagramSocket 对象,准备在 9999 接受数据
DatagramSocket socket = new DatagramSocket(9999)
) {
//2.构建一个 DatagramPacket 对象,用来接受数据
byte[] buf = new byte[1024];
DatagramPacket packet = new DatagramPacket(buf, buf.length);
//3.调用接受方法,将通过网络传输的 DatagramPacket 对象接收到本地
System.out.println("接收端 A 在等待接受数据~");
socket.receive(packet);
//4.将 packet 进行拆包,取出数据并显示
int length = packet.getLength();
byte[] data = packet.getData();
System.out.println(new String(data,0,length));

//=====回消息给 B 端============

//1.将需要发送的数据,封装到 DatagramPacket 对象
data = "好的,明天不见不散".getBytes();
packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.137.1"),9998);
socket.send(packet);//发送
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("A 端退出~");
}
}

发送端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package day08;

import java.io.IOException;
import java.net.*;

/**
* @author 李敏灿
* @version 1.0
* UDP 发送端
*/

public class UDPSenderB {
public static <date> void main(String[] args) {
//
try (
//1. 创建 DatagramSocket 对象,准备在 9998 端口 接受数据
DatagramSocket socket = new DatagramSocket(9998)
) {
//2. 将需要发送的消息封装到 DatagramPacket 对象
byte[] date = "hello,明天吃火锅~".getBytes();
DatagramPacket packet = new DatagramPacket(date, date.length, InetAddress.getByName("192.168.137.1"), 9999);
//3.发送
socket.send(packet);

//=======接受从 A 端发回来的消息
//1.接收
socket.receive(packet);
//2.取出数据
int length = packet.getLength();
date = packet.getData();
System.out.println(new String(date,0,length));
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("B 端退出");
}
}

结果图:image-20220530103524488image-20220530103530907

十五,反射

15.1 介绍

image-20220530110123373

15.2 机制原理图

image-20220530110342987

15.3 反射用途

image-20220530110421388

15.4 主要相关类

image-20220530135820807

15.5 代码入门

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
package day08;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

/**
* @author 李敏灿
* @version 1.0
* 演示基本反射
*/

public class Reflectiion01 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//1.准备 Properties 文件
Properties properties = new Properties();
properties.setProperty("classfullpath","day08.Cat");
properties.setProperty("method","say");
properties.store(new OutputStreamWriter(new FileOutputStream("src\\re.properties")),"为反射准备的");
//2.读取配置文件
String classfullpath = properties.getProperty("classfullpath");
String method = properties.getProperty("method");
//2.用反射拿到 类对象
Class aClass = Class.forName(classfullpath);
//实例化一个对象
Object o = aClass.newInstance();
System.out.println("o 的运行类型="+ o.getClass());
//实例化了一个方法
Method method1 = aClass.getMethod(method);
method1.invoke(o);
//尝试实例化构造器
Constructor<?> constructor = aClass.getConstructor();
System.out.println(constructor);//无参
Constructor<?> constructor1 = aClass.getConstructor(String.class);
System.out.println(constructor1);
}
}

15.6 优缺点

image-20220530135942496

15.7 反射性能优化

image-20220530140244231

image-20220530140302357

代码演示:

1
2
3
Method method1 = aClass.getMethod(method);
method1.setAccessible(true);//我就是优化
method1.invoke(o);

15.8 Class 类

15.8.1 介绍

image-20220530140851658

15.8.2 常用方法

image-20220530141257597

15.8.3 获取 Class 对象

image-20220530142416586

15.8.4 那些类型有 Class 对象

image-20220530142925622

15.9 类加载

15.9.1 说明

image-20220530143143759

15.9.2 类加载时机

image-20220530143333363

15.9.3 类加载过程图

image-20220530143430325

15.9.4 类加载各个阶段完成任务

image-20220530143652839

15.9.5 加载阶段

image-20220530143753525

15.9.6 连接阶段-验证

image-20220530143838082

15.9.7 连接阶段-准备

image-20220530143937076

解释:image-20220530144016874

15.9.8 连接阶段-解析

image-20220530144100984

15.9.10 初始化

image-20220530144157232

15.10 通过反射获取类的结构信息

15.10.1 Java.lang.Class

image-20220530145151346

getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。

15.10.2 Java.lang.reflect.Field

image-20220530145221479

15.10.3 Java.lang.reflect.Method

image-20220530145257775

15.10.4 Java.lang.reflect.Constructor

image-20220530145336497

15.11 通过反射创建对象

  1. image-20220530145840563

  2. 代码:

  3. package day08;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    /**
     * @author 李敏灿
     * @version 1.0
     * 通过反射构建实例
     */
    
    public class Reflection02 {
        public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            //1. 获取到 User 类的 Class 对象
            Class<?> aClass = Class.forName("day08.User");
            //2.用 public 无参构造器
            Object o = aClass.newInstance();
            System.out.println(o);
            //3.用 public 有参构造器
            Constructor<?> constructor = aClass.getConstructor(String.class);
            Object o1 = constructor.newInstance("李敏烂");
            System.out.println(o1);
            //4.通过 private 构造器
            Constructor<?> constructor1 = aClass.getDeclaredConstructor(int.class, String.class);
            constructor1.setAccessible(true);//爆破,将私有的限制突破。没有这个会报 IllegalAccessException
            Object o2 = constructor1.newInstance(19, "烂烂");
            System.out.println(o2);
        }
    }
    class User{
        private int age = 10;
        private String name = "Mecenlee";
    
        public User() {
        }
    
        public User(String name) {
            this.name = name;
        }
    
        private User(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    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

    结果图:![image-20220530152047467](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220530152047467.png)

    ### 15.12 通过反射访问类中的成员

    #### 15.12.1 访问属性

    1. 介绍![image-20220530152146899](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220530152146899.png)

    2. 代码演示:

    3. ```java
    package day08;

    import java.lang.reflect.Field;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示反射怎么操作属性
    */

    public class Reflection03 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
    //1. 得到 Student 对应的 Class 对象
    Class<?> aClass = Class.forName("day08.Student");
    //2. 得到实例对象
    Object o = aClass.newInstance();
    System.out.println(o.getClass());
    //3.使用反射得到 age 属性
    Field age = aClass.getField("age");
    age.set(o,99);
    System.out.println(age.get(o));
    //4.使用反射操作 name 属性
    Field name = aClass.getDeclaredField("name");
    name.setAccessible(true);
    name.set(null,"Jack");
    //System.out.println(name.get(o));
    System.out.println(name.get(null));

    }
    }
    class Student{
    public int age;
    private static String name;

    public Student() {
    }

    @Override
    public String toString() {
    return "Student{" +
    "age=" + age +
    '}';
    }
    }
    结果图:![image-20220530154241721](https://article-picture-resource.oss-cn-chengdu.aliyuncs.com/imags/image-20220530154241721.png)

15.12.2 访问方法

  1. 介绍image-20220530154319482

  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
    package day08;

    import com.sun.tools.javac.Main;

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示反射怎么访问方法
    */

    public class Reflection04 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    //1.获取 类对象
    Class<?> aClass = Class.forName("day08.Boss");
    //2.创建一个对象
    Object o = aClass.newInstance();
    //3.调用 public 的 hi 方法
    Method hi = aClass.getMethod("hi", String.class);
    hi.invoke(o, "HL");
    //4.调用 private 的 say 方法
    Method say = aClass.getDeclaredMethod("say", int.class, String.class, char.class);
    say.setAccessible(true);//直接上爆破
    System.out.println(say.invoke(null, 0, "lmc", '男'));//0lmc男

    //5.在反射中如果方法有返回值,统一返回 Object 。但是他的运行类型和方法定义的返回类型一致
    Method m1 = aClass.getMethod("m1");
    Object invoke = m1.invoke(o);
    System.out.println("invoke 的运行类型=" + invoke.getClass());//invoke 的运行类型=class day08.Monster


    }
    }

    class Monster {
    }

    class Boss {
    public int age;
    private static String name;

    public Boss() {
    }

    public Monster m1() {
    return new Monster();
    }

    //静态方法
    private static String say(int n, String s, char c) {
    return n + "" + s + "" + c;
    }

    //普通方法
    public void hi(String c) {
    System.out.println("hi~" + c);
    }
    }

十六,正则表达式

16.1 介绍

  1. 正则表达式是处理文本的利器
  2. 说直白一点就是,正则表达式就是对字符串值执行模式匹配的技术
  3. image-20220530162139897

16.2 底层

主要就是一个 find 和 group 源码的分析问题

代码:

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
package day10;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author 李敏灿
* @version 1.0
*/

public class RegThre {
public static void main(String[] args) {
//1.准备筛选内容和正则模板
String content = "1998 年的第7898场雪meiyou8909";
String regStr = "(\\d{2})(\\d{2})";
//2.实例化一个正则表达式对象
Pattern pattern = Pattern.compile(regStr);
//3.依次做个匹配器
Matcher matcher = pattern.matcher(content);
//4.开始工作
while (matcher.find()) {
/**
* find 方法解释
* 1. 根据指定的规则 ,定位满足规则的子字符串(比如(19)(98))
* 2. 找到后, 将 子字符串的开始的索引记录到 matcher 对象的属性 int[] groups;
* 2.1 groups[0] = 0 , 把该子字符串的结束的索引+1 的值记录到 groups[1] = 4
* 2.2 记录 1 组()匹配到的字符串 groups[2] = 0 groups[3] = 2
* 2.3 记录 2 组()匹配到的字符串 groups[4] = 2 groups[5] = 4
* 2.4.如果有更多的分组.....
* 3. 同时记录 oldLast 的值为 子字符串的结束的 索引+1 的值即 4, 即下次执行 find 时, 就从 4 开始匹
* 配
*/
System.out.println("找到什么:" + matcher.group(0));
System.out.println("第一个()中是什么:" + matcher.group(1));
System.out.println("第二个()中是什么:" + matcher.group(2));
/**
* group 方法解释:
* 源码:
* public String group(int group) {
* if (first < 0)
* throw new IllegalStateException("No match found");
* if (group < 0 || group > groupCount())
* throw new IndexOutOfBoundsException("No group " + group);
* if ((groups[group*2] == -1) || (groups[group*2+1] == -1))
* return null;
* 关键的是下面这句
* return getSubSequence(groups[group * 2], groups[group * 2 + 1]).toString();
* }
* 传入不同的值每次取出来就是不同组里面的成员,如果
* 0 -> 完整版找到的
* 1 -> 第一个括号中的
* 2 -> 第二个括号中的
* 联系上一个 find 原理正好可以解释这个
*/
}
}
}

结果图:image-20220601155500381

16.3 语法

16.3.1 介绍

image-20220530162736857

16.3.2 元字符

16.3.2.1 转义号 \\

image-20220530162945704

image-20220530163028610

16.3.2.2 字符匹配符

image-20220530163213224image-20220530163236526

16.3.2.3 选择匹配符

image-20220530163715664

16.3.2.4 限定符

image-20220530163759729

16.3.2.4 定位符

image-20220530164158540

16.3.2.5 分组
16.3.2.5.1 一览表
image-20220530164240124image-20220530164250376
16.3.2.5.2 代码演示
  1. 命名捕获(第二个):

代码:

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
package day10;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author 李敏灿
* @version 1.0
* 捕获分名字
*/

public class demo1 {
public static void main(String[] args) {
String content = "李敏猫1290李敏烂5545李敏狗2342";
//演示(?<name>pattern)
String regStr2 = "(?<g1>\\d{2})(?<g2>\\d{2})";//12 90 55 45 23 42
//2.实例化一个正则表达式对象
Pattern pattern = Pattern.compile(regStr2);
//3.依次做个匹配器
Matcher matcher = pattern.matcher(content);
//4.开始工作
while (matcher.find()) {
System.out.println("找到什么:" + matcher.group(0));
System.out.println("第一个()中是什么:" + matcher.group("g1"));
System.out.println("第二个()中是什么:" + matcher.group("g2"));
}
System.out.println("over");
}
}

结果图:image-20220601194121098

  1. 非命名捕获(第三个):

    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 day10;

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
    * @author 李敏灿
    * @version 1.0
    */

    public class demo1 {
    public static void main(String[] args) {
    String content = "李敏猫1290李敏烂5545李敏狗2342";
    //演示(?::pattern)的改良版
    String regStr1 = "李敏(猫|烂|狗)";//李敏猫李敏烂李敏狗
    //2.实例化一个正则表达式对象
    Pattern pattern = Pattern.compile(regStr1);
    //3.依次做个匹配器
    Matcher matcher = pattern.matcher(content);
    //4.开始工作
    while (matcher.find()) {
    System.out.println("找到什么:" + matcher.group(0));
    //下面的这些不能用,因为是非捕获分组
    // System.out.println("第一个()中是什么:" + matcher.group("g1"));
    // System.out.println("第二个()中是什么:" + matcher.group("g2"));
    }
    }
    }

    结果图:image-20220601165718611

  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
    package day10;

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
    * @author 李敏灿
    * @version 1.0
    * 捕获分名字
    */

    public class demo1 {
    public static void main(String[] args) {
    String content = "李敏猫1290李敏烂5545李敏狗2342";
    //演示(?<name>pattern)
    String regStr2 = "李敏(?=猫|狗)";//两个
    //2.实例化一个正则表达式对象
    Pattern pattern = Pattern.compile(regStr2);
    //3.依次做个匹配器
    Matcher matcher = pattern.matcher(content);
    //4.开始工作
    while (matcher.find()) {
    System.out.println("找到什么:" + matcher.group(0));
    //System.out.println("第一个()中是什么:" + matcher.group("g1"));
    //System.out.println("第二个()中是什么:" + matcher.group("g2"));
    }
    System.out.println("over");
    }
    }

    结果图:image-20220601194252161

  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
    package day10;

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
    * @author 李敏灿
    * @version 1.0
    * 捕获分名字
    */

    public class demo1 {
    public static void main(String[] args) {
    String content = "李敏猫1290李敏烂5545李敏狗2342";
    //演示(?<name>pattern)
    String regStr2 = "李敏(?!猫|狗)";//一个
    //2.实例化一个正则表达式对象
    Pattern pattern = Pattern.compile(regStr2);
    //3.依次做个匹配器
    Matcher matcher = pattern.matcher(content);
    //4.开始工作
    while (matcher.find()) {
    System.out.println("找到什么:" + matcher.group(0));
    //System.out.println("第一个()中是什么:" + matcher.group("g1"));
    //System.out.println("第二个()中是什么:" + matcher.group("g2"));
    }
    System.out.println("over");
    }
    }

    效果图:image-20220601194348817

16.4 应用

16.5 常用类

16.5.1 一览表

image-20220530180226853

16.5.2 Pattern 类的方法

matches:整个匹配

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

import java.util.regex.Pattern;

/**
* @author 李敏灿
* @version 1.0
* 演示 Pattern 的常用方法 matches 整个匹配
*/

public class demo2 {
public static void main(String[] args) {
String content = "李大花也是很厉害的额";
String regStr = "李.*";
System.out.println(Pattern.matches(regStr,content));//true

}
}

16.5.3 Matcher 类的方法

image-20220601203621078image-20220601203909722

16.6 分组,捕获,反向引用

image-20220530180421109

反向引用举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package day10;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author 李敏灿
* @version 1.0
* 反向引用
* 找到ABCBACC的数
*/

public class demo3 {
public static void main(String[] args) {
String content = "1232133jifajfd5454oajf";
String regStr = "(\\d)(\\d)(\\d)\\2\\1\\3{2}";
Pattern pattern = Pattern.compile(regStr);
Matcher matcher = pattern.matcher(content);
while (matcher.find()){
System.out.println(matcher.group(0));//1232133
}
}
}

16.7 正则 in String

  1. image-20220601103524296

  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
    package day10;

    /**
    * @author 李敏灿
    * @version 1.0
    * 演示正则表达式在 String 中的用途
    */

    public class StringReg {
    public static void main(String[] args) {
    String content = "2000 年 5 月, JDK1.3、 JDK1.4 和 J2SE1.3 相继发布, 几周后其\" +\n" +
    "\"获得了 Apple 公司 Mac OS X 的工业标准的支持。 2001 年 9 月 24 日, J2EE1.3 发\" +\n" +
    "\"布。 \" +\n" +
    "\"2002 年 2 月 26 日, J2SE1.4 发布。 自此 Java 的计算能力有了大幅提升";
    //1. 替换
    String s = content.replaceAll("JDK1\\.3|JDK1\\.4", "JDK");
    System.out.println(s);
    //2.验证
    content = "13809098987";
    if (content.matches("13(8|9)\\d{8}")){
    System.out.println("验证成功");
    }else{
    System.out.println("验证失败");
    }
    // 3.分割
    content = "hello#abc-jack12smith~北京";
    String[] split = content.split("#|-|~|\\d+");
    for (String s1 : split) {
    System.out.println(s1);
    }

    }
    }
  3. 结果图:image-20220601104858856

十七,JDBC

17.1 介绍

  1. 来由:image-20220601230244501

  2. 好处:image-20220601230430058

  3. JDBC 编写步骤:image-20220601230606977

  4. 注意:用 JDBC 记得把对应的驱动 jar 包引进带项目中来。

    详细教学见这篇博客:怎么引入驱动教学

    获取直接 jar 包点这里

17.2 获取连接

  1. 直接 new 的静态加载image-20220601231940912

  2. 用反射来动态加载image-20220601231952578

  3. 用DriverManagerimage-20220601232004092

  4. Class.forName 自动完成注册

    image-20220601232021062

  5. 这个才是用的最多的image-20220601232036457

17.3 结果集

17.3.1 介绍

image-20220601232224095image-20220601232241119

17.3.2 底层image-20220601232406547

17.3.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
package day11;

import com.sun.jdi.ArrayReference;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;

/**
* @author 李敏灿
* @version 1.0
* 演示结果集
*/

public class JDBCdemo2 {
public static void main(String[] args) throws IOException, SQLException {
//1.准备工作
Properties properties = new Properties();
properties.load(new FileInputStream("src\\mysql.properties"));
String sql = "select * from Student";
System.out.println("年龄"+"\t"+"名字"+"\t"+"身高");

try (
//2.获取连接(没有 driver 也是行的)
Connection connection = DriverManager.getConnection(properties.getProperty("url"), properties.getProperty
("user"), properties.getProperty("password"));
//3.得到Statement
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
) {
//4.取出数据
while (resultSet.next()) {//让光标向后移动,如果没有更多行就返回 false
System.out.println(resultSet.getInt("age")+"\t"+resultSet.getString("name")+"\t"+resultSet
.getInt("height"));
}
}
}
}

17.4 Statement

image-20220601232431872

演示 SQL 注入:

  1. 表的准备:image-20220602163216327

  2. 账号或者密码不对尝试-> 登不进去image-20220602163413821

  3. 账号密码都对就能登录那个账号:image-20220602163519952

  4. SQL 注入:

    账号:1’ or

    密码:or ‘1’='1

    image-20220602163704700

    就靠 SQL 注入,不知道账号密码也进去了,还是全部都暴露。

17.5 PreparedStatement

17.5.1 介绍

image-20220602092642995

17.5.2 好处

image-20220602092708649

17.5.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
package day11;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.Scanner;

/**
* @author 李敏灿
* @version 1.0
* 演示 PreparedStatement
*/

public class JDBCdemo3 {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
//登录输入
System.out.print("请输入管理员的名字:");
String admin_name = scanner.nextLine();//想看 SQL 注入,这里要用 nextLine。如果是 next 遇到空格就会结束
System.out.print("请输入管理员密码:");
String admin_pwd = scanner.nextLine();
//准备配置文件
Properties properties = new Properties();
String path = "src\\mysql.properties";
properties.load(new FileInputStream(path));
//获取对应信息
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String sqlStr = "select * from admin where name = ? and password = ?";

try (
//直接拿到连接
Connection connection = DriverManager.getConnection(url, user, password);
//拿到 PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sqlStr);
) {
//给 ? 赋值
//preparedStatement.setInt(1,18);
preparedStatement.setString(1,admin_name);
preparedStatement.setString(2,admin_pwd);
//执行 SQL 语句
final ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet.next()?"恭喜,登陆成功":"抱歉,登录失败");

} catch (SQLException e) {
}
}
}

验证图:

image-20220602173615642

成功:image-20220602173406647

失败:image-20220602173443764

防住了基本 SQL 注入:image-20220602173545875

17.6 API

image-20220602092734342

image-20220602092742728

17.7 封装JDBCUtils

image-20220602092800378

代码展示:

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 day11;

import Utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
* @author 李敏灿
* @version 1.0
* 上 JDBC 工具类
*/

public class JDBCdemo4 {
public static void main(String[] args) throws SQLException {
//1.通过工具类获取连接
Connection connection = JDBCUtils.getConnection();
//2.写 SQL 语句
String sql = "select * from student where age = ? and name = ?";
//3.拿到preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//4.补充
preparedStatement.setInt(1,18);
preparedStatement.setString(2,"LMC");
//5.查询拿到结果集
ResultSet resultSet = preparedStatement.executeQuery();
//6.遍历输出
while (resultSet.next()){
System.out.println(resultSet.getInt("age")+resultSet.getString("name"));
}
//7.关闭资源
JDBCUtils.close(resultSet,preparedStatement,connection);
}
}

截图:image-20220602200334977

17.8 事务

17.8.1 总结image-20220602092820257

17.8.2 说明

其实这个就是一种对语句的捆绑,要么都执行成功要么都失败。就像图中举的转账的例子,如果不用事务,假如 A 转了 100 出去,然后系统出问题了,B 没收到。那这 100 就凭空消失了。但是用了事务就不会,他会捆绑转出和接受只有两个都成功才行。

17.8.3 代码

17.8.3.1 数据库准备:
  1. image-20220603094245315
1
2
3
4
5
6
7
8
9
10
转账业务的演示

CREATE TABLE `account`(
id INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL DEFAULT '',
balance DOUBLE NOT NULL DEFAULT 0) CHARACTER SET utf8;
)

INSERT INTO `account` VALUES(NULL,'马云',3000);
INSERT INTO `account` VALUES(NULL,'马化腾',10000);
17.8.3.2 JDBC

不用事务结果图:image-20220603101146664

image-20220603101225796

用事务结果图:

image-20220603102637913

这次没有问题

image-20220603102700407

代码:

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
package day12;

import Utils.JDBCUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* @author 李敏灿
* @version 1.0
* 演示 JDBC 中的事务
* 转账
*/

public class demo1 {
//没有用事务
@Test
public void noTransaction(){
//准备 连接和 statement
Connection connection = null;
PreparedStatement preparedStatement = null;
//写好 sql
String sql1 = "update account set balance = balance - 100 where id = 1";
String sql2 = "update account set balance = balance + 100 where id = 2";
//为什么上面只声明?因为我们想在 finally 中用这个,写在上面可以扩大范围
try {
//获取连接
connection = JDBCUtils.getConnection();
//执行第一句 sql
preparedStatement = connection.prepareStatement(sql1);
preparedStatement.executeUpdate();
//故意制造异常
int i = 1 / 0;
//执行第二条 sql
preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
//关闭所有资源
JDBCUtils.close(null, preparedStatement, connection);
}


}
//用了事务
@Test
public void useTransaction() {
Connection connection = null;
PreparedStatement preparedStatement = null;

String sql1 = "update account set balance = balance - 100 where id = 1";
String sql2 = "update account set balance = balance + 100 where id = 2";

try {
connection = JDBCUtils.getConnection();
connection.setAutoCommit(false);//首先就是取消 connection 的自动提交

preparedStatement = connection.prepareStatement(sql1);
preparedStatement.executeUpdate();

int i = 1 / 0;

preparedStatement = connection.prepareStatement(sql2);
preparedStatement.executeUpdate();

connection.commit();//都执行 ok 了才提交
} catch (SQLException e) {
//这里我们可以进行回滚
System.out.println("执行发生异常,撤销执行的 SQL");
try {
connection.rollback();//执行有不对就直接回滚到开始的地方
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
JDBCUtils.close(null, preparedStatement, connection);
}
}
}

17.9 批处理

17.9.1 总结

image-20220602092837709

17.9.2 实操

17.9.2.1 数据库准备

image-20220603104333014

1
2
3
4
5
CREATE TABLE admin2(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) NOT NULL ,
`password` VARCHAR(32) NOT NULL ) CHARACTER SET utf8;
)

image-20220603104701347

17.9.2.2 Java 端

代码:

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
package day12;

import Utils.JDBCUtils;
import org.junit.jupiter.api.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
* @author 李敏灿
* @version 1.0
* 演示 JDBC 批处理
*/

public class demo2 {
//传统方法添加 500 条数据到 admin2
@Test
public void noBatch() throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null, ?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {//5000 执行
preparedStatement.setString(1, "jack" + i);
preparedStatement.setString(2, "666");
preparedStatement.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("传统的方式 耗时=" + (end - start));//传统的方式 耗时=1969
//关闭连接
JDBCUtils.close(null, preparedStatement, connection);
}



//批处理添加 500 条数据到 admin2
@Test
public void batch() throws SQLException {
Connection connection = JDBCUtils.getConnection();
String sql = "insert into admin2 values(null, ?, ?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
System.out.println("开始执行");
long start = System.currentTimeMillis();//开始时间
for (int i = 0; i < 5000; i++) {//5000 执行
preparedStatement.setString(1, "jack" + i);
preparedStatement.setString(2, "666");
//将 sql 语句加入到批处理包中 -> 看源码
/*
//1. //第一就创建 ArrayList - elementData => Object[]
//2. elementData => Object[] 就会存放我们预处理的 sql 语句
//3. 当 elementData 满后,就按照 1.5 扩容
//4. 当添加到指定的值后, 就 executeBatch
//5. 批量处理会减少我们发送 sql 语句的网络开销, 而且减少编译次数, 因此效率提高
public void addBatch() throws SQLException {
synchronized(this.checkClosed().getConnectionMutex()) {
if (this.batchedArgs == null) {
this.batchedArgs = new ArrayList();
}
for(int i = 0; i < this.parameterValues.length; ++i) {
this.checkAllParametersSet(this.parameterValues[i], this.parameterStreams[i], i);
}
this.batchedArgs.add(new PreparedStatement.BatchParams(this.parameterValues,
this.parameterStreams, this.isStream, this.streamLengths, this.isNull));
}
}
*/
preparedStatement.addBatch();
//当有 1000 条记录时, 在批量执行
if((i + 1) % 1000 == 0) {//满 1000 条 sql
preparedStatement.executeBatch();
//清空一把
preparedStatement.clearBatch();
}
}
long end = System.currentTimeMillis();
System.out.println("批量方式 耗时=" + (end - start));//批量方式 耗时=68
//关闭连接
JDBCUtils.close(null, preparedStatement, connection);
}
}

不用批处理:1969 ms

image-20220603105851068

用了批处理:68ms

image-20220603105937761

17.10 数据库连接池

17.10.1 传统问题

image-20220602092924778

17.10.2 数据库连接池种类

image-20220602092955409

17.10.3 代码实操

  1. C3P0 两种方法

代码:

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
package day12;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;

import java.beans.PropertyVetoException;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLOutput;
import java.util.Properties;

/**
* @author 李敏灿
* @version 1.0
* 演示数据库连接池 C3P0
*/

public class demo3 {
//裸配(不推荐太累了)
@Test
public void testC3P0_01() throws IOException, PropertyVetoException, SQLException {
//创建一个数据源对象
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//从 properties 读取信息
Properties properties = new Properties();
properties.load(new FileReader("src\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//设置数据源对象的方方面面
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setJdbcUrl(url);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
comboPooledDataSource.setInitialPoolSize(10);
comboPooledDataSource.setMaxPoolSize(20);
//正式开始用 c3p0 来连接数据库
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {

Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("用c3p0 5000次连接池耗时" + (end - start));//356

}

// 用配置文件(推荐)
public void testC3P0_02() throws SQLException {
//有了那个 xml 文件就可以像下面这样直接上刚
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("lmc");
//测试 5000 次连接 mysql
long start = System.currentTimeMillis();
System.out.println("开始执行....");
for (int i = 0; i < 500000; i++) {
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
//c3p0 的第二种方式 耗时=
System.out.println("c3p0 的第二种方式(500000) 耗时=" + (end - start));//342
}
}

xml 文件

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
<c3p0-config>
<!-- 数据源名称代表连接池 -->
<named-config name="lmc">
<!-- 驱动类 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- url-->
<property name="jdbcUrl">jdbc\:mysql\://localhost\:3306/jdbc_db?rewriteBatchedStatements=true</property>
<!-- 用户名 -->
<property name="user">root</property>
<!-- 密码 -->
<property name="password">lmc</property>
<!-- 每次增长的连接数-->
<property name="acquireIncrement">5</property>
<!-- 初始的连接数 -->
<property name="initialPoolSize">10</property>
<!-- 最小连接数 -->
<property name="minPoolSize">5</property>
<!-- 最大连接数 -->
<property name="maxPoolSize">50</property>

<!-- 可连接的最多的命令对象数 -->
<property name="maxStatements">5</property>

<!-- 每个连接对象可连接的最多的命令对象数 -->
<property name="maxStatementsPerConnection">2</property>
</named-config>
</c3p0-config>
  1. Druid

    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
    package day12;

    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import org.junit.jupiter.api.Test;

    import javax.sql.DataSource;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    import java.sql.Connection;
    import java.util.Properties;

    /**
    * @author 李敏灿
    * @version 1.0
    * Druid 数据库连接池的使用
    *
    */

    public class demo4 {
    @Test
    public void testDruid() throws Exception {
    //1. 加入 Druid 包
    //2.加入配置文件 druid.proper 拷贝进项目的 src 目录
    //3.创建 properties 对象,读取配置文件
    Properties properties = new Properties();
    properties.load(new FileReader("src\\druid.properties"));
    //4. 创建指定参数的 Druid 连接池
    DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
    long start = System.currentTimeMillis();
    for (int i = 0; i < 5000; i++) {
    Connection connection = dataSource.getConnection();
    connection.close();
    }
    long end = System.currentTimeMillis();
    System.out.println("Druid 连接池耗时"+(end - start));//403

    }
    }

17.11 Apache-DBUtils

17.11.1 问题描述

image-20220603172409425

17.11.2 介绍

image-20220602093056631

17.11.3 代码演示

演示用 DBUtils + Druid 完成对表的 crud

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 day12;

import Utils.JDBCUtils;
import Utils.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.jupiter.api.Test;

import java.sql.*;
import java.util.List;

/**
* @author 李敏灿
* @version 1.0
* 使用 DBUtils 和 Druid 演示对数据库表的 crud
*/

public class demo5 {
@Test
//查
public void Query() throws SQLException {
//1.通过 Druid 得到连接
Connection connection = JDBCUtilsByDruid.getConnection();
//2.引入 DBUtils 的 jar 包
//3.创建 QueryRunner
QueryRunner queryRunner = new QueryRunner();
//4. 执行相关办法,返回 ArrayList 结果集
String sqlStr = "select * from student where age >= ?";
List<student> list = queryRunner.query(connection, sqlStr, new BeanListHandler<>(student.class),17);
//要是多行就用 BeanListHandler;一行就用 BeanHandler ;一行中的一个对象就用 ScalarHandler
System.out.println("输出集合的信息");
for (student student : list) {
System.out.println(student);
}
JDBCUtilsByDruid.close(null,null,connection);

}
//增删改
@Test
public void dml() throws SQLException {
Connection connection = JDBCUtilsByDruid.getConnection();
QueryRunner queryRunner = new QueryRunner();

//String sql = "update student set height = ? where name = ?";
//String sql = "insert into student values(28,'LML',185)";
String sql = "delete from student where name = ?";
int hl = queryRunner.update(connection,sql,"LML");
System.out.println(hl>0?"执行成功":"执行没有影响到表");
JDBCUtilsByDruid.close(null,null,connection);

}
}
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
//queryRunner.query 源码解析
public <T> T query(Connection conn, String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException {
//上面这个 Object... params 是用来接受赋给 SQL 语句中的参数的
PreparedStatement stmt = null;
ResultSet rs = null;
Object result = null;

try {
stmt = this.prepareStatement(conn, sql);//初始化 preparedStatement
this.fillStatement(stmt, params);//填充 SQL 语句中的 ?
rs = this.wrap(stmt.executeQuery());//执行 SQL 返回 resultset
result = rsh.handle(rs);//将 resultset 结果集包装成 ArrayList[T] 这里用的是反射,注意 T 类中要有空构造器不然会说 cannot create
} catch (SQLException var33) {
this.rethrow(var33, sql, params);
} finally {
//关闭
try {
this.close(rs);
} finally {
this.close((Statement)stmt);
}
}

return result;//返回最好的那个 List
}


17.12 DAO和常用方法

17.12.1 问题

image-20220602093152000

17.12.2 说明

image-20220602093237101

17.12.3 实操

成功图:image-20220603215719583

代码模板:在这个 Gitee 地址

十八,个人补充

18.1 常识

  1. 什么是 BAT,TMD?

BAT 指 百度,阿里,腾讯

TMD 指 头条,美团,滴滴

18.2 知新

  1. 每一个类编译后都有一个 .class 文件

  2. 可以把 main 写在非 public 类中,然后编译后(javac)指定运行(java)这个类就可以让程序入口切换到非 public 类中的 main 方法

  3. image-20220521200152167

  4. .java是源文件,由一个个类构成;而每个类都会在javac后变成一个.class文件。

  5. Java 中 char 本质还是数字 ,对应的是 unicode (ASCII(只有英文) -> unicode(全了但是浪费空间) ->utf8

  6. Java中取余的本质是利用了除法的只能保留整数 10%3=10-10/3*3

  7. a = a++ 见 3.2.3

  8. System.in & System.out 见3.9.3

  9. 前一段时间一直纠结 Java 怎么没有 goto 。它用的是标签啊!看4.5.1.4

  10. String 不属于基础数据类型而是数据类型

  11. 类就是数据类型

  12. 打印数组的话,直接写数组名是不行滴。要么遍历嘛,要么就Arrays.toString(数组名)

  13. 局部变量是不能使用访问修饰符的

  14. 异常处理的时候 catch 多个,是子在前父在后

  15. JUnit 是 Java 语言的单元测试框架(Jshell 也挺好玩) 用的挺多的,就直接 @Test然后把第二个引进来就中。import org.junit.jupiter.api.Test;

  16. 建议大家无论干嘛最好类中留一个无参构造器,因为反射会用到这个,不然会报出来奇奇怪怪的错误,比如创建不了等啊。