从零开始的Java知识(上)
从零开始的Java知识
- CMD的常见命令(Day01)
- 配置环境变量的作用?
- 下载与安装JDK(略)
- 编写第一个程序HelloWorld(略)
- 高级记事本安装(略,正版收费)
- 各个语言的运行方式区别
- 为什么Java可以实现跨平台?
- JDK和JRE的认识
- JDK是什么?由什么组成
- JRE是什么?由什么组成
- JDK、JRE、JVM三者的包含关系
- 注释(Day02)
- 关键字
- 字面量
- 变量
- 计算机的数据存储规则
- 计算机有哪几种数据表现形式
- 1.文本
- 数字
- 字母
- 汉字
- 2.图片
- 3.声音
- Java的数据类型
- 基本数据类型
- 标识符
- 硬性规则
- 软性建议
- Scanner类
- 使用步骤
- IDEA(目前业内最好用的开发软件)
- 初始使用
- IEDA的基础设置
- 运算符和表达式(Day03)
- 算术运算符
- 自增自减运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 三元运算符
- 原码,反码,补码
- 其他的运算符
- 流程控制语句(Day04)
- 顺序结构
- 分支结构
- 循环结构
- 流程控制语句练习(Day05)
- 逢7过题目
- 平方根
- 质数判断
- 随机数猜测
- 数组
- 数组的定义
- 数组的初始化
- 打印数组分析
- 数组索引
- 数组内存图
- 方法(Day06)
- 方法的定义和调用
- 方法的重载
- 方法的内存图
- 综合练习(Day7)
- 二维数组
- 二位数组的创建和初始化
- 二维数组的内存图
- 面向对象(Day8)
- 类和对象
- 封装
- private
- this
- 构造方法
- 标准JavaBean
- 对象的内存图
- 执行Test类main方法生成一个User对象的内存过程
- 基本数据类型和引用数据类型的区别
- this的内存原理
- 成员变量和局部变量区别
- 面向对象综合练习(Day9)
- Scanner拓展
- 字符串(Day10)
- API
- String
- String的初始化
- == 究竟比较的是什么?
- StringBuilder
- StringJoiner
- 字符串原理
- 字符串的拼接原理
- 集合(Day11)
- 控制台版学生管理系统(略)
- 学生管理系统升级(Day12)
- static(Day13)
- 静态变量
- 静态方法
- 静态方法的注意事项
- 重新认识main方法
- 继承
- 继承注意事项
- 子类到底能继承父类哪些内容
- 继承中成员变量和成员方法的访问特点
- 重写
- 构造方法的访问特点
- this & super
- 多态(Day14)
- 多态中成员变量的调用
- 多态中成员方法的调用
- 多态的优缺点
- 包
- final关键字
- 权限访问修饰符
- 代码块
- 抽象类(Day15)
- 接口
- 接口的成员特点
- 接口与接口的关系
- 接口相关拓展
- 设计模式
- 适配器设计模式
- 内部类
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类
- 拼图小游戏(Day16)
- JFrame 窗体
- 菜单
- 窗体内容
- 窗体内容美化(Day17)
- 移动图片
- 弹出提示图
- 其他(略)
- 常用API(Day18)
- Math
- System
- Runtime
- Object和Objects
- BigInteger
- BigDecimal
- Arrays
- 正则表达式(Day19)
- 1. 字符数据的校验
- 2.文本数据的爬取
- 3.文本数据爬取拓展
- 4.捕获分组和非捕获分组
- 日期类(Day20)
- JDK7版本相关
- Date类
- SimpleDateFormat类
- Calendar类
- JDK8版本相关
- ZoneId类
- Instant类
- ZonedDateTime
- DateTimeFormatter
- LocalDate
- LocalTime
- LocalDateTime
- Duration
- Period
- ChronoUnit
- 包装类
- Integer装箱和拆箱
- Integer的进制转换和字符串解析
- 算法基础(Day21)
- 查找算法
- 基本查找(顺序查找)
- 二分查找
- 插值查找
- 分块查找
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 递归算法
- 快速排序
- Lambda表达式
- 集合进阶(Day22)
- 单列集合顶层接口(Collection)
- 迭代器(Iterator)
- 增强For和Lambda表达式
- 单列有序集合接口(List)
- 单列集合List遍历特有方式
- 数据结构
- ArrayList的扩容机制
- LinkedList区别与ArrayList
- iterator迭代器底层
- 泛型
- 树
- 二叉树的演变
- 平衡二叉树的旋转机制
- 红黑树
- Set系列集合
- HashSet集合
- LinkedHashSet
- TreeSet
CMD的常见命令(Day01)
-
切换盘符 (磁盘名称:)

-
展示当前文件夹文件 (dir)
-

-
进入子文件夹 (cd 子文件夹名)

-
清空 (cls)


-
返回上一级文件夹 (cd …)

-
进入指定文件夹(cd \根目录文件夹\下一级文件夹)

-
退出(exit)
-
打开文件(文件名)
配置环境变量的作用?
为了在任何位置都能够使用或读取环境变量配置的文件。后续JRE的配置需要设置在环境变量中。
下载与安装JDK(略)
编写第一个程序HelloWorld(略)
高级记事本安装(略,正版收费)
各个语言的运行方式区别
- C和C++ 是编译型语言,需要整体编译通过,再交给解释器整体运行
- Python 是解释型语言,不需要编译,交给解释器逐行运行
- JAVA 是混合型语言,需要整体编译通过,交给解释器逐行运行
为什么Java可以实现跨平台?
- 关键是Java编译后的文件,可以再不同的JVM中运行
- 也是因为Java运行不是再操作系统上,而是JDK中Java虚拟机上
JDK和JRE的认识
JDK是什么?由什么组成
JDK是开发工具包,由JVM、核心类库、开发工具组成
JRE是什么?由什么组成
JRE是JAVA运行环境,由JVM、核心类库、运行工具组成
JDK、JRE、JVM三者的包含关系
JDK包含JRE,JRE包含JVM
注释(Day02)
- 注释是对代码解释说明性的文字
- 注释分为单行注释、多行注释和文档注释
- 注释不参与编译运行
- 多行注释不可以嵌套使用
关键字
- 关键字是Java赋予特定含义的英文字母
- 全部小写
- 再常见的代码编译器中,关键字会高亮显示
- class关键字是对类的定义,后面跟类名
字面量
- 字面量是Java程序对数据格式的定义
- 字面量包括整数,小数,字符,字符串,布尔,空值
- 特殊的字面量 \t ,称之为制表符,可以将字符补全至8位或者是8的倍数(正好8的倍数则再补全8位)
- 特殊的字面量null,不可以直接打印
变量
- 在程序中,值可能会不断变化的量
- 书写格式:数据类型 变量名 = 数据值;
- 变量名在一个方法体中不能重复
- 变量在使用之前必须赋值
计算机的数据存储规则
计算机有哪几种数据表现形式
1.文本
文本又包含了数字,字母和汉字
数字
数字有不同的进制,包括二进制,八进制,十进制,十六机制
- 二进制是由0,1数字组成,计算机的任何数据都是以二进制的方式存储的
- 八进制是由0~7数字组成
- 十六进制是由0 ~ 9,a ~ b组成的
其他进制转换成十进制
系数 * 基数 的 权次幂 之和
系数是指每一个位置的数字
基数是指当前的进制是几
全次幂是指数字从右向左数(0,1,2,3…)
十进制转换成其他进制
除基取余法 当商为零时,余数倒着读就是对应的进制数
数字是转换成二进制后,存入计算机中
字母
字母是从ASCII码表找到字母对应的二进制后,存入计算机中
汉字
汉字是从GBK或Unicode码表找到对应汉字的二进制后,存入计算机中
2.图片
- 图片又包含黑白图,灰度图,彩色图
- 图片的三要素包含分辨率,像素,三原色
- 分辨率是指一个屏幕宽和高分别由多少个像素组成的集合
- 像素对黑白图来说就是0,1,对灰度图来说,根据灰度表是0~255,对彩色图来说是RGB对应的(0 ~ 255)的集合
3.声音
声音是对波形图进行采样,然后以二进制的方式存储在计算机中的。
Java的数据类型
Java数据类型分为基本数据类型和引用数据类型。
基本数据类型
| 基本数据类型 | 取值范围 | 占用字节 | 注意事项 |
|---|---|---|---|
| byte | -128~127 | 1 | |
| short | 2 | ||
| int | -21亿~21亿 | 4 | 默认 |
| long | 8 | 尾部加L/l | |
| float | 4 | 尾部加F/f | |
| double | 8 | 默认浮点型 | |
| char | 2 | 单引号且只有一个字符 | |
| boolean | 1 | 只有true,false |
取值范围顺序由大到小 double float long int short byte
标识符
对变量,方法或类名起的名字,叫标识符。
硬性规则
- 组成:由字母,数字,下划线(_)和美元符($)组成
- 开头:不可以以数字开头
- 关键字:不可以是Java关键字
- 大小写:区分大小写
软性建议
- 命名采用驼峰式命名法
- 对于类名采用大驼峰命名法; 每个首字母大写
- 对于变量名和方法名采用小驼峰命名法; 第二个字母开始首字母大写
- 标识符要见名知意
Scanner类
Scanner类的作用可以接收键盘输入的数字。用于变量接收一个动态的数字。
使用步骤
- 导包:在类名之前导入Sacnner包,import java.util.Scanner;
- 创建对象:实例化Scanner对象,Scanner sc = new Scanner(System.in);
- 接收键盘数据: int num = sc.nextInt();
IDEA(目前业内最好用的开发软件)
- IDEA是用于Java语言开发的集成环境。
- 集成环境指把编写,编译,运行,调试等多种功能综合在一起的开发工具。
- IDEA的项目结构: 项目(project) -> 模块(module) -> 包(package) -> 类(class)
初始使用
- 新建一个Empty Project
- 新建一个Module
- 在src下新建包
- 在包下新建类
IEDA的基础设置
-
更换主题

-
设置字体的大小和样式

-
设置注释的样式和颜色

-
自动导包

-
提示忽略大小写

-
设置软件背景图

运算符和表达式(Day03)
运算符是对字面量或变量操作的符号
表达式是通过运算符连接字面量或变量的并且符合Java语法的式子。
| 运算符分类 | 符号 |
|---|---|
| 算术运算符 | + - * / % |
算术运算符
算术运算符包括:+ - * / %
注意:
- 只有整数参与运算,结果只会是整数
- 浮点数参与运算,结果可能不准确
- 不同的数值类型参与算术运算,取值范围小数值类型会隐式转换为取值范围大的数值类型,然后参与运算
- byte,short,char参与算术运算的时候,会先转换成int,然后参与运算
- 数值类型取值大小:byte < short < int < long < float < double
- 当取值范围大的数值类型转换成取值范围小的数值类型,需要强制转换,强制转换可能会发生错误
- 字符串 + 数据类型,是拼接操作,不是算术运算
- 字符参与算术运算,字符会查询ASCII码表找到对应的数值后,然后参与运算
自增自减运算符
++(自变量+1)、–(自变量-1)
- 后加加 参与运算时,先用后加
- 先加加 参与运算时,先加后用
赋值运算符
=、+=、-=、 *= 、/=、 %=
注意:+=、-=、 *= 、/=、 %=在运算的隐藏执行了强转。
例子: short s = 10; s+=1; => s = (short)(s+1);
关系运算符
== != > >= < <= 返回值为boolean类型,true/false
逻辑运算符
&(并且) |(或者) &&(短路并且) ||(短路或者) ^(异或) !(取反)
其中最常用的是**&&**,||, !,短路与,短路或具有短路效果
三元运算符
三元运算符的表达式结果必须要被接收或打印。
关系表达式?表达式1:表达式2; // 关系表达式为真,返回表达式1的结果,为假,返回表达式2的结果。
原码,反码,补码
- 正数的原码,反码,补码是相同的
- 负数的反码是原码除符号位其他位取反,补码是反码+1
- 反码最初是为了解决负数的运算,但是运算过程存在误差正负零,然后补码解决了此问题
- 一个字节的情况,-128的补码1000 0000,没有原码和反码
- 原码是计算机展示的十进制数转换成二进制的数字
- 计算机中存储的二进制是补码,因此byte的取值范围是-128~127
- 计算机展示的二机制的原码转换成十进制
- 原码的首位是符号位,其他位是数值位,首位为零时表示正,为一表示负
其他的运算符
- &(按位与) |(按位或) <<(左移) >>(右移) >>>(无符号右移)
- 按位与是都为1,才为1
- 按位或是有1,就为1
- 左移是低位补零,移动一位就乘以2
- 右移是符号位不变
- 无符号右移是符号位补零
流程控制语句(Day04)
顺序结构
Java代码执行是自上而下的顺序结构
分支结构
- if分支(已掌握) (多应用范围)
- switch分支(有限个列举)
/*1.表达式最初只能是byte,short,int,char2.jdk5引入枚举3.jdk7引入String
*/
switch(表达式){ // 1.值必须是字面量,不可以是变量// 2.值不能相同// 3.全部没有匹配(没有break,case穿透也走),则走default语句case 值1:语句体1;break;case 值2:语句体2;break;...// default的位置是不固定的,不是必须存在的部分,可以省略default:语句体n+1;break;
}
- switch在JDK12引入新特性
package com.itheima.demo;import java.util.Scanner;/*** switch 新特性案例*/
public class WeekDay {static Scanner sc = new Scanner(System.in);public static void main(String[] args) {int num = sc.nextInt();String result = switch (num){case 1,2,3,4,5 -> "工作日";case 6,7-> "休息日";default-> "??";};System.out.println(result);}
}
循环结构
- 循环表示重复做某件事情。
- 循环要有明确的开始和结束条件
- for循环入门案例
package com.itheima.demo;/*** 打印五次helloWorld*/
public class ForDemo {public static void main(String[] args) {// (初始化语句;条件判断语句;条件控制语句)for (int i = 1; i <= 5; i++) {// 循环体System.out.println("Hello World");}}
}
- for循环累加思想练习
package com.itheima.demo;/*** 累加思想入门:求1~5的和*/
public class ForDemo {public static void main(String[] args) {// 变量的作用域是在当前大括号内,如果定义在for循环,for循环外无法使用,出了for循环变量就被销毁啦int sum = 0;for (int i = 1; i <= 5; i++) {sum += i;}System.out.println("和是:" + sum);}
}
- while循环入门案例
package com.itheima.demo;/*** while循环入门案例*/
public class WhileDemo {public static void main(String[] args) {// 初始化语句int i = 1;while (i <= 100) { // 条件判断语句System.out.println(i); // 循环体i++; // 条件控制语句}}
}
- 回文练习
package com.itheima.demo;import java.util.Scanner;/*** 输入一个数,判断是否是回文数*/
public class ForDemo3 {static Scanner sc = new Scanner(System.in);public static void main(String[] args) {System.out.print("请输入一个整数:");int number = sc.nextInt();// 方法一(涉及知识点String API, for循环):// 目的: 对一个数字进行前后的对比String str = String.valueOf(number);boolean result = true;for (int i = 0; i < str.length(); i++) {if (!str.substring(i, i + 1).equals(str.substring(str.length() - i - 1, str.length() - i))) {result = false;}}System.out.println(result);// 方法二(while循环):// 目的:得到一个倒着的数字,然后比较int zheng = number;int fan = 0;while (number != 0) {int weiNumber = number % 10;fan = fan * 10 + weiNumber;number /= 10;}System.out.println(zheng == fan);}
}
- do…while(了解)
- 无限循环(顾名思义)
// for(;;){循环体}
// while(true){循环体}(常用)
- continue关键字,结束本次循环,进入下次循环
- break关键字,跳出整个循环
流程控制语句练习(Day05)
逢7过题目
package com.itheima.demo;/*** 1~100逢7过*/
public class ForDemo4 {public static void main(String[] args) {for (int i = 1; i <= 100; i++) {// 是7的倍数 或者个位或者十位是7if (i % 7 == 0 || i % 10 == 7 || i / 10 % 10 == 7) {System.out.println("过");continue;}System.out.println(i);}}
}
平方根
package com.itheima.demo;import java.util.Scanner;/*** 平方根 键盘输入大于等于2的整数,获取平方根的整数部分* 本题难点:思路*/
public class WhileDemo05 {static Scanner sc = new Scanner(System.in);public static void main(String[] args) {System.out.println("输入大于等于2的整数:");int num = sc.nextInt();for (int i = 1; i < num; i++) {if (i * i == num) {// 如果i*i == num;则i是num的平方根System.out.println(i + "是" + num + "的平方根");break;} else if (i * i < num && (i + 1) * (i + 1) > num) {// 如果i*i < num && (i+1)*(i+1) >num,则i是num平方根的整数部分System.out.println(i + "是" + num + "的平方根的整数部分");break;}}}
}
质数判断
package com.itheima.demo;import java.util.Scanner;/*** 键盘录入一个正整数,判断是否是质数*/
public class ForDemo5 {static Scanner sc = new Scanner(System.in);public static void main(String[] args) {System.out.println("请输入一个正整数:");int num = sc.nextInt();boolean result = true;// i到num的平方根为止就可以判断是否是质数for (int i = 2; i <= Math.sqrt(num); i++) {//只能被1或其本身整除的数是质数if (num % i == 0) {result = false;break;}}System.out.println("结果是" + result);}
}
随机数猜测
package com.itheima.demo;import java.util.Random;/*** 生成一个随机数(1~100),程序猜测这个数*/
public class ForDemo6 {// 引入Randomstatic Random random = new Random();public static void main(String[] args) {// 包头不包尾int min = 1;int max = 2;int num = random.nextInt(max - min + 1) + min;System.out.println("生成一个随机数(1~100):" + num);int count = 0;for (int i = 1; i <= num; i++) {count++;if (i == num) {System.out.println("随机数是:" + i);break;}}System.out.println("一共猜了" + count + "次");System.out.println("---优化后----");// 代码优化// 二分查找法int count2 = 0;int i = 0;do {if (i < num) {min = i + 1;} else {max = i - 1;}i = (min + max) / 2;count2++;System.out.println("第" + count2 + "次猜" + i);} while (i != num);System.out.println("随机数是:" + i);System.out.println("一共猜了" + count2 + "次");}
}
数组
1.数组是一种容器,可以一次存储多个相同类型的数据
数组的定义
int[] array1;int array2[];
数组的初始化
// 1.两种静态初始化 静态初始化当开始明确了数组填充的值使用// 1.1 完整格式int[] array3 = new int[]{18, 24, 32};// 1.2 简写格式double array4[] = {175.5, 173.5, 182.0};// 2. 一种动态初始化 动态初始化开始只可以确定长度时使用int[] array33 = new int[10];array33[0] = 1;// 注意:数组动态初始化,会赋予默认值// 基本数据类型中:整数 0 浮点数 0.0 char '\u0000' boolean false // 引用数据类型: null
打印数组分析
// 打印是数组的地址//[I@4eec7777、[D@3b07d329 [表示数组 I/D表示数组的类型 @后面是16进制,真正的地址
数组索引
- 数组索引从零开始
- 注意:数组索引不在范围内会报错
数组内存图
- 方法运行就会进入栈中执行
- new出现就会在堆中开辟地址 图1
- 多个数组可以指向同一个地址 图2


方法(Day06)
- 方法是程序执行的最小单位
- 方法用于打包重复或者独立功能的代码
- 方法提高了代码的复用性和可维护性
方法的定义和调用
package com.itheima.demo;/*** 方法练习*/
public class MehthodDemo01 {public static void main(String[] args) {// 无参无返回值方法的调用// 看到方法调用方法,方法执行完毕返回调用处。playGame();//有参无返回值方法的调用// 方法调用填充的变量或字面量的数目和类型要与方法定义是参数保持一致// 方法调用的参数叫实参ChangC(15, 5);cicleS(2.2);// 有参有返回值方法的调用double s1 = getArea(1.2, 2.2);double s2 = getArea(1.5, 1.7);if (s1 > s2) {System.out.println("第一个长方形的面积大");} else if (s1 < s2) {System.out.println("第二个长方形的面积大");} else if (s1 == s2) {System.out.println("两个长方形的面积一样大");}}// 无参无返回值方法的定义public static void playGame() {System.out.println("1.选择角色");System.out.println("2.进入王者峡谷");System.out.println("3.对线");System.out.println("4.推水晶");System.out.println("5.打赢结束");}// 有参无返回值方法的定义// 方法定义的是形参// 计算长方形的周长public static void ChangC(int length, int width) {System.out.println(length * 2 + width * 2);}// 计算圆的面积public static void cicleS(double r) {System.out.println(r * r * Math.PI);}// 比较两个长方形的面积public static double getArea(double len, double width) {return len * width;}
}
方法的重载
- 同一个类中,有数目不同或形参类型不同的多个重名方法称之为重载,与返回值类型无关
方法的内存图
- 基本数据类型和引用数据类型的区别
- 基本数据类型的数据值是存在栈里变量定义的空间中的
- 引用数据类型的数据值是存在堆里开辟的空间的,开辟空间的地址存在栈里变量定义的空间中
- 数据类型的传值传递的就是栈里存储的数据。对应基本数据类型是真正的值,对应引用数据类型是地址。
综合练习(Day7)
二维数组
二维数组应用于对一组数据进行分组
二位数组的创建和初始化
- 静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5,6}};
int[][] arr2 = {{1,2,3},{4,5,6}};
- 动态初始化
int[][] arr3 = new int[2][3];
arr[0][0] = 1;
- 遍历二维数组(略)
- 动态给二维数组赋值(略)
二维数组的内存图
- 栈中执行main方法
- 栈中创建arr变量
- 堆中初始化一个长度为2的二维数组 索引0和1初始为null
- 堆中初始化两个长度为3的一位数组 索引填充值为0,地址分别填充至二维数组
- 栈中arr变量保存堆中的二维数组地址

1.特殊情况1 不指定每个一位数组的长度

- 特殊情况2

面向对象(Day8)
类和对象
- 类是设计图纸,对象是真正的实例
- 定义一个类后,就可以用 类名 对象名 = new 类名();创建一个对象
- 通过对象名.属性; 获取属性值, 通过对象名.方法名(…); 执行方法;
- 建议类型使用大驼峰命名法,见名知意
- 类中可以由很多个class类,但是只能由一个public修饰的class类,且public修饰的类名必须和文件名保持一致
- 类中定义的属性完整是 修饰符 数据类型 属性名 = 值; 在没有显式赋值时,属性是有默认值的,参考数组的默认值
封装
- 封装告诉我们如何正确的设计对象的属性和方法
- 对象是谁,就要封装他的数据,提供他的行为方法
private
- private是私有访问修饰符
- private修饰的成员只可以在本类中被访问
- private修饰的成员如果想要外界使用,需要对应的getter,setter方法,用于获取属性和设置属性
- private为封装的对象提高了安全性
this
- this可用于区分成员变量和局部变量 this.成员变量是类的属性,局部变量是方法中的变量
- 局部变量的优先级高于成员变量
构造方法
- 构造方法是在创建对象的时候,由虚拟机调用的,为成员变量初始化赋值
- 构造方法在不写的时候,会有一个隐式的无参构造方法
- 构造方法一旦显式定义,不会再提供隐式的无参的构造方法,需要显式定义
标准JavaBean
- 类名见名知意
- 类名的属性用private修饰
- 设置对应的getter和setter方法
- 定义无参和全参构造方法
- 追加其他必要的方法
对象的内存图
- JDK8之前,JVM中的堆空间后跟着方法区
- JDK8后,JVM中的堆空间后方法区被去掉,设计出一块独立空间元空间,方法区的一部分功能划分给堆,一部分划分给元空间
- 字节码文件运行时,会进入元空间运行,临时存储
- 元空间会存储字节码文件的信息,包括了成员变量和成员方法
执行Test类main方法生成一个User对象的内存过程
- 执行有main方法的Test类,Test.class进入元空间临时存储
- main方法进栈执行
- User.class进入元空间临时存储
- 栈中开辟一个空间,名称为user
- 堆中开辟一个空间,拷贝元空间中User.class的成员变量和成员方法的地址
- 成员变量先默认初始化,整数为0,字符串为null等
- 成员变量然后显示初始化,如果成员变量有直接赋值操作
- 最后构造函数初始化成员变量
- 完成后,将开辟空间的地址回传给栈中user存储
- 打印user,显示的时地址值
- user.方法名() 过程是通过地址,向堆中找方法的地址,找到后元空间对应的类中的方法,让方法进栈执行,执行完毕出栈
- 当main方法结束后,栈中的变量也随之消失,堆中的地址没有任何变量指向,也会作为垃圾回收
- 定义多个User时,字节码文件只会加载一次
基本数据类型和引用数据类型的区别
- 基本数据类型存储的是真正的值,引用数据类型存储的是地址
- 基本类型给其他基本类型赋值就是真正的值,引用数据类型给其他引用数据类型赋值是地址,指向同一个内存空间
this的内存原理
this表示方法调用的地址值
成员变量和局部变量区别
| 不同点 | 成员变量 | 局部变量 |
|---|---|---|
| 定义位置不同 | 方法外类中 | 方法中 |
| 内存存储位置不同 | 堆内存 | 栈内存 |
| 生命周期不同 | 随对象创建和消失 | 随方法调用创建和消失 |
| 初始化不同 | 有默认值 | 无默认值,使用需赋值 |
| 作用域 | 整个类 | 方法中有效 |
面向对象综合练习(Day9)
Scanner拓展
package com.itheima.day09.test;import java.util.Scanner;/*** 键盘录入扩展* * @author M10246C**/
public class ScannerTest {static Scanner sc = new Scanner(System.in);public static void main(String[] args) {/*** 体系1: * 1.sc.nextInt(); // 接收整数 * 2.sc.nextDouble(); // 接收小数 * 3.sc.next(); // 接收字符串* 体系1不接收键盘录入的空格和回车,空格之后会作为下一次键盘录入的值接收*/int num1 = sc.nextInt();double num2 = sc.nextDouble();String str1 = sc.next();System.out.println(num1);System.out.println(num2);System.out.println(str1);/*** 体系2: * 1.sc.nextLine(); // 接收字符串 * 体系2接收键盘录入的空格,接收到回车便结束本次录入* 若先用体系1再用体系2,体系2会接收到体系1的回车直接结束*/String str2 = sc.nextLine();System.out.println(str2);}}
控制台结果

结论:体系1和体系2不混用
字符串(Day10)
API
API:目前JDK提供的各种功能的Java类。
API帮助文档:帮助开发人员更好的使用API和查询API的一个工具
String
- 字符串在创建之后是不可变的
- 字符串相加会得到新的字符串
- String 在java.lang包,无需导包
String的初始化
String = "abc"; // 直接赋值操作,字符串会存入堆中的串池,可用复用
String = new String("abc"); // new出来的操作,字符串会在堆中开辟的空间,存储字符串的值,无法复用
String = new String(new char[]{'a','b','c'}); // sc.next()底层也是new出来的
String = new String(new byte[]{97,98,99});
== 究竟比较的是什么?
== 比较的是栈中真正存储的值,是地址就比较地址,是真值就比较真值
StringBuilder
- 因为String一旦创建,就不可变,不适用于对字符串频繁操作的场景,此时引入我们的StringBuilder
- StringBuilder是容器,可以对字符串进行一些操作,如追加(append),反转,替换,StringBuilder也可以转换成String
package com.itheima.demo;/*** StringBuilder学习*/
public class StringBuilderTest {public static void main(String[] args) {int[] arr = new int[]{1, 2, 3, 4, 5};// StringBuilder初始化 他是可以编辑字符串的容器StringBuilder sb = new StringBuilder();sb.append("[");for (int i = 0; i < arr.length; i++) {if (i == arr.length - 1) {sb.append(arr[i]);} else {sb.append(arr[i] + ", ");}}sb.append("]");System.out.println(sb);}
}
StringJoiner
- StringBuilder在拼接字符串的时候,没有一个便捷的方法,可以在开始和结尾追加一个字符串,中间加入分割符,在JDK8出现后,StringJoiner可以更加高效方便的对字符串进行操作
- StringJoiner没有无参构造方法,参数1表示分割符号,参数2是开头,参数3是结尾
package com.itheima.demo;import java.util.StringJoiner;/*** StringJoiner学习*/
public class StringJoinerTest {public static void main(String[] args) {int[] arr = new int[]{1, 2, 3, 4, 5};
// int[] arr = new int[0];// StringBuilder初始化 他是可以编辑字符串的容器StringJoiner sb = new StringJoiner(", ", "[", "]");for (int i = 0; i < arr.length; i++) {sb.add("" + arr[i]);}System.out.println(sb);}
}
字符串原理
字符串的拼接原理
1.字符串的拼接过程没有变量参与的时候,在Java文件编译成字节码文件的时候,会进行优化,直接把最终的字符串,存入常量池,实际开发过程中,是可以复用串池中的它的。

2. 字符串拼接过程中,有变量参与时,底层实现则是新建一个StringBuilder对象,使用append方法追加两次,最后toString()生成一个String对象,将String对象的地址给栈中进行存储

3.StringBuilder的提高效率原理,是因为只会创建一个StringBuilder对象,然后append方法,只会更新StringBuilder内部的字符串的值,相对于String的拼接操作,即使是JDK8后,String的拼接做了长度预估的优化操作,效率仍然不及StringBuilder。

4.StringBuilder的源码分析
StringBuilder的默认容量是16,默认长度为0,如果append的长度大于16后,会进行扩容,容量*2+2,如果append的长度比扩容后的还长,以实际长度为准
集合(Day11)
- 因为数组是不可变的,我们在不确定一个容器长度的时候,应该选择一个长度可变的容器,此时引出了ArrayList
- ArrayList需要指定泛型,泛型不可以是基本数据类型,我们可以使用基本数据类型的包装类
- 在创建ArrayList对象的时候,JDK7之前两个泛型位置都需要填写泛型,JDK7之后,只需要填写前面位置的泛型,但后面也需要<>
- ArrayList的容器默认的容量为0,可以自动扩容
- ArrayList有便捷的方法,新增add(E)->boolean,修改set(index,E) ->oldE,删除remove(E)->boolean 或 remove(index)->E,查询get(index)->E
控制台版学生管理系统(略)
学生管理系统升级(Day12)
static(Day13)
静态变量
1.用static修饰的成员变量就是静态变量
2.静态变量创建是随着类的字节码文件加载到元空间后,在堆内存中的静态区添加该静态变量进行存储,先设置默认初始值,然后显式赋值,然后方法里赋值,成员变量是随着对象的创建,在堆内存中开辟空间存储
3.静态变量是属于类的,可以被这个类所有的对象共享编辑或使用
4.一般通过类名.静态变量名调用
5.同一种对象共用的一个属性就可以定义成静态变量
静态方法
1.用static修饰的成员方法就是静态方法
2.静态方法用于Test类和工具类
3.JavaBean类指的是描述一类事物的类
4.Test类是用于测试其他类的类
5.工具类是帮我们做一些事情的类,不描述一类事物
6.工具类要求见名知其义
7.私有化构造方法,不可以定义工具类的对象,因为工具类的对象是无意义的
8.方法定义成静态的,方便调用
9.用类名.静态方法名(…),是从元空间中找到这个类的字节码文件,找到对应的静态方法名进栈运行
静态方法的注意事项
- 静态方法中是没有this关键字的,其实是因为成员方法中this作为第一个形参隐藏起来啦,给的值就是调用者的地址,而静态方法没有this
- 静态方法不用成员变量和成员方法,原因就是没有this关键字
- 成员方法既可以调用成员方法又可以调用静态方法,每个对象都可以调用类创建的静态区的静态变量
重新认识main方法
- public给出足够大的访问权限,便于JVM调用
- staticJVM不需要实例化,直接调用
- voidJVM不提供返回值
- main被JVM查询的方法入口名称,固定
- String[] args过去接收一些参数,现在由Scanner代替
继承
为什么需要继承?
- 当类与类之前有相同的属性和方法时,并且这些类又是同一大类,就可以使用继承
- 可以把子类中共同的属性和方法提取到父类中,提高代码的复用性
- 子类 extends 父类,可以让子类使用父类的属性和方法,子类只需要补充编写特有的属性和方法
- extends关键字是让两个类建立继承关系,满足语句:子类是父类。
继承注意事项
- Java是单继承,只能继承一个直接父类,支持多层继承,可以有多个间接父类
- Java中所有的类都直接或间接的继承Object类
- 子类只能访问父类中非私有的成员变量和成员方法(代码层面)
子类到底能继承父类哪些内容
- 子类不能继承父类的构造方法,父类的构造方法拷贝过来不符合要求 public/private 类名
- 子类可以继承父类的成员变量, 虽然可以继承父类的私有成员变量,但无法直接使用
- 子类可以继承父类非private且非static且非final的成员方法,它们属于虚方法,会传给子类方便直接调用,如果不是虚方法,就需要先找子类,再找父类,周而复始,没有则报错
继承中成员变量和成员方法的访问特点
- 就近原则 先局部找,然后在本类找,最后再父类中找,逐级向上,找不到报错
- 可以用this,super改变找的起点
重写
- 当父类的方法不满足子类的需求,可以重写该方法
- 重写要求方法名和形参列表完全相同
- 要求访问修饰符权限子类>=父类
- 要求返回值范围子类 <= 父类
- 重写写@Override注解,方便进行重写格式的校验
- 重写只可以重写虚方法,重写本质就是覆盖虚方法
构造方法的访问特点
- 父类的构造方法不能被子类继承
- 子类构造方法默认情况调用父类的无参构造方法super();,为了给父类属性完成初始化,父类的属性初始化完毕,子类才可以调用
- super(); this();必须出现在构造方法的第一行,两者互斥
- 想要调用有参的父类构造方法,需要显式的在子类构造方法的第一行调用。
this & super
- this指的是调用者的地址值,可以理解一个形参变量
- super代表父类的存储空间
多态(Day14)
- 什么是多态:对象的多种形态
- 多态的前提是:有继承或实现关系;子类重写父类的方法,父类接收子类对象
- 多态的好处:父类作为形参时,可以接收任何的子类对象,可调用子类的重写方法
多态中成员变量的调用
- 编译过程看左边,运行过程也看左边
多态中成员方法的调用
- 编译过程看左边,运行过程看右边
多态的优缺点
- 当父类作为参数时,可以接收任意的子类对象
- 但是参数不能调用子类的特有功能
- 想要调用子类的特有功能需要强制类型转换
- 转换如果类型不一致,可能会报错,classCaseException
- 可以通过 参数 instanceof 子类,判断是不是可以转换成这个类,返回值为true,表示是,返回值为false表示
- JDK14增加了新特性,简化了代码让判断和强转一气呵成,参数 instanceof 子类 变量名,是这个子类,就会为变量名赋值
包
- 包的本质是文件夹,用于管理不同的Java类,便于代码的维护
- 包的书写规则是公司的域名反写+包的作用,需要全部小写,见名知意
- 全类名,也叫全限定名是包名+类名
- 如果引用的类是同一个包下,是不需要导包的
- 如果引用的类是java.lang下的,也是不需要导包的
- 如果引用的是其他包下的,是需要导包或者写全类名的
- 如果引用了两个不同包下的同名类,是需要使用全类名来区分的
final关键字
- final修饰的关键字,可以修饰类,方法,变量
- final修饰的类不可以被继承,如String,Math
- final修饰的方法,不可以被子类继承到,无法重写
- final修饰的变量,只能赋值一次,被称为常量,大写用下划线分隔
- 常量如果是基本数据类型,值不可以改变
- 常量如果是引用数据类型,地址值不可以改变,内存中的成员变量是可以改变
权限访问修饰符
| 关键字 | 本类 | 本包 | 其他包子类 | 项目 |
|---|---|---|---|---|
| private | √ | |||
| 默认 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
- private和public日常用的最多
- private主要用于修饰成员变量,一些频繁被调用的方法也会被设置为private
- public主要用于修饰成员方法和类
代码块
- 代码块分为三种,局部代码块,构造代码块,静态代码块
- 局部代码块在方法体内部,用于控制变量的生命周期,随着内存的增多,被淘汰了
- 构造代码块与成员变量同级,用于提取构造方法中的重复代码,它先于构造方法运行,局限性大,不常使用
- 静态代码块是用static修饰的构造代码块,用于初始化静态属性,随着字节码文件加载到元空间就执行,只能调用静态成员
抽象类(Day15)
- 抽象类的由来是,当多个子类都有同一种行为,但是行为的具体内容又不完全一样,就是希望,父类不定义具体的方法体,但是子类必须重写父类的方法,这种需求就诞生了抽象方法,抽象方法是没有方法体的,但是非抽象的子类,必须重写父类的抽象方法
- 要注意,有抽象方法的类,一定是抽象类,而抽象类不一定有抽象方法
- 抽象类不可以被实例化
- 抽象类是可以有构造方法的,需要子类调用父类的构造方法,完成对父类的成员变量初始化
- 抽象类可以被抽象类继承,这种情况可以不用重写父类的抽象方法
- 但是非抽象类继承了抽象类后,必须重写父类的所有抽象方法
接口
- 接口的由来是,多个类共有的行为,但是这些类不属于某个大类,但是希望以相同的规则定义共有行为,此时就用到了接口,它其实是定义一种规则。
- 要注意,接口的关键字是interface
- 接口不能被实例化
- 接口如果被抽象类实现,这种情况不用重写接口的抽象方法
- 接口如果被非抽象类实现,必须实现接口的所有抽象方法
- 实现的关键字是implements
- Java是支持多实现的
- Java的继承一个父类的同时,实现多个接口
接口的成员特点
- 成员变量默认都是常量,自动追加public static final前缀
- 成员方法默认都是抽象方法,自动追加public abstract
- 没有构造方法
接口与接口的关系
- 类和类之间是继承关系
- 类和接口之间是实现关系
- 接口与接口之前是继承关系
- 当一个类实现了多个接口,接口中有重复的抽象方法,只需要实现一个
- 当接口继承了接口,类在实现子接口时,需要重写子接口和父接口的所有的抽象方法
接口相关拓展
- JDK7以前,包括JDK7,接口中的方法只有抽象方法,public abstract可以默认不写
- JDK8后,为了在项目过程中,新增接口方法的时候,不需要立即重写,引入的默认方法,public default 返回值类型 方法名(形参){方法体;}
- 默认方法也可以被实现类重写,重写的时候实现类就需要去掉default关键字了
- 如果实现的多个接口中有重名的默认方法,则实现类必须重写默认方法
- JDK8后,接口还引入的静态方法,静态方法的static关键字不可以被省略,接口中的静态方法必须用接口名.方法名调用,无法被重写
- JDK9后,引入了私有方法和私有静态方法,分别是为默认方法和静态方法服务,私有方法提取共同的代码,不被其他类调用
- 接口代表规则,是行为的抽象,类需要哪些行为就实现哪些接口
- 接口多态就是 变量的类型是接口,接收的对象时任意一个实现类,遵从的也是编译看左边,运行看右边
设计模式
- 设计模式是一套惯用的代码编写套路
适配器设计模式
- 场景是一个实现类不想实现xxx接口所有的抽象方法,可以使用适配器设计模式
- 中间抽象类xxxAdapter实现xxx接口,重写的方法默认为空方法体,继承实际实现类的父类,没有则不需要继承
- 实际实现类继承中间抽象类xxxAdapter,对指定的方法再次重写
内部类
- 类的五大成员:属性,方法,构造方法,代码块,内部类
- 内部类是类里面定义的类
- 内部类是外部类的一部分,并且单独存在没有意义,像是汽车的发动机,ArrayList的迭代器,人的心脏等。
- 内部类分为四种成员内部类,静态内部类,局部内部类,匿名内部类
成员内部类
- 成员内部类是定义在外部类中,外部类的方法之外的类
- 成员内部类的对象的创建分为两种情况,当成员内部类是私有的,外部类需要提供一个public方法,返回成员内部类的对象。当成员内部类是非私有的,可以直接创建,外部类.内部类 变量名 = new 外部类().new 内部类();
- 成员内部类的方法如何调用重名的外部类的成员 :外部类.this.成员
- 成员内部类和外部类在加载到元空间的时候,是独立的两部分,具体可以看编译后生成的字节码文件
- 成员内部类的字节码文件名:外部类$内部类.class
- 成员内部类直接创建对象时,外部类会开辟一个堆内存空间,内部类会开辟一个堆内存空间,内部类的堆内存空间会存储一个外部类的地址值,存入this中,内部类的地址值给定义的变量来接收,形成完整的数据连接
静态内部类
- 静态内部类是成员内部类的一种,用static修饰的成员内部类是静态内部类
- 静态内部类中只能访问外部类的静态成员
- 静态内部类的创建: 外部类.静态内部类 变量名 = new 外部类.静态内部类();
- 调用静态内部类中的成员方法,需要先实例化静态内部类,然后调用
- 调用静态内部类的静态方法,格式:外部类.静态内部类.静态成员
局部内部类
- 定义在方法里面的类就是局部内部类
- 局部内部类不可以在方法外被实例化
- 局部内部类相当于局部变量,局部变量可以被什么修饰,局部内部类就可以被什么修饰
- 方法里需要实例化才可以调用局部内部类的方法
- 局部内部类中的方法可以访问外部类的成员和声明好的局部变量
匿名内部类
- 没有名字的内部类是匿名内部类
- 可以定义在成员位置或者是方法内部
- 格式: new 接口名/父类名(){重写方法};
- {重写方法}是实际的匿名类,整体其实是匿名内部类的对象
- 格式细节:包含继承或实现关系,重写方法,创建对象
- 匿名内部类在字节码文件中的名称体现为:外部类$序号.class 序号从1开始
- 使用场景:一个接口或抽象类作为参数,程序只用一次,则不需要定义一个实现类,直接写成匿名内部类对象形式赋值即可,简化代码开发
拼图小游戏(Day16)
JFrame 窗体
JFrame jFrame = new JFrame();// 设置窗体宽高jFrame.setSize(400, 500);// 设置窗体标题jFrame.setTitle("测试弹窗");// 设置窗体置顶jFrame.setAlwaysOnTop(true);// 设置窗体居中jFrame.setLocationRelativeTo(null);// 设置窗体关闭模式jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);// 取消窗体默认居中,如此设置坐标生效jFrame.setLayout(null);// 窗体可见jFrame.setVisible(true);
菜单
JFrame jFrame = new JFrame();// 菜单行JMenuBar jMenuBar = new JMenuBar();// 菜单具体栏JMenu jMenu = new JMenu("功能");// 菜单具体栏下拉框JMenuItem jMenuItem1 = new JMenuItem("重新开始");JMenuItem jMenuItem2 = new JMenuItem("退出");jMenu.add(jMenuItem1);jMenu.add(jMenuItem2);jMenuBar.add(jMenu);jFrame.setJMenuBar(jMenuBar);

窗体内容
用于短文本字符串或图像或两者的显示区域。
// 填充一个图片public static void addImage(JFrame jFrame) {// 创建图标对象ImageIcon imageIcon = new ImageIcon("C:/workspace/lv_learn/image/1.png");// jLabel填充图标JLabel jLabel = new JLabel(imageIcon);// jLabel设置坐标和宽高jLabel.setBounds(0, 0, 105, 105);// 窗体填充jLabeljFrame.getContentPane().add(jLabel);}// 填充一个动作监听按钮1public static void addActionButton1(JFrame jFrame) {JButton jButton = new JButton("动作监听1");jButton.setBounds(105, 0, 105, 30);// 设置监听方式1: 使用匿名内部类jButton.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("动作监听捕获1");}});jFrame.getContentPane().add(jButton);}// 填充一个鼠标监听按钮1public static void addMouseButton1(JFrame jFrame) {JButton jButton = new JButton("鼠标监听1");jButton.setBounds(0, 105, 105, 30);jButton.addMouseListener(new MouseListener() {@Overridepublic void mouseReleased(MouseEvent e) {System.out.println("鼠标释放");}@Overridepublic void mousePressed(MouseEvent e) {System.out.println("鼠标按下");}@Overridepublic void mouseExited(MouseEvent e) {System.out.println("鼠标划出");}@Overridepublic void mouseEntered(MouseEvent e) {System.out.println("鼠标划入");}@Overridepublic void mouseClicked(MouseEvent e) {System.out.println("鼠标点击");}});jFrame.add(jButton);}// 填充一个键盘监听按钮1public static void addKeyButton1(JFrame jFrame) {JButton jButton = new JButton("键盘监听1");jButton.setBounds(0, 105 * 2, 105, 30);jButton.addKeyListener(new KeyListener() {@Overridepublic void keyTyped(KeyEvent e) {System.out.println("键盘按下之后");}@Overridepublic void keyReleased(KeyEvent e) {System.out.println("键盘释放");int keyCode = e.getKeyCode();switch (keyCode) {case 65:System.out.println("键盘释放A");break;case 66:System.out.println("键盘释放B");break;default:System.out.println("键盘释放未知");break;}}@Overridepublic void keyPressed(KeyEvent e) {System.out.println("键盘按下");}});jFrame.add(jButton);}
窗体内容美化(Day17)
- 拼图图片位置整体偏移至窗体中间
- 加入背景图,背景图的加入位置放在拼图图片后面,原因是先加入的图片会再后加入图片的上方。
- 为拼图图片设置边框
- 将拼图图片的路径由绝对路径修改为相对路径(模块名/…)
移动图片
- 整个窗体设置键盘监听事件, jFrame.setKeyListener…
- 记录空白拼图下标 x,y
- 键盘松开逻辑判断设置,注意空白拼图的左移,相当于图片的右移(逻辑比较绕)
- 键盘松开逻辑判断后,重新设置一次拼图 initImage
- 重新设置需要窗体内容部分(jFrame.getContentPane().removeAll()),删除当前的拼图部分,最后重新repaint一下(jFrame.getContentPane().repaint())
弹出提示图
- 窗体对象设置键盘监听事件, jFrame.setKeyListener…
- 键盘按下显示提示图
- 键盘松开还原拼图
其他(略)
常用API(Day18)
Math
package com.itheima.day18;public class MathTest {public static void main(String[] args) {// abs 取值范围最小值不能取到绝对值System.out.println(Math.abs(-11)); // 11System.out.println(Math.abs(11)); // 11System.out.println(Math.abs(-2147483648)); // -2147483648System.out.println(Math.abs(-2147483647)); // 2147483647// ceil// floor// roundSystem.out.println(Math.ceil(11.2)); // 12.0System.out.println(Math.ceil(-11.2));// -11.0System.out.println(Math.floor(11.2)); // 11.0System.out.println(Math.floor(-11.2));// -12.0System.out.println(Math.round(11.2));// 11.0System.out.println(Math.round(11.5));// 12.0System.out.println(Math.round(-11.2));// -11.0System.out.println(Math.round(-11.6));// -12.0// pow// sqrt// cbrtSystem.out.println(Math.pow(2, 3)); // 8.0System.out.println(Math.sqrt(16)); // 4.0System.out.println(Math.cbrt(8)); // 2.0// max// minSystem.out.println(Math.max(3, 2)); // 3System.out.println(Math.min(3, 2)); // 2// randomfor (int i = 0; i < 100; i++) {System.out.println(Math.ceil(Math.random() * 100)); // 1.0~100.0}}
}
System
package com.itheima.demo;public class SystemTest {public static void main(String[] args) {// exit 终止java虚拟机// 0:正常结束 非0:异常结束//System.exit(0);// currentTimeMillis(); 记录当前事件距离时间原点的毫秒差值// 时间原点:1970.01.01 00:00:00// 北京东八区时间原点:1970.01.01 08:00:00long start = System.currentTimeMillis();System.out.println(start);// arraycopy(数组源头,数组源头开始下标,拷贝数组,数组开始下标,拷贝长度)// 注意1:如果都是基本数据类型,则数据类型必须保持一致,否则报错try {int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};double[] arr2 = new double[10];System.arraycopy(arr1, 0, arr2, 0, arr1.length);for (int i = 0; i < arr2.length; i++) {System.out.println(arr2[i]);}} catch (Exception e) {System.out.println(e.toString());}// 注意2:如果都是引用数据类型,子类对象可以拷贝给父类对象try {Student[] arr1 = {new Student("张三", 18), new Student("李四", 19), new Student("王五", 20)};Person[] arr2 = new Person[10];System.arraycopy(arr1, 0, arr2, 3, arr1.length);for (int i = 0; i < arr2.length; i++) {System.out.println(arr2[i]);}} catch (Exception e) {System.out.println(e.toString());}// 注意3:如果超出拷贝数组长度,报错try {Student[] arr1 = {new Student("张三", 18), new Student("李四", 19), new Student("王五", 20)};Person[] arr2 = new Person[3];System.arraycopy(arr1, 0, arr2, 1, 3);for (int i = 0; i < arr2.length; i++) {System.out.println(arr2[i]);}} catch (Exception e) {System.out.println(e.toString());}}
}class Person {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}
}class Student extends Person {public Student(String name, int age) {super(name, age);}
}
Runtime
package com.itheima.demo;import java.io.IOException;public class RuntimeTest {public static void main(String[] args) throws IOException {// getRuntime() 获取当前程序运行环境Runtime runtime = Runtime.getRuntime();// exit 终止JVM
// runtime.exit(0);// availableProcessors 返回JVM可用的处理器的数目System.out.println(runtime.availableProcessors());// 返回JVM最大使用的内存大小byteSystem.out.println(runtime.maxMemory() / 1024 / 1024);// 返回JVM已经获取的内存大小byteSystem.out.println(runtime.totalMemory() / 1024 / 1024);// 返回JVM未使用的内存大小byteSystem.out.println(runtime.freeMemory() / 1024 / 1024);// exec 执行cmd命令runtime.exec("notepad");// shutdown -s 默认一分钟后关机// shutdown -s -t 秒数// shutdown -a 终止关机// shutdown -r 关机重启}
}
Object和Objects
package com.itheima.demo;public class ObjectTest {public static void main(String[] args) {Object object = new Object();System.out.println(object);// object.toString() 全类名+@+十六进制地址System.out.println(object.toString());//object.equals(..) 返回地址比较的结果System.out.println(object.equals("12"));String str = "123";StringBuilder stringBuilder = new StringBuilder("123");// String.equals(..) 先比较地址是否相同,如果相同return true// 否则比较是不是都是String类型的数据,如果不是return false// 如果是则比较字符是否一一对应System.out.println(str.equals(stringBuilder)); // false// StringBuilder没有重写equals方法,调用的是object方法System.out.println(stringBuilder.equals(str)); // false}
}
package com.itheima.demo;import java.util.Objects;
import java.util.StringJoiner;public class Object2Test {public static void main(String[] args) throws CloneNotSupportedException {// clone() 类继承Cloneable接口后// 默认的克隆方式是浅克隆// 意思就是把栈中的值传给新的对象// 深克隆需要自定义方法,或者引用第三方工具类Teacher t1 = new Teacher("张三", 18, new String[]{"吃饭", "睡觉", "打豆豆"});Teacher t2 = (Teacher) t1.clone();t1.interests[0] = "学习";System.out.println(t1);System.out.println(t2);System.out.println(t1 == t2); // falseSystem.out.println(t1.interests == t2.interests); // 深克隆 false 浅克隆 trueSystem.out.println("===============================");//Objects// equals 进行了空判定,返回为false// isNull 是否为空// nonNull 是否不为空Teacher t3 = null;Teacher t4 = null;System.out.println(Objects.equals(t1, t2)); // falseSystem.out.println(Objects.equals(t2, t3)); // falseSystem.out.println(Objects.equals(t3, t4)); // falseSystem.out.println(Objects.isNull(t1)); // falseSystem.out.println(Objects.isNull(t3)); // trueSystem.out.println(Objects.nonNull(t1)); // trueSystem.out.println(Objects.nonNull(t3)); // false}
}// 克隆必须实现Cloneable
class Teacher implements Cloneable {String name;int age;String[] interests;public Teacher(String name, int age, String[] interests) {this.name = name;this.age = age;this.interests = interests;}@Overrideprotected Object clone() throws CloneNotSupportedException {// return super.clone();Teacher t2 = (Teacher) super.clone();String[] newStrs = new String[interests.length];System.arraycopy(interests, 0, newStrs, 0, interests.length);t2.interests = newStrs;return t2;}@Overridepublic String toString() {return "Teacher{" +"name='" + name + '\'' +", age=" + age +", interests=" + interestsStr() +'}';}private String interestsStr() {StringJoiner stringJoiner = new StringJoiner(",", "[", "]");for (int i = 0; i < interests.length; i++) {stringJoiner.add(interests[i]);}return stringJoiner.toString();}
}
BigInteger
package com.itheima.demo;import java.math.BigInteger;
import java.util.Random;public class BigIntegerTest {public static void main(String[] args) {// 1.new BigInteger(int,random) 随机0 ~ 2^4 - 1BigInteger bigInteger1 = new BigInteger(4, new Random());System.out.println(bigInteger1);// 2.new BigInteger(str) str转大整数BigInteger bigInteger2 = new BigInteger("100");System.out.println(bigInteger2);// 3.new BigInteger(str,int) 其他进制转十进制BigInteger bigInteger3 = new BigInteger("100", 2);System.out.println(bigInteger3);// 4.BigInteger.valueOf(long) long转大整数// valueOf(-16~16)是类的静态属性值赋予的// 超出long的最大值报错BigInteger bigInteger4 = BigInteger.valueOf(100l);System.out.println(bigInteger4);System.out.println("==============================");BigInteger b1 = BigInteger.valueOf(10);BigInteger b2 = BigInteger.valueOf(2);// 1.addBigInteger b3 = b1.add(b2);System.out.println(b3); // 12// 2.subtractBigInteger b4 = b1.subtract(b2);System.out.println(b4); // 8// 3.multiplyBigInteger b5 = b1.multiply(b2);System.out.println(b5); // 20// 4.divideBigInteger b6 = b1.divide(b2);System.out.println(b6); //5// 5.divideAndRemainderBigInteger[] b7 = b1.divideAndRemainder(b2);System.out.println(b7[0] +" "+b7[1]); //5 0// 6.equalsSystem.out.println(b1.equals(b2)); // false// 7.powBigInteger b8 = b1.pow(2);System.out.println(b8); // 100// max/minBigInteger b9 = b1.max(b2);BigInteger b10 = b1.min(b2);System.out.println(b9 == b1); //trueSystem.out.println(b9 == b2); //falseSystem.out.println(b10 == b1); //falseSystem.out.println(b10 == b2); //true// intValueint b11 = b1.intValue();System.out.println(b11); // 10// signum 代码正负号 或零// [] 从大到小存储划分32位的int数据[...,64~33,32~1]// 理论最大范围:int最大数的int范围的幂次方}
}
BigDecimal
package com.itheima.demo;import java.math.BigDecimal;
import java.math.RoundingMode;public class BigDecimalTest {public static void main(String[] args) {// 1.浮点型的运算可能会因为小数位转换成二进制后过长,从而丢失了超出长度的二进制位的数据// 导致浮点数的运算之间也出现了误差// BigDecimal的出现解决了两个浮点数的两个问题// 一. 可以存储更大的浮点数数据// 二. 可以进行浮点数的精确运算// 2.实例化BigDecimal的方式// 一. 如果数据过大 new BigDecimal(str)// 二. 如果是小的浮点数 BigDecimal.valueOf(double)// 三. 如果是0~10包括10的整数 BigDecimal.valueOf(long) 节省内存空间BigDecimal bd1 = new BigDecimal(0.226);BigDecimal bd2 = new BigDecimal("0.226");BigDecimal bd3 = BigDecimal.valueOf(0.226);BigDecimal bd4 = BigDecimal.valueOf(10);BigDecimal bd5 = BigDecimal.valueOf(10);BigDecimal bd6 = BigDecimal.valueOf(11);BigDecimal bd7 = BigDecimal.valueOf(11);System.out.println(bd1); // 有误差 0.2260000000000000064392935428259079344570636749267578125System.out.println(bd2); // 0.226System.out.println(bd3); // 0.226System.out.println(bd4 == bd5); // trueSystem.out.println(bd6 == bd7); // false// 3.常用的运算方法BigDecimal bd8 = BigDecimal.valueOf(10);BigDecimal bd9 = BigDecimal.valueOf(4);BigDecimal bd10 = BigDecimal.valueOf(3);// 加System.out.println(bd8.add(bd9)); // 14// 减System.out.println(bd8.subtract(bd9)); // 6// 乘System.out.println(bd8.multiply(bd9)); // 40// 除System.out.println(bd8.divide(bd9)); // 2.5 除尽try {System.out.println(bd8.divide(bd10)); // 除不尽报异常} catch (Exception e) {System.out.println(e.toString());}// 四舍五入保留两位小数System.out.println(bd8.divide(bd10, 2, RoundingMode.HALF_UP)); // 3.33// 4.底层存储是将double->byte[] 报错每一位的ASCII码,包括负号BigDecimal bigDecimal1 = BigDecimal.valueOf(-3.33);// -3.33 -> [45,51,46,51,51]BigDecimal bigDecimal2 = BigDecimal.valueOf(3.33);// 3.33 -> [51,46,51,51]}
}
Arrays
package com.itheima.demo;import java.util.Arrays;
import java.util.Comparator;public class ArraysTest {public static void main(String[] args) {int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };// 1.Arrays.toString(数组): 数组转换成数组字符串System.out.println(Arrays.toString(nums));// 2.Arrays.copyOf(旧数组,新数组长度);System.out.println("---------------------------------------------------");System.out.println(Arrays.toString(Arrays.copyOf(nums, nums.length - 1))); // 部分拷贝System.out.println(Arrays.toString(Arrays.copyOf(nums, nums.length)));System.out.println(Arrays.toString(Arrays.copyOf(nums, nums.length + 1))); // 超出进行赋默认值// 3.Arrays.copyOfRange(旧数组,开始下标,结束下标) 包左不包右System.out.println("---------------------------------------------------");System.out.println(Arrays.toString(Arrays.copyOfRange(nums, 0, 2)));System.out.println(Arrays.toString(Arrays.copyOfRange(nums, 2, nums.length)));// 4.Arrays.binarySearch(数组,查询值) 数组必须是正有序数据System.out.println("---------------------------------------------------");System.out.println(Arrays.binarySearch(nums, 1)); // 0System.out.println(Arrays.binarySearch(nums, 11));// -11System.out.println(Arrays.binarySearch(nums, 0)); // -1// 5.Arrays.fill(值) 填充覆盖原始数据System.out.println("---------------------------------------------------");Arrays.fill(nums, 100);System.out.println(Arrays.toString(nums));// 6.Arrays.sort(数组)System.out.println("---------------------------------------------------");Integer[] nums1 = { 1, 7, 8, 9, 10, 2, 3, 4, 5, 6 };
// Arrays.sort(nums1);
// System.out.println(Arrays.toString(nums1)); // 正序// 插值排序 + 二分查找Arrays.sort(nums1, new Comparator<Integer>() {/*** o1 插入值 o2 二分查找值*/@Overridepublic int compare(Integer o1, Integer o2) {System.out.println("o1:" + o1 + ",o2:" + o2);return o2 - o1;}});System.out.println(Arrays.toString(nums1));// -11}
}
正则表达式(Day19)
1. 字符数据的校验
更详细的正则相关字符说明查询Pattern API
链接:https://doc.qzxdp.cn/jdk/17/zh/api/java.base/java/util/regex/Pattern.html
package com.itheima.demo;public class PatternTest {public static void main(String[] args) {// 正则表达式主要是为了快速校验而出现的数据// 1.字符类System.out.println("a".matches("[abc]")); //trueSystem.out.println("a".matches("[^123]")); //非 trueSystem.out.println("a".matches("[a-bd-f]"));// 和 trueSystem.out.println("a".matches("[a-c&&c-e]")); // 交集 falseSystem.out.println("c".matches("[a-c&&c-e]")); // trueSystem.out.println("c".matches("[a-c&&[^c-e]]")); // falseSystem.out.println("=====================================");// 2.预定义字符类System.out.println("a".matches("\\d")); //[0-9] falseSystem.out.println("a".matches("\\D")); //[^0-9] trueSystem.out.println("a".matches("\\w")); //[a-zA-Z0-9] trueSystem.out.println("a".matches("\\W")); //[^a-zA-Z0-9_] //falseSystem.out.println("a".matches(".")); // 任意字符(\n除外) trueSystem.out.println("a".matches("\\."));// . falseSystem.out.println("=====================================");// 3.所有格量词类System.out.println("aa".matches("\\w?")); //0或1 falseSystem.out.println("aa".matches("\\w+")); //1或n trueSystem.out.println("aa".matches("\\w*")); //0或n trueSystem.out.println("aa".matches("\\w{2}")); // 2 trueSystem.out.println("aaa".matches("\\w{2,}")); // >=2 trueSystem.out.println("aaa".matches("\\w{2,3}")); // >=2 <=3 true// 4. | 并集// 5. (?i) 忽略大小写// 6. () 分组}
}
2.文本数据的爬取
package com.itheima.demo;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class PatternTest02 {public static void main(String[] args) {String text = "JDK是java开发工具,最常用的JDK有JDK7,JDK8,JDK17";// 1.定义正则表达式对象Pattern pattern = Pattern.compile("JDK\\d{0,2}");// 2.生成指定文本的匹配器Matcher matcher = pattern.matcher(text);// 3.遍历// 3.1 matcher.find() 记录是否匹配成功,匹配成功返回true,内部记录开始的索引,和结束索引+1while(matcher.find()){// 3.2 matcher.group() 记录匹配成功的数据System.out.println(matcher.group());}}
}
3.文本数据爬取拓展
package com.itheima.demo;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class PatternTest03 {public static void main(String[] args) {// ?= 爬取匹配数据且只打印指定部分数据// ?: 爬取匹配数据打印匹配数据// ?! 爬取不满足匹配设置的数据, 且只打印指定部分数据String text = "JDK是JAVA开发工具,最常用的JDK版本有JDK7,JDK8,JDK17;JDK21也即将成为稳定的版本";Pattern pattern1 = Pattern.compile("JDK(?=\\d{0,2})");Matcher matcher1 = pattern1.matcher(text);while (matcher1.find()) {System.out.print(matcher1.group() + " ");}// result: JDK JDK JDK JDK JDK JDKSystem.out.println("");Pattern pattern2 = Pattern.compile("JDK(?:\\d{0,2})");Matcher matcher2 = pattern2.matcher(text);while (matcher2.find()) {System.out.print(matcher2.group() + " ");}// result: JDK JDK JDK7 JDK8 JDK17 JDK21System.out.println("");Pattern pattern3 = Pattern.compile("JDK(?!\\d{2})");Matcher matcher3 = pattern3.matcher(text);while (matcher3.find()) {System.out.print(matcher3.group() + " ");}// result: JDK JDK JDK JDK// 正则匹配器默认是贪婪爬取,即尽可能匹配的更长// 在+ * 等数量词后面加上问号(?) 变成非贪婪爬取,即尽可能匹配的更短System.out.println("");Pattern pattern4 = Pattern.compile("JDK\\d+");Matcher matcher4 = pattern4.matcher(text);while(matcher4.find()){System.out.print(matcher4.group()+" ");}// JDK JDK JDK7 JDK8 JDK17 JDK21System.out.println("");Pattern pattern5 = Pattern.compile("JDK\\d+?");Matcher matcher5 = pattern5.matcher(text);while(matcher5.find()){System.out.print(matcher5.group()+" ");}// JDK JDK JDK7 JDK8 JDK1 JDK2// 字符串的相关方法的正则应用,需要以API的方法为准// 例子:// matches(String regex)// replaceAll(String regex, String replacement)// replaceFirst(String regex, String replacement)// split(String regex)// split(String regex, int limit)System.out.println("");String text1 = "吕懿洋afljsldkjflkj1243李桂祥asjdflkdjsflkj李敏";// && 后面追加的也要用[] 括起来System.out.println(text1.replaceAll("[\\w&&[^_]]+","vs")); // 吕懿洋vs李桂祥vs李敏String[] array = text1.split("[\\w&&[^_]]+");for (int i = 0; i < array.length; i++) {System.out.print(array[i]+" "); // 吕懿洋 李桂祥 李敏}}
}
4.捕获分组和非捕获分组
package com.itheima.demo;public class PatternTest04 {public static void main(String[] args) {// 1.分组 ()// 以左括号为准记录组数,从1开始// 正则表达式默认是捕获分组 \\1代表捕获第一组的值System.out.println("a123a".matches("(.).+\\1")); // trueSystem.out.println("b456b".matches("(.).+\\1")); // trueSystem.out.println("17891".matches("(.).+\\1")); // trueSystem.out.println("&abc&".matches("(.).+\\1")); // trueSystem.out.println("===================================");System.out.println("abc123abc".matches("(.+).+\\1")); // trueSystem.out.println("b456b".matches("(.+).+\\1")); // trueSystem.out.println("123789123".matches("(.+).+\\1")); // trueSystem.out.println("&!@abc&!@".matches("(.+).+\\1")); // trueSystem.out.println("===================================");System.out.println("aaa123aaa".matches("((.)\\2*).+\\1")); // trueSystem.out.println("bbb456bbb".matches("((.)\\2*).+\\1")); // trueSystem.out.println("111789111".matches("((.)\\2*).+\\1")); // trueSystem.out.println("&&abc&&".matches("((.)\\2*).+\\1")); // trueSystem.out.println("ababcab".matches("((.)\\2*).+\\1")); // falseSystem.out.println("===================================");String str = "我要学学学学学学编编编编编编编程程程程程程程程程程程";// $1 约等于 \\1System.out.println(str.replaceAll("(.)\\1+","$1")); // 我要学编程//2. ?: ?! ?= 都是非捕获分组,是没有需要的System.out.println(str.replaceAll("(.)(?:\\1)\\1*","$1")); // 我要学编程}
}
日期类(Day20)
JDK7版本相关
Date类
package com.glad.flowable;import java.util.Date;
import java.util.Random;public class DateTest01 {public static void main(String[] args) {// 1.时间知识普及:// 格林威治标准时间 标准开始时间: 1970.01.01 00:00:00 GMT// 中国:东八区时间 +8小时// 日本:东九区时间 +9小时// 2.创建Date对象Date d1 = new Date(); // 返回当前时区当前时间System.out.println(d1);Date d2 = new Date(0L); // 返回当前时区开始时间System.out.println(d2);// 3.设置时间d1.setTime(1000L); // 加1秒System.out.println(d1);// 4.获取时间long time1 = d1.getTime();System.out.println(time1); // 1000// 小练1: 获取当前时间和当前时间+1年Date d3 = new Date();long time2 = d3.getTime();Date d4 = new Date(time2 + 1000L * 60 * 60 * 24 * 365);System.out.println(d3);System.out.println(d4);// 小练2: 随机生成两个时间,判断日期大小Random random = new Random();Date d5 = new Date(Math.abs(random.nextInt()));Date d6 = new Date(Math.abs(random.nextInt()));long time3 = d5.getTime();long time4 = d6.getTime();System.out.println(d5);System.out.println(d6);if (time3 > time4) {System.out.println("第一个大");} else if (time4 > time3) {System.out.println("第二个大");} else {System.out.println("一样大");}}
}
SimpleDateFormat类
用于解析字符串转换成日期,或者格式化日期转换成字符串
package com.itheima.demo;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateTest {public static void main(String[] args) throws ParseException {// SimpleDateFormat作用// 1.格式化日期,返回字符串Date date = new Date();SimpleDateFormat sdf = new SimpleDateFormat();System.out.println(sdf.format(date));SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println(sdf2.format(date));// 2.解析字符串,返回日期,注意:解析内容必须和格式化内容保持一致Date date1 = sdf2.parse("2024-01-11 20:53:34");System.out.println(date1);// 字符串2011-11-11 -> 日期2011年11月11日String dateStr = "2011-11-11";SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat sdf4 = new SimpleDateFormat("yyyy年MM月dd日");System.out.println(sdf4.format(sdf3.parse(dateStr)));// 秒杀时间是2023年11月11日 0:0:0 ~ 2023年11月11日 0:10:0String d1 = "2023年11月11日 0:0:0";String d2 = "2023年11月11日 0:10:0";String d3 = "2023年11月11日 0:1:0";String d4 = "2023年11月11日 0:11:0";SimpleDateFormat sdf5 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");Date d11 = sdf5.parse(d1);Date d22 = sdf5.parse(d2);Date d33 = sdf5.parse(d3);Date d44 = sdf5.parse(d4);long start = d11.getTime();long end = d22.getTime();if (start <= d33.getTime() && end >= d33.getTime()) {System.out.println("小红参与了");} else {System.out.println("小红没参与了");}if (start <= d44.getTime() && end >= d44.getTime()) {System.out.println("小蓝参与了");} else {System.out.println("小蓝没参与了");}System.out.println(d11);System.out.println(d22);}
}
Calendar类
package com.itheima.demo;import java.util.Calendar;
import java.util.Date;public class CalendarTest {public static void main(String[] args) {// Calendar的出现是便于我们对时间的操作// 1.设置时间// 细节1:Calendar不是new出来的Calendar c1 = Calendar.getInstance(); // 默认发挥当前时区时间System.out.println(c1);c1.setTime(new Date(0L)); // 时间原点System.out.println(c1);c1.setTimeInMillis(1000L); // 时间原点+1秒System.out.println(c1);// 2.改变时间// 月份是0~11 星期从周日开始1~7c1.set(1, 2000); // 设置年为2000c1.set(2,11); // 设置月为12月System.out.println(c1);// 3.加减时间c1.add(2,1); // +一个月System.out.println(c1);}
}
JDK8版本相关
注意:JDK8的日期对象,为了线程安全,都是不可变的。对日期对象的操作都会生成一个新的日期对象。
ZoneId类
package com.itheima.demo;import java.time.ZoneId;
import java.util.Set;public class ZoneIdTest {public static void main(String[] args) {// 1.获取Java支持的所有时区Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();System.out.println(availableZoneIds);// 2.获取当前时区ZoneId zoneId = ZoneId.systemDefault();System.out.println(zoneId);// 3.指定一个时区ZoneId of = ZoneId.of("Africa/Monrovia");System.out.println(of);}
}
Instant类
package com.itheima.demo;import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;public class InstantTest {public static void main(String[] args) {// 1.默认获取当前时间(无时区)Instant now = Instant.now();System.out.println(now);// 2.指定毫秒/秒/纳秒对应的无时区时间Instant instant = Instant.ofEpochMilli(0L);Instant instant1 = Instant.ofEpochSecond(1L);//+1Instant instant2 = Instant.ofEpochSecond(1L, 1000000000L);//+2System.out.println(instant);System.out.println(instant1);System.out.println(instant2);// 3.加入时区ZonedDateTime zonedDateTime = instant.atZone(ZoneId.systemDefault());System.out.println(zonedDateTime);// 4.比较时间boolean before = instant.isBefore(instant1);System.out.println(before); // falseboolean after = instant.isAfter(instant1);System.out.println(after);// 5.修改时间Instant instant3 = instant.plusMillis(1000L);System.out.println(instant3);Instant instant4 = instant.minusMillis(1000L);System.out.println(instant4);}
}
ZonedDateTime
package com.itheima.demo;import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;public class ZonedDateTimeTest {public static void main(String[] args) {// 1.获取当前时间(有时区)ZonedDateTime now = ZonedDateTime.now();System.out.println(now);// 2.指定有时区的时间(两种方式)ZonedDateTime of = ZonedDateTime.of(2024, 1, 14, 11, 12, 12, 0, ZoneId.systemDefault());ZonedDateTime of1 = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());System.out.println(of);System.out.println(of1);// 3.修改时间ZonedDateTime zonedDateTime = of.withYear(2025);System.out.println(zonedDateTime);// 4.减时间ZonedDateTime zonedDateTime1 = of.minusYears(1L);System.out.println(zonedDateTime1);// 5.加时间ZonedDateTime zonedDateTime2 = of.plusYears(1L);System.out.println(zonedDateTime2);}
}
DateTimeFormatter
package com.itheima.demo;import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;public class DateTimeFormatTest {public static void main(String[] args) {// 日期格式化类DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");// 获取当前时区时间ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(Instant.now(), ZoneId.systemDefault());// 格式化当前时区时间转换成字符串打印String format = dateTimeFormatter.format(zonedDateTime);System.out.println(format);}
}
LocalDate
package com.itheima.demo;import java.time.LocalDate;
import java.time.Month;public class LocalDateTest {public static void main(String[] args) {// 1.获取当前日期LocalDate now = LocalDate.now();System.out.println(now);// 2024-1-15// 2.设置日期LocalDate of = LocalDate.of(2024, 1, 15);System.out.println(of);// 2024-1-15// 3.获取日期信息int year = of.getYear();Month month = of.getMonth();int value = month.getValue();System.out.println(year);// 2024System.out.println(value);// 1// 4.设置日期信息LocalDate localDate = of.withYear(2025);System.out.println(localDate);// 2025-1-15// 5.加日期信息LocalDate localDate1 = of.minusYears(1);// 2023-1-15System.out.println(localDate1);// 6.减少日期信息LocalDate localDate2 = of.plusYears(1); // 2025-1-15System.out.println(localDate2);}
}
LocalTime
package com.itheima.demo;import java.time.LocalTime;public class LocalTimeTest {public static void main(String[] args) {// 1.获取当前时间LocalTime now = LocalTime.now();System.out.println(now); // 19:27...// 2.指定时间LocalTime of = LocalTime.of(20, 30, 10, 1);System.out.println(of);//20:30:10.000000001// 3.设置时间信息LocalTime localTime = of.withHour(21);System.out.println(localTime);//21:30:10.000000001// 4.获取时间信息int hour = of.getHour();System.out.println(hour);//20// 5.加时间信息LocalTime localTime1 = of.plusHours(1);System.out.println(localTime1);//21:30:10.000000001// 6.减时间信息LocalTime localTime2 = of.minusHours(1);System.out.println(localTime2);//19:30:10.000000001}
}
LocalDateTime
package com.itheima.demo;import java.time.LocalDateTime;public class LocalDateTimeTest {public static void main(String[] args) {// 1.获取当前日期时间LocalDateTime now = LocalDateTime.now();System.out.println(now);// 2024-1-15T19:34:1 ...// 2.指定日期时间LocalDateTime of = LocalDateTime.of(2024, 1, 15, 19, 34, 1, 1);System.out.println(of);// 2024-1-15T19:34:1 000000001// 3.设置日期时间信息LocalDateTime localDateTime = of.withHour(20);System.out.println(localDateTime);// 2024-1-15T20:34:1 000000001// 4.获取日期时间信息int value = of.getMonth().getValue();System.out.println(value);// 1// 5.加日期时间信息LocalDateTime localDateTime1 = of.plusMonths(1);System.out.println(localDateTime1); // 2024-2-15T19:34:1 000000001// 6.减日期时间信息LocalDateTime localDateTime2 = of.minusMonths(1);System.out.println(localDateTime2);// 2023-12-15T19:34:1 000000001}
}
Duration
package com.itheima.demo;import java.time.Duration;
import java.time.LocalDateTime;public class DurationTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();LocalDateTime bir = LocalDateTime.of(1999, 5, 29, 0, 0);// 1.记录两个日期时间的差值(时,分,秒)Duration between = Duration.between(bir, now);System.out.println(between);// 2.不同单位下的差值long l1 = between.toDays(); // 天System.out.println(l1);long l = between.toHours(); // 小时System.out.println(l);long l2 = between.toMinutes(); // 分钟System.out.println(l2);long l3 = between.toSeconds(); // 秒System.out.println(l3);}
}
Period
package com.itheima.demo;import java.time.LocalDateTime;
import java.time.Period;public class PeriodTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();LocalDateTime bir = LocalDateTime.of(1999, 5, 29, 0, 0);// 1.日期差值对象(年,月,日)Period between = Period.between(bir.toLocalDate(), now.toLocalDate());System.out.println(between);// 2.不记录整体差值int years = between.getYears();int months = between.getMonths();int days = between.getDays();System.out.println(years);System.out.println(months);System.out.println(days);/*** 结果案例* P24Y7M17D* 24* 7* 17*/}
}
ChronoUnit
package com.itheima.demo;import java.time.LocalDateTime;
import java.time.MonthDay;
import java.time.temporal.ChronoUnit;public class ChronoUnitTest {public static void main(String[] args) {LocalDateTime now = LocalDateTime.now();LocalDateTime bir = LocalDateTime.of(1999, 5, 29, 0, 0);// 各个单位下的差值比较long between = ChronoUnit.YEARS.between(bir, now);long between1 = ChronoUnit.MONTHS.between(bir, now);long between2 = ChronoUnit.DAYS.between(bir, now);long between3 = ChronoUnit.HOURS.between(bir, now);long between4 = ChronoUnit.MINUTES.between(bir, now);long between5 = ChronoUnit.SECONDS.between(bir, now);System.out.println(between);System.out.println(between1);System.out.println(between2);System.out.println(between3);System.out.println(between4);System.out.println(between5);System.out.println("==========拓展API==========");// 只关注月份和天MonthDay bir1 = MonthDay.of(5, 29);MonthDay now1 = MonthDay.from(now);System.out.println(bir1.equals(now1));}
}
包装类
Integer装箱和拆箱
package com.itheima.demo;public class IntegerTest {public static void main(String[] args) {// 1.什么是包装类// 基本数据类型对应的类Integer i = Integer.valueOf(10); // 装箱操作,把int装入Integer对象中int j = i.intValue(); // 拆箱操作,把Integer对象拆成intInteger result = Integer.valueOf(i.intValue() + j);System.out.println(result);// 2.JDK5后,包装类引入了什么新特性// 实现了自动装箱和自动拆箱Integer i1 = 10;int j1 = i1;Integer result1 = i1 + j1;System.out.println(result1);}
}
Integer的进制转换和字符串解析
package com.itheima.demo;public class Integer2Test {public static void main(String[] args) {// 1.十进制转换成其他进制字符串String s1 = Integer.toBinaryString(100); //110010String s2 = Integer.toOctalString(100); // 144String s3 = Integer.toHexString(100); // 64System.out.println(s1);System.out.println(s2);System.out.println(s3);// 2.字符串解析成其他数据类型Integer num = Integer.parseInt("100"); // 必须是数字,否则报异常Double num1 = Double.parseDouble("100");System.out.println(num + num1); //200.0}
}
算法基础(Day21)
查找算法
基本查找(顺序查找)
数据不要求有序
二分查找
要求是有序数据
package com.itheima.demo;public class TwoDivideTest {public static void main(String[] args) {// 1.定义一个有序的数组int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 2.要查询的数据int search = 3;// 3.查询数据对应的下标int index = -1;// 4. 查询的次数int count = 0;// 5. 索引最小值int min = 0;// 6. 索引最大值int max = nums.length - 1;while (true) {// 查询次数+1count++;// 如果索引最小值大于最大值结束查询if (min > max) {break;} else {// 中间索引int middle = (min + max) / 2;if (search < nums[middle]) {// 查询数据 在 中间索引值 左侧 max = middle - 1max = middle - 1;} else if (search == nums[middle]) {// 查询数据 等于 中间索引值 结束最小值查询index = middle;break;} else {// 查询数据 在中间索引值 右侧 min = middle + 1min = middle + 1;}}}System.out.println(index);System.out.println(count);}
}
插值查找
要求数据有序,适用于有序且分布均匀的数据
package com.itheima.demo;/*** 插值查找*/
public class InterpolationSearchTest {public static void main(String[] args) {// 1.定义一个有序(间隔均匀)的数组int[] nums = {0, 2, 4, 6, 7, 9};// 2.定义要查找的值int searchNum = 6;// 3.定义下标最小值int minIndex = 0;// 4.定义下标最大值int maxIndex = nums.length - 1;// 5. 定义查询值索引int searchIndex = -1;int count = 0;while (true) {count++;// 6.如果最小索引大于最大索引 未找到结束查询if (minIndex > maxIndex) {break;}// 7.定义自适应中间下标// 7.1 最小值下标(偏移量) + ((查询值-最小值)/(最大值-最小值)) * (最大值下标 - 最小值下标)int autoIndex = minIndex + (searchNum - nums[minIndex]) * (maxIndex - minIndex) / (nums[maxIndex] - nums[minIndex]);if (nums[autoIndex] == searchNum) {searchIndex = autoIndex;break;} else if (searchIndex < nums[autoIndex]) {maxIndex = autoIndex - 1;} else {minIndex = autoIndex + 1;}}System.out.println(searchIndex);System.out.println(count);}
}
分块查找
要求块间不相交,块内无序
情况1:块间有序
package com.itheima.demo;public class PiecemealSearchTest {public static void main(String[] args) {// 1.定义一个不完全有序的数据int[] nums = {5, 3, 4, 2, 1,10, 8, 7, 6, 9,11, 15, 16, 13, 14};// 2.分为有序块Piece piece = new Piece(5, 0, 4);Piece piece1 = new Piece(10, 5, 9);Piece piece2 = new Piece(16, 10, 14);// 3.创建索引表Piece[] pieces = {piece, piece1, piece2};// 4.查询值int searchNum = 16;// 5.获取查询值下标int searchIndex = getIndex(searchNum, nums, pieces);// 6.打印查询值System.out.println(searchIndex);}private static int getIndex(int searchNum, int[] nums, Piece[] pieces) {// 7.获取指定块Piece piece = getPiece(searchNum, pieces);if (piece == null) {return -1;}// 8.顺序查找获取下标for (int i = piece.startIndex; i <= piece.endIndex; i++) {if (nums[i] == searchNum) {return i;}}// 9.找不到返回-1return -1;}private static Piece getPiece(int searchNum, Piece[] pieces) {// 顺序遍历索引表for (Piece piece : pieces) {if (searchNum <= piece.max) {return piece;}}return null;}
}class Piece {int max;int startIndex;int endIndex;public Piece(int max, int startIndex, int endIndex) {this.max = max;this.startIndex = startIndex;this.endIndex = endIndex;}
}
情况2:块间无序
package com.itheima.demo;public class PiecemealSearchTest1 {public static void main(String[] args) {// 1.定义无序的数据int[] nums = {9, 8, 7, 6, 2, 3, 4, 5, 10, 15, 13, 16, 20, 18, 19};// 2.分块无交集Piece1 piece1 = new Piece1(6, 9, 0, 3);Piece1 piece11 = new Piece1(2, 5, 4, 7);Piece1 piece12 = new Piece1(10, 15, 8, 11);Piece1 piece13 = new Piece1(16, 20, 12, 15);// 3.创建索引表Piece1[] piece1s = {piece1, piece11, piece12, piece13};// 4.定义查找值int searchNum = 21;// 5.获取查找值下标int searchIndex = getIndex(searchNum, nums, piece1s);// 6.打印下标System.out.println(searchIndex);}private static int getIndex(int searchNum, int[] nums, Piece1[] piece1s) {// 7.获取指定块Piece1 piece1 = getPiece(searchNum, piece1s);// 8. 顺序遍历块获取下标if (piece1 == null) {return -1;}for (int i = piece1.startIndex; i <= piece1.endIndex; i++) {if (nums[i] == searchNum) {return i;}}return -1;}private static Piece1 getPiece(int searchNum, Piece1[] piece1s) {for (Piece1 piece1 : piece1s) {if (searchNum <= piece1.max && searchNum >= piece1.min) {return piece1;}}return null;}
}class Piece1 {int min;int max;int startIndex;int endIndex;public Piece1(int min, int max, int startIndex, int endIndex) {this.min = min;this.max = max;this.startIndex = startIndex;this.endIndex = endIndex;}
}
排序算法
冒泡排序
思想:一组数据,从左到右两两比较,小的放前,大的放后
/*** 冒泡排序* * @param nums* @return*/public static int[] maoPao(int[] nums) {int count = 0;for (int i = 0; i < nums.length - 1; i++) {for (int j = 0; j < nums.length - 1 - i; j++) {count++;if (nums[j] > nums[j + 1]) {int temp = nums[j];nums[j] = nums[j + 1];nums[j + 1] = temp;}}}System.out.println("冒泡count:" + count);return nums;}
选择排序
思想:一组数据从索引0开始,索引0与后面的数据依次比较,小的放前,大的放后
/*** 选择排序* * @param nums* @return*/public static int[] xuanZe(int[] nums) {int count = 0;for (int i = 0; i < nums.length - 1; i++) {for (int j = i + 1; j < nums.length; j++) {count++;if (nums[i] > nums[j]) {int temp = nums[i];nums[i] = nums[j];nums[j] = temp;}}}System.out.println("选择count:" + count);return nums;}
插入排序
思想:将数组分为有序和无序的两组数据,无序数据从右往左依次比较有序数据,插入合适位置
/*** 插入排序* * @param nums* @return*/public static int[] chaRu(int[] nums) {int count = 0;for (int i = 0; i < nums.length - 1; i++) {for (int j = i; j >= 0; j--) {count++;if (nums[j] > nums[j + 1]) {int temp = nums[j];nums[j] = nums[j + 1];nums[j + 1] = temp;} else {break;}}}System.out.println("插入count:" + count);return nums;}
递归算法
/*** 累加递归* * @param num* @return*/public static int leiJia(int num) {if (num > 1) {return num + leiJia(num - 1);} else {return num;}}
快速排序
思想:以数组开始索引元素为基准数,数组首先从右向左找小于基准数的停下,然后从左往右找大于基准数的停下,两者交换,当两个寻找下标相同时,与基准数替换,称基准数归位,基准数的左右两边进行重复的递归操作,当开始下标大于结束下标时,结束递归
/*** 快速排序* * @param nums* @return*/public static void kuaiSu(int[] nums, int start, int end) {if (start > end) {return;}int i = start;int j = end;// 注意:如果基准数时索引0的值,则必须先移动end,再移动startint index1 = start;while (start < end) {if (nums[end] > nums[index1]) {end--;continue;}if (nums[start] < nums[index1]) {start++;continue;}int temp = nums[start];nums[start] = nums[end];nums[end] = temp;}int temp = nums[index1];nums[index1] = nums[end];nums[end] = temp;kuaiSu(nums, i, start - 1);kuaiSu(nums, start + 1, j);}
Lambda表达式
- Lambda表达式的作用?
简化了匿名内部类的创建 - Lambda表达式使用前提?
匿名内部类必须是有且仅有一个抽象方法的接口,才可以使用Lambda表达式 - Lambda表达式好处?
使得代码更简洁、更灵活、更紧凑 - 完整格式:
(参数类型 参数名)->{return xxxx;} - 省略格式:
参数名->xxxx
package com.itheima.demo;public class LambdaTest {public static void main(String[] args) {// 匿名内部类swimming(new Swim() {@Overridepublic void swimming() {System.out.println("swimming...");}});System.out.println("----------------------");// Lambda表达式swimming(() -> {System.out.println("swimming...");});}public static void swimming(Swim swim) {swim.swimming();}
}interface Swim {void swimming();
}
集合进阶(Day22)
单列集合顶层接口(Collection)
/*** 1.单列集合顶层接口入门*/public static void CollectionInit() {Collection<Student> list = new ArrayList<>();// 1.添加元素 boolean add(E e)// 细节1:Set添加重复元素时为falselist.add(new Student("张三", 18));list.add(new Student("李四", 20));list.add(new Student("张三", 18));System.out.println(list);// 2.清空集合 clear()
// list.clear();
// System.out.println(list);// 3.删除元素 boolean remove(E e)list.remove(new Student("张三", 18));System.out.println(list);// 4.是否包含元素 boolean contains(Object obj)System.out.println(list.contains(new Student("张三", 18)));// 5.列表是否为空System.out.println(list.isEmpty());// 6.列表的长度System.out.println(list.size());}
迭代器(Iterator)
使用场景:遍历过程中需要删除元素时使用
/*** 2.迭代器遍历* * @return*/public static void iteratorTest() {Collection<Student> list = new ArrayList<>();list.add(new Student("张三", 18));list.add(new Student("李四", 20));// 1.获取Iterator// 细节:Iterator方式遍历,不依赖索引,通过创建指针,移动指针实现遍历Iterator<Student> iterator = list.iterator();// 2.指针指向存在元素while (iterator.hasNext()) {// 3.获取指针元素,移动指针Student student = iterator.next();System.out.println(student);try {
// list.remove(student);iterator.remove();} catch (Exception e) {// ConcurrentModificationExceptione.printStackTrace();}}try {// 细节1:指针指向不存在元素情况下,获取指针元素 报NoSuchElementException// 细节2:next不会归位,迭代器遍历没有回头路// 细节3:while中不应该存在多个next,可能会包NoSuchElementException// 细节4:不能再遍历过程中用集合的方式增删数据,可以用迭代器自带的方法进行删除iterator.next();} catch (Exception e) {e.printStackTrace();}}
增强For和Lambda表达式
场合:适用于仅遍历的场合使用
/*** 3.增强for遍历*/public static void forTest() {Collection<Student> list = new ArrayList<>();list.add(new Student("张三", 18));list.add(new Student("李四", 20));// 增强for// 1.作用 简化迭代器代码的书写// 2.底层 就是迭代器遍历// 3.使用范围 单列集合和数组// 4.JDK支持版本 JDK5+for (Student student : list) {// 细节1: 修改for的变量内部属性会改变list中的值student.setAge(student.getAge() + 1);// 细节2: 修改for的变量本身不会改变集合中的值student = new Student("张三", 20);}for (Student student : list) {System.out.println(student);}System.out.println("-------------------------------------");// Lambda表达式遍历JDK8+// 匿名内部类方式遍历list集合list.forEach(new Consumer<Student>() {@Overridepublic void accept(Student student) {System.out.println(student);}});System.out.println("-------------------------------------");// Lambda表达式方式遍历list集合list.forEach(student -> System.out.println(student));}
单列有序集合接口(List)
List的存取是有序的,数据可以重复,有索引
/*** 4.单列集合List*/public static void listTest() {List<Student> list = new ArrayList<>();// 1. 指定下标添加数据list.add(new Student("张三", 18));list.add(new Student("王五", 22));list.add(1, new Student("李四", 20));System.out.println(list);// 2. 指定下标设置数据System.out.println("-------------------------------------");list.set(1, new Student("李四1", 20));System.out.println(list);// 3. 指定下标删除System.out.println("-------------------------------------");list.remove(1);System.out.println(list);// 4. 获取指定下标值System.out.println("-------------------------------------");System.out.println(list.get(1));}
单列集合List遍历特有方式
使用场景:
- 需要索引时,用普通for循环遍历
- 需要再遍历时,添加数据时,用列表迭代器遍历
/*** 5.单列集合List特有遍历*/public static void listIteratorTest() {List<Student> list = new ArrayList<>();list.add(new Student("张三", 18));list.add(new Student("王五", 22));list.add(1, new Student("李四", 20));// 普通for循环遍历for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}System.out.println("-------------------------------------");// 列表迭代器遍历ListIterator<Student> iteratorList = list.listIterator();while (iteratorList.hasNext()) {System.out.println(iteratorList.next());iteratorList.add(new Student("老刘", 25));}System.out.println("-------------------------------------");for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}}
数据结构
- 栈:先进后出
- 队列:先进先出
- 数组(数据在内存中的存储是连续的)
查询速度快(初始地址值+索引)
删除效率低(原始数据删除,后面数据前移)
插入效率低(插入位置后面数据后移,再插入元素) - 链表
链表的各个节点在内存中的存储时不连续的
查询速度低(所有的查询都要从头开始查询)
删除效率高(相较于数组)
插入效率高(相较于数组)
ArrayList的扩容机制
- ArrayList的数据存储底层是数组
- 对于创建空参的ArrayList,底层数组长度为0
- 第一次加入数据时,底层数据会进行扩容 首次扩容为10 一次加入数据超出10 则扩容为加入数据长多
- 当首次扩容数组被填满再次添加数据, 底层数组会扩容1.5倍,若加入数据超出1.5倍(10->15),则扩容为加入(数据长度+底层数组原长)
LinkedList区别与ArrayList
- LinkedList的底层是双向链表 链表结点在堆内存的存储是不连续的
- 其中有头结点和尾结点开始都为null
- 如果添加一个结点,则头尾结点都存储该结点的地址值
- 如果再添加一个结点,则尾结点等于添加的结点,且添加结点创建时,填充前个结点为原尾结点地址,原尾结点的下一个结点则存在新结点地址
iterator迭代器底层
- list.iterator()是创建时,当前下标为0 上一轮下标为-1
- iterator.hasNext()是判断当前下标!=size则为true
- iterator.next()是让当前下标后移一位,上一轮下标为原当前下标,并返回原当前下标对应的数据
泛型
- 应用场景:
当定义一个类、方法、接口的数据类型不确定,可以使用泛型类,泛型方法,泛型接口
泛型类在定义对象的时候确定数据类型
泛型方法在调用方法的时候确定数据类型
泛型接口在定义实现类确定泛型 - 泛型什么时候出现的,泛型出现的作用或好处
泛型在JDK5出现
JDK5之前都是Object,泛型出现后,不仅在编译阶段约束数据类型,还将运行阶段可能出现的强转报错提前至编译阶段,泛型在确定类型后,可以存储本类及其子类,泛型出现可以调用泛型类的特有行为 - 如果类型希望是某个类的子类(包括它自己)或者父类(包括它自己)可以用
? extends 类名或? super 类名,其中?是泛型通配符 - 注意泛型不能是基本数据类型,只能使用基本数据类型的包装类
// 所有的方法都可以用这个泛型
// 泛型类:泛型定义在类名后面的类
// 同理 泛型接口:泛型定义在接口名后面的接口
class MyArrayList<E> {Object[] objs = new Object[10];int size;void add(E e) {objs[size++] = e;}@SuppressWarnings("unchecked")E get(int index) {return (E) objs[index];}// 泛型方法:泛型定义在方法修饰符后面的方法<T> void show(T t) {System.out.println("Hello " + t);}@Overridepublic String toString() {return Arrays.toString(objs);}
}
树
相关名词:
父节点
左子节点
右子节点
度:子节点的数目
二叉树:任意节点的度都小于或等于2
树高:总层数
左子树
右子树
二叉树的演变
1.普通的二叉树数据存储是无规律的
2.查找二叉树也称搜索二叉树,排序二叉树,遵循的规则是数据大小左节点 < 当前节点 < 右节点
3.二叉树遍历方式:
前序遍历 当前节点 左子树 右子树
中序遍历 左子树 当前节点 右子树
后序遍历 左子树 右子树 当前节点
层序遍历 按层遍历
4.平衡二叉树 任意节点的左子树和右子树层数差小于等于1
平衡二叉树的旋转机制
平衡二叉树有左旋和右旋两种方式,不平衡点降阶为左子树时,原左子树作为不平衡点的右子树,降为右子树时,原右子树作为不平衡点的左子树。
对于插入平衡二叉树的节点进行旋转的情况分为四类:
左左:不平衡点的左子节点的左子树上追加数据,不平衡点仅需要一次右旋完成
左右:不平衡点的左子节点的右子树上追加数据,左子节点完成一次左旋后,不平衡完成一次右旋完成
右右:不平衡点的右子节点的右子树上追加数据,不平衡点仅需要一次左旋完成
右左:不平衡点的右子节点的左子树上追加数据,右子节点完成一次右旋后,不平衡完成一次左旋完成
红黑树
红黑树的特点:
- 所有的节点只能是黑色或者红色
- 根节点必定为黑色
- 节点之间不可能红红相连
- 如果一个节点没有子节点或者父节点,则将会指向Nil节点,Nil节点也会视叶节点,Nil节点一定是黑色
- 任意一个节点到后代叶节点的简单路径,黑色节点的数目完全一致
红黑树添加节点的规则:
- 添加的节点默认设置为红色
- 若添加的节点为根节点 则直接变成黑色
- 若添加的节点不是根节点则分情况讨论
- 若添加的节点的父节点是黑色,不做任何操作
- 若添加的节点的父节点是红色则分情况讨论
- 若添加的节点的叔叔节点为红色,则叔叔节点和父节点都变为黑色,祖父节点变为红色,如果祖父节点是根节点则变为黑色,若不是根节点,祖父节点作用目标节点进一步处理
- 若添加的节点的叔叔节点为黑色,且添加节点为左子节点时,父节点变为黑色,祖父节点变为红色,以祖父节点进行右旋(左左,右右)
- 若添加的节点的叔叔节点为黑色,且添加节点为右子节点时,以父节点进行左旋,并将原父节点作为目标节点进一步处理(左右,右左)
Set系列集合
List集合和Set集合之间的区别?
List集合:有序,可重复,有索引
Set集合:无序,不重复,无索引
无序?
存取顺序不一致
Set集合的实现类
- HashSet: 无序,不重复,无索引
- LinkedHashSet: 有序,不重复,无索引
- TreeSet: 可排序,不重复,无索引
HashSet集合
- 底层:初始化时长度为16,增幅0.75的数组,叫table
- HashSet在增加元素时,首先根据哈希值&(数组长度-1)确定插入下标,如果下标没有数据,则直接填入,若有数据则(JDK7)新数据连接旧数据,(JDK8)旧数据连接新数据
- 之所以存取不一致,是因为取时,从下标零开始遍历,每个元素遍历完毕才取下一个索引数据
- 因为底层数据结构问题,JDK8更是加入了红黑树,所以没有索引
- HashSet保证去重的原理是,重写了equals和hashCode方法,保证了在属性完全相同的时候,插入的数据会和插入索引的链表进行equals比较,相同则不插入
- JDK8在底层链表长度大于8并且数组长度大于64时,变成红黑树
LinkedHashSet
加入了双向链表属性,保证了前后数据的关联性,保证了数据的存取时有序的
TreeSet
- TreeSet底层是红黑树
- 对于数值类型的数据,默认按照升序排列,Integer,Double,Float实现了Comparable方法
- 对于字符或者我字符串的数据,默认按照Ascii码进行升序排列
- 对于存储的数据是自定义类对象,要求该类必须实现Comparable接口,重写compareTo方法,自定义排序规则,否则报运行时异常
- 当默认的Comparable接口的排序不满足需求时,可以使用TreeSet(Comparator comparator)构造方法进行排序
- 实现方法compareTo(T t)/compare(T t1,T t2),返回负数,放在红黑树左边,返回正数,放在红黑树右边,返回零表示重复数据,不添加