Java SE学习笔记
准备知识
Markdown的常用语法
-
#的数量决定几标题
-
*±表示无序列表,可以嵌套
-
有序列表用1.2.3.表示
-
文字表示斜体,文字黑体,***文字***加粗斜体,
文字删除线。 -
表示引用,>>和>>>可以嵌套
-
—/***三个及以上表示分割线
-
[链接名字](“链接地址”,“链接title”)。(“链接title”可以省略)(网址要写https:// or http://)
-
! 图片描述
-
两个`之间放代码,两个```之间放语言标识和代码块。
写Blog
Typora,语雀,CSDN,简书,博客园…
常见的Dos命令
#盘符切换
#查看当前目录下的所有文件 dir
#切换目录 cd
#清屏 cls
#退出终端 exit
#查看电脑的ip ipconfig
#打开应用
calc(计算器)
mspaint
notepad
#ping
#文件操作
md 目录名
rd 目录名
cd> 文件名(创建文件)
del 文件名
常见的IDEA快捷键
- psvm + 回车 主函数
- sout + 回车 输出语句
- win + shift + s win10 截图
- ctrl + D 复制当前行到下一行。
- Alt + shift + F10 改为 ctrl + Enter 表示允许程序
- Alt + Insert 改为 Alt+空格 表示 Generate
1.Ctrl
快捷键 | 介绍 |
---|---|
Ctrl + F | 在当前文件进行文本查找 (必备) |
Ctrl + R | 在当前文件进行文本替换 (必备) |
Ctrl + Z | 撤销 (必备) |
Ctrl + Y | 删除光标所在行 或 删除选中的行 (必备) |
Ctrl + X | 剪切光标所在行 或 剪切选择内容 |
Ctrl + C | 复制光标所在行 或 复制选择内容 |
Ctrl + D | 复制光标所在行 或 复制选择内容,并把复制内容插入光标位置下面 (必备) |
Ctrl + W | 递进式选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展选中范围 (必备) |
Ctrl + E | 显示最近打开的文件记录列表 (必备) |
Ctrl + N | 根据输入的 类名 查找类文件 (必备) |
Ctrl + G | 在当前文件跳转到指定行处 |
Ctrl + J | 插入自定义动态代码模板 (必备) |
Ctrl + P | 方法参数提示显示 (必备) |
Ctrl + Q | 光标所在的变量 / 类名 / 方法名等上面(也可以在提示补充的时候按),显示文档内容 |
Ctrl + U | 前往当前光标所在的方法的父类的方法 / 接口定义 (必备) |
Ctrl + B | 进入光标所在的方法/变量的接口或是定义处,等效于 Ctrl + 左键单击 (必备) |
Ctrl + K | 版本控制提交项目,需要此项目有加入到版本控制才可用 |
Ctrl + T | 版本控制更新项目,需要此项目有加入到版本控制才可用 |
Ctrl + H | 显示当前类的层次结构 |
Ctrl + O | 选择可重写的方法 |
Ctrl + I | 选择可继承的方法 |
Ctrl + + | 展开代码 |
Ctrl + - | 折叠代码 |
Ctrl + / | 注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号 (必备) |
Ctrl + [ | 移动光标到当前所在代码的花括号开始位置 |
Ctrl + ] | 移动光标到当前所在代码的花括号结束位置 |
Ctrl + F1 | 在光标所在的错误代码处显示错误信息 (必备) |
Ctrl + F3 | 调转到所选中的词的下一个引用位置 (必备) |
Ctrl + F4 | 关闭当前编辑文件 |
Ctrl + F8 | 在 Debug 模式下,设置光标当前行为断点,如果当前已经是断点则去掉断点 |
Ctrl + F9 | 执行 Make Project 操作 |
Ctrl + F11 | 选中文件 / 文件夹,使用助记符设定 / 取消书签 (必备) |
Ctrl + F12 | 弹出当前文件结构层,可以在弹出的层上直接输入,进行筛选 |
Ctrl + Tab | 编辑窗口切换,如果在切换的过程又加按上delete,则是关闭对应选中的窗口 |
Ctrl + End | 跳到文件尾 |
Ctrl + Home | 跳到文件头 |
Ctrl + Space | 基础代码补全,默认在 Windows 系统上被输入法占用,需要进行修改,建议修改为 Ctrl + 逗号 (必备) |
Ctrl + Delete | 删除光标后面的单词或是中文句 (必备) |
Ctrl + BackSpace | 删除光标前面的单词或是中文句 (必备) |
Ctrl + 1,2,3…9 | 定位到对应数值的书签位置 (必备) |
Ctrl + 左键单击 | 在打开的文件标题上,弹出该文件路径 (必备) |
Ctrl + 光标定位 | 按 Ctrl 不要松开,会显示光标所在的类信息摘要 |
Ctrl + 左方向键 | 光标跳转到当前单词 / 中文句的左侧开头位置 (必备) |
Ctrl + 右方向键 | 光标跳转到当前单词 / 中文句的右侧开头位置 (必备) |
Ctrl + 前方向键 | 等效于鼠标滚轮向前效果 (必备) |
Ctrl + 后方向键 | 等效于鼠标滚轮向后效果 (必备) |
2.Alt
快捷键 | 介绍 |
---|---|
Alt + `|显示版本控制常用操作菜单弹出层 (必备) | |
Alt + Q | 弹出一个提示,显示当前类的声明 / 上下文信息 |
Alt + F1 | 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择 (必备) |
Alt + F2 | 对于前面页面,显示各类浏览器打开目标选择弹出层 |
Alt + F3 | 选中文本,逐个往下查找相同文本,并高亮显示 |
Alt + F7 | 查找光标所在的方法 / 变量 / 类被调用的地方 |
Alt + F8 | 在 Debug 的状态下,选中对象,弹出可输入计算表达式调试框,查看该输入内容的调试结果 |
Alt + Home | 定位 / 显示到当前文件的 Navigation Bar |
Alt + Enter | IntelliJ IDEA 根据光标所在问题,提供快速修复选择,光标放在的位置不同提示的结果也不同 (必备) |
Alt + Insert | 代码自动生成,如生成对象的 set / get 方法,构造函数,toString() 等 (必备) |
Alt + 左方向键 | 切换当前已打开的窗口中的子视图,比如Debug窗口中有Output、Debugger等子视图,用此快捷键就可以在子视图中切换 (必备) |
Alt + 右方向键 | 按切换当前已打开的窗口中的子视图,比如Debug窗口中有Output、Debugger等子视图,用此快捷键就可以在子视图中切换 (必备) |
Alt + 前方向键 | 当前光标跳转到当前文件的前一个方法名位置 (必备) |
Alt + 后方向键 | 当前光标跳转到当前文件的后一个方法名位置 (必备) |
Alt + 1,2,3…9 | 显示对应数值的选项卡,其中 1 是 Project 用得最多 (必备) |
3.Shift
快捷键 | 介绍 |
---|---|
Shift + F1 | 如果有外部文档可以连接外部文档 |
Shift + F2 | 跳转到上一个高亮错误 或 警告位置 |
Shift + F3 | 在查找模式下,查找匹配上一个 |
Shift + F4 | 对当前打开的文件,使用新Windows窗口打开,旧窗口保留 |
Shift + F6 | 对文件 / 文件夹 重命名 |
Shift + F7 | 在 Debug 模式下,智能步入。断点所在行上有多个方法调用,会弹出进入哪个方法 |
Shift + F8 | 在 Debug 模式下,跳出,表现出来的效果跟 F9 一样 |
Shift + F9 | 等效于点击工具栏的 Debug 按钮 |
Shift + F10 | 等效于点击工具栏的 Run 按钮 |
Shift + F11 | 弹出书签显示层 (必备) |
Shift + Tab | 取消缩进 (必备) |
Shift + ESC | 隐藏当前 或 最后一个激活的工具窗口 |
Shift + End | 选中光标到当前行尾位置 |
Shift + Home | 选中光标到当前行头位置 |
Shift + Enter | 开始新一行。光标所在行下空出一行,光标定位到新行位置 (必备) |
Shift + 左键单击 | 在打开的文件名上按此快捷键,可以关闭当前打开文件 (必备) |
Shift + 滚轮前后滚动 | 当前文件的横向滚动轴滚动 (必备) |
4.Ctrl + Alt
快捷键 | 介绍 |
---|---|
Ctrl + Alt + L | 格式化代码,可以对当前文件和整个包目录使用 (必备) |
Ctrl + Alt + O | 优化导入的类,可以对当前文件和整个包目录使用 (必备) |
Ctrl + Alt + I | 光标所在行 或 选中部分进行自动代码缩进,有点类似格式化 |
Ctrl + Alt + T | 对选中的代码弹出环绕选项弹出层 (必备) |
Ctrl + Alt + J | 弹出模板选择窗口,将选定的代码加入动态模板中 |
Ctrl + Alt + H | 调用层次 |
Ctrl + Alt + B | 在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口 |
Ctrl + Alt + C | 重构-快速提取常量 |
Ctrl + Alt + F | 重构-快速提取成员变量 |
Ctrl + Alt + V | 重构-快速提取变量 |
Ctrl + Alt + Y | 同步、刷新 |
Ctrl + Alt + S | 打开 IntelliJ IDEA 系统设置 (必备) |
Ctrl + Alt + F7 | 显示使用的地方。寻找被该类或是变量被调用的地方,用弹出框的方式找出来 |
Ctrl + Alt + F11 | 切换全屏模式 |
Ctrl + Alt + Enter | 光标所在行上空出一行,光标定位到新行 (必备) |
Ctrl + Alt + Home | 弹出跟当前文件有关联的文件弹出层 |
Ctrl + Alt + Space | 类名自动完成 |
Ctrl + Alt + 左方向键 | 退回到上一个操作的地方 (必备) |
Ctrl + Alt + 右方向键 | 前进到上一个操作的地方 (必备) |
Ctrl + Alt + 前方向键 | 在查找模式下,跳到上个查找的文件 |
Ctrl + Alt + 后方向键 | 在查找模式下,跳到下个查找的文件 |
Ctrl + Alt + 右括号(]) | 在打开多个项目的情况下,切换下一个项目窗口 |
Ctrl + Alt + 左括号([) | 在打开多个项目的情况下,切换上一个项目窗口 |
5.Ctrl + Shift
快捷键 | 介绍 |
---|---|
Ctrl + Shift + F | 根据输入内容查找整个项目 或 指定目录内文件 (必备) |
Ctrl + Shift + R | 根据输入内容替换对应内容,范围为整个项目 或 指定目录内文件 (必备) |
Ctrl + Shift + J | 自动将下一行合并到当前行末尾 (必备) |
Ctrl + Shift + Z | 取消撤销 (必备) |
Ctrl + Shift + W | 递进式取消选择代码块。可选中光标所在的单词或段落,连续按会在原有选中的基础上再扩展取消选中范围 (必备) |
Ctrl + Shift + N | 通过文件名定位 / 打开文件 / 目录,打开目录需要在输入的内容后面多加一个正斜杠 (必备) |
Ctrl + Shift + U | 对选中的代码进行大 / 小写轮流转换 (必备) |
Ctrl + Shift + T | 对当前类生成单元测试类,如果已经存在的单元测试类则可以进行选择 (必备) |
Ctrl + Shift + C | 复制当前文件磁盘路径到剪贴板 (必备) |
Ctrl + Shift + V | 弹出缓存的最近拷贝的内容管理器弹出层 |
Ctrl + Shift + E | 显示最近修改的文件列表的弹出层 |
Ctrl + Shift + H | 显示方法层次结构 |
Ctrl + Shift + B | 跳转到类型声明处 (必备) |
Ctrl + Shift + I | 快速查看光标所在的方法 或 类的定义 |
Ctrl + Shift + A | 查找动作 / 设置 |
Ctrl + Shift + / | 代码块注释 (必备) |
Ctrl + Shift + [ | 选中从光标所在位置到它的顶部中括号位置 (必备) |
Ctrl + Shift + ] | 选中从光标所在位置到它的底部中括号位置 (必备) |
Ctrl + Shift + + | 展开所有代码 (必备) |
Ctrl + Shift + - | 折叠所有代码 (必备) |
Ctrl + Shift + F7 | 高亮显示所有该选中文本,按Esc高亮消失 (必备) |
Ctrl + Shift + F8 | 在 Debug 模式下,指定断点进入条件 |
Ctrl + Shift + F9 | 编译选中的文件 / 包 / Module |
Ctrl + Shift + F12 | 编辑器最大化 (必备) |
Ctrl + Shift + Space | 智能代码提示 |
Ctrl + Shift + Enter | 自动结束代码,行末自动添加分号 (必备) |
Ctrl + Shift + Backspace | 退回到上次修改的地方 (必备) |
Ctrl + Shift + 1,2,3…9 | 快速添加指定数值的书签 (必备) |
Ctrl + Shift + 左键单击 | 把光标放在某个类变量上,按此快捷键可以直接定位到该类中 (必备) |
Ctrl + Shift + 左方向键 | 在代码文件上,光标跳转到当前单词 / 中文句的左侧开头位置,同时选中该单词 / 中文句 (必备) |
Ctrl + Shift + 右方向键 | 在代码文件上,光标跳转到当前单词 / 中文句的右侧开头位置,同时选中该单词 / 中文句 (必备) |
Ctrl + Shift + 前方向键 | 光标放在方法名上,将方法移动到上一个方法前面,调整方法排序 (必备) |
Ctrl + Shift + 后方向键 | 光标放在方法名上,将方法移动到下一个方法前面,调整方法排序 (必备) |
6.Alt + Shift
快捷键 | 介绍 |
---|---|
Alt + Shift + N | 选择 / 添加 task (必备) |
Alt + Shift + F | 显示添加到收藏夹弹出层 / 添加到收藏夹 |
Alt + Shift + C | 查看最近操作项目的变化情况列表 |
Alt + Shift + I | 查看项目当前文件 |
Alt + Shift + F7 | 在 Debug 模式下,下一步,进入当前方法体内,如果方法体还有方法,则会进入该内嵌的方法中,依此循环进入 |
Alt + Shift + F9 | 弹出 Debug 的可选择菜单 |
Alt + Shift + F10 | 弹出 Run 的可选择菜单 |
Alt + Shift + 左键双击 | 选择被双击的单词 / 中文句,按住不放,可以同时选择其他单词 / 中文句 (必备) |
Alt + Shift + 前方向键 | 移动光标所在行向上移动 (必备) |
Alt + Shift + 后方向键 | 移动光标所在行向下移动 (必备) |
7.Ctrl + Shift + Alt
快捷键 | 介绍 |
---|---|
Ctrl + Shift + Alt + V | 无格式黏贴 (必备) |
Ctrl + Shift + Alt + N | 前往指定的变量 / 方法 |
Ctrl + Shift + Alt + S | 打开当前项目设置 (必备) |
Ctrl + Shift + Alt + C | 复制参考信息 |
8.其他
快捷键 | 介绍 |
---|---|
F2 | 跳转到下一个高亮错误 或 警告位置 (必备) |
F3 | 在查找模式下,定位到下一个匹配处 |
F4 | 编辑源 (必备) |
F7 | 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中 |
F8 | 在 Debug 模式下,进入下一步,如果当前行断点是一个方法,则不进入当前方法体内 |
F9 | 在 Debug 模式下,恢复程序运行,但是如果该断点下面代码还有断点则停在下一个断点上 |
F11 | 添加书签 (必备) |
F12 | 回到前一个工具窗口 (必备) |
Tab | 缩进 (必备) |
ESC | 从工具窗口进入代码文件窗口 (必备) |
连按两次Shift | 弹出 Search Everywhere 弹出层 |
相对无关内容
找开源项目的方式
高级检索(同样适用于Google)
#in:name example 名字中有“example”
#in:readme example readme中有“example”
#in:description example 描述中有“example”
#stars:>1000 star>1000
#forks:>1000 fork>1000
#pushed:>2019-09-01 2019年9月1日后有更新的
#language:java 用Java编写的项目
参考文档:https://docs.github.com/en/github/searching-for-information-on-github/searching-for-repositories
Java基础知识
入门注意:
● 如果手工输入源程序,一定要注意大小写。尤其是类名为Welcome,而不是welcome或WELCOME。
● 编译器需要一个文件名(Welcome.java),而运行程序时,只需要指定类名(Welcome),不要带扩展名.java或.class。
卸载JDK:
(从入门到卸载)
-
删除Java的安装目录
-
删除JAVA_HOME
-
删除path下关于Java的目录
-
java -version检查
数据类型:
强类型,严格定义
基本数据类型(Primitive Type):
八个:
-
数值类型:
- 整数类型:byte(1),short(2),int(4),long(8)
- 浮点类型:float(4),double(8)
- 字符类型:char(2)
-
boolean类型:true/false
(字符串String不是关键字,是类)
引用数据类型(Reference Type):
- 类
- 接口
- 数组
类型转换:
- 自动类型转换:低转高
- 强制类型转换:高转低
拓展:
- 少使用浮点数进行比较
- 进制:
整数各种进制的字面量如下:
十进制数,没有前缀
二进制数,前缀是0b
八进制数,前缀是0o
十六进制数,前缀是0x
let decimalInteger = 17
let binaryInteger = 0b10001 // 二进制的17
let octalInteger = 0o21 // 八进制的17
let hexadecimalInteger = 0x11 // 十六进制的17
可以使用十六进制表示浮点数值。例如,0.125=2-3可以表示成0x1.0p-3。在十六进制表示法中,使用p表示指数,而不是e。注意,尾数采用十六进制,指数采用十进制。指数的基数是2,而不是10。
变量和常量:
变量:
type varName [=value];
作用域:
类变量
实例变量
局部变量
常量:
final MAX_A = 10;
命名规范:
- 见名知意
- 驼峰命名(变量,方法)
- 类:首字母大写,驼峰命名
- 常量:大写+下划线
- 不要使用拼音命名
运算符:
-
算术运算符: + - * / % ++ –
-
赋值运算符: =
-
关系运算符:> < >= <= == != instanceof
-
逻辑运算符: && || !
-
位运算符: & | ^ ~ >> << >>>
-
条件运算符: ? :
-
扩展运算符: += -= *= /=
包机制:
-
域名倒写
-
防止冲突
-
package在最前 import
JavaDoc
生成JDK帮助文档
javadoc:
- @author
- @version
- @since
- @param
- @return
- @throws
public class HelloWorld{
/**
*@author Doke
*@param args 命令参数
*@since 8.0
*@throws 没有异常输出
*/
public static void main(String[] args){
System.out.println("hello");
}
}
编译结束后,执行如下命令可生成doc文件
javadoc -encoding UTF-8 -charset UTF-8 HelloWorld.java
流程控制
顺序结构:
程序默认执行顺序
选择结构:
if 单选择结构
if-else多选择结构
if-else if-else多选择结构
switch:1. 支持String类型 2. case穿透现象 3. default 4. break
循环结构:
while
do while
for
增强for循环(for-each)
-
break&continue&return
break:跳出循环
continue:终止当次循环
带标签continue
return:结束方法运行
方法(Method):
Java方法是语句的集合,它们在一起执行一个功能。
方法是解决一类问题的步骤的有序组合
方法包含于类或对象中
方法在程序中被创建,在其他地方被引用
**修饰符 返回值 方法名 (参数名) {return 返回值;} **
设计方法的原则:
功能块,实现功能的语句块集合。设计方法最好保持方法的原子性,就是一个方法只完成一个功能,有利于后期扩展。
方法的调用:
类名.方法 / 对象.方法
值传递和引用传递:
- call by value
- call by reference
重载方法:
-
在一个类中,有相同的函数名称,但形参不同的函数。
-
规则:
- 方法名称必须相同
- 参数列表必须不同(个数不同,类型不同,参数排列顺序不同等)。
- 方法的返回类型可以相同可以不同
- 仅仅返回类型不同不足以成为方法的重载
- 实现理论:方法名称相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,如果匹配失败,报错。
P.S.
-
命令行传参:给main方法传递参数
-
可变长参数:… 必须放在最后一个参数
-
递归:自己调用自己
-
**静态方法与非静态方法比较:**类的调用是否需要实例化(new)
静态方法是和类一起加载的!非静态方法是在类实例化之后才存在的。所以在静态方法中没法调用非静态方法。
数组
定义:
new int[5] {1,2,3,4,5} 同一类型
使用:
通过下标拿到值
Array index out of Bounds
增强for循环遍历数组
二维数组
int[] []
Arrays工具类
排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 归并排序
- 希尔排序
- 堆排序
- 桶排序(基数排序)
面向对象(Object-Oriented Programming)
-
本质:以类的方式组织代码,以对象的方式封装数据。
-
抽象
-
三大特性:
- 封装
- 继承
- 多态
-
类是对象的模板。先有对象后有类,类是对对象的抽象。
类 = 属性 + 方法。
类实例化后会返回一个自己的对象
(**PS:**一个项目里应该只存在一个main方法)
-
创建与对象初始化
使用new关键字创建对象:除了分配空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
-
构造器:即构造方法,是在创建对象(即使用new关键字时)的时候必须要调用的。
特点如下:
1.必须和类的名字相同
2.必须没有返回类型,也不能写void
分类:
-
无参构造器:实例化初始字段
-
有参构造器:一旦定义了有参构造,无参构造就必须显式定义,否则进行如下操作会报错。
Student student = new Student();
所以定义了有参构造,一定显式定义无参构造器。有参构造后即可:
public Student(String name){ } Student student = new Student("gxx")
作用:
1.new的本质是在调用构造器
2.初始化对象的值
-
-
P.S. this指向的是这个类的实例
-
创建对象的内存分析:
对象是通过引用来操作的: 栈 ---->堆
package com.oop;
import com.oop.demo03.Pet;
public class Application{
public static void main(String[] args){
Pet dog = new Pet();
dog.name = "旺财";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
Sysytem.out.println(dog.age);
Pet cat = new Pet();
}
}
package com.oop.demo03;
public class Pet{
public String name;
public int age;
//无参构造
public void shout(){
System.out.println("叫了一声");
}
}
上述代码的内存分配如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RFacJYga-1615354021890)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210217170212349.png)]
封装:
“高内聚,低耦合”
属性私有 + 可操作属性的方法(public的get,set方法)
好处:1. 提高程序安全性
2. 隐藏代码实现细节
3. 统一接口
4. 系统可维护性增加
继承:
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模
extends,子类是对父类的扩展,两者之间的关系为 ***“is a”***。
Java中只有单继承,没有多继承。
继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖,组合,聚合等。
子类继承父类,就会拥有父类的全部方法。
私有的东西无法继承!
Java中所有类都继承于Object类。
Super关键字
调用父类字段和方法
-
super调用父类的构造器,必须要在子类构造器的第一行(默认调用 )
-
super必须只能出现在子类的方法或者构造方法中
-
super和this不能同时调用构造方法
VS this:
代表的对象不同:
this :本身调用者这个对象
super:代表父类对象的应用
前提:
this:没有继承也可使用
super:只能在继承条件下使用
构造方法:
this();本类的构造
super();父类的构造
方法重写(@Override)
需要有继承关系,子类重写父类的方法,方法体不同。
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大; pubilc > Protected >Default >private
- 输出的异常:范围,可以被缩小,但不能扩大; Class Not Found Exception -->Exception(大)
非静态方法才能重写
静态方法,方法的调用只看左边。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzsD38YI-1615354021895)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210217192359461.png)]
多态:
动态编译,增强可扩展性!
即同一种方法可以根据发送对象的不同而采用多种不同的行为方式。
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多。
存在条件:
-
有继承关系
-
子类重写父类方法
-
父类引用指向子类对象
重写后,执行子类方法!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mgTUWbwx-1615354021900)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210217195209268.png)]
P.S. 多态是方法的多态,属性没有多态性。
不能实现多态:
- static 静态方法,属于类,它不属于实例。
- final 常量
- private 方法
instanceof关键字
System.out.println(X instanceof Y); //能不能编译通过看X和Y是否有父子关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XSi01VGH-1615354021903)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210217201742484.png)]
//类型之间的转换
子类转换为父类可能丢失一些方法
static关键字
静态字段和类一起加载,可以类名.变量
非静态方法可以直接访问类中的静态方法。
-
匿名代码块和静态代码块
静态代码块只执行一次
先执行静态代码块,然后是匿名代码块,最后构造方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PNw7nOdS-1615354021905)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210218120535653.png)]
-
静态导入包
import static java.lang.Math.random; import static java.lang.Math.PI; pubilc class Test{ public static void main(String[] args){ System.out.println(random());//No error System.out.println(PI); } }
抽象类:
abstract关键字
约束!
public abstract class Action{
pubilc abstract void doSomething();//抽象方法。只有名字,没有实现。
}
抽象类的所有方法,继承它的子类,都必须要实现它的方法,除非子类也是抽象类。
public class A extends Action{
@Override
public void doSomething(){
}
}
- 不能new一个抽象类,只能靠子类去实现;约束
- 抽象类中可以写普通方法
- 抽象方法必须在抽象类中
接口:
比较:
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有!
接口:只有规范!自己无法写方法,专业的约束!约束和实现分离。
接口的本质是契约。interface
接口就是规范,定义的是一组规则,体现了现实世界“如果你是…则必须能…”的思想。
接口中的所有定义其实都是抽象的public abstract
接口中所有定义的属性都是常量pubilc static final
(一般不用)
接口都需要有实现类
public interface UserService{
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
implements,实现了接口的类,就需要重写接口中的方法。
public class UserServiceImpl implements UserService{
@Override
public void add(String name){
}
@Override
public void delete(String name){
}
@Override
public void update(String name){
}
@Override
public void query(String name){
}
}
侧面实现多继承!
只有一个方法的接口叫做函数式接口,可以使用lambda表达式简化
内部类:
在一个类的内部再定义一个类。
通过外部类来实例化内部类,可以获取外部类私有字段。
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类(重点)
异常
程序运行中出现的不期而至的各种状况。
-
检查性异常
用户错误或问题引起的异常
-
运行时异常
可以在编译时被忽略
-
错误:不是异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w69Mx9nF-1615354021907)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210218155426412.png)]
五个关键字:
try catch throw throws finally :
throw手动抛出异常
throws方法抛出异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZsjAUkEa-1615354021908)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210218160504822.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lPwJTI6a-1615354021909)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210218160524718.png)]
自定义异常
只需继承Exception类。
步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理异常;否则在方法的声明处通过throws关键字指明要抛出给方法调用着的异常,继续进行下一步操作。
- 在出现异常方法的调用者中捕获并处理异常。
总结:
- 处理运行时异常时,采用逻辑合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能被遗漏的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切忌只是简单地调用printStackTrace()去打印输出
- 具体如何去处理异常,需根据不同的业务需求和异常类型决定
- 尽量添加finally语句块去释放占用的资源
Java高级特性
常用类
-
Object类
- hashcode()
- toString()
- clone()
- getClass()
- notify()
- wait()
- equals()
-
String类
不可变性 final
操作量较小
-
String Buffer
可变长 append();
多线程数据量较大
效率低,安全
-
String Builder
可变长
单线程数据量较大
效率高不安全
-
Math类
-
Random类
生成随机数 UUID
-
File类
- 创建文件
- 查看文件
- 修改文件
- 删除文件
-
包装类
自动装箱和拆箱
-
Date类
Date
SimpleDateFormat yyyy-MM-dd HH:mm:ss
Calendar(建议使用)
集合框架
Collection
-
list(有序可重复)
-
ArrayList
add/remove/contains/size
-
LinkedList
getFirst()
getLast()
removeFirst()
addFirst()
-
Vector
-
Stack
-
-
set(无序不可重复)
- HashSet(常用)
- TreeSet
-
Iterator:迭代器
Map
-
HashMap(重点,高频)
JDK1.7: 数组+链表
JDK1.8:hash表=数组+链表+红黑树
Collections工具类
泛型
<>约束,避免类型转换之间的问题
IO流
字节流
输入:InputStream
输出:OutputStream
字符流
Reader
Writer
节点流
CharArrayReader,Writer,InputStream,OutputStream
StringReader,Writer
pipe(管道流) PipedOutputStream
File(…)
处理流
-
buffer
bufferInputStream
bufferOutputStream
bufferReader
bufferWriter
-
data
DataInputStream
DataOutputStream
-
转换流
InputStreamReader
OutputStreamWriter
-
Filter
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
-
print
PrintWriter
PrintStream
-
object流
-
序列化 反序列化
多线程
进程(Process)和线程(Thread)
程序是指令和数据的有序集合,本身无运行含义,是一个静态概念。
进程则是执行程序的一次执行过程,它是一个动态的概念。是系统资源的分配的单位。
线程,一个进程中包含多个线程。
线程创建的方式
-
Thread class 继承Thread类(不推荐使用:避免OOP单继承局限性)
-
自定义线程类继承Thread类
-
重写run()方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
public class TestThread1 extends Thread{ @Override public void run(){ //run方法线程体 for(int i=0;i<20;i++) { System.out.println("我在看代码"+i); } } pubilc static void main(String[] args){ //main线程 主线程 //创建一个线程对象 TestThread1 testthread1 = new TestThread1(); //调用start()方法开启线程 testthread1.start(); //testthread1.run(); //run()方法不是开启线程 for(int i=0;i<2000;i++) { System.out.println("我在学习多线程") } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iOU6zdPl-1615354021910)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210222125848469.png)]
线程开启不一定立即执行,由CPU调度执行
-
P.S 利用多线程下载图片
准备commons-io放入lib中
package com.java;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//练习Thread,实现多线程下载图片
public class TestThread1 extends Thread{
private String url;
private String name;
public TestThread1() {
}
public TestThread1(String url,String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
Webdownloader webdownloader = new Webdownloader();
webdownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
}
public static void main(String[] args) {
TestThread1 t1 = new TestThread1("http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg","1.jpg");
TestThread1 t2 = new TestThread1("http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg","2.jpg");
TestThread1 t3 = new TestThread1("http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg","3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器
class Webdownloader{
//下载方法
public void downloader(String url,String name) {
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
}
catch(IOException e)
{
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B8hy2BaN-1615354021911)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210222142444870.png)]
Done!
-
Runnnable 实现Runnnable接口(推荐使用:避免单继承局限性,灵活。方便一个对象被多个线程使用)
-
定义MyRunnable类实现Runnnable接口
-
**实现run()**方法,编写线程执行体
-
创建线程对象,调用start()方法启动线程
传入目标对象+Thread对象.start()
-
public class TestThread2 implements Runnable{
@Override
public void run(){
//run方法线程体
for(int i=0;i < 200;i++){
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args){
//创建runnable接口的实现类对象
TestThread2 testThread2 = new TestThread2();
//创建线程对象,通过线程对象来开启我们的线程,代理
//Thread thread = new Thread(testThread2);
//thread.start();
//上面两句可以简化为下面一句
new Thread(testThread2).start();
for(int i=0;i<1000;i++){
System.out.println("我在学习多线程---");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aL5wZvKE-1615354021912)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210223121338511.png)]
P.S. 初始并发实例
买车票:
package com.java;
//多个线程同时操作同一个对象
//买火车票的例子
public class TestThread4 implements Runnable{
//票数
private int ticketNumbers = 10;
@Override
public void run() {
while (true) {
if(ticketNumbers<=0)
{
break;
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNumbers-- + "票");
}
}
public static void main(String[] args) {
TestThread4 ticket = new TestThread4();
new Thread(ticket,"小明").start();
new Thread(ticket,"小红").start();
new Thread(ticket,"黄牛党").start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ukIAAQ8-1615354021913)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210223125850321.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQSTWCxU-1615354021914)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210225114014753.png)]
每次运行结果不一。
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
案例:龟兔赛跑
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7e8DAhQn-1615354021915)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210223140251136.png)]
package com.java;
//模拟龟兔赛跑
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i=0;i<=100;i++){
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//如果比赛结束了,就停止程序
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if(winner!=null){
//已经存在胜利者了
return true;
}
{
if(steps==100){
winner = Thread.currentThread().getName();
System.out.println("Winner is" + winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4SN7RPx6-1615354021916)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210223142254519.png)]
DONE!
-
Callable 实现Callable接口
-
实现Callable接口,需要返回值类型
-
重写call方法,需要抛出异常
-
创建目标对象
-
创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交执行
Future<Boolean> result1 = ser.submit(t1);
-
获取结果:
boolean r1 = result1.get();
-
关闭服务:
ser.shutdownNow();
实现Callable接口来下载网络图片:
-
//实现Callable接口来下载网络图片:
package com.java;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean>{
private String url;
private String name;
public TestCallable() {
}
public TestCallable(String url,String name){
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
Webdownloader webdownloader = new Webdownloader();
webdownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("http://e.hiphotos.baidu.com/image/pic/item/a1ec08fa513d2697e542494057fbb2fb4316d81e.jpg","1.jpg");
TestCallable t2 = new TestCallable("http://c.hiphotos.baidu.com/image/pic/item/30adcbef76094b36de8a2fe5a1cc7cd98d109d99.jpg","2.jpg");
TestCallable t3 = new TestCallable("http://h.hiphotos.baidu.com/image/pic/item/7c1ed21b0ef41bd5f2c2a9e953da81cb39db3d1d.jpg","3.jpg");
//创建执行任务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果
boolean resurt1 = r1.get();
boolean resurt2 = r2.get();
boolean resurt3 = r3.get();
//关闭服务
ser.shutdownNow();
}
}
//下载器
class Webdownloader{
//下载方法
public void downloader(String url,String name) {
try{
FileUtils.copyURLToFile(new URL(url),new File(name));
}
catch(IOException e)
{
e.printStackTrace();
System.out.println("IO异常,downloader方法出现问题");
}}
}
callable的好处:
- 可以定义返回值
- 可以抛出异常
静态代理
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情
package com.java;
//结婚
public class StaticProxy{
public static void main(String[] args){
You you = new You();//你要结婚
//you.HappyMarry();本来的调用方法
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
//new WeddingCompany(new You()).HappyMarry();//一行取代上面两行
}
}
interface Marry{
void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{
@Override
public void HappyMarry(){
System.out.println("***要结婚了,超开心");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
//代理谁-->真实目标角色
private Marry target;
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry(){
before();
this.target.HappyMarry();//这就是真实对象
after();
}
public void before(){
System.out.println("结婚之前,准备婚礼");
}
public void after(){
System.out.println("结婚之后,收取尾款");
}
}
new Thread( () ->System.out.pirntln("***") ).start();//lambda表达式
静态代理,真实对象是Runnable接口
new WeddingCompany(new You()).HappyMarry();
即new Thread(Runnable).start();
线程底层实现模式!
Lambda表达式
函数式编程
好处:
- 避免内部类定义过多
- 代码简洁
- 去掉无意义代码,留下核心逻辑
函数式接口
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。
public interface Runnable{
public abstract void run();
}
对于函数式接口,可以通过Lambda表达式来创建该接口的对象。
推导lambda表达式,层层简化
package com.multi_thread;
/*
推导lambda表达式
*/
public class TestLambda1 {
//3.静态内部类(由于实现类放在外面十分麻烦)
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();//用接口new一个实现类
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda3");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda4");
}
};//这是语句,写分号;
like.lambda();
//6.用lambda简化
like = () -> {
System.out.println("I like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口
interface ILike{
void lambda();//在接口中隐式声明,自动abstract
}
//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda");
}
}
传参形式:
package com.multi_thread;
public class TestLambda2 {
//静态内部类
/*
static class Love implements ILove{
@Override
public void love(String a) {
System.out.println("I love gxx1-->"+a);
}
}
*/
public static void main(String[] args) {
//局部内部类
/*
class Love implements ILove{
@Override
public void love(String a) {
System.out.println("I love gxx2-->"+a);
}
}
*/
//匿名内部类
/*
ILove love = new ILove(){
@Override
public void love(String a) {
System.out.println("I love gxx3-->"+a);
}
};
*/
//lambda表达式
ILove love = (String a)->{
System.out.println("I love gxx4-->"+a);
};
//lambda表达式简化
// 简化1:去掉参数类型
love = (a)->{
System.out.println("I love gxx5-->"+a);
};
//简化2:简化括号
love = a -> {
System.out.println("I love gxx6-->"+a);
};
//简化3:去掉花括号
love = a -> System.out.println("I love gxx7-->"+a);
//总结:
//lambda只有一行代码才能去掉花括号,否则要用代码块包裹。
//前提是接口为函数式接口
//多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
love.love("forever");
}
}
interface ILove{
void love(String a);
}
//实现类
/*
class Love implements ILove{
@Override
public void love(String a) {
System.out.println("I love gxx-->"+a);
}
}*/
总结
-
lambda只有一行代码才能去掉花括号,否则要用代码块包裹。
-
前提是接口为函数式接口
-
多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号
P.S. Runnable是函数式接口
线程状态
五大状态:创建状态,就绪状态,运行状态,阻塞状态,死亡状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Q1oohYU-1615354021917)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210224131407622.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HCyCIcH-1615354021918)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210224163159999.png)]
线程方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIvoJHZl-1615354021918)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210224163326572.png)]
线程停止
- 建议线程正常停止–>利用次数,不建议死循环
- 建议使用标志位–>设置一个标志位
- 不要使用stop/destroy等过时或者JDK不建议使用的方法
package com.multi_thread.state;
//测试stop
//1. 建议线程正常停止-->利用次数,不建议死循环
//2.建议使用标志位-->设置一个标志位
//3.不要使用stop/destroy等过时或者JDK不建议使用的方法
public class TestStop implements Runnable{
//1.设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("run....Thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==999){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程停止了");
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2HX1xFdo-1615354021919)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210225131109672.png)]
***Q:***为什么900的时候线程已经停止,但是多次执行仍可能出现main999后仍有run…Thread???
线程休眠
- sleep(时间)指定当前线程阻塞的毫秒数
- sleep存在异常Interrupted Exception
- sleep时间达到后线程进入就绪状态
- sleep可以模拟网络延时,倒计时等
- 每一个对象都有一个锁,sleep不会释放锁
模拟网络延时:
package com.multi_thread.state;
//模拟网络延时:放大问题的发生性
public class TestSleep implements Runnable{
//票数
private int ticketNumbers = 10;
@Override
public void run() {
while (true) {
if(ticketNumbers<=0)
{
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了第" + ticketNumbers-- + "票");
}
}
public static void main(String[] args) {
TestSleep ticket = new TestSleep();
new Thread(ticket,"小明").start();
new Thread(ticket,"小红").start();
new Thread(ticket,"黄牛党").start();
}
}
模拟倒计时/打印系统当前时间:
package com.multi_thread.state;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep2 {
//模拟倒计时
public static void tenDown() throws InterruptedException {
int num=10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0) break;
}
}
public static void main(String[] args) throws InterruptedException {
// tenDown();
//打印系统当前时间
Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
while(true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
}
}
}
线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度,礼让不一定成功,看CPU心情!
package com.multi_thread.state;
//测试礼让线程
//礼让不一定成功,看CPU心情
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tSlggsvI-1615354021921)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210225133655243.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9jyC7vnn-1615354021922)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210225133624124.png)]
线程强制执行_Join
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
package com.multi_thread.state;
//测试join方法
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 1000; i++) {
if(i==200){
thread.join();//插队
}
System.out.println("main"+i);
}
}
}
//两交替执行,但是main200前join的线程一定执行完
观测线程状态
package com.multi_thread.state;
//观察测试线程的状态
public class TestState {
public static void main(String[] args) {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("///");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//NEW
//观察启动后
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//run
while(state != Thread.State.TERMINATED){
//只要线程不终止,就一直输出状态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
state = thread.getState();
System.out.println(state);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJaHEh0h-1615354021923)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210226142009355.png)]
总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WL4D4ZbU-1615354021924)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210226142132834.png)]
线程优先级
-
java提供了一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
-
线程优先级用数字表示,范围1~10
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
-
getPriority().setPriority(int xxx)
先设置优先级,后启动!
package com.multi_thread.state; //测试线程的优先级 public class TestPriority { public static void main(String[] args) { //主线程默认优先级 System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); Thread t5 = new Thread(myPriority); Thread t6 = new Thread(myPriority); //先设置优先级,再启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY);//10 t4.start(); //t5.setPriority(-1); //t5.start(); //ERROR! //t6.setPriority(11); //t6.start(); //ERROR! } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority()); } }
守护(daemon)线程
-
线程分为用户线程和守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
-
如,后台记录操作日志,监控内存,垃圾回收等待…
package com.multi_thread.state;
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true); //默认是false表示用户线程,正常的线程都是用户线程...
thread.start();//上帝守护线程启动
new Thread(you).start();//你 用户线程启动
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝保佑你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("我一辈子喜欢你");
}
System.out.println("=====goodbye world=======");
}
}
线程同步
多个线程操作同一个资源
**并发:**同一个对象被多个线程同时操作
形成条件:队列 + 锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XrKfoL0E-1615354021925)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210302112128156.png)]
三个线程不安全案例:
- 买票
package com.multi_thread.syn;
//线程不安全,有负数
//不安全买票
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station,"我").start();
new Thread(station,"你").start();
new Thread(station,"黄牛党").start();
}
}
class BuyTicket implements Runnable{
//票
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
//买票
while (flag)
{
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
//判断是否有票
if(ticketNums <= 0){
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CES0or4e-1615354021925)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210302121831170.png)]
- 银行取钱
package com.multi_thread.syn;
//不安全的取钱
//两个人去银行取钱
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account("结婚基金",100);
Drawing you = new Drawing(account,50,"你");
Drawing yourWife = new Drawing(account,100,"yourWife");
you.start();
yourWife.start();
}
}
class Account{
int money;//余额
String name;//卡名
public Account(String name,int money) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Account account;
//取了多少钱
int drawingMoney;
int nowMoney;
public Drawing(Account account,int drawingMoney,String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run(){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够,无法取钱");
return;
}
//sleep可以放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//余额
account.money -= drawingMoney;
//手里的钱
nowMoney += drawingMoney;
System.out.println(account.name + "余额为:"+account.money);
//因为继承Thread,所以this.getName()相当于Thread.currentThread().getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJwMF2pL-1615354021926)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210302121908741.png)]
- 列表
package com.multi_thread.syn;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
//同一时间存在线程往列表同一位置添加数据,被覆盖掉一些
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());//期待值为10000
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eNu5bVUz-1615354021927)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210302122041016.png)]
同步方法及同步块(synchronized):
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-20Ip0kBF-1615354021927)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210303091503134.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hNVGBqsx-1615354021928)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210303091556662.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4rAg7Ig-1615354021929)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210303091350516.png)]
同步方法锁的是类本身,同步块可以锁任何东西。
锁的对象就是变化的量,需要增删改的对象
修改不安全案例:
- 安全买票:
package Thread.demo05线程同步;
class BuyTicket implements Runnable {
private int ticktnumbers = 10;
boolean flag = true; // 外部停止方式
@Override
public void run() {
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// synchronized同步方法: 锁的是this
public synchronized void buy() throws InterruptedException {
if (ticktnumbers <= 0) {
flag = false;
return;
}
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "买了第" + ticktnumbers-- + "张票。");
}
}
public class UnSafeBuyTicket{
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
}
}
//结果
小明买了第10张票。
黄牛买了第9张票。
老师买了第8张票。
小明买了第7张票。
黄牛买了第6张票。
老师买了第5张票。
小明买了第4张票。
黄牛买了第3张票。
老师买了第2张票。
老师买了第1张票。
Process finished with exit code 0
- 安全的银行
package Thread.demo05线程同步;
import oop.demo05.A;
class Account{
int money; //余额
String name; //卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
// 银行:模拟取款
class Drawer extends Thread{
Account account;
int drawingMoney;
int nowMoney;
public Drawer(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
// 取钱的方法run加synchronized锁的是this,此处即为Drawer的实例对象
//所以不能在run上加synchronized,而应该给同步资源account加锁,因为增删改的对象是account
synchronized (account){
if(account.money - drawingMoney < 0){
System.out.println(Thread.currentThread().getName()+"正在取钱不够了,取不了");
return ;
}
try {
Thread.sleep(1000); // 放大问题的发生性
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 卡里的钱 - 取出的钱
account.money = account.money - drawingMoney;
// 手里的钱 = 手里的钱 + 取出的钱
nowMoney = nowMoney + drawingMoney;
System.out.println(account.name + "余额为:" +account.money);
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
}
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(200000,"结婚基金");
Drawer you = new Drawer(account,50000,"你");
Drawer wife = new Drawer(account,100000,"妻子");
you.start();
wife.start();
}
}
//
结婚基金余额为:150000
你手里的钱:50000
结婚基金余额为:50000
妻子手里的钱:100000
- 安全的列表
package Thread.demo05线程同步;
import java.util.ArrayList;
import java.util.List;
// 集合线程不安全
public class UnSafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
//同一时间存在线程往列表同一位置添加数据,被覆盖掉一些
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//System.out.println(list);
System.out.println(list.size());
}
}
//输出结果
10000
Q:代码3中当不延时结果仍旧小于10000.是否仍旧线程不安全?应该如何修改?
A:创建了线程要等CPU去调度才能执行,即lambda表达式里边的东西,这个时候若循环次数结束了,主函数即将输出sout函数,可能有些new线程没被cup调度,所以也就没有add进去,就被打印了
拓展:
测试JUC安全类型的集合(CopyOnWriteArrayList)
package com.multi_thread.syn;
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
//Q:为什么不延时仍小于10000
//A:这是子线程都启动了,但是还没运行玩,主线程for结束,cpu让主线程比他们快直接就输出了,
死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,导致两个或多个线程都在等待对方释放资源,都停止执行的情形。
产生死锁的原因:
(1) 因为系统资源不足。
(2) 进程运行推进的顺序不合适。
(3) 资源分配不当等。
如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
死锁的解除与预防:
理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源。因此,对资源的分配要给予合理的规划。
示例:
package com.multi_thread.syn;
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持。
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
public Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
//化妆: 互相持有对方的锁
private void makeup() throws InterruptedException {
if(choice==1){
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁。");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.girlName+"1s钟后获得镜子的锁。");
}
}
}else{
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁。");
Thread.sleep(2000);
synchronized (lipstick){
System.out.println(this.girlName+"1s钟后获得口红的锁。");
}
}
}
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(1,"灰姑凉");
Makeup g2= new Makeup(2,"白雪公主");
g1.start();
g2.start();
}
}
出现死锁:
解决:不让锁中抱对方的锁
package com.multi_thread.syn;
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
public Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
//化妆: 互相持有对方的锁
private void makeup() throws InterruptedException {
if(choice==1){
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁。");
Thread.sleep(1000);
}
synchronized (mirror){
System.out.println(this.girlName+"1s钟后获得镜子的锁。");
}
}else{
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁。");
Thread.sleep(2000);
}
synchronized (lipstick){
System.out.println(this.girlName+"1s钟后获得口红的锁。");
}
}
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(1,"灰姑凉");
Makeup g2= new Makeup(2,"白雪公主");
g1.start();
g2.start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CZWc3qn6-1615354021932)(C:\Users\doke\AppData\Roaming\Typora\typora-user-images\image-20210304201719393.png)]
Done!
Lock锁:
package com.multi_thread.syn;
import java.util.concurrent.locks.ReentrantLock;
//测试Lock锁
public class TestLock {
public static void main(String[] args) {
TestLock2 testLock2 = new TestLock2();
new Thread(testLock2).start();
new Thread(testLock2).start();
new Thread(testLock2).start();
}
}
class TestLock2 implements Runnable{
int ticketNums = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();//加锁
if(ticketNums>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}else{
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
线程协作
线程通信
-
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
-
wait(long timeout) 指定等待的毫秒数
-
notify() 唤醒一个处于等待状态的线程
-
notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程先调度
注:上述方法都是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出IIIegalMonitorSateException异常
生产者消费者问题(Producer-consumer problem)
该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”,在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
解决方式1-管程法
生产者消费者模型是一个并发协作的模型:
生产者:负责生产数据的模块(可能是方法、对象、线程、进程)
消费者:负责处理数据的模块(可能是方法、对象、线程、进程)
缓冲区(仓库):消费者和生产者之间通信的桥梁,生产者将生产好的产品放入缓冲区,消费者从缓冲区中取出产品
package com.multi_thread.syn;
//测试:生产者消费者问题-->利用缓冲区解决:管程法
//生产者,消费者,产品,缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container){
this.container = container;
}
//生产
@Override
public void run(){
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container){
this.container = container;
}
//消费
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->"+container.pop().id+"只鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer {
//需要一个容器大小
Chicken[] chickens = new Chicken[10];
int count = 0;
//生产者放入产品
public synchronized void push(Chicken chicken) {
//如果容器满了,就需要等待消费者消费
if (count == chickens.length) {
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() {
//判断能否消费
if (count == 0) {
//等待生产者生产,消费者等等
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者继续生产
this.notifyAll();
return chicken;
}
}
解决方式2-信号灯法
package com.multi_thread.syn;
//测试:生产者消费者模型-->标志法
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者--演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(i%2==0){
this.tv.play("快乐大本营");
}else{
this.tv.play("抖音纪录片");
}
}
}
}
//消费者-观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
this.tv.watch();
}
}
}
//产品--节目
class TV{
String voice;
boolean flag = true;
//表演
public synchronized void play(String voice){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+ voice);
this.notifyAll(); //通知观众观看
this.voice = voice;
this.flag = !this.flag;
}
//观看
public synchronized void watch() {
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看了:"+voice);
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
package com.multi_thread.syn;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//测试线程池
public class TestPool {
public static void main(String[] args) {
// 1.创建服务,创建线程池
// newFixedThreadPool(10); 参数是线程池的大小
ExecutorService service = Executors.newFixedThreadPool(10);
// 2. 执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//3. 关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}