javaSE总结
开发环境搭建
常用dos命名及系统快捷指令
-
dos 命令
-
键盘
-
打开运行窗口
-
常见命令
-
win + r //打开cmd
-
ipconfig //查看ip
-
cls //清屏
-
ping //查看ip 通否
-
p盘符:去对应的盘符
-
cd javase 去 javase 目录
-
-
系统常见快捷键
- win + e 打开我的电脑
- win + d 快速回到桌面
- win + L 锁屏
- alt + tab 切换窗口
-
jdk安装
-
jdk 安装
环境变量
-
环境变量的配置
-
此电脑-右键属性-高级系统设置-环境变量-配置 JAVA_HOME-配置path-%JAVA_HOME%\bin
-
测试 java -version javac -version
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWkOEFtU-1664677606209)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220305114633055.png)]
-
jdk 与 jre 关系
- jdk: 它是java的开发运行环境,开发人员需要安装jdk
- jre: java runtime environment(java 运行环境),如果只需要运行程序,不需要开发,可以只安装jre
- jdk 包含了 jre
java 加载和执行的过程
-
图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRjcpIud-1664677606211)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220305115710659.png)]
-
java 源文件:程序员编写的,扩展名是以.java 结尾的
-
编译:通过 javac 把java源文件编译成字节码文件(.class结尾)
-
运行:通过解释器(java)运行字节码文件
-
需要用到 dos 命令
基础语法
Java注释,关键字,标识符
java 注释
- 通常我们需要在源代码中添加文字去描述我们代码的作用,做一个解释说明,直接写就会报错,所以我们就可以用java 的注释来解决这一问题
- java 提供了三种注释方式:
- 单行注释 //注释内容
- 多行注释 /* 注释内容 */
- 文档注释 /** 注释内容 */
关键字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vHgeN57h-1664677606211)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220305140547701.png)]
- 关键字是我们 JAVA语言一些特殊的字母,具有特殊含义,java 中关键字是小写字母开头
标识符
-
标识符就是名称的意思,定义类,包,方法,变量名,目的是有一个号的命名。
-
标识符的组成:
- 英文的大小写字母 a-z A-Z
- 数字 0-9
- 符号 _ 与 $
-
标识符规则
-
数字不能打头
-
不可以使用关键字
-
严格区分大小写,做到见名知意
-
驼峰命名法(类名第一个单词的首字母大小,其它单词的首字母也大小)
public class StudentTest{ //驼峰命名法 }
-
数据类型
基本数据类型
四类 | 八种 | 字节数 | 数据范围 |
---|---|---|---|
整型 | byte | 1 | -128-127 |
short | 2 | -32768~32767 | |
int | 4 | -2147483648~2147483648 | |
long | 8 | -263~263-1 | |
浮点型 | float(f,F) | 4 | -3.403E–3.403E |
double(d,D) | 8 | ||
字符型 | char | 2 | ‘a’,‘A’,‘1’ |
布尔型 | boolean | 1 | true,false |
引用数据类型
- String (字符串)
- 其它的引用数据类型(数组,类)
基本数据强制类型转换
-
需求:定义一个byte 类型的数,并且给一个值为300;
-
问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HFlyADNa-1664677606212)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220305164005821.png)]
-
解决:强制类型转换前面加上(数据类型)
byte b = (byte)300;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EcUr4UGX-1664677606213)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220305164131467.png)]
变量
定义格式:
数据类型 变量名 赋值符号 数值
int num = 10;
常量
定义:永远不会变的量,10,10.3,1000L
public class ConstantDemo{
public static void main(String[] args){
System.out.println(10);
//之前定义的变量,前面加 final 关键字以后就变成常量
final int num = 10;
}
}
注意:使用 final 修饰的变量也是常量,并且不能重新赋值
运算符
算术运算符
-
运算符用来计算数据的,数据可以是常量,也可以是变量,被我们运算符操作的数,我们称之为操作数
-
算术运算符+、-、*、/、%、++、–
运算符 运算规则 例子 结果 + 正号 +10 10 + 加法 10+12 22 + 连接符 “名字:”+“上云” 名字:上云 - 负号 -10 -10 - 减法 20 - 10 10 * 乘法 10*2 20 / 除 6/3(5/2) 2 % 取模 5%2 1 ++ 自增 int a = 1;a++/++a; 2 – 自减 int b = 2; b–/–b; 1 注意:
- 前置++ 先+1 后运算(后做操作),后置++ 先操作后+1;
- 前置-- 变量先-1,后操作,后置-- 先操作,变量后-1;
赋值运算符
-
赋值运算符,用来为变量赋值的。=、+=、-=、*=、/=、%=
运算符 运算规则 例子 结果 = 赋值符号 int a = 10; 10 += 加后赋值 int a = 10; a+=2;(a = a+2) 12 -= 减后赋值 int a = 10; a-=2; 8 *= 乘后赋值 int a = 10;a*=2; 20 /= 除后赋值 int a = 10;a/=5; 2 %= 取模后赋值 int a = 10;a%=3; 1
关系运算符
-
关系运算符又叫比较运算符,用来判断两个操作数大小关系,以及是否相等,结果是我们boolean 类型,true false
运算符 运算规则 例子 结果 > 大于 5 > 3 true >= 大于或者等于 5>=5 true < 小于 5<3 false <= 小于等于 3<=4 true == 等于 3==3 true != 不等于 3!=4 true -
注意:赋值运算的 = 和关系运算符的 == 是有区别的,= 是做赋值, == 是做判断比较。
int a = 5; int b = 10; System.out.println( a == b); System.out.println(a = b);
逻辑运算符
-
逻辑运算符,用与 boolean 类型的值进行运算比较的。最终结果 true 或者是 false(按住 shift + 对应符号)
运算符 运算规则 例子 结果 & 与(两者为真) true & false false | 或(有一个真即为真) true | false true ! 非(取反) !true false ^ 异或(两者不同即为真) true ^ false true && 短路与 false && true false || 短路或 true || false true ^ 异或做二进制运算 二进制无进位相加
条件运算符
boolean ? 值1:值2;
true?"你好":"不好";
位运算符
当你使用数字操作位运算符时,是使用二进制操作的。
位运算符(1=true,0=false):
-
&(与) 1&1=1,1&0=0
-
|(或)1|1=1,1|0=1,0|0=0
-
^(异或) 11=0,10=1,0^0=0
-
〜(取反) 1=0,0=1(二进制元素位)
0000 0001 1111 1110
-
<< (左移)左移一位相当于乘以2,右移一位相当于除以2
0000 0001 <<2 0000 0100 >>1 右移 0000 0010 >>1 0000 0001 无符号右移 >>>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DUgdJARq-1664677606214)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220327012537641.png)]
条件与循环语句
选择结构 if
-
if 的定义
if(boolean){ //如果条件是 true执行代码块 } //需求 如果考试得了一百分,奖励手机
-
if…else 语句
//定义 if(boolean){ //语句块1 }else{ //语句块2 } //如果条件满足,执行语句块1,否则执行语句块2 //需求:如果考试得100奖励一个手机,否则,家庭作业翻倍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kjnnU31O-1664677606215)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220306153110197.png)]
-
if … else if… else if …else(只选其中一条路走)
//语法定义 if(boolean){ //语句块1 }else if(boolean){ //语句块2 }else if(boolean){ //语句块3 }else{ //语句块4 } //需求:考90分以上 等级A,[80-90) B [70-80) c [0-70)d
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYaYDYRh-1664677606216)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220306153125865.png)]
注意:最多只有一个分支执行,如果有 else 那一定分支执行
选择结构 Switch
-
定义
switch(表达式){ case 目标值: 执行语句 break; case 目标值: 执行语句 break; case 目标值: 执行语句 break; default: 执行语句 } //需求:int week = 1;1 打印星期1,2打印星期2 7打印星期天
循环结构
while 循环
-
循环:重复相同的步骤
//定义 while(boolean){ 代码块 } //需求1:播放音乐10次 //需求2:定义一个变量,从0开始,当变量小于5的时候,计算变量之间的和
do…while
-
先做一次,再判断
do{ 语句块 }while(boolean); //需求:不管你之前听音乐听多了多次,当我判断的时候都要一次 播放音乐<10;
for 循环
-
for 循环的定义
for(表达式1;表达式2;表达式3){ 语句 } for(①;②;③){ ④ } ⑤ 执行流程 第一步:先执行① 第二步:其次② 第三步:执行④ 第四步:执行③ 第五步:回到② 最后: 执行5 //需求:求1-5的和
嵌套循环
特点:最外层的循环执行一次,里面全部执行完,再继续去执行最外层的代码。
for(表达式1;表达式2;表达式3){
for(表达式1;表达式2;表达式3){
语句
}
}
//用 * 打印一个直角三角形
break
-
在switch条件语句和循环语句中都可以使用break语句。当它出现在switch条件语句中时,作用是终止某个case并跳出switch结构。
- 需求:定义一个变量num = 1,当num 大于10的时候,循环停止
-
标记语法
可以指定break 跳转到某一层循环
continue
- 终止本次循环,继续往下执行
- 需求:对1~100之内的奇数求和
注意:continue 是停止本次循环,继续执行下一次循环,而break 是整体把循环结束
数组
- 在生活中,会遇到如下场景,需求:统计班级学生数量,计算学生的平均年龄,找出学生最老的,找出学生最小的。
- 假如我们班有35个人,需要35个变量来表示我们学生的年龄,这样做很麻烦而且很臃肿。
- 数组的概念:一堆数的组合。20,21,23,20
- 数组中 length 属性,就是数组的长度
- 数组的索引是从 0 开始的,也就是数组的下标,最大值是数组长度 -1,也就是 length -1
- 数组长度一但固定,就不可变
数组的定义
-
动态定义:数组初始化时,数组元素是空的,需要我们重新赋值才有数据。
数据类型[] 数组名称 = new 数据类型[元素个数]; //样例 int[] ages = new int[35]; //赋值 数组名称[对应的索引]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zZQcW1zB-1664677606216)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220307101151566.png)]
-
静态定义:数组初始化的时候,就已经确定各索引元素的值
//方式1 数据类型[] 数组名称 = new 数据类型[]{数据1,数据2,数据3}; //方式2 数据类型[] 数组名称 = {数据1,数据2,数据3};
-
获取值,或者赋值
//给数组元素赋值 数组名称[索引] = 值; //获取值 数组类型 变量名 = 数组名称[索引];
- length:数组的长度
- 索引:从0开始,比length 少1
数组的遍历
-
再操作数组时,经常需要去拿元素取元素,这种操作就是数组的遍历。
-
遍历的语法
//for 循环 快捷键 fori for(int i =0;i<arr.length; i++){ } //foreach 遍历 数组变量名.iter for(数据类型 变量名:数组名称){ }
-
foreach 底层其实还是使用我们的for 循环
数组的特点
-
常见问题
-
索引越界异常(没有再索引范围内)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rfh6lN67-1664677606217)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220307110620888.png)]
-
数组未初始化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ynTnaK9b-1664677606217)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220307110718437.png)]
-
空指针异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7kQUz5DQ-1664677606218)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220307110801634.png)]
-
-
数组元素存储的特点
-
数组的元素数据类型必须一致(char 有 ASII码表对应)
-
数组元素连续,空间大小一致,并且内存地址连续,呈现线性结构
-
数组长度固定之后,不可改变
-
数组不仅可以存储基本数据类型,还可以存储引用数据类型,数组本身就是引用数据类型
String[] strs = {"1","2","3"};
-
-
优缺点
- 优点
- 根据索引去获取访问元素(快)
- 能存储较多的数据
- 缺点
- 数组的长度固定,不可改变(超过容量增加数组元素时,只能用新数组代替)
- 只能存储一种数据类型
- 删除很慢,根据内容找索引很慢
- 优点
数组的拷贝及其扩容
-
需求:原数组不够放,新增加了元素,只能考虑扩容和拷贝
//原来5个同学的年龄 int[] ages = {12,13,21,19,23}; //新加入一个同学 18; int[] newAges = new int[ages.length + 1]; for (int i = 0; i < ages.length; i++) { newAges[i] = ages[i]; } newAges[newAges.length - 1] = 18; for (int newAge : newAges) { System.out.println(newAge); }
-
需求2:从源数组某一个位置开始拷贝,拷贝某一个长度到目标数组里面去
//从源数组的第2个元素,拷贝3个元素到新数组里面去 //需求2的实现13,21,19 int[] ages2 = {12,13,21,19,23}; int[] newAges2 = new int[3]; //第二个元素 int srcPos = 1; //拷贝多长 int index = 3; //目标数组的起始位置 int destPos = 0; for (int i = srcPos; i < srcPos + index; i++) { //新数组从 destPos 开始拷贝,每次递增 newAges2[destPos++] = ages2[i]; } System.out.println("-----"); for (int newAge : newAges2) { System.out.println(newAge); }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ksW5C9oi-1664677606219)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220307113933106.png)]
数组的工具类 Arrays 简单了解
-
需求:int[] ages = {1,3,4,5,6}; 打印输出的时候,[1,3,4,5,6];
int[] ages = {1, 3, 4, 5, 6}; //最后字符串结果 String ret = "["; for (int i = 0; i < ages.length; i++) { //如果当前元素是最后一个元素,就不添加, if (i == ages.length - 1) { ret += ages[i] +"]"; } else { ret += ages[i] + ","; } // ret += i == ages.length - 1?ages[i] +"]":ages[i] + ","; } System.out.println(ret);
-
Arrays工具类
int iMax = ages.length - 1; String str = "["; for (int i = 0; ; i++) { str += ages[i]; if(i == iMax){ str+="]"; break; } str+=", "; }
方法
-
生活中的方法:处理某件事或者解决某个问题的办法。
-
java中的方法:用来解决某件事情或者实现某个功能的办法。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eFKK2MOb-1664677606220)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220308105158097.png)]
-
方法的定义
修饰符 返回值类型 方法名(形式参数){ 方法体 } //实现从老代码迭代成新代码 //修饰符 方法无返回 方法名 参数列表 public static void print(String str) { }
-
带返回类型的方法定义
修饰符 返回值类型 方法名(形式参数){ 方法体 return 返回值类型; }
-
return 的作用:结束当前的方法,可以单独用,还可以带返回类型用
-
返回类型:
- 八大基本数据类型
- 引用数据类型
- void(没有返回)
-
需求:定义加法计算器,带两个参数的,方法返回类型为 int
public static int add(int num1,int num2){ int ret = num1 + num2; return ret; }
-
需求:求5个数的和,返回类型 int
public static int addSum(int... nums) { int sum = 0; for (int num : nums) { sum += num; } return sum; }
-
注意:方法的可变参数底层实际上就是数组
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZkI8zsCU-1664677606221)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220308105125455.png)]
方法的重载
-
需求1.0:
- 定义一个求浮点型 double 的计算器
- 定义方法分别去求 float 计算器
- 定义方法分别去求 short 计算器
- 定义方法区求 String 字符串的拼接
-
需求2.0
- 定义打印 String 类型的字符串方法
- 定义打印 int 类型的方法
-
方法的重载:
- 定义:同一个类里面,方法允许存在一个以上的同名方法,要求参数列表不同
- 参数列表不同:
- 1.参数类型不同
- 2.参数顺序不同
- 3.参数个数不同
- 和返回值类型无关
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FBwASDZJ-1664677606221)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220308111100303.png)]
方法的递归
-
定义:方法自己调用自己
-
需求:
-
不使用递归计算 5 的阶乘
int num = 5; int ret = 1; for (int i = 0; i < 5; i++) { ret *= num--; } System.out.println(ret);
-
使用递归计算5的阶乘
public static int recursion(int num) { if (num == 1) { return 1; } int recursion = recursion(num - 1); int ret = num * recursion; return ret; }
-
类的定义
-
成员变量(字段)
-
方法
-
定义格式
public class 类名{ 0-N个字段(成员变量) 0-N个方法; }
对象的创建和使用
-
创建方式 类名 类变量名 = new 类名();
Student stu = new Student();
-
给对象成员变量设置值 类变量名.字段名=值
student.name="上云";
-
获取成员变量的值,数据类型 变量名 = 类变量名.字段名
String name = student.name;
-
实例方法的调用,类对象名.方法名
student.showInfo();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5cLUsZkM-1664677606222)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220308160331376.png)]
构造方法
-
也叫构造器(构造方法),是用来创建对象的,当你创建对象时,一定执行
-
构造方法:
- 也有修饰符
- 不具备方法的返回值类型
- 可以带参数
- 默认的构造方法,里面没有任何内容
- 构造方法可以重载
-
构造方法,创建对象时,可以传参
Book book = new Book("E3层");
-
注意:不提供构造方法时,默认提供一个不带参数的构造器
实例方法
-
定义方式:
- 具有修饰类型
- 有返回值类型
- 有方法名和参数列表
-
定义:
修饰符 返回值类型 方法名(参数列表){ }
-
调用方式,使用对象调用。
对象变量名.方法名
-
空指针异常(没有去创建对象,就是调用对象的属性或者是方法)
导包
- 如果存在重名的情况,需要去选择自己的包 alt + enter
static 关键字
-
static:是一个修饰符,表示静态的,可以用来修饰,方法、字段、代码块、内部类,最优先加载进内存的。
-
注意:
-
static 关键字,表示该资源属于类(而不是属于类对象)。只要使用 static 修饰的,直接使用类名.来调用,
不用创建对象
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GuaCq5IF-1664677606223)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309103935720.png)]
-
在非 static 方法中,可以去访问 static 方法,但是最好用类名.来调用。
-
在 static 方法中,不能直接访问普通方法
-
静态代码块优先于一切先执行
static { System.out.println("static 修饰的代码块"); }
-
-
什么时候使用 static 修饰的字段以及方法和代码块
- 在开发中,写工具类的时候。
- 资源加载,加载配置文件(mysql jdbc)
深入变量
变量的定义语法:
数据类型 变量名 = 值;
根据位置不同,分为两大类:
- 成员变量
- 类成员变量(类名.字段名调用)
- 生命周期:从类加载到程序结束
- 使用 static 修饰的,直接定义到类里面的
- 实例成员变量
- 生命周期:从创建对象开始到GC垃圾回收器回收垃圾结束
- 直接定义到类里面的
- 类成员变量(类名.字段名调用)
- 局部变量
- 生命周期:从变量定义开始,到最近的花括号结束}
- 方法内部的变量
- 参数列表
- 代码块里面的变量
- 什么时候使用成员变量,什么时候使用局部变量?
- 考虑变量的生存时间(影响内存的开销)
- 能减少作用域都去减少(减少内存开销)
- 定义工具类时(static 用起来比较方便)成员变量封装好,利于我们方法使用
package
-
类的全限定类名(反射使用)
-
sy.coder.VariableDemo.VariableDemo
-
-
有的需要导包,有的不需要导这是为啥?
- java.lang java 语言核心包,里面的类直接使用,不需要手动导包
- java.util java工具包,需要手动导包(Arrays)
封装
-
没有封装会有什么问题
-
是用封装,做到了信息隐藏。
-
什么是封装?
- 把对象的状态和行为看成了一个统一的整体,放到一个独立的模块中(类)
- 做到信息隐藏,把不需要外界看到的信息隐藏起来(private进行私有化),向外提供方法,保证外界的安全访问。
-
封装的好处和意义:
- 提供了代码的复用性(可以减少重复代码)
- 使用者可以正确操作,方便使用系统功能
- 把实现细节隐藏起来,提供了安全性
访问控制权限修饰符
-
封装的目的,有些类让另一些类看不到里面再做什么事情,所以提供了访问控制权限修饰符来解决。
-
访问权限修饰符
修饰符 类内部 同一个包 子类 任何地方 public √ √ √ √ protected √ √ √ 缺省 √ √ private √
封装的实现
-
规范
-
遵循 javabean 规范
-
set 后面的单词采用驼峰命名法,并且使用原单词 setAge(…)
-
get getAge();
private int age; public void setAge(int a){ age = a; } public int getAge(){ return age; }
-
-
变量:就近原则
-
this 关键字:代表当前对象的引用
-
需求:建一个学生类,name ,age,address 提供set get 方法。
public class Student { private String name; private int age; private String address; public Student(){ } public Student(String name){ this.name = name; } public void setName(String name){ this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } public void setAddress(String address){ System.out.println(this); this.address = address; } public String getAddress(){ return address; } }
-
-
对于私有属性传参
- 使用 set get
- 使用构造器
-
注意:
- static 静态的不能使用this 关键字,默认get 如果你不写 this,底层也会给我们加上 this
- this 关键字也可以再构造器里面使用
引出继承
父类:存储共性(状态,行为)
子类:存放自己的特性
继承的作用:代码复用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2EMf8es7-1664677606223)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309153230595.png)]
继承思想
定义:基于某个父类对其进行拓展,定义新的子类,子类可以继承父类原来的属性和行为,并且可以增加父类没有的特性,或者覆盖父类中的某些特性
继承关系:is a 用是造句
继承的基础语法:
使用 extends 关键字
public class 子类类名 extends 父类类名{
写自己的特征
}
注意:
- java 只支持单继承,允许多重继承(一个类只能有一个直接父类,但是可以有多个间接父类)
- 任何类都是 Object 的子类
创建对象时,构造方法如何执行
- 先执行父类构造器(先执行父类静态代码块,再执行子类静态代码块)
- 子类构造器
- set get 方法
- 创建对象时,创建的是谁,打印 this 对象就是谁(多态)
重写
- 重写:当父类特征不能满足子类特征的时候,可以对父类的方法进行重写
- 要求:
- 子类的访问修饰符 >= 父类本身
- 父类不能使用 private 修饰
- 方法返回类型,子类 <= 父类
重写和重载的区别
-
没有任何关系,只不过因为名字看起来相同,所以就拿来对比
-
重载:发生在同一个类中,方法名相同,参数列表不同,和方法的返回类型无关
-
重写:override
-
重载:overload
- 重载:解决了一个类中,相同功能方法名不同的问题
- 重写:解决子类继承父类,父类方法满足不了子类要求时,需要在子类里面重写
抽象类
- 需求:求圆(Circle),矩形(Rectangle)的面积
- 使用 abstract 关键字修饰,并且没有方法体
- 必须使用 abstract 关键字修饰,方法没有方法体,留给子类去实现/重写
- 不能使用 private 以及 static 和 final
- 抽象方法必须定义到抽象类中或者接口
- 使用 abstract 修饰的类特点:
- 不能实例化
- 抽象类可以有普通的方法
- 抽象类构造器不能够私有化
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7hTb4XlE-1664677606224)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309180504490.png)]
接口
-
什么是接口
- 硬件接口:两个设备之间的连接方式,包含数据传输协议
- type-c,usb,耳机接口
- 软件接口:程序代码,是一种规范
统一接口后的意义:根据规范设计产品,可以做到适配性。
- 买鼠标不会去关心是哪家厂商的,是什么接口
- 硬件接口:两个设备之间的连接方式,包含数据传输协议
JAVA 接口
-
定义:使用 interface 关键字
//使用 interface 代替传统的 class public interface 类名{ //都是抽象方法 void usb(); //定义常量 public static final int EANBLE = 1; }
-
注意:
-
接口里面只能去定义抽象方法
-
抽象方法默认提供了 public abstract 修饰符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ATmH0D1-1664677606224)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309181348651.png)]
-
接口不能实例化(和抽象类一样)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E7hyMjie-1664677606225)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309181430891.png)]
-
-
接口怎么去实现
-
使用 implements 关键字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VNSdZ5qj-1664677606225)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220309181846592.png)]
-
-
接口支持多实现,分别把每个接口的抽象方法都去实现了。
-
接口的多继承
抽象类和接口怎么选择
- 都可以使用
- 如果你不需要具体的实现时,就用接口。
- 如果说你除了定义规范还有一定的功能,你就用抽象类
模板方法设计模式
- 需求:统计不同操作的时间用时。
- 使用 String 拼接一万次
- 使用 int 累加 一万次
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MwDVg2VK-1664677606226)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\day09\模板方法设计模式.png)]
Object 类
Object 是所有类的超类。
可以通过Ctrl + H 查看类继承结构观察
Object 类的常用方法:
-
toString() 打印当前对象(直接打印对象就是打印对象的 toString 方法)
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
getClass() 获取类的字节码对象
public final native Class<?> getClass(); aClass.getName()//获取类的权限定类名
-
hashCode(); 获取对象的 哈希值,存到HashTables
public native int hashCode();
-
finalize() 当对象不被引用时,要被 gc 不定时回收,gc 去调用这个方法(程序员是不用自己调用,自动调用)
protected void finalize() throws Throwable { }
-
equals() 方法,比较是否是同一个对象
public boolean equals(Object obj) { return (this == obj);//== 比较的是地址值 }
-
equals 方法 和 == 区别面试题
- == 比较地址值
- equals 没有重写之前,比较的也是地址值
- 一般我们都会重写,只关心内容而不关心地址值
-
注意:
- 如果 equals 方法没有重写时,== 和equals 方法没有区别
- 如果 equals 方法重写,我们只关心内容而不关心地址值
多态
-
什么是多态?为啥产生多态:学生是学生,学生也是人 is a
-
语法:
public class Student extends Person{ } public class Person{ } Student stu = new Student(); //多态 父类 变量名 = new 子类(); Person p = new Student();
-
通过实现接口或者继承类,都可以产生多态
-
在实际开发中,要求面向(多态)接口编程
-
多态调用,如果父类(接口)没有子类的字段或者方法,也不能去调用
-
多态类型转换
Person p = new Student(); 真实类型 变量名 真实类型 变量名 Student stu = (Student) p;
-
面向多态(接口)编程的意义:
- 不需要面向具体
- 多态的作用是降低程序耦合度,提高程序扩展力
super 关键字
-
需求:子类对象重写父类方法时,调用的还是父类对象的方法
-
使用 super 关键字解决
@Override public void study() { super.study(); System.out.println("学生在学习"); }
内部类
-
成员内部类(类里面存在新的类)
public class 外部类名{ public class 内部类名{ } }
- 访问方式
- 外部对象 变量名 = new 外部对象();
- 外部类名.内部类名 内部类变量名 = 变量名.new 内部对象();
- 访问方式
-
匿名内部类(底层自动去帮我们生成了一个实现类)
public interface 接口名{ void user(); } 接口名 变量名 = new 接口名(){ public void user(){ //具体逻辑 } }
-
局部内部类(不要去使用)局部代码块里面的类
匿名对象
-
匿名对象:不给对象一个变量名
Student stu = new Student(); //不需要变量名 new Student();
-
作用和意义:少开辟空间,可以有效节约空间(GC可以不定期的回收)
代码块
-
静态代码块
//类中直接存在 static{ }
-
构造代码块
{ //构造代码块 }
-
局部代码块
public static void main(String[] args) { Test test = new Test(); { } }
- fori 典型的局部代码块
执行顺序
- 需求:写一个类继承另一个类,每一个类提供静态代码块,构造代码块,构造方法,普通方法,静态方法。
- 执行顺序:
- 父类静态代码块 -> 子类静态代码块 ->父类的构造代码块 ->父类的构造器 ->子类的构造代码块 -> 子类的构造器 ->子类的普通方法
- 注意:
- 构造代码块使用并不是很多,可以用构造器代替
- 子类不能够重写父类的 static 方法(static 修饰的是数据类,而不是对象)
- 静态方法和普通方法没有必然的先后顺序,根据我们的调用顺序决定
javaSE基础提高
常用类
System 系统类
-
说明:其它老师讲的时候,会给一个 api 文档,但是我不会给(在职的人不去看)跟着源码学习,不看 api
-
常用方法:
//学习数组的时候,自己写过数组拷贝的代码 public static native void arraycopy(Object src, int srcPos,Object dest, int destPos, int length); //查询当前系统时间 System.currentTimeMillis(); public static native long currentTimeMillis(); //垃圾回收器 public static void gc() { Runtime.getRuntime().gc(); }
字符串类(String)
-
构造器 -> 字段 -> 方法
-
构造器
//无参构造器 public String() { //做了数组初始化 this.value = new char[0]; } //带一个参数 public String(String original) { //给char 数组赋值 this.value = original.value; //缓存String 的 hash 值 this.hash = original.hash; } //传递 char 类型数组 public String(char value[]) { this.value = Arrays.copyOf(value, value.length); }
-
字段
/** The value is used for character storage. */ //存储字符的数组 private final char value[]; /** Cache the hash code for the string */ //缓存 hash code private int hash; // Default to 0
-
方法
//查看字符串长度 public int length() { return value.length; } //判断是否为空 public boolean isEmpty() { return value.length == 0; } //返回一个索引对应字符值 public char charAt(int index) { return value[index]; } //判断是否包含一个字符 public boolean contains(CharSequence s) { return indexOf(s.toString()) > -1; } //获取byte[] public byte[] getBytes() { return StringCoding.encode(value, 0, value.length); } //以什么开头 public boolean startsWith(String prefix) { return startsWith(prefix, 0); } //以什么结尾 public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); } //equals 方法 equals(Object obj) //以什么什么断开 public String[] split(String regex) { return split(regex, 0); } //去掉空字符串 “ abc ” public String trim() {}
-
需求练习
-
创建一个字符串 String str = " 我爱学习-java"
-
判断是否为空字符串
-
通过 “-” 分割字符串
-
去除字符串两端的空格
-
判断字符串是否包含 java
String str = " 我爱学习-java"; boolean empty = str.isEmpty(); System.out.println("当前字符串是否为空:"+empty); String[] split = str.split("-"); System.out.println(Arrays.toString(split)); String trim = str.trim(); System.out.println(trim); boolean isContains = str.contains("java"); System.out.println("当前字符串是否包含java:"+isContains);
-
-
创建两个字符串
- String str = “sy”;
- String strNew = new String(“sy”);
- 比较内容时怎么比较?
- 怎么判断这两块空间不是同一块?(javap -verbose xx.class)
- 画出String 创建的一个流程图?
-
字符串类可以被继承吗?
StringBuilder(字符缓冲区)
-
StringBuilder 也是final 修饰的不可以不继承
-
里面的 char[] 数组不是使用 final 修饰的,可以替换
-
构造器
//默认的数组长度为16 public StringBuilder() { super(16); } //指定容量 public StringBuilder(int capacity) { super(capacity); }
-
方法
//做char[] 的拷贝或者拼接 @Override public StringBuilder append(String str) { super.append(str); return this;//放回当前对象 } //将 StringBuilder 对象转换成字符串对象 @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
-
字段
/** * The value is used for character storage. */ char[] value; /** * The count is the number of characters used. */ int count;
StringBuffer
- 使用synchronized修饰,是线程安全
面试题 String StringBuilder StringBuffer 区别
- 拼接 10万次字符串,去比较各自的耗时
- 注意:
- StringBuilder 性能最好 > StringBuffer > String
- StringBuffer 是线程安全的
JAVA 编码规范
- 编码对于我们程序员来说,特点重要,原因如下:
- 一个软件的生命周期,90%时间都是在维护系统
- 良好的编码习惯可以改善代码的可读性
- 编码规范:
- 起名:做到见名知意,遵循标识符规范
- 不能使用拼音,采用驼峰命名法(StudentInfo)
- 边写代码边测试,打印中间变量进行观察,确保程序正确性
- 基本规范:
- 包名:域名倒写,工具类 utils , 控制层 controller
- 类名:一般使用名词,并且首字母大写,不要使用jdk 内置名字
- 接口名:单词前面习惯加字母 i (IUser)
- 方法名:(动词),eat(),saveUser();
- 变量名:首字母小写,多个单词用驼峰,studentName
- 常量名:final 修饰的,多个单词全部大写,用_隔开,MAX_VALUE,MIN_VALUE
- 起名:做到见名知意,遵循标识符规范
基本类型的包装类型
- 问题:
- 一切皆对象?int age = 18;
- 给你一个数,8888,转换成二进制,算法非常难
- double 类型,默认值 0.0,缺考怎么去表示?考试0分的时候怎么去表示?
- 基本数据类型,缺少对象,jdk 提供了包装类型来解决如上问题。
Number 抽象类的学习
//获取 int 类型的值
public abstract int intValue();
//获取 long 类型的值
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
Integer (int)
-
integer 继承结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TJSr7MLD-1664677606227)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220311100603306.png)]
-
构造器学习
public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); } //将字符串解析成 int 类型 public static int parseInt(String s) throws NumberFormatException { return parseInt(s,10); } //将字符串解析成 Integer 类型的数 public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10)); } //将Integer 对象转换成 int 数 public int intValue() { return value; }
-
自动装箱和拆箱(语法糖)
//自动装箱 Integer integer3 = 10; Integer integer3 = Integer.valueOf(10); //自动拆箱 int intValue = integer3; int intValue = integer3.intValue();
-
整数类型做了一个缓存处理
private static class IntegerCache { //缓存了 [-128,127] }
String Integer int 相互转换
-
String 转 Integer
Integer.valueOf() new Integer();
-
String 转 int
Integer.valueOf();
-
Integer 转 int
integer 对象.intValue();
拓展内容(面试内容,面试前再回头来听)
-
缓存设计的优点
- 节约空间开销
- 超过缓存范围才重新分配空间
-
Integer 类型和 int 类型不是同一个类型
- 通过方法的重载证明
-
两个方法分别定义 int a = 100000;当int 类型的值,超过 short 的最大值时,存储到常量池里面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M8Me7Unb-1664677606227)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220311110521367.png)]
包装类型对应
基本类型 | 默认值 | 包装类型 | 包装类型默认值 |
---|---|---|---|
int | 0 | Integer | null |
long | 0 | Long | null |
boolean | false | Boolean | null |
byte | 0 | Byte | null |
short | 0 | Short | null |
float | 0.0 | Float | null |
double | 0.0 | Double | null |
char | ‘’ | Character | null |
- 注意:开发过程中,建议使用包装类型
大数据运算
-
BigInteger : java 开发中,我们的数超过了 long 类型,使用它
public BigInteger(String val) { this(val, 10); }
- 四则运算:
public BigInteger add(BigInteger val) {} public BigInteger subtract(BigInteger val) {} public BigInteger multiply(BigInteger val) {} public BigInteger divide(BigInteger val) {}
-
BigDecimal:具有精度运算的
- 需求:
- 打印 0.09 + 0.01
- 打印 1 - 0.34
- 打印 1.403 / 100
- 注意:做金钱运算使用 BigDecimal
- 里面有加减乘除的方法
- 需求:
随机数 Random
-
需求:随机生成 1- 100 之间的数,生成10次
Random random = new Random(); for (int i = 0; i < 10; i++) { int j = random.nextInt(100); System.out.println(j); }
-
需求:随机生成 100-200 之间的随机数,生成10次
//多思考 Random random = new Random(); for (int i = 0; i < 10; i++) { int j = random.nextInt(100) + 100; System.out.println(j); }
Math 类()数学类
- Math 类包含了数学的常用方法:
- 平方
- 三角函数
- 平方根
- 指数
- 绝对值
Arrays 工具类
- 自己写过二分查找法
正则表达式
-
正则表达式:使用字符串来定义匹配规则(regex)
-
正则表达式的匹配练习:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2POPG8NH-1664677606230)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220311135413745.png)]
-
注意:正则不用手动去写,也不用去记,了解一下就行了。
-
去网站生成正则表达式就行
日期处理
Date
//创建当前日期对象
public Date() {
this(System.currentTimeMillis());
}
//打印符合我们本地人观看的时间日期
@Deprecated
public String toLocaleString() {
DateFormat formatter = DateFormat.getDateTimeInstance();
return formatter.format(this);
}
SimpleDateFormat -->DateFormat
把日期变成字符串
public final String format(Date date)
把字符串变成日期
public Date parse(String source)
最常用的日期格式
- yyyy-MM-dd
- yyyy-MM-dd HH:mm:ss
字母 | 含义 | 示例 |
---|---|---|
y | 年份。一般用 yy 表示两位年份,yyyy 表示 4 位年份 | 使用 yy 表示的年扮,如 11; 使用 yyyy 表示的年份,如 2011 |
M | 月份。一般用 MM 表示月份,如果使用 MMM,则会 根据语言环境显示不同语言的月份 | 使用 MM 表示的月份,如 05; 使用 MMM 表示月份,在 Locale.CHINA 语言环境下,如“十月”;在 Locale.US 语言环境下,如 Oct |
d | 月份中的天数。一般用 dd 表示天数 | 使用 dd 表示的天数,如 10 |
D | 年份中的天数。表示当天是当年的第几天, 用 D 表示 | 使用 D 表示的年份中的天数,如 295 |
E | 星期几。用 E 表示,会根据语言环境的不同, 显示不 同语言的星期几 | 使用 E 表示星期几,在 Locale.CHINA 语 言环境下,如“星期四”;在 Locale.US 语 言环境下,如 Thu |
H | 一天中的小时数(0~23)。一般用 HH 表示小时数 | 使用 HH 表示的小时数,如 18 |
h | 一天中的小时数(1~12)。一般使用 hh 表示小时数 | 使用 hh 表示的小时数,如 10 (注意 10 有 可能是 10 点,也可能是 22 点) |
m | 分钟数。一般使用 mm 表示分钟数 | 使用 mm 表示的分钟数,如 29 |
s | 秒数。一般使用 ss 表示秒数 | 使用 ss 表示的秒数,如 38 |
S | 毫秒数。一般使用 SSS 表示毫秒数 | 使用 SSS 表示的毫秒数,如 156 |
Calendar 日历类
-
获取日历对象
public static Calendar getInstance()
-
获取当前时间
public final Date getTime() { return new Date(getTimeInMillis()); }
工具类的设计
- 日期工具类的设计
- 需求:建立一个员工类, Employee, entry 入职时间,birthday 生日,创建员工管理类 EmployeeManager input 录入数据
- 发现如果不使用工具类,代码太臃肿了。所以需要抽工具类
- 数组工具类自行设计
枚举
-
需求:定义一个学生类,定义一个成员变量 restDay(表示学习哪天休息)
-
问题如下
-
非法数据设置、
-
解决办法1:使用指定的单独管日期的类 WeekDay,并且不能够修改内容,使用7个常量来表示,能保证选择数据安全性,但是还是吧不能保证数据安全
-
解决办法2:私有化构造器,使用对象来代替常量
-
枚举:
[public] enum 枚举名称{ 常量1,常量2; }
-
特点
-
构造器私有化
-
里面是一个个常量
-
枚举里面可以提供带参数构造器
-
可以提供 set get方法
-
可以定义普通方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOPs3GsD-1664677606231)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220311153608588.png)]
-
单例设计模式
- 单例模式:只能创建出一个对象,不能创建多个,提供方法去获取需要的对象。
没有异常处理时如何处理异常
-
什么是异常:非正常的,不正常的。
-
程序中:代码出现错误,程序会终止运行。异常指的不是语法错误,语法错误,编译不通过,不能产生字节码文件,根本不能运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSVIm0nV-1664677606233)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220312104131854.png)]
-
异常是衡量语言成熟的标准之一,java C++都支持异常处理机制。
-
上学迟到的异常:
public class Sleep { public static final int OK = 1;//准时起床 public static final int NO_OK = 0;//没起来 public int asleep(int num) { if(num == Sleep.OK){ System.out.println("准时起床"); return OK; }else{ System.out.println("起晚了翘课"); return NO_OK; } } } public class Student { public static final int LATE = 0;//代表迟到或者翘课 public static final int IN_TIME = 1;//准时上课 public Sleep sleep; public Student(Sleep sleep){ this.sleep = sleep; } public int gotoStudy(int num){ if (sleep.asleep(num) == Sleep.OK) { System.out.println("准时上课"); return Student.IN_TIME; }else{ System.out.println("翘课"); return Student.LATE; } } } Sleep sleep = new Sleep(); Student student = new Student(sleep); int i = student.gotoStudy(0); if(i == Student.IN_TIME){ System.out.println("准时上学"); }else{ System.out.println("翘课"); }
-
没有异常机制存在问题:
- 使用方法的返回值来表示异常,无法穷尽所有情况来表示
- 异常代码和正常流程混在一起了。代码更加臃肿,增加了程序的复杂性,可读性也变低了。
- 随着系统规模扩大,维护性变低了。
异常机制
-
解决上述问题,把不同的异常类型描述成不同的类(异常类),变得更加的灵活,自己不想处理的可以交给调用者处理
-
异常继承结构图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tR4ZIwtv-1664677606234)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220312110040864.png)]
- Error 错误:指JVM 相关的不可修复的错误,如:内存溢出,JVM错误等。常见的Error
- StackOverflowError 栈异常,使用递归没有出口的时候
- OutOfMemoryError 内存不够
- Exception: 常见异常
- NullportException 空指针异常
- ArrayIndexOutofBundsException 数组索引越界异常
- Error 错误:指JVM 相关的不可修复的错误,如:内存溢出,JVM错误等。常见的Error
-
查看异常信息(可以定义异常的行数的,大胆的去解决不要害怕)
异常处理
如果出现异常,程序是不能继续往下执行的,这个时候需要我们程序员手动处理异常。
- 如果该方法不处理:通过方法声明抛出,由调用该方法的调用者来处理(throws)
- 在方法中使用 try{}catch(){} 的语句进行处理
语法格式:
-
throws 关键字
-
声明:把问题标识出来,报告给调用者,由调用者处理(有甩锅的感觉)
-
在参数列表上 throws + 具体的异常类型
-
用在方法上
-
格式
public static Date getDateByString(String str) throws ParseException { DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date parse = format.parse(str); return parse; }
-
-
throw 关键字
-
用来抛出一个指定类型的异常
-
创建一个异常对象 throws 异常对象;
-
用在方法内部
-
格式
public static int divide(int a, int b) { if (b == 0) { throw new ArithmeticException("除数不能为0"); } return a / b; }
-
-
try…catch
-
用来处理异常
-
定义格式
try{ 可能出现异常的代码 }catch(异常的类型 变量){ 异常执行代码 } try{ int divide = divide(1, 0); }catch(ArithmeticException e){ // e.printStackTrace(); System.out.println("我们的除法计算器出现异常了"); } System.out.println("欢迎下次光临");
-
-
try…catch…finally
try{ 可能出现异常的代码 }catch(异常的类型 变量){ 异常执行代码 }finally{ 无论程序怎么执行,都会执行到这里 } finally 代码块永远会执行
-
什么时候使用 throws?throw?try…catch
- throws:当前方法不需要去处理,只需要提供异常信息,给调用者处理即可
- throw:方法会出现异常时,需要提醒调用者
- try…catch:具体需要处理的时候
-
Exception 和 RuntimeException 的区别
- Exception 是受检查异常,必须处理或者throws 出去
- RuntimeException 运行时异常,运行的时候才去检查
-
异常对象常用方法:
- getMessage() 获取具体的异常原因
- printStackTrace()//打印具体的错误
final、finalize、finally有什么区别(面试题)
- final:表示最终的,不可修改的。
- final int MAX = 10; 常量
- final class Student 不可以继承
- final 去修饰方法,不可以被重写
- finalize:Object 的一个方法,提供给GC垃圾回收器使用的。
- finally:异常处理的代码块,最终都要执行
重写复习
- 发生到子类和父类之间
- 方法签名必须相同(方法名和形式参数)
- 子类返回类型必须和父类一致或者比它小
- 子类的修饰符大于等于父类
- 建议:子类抛出的异常小于等于父类
自定义异常
-
现有的异常不能满足我的要求,需要自己去定义:
-
IndexOutofBundsException 索引越界异常
public class IndexOutOfBoundsException extends RuntimeException { public IndexOutOfBoundsException() { super(); } public IndexOutOfBoundsException(String s) { super(s); } }
-
NullpointerException 空指针异常
public class NullPointerException extends RuntimeException { public NullPointerException() { super(); } public NullPointerException(String s) { super(s); } }
-
-
自定义异常
-
语法
public class 自定义异常类 extends 异常类{ //定义无参数构造器 public 自定义异常类() { super(); } //带一个String类型参数的构造器 public 自定义异常类(String s) { super(s); } }
-
例子
public class MyNullPointerException extends Exception { public MyNullPointerException(){ super(); } public MyNullPointerException(String s){ super(s); } }
-
异常在开发中的使用:
- 项目中会做统一异常处理,状态码返回 200 400 404 500
- 普通的正常异常的处理
数据结构
-
什么是数据结构:是用来做计算存储,组织数据(插入数据,更新数据,删除数据,查询数据)的一种方式,是相互之间,存在一种或者多种特定关系的数据结合。数据结构需要高效的算法和索引技术。
-
常见的数据结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBOzBztA-1664677606235)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220312131213376.png)]
- 意义:带来更高效的存储和运行效率
- 要求:高效的检索算法和索引技术
- 作用(模拟:存储学生的信息)
- 保存学生信息
- 修改学生信息
- 删除学生信息
- 查询学生信息
- 查询多个学生信息
- ArrayList LinkedList HashMap (高度封装,别人给我们提供好的)
引出ArrayList
-
需求:加入你是大学生程序竞赛的导师(5个参赛名额,1-2个替补)
- 准备一个数组(5)
- 安排5个学生去比赛
- 查询某一个同学是几号比赛位
- 替补替换2号同学
- 3号同学作弊,禁止3号同学参赛(不能安排替补上场)
- 打印最后比赛参赛选手的名称
-
新需求
- 带队3个人参加数学比赛
- 带队10个人参加 ctf 比赛
分析 ArrayList 性能
- 大O表示法(BigO):用来描述时间的复杂度,专门用来衡量计算机性能
- 分析 ArrayList 性能
- 新增操作:
- 把数据添加到最后一个元素,至少操作一次
- 如果把数据放到数组的第一个位置,现在有N个元素,此时需要操作N次(整体后移)
- 平均:O((N+1)/2 ) O(N)
- 删除操作:
- 如果是最后一个元素,操作一次
- 如果删除第一个元素,操作N次
- 平均 O((N+1)/2) O(N)
- 修改操作:
- 操作一次
- 查询操作:
- 根据索引去查,也是操作1次
- 根据元素来查,1-N次之间 O((N+1)/2 ) O(N)
- 新增操作:
- 基于数组结够,做查询和修改非常快,但是做删除和新增会慢一点
链表 LinkedList
-
链表分为:
-
单向链表
增加和删除
-
双向链表
- 往前添加
- 往后添加
- 删除
分析 LinkedList 性能
- 增加:
- 直接从头尾增加,操作1次
- 删除
- 如果删除头尾元素,操作1次
- 删除中间元素,(1+N)/2
- 查询
- 查询头尾 1次
- 查询中间 (1+N)/2
- 修改
- 头尾1次
- 中间 (1+N)/2
基于数组和链表的对比:
- ArrayList:查询更改较快,新增和删除较慢
- LinkedList:查询,更改较慢,新增和删除较快
-
队列
队列是一种特殊的线性表,特殊之处在于只允许在表的前端或者后端操作
单向队列(Queue):先进先出(FIFO),只能从队尾插入数据,从头部删除
双向队列(Deque):可以从头尾什么都可以操作。
栈
- 栈(stack):受限制的线性表,先进后出(FILO)
- 被限制只允许从表的一段进行插入和删除运算,这一端称为栈顶,另一端就称为栈底
哈希表:Hash
- 一般数组中,通过索引操作的,索引和我们元素之间是不存在确定关系。查找值直接查索引就行
- hashtable:元素值和我们的hashtable 是存在对应关系的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXQCUjSh-1664677606235)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220312164448665.png)]
- 需求:不适用任何的集合,也不使用数据库,添加学生信息(id name),做到快速查询
- 插入查询特别快
- 元素位置—>哈希码—>链表
集合概述
-
集合框架(java.util)的由来:容器类(集合类)可以存储多个数据,数组明明可以存储多个数据,为啥还要定义容器类?
- 数组弊端:
- 长度一但固定就不可变
- 很多地方需要操作数组的(增删改查)都需要去编写对应的方法(代码重复了—>封装)
- 每个人定义各自的方法,可能存在别人找不到 这种情况,实现也容易存在bug
- 数组弊端:
-
什么是集合框架:容器类确实很好用,集合框架框架是为了提供一些规范和标准,任何实现类都需要包含对外的接口,接口的实现,对集合内部的算法(底层都是一种数据结构)
-
定义集合框架的目的:提供代码复用(封装的思想),让使用者专注于业务开发,而不是数据结构和算法。
-
常见集合类:
- List(列表):集合中对象按照索引位置排序,允许元素重复。
- Set(集):集合中的元素不按特定方式排序,不允许元素重复。
- Map(映射):集合中的元素(key-value),不允许key 重复,值可以重复。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YJHBjhLf-1664677606236)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220313110647116.png)]
Collection 常用方法
-
接口定义的常用方法规范
//集合容量大小 int size(); //判断集合是否为空 boolean isEmpty(); //包含是否有某一个元素 boolean contains(Object o); //迭代器 Iterator<E> iterator(); //转换成数组 Object[] toArray(); //添加元素的 boolean add(E e); //删除元素 boolean remove(Object o); //判断集合是否包含另一个集合 boolean containsAll(Collection<?> c); //添加一个集合 boolean addAll(Collection<? extends E> c); //清空 void clear(); //获取hashcode int hashCode();
-
通用迭代
Iterator it = 集合对象.iterator(); while(it.hasNext()){ Object ele = it.next(); } //迭代器接口 //判断是否有下一个元素 boolean hasNext(); //获取下一个元素 E next();
List 接口
-
List 接口规范:
//根据索引获取元素值 E get(int index); //设置某一个索引位置元素值 E set(int index, E element); //删除某一个索引位置的值 E remove(int index); //截取 List<E> subList(int fromIndex, int toIndex);
Vector 实现类
-
看源码学习
public class Vector<E> extends AbstractList<E> implements List<E>{ //元素数组 protected Object[] elementData; //元素个数 protected int elementCount; //扩容容量 protected int capacityIncrement; public Vector(int initialCapacity, int capacityIncrement) { this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; } public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector() { this(10); } public synchronized void addElement(E obj) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = obj; } //扩容逻辑 private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; //如果你传进来扩容容量,新容量=老容量+传进来的扩容容量否则2倍老容量 int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //给int类型的最大值 newCapacity = hugeCapacity(minCapacity); //数组的拷贝 elementData = Arrays.copyOf(elementData, newCapacity); } }
-
Vector 总结
- 底层使用 Object[] 数组(调用不带参数构造器时,默认长度为10,若不传扩容参数,扩容2倍)
- toString 方法已经重写并且可以直接打印出数组的样子
- 增查删改
- 常用方法
- add(E obj)
- addElement(E obj)
- 查询
- size() 查长度
- get(int index) 查具体索引位置的元素值
- isEmpty() 判断集合为空
- 删除
- remove(int index)删除具体索引位置的元素
- remove(“A”) 删除指定元素
- removeLast() 循环,设置 null ,等待gc 回收
- 修改
- set(int index,E obj);修改某一个索引位置元素值
Stack 栈
栈:是数据结构,First In Last Out
栈结构生活中的体现:
- QQ消息:AB两个人先后发送消息,最后发送的是最新的。
栈:底层可以用数组,或者链表
//添加元素
public E push(E item) {
addElement(item);
return item;
}
//取元素并且删除
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
//查看栈顶元素
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
- 建议:建议使用ArrayDeque(方法会更加友好)
ArrayList
ArrayList 是用来取代 Vector.两者底层原理和算法,一模一样。
区别:
- Vector:所有的方法都是用 synchronized 修饰符,表示线程安全的,性能低,适用于多线程环境
- ArrayList:线程不安全,性能高,即使在多线程环境下也是用它(Collections.synchronizedList(list))
- ArrayList 底层扩容是1.5倍,Vector 是两倍
- 底层构造器ArrayList 优化了,默认创建对象的时候是给一个空数组,第一次调用add 方法时,采取重新初始化数组(创建对象时,如果不存任何值,也浪费了堆空间)
LinkedList
-
LinkedList:是一个双向链表,双向队列(单向队列)list 的实现类。
Queue 方法 DQueue 方法 LinkedList boolean add(E e); void addFirst(E e); public E getFirst() E remove(); void addLast(E e); public E getLast() E poll(); E removeFirst(); public void addFirst(E e) E element(); E removeLast(); public void addLast(E e) E peek(); E pollFirst(); E pollLast(); E getFirst(); boolean add(E e); void push(E e); E pop(); -
面试题:请编写一个双向链表
-
注意:
- LinkedList 是非线程安全的,保证线程安全,使用Collections 线程安全方法
- 擅长操作头和尾,大多数你以后要用的方法, addFirst addLast removeFirst
- 链表不存在索引,但是调用get(index) 是因为底层提供了 ListItr 这个内部类 提供了一个int 的索引
- 擅长保存和删除操作
List 的总结
- 根据 Vector ArrayList LinkedList 类的所有特点进行一个抽象,定义一个规范
- 特点:
- 允许元素重复
- 会记录添加顺序
- 具有很多共同方法
实现类的选用
- ArrayList 取代 Vector 直接使用
- LinkedList:
- 数组结构的算法:插入和删除速度慢,查询和更改较快
- 链表结构的算法:插入和删除速度快,查询和更改较慢
泛型
-
为啥使用泛型?
- 存在一定问题:
- 取集合元素时,取出来的是Object 类型,需要强制类型转换才能使用
- 添加元素时候,缺乏规范,导致可能需要使用时,会出现类型转换异常
- 设计原则,不要写重复的代码,能抽就抽
- 存在一定问题:
-
泛型:是一种数据规范和约束,提供编译时期的安全检查机制,底层给我们做强制类型转换
-
如何使用泛型?
-
常见的字母
- T type(类型,使用到类上面)
- K V (key value)
- E element(元素)
-
使用
-
使用到类或者接口上
public class User<T> { T obj; } //多个的方法 public class User<T,E,k> { T obj; E ele; k value; }
-
接口
public interface Usb<T> { void user(T t); }
-
方法
public static <T> T print(T t){ return t; }
-
泛型的继承
public class Mouth<T,T1> implements Usb<T,T1>{ @Override public void user(T t, T1 t1) { } } public class Keyword<T> implements Usb<T,String>{ @Override public void user(T t, String s) { } }
-
-
Map 接口
-
map 概述:key—>value 的映射关系,是以键值对的方式存储
-
Map:是一种两个集合之间的一个映射关系,Map 并没有继承 Collection 接口
- key 是不可以重复
- value 是可以重复的
-
Map 最常用的实现类:
- HashTable
- HashMap
- LinkedHashMap
- Properties
- ConcurrentHashMap
-
map 常用的方法
int size(); //判断某一个key 是否存在 boolean containsKey(Object key); //获取元素值 V get(Object key); //设置值 V put(K key, V value); //删除某一个key 的值 V remove(Object key); //获取所有的key Set<K> keySet(); //获取所有的value Collection<V> values(); //获取键值对对象 Set<Map.Entry<K, V>> entrySet();
HashMap
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EX4oREPL-1664677606237)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220313163441465.png)]
-
put 方法的实现原理
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } hash(key); Node<K,V> 单向的 TreeNode<K,V>
-
get 方法原理
hash(key)//先找hash表中的位置 //判断是treeNode,如果是使用红黑树算法寻找value,不是使用链表遍历 treeNode 算法,使用hash 往左节点或者右节点直接找。 遍历链表,判断hash 的同时,去判断 key 值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNKVwIt4-1664677606238)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220313165003690.png)]
-
面试题:java7 个 hava8 hashMap 的区别
-
使用
-
key — String
-
value 无所谓
-
注意,存String 类型无所谓,存对象类型,需要重写equals 方法和 hashCode();
Map<Student,String> mapStu = new HashMap<>(); mapStu.put(new Student(10),"sy"); mapStu.put(new Student(12),"zs"); mapStu.put(new Student(13),"ls"); mapStu.put(new Student(10),"zz"); Set<Map.Entry<Student, String>> entries = mapStu.entrySet(); System.out.println(entries); for (Map.Entry<Student, String> entry : entries) { System.out.println(entry.getKey()); System.out.println("-----"); System.out.println(entry.getValue()); }
-
嵌套使用
-
LinkedHashMap
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x2EwIPsG-1664677606238)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220313181350172.png)]
- 记录添加顺序,key 不允许重复,判断 key 是否重复和 HashMap 标准一致
HashTable
- HashTable:和HashMap 几乎一致,它是线程安全的,被HashMap 所替代了。采用hash表算法,所有的方法synchronized修饰符修饰,性能低一点。
Properties
用来加载资源文件
public synchronized void load(InputStream inStream) throws IOException {
load0(new LineReader(inStream));
}
TreeMap
-
采用红黑树算法,里面的key 会按照自然顺序(不自己指定排序规则)自动排序,或者定制排序,可以不能重复,key 判断重复的标准,Comparator compare 的返回值判断
- 返回值 = 0
- 返回值 > 0 升序 ASC
- 返回值 < 0 降序 DESC
-
需求:计算一个字符串中字符出现的次数 “fwihfiwehfihewifhiaufiuhailfawigfuauekgfweufuiwegfieuw”,并按照 a b c 的方式排序;
String str = "fwihfiwehfihewifhiaufiuhailfawigfuauekgfweufuiwegfieuw"; char[] chars = str.toCharArray(); Map<Character,Integer> map = new TreeMap<>(); for (char aChar : chars) { //如果从map 里面找不到,说明没值 if(map.get(aChar) == null ){ map.put(aChar,1); }else{ Integer integer = map.get(aChar); map.put(aChar,++integer); } } System.out.println(map);
-
排序源码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iVF9oltj-1664677606239)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220313185353668.png)]
Set 接口
-
Set 接口是 Collection 的子接口,相当于数学上的集合
-
Set 存储元素的特点:
- 不允许元素重复,尝试添加相同元素,会返回false
- 不会记录元素的先后添加顺序
- 判断两个元素对象是否相等用的是equals 方法
-
hash 表与 数组对比
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zlPECyDi-1664677606240)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220314102329611.png)]
HashSet
-
HashSet 是 Set 最常用的接口,底层使用 Hash(散列) 算法,查询速度和插入速度较快。
-
HashSet 判断两个对象是否相等,equasl 比较。返回 true 表示相等。
-
对象的 HashCode 值决定了在hash 表中的位置。
- 判断添加对象和集合元素对象HashCode值。
- 不等:直接将新添加对象存储导对应位置
- 相等:再继续判断新对象和集合中对象的具体值,equals 方法判断
- HashCode 相同,equals true ,则是同一个对象,则不保存hashtable 中
- HashCode 相同,equasl false,存储到同槽的链表上。
- 判断添加对象和集合元素对象HashCode值。
-
HashSet 基于 HashMap 实现的
//直接使用HashMap写好的代码,体现了封装的原则,减少重复代码 private transient HashMap<E,Object> map; //map.put value 永远设置同一块空间 new Object 的静态常量 public boolean add(E e) { return map.put(e, PRESENT)==null; } //得到 HashMap 的 key public Iterator<E> iterator() { return map.keySet().iterator(); }
-
注意:记得重写 equals 和 hashCode 方法
LinkedHashSet
-
底层使用哈希表算法,和链表算法,
- 哈希表用来保证唯一性,HashSet 里面是不记录添加先后顺序的。
- 链表,来记录元素的添加顺序
-
底层基于LinkedHashMap 实现
public LinkedHashSet() { super(16, .75f, true); } HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
TreeSet
-
TreeSet:底层使用红黑树算法,会对存储的元素做自然排序(小-大)
-
注意:使用TreeSet 时,必须使用同一种数据类型。因为需要比较,否则就会报类型转换异常
- 底层肯定实现了Comparable 接口
- 比较结果
- 大于0
- 等于0 则说明是同一个对象
- 小于0
-
底层都是基于 TreeMap 实现的
//底层使用 treeMap public TreeSet() { this(new TreeMap<E,Object>()); } //可以传一个自定义比较器 public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); }
Collections集合工具类
-
常用方法
//获取线程安全的集合 Collections.synchronizedList(new ArrayList()); //获取线程安全的集合 Collections.synchronizedCollection(Collection); //排序,好挺常用 public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null); }
-
其它常用
public static final List EMPTY_LIST = new EmptyList<>(); public static final Map EMPTY_MAP = new EmptyMap<>(); public static final Set EMPTY_SET = new EmptySet<>();
集合类整体总结
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2iDj94CK-1664677606240)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220314121314501.png)]
是否记录添加顺序的总结如下:
数据类型 | 是否记录添加顺序 | 是否添加就排序 | 底层算法 |
---|---|---|---|
arrayList | true | false | 数组 |
linkedList | true | false | 链表 |
hashSet | false | false | 哈希算法,红黑树 |
linkedHashSet | true | fasle | 哈希算法,链表,红黑树 |
treeSet | false | true | 红黑树 |
hashMap | false | false | 哈希算法,红黑树 |
linkedHashMap | true | false | 哈希算法,链表,红黑树 |
treeMap | false | true | 红黑树 |
IO概述
- 问题:数据是存储到内存中的,当我们程序运行结束时,数据就没了,此时就需要把数据持久化存储起来。
- I:input 输入(读)
- O:output 输出(写)
- java.io 包
File 类
-
File 类:文件、目录的一个抽象封装
-
文件路径分隔符:
- windows:使用 \ 分割,java 中要使用\\ ,分割属性;(不区分大小写)
- Unix 系统:/ 分割(区分大小写)
-
获取当前系统的分割符:
//获取分割属性 File.pathSeparator; File.pathSeparatorChar; //分割符 File.separator; File.separatorChar;
-
获取路径和名称
-
相对路径:参考一个坐标,与这个坐标的路径
. 代表当前目录 ..返回上一层 ../test 上一层目录下面的 test 目录 注意,现在拷贝的时候,尽量使用绝对路径来做,以后到框架阶段,用相对路径来做
-
绝对路径:从磁盘的跟目录一直到你的文件
//获取绝对路径 public String getAbsolutePath() //获取文件路径 public File getAbsoluteFile() //获取文件路径 public String getPath() //获取上级目录文件 public File getParentFile() //获取上级目录路径 public String getParent()
-
-
构造方法
//直接使用路径创建文件 pathname 可以是文件也可以是目录 public File(String pathname) //从目录对象下创建文件对象 public File(File parent, String child) //目录路径和文件路径分开传 public File(String parent, String child)
-
方法
//创建新文件 public boolean createNewFile() //判断是否为目录 public boolean isDirectory() //判断是否为文件 public boolean isFile() //删除文件 public boolean delete() //判断文件是否存在 public boolean exists() //创建文件目录 public boolean mkdir() //创建多个目录 public boolean mkdirs() //获取当前层级的文件名 public String[] list() //重命名(可以做拷贝) public boolean renameTo(File dest)
递归操作文件
-
需求:列出指定目录中的所有文件,包括子文件夹中的所有文件及其子文件。(Recursion) 递归
-
递归回顾:
public static int recursion(int num){ if(num == 1){ return 1; } return num * recursion(--num); }
-
需求实现
public static void allFiles(File file, String path) { //寻找第一层的所有儿子 File[] files = file.listFiles(); System.out.println(path + file.getAbsolutePath()); for (File f : files) { if (f.isDirectory()) { //如果是目录就需要递归 allFiles(f, path + path + " -"); } else { System.out.println(path + path + f.getName()); } } }
-
-
需求:批量修改文件名
public static void rename(String deleteName,String newName,File file){ File[] files = file.listFiles(); for (File f : files) { if(f.isFile()){ if(f.getName().contains(deleteName)){ //替换名字字符串 String replace = f.getName().replace(deleteName, newName); //创建替换文件对象 File file1 = new File(f.getParent(), replace); f.renameTo(file1); } }else{ //递归 rename(deleteName,newName,f); } } }
文件过滤器(FilenameFilter)
-
需求:打印指定目录下以.avi 结尾的资源
public static void filterFiles(List<File> list, File file, String filterName){ File[] files1 = file.listFiles(); int count = 0; for (File file1 : files1) { if(file1.isFile()){ File[] files = file.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return new File(dir, name).isFile() && name.endsWith(filterName); } }); if(count > 0){ continue; } for (File file2 : files) { list.add(file2); } count++; }else{ filterFiles(list,file1,filterName); } } }
-
文件过滤器(FilenameFilter)规范,需要自己实现逻辑。
FilenameFilter filter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { return false; } };
IO概述
- io:输入和输出
- 输入设备:
- 键盘,鼠标,麦克风
- 输出设备
- 显示器,打印机,音响
- 输入设备:
- 为什么程序需要io
- 做数据存储持久化。
- io流的分类(java.io 包下面的)
- 按流向(输入和输出流)
- 按数据单位(字节流字符流)
- 四大基本流
- 字节输入流
- 字节输出流
- 字符输入流
- 字符输出流
- 不管你是什么流,都需要关闭资源如果不关闭资源,磁盘文件会被占用,不能删除也不能修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0u6GA7fF-1664677606241)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220315111454242.png)]
注意:操作IO流
- 输入输出的目标搞明白,程序写出去,输出,程序读进来,输入
- 输入
- read()
- 输出
- write()
- 输入
文件流
字节流
-
字节流:
-
FileInputStream 文件输入流
-
需求:读 my.txt 的文件内容出来
常用方法 public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } //通过文件获取输入流对象 public FileInputStream(File file) //通过 byte[] 数组读 public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } //关闭资源的方式 public void close() ;
//获取文件对象 File file = new File("D:\\javase\\homework\\practice\\day20\\my.txt"); //获取文件输入流对象 FileInputStream in = null; try { in = new FileInputStream(file); int length = -1; // while ((length = in.read()) != -1){ // System.out.println(length); // } byte[] bytes = new byte[1024]; while ((length = in.read(bytes)) != -1){ String s = new String(bytes); System.out.println(s); } } catch (Exception e) { e.printStackTrace(); }finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } }
-
-
FileOutputStream 文件输出流
-
需求:把 sy666 写进刚才读的文件里面
//append 表示是否追加 public FileOutputStream(String name, boolean append) //关闭资源 public void close() //写 public void write(byte b[])
//获取文件对象 File file = new File("D:\\javase\\homework\\practice\\day20\\my.txt"); FileOutputStream out = null; try { out = new FileOutputStream(file,true); String sy = "sy666"; byte[] bytes = sy.getBytes(); out.write(bytes); } catch (Exception e) { e.printStackTrace(); }finally { if(out != null){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } }
-
-
需求:将 sy.txt 文件中的内容,拷贝成一个新文件 new.txt
FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream("D:\\javase\\homework\\practice\\day20\\my.txt"); out = new FileOutputStream("D:\\javase\\homework\\practice\\day20\\new.txt"); byte[] buffer = new byte[10]; int length = -1; // in.read 读数据到 buffer while ((length = in.read(buffer)) != -1){ out.write(buffer);//把 buffer 中的数据写出去 } } catch (Exception e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException e) { e.printStackTrace(); } }
-
字符流
-
FileReader 输入
FileReader reader = null; try { reader = new FileReader("D:\\javase\\homework\\practice\\day20\\my.txt"); char[] buffer = new char[1024]; int length = -1; while ((length = reader.read(buffer) ) != -1){ String s = String.valueOf(buffer); System.out.println(s); } } catch (Exception e) { e.printStackTrace(); }finally { reader.close(); }
-
FileWriter 输出
FileWriter fileWriter = new FileWriter("D:\\javase\\homework\\practice\\day20\\my.txt", true); fileWriter.write("你好"); fileWriter.write("上云"); //关闭资源 fileWriter.close();
-
字节流与字符流的选择
- 二进制文件,图片,音频,视频必须使用字节流
- 文本文件(txt)使用字符流
- 不清楚类型,用字节流
-
flush(刷新操作)
- 计算机直接访问磁盘文件的时候,比操作内存慢,设置一个缓冲区,写的时候直接先写到内存,达到特定值再写到磁盘
- 使用缓冲区意义:
- 提高cpu的使用率
- 回滚写入的数据
- 操作系统使用 -1 代表磁盘文件结尾的标志
- IO 是最影响程序性能的,缓冲区设置容量的整数倍,可以去有效的提高 io 性能 1024.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gZwP1xGu-1664677606241)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220315134846949.png)]
try catch 资源自动释放
-
语法
try(需要关闭资源的文件写到这里){ }catch(){ } public static void copyFile1(String srcFilePath, String destFilePath) { try (FileInputStream in = new FileInputStream(srcFilePath); FileOutputStream out = new FileOutputStream(destFilePath); ) { byte[] buffer = new byte[10]; int length = -1; // in.read 读数据到 buffer while ((length = in.read(buffer)) != -1) { out.write(buffer);//把 buffer 中的数据写出去 } } catch (Exception e) { e.printStackTrace(); } }
缓冲流
- 缓冲流:其实就是个包装流,目的是起缓冲的作用,提升io 性能
- 字节缓冲流
- BufferedInputStream 字节缓冲输入流
- BufferedOutputStream 字节缓冲输出流
- 字符缓冲流
- BufferedReader 字符缓冲输入流
- BufferedWriter 字符缓冲输出流
使用缓冲流与不使用缓冲流性能的对比
-
使用单字节拷贝方式
-
使用传统字节流
public static void copy(String srcPath, String destPath) throws IOException { long begin = System.currentTimeMillis(); FileInputStream in = new FileInputStream(srcPath); FileOutputStream out = new FileOutputStream(destPath); int length = -1; while ((length = in.read()) != -1) { out.write(length); } in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("拷贝耗时:" + (end - begin)); }
-
使用 Buffered 缓冲流
public static void copyBuffer(String srcPath, String destPath) throws IOException { long begin = System.currentTimeMillis(); FileInputStream in = new FileInputStream(srcPath); FileOutputStream out = new FileOutputStream(destPath); BufferedInputStream bufferedIn = new BufferedInputStream(in); BufferedOutputStream bufferedOut = new BufferedOutputStream(out); int length = -1; while ((length = bufferedIn.read()) != -1){ bufferedOut.write(length); } bufferedIn.close(); bufferedOut.close(); in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("buffered 拷贝耗时:" + (end - begin)); }
-
-
读和写外面再加buffer
-
使用传统字节流
long begin = System.currentTimeMillis(); FileInputStream in = new FileInputStream(srcPath); FileOutputStream out = new FileOutputStream(destPath); int length = -1; byte[] buffered = new byte[10]; while ((length = in.read(buffered)) != -1) { out.write(buffered,0,length); } in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("copyBufferedBuffered拷贝耗时:" + (end - begin));
-
使用缓冲流
public static void copyBufferBuffered(String srcPath, String destPath) throws IOException { long begin = System.currentTimeMillis(); FileInputStream in = new FileInputStream(srcPath); FileOutputStream out = new FileOutputStream(destPath); BufferedInputStream bufferedIn = new BufferedInputStream(in); BufferedOutputStream bufferedOut = new BufferedOutputStream(out); byte[] buffer = new byte[10]; int length = -1; while ((length = bufferedIn.read(buffer)) != -1){ bufferedOut.write(buffer,0,length); } bufferedIn.close(); bufferedOut.close(); in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("buffered buffer 拷贝耗时:" + (end - begin)); }
-
总结:使用缓冲流真的快
-
字符编码
-
字符编码的发展流程:
- 一开始的时候只能存储一个字节,只能认识数字还可以去识别(a-z A-z)ASCII 美国标准信息交换码
- 随着计算机的普及,很多国家都把自己的字符引入了计算机(汉字)一个字节存储范围太小,用两个字节来存储汉字,使用GB2312 编码 —GBK(2字节)
- 中国人肯定使用汉字,但是把汉字给其它国家的使用,其它国家不认识,显示?乱码需要统一的编码来管理,Unicode 编码(2字节)
-
常见的字符集:
- ASCII:占一个字节,只能够包含128符号,不能表示汉字
- ISO-8859-1(lanin-1) : 占一个字节,收录西欧语言,不能表示汉字
- ANSI:占两个字节,可以表达汉字(GB2312,GBK)
- UTF-8:是一种针对 Unicode 的可变长度的字符编码,又称万国码。互联网工程小组,要求所有的互联网协议都必须支持UTF-8
-
字符的编码以及解码(保证必须使用相同的编码)
- 编码:把字符串转换成byte 数组
- 解码:把byte 数组转换成字符串
-
实操:
String str = "上云"; //将字符串变成一个byte 数组,编码 byte[] bytes = str.getBytes("utf-8"); System.out.println(Arrays.toString(bytes)); // String csn = Charset.defaultCharset().name(); // System.out.println(csn); //解码操作 String s = new String(bytes,"ISO-8859-1"); System.out.println(s); //编码 byte[] bytes1 = s.getBytes("ISO-8859-1"); System.out.println(Arrays.toString(bytes1)); String s1 = new String(bytes1, "UTF-8"); System.out.println(s1);
转换流
- 转换流:把字节流转换成字符流
- InputStreamReader
- OutputStreamWriter
合并流
-
合并流(SequenceInputStream):把多个输入流合并成一个流对象
对象流
-
序列化以及反序列化
- 序列化(写):把内存中的java对象数据,存储到磁盘中去,或者给其它的网络节点使用
- 反序列化(读):把磁盘文件的数据恢复成java对象过程
-
为什么要使用序列化
- 在分布式系统中,数据的共享都需要实现序列化(redis)
- 服务的钝化,把我们不常用的对象存储到磁盘中,节约内存开销
- 使用 json 传输的时候
-
要求:必须实现序列化接口 public interface Serializable
-
对象流:
-
ObjectInputStream 做反序列化的,从文件转换成 java 对象
public static Object readerObject(String path) throws IOException, ClassNotFoundException { ObjectInputStream stream = new ObjectInputStream(new FileInputStream(path)); Object o = stream.readObject(); stream.close(); return o; }
-
ObjectOutputStream 序列化的,把 java 对象转换成文件
public static void writeObject(String path, Object o) throws IOException { ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(path)); stream.writeObject(o); stream.close(); }
-
-
常见问题
- class invalid for deserialization(没有实现序列化接口)
- 序列号版本对不上(引入版本号,避免因为属性该表导致不兼容问题)public static final long serialVersionUID = 1;
打印流
-
打印流:打印数据的,打印流就只能是输出流
- PrintStream:字节打印流
- PrintWriter:字符打印流
-
拓展点:
System.out.println(); == PrintStream out = System.out; out.println();
-
打印流中的格式化输出(printf(String fomart,Object…arr));
System.out.printf("姓名:%s,年龄:%d", "上云", 18);
扫描器类(Scanner)没啥用
-
java.util.Scanner :扫描器类,做输入操作的。
hashNextXXX();//判断有没有这种数据类型的数据 Xxx nextXX();
-
基本使用
public static void main(String[] args) { Scanner scanner = new Scanner(System.in); boolean b = scanner.hasNext(); boolean b1 = scanner.hasNextInt(); if(b) { String next = scanner.next(); System.out.println(next); } }
数据流
-
数据流:提供了可以读/写任何数据类型的方法
- DataInputStream 提供了 readXXX()方法
- DataOutputStream 提供了 writeXX(obj)
-
操作:
public static void read(String path) throws IOException { DataInputStream in = new DataInputStream(new FileInputStream(path)); System.out.println(in.readInt()); System.out.println(in.readUTF()); in.close(); } public static void write(String path) throws IOException { DataOutputStream out = new DataOutputStream(new FileOutputStream(path)); out.writeInt(123123); out.writeUTF("你好,这里是上云教学课"); out.close(); }
Properties
-
配置文件,资源文件以 .properties 作为拓展名
- username=root
- password=123456
-
db.properties 文件来做配置文件
#key=value username=root password=123456
-
具体操作
Properties properties = new Properties(); FileInputStream in = new FileInputStream("D:\\javase\\homework\\practice\\day21\\src\\cn\\sycoder\\PropertiesDemo\\db.properties"); properties.load(in); System.out.println(properties); System.out.println(properties.get("username")); System.out.println(properties.get("password"));
-
ClassLoader类加载器去读(反射的时候补充)
NIO
-
jdk1.4之后提供的,可以把一块磁盘文件映射到内存中去,java.nio
-
应用领域(云服务器)
-
jdk1.7 提供了一个工具类,Files
Files.copy(Paths.get("D:\\javase\\homework\\practice\\day21\\my.txt"), new FileOutputStream("D:\\javase\\homework\\practice\\day21\\AAA.txt"));
-
与 io 有啥不同
- Channels Buffers(通道和缓冲区):使用通道和缓冲区进行操作,性能更快
- 异步的:线程从通道读数据到缓冲区的时候,线程还可以做其他的事。
- Selectors(选择器):选择器可以监听多个通道的事件
反射
类加载机制
-
jvm 和类的关系
- 运行带有main方法的类(主方法),启动 jvm 进程,并加载字节码,同一个jvm 所有线程已经变量都处于同一个进程中
- jvm 何时退出?
- 程序正常运行结束
- 出现异常,没有捕获异常时
- System.exit() 方法
- 强制杀进程的时候
- 重点:jvm 进程一旦结束,进程中的内存中的数据会丢失
-
类加载机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ZH2aRXO-1664677606242)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220316102102812.png)]
-
类加载初始化具体步骤(加载,连接,初始化)
- 类的加载
- 类加载器(ClassLoader),将字节码文件(class 文件)加载进内存中,并且创建一个字节码对象(java.lang.Class),类加载器由JVM提供,或者自定义
- 类的连接(类加载进内存之后,生成 Class 对象,把类的二进制数据合并到 JRE 中)
- 验证:检测被加载的类是否有正确的内部结构
- 准备:负责为类的 static 变量分配内存,设置默认值(并不是初始化操作)
- 解析:把类的二进制数据中的符号引用替换为直接引用(深入分析 jvm)
- 类的初始化(jvm 负责对类进行初始化,主要是对 static 变量进行初始化)
- 如果该类未被加载和连接,则程序先加载并连接该类(类还可以动态加载)groovy
- 如果该类的父类未被初始化,则优先初始化父类
- 如果该类有初始化语句(静态代码块),系统依次执行这些初始化语句
- 类的加载
反射的概述
- 问题:Object obj = new String(“abc”);
- 需求:如何调用 String 中的length 方法
- 使用强制类型转换
- 如果不知道真实的类型怎么办?
- 需求:如何调用 String 中的length 方法
- 问题:一切皆对象,**类这种事物是啥对象?**使用什么类来表示这种对象?
- Class :表示所有的类
- Constructor:表示所有的构造器
- Method :表示所有的方法
- Field:表示所有的字段
- 通过反射:得到类的元数据信息(构造器,方法,字段,内部类)
- 类加载进内存,变成 Class 对象,也叫字节码对象
Class 类和 Class 实例
-
Class 表示所有的类
-
Class 实例表示:一份份的字节码(类,接口,注解)
-
各种类如何表示?
- String Class<java.lang.String>
- HashMap Class<java.util.HashMap>
-
如何获取Class 的实例
//1:使用类名.class Class<String> stringClass = String.class; //2:通过对象调用 getClass(); String str = "abc"; Class<? extends String> aClass = str.getClass(); //3:使用类的全限定类名 java.lang.String Class<?> aClass1 = Class.forName("java.lang.String"); Class<?> stuClass = Class.forName("cn.sycoder.ClassObjectDemo.Student"); System.out.println(stuClass);
- 注意:同一个JVM中,只存在一个类的一份字节码和一份字节码对象
- 使用最多的 Class.forName(类的全限定类名);
-
基本数据类型的字节码如何表示:
-
jvm 已经提前内置好基本数据类型字节码实例
Class<Integer> aClass2 = int.class;
-
包装类型提供了一个 TYPE 常量,也是可以去获取基本类型的类实例
Class<Integer> type = Integer.TYPE;
-
如何证明 Integer 和 int 不是同一种数据类型
Integer.class == int.class
-
类加载
-
直接使用类.class
Class<String> stringClass = String.class;
-
使用对象调用getClass()
String str = "abc"; Class<? extends String> aClass = str.getClass();
-
使用类全限定类名
Class<?> aClass1 = Class.forName("java.lang.String"); Class<?> stuClass = Class.forName("cn.sycoder.ClassObjectDemo.Student");
反射操作构造器
-
Class 去操作 Constructor 类
-
常用的方法
//获取构造器对象 public Constructor<T> getConstructor(Class<?>... parameterTypes) //获取全部构造器对象 public Constructor<?>[] getConstructors() //获取全部构造器包括私有 public Constructor<?>[] getDeclaredConstructors()
-
常见错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zgxgD35P-1664677606242)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220316111632146.png)]
-
常用获取构造器方法
//获取单个 Class<?> aClass = Class.forName("cn.sycoder.ConstructorDemo.User"); Constructor<?> constructor = aClass.getConstructor(); //获取非私有全部 Constructor<?>[] constructors = aClass.getConstructors(); //获取私有的单个 Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(); //获取带私有的全部 Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors(); //带参数的 Constructor<?> constructor1 = aClass.getConstructor(String.class);
反射创建对象
-
问题:为啥使用反射创建对象,不直接使用 new 创建对象
- 框架中,多半使用反射创建对象,使用类全限定类名操作
-
如何使用反射创建对象
-
获取类字节码对象
-
获取构造器对象
-
通过反射获取的构造器创建对象
Class<?> aClass = Class.forName("cn.sycoder.ConstructorNewObject.Person"); Constructor<?> constructor = aClass.getConstructor(); //通过构造器创建对象 Object o = constructor.newInstance();
-
如果构造器是私有的,还需要去设置可以访问
constructor.setAccessible(true);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DPNulnui-1664677606243)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220316112823727.png)]
-
-
常见错误:
-
cn.sycoder.ConstructorNewObject.Person with modifiers “private”
constructor.setAccessible(true);
-
Exception in thread “main” java.lang.IllegalArgumentException: wrong number of arguments
Constructor<?> constructor1 = aClass.getConstructor(String.class, int.class); Object o1 = constructor1.newInstance("上云",18);
-
反射操作方法
-
实例方法
-
获取字节码对象
-
使用构造器获取对象
-
获取方法对象
-
反射调用方法
-
调用方法
- Object invoke(Object obj,Object…args)
Class<?> aClass = Class.forName("cn.sycoder.MethedDemo.MethodDemo"); Constructor<?> constructor = aClass.getConstructor(); Object o = constructor.newInstance(); Method eat = aClass.getMethod("eat"); Object invoke = eat.invoke(o); Method eat1 = aClass.getMethod("eat", String.class); Object obj = eat1.invoke(o, "鱼");
-
-
-
静态方法
-
静态方法不属于对象,属于类本身
-
如何执行:
-
获取类字节码对象
-
获取方法对象
-
执行(invoke)不需要传对象
Method getName = aClass.getMethod("getName", String.class); Object object = getName.invoke(null, "上云"); System.out.println(object);
-
-
反射操作字段
-
获取类字节码对象
-
获取对象
-
操作字段
Class<?> aClass = Class.forName("cn.sycoder.FiledDemo.FiledDemo"); Constructor<?> constructor = aClass.getConstructor(); Object o = constructor.newInstance(); Field age = aClass.getDeclaredField("age"); age.setAccessible(true); age.set(o,19); Object o1 = age.get(o); System.out.println(o1);
类加载器加载 Properties
-
使用绝对路径
//使用绝对路径 Properties properties = new Properties(); FileInputStream in = new FileInputStream(path); properties.load(in); System.out.println(properties);
-
使用相对路径从根目录开始找
Properties properties = new Properties(); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream(path); properties.load(resourceAsStream); System.out.println(properties);
-
使用相对路径(相对于我们使用的类的文件夹开始)
Properties properties = new Properties(); InputStream inputStream = Test.class.getResourceAsStream(path); properties.load(inputStream); System.out.println(properties);
多线程
并行和并发
-
需求:边打英雄联盟和边听音乐
-
问题:只能先后关系,并不能同时发生
- 多进程或者多线程来解决
-
并行和并发:
-
并行:多件事情在同一时刻发生
-
并发:多件事情在同一时间段发生,同一时间段多个程序同时运行
-
-
单个CPU:一个时间点只能执行一个程序,多个程序可以交替执行。
-
线程调度:多个线程以某个顺序执行
进程与线程
- 进程:内存中的应用程序,每个进程有一块独立的内存空间,一个应用程序可以同时启用多个进程,有独立内存空间,至少有一个线程
- 线程:进程中的执行任务单元,一个进程里面可以拥有多个线程,栈空间独立,堆内存共享
- 多线程的优势:因为堆内存是共享的,所以内存的开销是不是较少。
- 多线程具体运行那个线程,取决于 CPU调度
- java 程序中最少有几个线程:
- 主线程
- GC垃圾回收线程
- 线程的调度:JVM 采用抢占式的调度方式,没有采用分时概念,谁抢到谁执行。
- 多线程的应用:
- 抢票
- 游戏
- 多线程下载
- 宽带带宽:以位(bit)计算的,下载速度以字节(byte),1字节=8位
- 1024KB/s 代表 1M,下载速度,1024/8 128KB/s
多线程实现方式
-
继承 Thread 类
-
重写 run 方法
public class MusicDemo extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("听音乐:"+i); } } }
-
启动线程,调用 start 方法
MusicDemo musicDemo = new MusicDemo(); musicDemo.start();
-
注意:不要去调用 run() 方法
-
-
实现 Runnable 接口
-
实现Runnable 接口,实现 Run 方法
public class PlayGame implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("打游戏:" + i); } } }
-
启动(先创建对象,再创建 Thread 对象通过构造器获得线程对象)
PlayGame playGame = new PlayGame(); Thread thread = new Thread(playGame); thread.start();
-
-
使用匿名内部类来创建线程(只适用于只使用一次的方式)
new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("统计人头数:" + i); } } }).start();
线程的调度
-
线程控制操作:Thread.sleep(1000L);
-
线程合并(联合线程):
- join方法(操作线程先执行完之后,等待线程才执行)
-
守护线程:GC 使用
- 目的:为其它线程服务的—GC—
- 特点:主线程结束,守护线程就结束
- 创建线程:默认是前台线程
- 获取守护线程thread.setDaemon(true),必须再 start() 方法之前
-
获取当前线程及其名称
- Thread t = Thread.currentThread();
- t.getName();
-
线程设置优先级
-
注意:优先级高,只不过是获取执行机会更大(取决于CPU调度)
/** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
-
-
线程的让位:
- yield 方法:当前线程对象去提示CPU调度器自己愿意让出CPU资源(但是调度器可以忽略你的请求)
- sleep 和 yield 方法的区别
- 都能使当前处于运行状态的线程暂时放弃 cpu 把机会给其它线程
- sleep 方法给其它线程运行机会,但是不考虑其它线程的优先级,yield 只会给相同优先级,或者更高优先级的线程运行机会
- 调用 sleep 方法之后,线程进入计时等待状态,yield 进入就绪状态
线程同步
-
需求:猴子(3个 A B C)吃香蕉30个,使用多线程的方式实现
-
继承 Thread 类的方式
-
同一个编号的香蕉被多个猴子吃
public class MonkeyDemo extends Thread { private int num = 30;//香蕉总数 @Override public void run() { for (int i = 0; i < 30; i++) { if (num > 0) { System.out.println(super.getName() + "吃了编号为:" + num-- + "的香蕉"); } } } } MonkeyDemo monkeyA = new MonkeyDemo(); monkeyA.setName("A"); MonkeyDemo monkeyB = new MonkeyDemo(); monkeyB.setName("B"); MonkeyDemo monkeyC = new MonkeyDemo(); monkeyC.setName("C"); monkeyA.start(); monkeyB.start(); monkeyC.start();}}
-
-
实现 Runnable 接口
- 是否可以共享同一个资源的原因,实现方式可以做到(当有网络延迟的时候,也不可以做到)
public class MonkeyRunnable implements Runnable{ private int num = 30; @Override public void run() { for (int i = 0; i < 30; i++) { if (num > 0) { System.out.println(Thread.currentThread().getName() + "吃了编号为:" + num-- + "的香蕉"); } } } } MonkeyRunnable runnable = new MonkeyRunnable(); new Thread(runnable,"A").start(); new Thread(runnable,"B").start(); new Thread(runnable,"C").start();
-
-
解决多个猴子不能同时吃到同一个香蕉的案例
-
同步代码块
-
语法
-
非静态
-
同步锁是谁?
- 非静态的来说,同步锁就是 this
- 静态的来说,类的字节对象(MonkeyRunnable.class)
synchronized(同步锁){ }
-
为了保证每个线程都能够正常执行,原子操作,一般的,把当前并发访问的共同资源作为同步监听对象
-
任何时候,只能运行一个线程拥有同步锁,谁拿到谁执行,其它的等待
-
不要使用同步锁去修饰 run 方法,修饰之后,某一个线程执行完了其它的才可以执行
-
-
同步方法
synchronized public void eat() { //业务逻辑 }
-
锁机制(Lock):
Lock lock = new ReentrantLock(); //上锁 lock.lock(); if (num > 0) { System.out.println(Thread.currentThread().getName() + "吃了编号为:" + num-- + "的香蕉"); } //解锁 lock.unlock();
-
-
同步代码块,同步方法,同步锁如何选用?
- 尽量减少锁的作用域(Synchronized)
- 建议使用锁机制(很好的去控制锁的作用范围,性能更高)
生产者与消费者模式
-
线程通信:不同线程执行不同任务,如果这些任务存在关系,线程之间必须能够通信,协调完成工作
-
需求:实现生产者与消费者模式
- 通过生产者与消费者操作共同资源
- 使用多个线程来做生产者(Producer)
- 使用多个线程来做消费者(Consumer)
-
生产者以及消费者示意图:(体现面向对象设计原则:低耦合)
-
主板与集成显卡(高耦合)
//主板 public class Producer{ private Consumer cu; } //集成显卡 public class Consumer{ private Producer p; }
-
主板和内存条(方便升级)(低耦合)
//主板 public class Producer{ //资源 private ShareResource resource; } //内存 public class Consumer{ //资源 private ShareResource resource; } //内存卡槽 public class ShareResource{ }
-
实现生产者与消费者
public class Producer implements Runnable {
private ShareResource resource;
public Producer(ShareResource resource){
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i % 2 == 0){
resource.push("女士服装","s");
}else{
resource.push("男士服装","XXXL");
}
}
}
}
public class Consumer implements Runnable{
private ShareResource resource;
public Consumer(ShareResource resource){
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
resource.pop();
}
}
}
public class ShareResource {
private String name;
private String size;
/**
* 给生产者推送数据的
*
* @param name
* @param size
*/
public void push(String name, String size) {
this.name = name;
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.size = size;
}
public void pop() {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("服装名称:" + this.name + " 尺码: " + this.size);
}
}
生产者与消费者出现数据紊乱的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qYO4vRSb-1664677606244)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220317111644139.png)]
- 问题1:出现数据紊乱问题
- 解决方案:只要保证生产名字和尺码的过程中保持同步,中间不被消费者消费
- 同步方法/同步代码块/锁来保持同步性
- 解决方案:只要保证生产名字和尺码的过程中保持同步,中间不被消费者消费
- 问题2:应该生产一个数据消费一个数据
- 应该交替出现,男士–XXL 女士–s 男士–xxl 女士–s
- 使用等待唤醒机制
- 应该交替出现,男士–XXL 女士–s 男士–xxl 女士–s
线程通信
-
线程通信(wait)和(notify) 方法介绍
- wait():执行该方法的线程对象要释放同步锁,JVM 会把该线程放到等待池里面,等待其它的线程唤醒它
- notify():执行该方法的线程唤醒在等待池中的线程,把线程转到锁池中去等待。
- notifyAll():执行该方法的线程,唤醒在等待池中的所有线程,把这些线程转移到锁池中去等待。
- 注意点:这些方法,只能被同步监听锁对象锁(同步锁)调用
-
同步监听锁对象(同步锁):多个线程有共同对象使用时,多个线程之间才会出现互斥现象,共同的这个对象就叫同步监听锁对象
-
同步锁池:同步锁必须选择多个线程共同的资源对象,当生产者生产数据时(先获取锁),生产者没生产出来的时候,消费者只能等待(锁池等待),等生产者生产完,释放同步锁时,消费者此时就可以抢锁来进行消费。
-
P线程和C线程S对象(同步锁),P,C线程如何通信(wait notify)
- P线程执行S对象的同步方法,P线程持有S对象的锁,C线程是不是在S对象的锁池中等待。
- P线程生产完,执行 wait() ,释放S对象的锁,进入S对象的等待池中去等待生产。
- S对象锁池中等待的C线程获取S对象的锁,执行S对象的消费同步方法。
- C线程消费结束,执行完S对象的同步消费方法时,执行notify()唤醒P线程,JVM把P从S对象等待池中移到S对象的锁池中,等待获取锁,C 线程释放锁,P获取锁,继续循环。
public class ShareResource { private String name; private String size; private boolean isEmpty = true;//判断资源对象是否为空 /** * 给生产者推送数据的 * * @param name * @param size */ synchronized public void push(String name, String size) { try { //判断资源对象是否为空,空的时候等待 while (!isEmpty){ this.wait(); } this.name = name; Thread.sleep(1000L); this.size = size; isEmpty = false; } catch (InterruptedException e) { e.printStackTrace(); }finally { this.notify();//唤醒一个消费者 } } synchronized public void pop() { try { //如果资源为空,消费者等待 while (isEmpty){ this.wait(); } Thread.sleep(1000L); System.out.println("服装名称:" + this.name + " 尺码: " + this.size); isEmpty = true; this.notify(); } catch (InterruptedException e) { e.printStackTrace(); } } }
-
Lock 和 Condition 接口:
- wait 和 notify 只能被同步监听锁对象调用,否则报错,但是Lock 机制根本没有同步锁,也没有获取释放锁的逻辑。
- 使用Condition 接口对象的 await singnal signalAll 方法取代 Object 类中的 wait notify notifyAll 方法
public class ShareResource { private String name; private String size; private boolean isEmpty = true;//判断资源对象是否为空 private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); /** * 给生产者推送数据的 * * @param name * @param size */ public void push(String name, String size) { lock.lock();//获取锁 try { //判断资源对象是否为空,空的时候等待 while (!isEmpty){ condition.await(); } this.name = name; Thread.sleep(1000L); this.size = size; isEmpty = false; condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock();//释放锁 } } public void pop() { lock.lock();//上锁 try { //如果资源为空,消费者等待 while (isEmpty){ condition.await(); } Thread.sleep(1000L); System.out.println("服装名称:" + this.name + " 尺码: " + this.size); isEmpty = true; condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock();//释放锁 } } }
建议:建议使用第二种方式
死锁
- 多线程通信时,很容易出现死锁现象,死锁并且是不能解决的,只能避免。
- P线程等待有C线程持有的锁,而我们C线程又去等待P线程持有的锁,发生死锁现象,JVM不检测也不是图避免这种情况
- 如何避免死锁:当我们多个线程去访问共享资源的时候 A,B,C,保证每一个线程都按照相同的顺序去访问。
- Thread 类有一些过时的方法:
- suspend() 使正在运行的线程放弃 CPU 暂停运行。
- resume() 是暂停线程恢复运行
- 这两方法很容易导致死锁,不要去使用
- P线程获取锁对象,去执行同步方法,如果C线程调用P线程的暂停方法,此时P就会放弃CPU但是不释放锁。
- 如何避免:上锁—释放锁
线程的生命周期
-
生命周期:一个物品从出生—死亡
-
线程的生命周期(Thread.State):
-
NEW:使用new 创建线程对象的时候,只分配了空间,调用 start() 方法的时候,线程启动
-
RUNNABLE:(可运行状态)使用了 start 之后,变成了两种状态
- ready:准备就绪,等待 cpu 的调度
- running :获取到了 cpu 的调度
-
BLOCKED:(阻塞的状态)运行中的线程,因为某些原因,放弃了 cpu 暂停运行,此时 jvm 不会给线程分配 cpu
- 当 P线程处于运行状态时,但是,没有获取到同步锁, JVM 就会把 P线程放到同步锁对象的锁池中去,P阻塞
- 线程运行中发出(IO请求),此时阻塞
-
WAITING:(等待的状态)等待状态只能被其它线程唤醒,
- 运行中的线程调用了 wait() 方法,此时 jvm 把当前的线程存放在对象等待池中
-
TIMED_WAITING:(计时等待)wait(long),sleep(long),就变成计时等待
- 线程运行过程中,调用上面方法,此时 JVM把当前线程存在对象等待池中
-
TERMINATED:(停止)表示线程结束了
- 正常的执行完线程
- 出现异常
-
定时器
1.Timer 里面有一个常用的方法
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("----");
}
},0,1000L);
线程组
-
可以对线程集中管理,ThreadGroup
-
用户创建线程对象时,可以通过构造器指定所属的线程组
ThreadGroup groups = new ThreadGroup("商品上架线程组"); ThreadGroup groups1 = new ThreadGroup("fihfiwehuif"); GoodsThread a = new GoodsThread(groups, "A"); a.start(); new GoodsThread(groups,"B").start(); new GoodsThread(groups1,"C").start(); new GoodsThread("efwefewfewfe").start(); public GoodsThread(ThreadGroup group, String name) { super(group,name); }
-
A线程中创建B线程,B线程默认加入A线程线程组
-
默认创建的线程,都属于main 线程
注解
-
java 注解:又称为java标记,jdk1.5开始引入的注释机制,注解可以通过反射获取标记的内容,编译器生成类字节码文件时,标记也可嵌入到字节码中。
-
java 内置注解:
-
使用在代码上的注解
-
@Override-检查方法是否被重写
public class Test extends AnnotationDemo { @Override public void test() { super.test(); } }
-
@Deprecated-标记过时
@Deprecated public String toLocaleString() { DateFormat formatter = DateFormat.getDateTimeInstance(); return formatter.format(this); }
-
@SuppressWarnings-忽略注解中声明的警告
-
-
元注解(DIV):
- @Documented:标记这个注解是否包含在用户文档中
- @Retention:标记注解保存的时间
- SOURCE:源代码中
- CLASS:字节码中
- RUNTME:运行时
- @Target:标记注解的使用目标
- METHOD
- FIELD
- TYPE
- @Inherited-标记注解是继承哪个注解类(默认是没有继承的)
-
自定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "MyAnnotation"; } @MyAnnotation("eat1") public void eat(){ //s }
-
自定义注解
-
案例:自定义关系型数据库的动态sql (mybatis)
select username,address,phone from user where username=zhangsan and password=123456
java8 特性
-
Lambda表达式:允许一个函数作为方法参数传递到方法中去
(parm)->expression; 或 (parm)->{expression;}
-
实操
Callable<Integer> integerCallable = () -> 5; IntFunction<Integer> integerIntFunction = (int x) -> (3 * x); IntBinaryOperator intBinaryOperator = (int a, int b) -> a + b; Consumer<String> stringConsumer = (String s) -> { System.out.println(s); };
-
-
Stream:数据源的元素队列,支持聚合操作(想象成流)
-
数据源:集合,数组
-
聚合操作:filter,map,reduce,find,match,sorted
-
实操
-
filter(过滤)
List<String> list = new ArrayList(); list.add("A"); list.add(""); list.add("B"); System.out.println(list); List<String> collect = list.stream().filter(x -> !x.isEmpty()).collect(Collectors.toList());
-
forEach(循环)
list.stream().forEach(x-> System.out.println(x));
-
map(做映射)
List<Integer> collect1 = list1.stream().map(x -> x * x).collect(Collectors.toList());
-
limit(截取指定数量)
List<Integer> collect2 = list1.stream().limit(2).collect(Collectors.toList());
-
sorted(排序)
List<Integer> collect3 = list1.stream().sorted((a, b) -> b.compareTo(a)).collect(Collectors.toList());
-
Collectors(工具类)
Collectors.toList() Collectors.toSet() Collectors.toMap()
-
mapToInt(统计操作,max,min,avg,sum)
IntSummaryStatistics intSummaryStatistics = list1.stream().mapToInt(a -> a).summaryStatistics(); System.out.println(intSummaryStatistics.getMin());
-
-
-
接口中提供默认方法和静态方法
-
默认方法
public interface DefaultInterfaceDemo { default void eat(String name){ System.out.println(name); } }
-
静态方法
static void eat(String name,int count){ System.out.println(name); }
-
-
新的 date api
-
旧版存在问题:
- 设计较差(java.util,java.sql 都有date)
- 非线程安全的
- 处理时区比较麻烦
-
新版提供:
- Local(本地):简化了日期处理,没有时区的问题
- Zoned(时区):指定时区处理时间
//获取当前时间 LocalDateTime now = LocalDateTime.now(); System.out.println(now); //获取时间的年月日 System.out.println(now.getYear() +""+ now.getMonthValue() + now.getDayOfMonth()); //解析 DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime parse = LocalDateTime.parse("2025-10-11 11:11:12", pattern); System.out.println(parse); ZoneId zoneId = ZoneId.systemDefault(); System.out.println(zoneId); ZoneId of = ZoneId.of("Asia/Shanghai"); System.out.println(of);
-
网络编程
网络概述
-
什么是计算机网络:指将地理位置不同的具有独立功能的多台计算机及其设备,通过通信线路连接起来,在网络操作系统,网络管理软件及其网络通信协议的管理和协调下,实现信息共享和资源传递
-
划分:
-
网络划分:
- 局域网
- 广域网
- 互联网
-
网络分层:减少网络设计的复杂性,提供一个规范。不同机器上同等功能层必须采用相同的协议
-
网络模型:使用OSI七层架构TCP和IP体系
-
TCP/IP :是网络互联的通信协议
- 应用层:TELNET,FTP,SMTP等
- 运输层:
- TCP:是可靠的,通过三次握手来连接数据传输服务
- UDP:不可靠的,是无连接的数据传输服务
- 网络层:主要解决主机到主机之间的通信问题(网络协议,IP可靠的协议,无连接的数据传递服务)
- 网络接口层:负责监视数据在主机和网络之间的交换
-
-
套接字:源IP和目的IP以及端口号和目的端口号的组合,用来标识客户端请求的服务器和服务
-
网络编程:使用套接字来达到进程间通信目的的编程就是网络编程
-
进程间通信:同一电脑不同进程之间通信,(网络连接)A电脑中的进程和B电脑进程通信
-
java.net 包
IP
-
ip:网络之间互联的协议
-
ip版本:
- ipv4:192.168.2.12
- ipv6:fe80::495a:afb0:a010:1442%7
- ip 地址编址方案:
- A(10.0.0.0)10.255.255.255
- B(172.16.0.0)172.16.255.255
- C(192.168.0.0)192.168.255.255
-
本机地址
- localhost
- 127.0.0.1
- 本机ip(cmd - ipconfig)
-
实操
InetAddress ip = InetAddress.getByName("LAPTOP-IQLK33VN"); System.out.println(ip.getHostName()); System.out.println(ip.getHostAddress());
PORT
- 端口:设备和外界设备通信交流的出口
- 常用端口:80,8080,3306,6379
- 注意:同一台电脑中,不能同时出现同一个端口,端口冲突
协议
- 协议:网络协议的简称,是计算机双方必须遵守的规则
- http:超文本传输协议(不收费)
- https:使用安全的套接字传输的超文本协议(收费)
- ftp:文件传输协议
- file:文件,本机电脑或者网上分享的文件
URI 与 URL
- URI:统一资源标识符,是用来标识某一个互联网资源名称的字符串
- 包含:主机名,端口,标识符,相对uri
- http://www.baidu.com/hello.html
- URL:统一资源定位符,是互联网的标准资源地址
- 包含:主机名,端口,标识符,具体路径
- http://ip:80/home/index.png
传输层协议
- TCP:面向连接(经历三次握手)、传输可靠(保证了数据正确性,数据顺序的正确性),用来传输数据量大,(流模式)、速度慢,建立连接开销大。服务端和客户端
- UDP:面向非连接、传输不可靠(丢包(数据丢失))、用于传输少量数据(数据报包模式)、速度快。发送端和接收端
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6EkgfB56-1664677606244)(G:\学习资料\十指波\上云JavaSE最新基础课程\笔记\picture-master\static\image-20220318135045397.png)]
TCP
public class Client {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 9999);
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(in);
while (scanner.hasNext()){
System.out.println(scanner.next());
}
scanner.close();
socket.close();
}
}
public class Server {
public static void main(String[] args) throws IOException {
//创建服务端,指定端口为9999
ServerSocket socket = new ServerSocket(9999);
System.out.println("服务端准备就绪");
//获取客户端的连接请求,阻塞的。
Socket accept = socket.accept();
System.out.println("获取到客户端的连接:"+accept.getInetAddress());
PrintStream printStream = new PrintStream(accept.getOutputStream());
printStream.print("你好,这里是服务端,感谢你的连接");
printStream.close();
socket.close();
}
}
UDP
public class Send {
public static void main(String[] args) throws IOException {
String data = "我是发送者";
//创建发送端对象
DatagramSocket datagramSocket = new DatagramSocket(10010);
//发送数据
DatagramPacket packet = new DatagramPacket(data.getBytes(),//发送的数据
data.getBytes().length,//发送多长
InetAddress.getLocalHost(),//目标发送地址
110);//发送的端口
datagramSocket.send(packet);
datagramSocket.close();
}
}
public class Receive {
public static void main(String[] args) throws IOException {
//创建接收对象
DatagramSocket datagramPacket = new DatagramSocket(10010);
byte[] buffer = new byte[1024];
//接收数据
DatagramPacket packet = new DatagramPacket(buffer, 1024);
datagramPacket.receive(packet);
String string = new String(buffer, 0, packet.getLength());
System.out.println(string);
datagramPacket.close();
}
}
常见错误
Exception in thread "main" java.net.BindException: Address already in use: Cannot bind
at java.net.DualStackPlainDatagramSocketImpl.socketBind(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.bind0(DualStackPlainDatagramSocketImpl.java:80)
at java.net.AbstractPlainDatagramSocketImpl.bind(AbstractPlainDatagramSocketImpl.java:94)
at java.net.DatagramSocket.bind(DatagramSocket.java:392)
at java.net.DatagramSocket.<init>(DatagramSocket.java:242)
at java.net.DatagramSocket.<init>(DatagramSocket.java:299)
at java.net.DatagramSocket.<init>(DatagramSocket.java:271)
at cn.sycoder.UdpDemo.Send.main(Send.java:14)