Java基础部分3

数组

数组概述

①什么是数组?

1.在Java中,数组是一种用于存储多个相同数据类型元素的容器。

2.例如一个存储整数的数组:int[] nums = {100, 200, 300};

3.例如一个存储字符串的数组:String[] names = {“jack”,“lucy”,“lisi”};

4.数组是一种引用数据类型,隐式继承Object。因此数组也可以调用Object类中的方法。

5.数组对象存储在堆内存中。

②数组的分类?

1.根据维数进行分类:一维数组,二维数组,三维数组,多维数组。

2.根据数组中存储的元素类型分类:基本类型数组,引用类型数组。

3.根据数组初始化方式不同分类:静态数组,动态数组。

③Java数组存储元素的特点?

1.数组长度一旦确定不可变。

2.数组中元素数据类型一致,每个元素占用空间大小相同。

3.数组中每个元素在空间存储上,内存地址是连续的。

4.每个元素有索引,首元素索引0,以1递增。

5.以首元素的内存地址作为数组对象在堆内存中的地址。

6.所有数组对象都有length属性用来获取数组元素个数。末尾元素下标:length-1

①数组优点?

1.根据下标查询某个元素的效率极高。数组中有100个元素和有100万个元素,查询效率相同。时间复杂度O(1)。也就是说在数组中根据下标查询某个元素时,不管数组的长短,耗费时间是固定不变的。

原因:知道首元素内存地址,元素在空间存储上内存地址又是连续的,每个元素占用空间大小相同,只要知道下标,就可以通过数学表达式计算出来要查找元素的内存地址。直接通过内存地址定位元素。

②数组缺点?

1.随机增删元素的效率较低。因为随机增删元素时,为了保证数组中元素的内存地址连续,就需要涉及到后续元素的位移问题。时间复杂度O(n)。O(n)表示的是线性阶,随着问题规模n的不断增大,时间复杂度不断增大,算法的执行效率越低。(不过需要注意的是:对数组末尾元素的增删效率是不受影响的。)

2.无法存储大量数据,因为很难在内存上找到非常大的一块连续的内存。

一维数组

①一维数组是线性结构。二维数组,三维数组,多维数组是非线性结构。

②如何静态初始化一维数组?

1.第一种:int[] arr = {55,67,22}; 或者 int arr[] = {55,67,22};

2.第二种:int[] arr = new int[]{55,67,22};

1
2
Animal[] animals = {a1,a2,new Animal()};
//静态初始化

③如何访问数组中的元素?

1.通过下标来访问。

2.注意ArrayIndexOutOfBoundsException异常的发生。

④如何遍历数组?

1.普通for循环遍历

2.for-each遍历(优点是代码简洁。缺点是没有下标。)

1
2
3
4
5
6
7
8
for(int i = 0 ; i < citys.length;i++){
System.out.println(citys[i]);
}
//for each
for(String name:names){
System.out.println(name);
}

⑤如何动态初始化一维数组?

在创建数组的时候,不确定具体存储那些数据,但是确定长度(就是开辟空间)

数据类型[] 变量名 = new 数据类型[长度]

1.int[] arr = new int[4];

2.Object[] objs = new Object[5];

3.数组动态初始化的时候,确定长度,并且数组中每个元素采用默认值。

⑥方法在调用时如何给方法传一个数组对象?

1
2
3
4
5
6
7
8
9
10
11
   public static void display(int[] arr){
for(int i : arr){
System.out.println(i);
}
}

int[] a = {1,2,3};
display(a);
display({1,2,3})//error
display(new int[]{1,2,3});//静态初始化
display(new int[10]);//动态初始化

⑦当一维数组中存储引用时的内存图?

1
2
3
4
5
6
Animals[] animals = new Animals[5];

Object[] test = {new Bird(),new Cat()}
public class Animal {
}

image-20240227110419147

数组中存储的每个元素的空间大小都是一样的,所以空间是不可能一样的,所以是不可能存储引用对象的,所以是存储的是引用(对象在堆内存中的地址)

image-20240227111451718

image-20240227111729307

⑧如何获取数组中的最大值?

​ 假设首元素是最大的,然后遍历数组中所有元素,只要有更大的,就将其作为最大值。

​ 思考:找出最大值的下标怎么做?

⑨如果知道值,如何通过值找它的下标?

⑩如何将数组中的所有元素反转?

第一种方式:创建一个新的数组。

就是倒着遍历第一个,然后赋值过去

第二种方式:首尾交换。

就是第一个和倒数第一个交换,以此类推

⑾关于main方法的形参args?

​ 接收命令行参数,

​ JVM 负责调用这个类名.main()方法的

image-20240227112624635

JVM会把以上字符串以“空格”进行拆分,生成一个新的数组对象

最后这个数组对象是String[] args = {“abc”,”def”,”xyz”}

​ 在DOS命令窗口中怎么传?在IDEA中怎么传?

image-20240227112947509

⑿关于方法的可变长度参数?

​ 可变长参数只能出现在形参列表中的最后一个位置。

​ 可变长参数可以当做数组来处理。

1
2
3
4
5
6
public static void mi(int... nums)
{
for(int i = m0; i < nums;i++){
System.out.println(nums[i])
}
}

⒀一维数组的扩容

①数组长度一旦确定不可变。

②那数组应该如何扩容?

==只能创建一个更大的数组将原数组中的数据全部拷贝到新数组中==

==可以使用System.arraycopy()方法完成数组的拷贝。==

1
System.arraycopy(src,srcpos:0,dest,destpos:0,src.length)//注意越界问题

③数组扩容会影响程序的执行效率,因此尽可能预测数据量,创建一个接近数量的数组,减少扩容次数。

二维数组

①二维数组是一个特殊的一维数组,特殊在:这个一维数组中每个元素是一个一维数组(相当于存的还是一个地址,是一维数组的首位地址)。

②二维数组的静态初始化

1
2
3
int[][] arr = new int[][]{{},{},{}};

int[][] arr = {{},{},{}};

③二维数组的动态初始化(等长)

1
int[][] arr = new int[2][3];

④二维数组的动态初始化(不等长)

1
int[][] arr = new int[3][];

⑤二维数组中元素的访问

1
2
3
第一个元素:arr[0][0]

最后一个元素:arr[arr.length-1][arr[arr.length-1].length-1]

⑥二维数组中元素的遍历

IDEA的Debug

image-20240227122020670

image-20240227122031704

image-20240227122037930

image-20240227122045388

image-20240227122051856

image-20240227122056862

单元测试

image-20240227123624933

①什么是单元测试,为什么要进行单元测试?

1.一个项目是巨大的,只有保证你写的每一块都是正确的,最后整个项目才能正常运行。这里所谓的每一块就是一个单元。

②做单元测试需要引入JUnit框架,JUnit框架在JDK中没有,需要额外引入,也就是引入JUnit框架的class文件(jar包)

step1:文件目录下新建lib文件夹;

step2:将三个jar包复制进去,

step3:全选右键,Add as library

image-20240227123922794

③单元测试类(测试用例)怎么写?

1.单元测试类名:XxxTest

image-20240227133137828

image-20240227133302806

④单元测试方法怎么写?

1.单元测试方法需要使用@Test注解标注。

2.单元测试方法返回值类型必须是void

3.单元测试方法形参个数为0

4.建议单元测试方法名:testXxx

⑤什么是期望值,什么是实际值?

1.期望值就是在程序执行之前,你觉得正确的输出结果应该是多少

2.实际值就是程序在实际运行之后得到的结果

⑥常见注解:

1.@BeforeAll @AfterAll 主要用于在测试开始之前/之后执行必要的代码。被标注的方法需要是静态的。

2.@BeforeEach @AfterEach 主要用于在每个测试方法执行前/后执行必要的代码。

⑦单元测试中使用Scanner失效怎么办?

1.选中导航栏的“Help”,然后选中“Edit Custom VM Options…”,接着在“IDEA64.exe.vmoptions”文件中添加内容“-Deditable.java.test.console=true”,最后在重启IDEA即可解决

数据结构

跳过

Arrays工具类

java.util

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Arrays.toString()方法:将数组转换成字符串

这个和Object.toString不同,
/*************************************************/
Object.toString()
Arrays.toString(att)//很显然,这个是一个静态数组
/*************************************************/

Arrays.deepToString()方法:可以将二维数组转换成字符串

Arrays.equals(int[] arr1, int[] arr2)方法:判断两个数组是否相等

Arrays.equals(Object[] arr1, Object[] arr2)方法

Arrays.deepEquals(Object[] arr1, Object[] arr2)方法:判断两个二维数组是否相等

Arrays.sort(int[] arr)方法:基于快速排序算法,适合小型数据量排序。

Arrays.sort(String[] arr)方法

Arrays.parallelSort(int[] arr)方法:基于分治的归并排序算法,支持多核CPU排序,适合大数据量排序。//多个线程

int binarySearch(int[] arr, int elt)方法:二分法查找

Arrays.fill(int[] arr, int data)方法:填充数组

Arrays.fill(int[] a, int fromIndex, int toIndex, int val)方法

int[] Arrays.copyOf(int[] original, int newLength)方法:数组拷贝
int[] arr = {1,2,3};
int[] arr2 = Arrays.copyOf(arr,newLength:3)

//拷贝一个范围内的数组,to不包含
int[] Arrays.copyOfRange(int[] original, int from, int to)

Arrays.asList(T... data)方法:将一组数据转换成List集合。

异常Exception

异常概述

①什么是异常?有什么用?

1.Java中的异常是指程序运行时出现了错误或异常情况,导致程序无法继续正常执行的现象。例如,数组下标越界、空指针异常、类型转换异常等都属于异常情况。

2.Java提供了异常处理机制,即在程序中对可能出现的异常情况进行捕捉和处理。异常机制可以帮助程序员更好地管理程序的错误和异常情况,避免程序崩溃或出现不可预测的行为。

3.没有异常机制的话,程序中就可能会出现一些难以调试和预测的异常行为,可能导致程序崩溃,甚至可能造成数据损失或损害用户利益。因此,异常机制是一项非常重要的功能,是编写可靠程序的基础。

②异常在Java中以类和对象的形式存在。

1.现实生活中也有异常,比如地震,火灾就是异常。也可以提取出类和对象,例如:

1.地震是类:512大地震、唐山大地震就是对象。

2.空指针异常是类:发生在第52行的空指针异常、发生在第100行的空指针异常就是对象。

2.也就是说:在第52行和第100行发生空指针异常的时候,底层一定分别new了一个NullPointerException对象。在程序中异常是如何发生的?

image-20240227140425131

image-20240227140431623

异常继承结构

①所有的异常和错误都是可抛出的。都继承了Throwable类。

②Error是无法处理的,出现后只有一个结果:JVM终止。

③Exception是可以处理的。

④Exception的分类:

1.所有的RuntimeException的子类:运行时异常/未检查异常(UncheckedException)/非受控异常

2.Exception的子类(除RuntimeException之外):编译时异常/检查异常(CheckedException)/受控异常

⑤编译时异常和运行时异常区别:

  • 1.编译时异常特点:在编译阶段必须提前处理,如果不处理编译器报错。
  • 2.运行时异常特点:在编译阶段可以选择处理,也可以不处理,没有硬性要求。
  • 3.编译时异常一般是由外部环境或外在条件引起的,如网络故障、磁盘空间不足、文件找不到等
  • 4.运行时异常一般是由程序员的错误引起的,并且不需要强制进行异常处理

注意:编译时异常并不是在编译阶段发生的异常,所有的异常发生都是在运行阶段的,因为每个异常发生都是会new异常对象的,new异常对象只能在运行阶段完成。那为什么叫做编译时异常呢?这是因为这种异常必须在编译阶段提前预处理,如果不处理编译器报错,因此而得名编译时异常。

image-20240227141452414

image-20240227141713987

1
2
3
NullPointerException e = new NullPointerException();
throw e;
throw new NullPointerException();

自定义异常

step1:继承Exception(编译时异常)/RuntimeException

step2: 提供一个无参数构造方法,再提供一个带String msg参数的构造方法,在构造方法中调用父类的构造方法。

image-20240227143030302

异常的处理包括两种方式:

1.声明异常:类似于推卸责任的处理方式

在方法定义时使用throws关键字声明异常,**==告知调用者,调用这个方法可能会出现异常==**。这种处理方式的态度是:如果出现了异常则会抛给调用者来处理。

如果一个异常发生后希望调用者来处理的,使用声明异常(俗话说:交给上级处理)

==注意这里是throws,然后在后面需要throw e的时候是没有s的==

1
public void m() throws AException, BException... {}

如果Exception和Exception都继承了Exception,那么也可以这样写:

1
public void m() throws XException{}

==调用者在调用m()方法时,编译器会检测到该方法上用throws声明了异常,表示可能会抛出异常,编译器会继续检测该异常是否为编译时异常,如果为编译时异常则必须在编译阶段进行处理,如果不处理编译器就会报错。==

如果所有位置都采用throws,包括main方法的处理态度也是throws,如果运行时出现了异常,最终异常是抛给了main方法的调用者(JVM),JVM则会终止程序的执行。==因此为了保证程序在出现异常后不被中断,至少main方法不要再使用throws进行声明了。==

发生异常后,在发生异常的位置上,往下的代码是不会执行的,除非进行了异常的捕捉。

2.捕捉异常:真正的处理捕捉异常(真正的处理异常 (try…catch…关键字))

在可能出现异常的代码上使用try..catch进行捕捉处理。这种处理方式的态度是:把异常抓住。其它方法如果调用这个方法,对于调用者来说是不知道这个异常发生的。因为这个异常被抓住并处理掉了。

如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
try{
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
}catch(AException e){
// 如果捕捉到AException类型的异常,在这里处理
}catch(BException e){
// 如果捕捉到BException类型的异常,在这里处理
}catch(XException e){
// 如果捕捉到XException类型的异常,在这里处理
}
// 当try..catch..将所有发生的异常捕捉后,这里的代码是会继续往下执行的。




try{
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
}catch(AException e|BException e2){
// 如果捕捉到AException类型的异常,在这里处理
}catch(XException e){
//

catch可以写多个。并且遵循自上而下,从小到大。

因为,如果最大的在上面,后面的小的异常都不会抛出了,就会导致异常抛出的不够精确

异常在处理的整个过程中应该是:声明和捕捉联合使用。

什么时候捕捉?什么时候声明?

如果**==异常发生后需要调用者来处理的,需要调用者知道的,则采用声明方式。==**否则采用捕捉。

异常的常用方法

获取异常的简单描述信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	exception.getMessage();

try{
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
}catch(AException e){
// 如果捕捉到AException类型的异常,在这里处理
}catch(BException e){
// 如果捕捉到BException类型的异常,在这里处理
}catch(XException e){
String message = e.getMessage();
System.out.println(message);
// 如果捕捉到XException类型的异常,在这里处理
}
         获取的message是通过构造方法创建异常对象时传递过去的message。

打印异常堆栈信息:

1
exception.printStackTrace();

要会看异常的堆栈信息:

​ 异常信息的打印是符合栈数据结构的。

​ 看异常信息主要看最开始的描述信息。看栈顶信息。

image-20240227152502694

finally语句块

finally语句块中的代码是一定会执行的。

finally语句块不能单独使用,至少需要配合try语句块一起使用:

1
2
3
try...finally

try...catch...finally

通常在finally语句块中完成资源的释放

资源释放的工作比较重要,如果资源没有释放会一直占用内存。

为了保证资源的关闭,也就是说:不管程序是否出现异常,关闭资源的代码一定要保证执行。

因此在finally语句块中通常进行资源的释放。

final、finally、finalize分别是什么?

final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。

finally是一个关键字,和try一起使用,finally语句块中的代码一定会执行。\

finalize是一个标识符,它是Object类中的一个方法名。

image-20240227152745145

innotnull

image-20240227153843258

面试经典题

1.image-20240227154130653

i为多少? 答:100

原因:

两个原则,(1)程序是自上而下执行的,(2)finally肯定是要执行的

所以,i返回的是100,但是会新建一个临时变量去执行i++

执行流程是先用一个临时变量做i++;然后做return,所以如果是return true;返回的就是true

2.image-20240227213044733

false:从上往下,finally一定会在最后执行

3.image-20240227213351296

执行下一个

原则:就近原则(从继承关系的角度上讲)

null引用类型,距离String更近,String是Object的子类

4.image-20240227213539958
5.image-20240227214002828

常理来说:a==10;

因为static块是在类加载的时候运行的,最先运行的,按照顺序运行的.

在创建static int a的时候,(method这个方法还没有运行到???运行到了吗),b还没有赋值,默认值0.所以a没有被这个方法成功赋值,此外,由于静态变量即使不赋值也会赋值默认值,所以赋值了0

6.image-20240227214340480

代码的执行顺序

我答:答不出来一点

答案:

image-20240227215936922

所以static不是一定要在这个B使用之前就跑了,就想B的静态代码就是最后跑的,但是此刻已经实现了new B()

方法覆盖与异常

方法重写之后,不能比父类方法抛出更多的异常,可以更少。

就比如父类抛的是一个RuntimeExceptipn ,子类不能抛Exception

image-20240227154550203

常用类

String类(注意:引用数据类型)

String初识

String类是无法继承的;

jdk9 之后引入了一种字符串压缩机制

Java中的String属于引用数据类型,代表字符串。
Java专门在==堆==中为字符串准备了一个==字符串常量池==。(JDK8)

因为字符串使用比较频繁,放在字符串常量池中省去了对象的创建过程,从而提高程序的执行效率。(常量池属于一种缓存技术,缓存技术是提高程序执行效率的重要手段。)

在编译阶段,就会把” “起来的字符串全部创建到字符串常量池里面(必须要有一份),然后用的时候直接拿

JVM在启动的时候,会进行一系列的初始化,其中就包括字符串常量池的初始化会在类加载的过程中就初始化出来了,程序在真正运行中,是不用创建字符串对象的// 一种缓存技术

1
2
3
4
5
String s1 = “hello”; 
String s2 = “hello”;
System.out.println(s1 == s2); // true 说明s1和s2指向了字符串常量池中的同一个字符串对象。
注意:字符串字面量在编译的时候就已经确定了将其放到字符串常量池中。JVM启动时会立即程序中带有双引号的字符串全部放入字符串常量池。

Java8之后字符串常量池在堆中。Java8之前字符串常量池在永久代。
字符串字面量一旦创建是不可变的。(底层String源码中有一个属性:private final byte[] value;)
1
String s = “hello”; 其中“hello”存储在字符串常量池中。

“hello”不可变。不能变成“hello123”。如果进行字符串拼接,必然创建新的字符串对象。
是 “hello”不可变,不是s不可变,s可以指向其它的字符串对象:s = “xyz”;

从底层源码看,底层String源码中有一个属性:private final byte[] value;底层是个byte[]数组,数组长度不可变,所以长度不可变,

此外,这个数组是用private final修饰的,所以这个变量不可以被访问和继承修改

所以String的字面量一旦创建不可变

StringBuilder是可变的字符串数组,它的底层是byte[] value,所以可以改变,可以创建一个更大的数组,然后这个value就可以指向新的数组

String的拼接

①动态拼接之后的新字符串不会自动放到字符串常量池中:
1
2
3
4
5
6
7
1.String s1 = “abc”;

2.String s2 = “def”;

3.String s3 = s1 + s2;

4.String s4 = “abcdef”;

==5.System.out.println(s3 == s4); // false 说明拼接后的字符串并没有放到字符串常量池==

==6.以上程序中字符串常量中有三个: “abc” “def” “abcdef”==

==7.以上程序中除了字符串常量池的字符串之外,在堆中还有一个字符串对象 “abcdef”==

②两个字符串字面量拼接会做编译阶段的优化,在编译阶段就会进行字符串的拼接。
1
1.String s1 = “aaa” + “bbb”;

以上程序会在编译阶段进行拼接,因此以上程序在字符串常量池中只有一个: “aaabbb”

常量池可以改变吗?

可以,语句:s3.intern()

但是我们不能去删除常量池中的内容,系统自己有一些调整的操作

此时,s4和s5都在字符串常量池中

image-20240227193208940

String类常用的构造方法有以下几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String(char[] value)://根据字符数组创建一个新的字符串对象。
String(char[] value, int offset, int count)://根据字符数组的指定部分创建一个新的字符串对象。
String(byte[] bytes)://根据字节数组创建一个新的字符串对象,默认使用平台默认的字符集进行解码。
String(byte[] bytes, int offset, int length)://根据字节数组的指定部分创建一个新的字符串对象,默认使用平台默认的字符集进行解码。
String(byte[] bytes, Charset charset):
//根据字节数组和指定的字符集创建一个新的字符串对象。
new String(bytes, Charset.defaultCharset());
String(byte[] bytes, String charsetName):
//根据字节数组和指定的字符集名称创建一个新的字符串对象。
//这是一个解码的过程。你需要提前知道“byte[] bytes”是通过哪个编码方式进行编码得到的。
//如果通过GBK的方式进行编码得到的“byte[] bytes”,调用以上构造方法时采用UTF-8的方式进行解码。就会出现乱码。
String(String original):
//通过复制现有字符串创建一个新的字符串对象。
//这个方法被@IntrinsicCandidate标注,这个注解的作用是告诉编译器,该方法或构造函数是一个内在的候选方法,可以被优化和替换为更高效的代码。因此它是不建议使用的。
new String(“hello”); 这个代码会让常量池中有一个 “hello”,并且在堆中也有有一个String对象。
1
2
3
4
5
6
7
8
9
10
11
char[] chars = new char[]('a','b','c','d','e','f','g');
String s1 = new String(chars);
String s2 = new String(chars,offset=1,count=4);

byte[] bytes = {99,98,99,100}
String s2 = new String(bytes);//这是一个解码的过程,也是采用的平台默认的字符集.

byte[] bs2 = "sssssfda".getBytes(Charset.defaultCharset());

String s6 = new String(bs2,Charset.defaultCharset());
String s7 = new String(bs2,StanderCharsets.UTF_8);

image-20240227195325953

这样会乱码

String的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
char charAt(int index); //返回索引处的char值
int length(); //获取字符串长度

boolean isEmpty(); //判断字符串是否为空字符串,如果length()是0就是空字符串。//

boolean equals(Object anObject); //判断两个字符串是否相等。

boolean equalsIgnoreCase(String anotherString); //判断两个字符串是否相等,忽略大小写。

boolean contains(CharSequence s); //判断当前字符串中是否包含某个子字符串

boolean startsWith(String prefix); //判断当前字符串是否以某个字符串开头

boolean endsWith(String suffix); //判断当前字符串是否以某个字符串结尾

int compareTo(String anotherString); //两个字符串按照字典顺序比较大小

int compareToIgnoreCase(String str); //两个字符串按照字典顺序比较大小,比较时忽略大小写

int indexOf(String str);// 获取当前字符串中str字符串的第一次出现处的下标。

int indexOf(String str, int fromIndex); //从当前字符串的fromIndex下标开始往右搜索,获取当前字符串中str字符串的第一次出现处的下标。

int lastIndexOf(String str); //获取当前字符串中str字符串的最后一次出现处的下标。

int lastIndexOf(String str, int fromIndex); //从当前字符串的fromIndex下标开始往左搜索,获取当前字符串中str字符串的最后一次出现处的下标。

String stripLeading(); 去除前空白
String stripTrailing(); 去除后空白
String toString();
String intern(); 获取字符串常量池中的字符串,如果常量池中没有,则将字符串加入常量池并返回。
byte[] bytes = {97,98,99,100}; String s = new String(bytes);
String s2 = s.intern(); // 将字符串 “abcd”放入字符串常量池并返回常量池中的字符串 “abcd”
static String join(CharSequence d, CharSequence... elements); //将多个字符串以某个分隔符连接(Java8新增)
static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements);
static String valueOf(boolean b); //以下所有的静态方法valueOf作用是将非字符串类型的数据转换为字符串形式。
static String valueOf(char c);
static String valueOf(char[] data);
static String valueOf(char[] data, int offset, int count);
static String valueOf(double d);
static String valueOf(float f);
static String valueOf(int i);
static String valueOf(long l);
static String valueOf(Object obj);
byte[] getBytes(); //将字符串转换成字节数组。其实就是对字符串进行编码。默认按照系统默认字符集。
byte[] getBytes(String charsetName); //将字符串按照指定字符集的方式进行编码。
byte[] getBytes(Charset charset);
char[] toCharArray(); //将字符串转换字符数组。
String toLowerCase(); //转小写
String toUpperCase(); //转大写
String concat(String str); //进行字符串的拼接操作。和 + 的区别?
//+ 既可以进行求和,也可以进行字符串的拼接,底层拼接时会创建StringBuilder对象进行拼接。+ 拼接null时不会出现空指针异常。
//concat方法参数只能时字符串类型,拼接时不会创建StringBuilder对象,拼接完成后返回一个新的String对象。拼接null会出现空指针异常。
//+ 使用较多。如果进行大量字符串拼接,这两个都不合适。
String substring(int beginIndex); //从指定下标beginIndex开始截取子字符串
String substring(int beginIndex, int endIndex);
String trim(); //去除字符串前后空白(只能去除ASCII码中的空格和制表符)
String strip(); //去除字符串前后空白(支持所有的编码形式的空白,可以将全角空格去除,\u3000是全角空格,Java11新增)

image-20240227202458188

前>后;负

前<后;正

相等;0

右键 show Diagram

正则表达式

①正则表达式(regular expression),简称为regex或regexp,是一种用于描述特定模式的表达式。它可以匹配、查找、替换文本中与该模式匹配的内容,被广泛应用于各种文本处理和匹配相关的应用中。

②正则表达式的应用:

1.验证输入内容的格式是否正确。例如,邮箱,手机号,密码等

2.在文本编辑器中进行搜索和替换。例如,在代码编辑器中查找指定字符串或替换错误的代码成为正确的代码块

3.数据挖掘和信息提取。正则表达式可以从HTML、XML、JSON等格式的数据中提取所需的信息

4.用于编写脚本语言,如awk,grep和sed

5.服务器端编程。正则表达式在处理数据和字符串时具有高效的性能,可以在开发Web应用程序时被广泛应用

③正则表达式和Java语言的关系?

Java语言中可以使用正则表达式。C语言以及其它大部分编程语言都是支持正则表达式的。

==String的正则表达式相关的方法:==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String replace(CharSequence target, CharSequence replacement);

//将当前字符串中所有的target替换成replacement,返回一个新的字符串。

String replaceAll(String regex, String replacement);

//将当前字符串中所有符合正则表达式的regex替换成replacement。

String[] split(String regex);

//将当前字符串以某个正则表达式表示的子字符串进行分割,返回一个字符串数组。

boolean matches(String regex);

//判断当前字符串是否符合正则表达式regex。

String的面试题

1.
image-20240227204034015

false;

true

2.image-20240227204118497

true;因为这种拼接会在编译阶段完成,编译器优化策略

true

3.image-20240227204222677

false:yin_yang:这个和上面那个不一样,上面那个是字面量相加,是可以在编译阶段实现,这个是变量,不在编译阶段实现,是存放在堆里的字符串变量;

如果s3.intern(),那就是一样了

true

4.image-20240227204622761

3个对象,a一个b一个,s一个,放在字符串常量区里

5.image-20240227204749867
1
2
3
4
5
6
7
8
9
//字符串常量池中一个"a"
String s1 = "a";

//字符串常量池中一个"b",堆中一个s2

String s2 = new String("b");

//s3-->堆中两个,一个StringBuilder(在拼接的时候自动创建的),一个string(StringBuilder调用toString转换的)
String s3 = s1+s2
6.image-20240227210701818

字符串常量区:“a”,”b”

堆:new的a一个,new的b一个,拼接生成的StringBuilder一个,调用toString生成的一个String一个

一共6个对象

7.image-20240227210824500

答案:不会出现异常,底层会默认调用valueOf,将非字符串类型的数据转换为字符串形式。结果null

8.image-20240227211247488

没有final的话,false,s3不在字符串常量池里

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

true

final修饰后,s2不可变,就是个常量,所以这个拼接是在编译时候完成的,没有创建StringBuilder对象

所以是true

9.image-20240227211841286

false;

因为getB()是一个方法,只能在运行阶段,不能在编译阶段,所以虽然s2是个常量,但是他不在字符串常量区,在堆里面。

所以s3这个拼接操作也是在运行时候执行的,地址不一样,所以是false

10.image-20240227212157813

false

equals 的前提,两种类型要一样,这俩类型不一样

StringBuffer与StringBuilder

StringBuffer和StringBuilder:可变长度字符串

image-20240227220453859
  • 使用情况:这两个类是专门为频繁进行字符串拼接而准备

  • 二者差别:StringBuffer先出现的,Java5的时候新增了StringBuilder。StringBuffer是线程安全的。在不需要考虑线程安全问题的情况下优先选择StringBuilder,效率较高一些。

  • 底层: 是 byte[] 数组,并且这个 byte[] 数组没有被final修饰,这说明如果byte[]数组满了,可以创建一个更大的新数组来达到扩容,然后它可以重新指向这个新的数组对象。

  • 优化策略:==创建StringBuilder对象时,预估计字符串的长度,给定一个合适的初始化容量,减少底层数组的扩容。==

  • StringBuilder默认初始化容量:16

  • StringBuilder一次扩容多少?可以通过Debug跟踪一下append方法。扩容策略是:从当前容量开始,**==每次扩容为原来的2倍再加上2==**

原来是i,扩容后就是2i+2,如果是默认的,就开始2*16+2 ,然后2*(2+16)+2

如果拼接的字符串很大,大于了预期增长值,就会增大到拼接的字符串长度

假如是初始化len:50的字符串:

l16+minCapacity(34)==50

34>16+2,所以扩容大小为34

count 目前真实的存储数量

StringBuffer和StringBuilder构造方法

1
2
3
4
5
StringBuilder() //构造一个字符串生成器,其中不包含任何字符,初始容量为16个字符。

StringBuilder(int capacity) //构造一个字符串生成器,其中不包含任何字符,并且具有由容量参数指定的初始容量。

StringBuilder(String str)// 构造初始化为指定字符串内容的字符串生成器

image-20240227222910252

StringBuffer和StringBuilder常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
StringBuilder append(Type data);

StringBuilder delete(int start, int end);

StringBuilder deleteCharAt(int index);

StringBuilder insert(int offset, String str);

StringBuilder replace(int start, int end, String str)

StringBuilder reverse();

void setCharAt(int index, char ch);

void setLength(int newLength);//会把已有的数据抹掉


char charAt(int index);

int indexOf(String str);

int indexOf(String str, int fromIndex);

int lastIndexOf(String str);

int lastIndexOf(String str, int fromIndex);

int length();

String substring(int start);

String substring(int start, int end);

String toString();

long begin = System.current.TimeMills();

从1970年1月1日 0时0分0秒开始 的总毫秒数

1
2
3
4
5
6
7
8
9
10
11
12
//以下这种写法尽量避免,效率太低:
String s = “”;
for(int i = 0; i < 100000; i++){
// 优化策略:底层会新建一个StringBuilder对象
// 然后调用StringBuilder的append(i)方法进行追加
// 然后再调用StringBuilder toString()方法转成String类型
// 也就是说:这里会频繁的创建String对象,导致效率很低
// 同时给GC带来巨大压力。
s += i;
}
因此建议使用下面的方式,只创建一个StringBuilder对象

image-20240228101346290

包装类

什么是包装类?有什么用?

我的理解,将基础类型进行包装,形成一个引用类型,方便编程

image-20240228101801236

比如在一个函数中,其接受的参数是引用类型(Object),实际应用起来是对于数字操作的,直接传进去数字是不行的,是会报错的,所以我们把这个int的10,包装成引用类型的10.

image-20240228102517804

包装类中的6个数字类型都继承了Number类

boolean 和character没有继承

①Byte、Short、Integer、Long、Float、Double都继承了Number类,因此这些类中都有以下这些方法:

1
2
3
4
5
6
byteValue()
shortValue()
intValue()
longValue()
floatValue()
doubleValue()

==这些方法的作用就是将包装类型的数据转换为基本数据类型。==

包装类转换成基本数据类型的过程我们称为:==拆箱 unboxing==

Boolean的拆箱方法:booleanValue();

Character的拆箱方法:charValue();

image-20240228102834512

Integer的常量(为例子)

①通过Integer提供的常量可以获取int的最大值和最小值:

1
2
3
①最大值:Integer.MAX_VALUE

②最小值:Integer.MIN_VALUE

②当然,其它5个数字包装类也有对应的常量:

1
2
3
4
5
6
7
8
9
①byte最大值:Byte.MAX_VALUE

②byte最小值:Byte.MIN_VALUE

③short最大值:Short.MAX_VALUE

④short最小值:Short.MIN_VALUE

⑤......

Integer的构造方法

①Integer(int value)

1.Java9之后标记已过时,不建议使用。

2.该构造方法可以将基本数据类型转换成包装类。这个过程我们称为装箱boxing

1
2
3
4
 int i = 100;
Integer i1 = new Integer(i);
String s = "100";
Integer i2 = new Integer(s);
②Integer(String s)

1.Java9之后标记已过时,不建议使用。

2.该构造方法可以将字符串数字转换成包装类。但字符串必须是整数数字,如果不是会出现异常:NumberFormatException

Integer的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static int compare(int x, int y); //比较大小

static int max(int a, int b); //最大值

static int min(int a, int b); //最小值

static int parseInt(String s); //将字符串数字转换成数字类型。其它包装类也有这个方法:
int num1 = Integer.parseInt("123");
Double.parseDouble(String s)**

static String toBinaryString(int i); //获取数字二进制的字符串表示形式

static String toHexString(int i);// 获取数字十六进制的字符串表示形式

static String toOctalString(int i); //获取数字八进制的字符串表示形式

int compareTo(Integer anotherInteger); //比较大小,可见实现了Comparable接口

boolean equals(Object obj); //包装类已经重写了equals()方法。

String toString(); //包装类已经重写了toString()方法。
int intValue(); //将包装类拆箱为基本数据类型

//装箱
Integer i = new Integer(100);
//拆箱
int num = i.intValue();

static String toString(int i); //将基本数据类型转换成字符串

static Integer valueOf(int i); //将基本数据类型转换成Integer

static Integer valueOf(String s) /将字符串转换成Integer(这个字符串必须是数字字符串才行,不然出现NumberFormatException)

image-20240228111527964

Java5新特性:自动装箱和自动拆箱

编译阶段的功能

①Java5之后为了开发方便,引入了新特性:自动拆箱和自动装箱。

②自动装箱:auto boxing

1
Integer a = 10000;

③自动拆箱:auto unboxing

1
2
3
int b = a;

System.out.println(a + 1); 这里的a也会做自动拆箱。

⑤注意空指针异常:

1
2
3
Integer a = null;

System.out.println(a + 1);

以上代码出现空指针异常的原因是a在进行自动拆箱时,会调用 a.intValue()方法。

因为a是null,访问实例方法会出现空指针异常,因此使用时应注意。
image-20240228111945428

image-20240228112019609

整数型常量池

image-20240228112206054

①**==[-128 ~ 127] Java为这个区间的Integer对象创建了整数型常量池。==**

②也就是说如果整数没有超出范围的话,直接从整数型常量池获取Integer对象。

③以下是一个面试题:请说出它的输出结果:

1
2
3
4
5
6
7
8
9
10
11
Integer x = 128;

Integer y = 128;

System.out.println(x == y); // false

Integer a = 127;

Integer b = 127;

System.out.println(a == b); // true

大数字

如果整数超过long的最大值怎么办?

这个是引用数据类型

①java中提供了一种引用数据类型来解决这个问题:**==java.math.BigInteger==。它的父类是==Number。==**

②常用构造方法:BigInteger(String val)

③常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BigInteger add(BigInteger val); 求和

BigInteger subtract(BigInteger val); 相减

BigInteger multiply(BigInteger val); 乘积

BigInteger divide(BigInteger val); 商

int compareTo(BigInteger val); 比较

BigInteger abs(); 绝对值

BigInteger max(BigInteger val); 最大值

BigInteger min(BigInteger val); 最小值

BigInteger pow(int exponent); 次幂

BigInteger sqrt(); 平方根

如果浮点型数据超过double的最大值怎么办?

①java中提供了一种引用数据类型来解决这个问题**==:java.math.BigDecimal==经常用在财务软件中)。它的父类是==Number。==**

②构造方法:**==BigDecimal(String val)==**

③常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
BigDecimal add(BigDecimal augend); 求和

BigDecimal subtract(BigDecimal subtrahend); 相减

BigDecimal multiply(BigDecimal multiplicand); 乘积

BigDecimal divide(BigDecimal divisor); 商

BigDecimal max(BigDecimal val); 最大值

BigDecimal min(BigDecimal val); 最小值

BigDecimal movePointLeft(int n); 向左移动小数点

BigDecimal movePointRight(int n); 向右移动小数点

数字格式化

image-20240228113359145

日期处理

日期相关API

①**==long l = System.currentTimeMillis();==** // 获取自1970年1月1日0时0分0秒到系统当前时间的总毫秒数。

②java.util.Date 日期类

​ ①构造方法:Date()

​ ②构造方法:Date(long 毫秒)

③java.util.SimpleDateFormat 日期格式化类

​ ①日期转换成字符串(java.util.Date -> java.lang.String)

​ ②字符串转换成日期(java.lang.String -> java.util.Date)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Date ---》string  format
Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String str = sdf.format(now);

//string--》Date parse
String strDate = "2008-08-08 08:08:08 888";//字符串格式和下面这个format格式要对上,不然就会报解析异常
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
Date date = sdf2.parse(strDate)

//获取指定的时间,单位为毫秒级别
Date date1 = new Date(1000);
System.out.println(date1);

//获取当前系统的前十分钟
Date date2 = new Date(System.currentTimeMillis() - 1000 * 60 * 10);
System.out.println(date2);

④java.util.Calendar 日历类

​ ①获取当前时间的日历对象:Calendar c = Calendar.getInstance();

​ ②获取日历中的某部分:int year = c.get(Calendar.YEAR);

1
2
3
4
5
6
7
8
9
Calendar.YEAR //获取年份 Calendar.MONTH 获取月份,0表示1月,1表示2月,...,11表示12月

Calendar.DAY_OF_MONTH //获取本月的第几天 Calendar.DAY_OF_YEAR 获取本年的第几天

Calendar.HOUR_OF_DAY //小时,24小时制 Calendar.HOUR 小时,12小时制

Calendar.MINUTE //获取分钟 Calendar.SECOND 获取秒

Calendar.MILLISECOND //获取毫秒 Calendar.DAY_OF_WEEK 获取星期几,1表示星期日,...,7表示星期六

image-20240228130311011

①java.util.Calendar 日历类

​ 1.日历的set方法:设置日历

静态变量

​ 1.calendar.set(Calendar.YEAR, 2023);

​ 2.calendar.set(2008, Calendar.SEPTEMBER,8);

​ 2.日历的add方法(日历中各个部分的加减):

​ 1.calendar.add(Calendar.YEAR, 1);

​ 3.日历对象的setTime()让日历关联具体的时间

​ 1.calendar.setTime(new Date());

​ 4.日历对象的getTime()方法获取日历的具体时间:

​ 1.Date time = calendar.getTime();

image-20240228131052018

1
2
3
4
5
6
7
Calendar cal = new Calendar();
String strDate = "2008-05-12 15:30:30";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(strDate);
cal.setTime(date);
System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));

Java8的新日期API

传统的日期API存在线程问题

新的API在java.time包

传统的日期API存在线程安全问题,Java8又提供了一套全新的日期API

  • ①java.time.LocalDate、java.time.LocalTime、java.time.LocalDateTime 日期、时间、日期时间
  • ②java.time.Instant 时间戳信息197001010000到现在的时间毫秒
  • ③java.time.Duration 计算两个时间对象之间的时间间隔,精度为纳秒
  • ④java.time.Period 计算两个日期之间的时间间隔,以年、月、日为单位。
  • ⑤java.time.temporal.TemporalAdjusters 提供了一些方法用于方便的进行日期时间调整
  • ⑥java.time.format.DateTimeFormatter 用于进行日期时间格式化和解析

LocalDate日期、LocalTime时间、LocalDateTime日期时间

①获取当前时间(精确到纳秒,1秒=1000毫秒,1毫秒=1000微秒,1微秒=1000纳秒)
1
LocalDateTime now = LocalDateTime.now(); 
②获取指定日期时间
1
LocalDateTime ldt = LocalDateTime.of(2008,8,8,8,8,8,8); // 获取指定的日期时间
③加日期和加时间
1
LocalDateTime localDateTime = ldt.plusYears(1).plusMonths(1).plusDays(1).plusHours(1).plusMinutes(1).plusSeconds(1).plusNanos(1);
④减日期和减时间
1
LocalDateTime localDateTime = ldt.minusYears(1).minusMonths(1).minusDays(1).minusHours(1).minusMinutes(1).minusSeconds(1).minusNanos(1);
⑤获取年月日时分秒
1
2
3
4
5
6
7
8
9
int year = now.getYear(); // 年 int month = now.getMonth().getValue(); // 月

int dayOfMonth = now.getDayOfMonth(); // 一个月的第几天 int dayOfWeek = now.getDayOfWeek().getValue(); // 一个周第几天

int dayOfYear = now.getDayOfYear(); // 一年的第几天 int hour = now.getHour(); // 时

int minute = now.getMinute(); // 分 int second = now.getSecond(); // 秒

int nano = now.getNano(); // 纳秒

==Instant 时间戳(获取1970年1月1日 0时0分0秒到某个时间的时间戳)==

①获取系统当前时间(UTC:全球标准时间)
1
Instant instant = Instant.now(); 
②获取时间戳
1
long epochMilli = instant.toEpochMilli(); 

Duration 计算时间间隔

①计算两个时间相差时间间隔Duration.between(now1, now2);
1
2
3
4
5
6
7
8
9
10
11
12
13
LocalDateTime now1 = LocalDateTime.of(2008,8,8,8,8,8);

LocalDateTime now2 = LocalDateTime.of(2009,9,9,9,9,9);

Duration between = Duration.between(now1, now2);

// 两个时间差多少个小时

System.out.println(between.toHours());

// 两个时间差多少天

System.out.println(between.toDays());

Period 计算日期间隔

①计算两个日期间隔Period.between(now1, now2);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    LocalDate now1 = LocalDate.of(2007,7,7);

LocalDate now2 = LocalDate.of(2008,8,8);

Period between = Period.between(now1, now2);

// 相差年数

​ System.out.println(between.getYears());

// 相差月数

​ System.out.println(between.getMonths());

// 相差天数

​ System.out.println(between.getDays());

TemporalAdjusters 时间矫正器

1
2
3
4
5
6
7
8
9
10
11
12
13
LocalDateTime now = LocalDateTime.now(); // 获取系统当前时间

②now.with(TemporalAdjusters.firstDayOfMonth()); // 当前月的第一天

③now.with(TemporalAdjusters.firstDayOfNextYear()); // 下一年的第一天

④now.with(TemporalAdjusters.lastDayOfYear()); // 本年最后一天

⑤now.with(TemporalAdjusters.lastDayOfMonth()); // 本月最后一天

⑥now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 下周一

⑦......

DateTimeFormatter 日期格式化

①日期格式化 (LocalDateTime –> String)
1
2
3
4
5
LocalDateTime now = LocalDateTime.now();

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String s = dtf.format(now);
②将字符串转换成日期(String –> LocalDateTime)
1
2
3
4
5
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

LocalDateTime localDateTime = LocalDateTime.parse("2008-08-08 08:08:08", dtf);

System.out.println(localDateTime);

Math

java.lang.Math 数学工具类,都是**==静态方法==**

①常用属性:static final double PI(圆周率)
②常用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static int abs(int a); //绝对值

static double ceil(double a); //向上取整

static double floor(double a); //向下取整

static int max(int a, int b); //最大值

static int min(int a, int b);// 最小值

static double random(); //随机数[0.0, 1.0) int num = (int)(Math.random() * 100);可以获取[0-100)的随机数

static long round(double a); //四舍五入

static double sqrt(double a); //平方根

static double pow(double a, double b); //a的b次幂

枚举

枚举(Java5新特性)

①枚举类型在Java中是一种==引用数据类型。==

②合理使用枚举类型可以让代码更加清晰、可读性更高,可以有效地避免一些常见的错误。

③什么情况下考虑使用枚举类型?

​ 1.这个数据是有限的,并且可以一枚一枚列举出来的。

​ 2.枚举类型是类型安全的,它可以有效地防止使用错误的类型进行赋值。

④枚举如何定义?以下是最基本的格式:

1
2
3
4
5
enum 枚举类型名 {

枚举值1, 枚举值2, 枚举值3, 枚举值4

}

⑤通过反编译(javap)可以看到:

1.所有枚举类型默认继承java.lang.Enum,因此枚举类型无法继承其他类。

2.所有的枚举类型都被final修饰,所以枚举类型是无法继承的

3.所有的枚举值都是常量

4.所有的枚举类型中都有一个values数组(可以通过values()获取所有枚举值并遍历)

image-20240228134745351

image-20240228134809247

枚举的高级用法

①普通类中可以编写的元素,枚举类型中也可以编写。

静态代码块,构造代码块

实例方法,静态方法

实例变量,静态变量

②枚举类中的==构造方法是私有化==的(默认就是私有化的,只能在本类中调用)

构造方法调用时不能用new。直接使用“枚举值(实参);”调用。

每一个枚举值相当于枚举类型的实例。

枚举类最开始的时候 定义的枚举值就是枚举类对象,会调用构造函数,默认会调用无参构造函数

如果定义了有参构造函数,没有修改在上面定义的枚举值的话,就会报错

所以需要对于枚举值修改一下构造函数

image-20240228144551282

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public enum Season {
SPRING("春天") ,SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天");
public static int i = 10;
private String name = "春天";
Season(String name){
this.name = name;
}
}

public class SeasonTest {
public static void main(String[] args) {
Season season = get();
switch (season){
case SPRING -> System.out.println(Season.SPRING.name());
case SUMMER -> System.out.println(Season.SUMMER.name());
case AUTUMN -> System.out.println(Season.AUTUMN.name());
case WINTER -> System.out.println(Season.WINTER.name());
}
}
public static Season get(){
return Season.SPRING;
}
}

③枚举类型中如果编写了其他代码,==必须要有枚举值,枚举值的定义要放到最上面,==

==最后一个枚举值的分号不能省略。==

④枚举类因为默认继承了==java.lang.Enum==,因此不能再继承其他类,但可以实现接口。

第一种实现方式:在枚举类中实现。

第二种实现方式:让每一个枚举值实现接口。

image-20240228134927319
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public enum Season implements Eatable{
SPRING("春天"){
@Override
public void eat() {
System.out.println("apple");
}
},SUMMER("夏天"){
@Override
public void eat() {
System.out.println("lalala");
}
},AUTUMN("秋天"){
@Override
public void eat() {
System.out.println("111");
}
},WINTER("冬天"){
@Override
public void eat() {
System.out.println("111");
}
};

或者
public enum Season implements Eatable{
SPRING("春天"),SUMMER("夏天"),AUTUMN("秋天"),WINTER("冬天");
public static int i = 10;
private String name = "春天";
Season(String name){
this.name = name;
}


@Override
public void eat() {

}
}

Random

java.util.Random 随机数生成器(生成随机数的工具类)

①常用构造方法:
1
Random()
②常用方法:
1
2
3
4
5
int nextInt(); 获取一个int类型取值范围内的随机int

int nextInt(int bound); 获取[0,bound)区间的随机数

double nextDouble(); 获取[0.0, 1.0)的随机数。

xxxxxxxxxx package com.powernode.javase.oop45;​/** * 匿名内部类:没有名字的类。只能使用一次。 */public class Test {    public static void main(String[] args) {        // 创建电脑对象        Computer computer = new Computer();        //computer.conn(new Printer());​        // 以下conn方法参数上的代码做了两件事:        // 第一:完成了匿名内部类的定义。        // 第二:同时实例化了一个匿名内部类的对象。        computer.conn(new Usb(){            // 接口的实现            @Override            public void read() {                System.out.println(“read…..”);           }​            @Override            public void write() {                System.out.println(“write…..”);           }       });   }}​class Computer {    public void conn(Usb usb){        usb.read();        usb.write();   }}​interface Usb {    void read();    void write();}​// 编写一个接口的实现类/*class Printer implements Usb {​    @Override    public void read() {        System.out.println(“打印机开始读取数据”);    }​    @Override    public void write() {        System.out.println(“打印机开始打印”);    }} */java

java.lang.System类的常用方法:

①常用属性:
1
2
3
4
5
static final PrintStream err 标准错误输出流(System.err.println(“错误信息”);输出红色字体)

static final InputStream in 标准输入流

static final PrintStream out 标准输出流
②常用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length); 数组拷贝

static void exit(int status); 退出虚拟机

static void gc(); 建议启动垃圾回收器

static long currentTimeMillis(); 获取自1970-01-01 00:00:00 000到系统当前时间的总毫秒数

static long nanoTime(); 获取自197011000秒以来,当前时间的纳秒数

static Map<String,String> getenv(); 获取当前系统的环境变量,例如Path,JAVA_HOME,CLASSPATH等。

static Properties getProperties(); 获取当前系统的属性。

static String getProperty(String key); 通过key获取指定的系统属性。

UUID

具有全球唯一性的标识

UUID(通用唯一标识符)是一种软件构建的标准,用来生成具有唯一性的ID。

UUID具有以下特点:

​ ①UUID可以在分布式系统中生成唯一的标识符,避免因为主键冲突等问题带来的麻烦。

​ ==②UUID具有足够的唯一性,重复的概率相当低。UUID使用的是128位数字,除了传统的16进制表示之外(32位的16进制表示),还有基于62进制的表示,可以更加简洁紧凑。==

③UUID生成时不需要依赖任何中央控制器或数据库服务器,可以在本地方便、快速地生成唯一标识符。

④UUID生成后可以被许多编程语言支持并方便地转化为字符串表示形式,适用于多种应用场景。

在Java开发中,UUID的使用是非常普遍的。它可以用于生成数据表主键、场景标识、链路追踪、缓存Key等。使用UUID可以方便地避免主键、缓存Key等因冲突而产生的问题,同时能够实现多种功能,例如追踪、缓存、日志记录等。

Java中的java.util.UUID类提供对UUID的支持

①生成UUID:static UUID randomUUID();

②将UUID转换为字符串:String toString();