Skip to content
小鹏的Notes
Main Navigation 首页 笔记汇总
📱 3D翻盖手机
🎬 Siena Film
✨ 设计动效
🎲 疯狂 3D 游戏库
Github仓库 留言板 说说
黑马八股
小林Coding
导航网站
电脑截屏工具Snipaste
PDF工具
编辑图片
ip解析
电脑屏幕检测
文件格式转换

深浅模式

Sidebar Navigation

自我介绍

👨「代码炼金术士」

🌍「诗的浪漫极客」

Java基础学习

Java基础知识

Java集合

IO流

多线程

File文件

异常

网络编程

注解

Stream流

log日志

XML

反射和动态代理

方法引用

单元测试

类加载器

Java后端

JavaWeb SpringBoot 学习

苍穹外卖

微服务

SpringCloud学习

RabbitMQ

Nacos源码分析

Sentinel源码分析

Linux学习

Linux学习

Git学习

Git学习

Docker学习

Docker学习

中间件

ElasticSearch

RabbitMQ

数据库学习

Mysql学习-汇总

Mysql学习-基础篇

Mysql学习-高级篇

Mysql学习-运维篇

中间件学习

MybatisPlus学习

待续...

前端学习

Vue学习

HTML+CSS+移动端

目录

Java学习笔记: ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

一、Java的基础知识 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

  • java编译基础及java环境配置 ​

  • 变量 ​

  • 二进制中的原码,补码,反码 和 运算符 ​

  • 程序控制结构 ​

  • 数组,排序(冒号排序),查找,多维数组 ​

1.1 java编译基础及java环境配置 ​

1.1.1 java中的javac和java;.java和.class文件区别: ​

image-20250305131544908

先javac编译java文件生成class文件。

1.1.2 java执行流程分析: ​

image-20250305131715960

1.1.3 什么是JVM,JDK和JRE ​

JVM: 1.JVM 是一个虚拟的计算机,具有指令集并使用不同的存储区域。负责执行指令,管理数据、内存、寄存器,包含在JDK 中。 2.对于不同的平台,有不同的虚拟机。 3.Java 虚拟机机制屏蔽了底层运行平台的差别,实现了“一次编译,到处运行”。

image-20250305131544908

JDK,JRE:

image-20250305131741371

JDK,JRE,JVM的包含关系:

​ image-20250305131809835

1.1.4 环境变量path的配置: ​

​ 1.为什么要配置path:

image-20250305131917285 2.配置步骤:

​ image-20250305131932365

1.1.5 java基础知识: ​

image-20250305131950267

image-20250305132004123

文档注释 1.概念: image-20250305132022107

​

2.效果(生成一个html网页):

image-20250305132048724

image-20250305132106824

1.2 变量 ​

1.2.1 变量三要素: ​

变量=变量名+值+数据类型;

1.2.2 数据类型: ​

![](./Java-Learning.assets/屏幕截图 2023-07-20 170049.png)

1.整型: image-202307211141161952.浮点类型:

image-20230721114205628

3.字符类型:

image-20230721114625720

image-20230721114739968

4.布尔类型:

image-20230721115336114

1.2.3 类型转换: ​

1.自动类型转换:

image-20230721115539487

2.强制类型转化:

​ 概念:自动类型转换的逆过程,将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符 ( ),但可能造成精度降低或溢出,格外要注意。

image-20230721115722739

1.2.4 ASCII,Unicode,UTF-8编码简单介绍: ​

image-20230721115158857

1.2.5 Java API 文档: ​

​ 1.作用:寻找类的具体方法功能介绍。

image-20230721120012191

​ 2.Java类的组织形式:

​ image-20230721120111219

1.3 运算符 ​

1.3.1 二进制中的原码、反码、补码: ​

1.二进制:

image-20230721122103039

2.原码、反码、补码的解释:

image-20230721122200945

1.3.2 运算符的介绍: ​

运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。

  1. 算术运算符
  2. 赋值运算符
  3. 关系运算符 (比较运算符)
  4. 逻辑运算符
  5. 位运算符 (需要二进制基础)
  6. 三元运算符

1.算术运算符: ​

image-20230721120802977

​ 细节:

​ image-20230721120916084

3.关系运算符: ​

image-20230721121023234

4.逻辑运算符: ​

  • a&b : & 叫逻辑与:规则:当 a 和 b 同时为 true ,则结果为 true, 否则为 false
  • a&&b : && 叫短路与:规则:当 a 和 b 同时为 true ,则结果为 true,否则为 false
  • a|b : | 叫逻辑或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
  • a||b : || 叫短路或,规则:当 a 和 b ,有一个为 true ,则结果为 true,否则为 false
  • !a : 叫取反,或者非运算。当 a 为 true, 则结果为 false, 当 a 为 false 是,结果为 true
  • a^b: 叫逻辑异或,当 a 和 b 不同时,则结果为 true, 否则为 false

image-20230721121223764

1.&& 和 & 使用区别

  • &&短路与:如果第一个条件为 false,则第二个条件不会判断,最终结果为 false,效率高
  • & 逻辑与:不管第一个条件是否为 false,第二个条件都要判断,效率低

2.|| 和 | 使用区别

  • ||短路或:如果第一个条件为 true,则第二个条件不会判断,最终结果为 true,效率高
  • | 逻辑或:不管第一个条件是否为 true,第二个条件都要判断,效率低

5.位运算符: ​

1.介绍:

image-20230721122359477

2.三 个位运算符 >>、<< 和 >>> , 运算规则:

  • ​ 算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位
  • ​ 算术左移 <<: 符号位不变,低位补 0
  • " >>> "逻辑右移也叫无符号右移,运算规则是: 低位溢出,高位补 0
  • 特别说明:没有 " <<< " 符号

1.4 程序控制结构 ​

在程序中,程序运行的流程控制决定程序是如何执行的,是我们必须掌握的,主要有三大流程控制语句。

  • 顺序控制 (程序从上到下逐行地执行,中间没有任何判断和跳转;)
  • 分支控制 (if-else的单分支,双分支,多分支以及嵌套使用;switch的使用)
  • 循环控制 (for,while,do-while循环使用;break和continue的使用,多重循环的使用)

1.4.1 switch语句的穿越: ​

1.switch的解读:

image-20230721124104336

2.switch流程图:

image-20230721123903500

3.switch的穿越:

1.概念:就是如果case执行语句块里面没有break;会接着执行下一个case语句直到遇到break或者跳出switch语句。

2.例子:(四季的月份分配)

image-20230721124507584

1.4.2 for的多重循环: ​

1.典型例子

  • (冒号排序)具体实现看下一节排序
  • 九九乘法表:
java
//九九乘法表
public class multiplication {
		public static void main(String[] args) {
			int i = 1;
			int j = 1;
			int product;
			for (i = 1; i <= 9; i++) {
				for (j = 1; j <= i; j++) {
					product = i * j;
					System.out.print(j + "*" + i + "=" + product + "\t");
					if (i == j) {
						System.out.print("\n");
					}
				}
			}
		}
}
  • 空心金字塔:
java
public class Stars { 

	//编写一个main方法
	public static void main(String[] args) {

		/*
		
			    *
			  *  *
			 *    *
			********

		思路分析
		化繁为简
		1. 先打印一个矩形
		*****
		*****
		*****
		*****
		*****
		2. 打印半个金字塔

		*    	//第1层 有 1个*
		**   	//第2层 有 2个*
		***		//第3层 有 3个*
		****    //第4层 有 4个*
		*****   //第5层 有 5个*
		
		3. 打印整个金字塔
		*       //第1层 有 1个*   2 * 1 -1   有4=(总层数-1)个空格
	   ***      //第2层 有 3个*   2 * 2 -1   有3=(总层数-2)个空格
	  *****     //第3层 有 5个*   2 * 3 -1   有2=(总层数-3)个空格
	 *******    //第4层 有 7个*   2 * 4 -1   有1=(总层数-4)个空格
	*********   //第5层 有 9个*   2 * 5 -1   有0=(总层数-5)个空格

		4. 打印空心的金字塔 [最难的]
	    *       //第1层 有 1个*   当前行的第一个位置是*,最后一个位置也是*
	   * *      //第2层 有 2个*   当前行的第一个位置是*,最后一个位置也是*
	  *   *     //第3层 有 2个*   当前行的第一个位置是*,最后一个位置也是*
	 *     *    //第4层 有 2个*   当前行的第一个位置是*,最后一个位置也是*
	*********   //第5层 有 9个*   全部输出*
	
		先死后活
		5 层数做成变量 int totalLevel = 5;
	
	//小伙伴 技术到位,就可以很快的把代码写出
		 */
		int totalLevel = 20; //层数
		for(int i = 1; i <= totalLevel; i++) { //i 表示层数

			//在输出*之前,还有输出 对应空格 = 总层数-当前层
			for(int k = 1; k <= totalLevel - i; k++ ) {
				System.out.print(" ");
			}

			//控制打印每层的*个数
			for(int j = 1;j <= 2 * i - 1;j++) {
				//当前行的第一个位置是*,最后一个位置也是*, 最后一层全部 *
				if(j == 1 || j == 2 * i - 1 || i == totalLevel) {
					System.out.print("*");
				} else { //其他情况输出空格
					System.out.print(" ");
				}
			}
			//每打印完一层的*后,就换行 println本身会换行
			System.out.println("");
		}
	}
}

空心菱形:

java
//空心菱形变量版
public class exercise { 

	//编写一个main方法
	public static void main(String[] args) {
	int totallevel = 40;
	for(int i = 1; i <= (int)(totallevel/2) + 1; i++) { //i 表示层数

		//在输出*之前,还有输出 对应空格 = 总层数-当前层
		for(int k = 1; k <= (int)(totallevel/2) + 1 - i; k++ ) {
			System.out.print(" ");
		}

		//控制打印每层的*个数
		for(int j = 1;j <= 2 * i - 1;j++) {
			//当前行的第一个位置是*,最后一个位置也是*, 最后一层全部 *
			if(j == 1 || j == 2 * i - 1) {
				System.out.print("*");
			} else { //其他情况输出空格
				System.out.print(" ");
			}
		}
		System.out.print("\n");
	}
	for (int m = (int)(totallevel/2); m >= 1; m--) {
		for (int p = m; p <= (int)(totallevel/2); p++) {
		//for (int p = 1; p <= (int)(totallevel/2)+1 - m; p++) {
			System.out.print(" ");
		}
		for (int n = 1; n <= 2*m - 1 ; n++) {
			if(n == 1 || n == 2*m - 1) {
				System.out.print("*");
			} else { //其他情况输出空格
				System.out.print(" ");
			}
		}
		System.out.print("\n");
	}
		//每打印完一层的*后,就换行 println本身会换行	
}
}

1.5 数组,排序(冒号排序),查找,多维数组: ​

1.5.1 数组 ​

1.5.1.1 初始化数值: ​

​ 1.动态分配方式(动态初始化):

​ image-20230721125427201

​ 2.静态初始化:

​ image-20230721125629798

1.5.1.2 数组默认值 ​

	数组创建后,如果没有赋值,有默认值
	int 	0,short 0, byte 0, long 0, 
	float 0.0,double 0.0,char \u0000(\u 表示十进制),
	boolean false,String null

1.5.2 排序(冒号排序) ​

java
public class BubbleSort { 
    //编写一个main方法
	public static void main(String[] args) {
	/*
		数组 [24,69,80,57,13]
		第1轮排序: 目标把最大数放在最后
		第1次比较[24,69,80,57,13]
		第2次比较[24,69,80,57,13]
		第3次比较[24,69,57,80,13]
		第4次比较[24,69,57,13,80]

	 */
	int[] arr = {24,69,80,57,13};
	int temp = 0; //用于辅助交换的变量

	//将多轮排序使用外层循环包括起来即可
	//先死后活 =》 4就是 arr.length - 1
	for( int i = 0; i < arr.length - 1; i++) {//外层循环是4次

		for( int j = 0; j < arr.length - 1 - i; j++) {//4次比较-3次-2次-1次
			//如果前面的数>后面的数,就交换
			if(arr[j] > arr[j + 1]) {
				temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;  
			}
		}
		System.out.println("\n==第"+(i+1)+"轮==");
		for(int j = 0; j < arr.length; j++) {
			System.out.print(arr[j] + "\t");
		}

	}
}
}

1.5.3 查找 ​

java
import java.util.Scanner;
public class SeqSearch { 

	//编写一个main方法
	public static void main(String[] args) {
		/*
		有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王猜数游戏:
		从键盘中任意输入一个名称,判断数列中是否包含此名称【顺序查找】 
		要求: 如果找到了,就提示找到,并给出下标值

		思路分析
		1. 定义一个字符串数组
		2. 接收用户输入, 遍历数组,逐一比较,如果有,则提示信息,并退出
		 */
		
		//定义一个字符串数组
		String[] names = {"白眉鹰王", "金毛狮王", "紫衫龙王", "青翼蝠王"};
		Scanner myScanner = new Scanner(System.in); 

		System.out.println("请输入名字");
		String findName = myScanner.next();

		//遍历数组,逐一比较,如果有,则提示信息,并退出
		//这里老师给大家一个编程思想/技巧, 一个经典的方法
		int index = -1;
		for(int i = 0; i < names.length; i++) {
			//比较 字符串比较 equals, 如果要找到名字就是当前元素
			if(findName.equals(names[i])) {
				System.out.println("恭喜你找到 " + findName);
				System.out.println("下标为= " + i);
				//把i 保存到 index
				index = i;
				break;//退出 
			} 
		}

		if(index == -1) { //没有找到
			System.out.println("sorry ,没有找到 " + findName);
		}

	}
}

1.5.4 多维数组 ​

1.5.4.1 初始化: ​

​ 1.动态初始化:

java
public class TwoDimensionalArray02 { 

	//编写一个main方法
	public static void main(String[] args) {

		//int arr[][] = new int[2][3];
		
		int arr[][]; //声明二维数组
		//int[][] arr;
		arr = new int[2][3];//再开空间 
		
		arr[1][1] = 8;
		//遍历arr数组
		for(int i = 0; i < arr.length; i++) {
			for(int j = 0; j < arr[i].length; j++) {//对每个一维数组遍历
				System.out.print(arr[i][j] +" ");
			}
			System.out.println();//换行
		}
	}
}

​

java
public class TwoDimensionalArray03 { 

	//编写一个main方法
	public static void main(String[] args) {

		/*
		看一个需求:动态创建下面二维数组,并输出
		
		 i = 0:	1		
		 i = 1:	2	2	
		 i = 2:	3	3	3

		 一个有三个一维数组, 每个一维数组的元素是不一样的
		 */
		
		//创建 二维数组,一个有3个一维数组,但是每个一维数组还没有开数据空间
		int[][] arr = new int[3][]; 
		
		for(int i = 0; i < arr.length; i++) {//遍历arr每个一维数组
			//给每个一维数组开空间 new
			//如果没有给一维数组 new ,那么 arr[i]就是null
			arr[i] = new int[i + 1]; 

			//遍历一维数组,并给一维数组的每个元素赋值
			for(int j = 0;  j < arr[i].length; j++) {
				arr[i][j] = i + 1;//赋值
			}

		}
}
}

​ 2.静态初始化:

java
			int[][] arr = {{1,1,1}, {8,8,9}, {100}};

​

1.5.4.2 例子: ​

  • 杨辉三角:
java
public class YangHui { 

	//编写一个main方法
	public static void main(String[] args) {
		/*
		使用二维数组打印一个 10 行杨辉三角
		1
		1 1
		1 2 1
		1 3 3  1
		1 4 6  4  1
		1 5 10 10 5 1

		规律
		 1.第一行有 1 个元素, 第 n 行有 n 个元素
		 2. 每一行的第一个元素和最后一个元素都是 1
		 3. 从第三行开始, 对于非第一个元素和最后一个元素的元素的值. arr[i][j] 
		  arr[i][j]  =  arr[i-1][j] + arr[i-1][j-1]; //必须找到这个规律

		 */
		int[][] yangHui = new int[12][];
		for(int i = 0; i < yangHui.length; i++) {//遍历yangHui的每个元素

			//给每个一维数组(行) 开空间
			yangHui[i] = new int[i+1];
			//给每个一维数组(行) 赋值
			for(int j = 0; j < yangHui[i].length; j++){
				//每一行的第一个元素和最后一个元素都是1
				if(j == 0 || j == yangHui[i].length - 1) {
					yangHui[i][j] = 1;
				} else {//中间的元素
					yangHui[i][j]  =  yangHui[i-1][j] + yangHui[i-1][j-1];
				}
			}
		}
		//输出杨辉三角
		for(int i = 0; i < yangHui.length; i++) {
			for(int j = 0; j < yangHui[i].length; j++) {//遍历输出该行
				System.out.print(yangHui[i][j] + "\t");
			}
			System.out.println();//换行.
		}
		
	}
}

二、面向对象编程基础部分 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

2.1类和对象的区别: ​

  1. 类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.
  2. 对象是具体的,实际的,代表一个具体事物, 即 是实例.
  3. 类是对象的模板,对象是类的一个个体,对应一个实例

2.2 对象在内存中存在形式: ​

![](./Java-Learning.assets/屏幕截图 2023-07-21 162910.png)

2.3 如何创建对象,访问属性: ​

一、创建对象: ​

1.先声明再创建

​ Cat cat ; //声明对象 cat

​ cat = new Cat(); //创建

2.直接创建

​ Cat cat = new Cat();

二、访问属性: ​

1.基本语法:

对象名.属性名;cat.name ; cat.age; cat.color;

2.类和对象的内存分配机制:

image-20230728114310609

image-20230728114330818

Java 内存的结构分析 ​

1.概念: ​

栈: 一般存放基本数据类型(局部变量),还有对象的引用;

堆: 存放对象(Cat cat , 数组等);

方法区:常量池(常量,比如字符串), 类加载信息;

2.流程: ​

  1. 先加载 Person 类信息(属性和方法信息, 只会加载一次);
  2. 在堆中分配空间, 进行默认初始化(看规则);
  3. 把地址赋给 p1 , p1 就指向对象,把p1赋给了p2,p2也指向了对象;

2.4 属性/成员变量/字段: ​

1.概念: ​

  • 从概念或叫法上看: 成员变量 = 属性 = field(字段)。
  • 属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。

2.细节: ​

属性的定义语法同变量,示例:访问修饰符 属性类型 属性名。

修饰符: 控制属性的访问范围有四种访问修饰符 public, protected, 默认, private ,具体中级部分详细介绍。

属性的定义类型可以为任意类型,包含基本类型或引用类型;属性如果不赋值,有默认值,规则和数组一致。具体说: int 0,short 0, byte 0, long 0, float 0.0,double 0.0,char \u0000,

boolean false,String null;

2.5 成员方法: ​

1.成员方法的定义: ​

(1)格式:

访问修饰符 返回数据类型 方法名(形参列表..) {

​ //方法体语句;

​ return 返回值;

}

(2)概念:

  • 形参列表:表示成员方法输入 cal(int n) , getSum(int num1, int num2);
  • 返回数据类型:表示成员方法输出, void 表示没有返回值
  • 方法主体:表示为了实现某一功能代码块
  • return 语句不是必须的。

(3)细节:

  1. 调用带参数的方法时,一定对应着参数列表传入相同类型或兼容类型 的参数;
  2. 实参和形参的类型要一致或兼容、个数、顺序必须一致;
  3. 方法不能嵌套定义;
  4. 一个方法最多有一个返回值 [思考:如何返回多个结果 返回数组 ];
  5. 返回类型可以为任意类型,包含基本类型或引用类型(数组,对象);
  6. 如果方法要求有返回数据类型,则方法体中最后的执行语句必须为 return 值;
  7. /如果方法是 void,则方法体中可以没有 return 语句,或者 只写 return ;

2.方法的调用: ​

image-20230728120434910

3.方法的调用机制原理:(jvm内存示意图) ​

image-20230728120612364

4.成员方法传参机制: ​

(1)基本数据类型的传参机制:

image-20230728121401611

​ 例题:

image-20230728121509184

(2)引用数据类型的传参机制:

总结:引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!

例题:

image-20230728121812562

image-20230728121827294

image-20230728122102102

2.6方法的递归调用: ​

1.JVM内存示意图:

image-20230728122417034

2.经典阶乘问题:

image-20230728122636010

3.递归重要规则:

image-20230728122716691

4.例题:

image-20230728123108290

(1)斐波那契数列:

java
public class RecursionExercise01 {
//编写一个 main 方法
public static void main(String[] args) {
T t1 = new T();
int n = 7;
int res = t1.fibonacci(n);
if(res != -1) {
System.out.println("当 n="+ n +" 对应的斐波那契数=" + res);
}
    
class T {
/*
请使用递归的方式求出斐波那契数 1,1,2,3,5,8,13...给你一个整数 n,求出它的值是多
思路分析
1. 当 n = 1 斐波那契数 是 1
2. 当 n = 2 斐波那契数 是 1
3. 当 n >= 3 斐波那契数 是前两个数的和
4. 这里就是一个递归的思路
*/
public int fibonacci(int n) {
if( n >= 1) {
	if( n == 1 || n == 2) {
	return 1;
} else {
return fibonacci(n-1) + fibonacci(n-2);
}
} else {
System.out.println("要求输入的 n>=1 的整数");
return -1;
}
}

(2)猴子吃桃问题:

java
public class RecursionExercise01 {
//编写一个 main 方法
public static void main(String[] args) {
T t1 = new T();
//桃子问题
int day = 0;
int peachNum = t1.peach(day);
if(peachNum != -1) {
System.out.println("第 " + day + "天有" + peachNum + "个桃子");
}
    }
}
class T {
    /*
猴子吃桃子问题:有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!
以后每天猴子都吃其中的一半,然后再多吃一个。当到第 10 天时,
想再吃时(即还没吃),发现只有 1 个桃子了。问题:最初共多少个桃子?
思路分析 逆推
1. day = 10 时 有 1 个桃子
2. day = 9 时 有 (day10 + 1) * 2 = 4
3. day = 8 时 有 (day9 + 1) * 2 = 10
4. 规律就是 前一天的桃子 = (后一天的桃子 + 1) *2//找规律
5. 递归
*/
public int peach(int day) {
if(day == 10) {//第 10 天,只有 1 个桃
return 1;
} else if ( day >= 1 && day <=9 ) {
return (peach(day + 1) + 1) * 2;//规则,自己要想
} else {
System.out.println("day 在 1-10");
return -1;
}
}
}

5.递归调用应用实例-迷宫问题 (回溯法)

image-20230728124007419

重要思路:

设计查找路线的优先级, 先确定老鼠找路策略 下->右->上->左;根据优先级实现代码;

java
public class MiGong { 

	//编写一个main方法
	public static void main(String[] args) {

		//思路
		//1. 先创建迷宫,用二维数组表示 int[][] map = new int[8][7];
		//2. 先规定 map 数组的元素值: 0 表示可以走 1 表示障碍物 
		
		int[][] map = new int[8][7];
		//3. 将最上面的一行和最下面的一行,全部设置为1
		for(int i = 0; i < 7; i++) {
			map[0][i] = 1;
			map[7][i] = 1;
		}
		//4.将最右面的一列和最左面的一列,全部设置为1
		for(int i = 0; i < 8; i++) {
			map[i][0] = 1;
			map[i][6] = 1;
		}
		map[3][1] = 1;
		map[3][2] = 1;
		map[2][2] = 1; //测试回溯 
		// map[2][1] = 1;
		// map[2][2] = 1;
		// map[1][2] = 1;

		//输出当前的地图
		System.out.println("=====当前地图情况======");
		for(int i = 0; i < map.length; i++) {
			for(int j = 0; j < map[i].length; j++) {
				System.out.print(map[i][j] + " ");//输出一行
			}
			System.out.println();
		}

		//使用findWay给老鼠找路
		T t1 = new T();
		//下右上左
		t1.findWay(map, 1, 1);

		System.out.println("\n====找路的情况如下=====");

		for(int i = 0; i < map.length; i++) {
			for(int j = 0; j < map[i].length; j++) {
				System.out.print(map[i][j] + " ");//输出一行
			}
			System.out.println();
		}

	}
}

class T  {

	//使用递归回溯的思想来解决老鼠出迷宫
	
	//老韩解读
	//1. findWay方法就是专门来找出迷宫的路径
	//2. 如果找到,就返回 true ,否则返回false
	//3. map 就是二维数组,即表示迷宫
	//4. i,j 就是老鼠的位置,初始化的位置为(1,1)
	//5. 因为我们是递归的找路,所以我先规定 map数组的各个值的含义
	//0 表示可以走 1 表示障碍物 2 表示已经走过了(并且可以走) 3 表示走过,但是走不通是死路
	//6. 当map[6][5] =2 就说明找到通路,就可以结束,否则就继续找.
	//7. 先确定老鼠找路策略 下->右->上->左
	
	public boolean findWay(int[][] map , int i, int j) {
		if(map[6][5] == 2) {//说明已经找到
			return true;
		} else {
			if(map[i][j] == 0) {//当前这个位置0,说明表示可以走
				//我们假定可以走通
				map[i][j] = 2;
				//使用找路策略,来确定该位置是否真的可以走通
				//下->右->上->左
				if(findWay(map, i + 1, j)) {//先走下	 //递归思想:从这个第一个if中的findway开始进入递归,
					return true;						//后面的每一个else-if都是对从第一个findway开始之后的递归调用  做的"预判";
				} else if(findWay(map, i, j + 1)){//右	//由于 这几个if,else-if语句顺序已经规定好了,所以
					return true;						//优先走的顺序为下-右-上-左;
				} else if(findWay(map, i-1, j)) {//上   //(相当于是从第一个if语句的findway作为递归的入口)
					return true;
				} else if(findWay(map, i, j-1)){//左
					return true;	//这里几个if,else-if返回true对应的是boolean类型是为了跳出当前的findway方法;
				} else {
					map[i][j] = 3;
					return false; //如果下一步的"下右上左"都走不通就返回false,回溯(返回到之前的下一步)
				}
			} else { //map[i][j] = 1 , 2, 3
				return false; 
			}
		}
	}

	//修改找路策略,看看路径是否有变化
	//下->右->上->左 ==> 上->右->下->左
	public boolean findWay2(int[][] map , int i, int j) {
		if(map[6][5] == 2) {//说明已经找到
			return true;
		} else {
			if(map[i][j] == 0) {//当前这个位置0,说明表示可以走
				//我们假定可以走通
				map[i][j] = 2;
				//使用找路策略,来确定该位置是否真的可以走通
				//上->右->下->左
				if(findWay2(map, i - 1, j)) {//先走上
					return true;
				} else if(findWay2(map, i, j + 1)){//右
					return true;
				} else if(findWay2(map, i+1, j)) {//下
					return true;
				} else if(findWay2(map, i, j-1)){//左
					return true;
				} else {
					map[i][j] = 3;
					return false;
				}
			} else { //map[i][j] = 1 , 2, 3
				return false; 
			}
		}
	}
}

6.递归调用应用实例-汉诺塔:

image-20230728124235308

java
public class HanoiTower { 

	//编写一个main方法
	public static void main(String[] args) {

		Tower tower = new Tower();
		tower.move(64, 'A', 'B', 'C');
	}
}

class Tower {

	//方法
	//num 表示要移动的个数, a, b, c 分别表示A塔,B 塔, C 塔
	public void move(int num , char a, char b ,char c) {
		//如果只有一个盘 num = 1
		if(num == 1) {
			System.out.println(a + "->" + c);
		} else {
			//如果有多个盘,可以看成两个 , 最下面的和上面的所有盘(num-1)
			//(1)先移动上面所有的盘到 b, 借助 c
			move(num - 1 , a, c, b);
			//(2)把最下面的这个盘,移动到 c
			System.out.println(a + "->" + c);
			//(3)再把 b塔的所有盘,移动到c ,借助a
			move(num - 1, b, a, c);
		}
	}
}
/*4.接下来是考虑中间这个整体了,中间的整体看做一个全新的汉诺塔模型,比最初的就少了底下一层
1.思想就是,先写一个只有一个盘的时候的情况"a->c"
5.然后中间的整体模型可以看做初始位置在b,最终位置要到c,中间柱子为a的模型
6.那么中间的模型怎么移动呢,同样调用我们写的move方法,就这样一层层递归,最终递归到num==1
*/

7.递归调用应用实例-八皇后问题

单击下面超链接使用百度网盘查看(提取码:ak47):

八皇后问题代码

2.7 方法重载 ​

1.注意事项和使用细节:

image-20230728181313313

2.例题:

image-20230728181516675

2.8 可变参数 ​

1.概念:java 允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。

就可以通过可变参数实现

2.基本语法:

访问修饰符 返回类型 方法名(数据类型... 形参名) {

}

3.注意事项和使用细节:

image-20230728181945938

4.例题:

java
public class VarParameterExercise {
//编写一个 main 方法
public static void main(String[] args) {
HspMethod hm = new HspMethod();
System.out.println(hm.showScore("milan" , 90.1, 80.0 ));
System.out.println(hm.showScore("terry" , 90.1, 80.0,10,30.5,70 ));
}
}
class HspMethod {
/*
有三个方法,分别实现返回姓名和两门课成绩(总分),
返回姓名和三门课成绩(总分),返回姓名和五门课成绩(总分)。
封装成一个可变参数的方法
*/
//分析 1. 方法名 showScore 2. 形参(String ,double... ) 3. 返回 String
//听课小伙伴,老师要求必须自己动手写
public String showScore(String name ,double... scores ) {
    double totalScore = 0;
for(int i = 0; i < scores.length; i++) {
totalScore += scores[i];
}
return name + " 有 " +scores.length + "门课的成绩总分为=" + totalScore;
}
}

2.9 作用域 ​

1.基本使用:

image-20230728182324242

2.注意事项和使用细节:

image-20230728182448409

2.10 构造方法/构造器 ​

1.基本介绍:

构造方法又叫构造器(constructor),是类的一种特殊的方法,它的主要作用是完成对新对象的初始化。它有几个特点:

  • 方法名和类名相同
  • 没有返回值
  • 在创建对象时,系统会自动的调用该类的构造器完成对象的初始化。

2.基本语法:

[修饰符] 方法名(形参列表){

方法体;

}

3.说明:

  1. 构造器的修饰符可以默认, 也可以是 public protected private;
  2. 构造器没有返回值;
  3. 方法名 和类名字必须一样;
  4. 参数列表 和 成员方法一样的规则;
  5. 构造器的调用, 由系统完成;

4.注意事项和使用细节:

image-20230728183057536

2.11 对象创建的流程分析 ​

1.案例:

image-20230728183319835

2.流程分析:

image-20230728183440585

2.12 this关键字 ​

1.概念:

​ java虚拟机会给每个对象分配this,表示当前对象; this.name;就表示当前对象的属性,可以和局部变量进行区分;

2.案例:

image-20230728183925148

3.this的注意事项和使用细节:

  1. this 关键字可以用来访问本类的属性、方法、构造器
  2. this 用于区分当前类的属性和局部变量
  3. 访问成员方法的语法:this.方法名(参数列表);
  4. 访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, 必须放在第一条语句)
  5. this 不能在类定义的外部使用,只能在类定义的方法中使用。

三、面向对象编程中级部分 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

3.1 IDE(集成开发环境)-IDEA ​

1.导出文件:

image-20230728184734566

2.IDEA常用快捷键:

  1. 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d
  2. 复制当前行, 自己配置 ctrl + alt + 向下光标
  3. 补全代码 alt + /
  4. 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】

image-20230728185105071

image-20230728185120976

3.模版/自定义模版:

image-20230728185229080

3.2 包 ​

1.包的本质分析:

image-20230728185429726

2.包的基本语法:

image-20230728185504310

3.包的作用:

image-20230728185531737

4.命名规则:

image-20230728185622002

5.引入包:

image-20230728185704923

6.注意事项和使用细节:

image-20230728185742844

3.3 访问修饰符 ​

1.基本介绍:

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用 public 修饰,对外公开
  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开
  3. 默认级别:没有修饰符号,向同一个包的类公开.
  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开

2.访问范围:

image-20230728185949582

3.使用的注意事项:

image-20230728190022936

面向对象编程的三大特征:封装、继承、多态 ​

3.4 封装 ​

1.介绍:

image-20230728190320489

2.封装的理解和好处:

image-20230728190352959

3.封装的实现步骤:

image-20230728190422266

3.5 继承 ​

1.继承的作用:

**继承可以解决代码复用,**让我们的编程更加靠近人类思维.

当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。

2.继承示意图:

image-20230728190711810

3.继承的语法:

image-20230728190737858

4.细节:

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问;(set和get方法,体现了封装的作用)
  2. 子类必须调用父类的构造器, 完成父类的初始化;
  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器(默认有super()在第一行隐式被调用了),如果父类没有提供无参构造器,则必须在子类的构造器中**用 super 去指定使用父类的哪个构造器完成对父类的初始化工作**,否则,编译不会通过;
  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
  5. super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
  6. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
  7. java 所有类都是 Object 类的子类, Object 是所有类的基类.
  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类**(顶级父类)**
  9. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
  10. 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
  11. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

5.继承的本质:

按照查找关系来返回信息

(1) 首先看子类是否有该属性

(2) 如果子类有这个属性,并且可以访问,则返回信息

(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)

(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object...

继承内存布局:

image-20230728191753343

3.6 super关键字 ​

1.基本介绍:

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

2.基本语法:

image-20230728191938895

3.super 给编程带来的便利/细节:

image-20230728192116872

4.super和this的比较:

image-20230728192152953

3.7 方法重写/覆盖(override) ​

1.概念:

image-20230728201131726

2.注意事项和使用细节:

image-20230728201235099


3.重载和重写的比较:

image-20230728201335417

3.8 多态 ​

1.基本介绍: ​

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

2.方法的多态:(重写和重载就体现多态) ​

java
package com.hspedu.poly_;
public class PloyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B { //父类
public void say() {
System.out.println("B say() 方法被调用...");
}
}
class A extends B {//子类
public int sum(int n1, int n2){//和下面 sum 构成重载
return n1 + n2;
}
public int sum(int n1, int n2, int n3){
return n1 + n2 + n3;
}
public void say() {
System.out.println("A say() 方法被调用...");
}
}

3.对象的多态: ​

image-20230728201805826

4.多态注意事项和细节讨论(向上/下转型): ​

  • 多态的前提是:两个对象(类)存在继承关系; ​

  • 多态的向上转型: ​

image-20230728202236832

总结:

​ **1.语法:**父类类型引用名 = new 子类类型();

​ 2.向上转型调用方法的规则如下:

​ (1)可以调用父类中的所有成员(需遵守访问权限);

​ (2)但是不能调用子类的特有的成员;

​ (3)因为在编译阶段(写代码阶段),能调用哪些成员,是由编译类型来决定的;

​ (4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则(由下至上)一致;

​ 3.属性没有重写之说!属性的值看编译类型;

  • 多态向下转型: ​

image-20230728202255334

  • 多态参数: ​

    方法定义的形参类型为父类类型,实参类型允许为子类类型; ​

5.编译时类型和运行时类型具体区别: ​

编译时类型和运行时类型: ​

Java的引用变量有两个类型,一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,会出现所谓的多态。因为子类其实是一种特殊的父类,因此java允许把一个子类对象直接赋值给一个父类引用变量,无须任何类型转换或者被称为向上转型,由系统自动完成。

引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法(意思是说:编写代码时,只能调用父类中具有的方法,如果子类重写了该方法,运行时实际调用的是运行时类型的该方法。程序在编译时,会在编译类型中检查是否具有所调用的方法,如果编写代码时,使用引用变量调用子类中的特有方法,或者调用重载了父类中的方法,而父类中找不到该方法,则会报编译错误),因此,编写Java代码时,引用变量只能调用声明该变量所用类里包含的方法。与方法不同的是,对象的属性则不具备多态性。通过引用变量来访问其包含的实例属性时,系统总是试图访问它编译时类所定义的属性,而不是它运行时所定义的属性。

——要访问子类中特有的方法和属性,在编写代码时,则必须进行类型转换。(向下转型)

6.要访问子类中特有的方法和属性,在编写代码时,则必须进行类型转换(向下转型): ​

1.原因: ​

​ 编写Java代码时,引用变量只能调用声明该变量所用类里包含的方法。

如果想要在编写代码时,想要访问子类中特有的方法和属性,则必须进行类型转换(向下转型)

2.向下转型: ​

(1)语法:子类类型 引用名 =(子类类型)父类引用;

(2)要求父类的引用必须指向的是当前目标类型的对象

7.Java的动态绑定机制: ​

image-20230728204520609

8.多态的应用: ​

1.多态数组: ​

数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

2.实例: ​

java
package com.hspedu.poly_.polyarr_;
public class PloyArray {
public static void main(String[] args) {
//应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
// 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用 say
for (int i = 0; i < persons.length; i++) {
//提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有 JVM 来判断
System.out.println(persons[i].say());//动态绑定机制
//使用 类型判断 + 向下转型. if(persons[i] instanceof Student) {//判断 person[i] 的运行类型是不是 Student
Student student = (Student)persons[i];//向下转型
student.study();
//也可以使用一条语句 ((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();

9.instanceof 比较操作符 ​

作用:instanceof 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型; ​

3.9 Object类详解 ​

3.9.1 equals方法: ​

1.==和 equals 的对比:

image-20230728205643360

2.equals方法重写:

  • Object 类的 equals 是默认的:

​ 即 Object 的 equals 方法默认就是比较对象地址是否相同

​ 也就是判断两个对象是不是同一个对象.

  • 看看 Jdk 的源码 String 类的 equals 方法:

​ 把 Object 的 equals 方法重写了,变成了比较两个字符串值是否相同

  • 从源码可以看到 Integer 也重写了 Object 的 equals 方法,:

​ 变成了判断两个值是否相同

3.如何重写equals方法:

java
package com.hspedu.object_;
public class EqualsExercise01 {
public static void main(String[] args) {
Person person1 = new Person("jack", 10, '男');
Person person2 = new Person("jack", 20, '男');
System.out.println(person1.equals(person2));//假
}
}
//判断两个 Person 对象的内容是否相等,
//如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false
class Person{ //extends Object
    private String name;
    private int age;
    private char gender;
    //重写 Object 的 equals 方法
    public boolean equals(Object obj) {
        //判断如果比较的两个对象是同一个对象,则直接返回 true
        if(this == obj) {
            return true;
        }
        //类型判断
        if(obj instanceof Person) {//是 Person,我们才比较
            //进行 向下转型, 因为我需要得到 obj 的 各个属性
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        //如果不是 Person ,则直接返回 false
        return false;
    }
    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public char getGender() {
        return gender;
    }
    public void setGender(char gender) {
        this.gender = gender;
    }
}

3.9.2 hashCode 方法: ​

image-20230728210308639

1.小结: ​

  1. 提高具有哈希结构的容器的效率!
  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!
  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的
  4. 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址。

​ 5.后面在集合,中 hashCode 如果需要的话,也会重写, 在集合时,具体说如何重写 hashCode();

3.9.3 toString 方法 ​

1.基本介绍: ​

默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】

子类往往重写 toString 方法,用于返回对象的属性信息;

2.重写 toString 方法: ​

打印对象或拼接对象时,都会自动调用该对象的 toString 形式

3.toString方法是默认调用的: ​

**当直接输出一个对象时,toString 方法会被默认的调用**, 比如System.out.println(monster); 就会默认调用monster.toString();

3.9.4 finalize 方法 ​

  • 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作;
  • 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用 finalize 方法;
  • 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制;

3.10 断点调试(debug) ​

1.需求:

image-20230728211404109

2.断点调试介绍:

image-20230728211429396

3.IDEA断点调试快捷键:

F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)

F7:跳入方法内.

F8: 逐行执行代码.

shift+F8: 跳出方法

image-20230728211546009

3.11 项目-零钱通 ​

**1.需求说明:**使用 Java 开发 零钱通项目 , 可以完成收益入账,消费,查看明细,退出系统等功能.

2.界面要求:

image-20230728212741389

3.细节要求:

image-20230728212819023

4.代码具体实现:

单击下面链接使用百度网盘下载观看,提取码:ak47

零钱通

3.12 项目-房屋出租系统 ​

**1.需求说明:**实现基于文本界面的《房屋出租软件》, ​

能够实现对房屋信息的添加、修改和删除(用数组实现),并能够打印房屋明细表

2.界面要求: ​

(1)项目界面 - 主菜单

image-20230728213512070

(2)项目界面- 新增房源

image-20230728213530881

(3)项目界面- 查找房源

image-20230728213551427

(4)项目界面- 删除房源

image-20230728213611798

(5)项目界面- 修改房源

image-20230728213629408

(6)项目界面- 房屋列表

image-20230728213650052

(7)项目界面- 退出系统

image-20230728213709010

3.项目设计-程序框架图(分成模式): ​

image-20230728213815887

4.准备工具类Utility,提高开发效率: ​

在实际开发中,公司都会提供相应的工具类和开发库,可以提高开发效率,程序员也需要能够看懂别人写的代码,并能够正确的调用。

单击下面链接使用百度网盘下载观看,提取码:ak47

工具类Utility

5.代码具体实现: ​

单击下面链接使用百度网盘下载观看,提取码:ak47

项目-房屋出租系统代码

四、面向对象编程高级部分 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

4.1 类变量和类方法: ​

一.类变量: ​

(1)定义:

image-20230803220139469

(2)定义语法:

image-20230803220321398

(3)访问类变量:

image-20230803220400624

(4)类变量使用注意事项和细节讨论:

image-20230803220458749

二.类方法: ​

(1)定义语法:

image-20230803220641308

(2)类方法的调用:

image-20230803220708927

(3)类方法经典的使用场景:

image-20230803220815768

(4)类方法使用注意事项和细节讨论:

image-20230803220925441

小结:静态方法,只能访问静态的成员,非静态的方法,可以访问静态成员和非静态成员;

4.2 main方法语法: ​

(1)形式: ​

image-20230803221325016

(2)如何在执行java命令的时候传递参数: ​

image-20230803221943069

(3)特别提示: ​

1.在 main()方法中,我们可以直接调用 main 方法所在类的静态方法或静态属性。

2.但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,

3.案例:

java
public class Main01 {
//静态的变量/属性
private static String name = "韩顺平教育";
//非静态的变量/属性
private int n1 = 10000;
//静态方法
public static void hi() {
System.out.println("Main01 的 hi 方法");
}
//非静态方法
public void cry() {
System.out.println("Main01 的 cry 方法");
}
public static void main(String[] args) {
//可以直接使用 name
//1. 静态方法 main 可以访问本类的静态成员
System.out.println("name=" + name);
hi();
//2. 静态方法 main 不可以访问本类的非静态成员
//System.out.println("n1=" + n1);//错误
//cry();
//3. 静态方法 main 要访问本类的非静态成员,需要先创建对象 , 再调用即可
Main01 main01 = new Main01();
System.out.println(main01.n1);//ok
main01.cry();
}
}

4.3 代码块: ​

(1)基本介绍: ​

image-20230803222228258

(2)基本语法: ​

image-20230803222257186

(3)好处: ​

image-20230803222330542

(4)代码块使用注意事项和细节讨论: ​

image-20230803222429375

(5) 类什么时候被加载: ​

1.创建对象实例时(new);

2.创建子类对象实例,父类也会被加载

3.使用类的静态成员时(静态属性,静态方法)

(6)创建一个对象时,在一个类中调用顺序 ​

image-20230803223259607

(7)构造器的最前面隐含了super()和调用普通代码块: ​

image-20230803223515263

代码演示:

java
public class CodeBlockDetail03 {
public static void main(String[] args) {
new BBB();//(1)AAA 的普通代码块(2)AAA() 构造器被调用(3)BBB 的普通代码块(4)BBB() 构造器被调用
}
}
class AAA { //父类 Object
{
System.out.println("AAA 的普通代码块");
}
public AAA() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("AAA() 构造器被调用....");
}
}
class BBB extends AAA {
{
System.out.println("BBB 的普通代码块...");
}
public BBB() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("BBB() 构造器被调用....");
}
}

(8)创建一个子类对象时(继承关系)类中的调用顺序: ​

image-20230803223823699

总结: ​

​ 第3点到第6点可以根据**(7)==构造器的最前面隐含了super()和调用普通代码块**==推得;

4.4 单例模式设计(饿汉式、懒汉式): ​

(1)什么是单例模式: ​

image-20230803224715431

(2)单例模式步骤: ​

image-20230803224851269

(3)饿汉式实例: ​

步骤[单例模式-饿汉式]

步骤 ​

  • 1. 将构造器私有化
  • 2. 在类的内部直接创建对象(该对象是 static)
  • 3. 提供一个公共的 static 方法,返回对象
java
public class SingleTon01 {
    public static void main(String[] args) {
// GirlFriend xh = new GirlFriend("小红");
// GirlFriend xb = new GirlFriend("小白");
//通过方法可以获取对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
//System.out.println(GirlFriend.n1);
//... }
}
//有一个类, GirlFriend
//只能有一个女朋友
class GirlFriend {
private String name;
    //public static int n1 = 100;
//为了能够在静态方法中,返回 gf 对象,需要将其修饰为 static
//对象,通常是重量级的对象,饿汉式可能造成创建了对象,但是没有使用,    
private static GirlFriend gf = new GirlFriend("小红红");
//如何保障我们只能创建一个 GirlFriend 对象
//步骤[单例模式-饿汉式]
//1. 将构造器私有化 
//2. 在类的内部直接创建对象(该对象是 static)
//3. 提供一个公共的 static 方法,返回 gf 对象
private GirlFriend(String name) {
System.out.println("构造器被调用.");
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}

(4)懒汉式实例: ​

步驟 ​

  • 1.仍然构造器私有化
  • 2.定义一个 static 静态属性对象
  • 3.提供一个 public 的 static 方法,可以返回一個 Cat 对象
  • 4.懒汉式,只有当用户使用 getInstance 时,才返回 cat对象, 后面再次调用时,会返回上次创建的 cat 对象
java
/**
* 演示懒汉式的单例模式
*/
public class SingleTon02 {
public static void main(String[] args) {
//new Cat("大黃");
//System.out.println(Cat.n1);
Cat instance = Cat.getInstance();
System.out.println(instance);
//再次调用 getInstance
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
//希望在程序执行过程中,只能创建一个Cat对象
//使用单例模式
class Cat {
private String name;
public static int n1 = 999;
private static Cat cat ; //默认是 null
//步驟
//1.仍然构造器私有化
//2.定义一个 static 静态属性对象
//3.提供一个 public 的 static 方法,可以返回一個 Cat 對象
//4.懒汉式,只有当用戶使用 getInstance 時,才返回 cat 對象, 后面再次调用时,会返回上次创建的 cat 對象
// 从而保证了单例
private Cat(String name) {
System.out.println("構造器調用...");
this.name = name;
}
public static Cat getInstance() {
if(cat == null) {//如果还沒有创建 cat 對象
cat = new Cat("小可愛");
}
return cat;
}
@Override
public String toString() {
    return "Cat{" +
"name='" + name + '\'' +
'}';
}
}

(5)饿汉式与懒汉式的比较: ​

image-20230804103820327

4.5 final关键字 ​

(1)基本介绍: ​

image-20230804104005145

(2)final使用注意事项和细节讨论: ​

image-20230804104313159

image-20230804104415636

4.6 抽象类 ​

(1)概念: ​

image-20230804104850806

(2)抽象类会被继承,有其子类来实现抽象方法: ​

java
public class Abstract01 {
public static void main(String[] args) {
}
}
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//思考:这里 eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 考虑将该方法设计为抽象(abstract)方法
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 当一个类中存在抽象方法时,需要将该类声明为 abstract 类
//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法. 
// public void eat() {
// System.out.println("这是一个动物,但是不知道吃什么..");
// }
    public abstract void eat() ;
}

(3)抽象类的介绍: ​

image-20230804105028214

(4)抽象类使用的注意事项和细节讨论: ​

细节一: ​

image-20230804105153045

java
public class AbstractDetail01 {
public static void main(String[] args) {
//抽象类,不能被实例化
//new A();
}
}
//抽象类不一定要包含 abstract 方法。也就是说,抽象类可以没有 abstract 方法
//,还可以有实现的方法。
abstract class A {
public void hi() {
System.out.println("hi");
}
}
//一旦类包含了 abstract 方法,则这个类必须声明为 abstract
abstract class B {
public abstract void hi();
}
//abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
// public abstract int n1 = 1;
}
细节二: ​

image-20230804105834816

image-20230804105857982

java
public class AbstractDetail02 {
public static void main(String[] args) {
System.out.println("hello");
}
}
//抽象方法不能使用 private、final 和 static 来修饰,因为这些关键字都是和重写相违背的
abstract class H {
public abstract void hi();//抽象方法
}
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为 abstract 类
abstract class E {
public abstract void hi();
}
abstract class F extends E {
}
class G extends E {
@Override
public void hi() { //这里相等于 G 子类实现了父类 E 的抽象方法,所谓实现方法,就是有方法体
    }
}
//抽象类的本质还是类,所以可以有类的各种成员
abstract class D {
public int n1 = 10;
public static String name = "韩顺平教育";
public void hi() {
System.out.println("hi");
}
public abstract void hello();
public static void ok() {
System.out.println("ok");
}
}

(5)抽象类最佳实践-模版设计模式 ​

1.基本介绍:

image-20230804110505969

2.模版设计模式能解决的问题:

image-20230804110604201

4.7 接口: ​

(1)基本介绍: ​

image-20230804111021749

(2)案例: ​

image-20230804111133075

java
public class text {
public static void main(String[] args) {
    MysqlDB mysqlDB = new MysqlDB();
	t(mysqlDB);
	OracleDB oracleDB = new OracleDB();
	t(oracleDB);
}
}
public static void t(DBInterface db) {
	db.connect();
	db.close();
}

public interface DBInterface { //项目经理
public void connect();//连接方法
public void close();//关闭连接
}

//A 程序
public class MysqlDB implements DBInterface {
@Override
public void connect() {
System.out.println("连接 mysql");
    }
@Override
public void close() {
System.out.println("关闭 mysql");
}
}
//B 程序员连接 Oracle
public class OracleDB implements DBInterface{
@Override
public void connect() {
System.out.println("连接 oracle");
}
@Override
public void close() {
System.out.println("关闭 oracle");
}
}

(3)注意事项和细节: ​

image-20230804111539744

image-20230804111549534

java
public class InterfaceDetail02 {
public static void main(String[] args) {
//接口中的属性,是 public static final
System.out.println(IB.n1);//说明 n1 就是 static
//IB.n1 = 30; 说明 n1 是 final
}
}
interface IB {
//接口中的属性,只能是 final 的,而且是 public static final 修饰符
int n1 = 10; //等价 public static final int n1 = 10;
void hi();
}
interface IC {
void say();
}//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
}
//接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE{}
//一个类同时可以实现多个接口
class Pig implements IB,IC {
@Override
public void hi() {
}
@Override
public void say() {
}
}

(4)小结: ​

  1. 当子类继承了父类,就自动的拥有父类的功能
  2. 如果子类需要扩展功能,可以通过实现接口的方式扩展.
  3. 可以理解 实现接口 是 对 java 单继承机制的一种补充.

(5)接口和继承解决的问题不同: ​

image-20230804112150361

(6)接口的多态特性: ​

image-20230804112406298

1.多态参数:

java
public class Interface01 {
public static void main(String[] args) {
//Phone 实现了 UsbInterface
	Phone phone = new Phone();
    computer.work(phone);//把手机接入到计算机
}
}
//Phone 类 实现 UsbInterface
//解读 1. 即 Phone 类需要实现 UsbInterface 接口 规定/声明的方法
public interface UsbInterface { //接口
    public void start();
	public void stop();
}
public class Phone implements UsbInterface {
@Override
public void start() {
System.out.println("手机开始工作...");
}
@Override
public void stop() {
System.out.println("手机停止工作.....");
}
}
class computer {
    public static void work(interface UsbInterface) { //体现了多态参数
        UsbInterface.start(); //动态绑定,运行类型是Phone;
        UsbInterface.stop();
    }
}

2.多态数组:

java
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组 -> 接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
/*
需求:
给 Usb 数组中,存放 Phone 和 相机对象,Phone 类还有一个特有的方法 call(),
请遍历 Usb 数组,如果是 Phone 对象,除了调用 Usb 接口定义的方法外,
还需要调用 Phone 特有方法 call
*/
for(int i = 0; i < usbs.length; i++) {
    usbs[i].work();//动态绑定 调用对应的运行类型的work方法;
if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
((Phone_) usbs[i]).call(); //需要进行类型的向下转型
	}
	}
}
}
interface Usb{
void work();
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
    public void work() {
System.out.println("相机工作中...");
}
}

3.多态传递现象:

java
/**
* 演示多态传递现象
*/
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
	IG ig = new Teacher();
    IH ih = new Teacher();
//如果 IG 继承了 IH 接口,而 Teacher 类实现了 IG 接口
//那么,实际上就相当于 Teacher 类也实现了 IH 接口. 
//这就是所谓的 接口多态传递现象. IH ih = new Teacher();
}
}
interface IH {
void hi();
}
interface IG extends IH{ }
class Teacher implements IG {
    @Override
public void hi() {
}
}

4.8 内部类: ​

(1)概要: ​

  1. 如果定义类在局部位置(方法中/代码块)

    (1) 局部内部类 (2) 匿名内部类

  2. 定义在成员位置

    (1) 成员内部类(没用static修饰) (2) 静态内部类(使用static修饰)

(2)基本介绍: ​

image-20230804114938881

image-20230804115033849

(3)局部内部类的使用细节: ​

image-20230804115234332

java
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {//
public static void main(String[] args) {
//演示一遍
	Outer02 outer02 = new Outer02();
	outer02.m1();
	System.out.println("outer02 的 hashcode=" + outer02);
}
}
class Outer02 {//外部类
	private int n1 = 100;
	private void m2() {
	System.out.println("Outer02 m2()");
					}//私有方法
public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用 final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
	final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
	private int n1 = 800;
	public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// 老韩解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了 m1, Outer02.this 就是哪个对象
	System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
	System.out.println("Outer02.this hashcode=" + Outer02.this);
	m2();
	}
	}
//6. 外部类在方法中,可以创建 Inner02 对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}

(4)匿名内部类的使用: ​

image-20230804120015577

1.基于接口的匿名内部类:

​ jdk底层实现了接口IA

java
/**
* 演示匿名内部类的使用
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
	Outer04 outer04 = new Outer04();
	outer04.method();
    Outer04 outer05 = new Outer04();
   	outer05.method();
}
}
interface IA {
    public void cry();
}
class Outer04 { //外部类
	private int n1 = 10;//属性
	public void method() {//方法
//基于接口的匿名内部类
//解读
//1.需求: 想使用 IA 接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger 的编译类型 ? IA
//6. tiger 的运行类型 ? 就是匿名内部类 Outer04$1
/*
                        我们看jdk底层会自动实现分配: 即产生一个类名为Outer04$1的类去实现IA接口;
                        class Outer04$1 implements IA {  jdk底层实现了接口IA
                        @Override
                        public void cry() {
                        System.out.println("老虎叫唤...");
                        }
                        }
*/

IA tiger = new IA() {	//7.jdk 底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1 实例,并且把地址返回给 tiger
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};8. 匿名内部类使用一次,就不能再使用
System.out.println("tiger 的运行类型=" + tiger.getClass()); //运行类型就是匿名内部类Outer04$1;编译类型为IA;
System.out.println("tiger 的运行类型=" + tiger);//8. 匿名内部类使用一次,就不能再使用:
/*其含义是匿名内部类的多次底层创建的类名相同,都为Outer04$1,但是每次hashCode都发生了变化,在Java中,hashCode能一定程度上反应出地址信息。之前创建的Outer04$1已经在返回地址给对象之后消失,但对象可以多次使用。
*/
tiger.cry();
tiger.cry();	
tiger.cry();
 }
}

2.基于类的匿名内部类:

​ jdk底层继承了类Father

java
//演示基于类的匿名内部类
//分析
//1. father 编译类型 Father
//2. father 运行类型 Outer04$2
                //3. jdk底层会创建匿名内部类
                /*
                class Outer04$2 extends Father{   //jdk底层继承了类Father
                @Override
                public void test() {
                System.out.println("匿名内部类重写了 test 方法");
                }
                }
                */
//4. 同时也直接返回了 匿名内部类 Outer04$2 的对象
//5. 注意("jack") 参数列表会传递给 构造器
Father father = new Father("jack"){
            @Override
            public void test() {
            System.out.println("匿名内部类重写了 test 方法");
            }
};
System.out.println("father 对象的运行类型=" + father.getClass());//Outer04$2
father.test();
class Father {//类
    public Father(String name) {//构造器
	System.out.println("接收到 name=" + name);
}
    public void test() {//方法
    }
}

3.基于抽象类的匿名内部类:

java
//基于抽象类的匿名内部类
                //1.jdk底层会创建匿名内部类
                /*
                class Outer04$3 extends Animal{   //jdk底层继承了类Animal
                @Override
                public void eat() {
                System.out.println("匿名内部类重写了 eat 方法");
                }
                }
                */
//2. 同时也直接返回了 匿名内部类 Outer04$3 的对象
Animal animal = new Animal(){
	@Override
    void eat() {
	System.out.println("小狗吃骨头...");
	}
};
animal.eat();
}
}
abstract class Animal { //抽象类
	abstract void eat();
}

(5)匿名内部类的使用细节: ​

image-20230804123954894

java
public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
    Outer05 outer05 = new Outer05();
    outer05.f1();
//外部其他类---不能访问----->匿名内部类
    System.out.println("main outer05 hashcode=" + outer05);
}
}
class Outer05 {
    private int n1 = 99;
    public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
    Person p = new Person(){
    private int n1 = 88;
    @Override
    public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
    System.out.println("匿名内部类重写了 hi 方法 n1=" + n1 +
" 外部内的 n1=" + Outer05.this.n1 );
//Outer05.this 就是调用 f1 的 对象
    System.out.println("Outer05.this hashcode=" + Outer05.this);
    }
};
    p.hi();//动态绑定, 运行类型是 Outer05$1
//也可以直接调用, 匿名内部类本身也是返回对象
        //jdk底层实现:
        // class 匿名内部类 extends Person {}
        // new Person(){
        // @Override
        // public void hi() {
        // System.out.println("匿名内部类重写了 hi 方法,哈哈...");
        // }
        // @Override
        // public void ok(String str) {
        // super.ok(str);
        // }
        // }.ok("jack");  //给ok函数传递参数;
}
}
class Person {//类
    public void hi() {
    System.out.println("Person hi()");
    }
public void ok(String str) {
    System.out.println("Person ok() " + str);
}
}

(6)匿名内部类的最佳实践(当做实参直接传递): ​

java
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效
    f1( new IL() {//匿名内部类当做实参直接传递对象,再用定义一个方法(接口做参数)去接受匿名内部类对象;从而调用show方法;
    @Override
    public void show() {
    System.out.println("这是一副名画~~...");
    }
    } );
//传统方法 f1(new Picture());
	}
//静态方法,形参是接口类型
public static void f1(IL il) {
    il.show();
	}
}
//接口
interface IL {
	void show();
}
//类->实现 IL => 编程领域 (硬编码)
/*
class Picture implements IL {
    @Override
    public void show() {
    System.out.println("这是一副名画 XX...");
}
}
*/

五、枚举和注解 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

1.枚举 ​

(1)枚举的概念: ​

  1. 枚举对应英文(enumeration, 简写 enum)
  2. 枚举是一组常量的集合。
  3. 可以这里理解:枚举属于一种特殊的类,里面只包含一组有限的特定的对象。

(2)枚举的两种实现方法: ​

  1. 自定义类实现枚举
  2. 使用 enum 关键字实现枚举

(3)自定义类实现枚举-应用案例: ​

image-20230804151834298

java
public class Enumeration02 {
public static void main(String[] args) {
    System.out.println(Season.AUTUMN);
    System.out.println(Season.SPRING);
}
}
//演示字定义枚举实现
class Season {//类
    private String name;
    private String desc;//描述
//定义了四个对象, 固定. 
    public static final Season SPRING = new Season("春天", "温暖");
    public static final Season WINTER = new Season("冬天", "寒冷");
    public static final Season AUTUMN = new Season("秋天", "凉爽");
    public static final Season SUMMER = new Season("夏天", "炎热");
//1. 将构造器私有化,目的防止 直接 new
//2. 去掉 setXxx 方法, 防止属性被修改
//3. 在 Season 内部,直接创建固定的对象
//4. 优化,可以加入 final 修饰符
private Season(String name, String desc) {
    this.name = name;
    this.desc = desc;
}
public String getName() {
    return name;
}
public String getDesc() {
    return desc;
}
@Override
public String toString() {
    return "Season{" +
    "name='" + name + '\'' +
    ", desc='" + desc + '\'' +
    '}';
}
}

小结:

  1. 构造器私有化
  2. 本类内部创建一组对象[四个 春夏秋冬]
  3. 对外暴露对象(通过为对象添加 public final static 修饰符)
  4. 可以提供 get 方法,但是不要提供 set

(4)enum关键字实现枚举 ​

java
public class Enumeration03 {
public static void main(String[] args) {
    System.out.println(Season2.AUTUMN);
    System.out.println(Season2.SUMMER);
}
}
//演示使用 enum 关键字来实现枚举类
enum Season2 {//类
    //定义了四个对象, 固定. 
 // public static final Season SPRING = new Season("春天", "温暖");
// public static final Season WINTER = new Season("冬天", "寒冷");
// public static final Season AUTUMN = new Season("秋天", "凉爽");
// public static final Season SUMMER = new Season("夏天", "炎热");
//如果使用了 enum 来实现枚举类
//1. 使用关键字 enum 替代 class
//2. public static final Season SPRING = new Season("春天", "温暖") 直接使用
// SPRING("春天", "温暖") 解读 常量名(实参列表)
//3. 如果有多个常量(对象), 使用 ,号间隔即可
//4. 如果使用 enum 来实现枚举,要求将定义常量对象,写在前面
//5. 如果我们使用的是无参构造器,创建常量对象,则可以省略 ()
SPRING("春天", "温暖"), WINTER("冬天", "寒冷"), AUTUMN("秋天", "凉爽"), SUMMER("夏天", "炎热")/*, What()*/;
    private String name;
    private String desc;//描述
private Season2() {//无参构造器
	}
private Season2(String name, String desc) {
    this.name = name;
    this.desc = desc;
	}
    public String getName() {
    return name;
	}
public String getDesc() {
    return desc;
	}
@Override
public String toString() {
    return "Season{" +
    "name='" + name + '\'' +
    ", desc='" + desc + '\'' +
    '}';
	}
}

(5)enum关键字实现枚举注意事项: ​

  1. 当我们使用 enum 关键字开发一个枚举类时,默认会继承 Enum 类, 而且是一个 final 类,使用 javap 工具来演示(反编译);

    image-20230804152958987

  2. 传统的 public static final Season2 SPRING = new Season2("春天", "温暖"); 简化成 SPRING("春天", "温暖"), 这里必须知道,它调用的是哪个构造器;

  3. 如果使用无参构造器 创建 枚举对象,则实参列表和小括号都可以省略;

  4. 当有多个枚举对象时,使用 , 间隔,最后有一个 分号 结尾;

  5. 枚举对象必须放在枚举类的行首;

(6)Enum 类的常用方法: ​

  1. toString:Enum 类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息
  2. name:返回当前对象名(常量名),子类中不能重写
  3. ordinal:返回当前对象的位置号,默认从 0 开始
  4. values:返回当前枚举类中所有的常量
  5. valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!
  6. compareTo:比较两个枚举常量,比较的就是编号!

(7)enum实现接口: ​

  1. 使用 enum 关键字后,就不能再继承其它类了,因为 enum 会隐式继承 Enum,而 Java 是单继承机制。
  2. 枚举类和普通类一样,可以实现接口,如下形式。

​ enum 类名 implements 接口 1,接口 2 {

​ }

2.注解 ​

(1)注解的理解: ​

  1. 注解(Annotation)也被称为元数据(Metadata),用于修饰解释 包、类、方法、属性、构造器、局部变量等数据信息。
  2. 和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
  3. 在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在 JavaEE 中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替 java EE 旧版中所遗留的繁冗代码和 XML 配置等。

(2)基本的Annotation介绍: ​

使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用。用于修饰它支持的程序元素;

​ 三个基本的 Annotation:

@Override: 限定某个方法,是重写父类方法, 该注解只能用于方法;

@Deprecated: 用于表示某个程序元素(类, 方法等)已过时;

@SuppressWarnings: 抑制编译器警告 (常用**@SuppressWarnings({"all"}),来抑制所有类型的警告**;)

(3)@Override: ​

image-20230804154314553

image-20230804154331883

(4)@Deprecated: ​

image-20230804154548680

java
public class Deprecated_ {
public static void main(String[] args) {
A a = new A();
a.hi();
System.out.println(a.n1);
}
}
//老韩解读
//1. @Deprecated 修饰某个元素, 表示该元素已经过时
//2. 即不在推荐使用,但是仍然可以使用
//3. 查看 @Deprecated 注解类的源码
//4. 可以修饰方法,类,字段, 包, 参数 等等
//5. @Deprecated 可以做版本升级过渡使用
/*
        @Documented
        @Retention(RetentionPolicy.RUNTIME)
        @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, 					TYPE})
        public @interface Deprecated {
        }
*/
@Deprecated
class A {
@Deprecated
public int n1 = 10;
@Deprecated
public void hi(){
}
}

(5)@SuppressWarnings: ​

  1. 当我们不希望看到这些警告的时候,可以使用 SuppressWarnings 注解来抑制警告信息

  2. 在 {" "} 中,可以写入你希望抑制(不显示)警告信息

    3.常用**@SuppressWarnings({"all"}),来抑制所有类型的警告**;

image-20230804154930796

(6)JDK的元Annotation(元注解) : ​

​ JDK 的元 Annotation 用于修饰其他 Annotation元注解: 本身作用不大,看源码时,可以知道他是干什么.

  1. @Retention //指定注解的作用范围,三种 SOURCE,CLASS,RUNTIME
  2. @Target // 指定注解可以在哪些地方使用
  3. @Documented //指定该注解是否会在 javadoc 体现
  4. @Inherited //子类会继承父类注解

六、异常-Exception ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

异常-Exception

(1)引入案例: ​

​ 快捷键:ctrl + alt + t -> 选中 try-catch

java
public class Exception01 {
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;//Scanner();
//解读
//1. num1 / num2 => 10 / 0
//2. 当执行到 num1 / num2 因为 num2 = 0, 程序就会出现(抛出)异常 ArithmeticException(算术异常)
//3. 当抛出异常后,程序就退出,崩溃了 , 下面的代码就不在执行
//4. 大家想想这样的程序好吗? 不好,不应该出现了一个不算致命的问题,就导致整个系统崩溃
//5. java 设计者,提供了一个叫 异常处理机制来解决该问题
// int res = num1 / num2;
//如果程序员,认为一段代码可能出现异常/问题,可以使用 try-catch 异常处理机制来解决
//从而保证程序的健壮性
//将该代码块->选中->快捷键 ctrl + alt + t -> 选中 try-catch
//6. 如果进行异常处理,那么即使出现了异常,程序可以继续执行
try {
    int res = num1 / num2;
} catch (Exception e) {
//e.printStackTrace();
    System.out.println("出现异常的原因=" + e.getMessage());//输出异常信息
}
    System.out.println("程序继续运行....");
}
}

(2)异常介绍: ​

1.概念:

image-20230804155744430

2.异常体系图:

image-20230804155913846

3.Exception 小结:

image-20230804155937478

(3)常见的运行时异常: ​

1.NullPointerException 空指针异常:

​ 当应用程序试图在需要对象的地方使用 null 时,抛出该异常

image-20230804160347114

java
public class NullPointerException_ {
public static void main(String[] args) {
    String name = null;
    System.out.println(name.length());
}
}

2.ArithmeticException 数学运算异常:

当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。

image-20230804160518342

java
public class NumberFormatException_ {
public static void main(String[] args) {
	String name = "韩顺平教育";
//将 String 转成 int
    int num = Integer.parseInt(name);//抛出 NumberFormatException
	System.out.println(num);//1234
}
}

3.ArrayIndexOutOfBoundsException 数组下标越界异常:

用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。

image-20230804160630742

java
public class ArrayIndexOutOfBoundsException_ {
public static void main(String[] args) {
	int[] arr = {1,2,4};
for (int i = 0; i <= arr.length; i++) {
	System.out.println(arr[i]);
	}
}
}

4.ClassCastException 类型转换异常:

当试图将对象强制转换为不是实例的子类时,抛出该异常。

image-20230804160740013

java
public class ClassCastException_ {
public static void main(String[] args) {
A b = new B(); //向上转型
B b2 = (B)b;//向下转型,这里是 OK
C c2 = (C)b;//这里抛出 ClassCastException
}
}
class A {}
class B extends A {}
class C extends A {}

5.NumberFormatException 数字格式不正确异常[]:

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

java
public class NumberFormatException_ {
public static void main(String[] args) {
	String name = "韩顺平教育";
//将 String 转成 int
	int num = Integer.parseInt(name);//抛出 NumberFormatException
	System.out.println(num);//1234
}
}

(4)编译异常: ​

1.介绍:

image-20230804160957314

2.常见的编译异常:

image-20230804161018884

3.案例:

java
import java.io.FileInputStream;
import java.io.IOException;
public class Exception02 {
public static void main(String[] args) {
try {
    FileInputStream fis;
	fis = new FileInputStream("d:\\aa.jpg");
	int len;
while ((len = fis.read()) != -1) {
	System.out.println(len);
}
	fis.close();
} catch (IOException e) {
	e.printStackTrace();
}
}
}

(5)异常处理: ​

1.基本介绍:

image-20230804161426632

2.两种异常处理方式:

image-20230804161450024

3.两种方式示意图:

image-20230804161603362

image-20230804161622107

(6)try-catch-finally异常处理: ​

1.基本语法:

image-20230804161841304

2.try-catch 方式处理异常-注意事项:

image-20230804161926777

image-20230804161942896

image-20230804161957824

3.try-catch-finally 执行顺序小结:

image-20230804162050830

(7)throws异常处理: ​

1.基本介绍:

image-20230804162402649

2.注意事项和异常处理:

image-20230804162624092

(8)自定义异常: ​

1.基本概念:

image-20230804162814143

2.步骤:

image-20230804162836827

3.案例:

java
public class CustomException {
public static void main(String[] args) /*throws AgeException*/ {
	int age = 180;
//要求范围在 18 – 120 之间,否则抛出一个自定义异常
	if(!(age >= 18 && age <= 120)) {
//这里我们可以通过构造器,设置信息
	throw new AgeException("年龄需要在 18~120 之间");
	}
	System.out.println("你的年龄范围正确.");
}
}
//自定义一个异常
//解读
//1. 一般情况下,我们自定义异常是继承 RuntimeException
//2. 即把自定义异常做成 运行时异常,好处时,我们可以使用默认的处理机制
//3. 即比较方便
class AgeException extends RuntimeException {
    public AgeException(String message) {//构造器
    super(message);
	}
}

(9)throw和throws的区别: ​

image-20230804163102759

七、常用类 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

1、包装类 ​

(1)包装类的分类: ​

image-20230804164200914

(2)类的体系图: ​

image-20230804164248041

image-20230804164302910

image-20230804164314381

(3)包装类和基本数据的转换: ​

image-20230804164433338

java
public class Integer01 {
public static void main(String[] args) {
//演示 int <--> Integer 的装箱和拆箱
//jdk5 前是手动装箱和拆箱
//手动装箱 int->Integer
    int n1 = 100;
    Integer integer = new Integer(n1);
    Integer integer1 = Integer.valueOf(n1);
//手动拆箱
//Integer -> int
    int i = integer.intValue();
//jdk5 后,就可以自动装箱和自动拆箱
    int n2 = 200;
//自动装箱 int->Integer
    Integer integer2 = n2; //底层使用的是 Integer.valueOf(n2)
//自动拆箱 Integer->int
    int n3 = integer2; //底层仍然使用的是 intValue()方法
}
}

(4)包装类型和 String 类型的相互转换: ​

案例演示, 以 Integer 和 String 转换为例,其它类似;

java
public class WrapperVSString {
public static void main(String[] args) {
//包装类(Integer)->String
    Integer i = 100;//自动装箱
//方式 1
    String str1 = i + "";
//方式 2
    String str2 = i.toString();
//方式 3
    String str3 = String.valueOf(i);
//String -> 包装类(Integer)
    String str4 = "12345";
    Integer i2 = Integer.parseInt(str4);//使用到自动装箱
    Integer i3 = new Integer(str4);//构造器
    System.out.println("ok~~");
}
}

(5)Integer 类和 Character 类的常用方法: ​

java
public class WrapperMethod {
public static void main(String[] args) {
    System.out.println(Integer.MIN_VALUE); //返回最小值
    System.out.println(Integer.MAX_VALUE);//返回最大值
    System.out.println(Character.isDigit('a'));//判断是不是数字
    System.out.println(Character.isLetter('a'));//判断是不是字母
    System.out.println(Character.isUpperCase('a'));//判断是不是大写
    System.out.println(Character.isLowerCase('a'));//判断是不是小写
    System.out.println(Character.isWhitespace('a'));//判断是不是空格
    System.out.println(Character.toUpperCase('a'));//转成大写
    System.out.println(Character.toLowerCase('A'));//转成小写
}
}

2、String类 ​

(1)String类图: ​

image-20230804170459353

  1. String 类实现了接口 Serializable(String 可以串行化:可以在网络传输)
  2. 接口 Comparable (String 对象可以比较大小)

(2)String 类的理解: ​

image-20230804170411952

java
public class String01 {
public static void main(String[] args) {
//1.String 对象用于保存字符串,也就是一组字符序列
//2. "jack" 字符串常量, 双引号括起的字符序列
//3. 字符串的字符使用 Unicode 字符编码,一个字符(不区分字母还是汉字)占两个字节
//4. String 类有很多构造器,构造器的重载
// 常用的有 String s1 = new String();
//String s2 = new String(String original);
//String s3 = new String(char[] a);
//String s4 = new String(char[] a,int startIndex,int count)
//String s5 = new String(byte[] b)
//5. String 类实现了接口 Serializable【String 可以串行化:可以在网络传输】
// 接口 Comparable [String 对象可以比较大小]
//6. String 是 final 类,不能被其他的类继承
//7. String 有属性 private final char value[]; 用于存放字符串内容
//8. 一定要注意:value 是一个 final 类型, 不可以修改(需要功力):即 value 不能指向
// 新的地址,但是单个字符内容是可以变化
    String name = "jack";
    name = "tom";
    final char[] value = {'a','b','c'};
    char[] v2 = {'t','o','m'};
    value[0] = 'H';
    //value = v2; 不可以修改 value 地址
}
}

(3)创建String对象的两种方式: ​

image-20230804170844363

(4)两种创建 String 对象的区别: ​

image-20230804170928988

image-20230804170950029

(5)String类的常用方法: ​

第一组:

image-20230804171448490

java
public class StringMethod01 {
public static void main(String[] args) {
//1. equals 前面已经讲过了. 比较内容是否相同,区分大小写
    String str1 = "hello";
    String str2 = "Hello";
    System.out.println(str1.equals(str2));//
// 2.equalsIgnoreCase 忽略大小写的判断内容是否相等
    String username = "johN";
if ("john".equalsIgnoreCase(username)) {
    System.out.println("Success!");
} else {
    System.out.println("Failure!");
}
// 3.length 获取字符的个数,字符串的长度
    System.out.println("韩顺平".length());
// 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从 0 开始,如果找不到,返回-1
    String s1 = "wer@terwe@g";
    int index = s1.indexOf('@');
    System.out.println(index);// 3
    System.out.println("weIndex=" + s1.indexOf("we"));//0
// 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从 0 开始,如果找不到,返回-1
    s1 = "wer@terwe@g@";
    index = s1.lastIndexOf('@');
    System.out.println(index);//11
    System.out.println("ter 的位置=" + s1.lastIndexOf("ter"));//4
// 6.substring 截取指定范围的子串
    String name = "hello,张三";
//下面 name.substring(6) 从索引 6 开始截取后面所有的内容
System.out.println(name.substring(6));//截取后面的字符
    //name.substring(0,5)表示从索引 0 开始截取,截取到索引 5-1=4 位置
System.out.println(name.substring(2,5));//llo
}
}

第二组:

image-20230804171801374

java
public class StringMethod02 {
public static void main(String[] args) {
// 1.toUpperCase 转换成大写
    String s = "heLLo";
    System.out.println(s.toUpperCase());//HELLO
// 2.toLowerCase
    System.out.println(s.toLowerCase());//hello
// 3.concat 拼接字符串
    String s1 = "宝玉";
    s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
    System.out.println(s1);//宝玉林黛玉薛宝钗 together
// 4.replace 替换字符串中的字符
    s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
//在 s1 中,将 所有的 林黛玉 替换成薛宝钗
// 老韩解读: s1.replace() 方法执行后,返回的结果才是替换过的. // 注意对 s1 没有任何影响
    String s11 = s1.replace("宝玉", "jack");
    System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉
    System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉
// 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等
    String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
//老韩解读:
// 1. 以 , 为标准对 poem 进行分割 , 返回一个数组
// 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \
    String[] split = poem.split(",");
    poem = "E:\\aaa\\bbb";
    split = poem.split("\\\\"); //一个转义符\ 管一个 "\" 符号;
    System.out.println("==分割后内容===");
for (int i = 0; i < split.length; i++) {
    System.out.println(split[i]);
}
// 6.toCharArray 转换成字符数组
    s = "happy";
    char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
    System.out.println(chs[i]);
}
// 7.compareTo 比较两个字符串的大小,如果前者大,
// 则返回正数,后者大,则返回负数,如果相等,返回 0
// 老韩解读
// (1) 如果长度相同,并且每个字符也相同,就返回 0
// (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小
// 就返回 if (c1 != c2) {
// return c1 - c2;
// }
// (3) 如果前面的部分都相同,就返回 str1.len - str2.len
    String a = "jcck";// len = 4
    String b = "jack";// len = 4
    System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2 的值
// 8.format 格式字符串
/* 占位符有:
* %s 字符串 %c 字符 %d 整型 %.2f 浮点型
*
*/
    String name = "john";
    int age = 10;
    double score = 56.857;
    char gender = '男';
    //将所有的信息都拼接在一个字符串. String info =
"我的姓名是" + name + "年龄是" + age + ",成绩是" + score + "性别是" + gender + "。希望大家喜欢我!
";
System.out.println(info);
//老韩解读
//1. %s , %d , %.2f %c 称为占位符
//2. 这些占位符由后面变量来替换
//3. %s 表示后面由 字符串来替换
//4. %d 是整数来替换
//5. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理
//6. %c 使用 char 类型来替换
String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
String info2 = String.format(formatStr, name, age, score, gender);
System.out.println("info2=" + info2);
}
}

3、 StringBuffer类 ​

(1)基本介绍: ​

image-20230804172648746

java
public class StringBuffer01 {
public static void main(String[] args) {
//老韩解读
//1. StringBuffer 的直接父类 是 AbstractStringBuilder
//2. StringBuffer 实现了 Serializable, 即 StringBuffer 的对象可以串行化 (在网络上传输)
//3. 在父类中 AbstractStringBuilder 有属性 char[] value,不是 final
// 该 value 数组存放 字符串内容,引出存放在堆中的
//4. StringBuffer 是一个 final 类,不能被继承
//5. 因为 StringBuffer 字符内容是存在 char[] value, 所以在变化(增加/删除)
// 不用每次都更换地址(即不是每次创建新对象), 所以效率高于 String
StringBuffer stringBuffer = new StringBuffer("hello");
}
}

(2)String VS StringBuffer: ​

image-20230804172837258

(3)String 和 StringBuffer 相互转换: ​

java
public class StringAndStringBuffer {
public static void main(String[] args) {
//看 String——>StringBuffer
    String str = "hello tom";
//方式 1 使用构造器
//注意: 返回的才是 StringBuffer 对象,对 str 本身没有影响
StringBuffer stringBuffer = new StringBuffer(str);
//方式 2 使用的是 append 方法
StringBuffer stringBuffer1 = new StringBuffer();
stringBuffer1 = stringBuffer1.append(str);
//看看 StringBuffer ->String
StringBuffer stringBuffer3 = new StringBuffer("韩顺平教育");
//方式 1 使用 StringBuffer 提供的 toString 方法
	String s = stringBuffer3.toString();
//方式 2: 使用构造器来搞定
	String s1 = new String(stringBuffer3);
}
}

(4)StringBuffer 类常见方法: ​

java
public class StringBufferMethod {
public static void main(String[] args) {
StringBuffer s = new StringBuffer("hello");
//增
s.append(',');// "hello,"
s.append("张三丰");//"hello,张三丰"
s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏 100true10.5" System.out.println(s);//"hello,张三丰赵敏 100true10.5"
//删
/*
* 删除索引为>=start && <end 处的字符
* 解读: 删除 11~14 的字符 [11, 14) 左闭右开
*/
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏 true10.5"
//改
//解读,使用 周芷若 替换 索引 9-11 的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若 true10.5"
//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//插
//解读,在索引为 9 的位置插入 "赵敏",原来索引为 9 的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若 true10.5"
//长度
System.out.println(s.length());//22
System.out.println(s);
}
}

4、 StringBuilder 类 ​

(1)基本介绍: ​

image-20230804173558100

(2)StringBuilder 常用方法: ​

image-20230804173640681

java
public class StringBuilder01 {
public static void main(String[] args) {
//解读
//1. StringBuilder 继承 AbstractStringBuilder 类
//2. 实现了 Serializable ,说明 StringBuilder 对象是可以串行化(对象可以网络传输,可以保存到文件)
//3. StringBuilder 是 final 类, 不能被继承
//4. StringBuilder 对象字符序列仍然是存放在其父类 AbstractStringBuilder 的 char[] value;
// 因此,字符序列是堆中
//5. StringBuilder 的方法,没有做互斥的处理,即没有 synchronized 关键字,因此在单线程的情况下使用
// StringBuilder
StringBuilder stringBuilder = new StringBuilder();
}
}

5、String、StringBuffer 和 StringBuilder 的比较与选择 ​

(1)String、StringBuffer 和 StringBuilder 的比较: ​

image-20230804174007675

(2)String、StringBuffer 和 StringBuilder 的选择: ​

image-20230804174037934

6、Math类 ​

(1)基本介绍: ​

​ Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

(2)Math类常见方法: ​

java
public class MathMethod {
public static void main(String[] args) {
//看看 Math 常用的方法(静态方法)
//1.abs 绝对值
    int abs = Math.abs(-9);
    System.out.println(abs);//9
//2.pow 求幂
	double pow = Math.pow(2, 4);//2 的 4 次方
	System.out.println(pow);//16
//3.ceil 向上取整,返回>=该参数的最小整数(转成 double);
	double ceil = Math.ceil(3.9);
	System.out.println(ceil);//4.0
//4.floor 向下取整,返回<=该参数的最大整数(转成 double)
	double floor = Math.floor(4.001);
	System.out.println(floor);//4.0
//5.round 四舍五入 Math.floor(该参数+0.5)
	long round = Math.round(5.51);
	System.out.println(round);//6
//6.sqrt 求开方
	double sqrt = Math.sqrt(9.0);
	System.out.println(sqrt);//3.0
//7.random 求随机数
// random 返回的是 0 <= x < 1 之间的一个随机小数
// 思考:请写出获取 a-b 之间的一个随机整数,a,b 均为整数 ,比如 a = 2, b=7
// 即返回一个数 x 2 <= x <= 7
// 老韩解读 Math.random() * (b-a) 返回的就是 0 <= 数 <= b-a
// (1) (int)(a) <= x <= (int)(a + Math.random() * (b-a +1) )
// (2) 使用具体的数给小伙伴介绍 a = 2 b = 7
// (int)(a + Math.random() * (b-a +1) ) = (int)( 2 + Math.random()*6)
// Math.random()*6 返回的是 0 <= x < 6 小数
// 2 + Math.random()*6 返回的就是 2<= x < 8 小数
// (int)(2 + Math.random()*6) = 2 <= x <= 7
// (3) 公式就是 (int)(a + Math.random() * (b-a +1) )
for(int i = 0; i < 100; i++) {
	System.out.println((int)(2 + Math.random() * (7 - 2 + 1)));
}
//max , min 返回最大值和最小值
    int min = Math.min(1, 9);
    int max = Math.max(45, 90);
    System.out.println("min=" + min);
    System.out.println("max=" + max);
}
}

7、 Arrays类 ​

(1)Arrays类常用方法: ​

image-20230804174757285

image-20230804174810052

(2)案例: ​

java
import java.util.Arrays;
import java.util.Comparator;
public class ArraysMethod01 {
public static void main(String[] args) {
    Integer[] integers = {1, 20, 90};
//遍历数组
// for(int i = 0; i < integers.length; i++) {
// System.out.println(integers[i]);
// }
//直接使用 Arrays.toString 方法,显示数组
// System.out.println(Arrays.toString(integers));//
//演示 sort 方法的使用
    Integer arr[] = {1, -1, 7, 0, 89};
//进行排序
//老韩解读
//1. 可以直接使用冒泡排序 , 也可以直接使用 Arrays 提供的 sort 方法排序
//2. 因为数组是引用类型,所以通过 sort 排序后,会直接影响到 实参 arr
//3. sort 重载的,也可以通过传入一个接口 Comparator 实现定制排序
//4. 调用 定制排序 时,传入两个参数 (1) 排序的数组 arr
// (2) 实现了 Comparator 接口的匿名内部类 , 要求实现 compare 方法
//5. 先演示效果,再解释
//6. 这里体现了接口编程的方式 , 看看源码,就明白
    // 源码分析
//(1) Arrays.sort(arr, new Comparator()
//(2) 最终到 TimSort 类的 private static <T> void binarySort(T[] a, int lo, int hi, int start, // Comparator<? super T> c)()
//(3) 执行到 binarySort 方法的代码, 会根据动态绑定机制 c.compare()执行我们传入的
// 匿名内部类的 compare ()
// while (left < right) {
// int mid = (left + right) >>> 1;
// if (c.compare(pivot, a[mid]) < 0)
// right = mid;
// else
// left = mid + 1;
// }
//(4) new Comparator() {
// @Override
// public int compare(Object o1, Object o2) {
// Integer i1 = (Integer) o1;
// Integer i2 = (Integer) o2;
// return i2 - i1;
// }
// }
//(5) public int compare(Object o1, Object o2) 返回的值>0 还是 <0
// 会影响整个排序结果, 这就充分体现了 接口编程+动态绑定+匿名内部类的综合使用
// 将来的底层框架和源码的使用方式,会非常常见
//Arrays.sort(arr); // 默认排序方法
//定制排序
    Arrays.sort(arr, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
    Integer i1 = (Integer) o1;
    Integer i2 = (Integer) o2;
    return i2 - i1;
}
});
    System.out.println("===排序后===");
    System.out.println(Arrays.toString(arr));//
}
}

8、 System类 ​

(1)常见方法: ​

image-20230804175333041

(2)案例: ​

java
public class System_ {
public static void main(String[] args) {
//exit 退出当前程序
// System.out.println("ok1");
// 解读
// //1. exit(0) 表示程序退出
// //2. 0 表示一个状态 , 正常的状态
// System.exit(0);//
// System.out.println("ok2");
//arraycopy :复制数组元素,比较适合底层调用,
// 一般使用 Arrays.copyOf 完成复制数组
    int[] src={1,2,3};
    int[] dest = new int[3];// dest 当前是 {0,0,0}
//解读
//1. 主要是搞清楚这五个参数的含义
//2. // 源数组
// * @param src the source array. // srcPos: 从源数组的哪个索引位置开始拷贝
// * @param srcPos starting position in the source array. // dest : 目标数组,即把源数组的数据拷贝到哪个数组
// * @param dest the destination array. // destPos: 把源数组的数据拷贝到 目标数组的哪个索引
// * @param destPos starting position in the destination data. // length: 从源数组拷贝多少个数据到目标数组
// * @param length the number of array elements to be copied. System.arraycopy(src, 0, dest, 0, src.length);
// int[] src={1,2,3};
	System.out.println("dest=" + Arrays.toString(dest));//[1, 2, 3]
//currentTimeMillens:返回当前时间距离 1970-1-1 的毫秒数
// 解读:
	System.out.println(System.currentTimeMillis());
}
}

9 、BigInteger 和 BigDecimal 类 ​

(1)介绍: ​

image-20230804175757559

(2)常用方法: ​

image-20230804175829051

(3)案例: ​

java
import java.math.BigInteger;
public class BigInteger_ {
public static void main(String[] args) {
//当我们编程中,需要处理很大的整数,long 不够用
//可以使用 BigInteger 的类来搞定
// long l = 23788888899999999999999999999l;
// System.out.println("l=" + l);
BigInteger bigInteger = new BigInteger("23788888899999999999999999999");
BigInteger bigInteger2 =new BigInteger("10099999999999999999999999999999999999999999999999999999999999999999999999999999999");
System.out.println(bigInteger);
//解读
//1. 在对 BigInteger 进行加减乘除的时候,需要使用对应的方法,不能直接进行 + - * /
//2. 可以创建一个 要操作的 BigInteger 然后进行相应操作
BigInteger add = bigInteger.add(bigInteger2);
System.out.println(add);//加
BigInteger subtract = bigInteger.subtract(bigInteger2);
System.out.println(subtract);//减
BigInteger multiply = bigInteger.multiply(bigInteger2);
System.out.println(multiply);//乘
BigInteger divide = bigInteger.divide(bigInteger2);
System.out.println(divide);//除
}
}
java
import java.math.BigDecimal;
public class BigDecimal_ {
public static void main(String[] args) {
//当我们需要保存一个精度很高的数时,double 不够用
//可以是 BigDecimal
// double d = 1999.11111111111999999999999977788d;
// System.out.println(d);
BigDecimal bigDecimal = new BigDecimal("1999.11");
BigDecimal bigDecimal2 = new BigDecimal("3");
System.out.println(bigDecimal);
//解读
//1. 如果对 BigDecimal 进行运算,比如加减乘除,需要使用对应的方法
//2. 创建一个需要操作的 BigDecimal 然后调用相应的方法即可
System.out.println(bigDecimal.add(bigDecimal2));
System.out.println(bigDecimal.subtract(bigDecimal2));
System.out.println(bigDecimal.multiply(bigDecimal2));
//System.out.println(bigDecimal.divide(bigDecimal2));//可能抛出异常 ArithmeticException
//在调用 divide 方法时,指定精度即可. BigDecimal.ROUND_CEILING
//如果有无限循环小数,就会保留 分子 的精度
System.out.println(bigDecimal.divide(bigDecimal2, BigDecimal.ROUND_CEILING));
}
}

10 、日期类 ​

(1)第一代日期类: ​

image-20230804180317158

(2)第二代日期类: ​

image-20230804180340543

(3)第三代日期类(常用): ​

image-20230804180418917

(4)DateTimeFormatter 格式日期类: ​

image-20230804180516562

(5)Instant 时间戳 ​

image-20230804180631922

(6)案例: ​

java
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
public class LocalDate_ {
public static void main(String[] args) {
//第三代日期
//解读
//1. 使用 now() 返回表示当前日期时间的 对象
LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now()
System.out.println(ldt);
//2. 使用 DateTimeFormatter 对象来进行格式化
// 创建 DateTimeFormatter 对象
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String format = dateTimeFormatter.format(ldt);
System.out.println("格式化的日期=" + format);
System.out.println("年=" + ldt.getYear());
System.out.println("月=" + ldt.getMonth());
System.out.println("月=" + ldt.getMonthValue());
System.out.println("日=" + ldt.getDayOfMonth());
System.out.println("时=" + ldt.getHour());
System.out.println("分=" + ldt.getMinute());
System.out.println("秒=" + ldt.getSecond());
LocalDate now = LocalDate.now(); //可以获取年月日
LocalTime now2 = LocalTime.now();//获取到时分秒
//提供 plus 和 minus 方法可以对当前时间进行加或者减
//看看 890 天后,是什么时候 把 年月日-时分秒
LocalDateTime localDateTime = ldt.plusDays(890);
System.out.println("890 天后=" + dateTimeFormatter.format(localDateTime));
//看看在 3456 分钟前是什么时候,把 年月日-时分秒输出
LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
System.out.println("3456 分钟前 日期=" + dateTimeFormatter.format(localDateTime2));
}
}

八、集合 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

集合

九、泛型 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

泛型

9.1 泛型 ​

(1)泛型的概念: ​

​ 泛型的本质是类型参数化或参数化类型,在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。

(2)泛型的意义: ​

image-20230810210921696

一般的类和方法,只能使用具体的类型:要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。

Java 在引入泛型之前,为了表示可变对象,通常使用 Object 来实现,但是在进行类型强制转换时存在安全风险。有了泛型后:

  • 编译期间确定类型,保证类型安全,放的是什么,取的也是什么,不用担心抛出 ClassCastException 异常。
  • 提升可读性,从编码阶段就显式地知道泛型集合、泛型方法等处理的对象类型是什么。
  • 泛型合并了同类型的处理代码提高代码的重用率,增加程序的通用灵活性。

(3)泛型的语法: ​

​ 1.泛型的声明:

image-20230810211055973

​ 2.泛型的实例化:

image-20230810211118094

(4)泛型使用的注意事项和细节: ​

image-20230810211319220

(5)自定义泛型类: ​

1.基本语法:

image-20230810211525529

2.应用:

image-20230810211634461

(6)自定义泛型接口: ​

image-20230810211830113

(7)自定义泛型方法: ​

1.基本语法:

image-20230810212015462

2.案例:

java
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class CustomMethodGeneric {
public static void main(String[] args) {
    Car car = new Car();
    car.fly("宝马", 100);//当调用方法时,传入参数,编译器,就会确定类型
    System.out.println("=======");
    car.fly(300, 100.1);//当调用方法时,传入参数,编译器,就会确定类型
    //测试
    //T->String, R-> ArrayList
    Fish<String, ArrayList> fish = new Fish<>();
    fish.hello(new ArrayList(), 11.3f);
    }
}
//泛型方法,可以定义在普通类中, 也可以定义在泛型类中
class Car {//普通类
    public void run() {//普通方法
    }
//说明 泛型方法
//1. <T,R> 就是泛型
//2. 是提供给 fly 使用的
    public <T, R> void fly(T t, R r) {//泛型方法
        System.out.println(t.getClass());//String
        System.out.println(r.getClass());//Integer
        }
}

class Fish<T, R> {//泛型类
    public void run() {//普通方法
    }
    public<U,M> void eat(U u, M m) {//泛型方法
    }
//说明
//1. 下面 hi 方法不是泛型方法
//2. 是 hi 方法使用了类声明的 泛型
    public void hi(T t) {
    }
//泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
    public<K> void hello(R r, K k) {
    System.out.println(r.getClass());//ArrayList
    System.out.println(k.getClass());//Float
    }
}

(8)泛型的继承和通配符: ​

image-20230810212653501

一般泛型有约定的符号:E 代表 Element,<E> 通常在集合中使用;T 代表 Type,<T>通常用于表示类;K 代表 Key,V 代表 Value,<K, V> 通常用于键值对的表示;? 代表泛型通配符。

泛型的表达式有如下几种:

  • 普通符号 <T>
  • 无边界通配符 <?>
  • 上界通配符 <? extends E> 父类是 E
  • 下界通配符 <? super E> 是 E 的父类

十、多线程 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

多线程

十一、Stream流 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

Stream流

十二、方法引用 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

方法引用

十三、文件File ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

文件File

十四、IO流 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

IO流

十五、网络编程 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

网络编程

十六、反射和动态代理 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

反射和动态代理-Local

十七、log日志 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

Log日志-Locall

十八、类加载器 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

类加载器-Local

十九、XML ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

XML-Local

二十、单元测试 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

单元测试-Local

二十一、注解 ​

更新: 2025/4/11 字数: 0 字 时长: 0 分钟

注解-Local

最后更新于:

Pager
上一篇🌍「诗的浪漫极客」
下一篇 Java集合

Released under the MIT License.

Copyright © 2025 渝ICP备2025054279号