Object类

Object类简介

Object类是所有Java类的根父类,如果在类的声明中未明确使用extends关键字指明其父类,则默认父类为java.lang.Object类,也就是说子类可以使用 Object 的所有方法。

1
2
3
4
5
6
7
public class Person { 
...
}
// 等价于
public class Person extends Object {
...
}

Object类的方法

方法简介
getClass()获取对象的运行时 class 对象,通常是和 Java 反射机制搭配使用的
hashCode()返回当前对象的哈希码值。
equals(Object obj)比较当前对象与指定对象是否相等。
toString()返回当前对象的字符串表示。
clone()保护方法,实现对象的浅复制,默认的 clone 方法是浅拷贝。
只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。
notify()唤醒在此对象监视器上等待的单个线程。
notifyAll()唤醒在此对象监视器上等待的所有线程。
wait()导致线程进入等待状态,直到被其他线程通过 notify() 或 notifyAll() 唤醒,该方法只能在同步方法中调用
wait(long timeout)等待其他线程调用该对象的 notify() 或 notifyAll() 方法,最多等待指定时间(以毫秒为单位)
wait(long timeout, int nanos)等待其他线程调用该对象的 notify() 或 notifyAll() 方法,最多等待指定时间(以毫秒和纳秒为单位)
finalize()在垃圾回收器回收对象之前调用,Jdk9中废弃

toString()方法

(1)Object中定义的toString方法,默认返回格式如下:对象的 class 名称 + @ + hashCode 的十六进制字符串。

1
2
3
4
// toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

(2)当我们输出一个对象的引用时,实际上默认调用当前对象的tostring()

1
2
3
4
5
6
7
public class Demo {
public static void main(String[] args) {
Object o = new Object();// 创建对象
System.out.println(o);// java.lang.Object@677327b6
System.out.println(o.toString());// java.lang.Object@677327b6
}
}

(3)如果一个类没有重写toString()方法,那么当打印该类的对象时,会调用Object类中的toString()方法,输出的结果是该对象的引用地址,但是在实际开发中,一般需要输出真实值,所以都会重写toString()方法

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
public class Demo {
public static void main(String[] args) {
Person1 person1 = new Person1();
Person2 person2 = new Person2();
System.out.println(person1);// Object类.Person1@14ae5a5
System.out.println(person2);// Person{name='null', age=0}
}
}

class Person1 {
private String name;
private int age;
}

class Person2 {
private String name;
private int age;

// 重写toString方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

(4)像String、Data、file、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回“实体内容信息”

1
2
3
4
// String类中的toString()方法
public String toString() {
return this;
}

equals()方法

(1)Object中equals()方法用来比较对象的引用地址是否相同,源码如下

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

(2)Object中的equals方法使用的是==进行的比较,实际上是比较的两个对象的内存地址是否相同,在现实的开发过程中,通常我们希望比较引用类型的对象内容是否相等,所以一般自定义的Bean都会重写equals()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
class Person{
private String name;
private int age;

// 重写equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
}

(3)像String、Data、File、包装类等都重写了Object类中的equals()方法,调用时是比较的是内容是否一致而不是地址一致

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
// String类中的equals()方法
public boolean equals(Object anObject) {
// 判断是否为同一个值,如X=X
if (this == anObject) {
return true;
}
// 判断是否为String对象
if (anObject instanceof String) {
String anotherString = (String)anObject;
// 此处value是String类中定义的当前调用此方法的String转换成的char数组
int n = value.length;
// 判断调用String的char数组和比较String的char数组长度是否一致
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// 循环判断每个字符是否一致
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

hashCode()方法

(1)hashCode()方法用于返回对象的哈希码值,哈希码是根据对象的内部状态计算得出的一个整数,用于标识对象的唯一性,通过哈希码,可以快速定位对象在哈希表中的存储位置,从而加快对象的查找速度。

(2)在每个类中,在重写equals方法的时侯,一定要重写hashcode方法。

  1. Object规范要求:如果两个对象通过equals方法比较相等,那么它们的hashCode方法返回的值必须相等。这意味着,在重写equals方法时,如果判断两个对象相等,就必须确保它们的hashCode方法返回相同的值。
  2. equals方法的目的是用于比较两个对象是否在逻辑上相等。默认的equals方法比较的是对象的内存地址,也就是判断两个对象是否引用同一个实例。在重写equals方法时,通常是根据需要比较对象的属性值,而不是仅仅比较内存地址。
  3. hashCode方法返回对象的哈希码值,用于在哈希表等数据结构中高效地存储和检索对象。默认的hashCode方法根据对象的内存地址计算哈希码。
  4. 在equals方法与hashCode方法都未重写的情况下,假如有两个对象的内存地址相同,对象的属性值也相同,equals方法会比较内存地址判断为true,hashCode方法根据对象的内存地址计算的哈希码会相同,符合Object规范要求
  5. 在equals方法重写但hashCode方法未重写的情况下,假如对象的内存地址不相同,但对象的属性值相同,此时equals方法会比较对象的属性值同样判断为true,但hashCode方法根据对象的内存地址计算的哈希码可能不相同,不符合Object规范要求

getClass()方法

(1)Object中的getClass()方法用于获取对象的运行时对象的类,方法的返回值是Class类型

1
public final native Class<?> getClass();

(2)万物皆对象,JVM会将字节码文件加载到其内部一个叫做方法区的地方,然后会在堆中创建这个字节码文件对应的对象,这个对象是java.lang.Class类型的,在JVM中每个字节码文件只有一个对应的Class对象,可以通过Object中提供的getClass方法来获取这个对象。

1
2
Object obj = new Object();
Class<?> clazz = obj.getClass();

(3)Class clazz = obj.getClass();根据clazz可以获取该对象的所有成员方法、成员方法等

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* 打印类的信息,包括类的构造函数,成员函数,成员变量
*/
public class ClassUtil {

/**
* 获取对象的成员方法的信息
*/
public static void printClassMethodMessage(Object obj) {
// 要获取类的信息 首先要获取类的类类型,传递的是哪个子类的对象 c就是该子类的类类型
Class clazz = obj.getClass();
// 获取类的名称
System.out.println("类的名称是:" + clazz.getName());
// Method类,方法对象 一个成员方法就是一个Method对象
// getDeclaredMethods()获取的是所有该类自己声明的方法,不包括访问权限
Method[] declaredMethods = clazz.getDeclaredMethods();
// getMethods()方法获取的是所有的public的函数,包括父类继承而来的
Method[] ms = clazz.getMethods();
for (int i = 0; i < ms.length; i++) {
// 得到方法的返回值类型的类类型
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName() + " ");
// 得到方法的名称
System.out.print(ms[i].getName() + "(");
// 获取参数类型--->得到的是参数列表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}

/**
* 获取对象的成员变量的信息
*/
public static void printFieldMessage(Object obj) {
Class clazz = obj.getClass();
// 成员变量也是对象 java.lang.reflect.Field Field类封装了关于成员变量的操作
// getFields()方法获取的是所有的public的成员变量的信息
Field[] fields = clazz.getFields();
// getDeclaredFields获取的是该类自己声明的成员变量的信息
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
// 得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
// 得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName + " " + fieldName);
}
}

/**
* 打印对象的构造函数的信息
*/
public static void printConMessage(Object obj) {
Class clazz = obj.getClass();
// 构造函数也是对象 java.lang.Constructor中封装了构造函数的信息
Constructor[] constructors = clazz.getConstructors();
// getConstructors获取所有的public的构造函数 getDeclaredConstructors得到所有的构造函数
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.print(constructor.getName() + "(");
// 获取构造函数的参数列表--->得到的是参数列表的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
}

clone()方法

(1)clone()方法是Object类中的保护方法,用于实现对象的浅复制(浅拷贝)。只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。

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
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable {
private String name;
private List<String> hobbies;

@Override
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认的浅拷贝
}
}

class Main {
public static void main(String[] args) throws CloneNotSupportedException {
List<String> hobbies = new ArrayList<>();
hobbies.add("读书");
hobbies.add("游泳");
Person person1 = new Person("小明", hobbies);
Person person2 = (Person) person1.clone();// 浅拷贝
person2.setName("小刚");
person2.getHobbies().add("唱歌");

System.out.println(person1.getName()); // 输出 小明
System.out.println(person2.getName()); // 输出 小刚
System.out.println(person1.getHobbies());// 输出 [读书, 游泳, 唱歌]
System.out.println(person2.getHobbies());// 输出 [读书, 游泳, 唱歌]
}
}

(2)如果需要实现深拷贝,则需要在具体的对象类中重写clone()方法。

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
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
class Person implements Cloneable {
private String name;
private List<String> hobbies;

@Override
public Object clone() {
List<String> clonedHobbies = new ArrayList<>(this.hobbies);
return new Person(this.name, clonedHobbies);// 重写为深拷贝
}

}

class Main {
public static void main(String[] args) {
List<String> hobbies = new ArrayList<>();
hobbies.add("读书");
hobbies.add("游泳");
Person person1 = new Person("小明", hobbies);
Person person2 = (Person) person1.clone();// 深拷贝
person2.setName("小刚");
person2.getHobbies().add("唱歌");

System.out.println(person1.getName()); // 输出 小明
System.out.println(person2.getName()); // 输出 小刚
System.out.println(person1.getHobbies());// 输出 [读书, 游泳]
System.out.println(person2.getHobbies());// 输出 [读书, 游泳, 唱歌]
}
}

字符串类

字符串相关类

(1)String类、StringBuffer类、StringBuilder类是三个字符串相关类。

字符串相关类简介
String不可变字符序列,存储在字符串常量池中,内容无法修改
StringBuffer底层是字符数组,系统会对该数组进行扩容,内容频繁修改字符串的情况考虑使用StingBuffer
StringBuilder与StringBuffer非常类似,均代表可变的字符序列,通过API可以看到提供相关功能的方法也一样

(2)String类的对象代表不可变的字符序列,StringBuilder类和StringBuffer类代表可变字符序列

(3)String类用来表示字符串,一旦被声明,值就不能被改变,如果需要改变,就要重新创建新的String对象,适用于存储较短的字符串或者是不需要频繁修改的字符串。为了方便操作字符串,JDK引入了StringBuffer来解决,StringBuffer是可变的字符串,底层使用的数组进行动态扩容,类中每个公开方法都使用了synchronized关键字修饰,所有是线程同步安全的,适用于多线程的场景,而StringBuilder与StringBuffer相似,也是可变字符串,不同的是类中方法只有少部分使用了synchronized关键字修饰,不能确保线程安全,适用于单线程的场景

String类

字符串初始化

在操作String类之前,首先需要对String类进行初始化,可以通过常量直接赋值对String类进行初始化,或者使用String的构造方法来初始化字符串对象

构造方法功能描述
String()创建一个内容为空的字符串
String(char[] value)根据指定的字符数组创建对象
String(String value)根据指定的字符串内容创建对象
1
2
3
4
5
6
7
8
// 1、使用字符串常量直接初始化一个String对象
String str1="abc";

// 2、使用String的构造方法初始化字符串对象
String str2=new String();// 创建一个空的字符串
String str3=new String ("小明");//创建字符串"小明"
char[] charArray=new char[]{'A','B','C'};
String str4=new String(charArray);//创建一个内容为字符数组的字符串

字符串创建后值不可改变

String是引用数据类型,通过源码可以看到String类前面加了final修饰,因此String类是不能够被继承的。将其设置为不能被继承的原因是为了减少歧义

1
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {...}

String创建好之后值是不可以被改变的,这里指的是在堆中的字符串的值是不可以被改变,不可变的主要原因是其底层使用了一个final修饰的char字符数组(Jdk8中)。在Jdk9之后版本中,String底层使用的是final修饰的byte字节数组

1
2
3
4
5
6
7
8
9
10
11
12
public class StringTest {
public static void main(String[] args) {
String str = "小明";
change(str); // 修改为:小刚
System.out.println(str); // 小明
}

public static void change(String str) {
str = "小刚";
System.out.println("修改为:" + str);
}
}

常用方法

方法名描述
split(String regex)字符串分隔,分割字符串为字符串数组
substring(int beginIndex, int endIndex)字符串截取,截取[beginIndex,endIndex)位置字符串,包含beginIndex下标元素,不包含endIndex下标元素
concat(String str)字符串拼接,将指定的字符串连接到原字符串的末尾
replace(char oldChar, char newChar)字符串替换,替换字符串中的字符
trim()字符串去空白,去除字符串两端空白
toLowerCase()字符串大写转小写,将字符串转换为小写字母
toUpperCase()字符串小写转大写,将字符串转换为大写字母
indexOf(char ch)字符串查找,返回指定字符的索引
charAt(int index)字符串查找,返回指定索引处的字符
equals()字符串比较,判断当前字符串与指定的对象是否相等,如果相等则返回true,否则返回false。
format(String format, Object… args)字符串格式化,使用指定的格式字符串和参数进行格式化操作。
length()返回字符串的长度,返回字符串的长度,即包含的字符数量。
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
public class StringMethodsExample {
/**
* 字符串分隔,分割字符串为字符串数组
*/
@Test
public void testSplit() {
String text = "你好,世界!";
String[] parts = text.split(",");
System.out.println("数组长度: " + parts.length);
System.out.println("部分 1: " + parts[0]);
System.out.println("部分 2: " + parts[1]);
}

/**
* 字符串截取,截取[beginIndex, endIndex)位置字符串,包含beginIndex下标元素,不包含endIndex下标元素
*/
@Test
public void testSubstring() {
String text = "你好,世界!";
String substring = text.substring(3, 5);
System.out.println("截取结果: " + substring);
}

/**
* 字符串拼接,将指定的字符串连接到原字符串的末尾
*/
@Test
public void testConcat() {
String text = "你好";
String result = text.concat(",GPT-3.5!");
System.out.println("拼接结果: " + result);
}

/**
* 字符串替换,替换字符串中的字符
*/
@Test
public void testReplace() {
String text = "你好,世界!";
String replacedText = text.replace(",", "-");
System.out.println("替换后的字符串: " + replacedText);
}

/**
* 字符串去空白,去除字符串两端空白
*/
@Test
public void testTrim() {
String text = " 你好,世界! ";
String trimmedText = text.trim();
System.out.println("去除空白后的字符串: " + trimmedText);
}

/**
* 字符串大写转小写,将字符串转换为小写字母
*/
@Test
public void testToLowerCase() {
String text = "HELLO,WORLD!";
String lowerCaseText = text.toLowerCase();
System.out.println("小写字母字符串: " + lowerCaseText);
}

/**
* 字符串小写转大写,将字符串转换为大写字母
*/
@Test
public void testToUpperCase() {
String text = "hello,world!";
String upperCaseText = text.toUpperCase();
System.out.println("大写字母字符串: " + upperCaseText);
}

/**
* 字符串查找,返回指定字符的索引
*/
@Test
public void testIndexOf() {
String text = "你好,世界!";
int index = text.indexOf("世界");
System.out.println("索引位置: " + index);
}

/**
* 字符串查找,返回指定索引处的字符
*/
@Test
public void testCharAt() {
String text = "你好,世界!";
char character = text.charAt(3);
System.out.println("索引位置为 3 的字符: " + character);
}

/**
* 字符串比较,判断当前字符串与指定的对象是否相等,如果相等则返回true,否则返回false。
*/
@Test
public void testEquals() {
String text1 = "你好";
String text2 = "你好";
boolean isEqual = text1.equals(text2);
System.out.println("两个字符串是否相等? " + isEqual);
}

/**
* 字符串格式化,使用指定的格式字符串和参数进行格式化操作。
*/
@Test
public void testFormat() {
String name = "小明";
int age = 25;
String formatted = String.format("我叫%s,今年%d岁。", name, age);
System.out.println("格式化后的字符串: " + formatted);
}

/**
* 返回字符串的长度,即包含的字符数量。
*/
@Test
public void testLength() {
String text = "你好,世界!";
int length = text.length();
System.out.println("字符串长度: " + length);
}
}

StringBuffer类

StringBuffer类简介

(1)由于字符串是常量,因此一旦创建,其内容和长度是不可改变的。如果需要对一个字符串进行修改,则只能创建新的字符串。为了便于对字符串的修改,JDK中提供了一个StringBuffer类(也称字符串缓冲区),与String类最大的区别在于StringBuffer的内容和长度都是可以改变的。

(2)StringBuffer 类提供了 3 个构造方法来创建一个字符串

构造方法功能描述
StringBuffer()构造一个空的字符串缓冲区,并且初始化为 16 个字符的容量
StringBuffer(int length)创建一个空的字符串缓冲区,并且初始化为指定长度 length 的容量
StringBuffer(String str)创建一个字符串缓冲区,并将其内容初始化为指定的字符串内容 str
字符串缓冲区的初始容量为 16 加上字符串 str 的长度

(3)StringBuffer是线程安全的,所有公开方法都是 synchronized 修饰,在多线程程序中也可以很方便的进行使用

StringBuffer工作原理

  1. StringBuffer 对象创建:当我们创建一个 StringBuffer 对象时,它会在内部初始化一个默认大小的字符数组,并保留一些额外的空间以便后续的字符串添加操作。
  2. 字符串的添加和修改:通过调用 StringBuffer 的 append()、insert()、delete() 等方法,我们可以向 StringBuffer 对象中添加、插入或删除字符。这些操作会在字符数组的末尾或指定位置进行。
  3. 自动扩容:当我们进行字符串添加操作时,如果当前内部字符数组的容量不足以容纳新的字符串,StringBuffer 会自动扩容。它会创建一个更大的字符数组,并将原始字符数组的内容复制到新数组中。
  4. 性能优化:为了提高性能,StringBuffer 内部使用了一些优化策略。例如,它会预分配一些额外的空间以减少频繁的扩容操作,同时也会使用一些技巧来避免不必要的字符复制。

StringBuffer类的常用方法

常用方法简介
append()字符串拼接(增)
delete()删除指定位置的内容(删)
deleteCharAt()移除序列中指定位置的字符(删)
replace()用指定字符串替换指定范围内的字符(改)
insert()在指定位置插入(改)
reverse()反转,把当前字符序列逆转(改)
setCharAt()替换字符,用于在字符串的指定索引位置替换一个字符(改)
charAt()返回指定位置的字符(查)
length()返回长度(查)
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
public class StringBufferTest {
public static void main(String[] args) {
// 创建一个 StringBuffer 对象
StringBuffer stringBuffer = new StringBuffer("Hello");

// 使用 append() 方法进行字符串拼接(增)
stringBuffer.append(" World");
System.out.println(stringBuffer); // 输出:Hello World

// 使用 delete() 方法删除指定范围内的字符(删)
stringBuffer.delete(5, 11);
System.out.println(stringBuffer); // 输出:Hello

// 使用 deleteCharAt() 方法移除指定位置的字符(删)
stringBuffer.deleteCharAt(1);
System.out.println(stringBuffer); // 输出:Hllo

// 使用 replace() 方法用指定字符串替换指定范围内的字符(改)
stringBuffer.replace(1, 2, "i");
System.out.println(stringBuffer); // 输出:Hillo

// 使用 insert() 方法在指定位置插入字符串(改)
stringBuffer.insert(1, "e");
System.out.println(stringBuffer); // 输出:Hello

// 使用 reverse() 方法反转字符序列(改)
stringBuffer.reverse();
System.out.println(stringBuffer); // 输出:olleH

// 使用 setCharAt() 方法替换指定位置的字符(改)
stringBuffer.setCharAt(0, 'H');
System.out.println(stringBuffer); // 输出:HlleH

// 使用 charAt() 方法返回指定位置的字符(查)
char c = stringBuffer.charAt(1);
System.out.println(c); // 输出:l

// 使用 length() 方法返回字符串长度(查)
int length = stringBuffer.length();
System.out.println(length); // 输出:5
}
}

StringBuilder类

StringBuilder与StringBuffer相似,也是可变字符串,不同的是类中方法只有少部分使用了synchronized关键字修饰,不能确保线程安全,适用于单线程的场景

包装类

什么是包装类?

在Java中,包装类(Wrapper Class)是一种用于将基本数据类型转换为对象的类。

为什么会有包装类?

Java是一个面向对象的编程语言,但是Java中的八种基本数据类型却是不面向对象的,无法直接参与面向对象的操作,于是Java提供了八种基本数据类型对应的包装类(封装类),使得基本数据类型的变量具有类的特征。有了类的特点,就可以调用类中的方法,Java才是真正的面向对象。

包装类分类

除了Integer和Character类以外,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可

基本数据类型占用存储空间(字节)默认值包装类
byte10Byte
short20Short
int40Integer
long80LLong
float40.0fFloat
double80.0dDouble
char2‘\u0000’(空字符)Character
boolean1falseBoolean

类型转换

  • 自动装箱:基本数据类型-->包装类,将 基本数据类型 封装为对象类型,来符合Java的面向对象的思想。
  • 自动拆箱:包装类-->基本数据类型,是指包装类对象赋给一个相对应基本数据类型的变量

自动装箱

基本数据类型–>包装类(装箱)

1
2
3
4
5
6
7
8
9
10
// 通过包装类的构造器实现
int num = 500;
Integer newNum = new Integer(num);
// 通过Integer类提供的values静态方法
Integer num = Integer.values(0);
// 自动装箱
int a = 13;
Integer b = a;
boolean c = true;
Boolean d = c;

基本数据类型、包装类–>字符串(装箱)

1
2
3
4
// 调用字符串重载的valueOf()方法
String str = String.valueOf(1.23f);
// 连接运算
String intStr = 5 + "";

自动拆箱

包装类–>基本数据类型(拆箱)

1
2
3
4
5
6
// 调用包装类的.xxxValue()方法
Integer a = new Integer(12);
int b = a.intValue();
// 自动拆箱
Integer a = 13;
int b = a;

字符串–>基本数据类型(拆箱)

1
2
3
4
// 通过包装类的构造器实现
int i = new Integer("123");
// 通过包装类的parseXxx(String s)静态方法
int num = Integer.parseInt("123");

包装类型的缓存机制

(1)Java 包装类的缓存机制,是在Java 5中引入的一个有助于节省内存、提高性能的功能

(2)Java 基本类型除了Float 和 Double 外,其他包装数据类型都使用了缓存机制来提升性能

(3)缓存机制实现方式是在类初始化时,提前创建好会频繁使用的包装类对象,当需要使用某个包装类的对象时,如果该对象包装的值在缓存的范围内,就返回缓存的对象,否则就创建新的对象并返回。

(4)缓存机制只有在自动装箱时有效,使用构造函数创建对象不适用,因为每次new会创建新的对象

(5)自动装箱会从缓存池中取对象,两种浮点数类型的包装类Float,Double并没有实现常量池技术,在其他包装类中,包装类Byte,Short,Integer,Long 默认创建了区间为 [-128, 127] 的缓存池缓存数据,包装类Character 创建了区间在 [0,127] 范围的缓存池缓存数据,包装类Boolean 直接返回 True or False。缓存的基本数据类型值的范围如下

基本数据类型包装类缓存范围
byteByte[-128, 127]
shortShort[-128, 127]
charCharacter[0,127]
intInteger[-128, 127]
longLong[-128, 127]
floatFloat无
doubleDouble无
booleanBooleanTrue、False

(6)包装类缓存原理:使用自动装箱将基本类型转为封装类对象这个过程其实底层实现是调用封装类的valueOf方法

1
2
3
4
5
6
7
// Integer包装类中的valueOf(int i)方法
public static Integer valueOf(int i) {
// 如果 IntegerCache.low <= i <= IntegerCache.high,就从IntegerCache中获取对象,否则创建一个新的Integer对象
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}

IntegerCache是Integer类中定义的一个private static的内部类

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
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];

static {
// high value may be configured by property(高值可以通过属性配置)
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE(最大数组大小为Integer.MAX_VALUE)
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.(若该属性不能被解析为int类型,则忽略)
}
}
high = h;

cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);

// range [-128, 127] must be interned (JLS7 5.1.7)
assert Integer.IntegerCache.high >= 127;
}

private IntegerCache() {}
}

数值处理类

BigInteger类

(1)Integer包装类能存储的最大值是有限的。如果要表示再大的整数,不管是基本数据类型还是包装类都无能为力,更不用说运算了。

(2)java.math包下的BigInteger用于处理任意精度的整数,可以对超过Integer范围内的数据进行运算。

(3)BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。

(4)BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、 位操作以及一些其他操作。

(5)成员方法

方法简介
add(BigInteger val)加法
subtract(BigInteger val)减法
multiply(BigInteger val)乘法
divide(BigInteger val)除法
divideAndRemainder(BigInteger val)带余除法,返回两个BigInteger,第一个是商,第二个是余数
pow(int exponet)进行取参数的 exponet 次方操作
negate()取相反数
shiftLegt(int n)将数字左移 n 位,如果 n 为负数,做右移操作
shiftRight(int n)将数字右移 n 位,如果 n 为负数,做左移操作
compareTo(BigInteger val)做数字比较操作
max(BigInteger val)返回较大的数值

BigDecimal类

(1)一般的float类型和double可以用来做科学计算或工程计算,但由于在运算的时候,float类型和double很容易丢失精度,在商业计算中, 要求数字精度比较高,为了能精确的表示、计算浮点数,Java提供了BigDecimal类

(2)BigDecimal用于处理任意精度的十进制浮点数,可以解决浮点型数据运算数据丢失问题,在金融行业可能会使用到该类

(3)成员方法

方法简介
add(BigDecimal value);加法
subtract(BigDecimal value);减法
multiply(BigDecimal value);乘法
divide(BigDecimal value);除法
divide(BigDecimal divisor, int scale, int roundingMode)第一参数表示除数, 第二个参数表示小数点后保留位数,第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式

(4)舍入模式介绍

模式简介
ROUND_UP向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一
ROUND_DOWN向接近零的方向舍入。舍弃非零部分,同时不会非零舍弃部分相邻的一位数字加一,采取截取行为
ROUND_CEILING向正无穷的方向舍入。如果为正数,舍入结果同ROUND_UP一致;如果为负数,舍入结果同ROUND_DOWN一致。注意:此模式不会减少数值大小
ROUND_FLOOR向负无穷的方向舍入。如果为正数,舍入结果同ROUND_DOWN一致;如果为负数,舍入结果同ROUND_UP一致。注意:此模式不会增加数值大小
ROUND_HALF_UP四舍五入
ROUND_HALF_DOWN五舍六入
ROUND_HALF_EVEN向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则相邻的偶数舍入。如果舍弃部分左边的数字奇数,则舍入行为与ROUND_HALF_UP相同,如果为偶数,则舍入行为与ROUND_HALF_DOWN相同。注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况,如果前一位为奇数,则入位,否则舍去
ROUND_UNNECESSARY断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException

Math类

java.lang.Math提供了一系列静态方法用于科学计算,其方法的参数和返回 值类型一般为double型

方法简介
abs绝对值
acos,asin,atan,cos,sin,tan三角函数
sqrt平方根
pow(double a,doble b)a的b次幂
log自然对数
expe为底指数
max(double a,double b)最大数
min(double a,double b)最小数
random()返回0.0到1.0的随机数
long round(double a)double型数据a转换为long型(四舍五入)
toDegrees(double angrad)弧度—>角度
toRadians(double angdeg)角度—>弧度
1
2
3
4
System.out.println(Math.max(10,2));//获得最大值
System.out.println(Math.min(10, 5));//获得最小值
System.out.println(Math.abs(-20));//获得绝对值
System.out.println(Math.random());//生成0-1的随机数

Random类

Random 类是 Java 中提供的一个用于生成随机数的工具类。它可以生成不同类型的随机数,包括整数、浮点数和布尔值

生成随机整数

1
2
3
4
5
6
7
8
9
public class RandomExample {
public static void main(String[] args) {
Random random = new Random();

// 生成一个[0, 100)范围内的随机整数
int randomNumber = random.nextInt(100);
System.out.println("随机整数:" + randomNumber);
}
}

生成随机浮点数

1
2
3
4
5
6
7
8
9
public class RandomExample {
public static void main(String[] args) {
Random random = new Random();

// 生成一个[0.0, 1.0)范围内的随机浮点数
double randomDouble = random.nextDouble();
System.out.println("随机浮点数:" + randomDouble);
}
}

生成随机布尔值

1
2
3
4
5
6
7
8
9
public class RandomExample {
public static void main(String[] args) {
Random random = new Random();

// 生成一个随机布尔值
boolean randomBoolean = random.nextBoolean();
System.out.println("随机布尔值:" + randomBoolean);
}
}

日期时间类

JDK8前的日期时间类

System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差。

1
2
long time=System.currentTimeMillis();
System.out.println(time);

Date类

(1)java.util.Date类

构造器简介
Date()使用无参构造器创建的对象可以获取本地当前时间。
Date(long date)创建指定毫秒数的Date对象
1
2
3
4
5
6
7
8
9
10
@Test
public void test() {
Date date1 = new Date(); // 使用空参构造创建java.util.Date类对象,打印当前的时间
System.out.println(date1.toString());
System.out.println(date1.getTime()); // 把日期对象转换成对应的时间毫秒值
System.out.println(date1.toLocaleString());// 根据本地格式转换日期对象

Date date2 = new Date(123456L);// 使用有参构造创建java.util.Date类对象,把毫秒值转成日期对象
System.out.println(date2.toString());
}

(2)java.sql.Date是java.util.Date类的子类,对应着数据库中的日期类型的变量

1
2
3
4
5
@Test
public void test() {
java.sql.Date date = new java.sql.Date(123456L);
System.out.println(date);
}

DateFormat类

(1)java.text.DateFormat 是日期/时间格式化子类的抽象类,通过这个类可以完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换

转换简介
格式化(日期 -> 文本)按照指定的格式,从Date对象转换为String对象。
解析(文本-> 日期)按照指定的格式,从String对象转换为Date对象。

(2)DateFormat类是一个抽象类,无法直接创建对象使用,可以使用DateFormat类的子类SimpleDateFormat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void test() throws ParseException {
// 使用无参构造实例化SimpleDateFormat
SimpleDateFormat dateFormat1 = new SimpleDateFormat();
// 格式化:日期—>字符串
String format1 = dateFormat1.format(new Date());
System.out.println(format1);
// 解析:字符串—>日期
Date parse1 = dateFormat1.parse("20-1-6 下午1:00");
System.out.println(parse1);

// 使用无参构造实例化SimpleDateFormat,按照指定的方式格式化和解析
SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
// 格式化:日期—>字符串
String format2 = dateFormat2.format(new Date());
System.out.println(format2);
// 解析:字符串—>日期
Date parse2 = dateFormat2.parse("2020-01-06 01:34:09");
System.out.println(parse2);
}

Calendar类

(1)java.util.Calendar是日历类,在Date后出现,替换掉了许多Date的方法。该类是一个抽象基类,将所有可能用到的时间信息封装为静态成员变量,方便获取,主用用于完成日期字段之间相互操作的功能。

(2)常用方法

常用方法简介
public int get(int field)返回给定日历字段的值。
public void set(int field, int value)将给定的日历字段设置为给定值。
public abstract void add(int field, int amount)根据日历的规则,为给定的日历字段添加或减去指定的时间量。
public Date getTime()返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Date对象。

(3)Calendar类中提供很多成员常量,代表给定的日历字段

字段值含义
YEAR年
MONTH月(从0开始,可以+1使用
DAY_OF_MONTH月中的天(几号)
HOUR时(12小时制)
HOUR_OF_DAY时(24小时制)
MINUTE分
SECOND秒
DAY_OF_WEEK周中的天(周几,周日为1,可以-1使用)

JDK8后的日期时间类

针对JDK8以前的日期类设计不足,比如不支持时区、线程不安全等,JDK8引入了java.time包来作为新的日期处理类

属性含义
Instant代表的是时间相当于Date
LocalDate代表日期,比如2022-01-14
LocalTime代表时间,比如12:20:30
LocalDateTime代表具体时间,比如2022-01-14 12:20:30
ZonedDateTime代表一个包含时区的完整的日期时间
Period代表时间段
ZoneOffset代表时区偏移量,比如+8:00
Clock代表时钟,比如获取美国纽约的时间
DateTimeFormatter日期和字符串格式转换

LocalDate类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LocalDateExample {
public static void main(String[] args) {
// 获取当前日期
LocalDate currentDate = LocalDate.now();
System.out.println("当前日期: " + currentDate);

// 创建指定日期
LocalDate specificDate = LocalDate.of(2023, 7, 1);
System.out.println("指定的日期: " + specificDate);

// 获取日期的年、月、日
int year = specificDate.getYear();
int month = specificDate.getMonthValue();
int day = specificDate.getDayOfMonth();
System.out.println("年: " + year + " 月: " + month + " 日: " + day);

// 日期增加一天
LocalDate nextDay = specificDate.plusDays(1);
System.out.println("下一天日期: " + nextDay);
}
}

LocalTime类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class LocalTimeExample {
public static void main(String[] args) {
// 获取当前时间
LocalTime currentTime = LocalTime.now();
System.out.println("当前时间: " + currentTime);

// 创建指定时间
LocalTime specificTime = LocalTime.of(9, 30, 0);
System.out.println("指定的时间: " + specificTime);

// 获取时间的小时、分钟、秒
int hour = specificTime.getHour();
int minute = specificTime.getMinute();
int second = specificTime.getSecond();
System.out.println("小时: " + hour + " 分钟: " + minute + " 秒: " + second);

// 时间增加一小时
LocalTime nextHour = specificTime.plusHours(1);
System.out.println("下一个小时时间: " + nextHour);
}
}

LocalDateTime类

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 class LocalDateTimeExample {
public static void main(String[] args) {
// 获取当前日期和时间
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("当前日期和时间: " + currentDateTime);

// 创建指定日期和时间
LocalDateTime specificDateTime = LocalDateTime.of(2023, 7, 1, 9, 30, 0);
System.out.println("指定的日期和时间: " + specificDateTime);

// 获取日期和时间的年、月、日、小时、分钟、秒
int year = specificDateTime.getYear();
int month = specificDateTime.getMonthValue();
int day = specificDateTime.getDayOfMonth();
int hour = specificDateTime.getHour();
int minute = specificDateTime.getMinute();
int second = specificDateTime.getSecond();
System.out.println("年: " + year + " 月: " + month + " 日: " + day + " 小时: " + hour + " 分钟: " + minute + " 秒: " + second);

// 日期和时间增加一天
LocalDateTime nextDay = specificDateTime.plusDays(1);
System.out.println("下一天日期和时间: " + nextDay);
}
}

Instant类

(1)时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。

(2)时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01 日08时00分00秒)起至现在的总秒数。

(3)常用方法

常用方法描述
now()获取本初子午线对应的标准时间
atOffset()添加时间的偏移量
toEpochMilli()获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数
ofEpochMilli()通过给定的毫秒数,获取Instant示例

(4)代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//now():获取本初子午线对应的标准时间
Instant time1 = Instant.now();
System.out.println(time1);

//atOffset():添加时间的偏移量
OffsetDateTime time2 = time1.atOffset(ZoneOffset.ofHours(8));
System.out.println(time2);

//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数
long time3 = time1.toEpochMilli();
System.out.println(time3);

//ofEpochMilli():通过给定的毫秒数,获取Instant示例
Instant time4 = time1.ofEpochMilli(1641470139866L);
System.out.println(time4);

DateTimeFormatter类

(1)用来格式化与解析日期或时间,类似SimpleDateFormat,不仅可以将日期、时间对象格式化成字符串,还能将特定格式的字符串解析成日期、时间对象

(2)获得DateTimeFormatter对象的方式

方式简介
预定义的标准格式使用静态常量创建DateTimeFormatter格式器,在DateTimeFormatter类中包含大量的静态常量,如BASIC_ISO_DATE、ISO_LOCAL_DATE、ISO_LOCAL_TIME等,通过这些静态常量都可以获取DateTimeFormatter实例
本地化相关的格式使用不同风格的枚举值来创建DateTimeFormatter格式器,在FormatStyle类中定义了FULL、LONG、MEDIUM和SHORT四个枚举值,它们表示日期和时间的不同风
自定义的格式根据模式字符串创建DateTimeFormatter格式器

(3)代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
public void test() {
LocalDateTime date = LocalDateTime.now();
//(1)使用常量创建DateTimeFormatter
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
System.out.println(dtf1.format(date));// 格式化:日期—>字符串
TemporalAccessor parse1 = dtf1.parse("2020-01-06T20:13:08.006");//解析:字符串—>日期
System.out.println(parse1);

//(2)使用MEDIUM类型风格的DateTimeFormatter
DateTimeFormatter dtf2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
System.out.println(dtf2.format(date));// 格式化:日期—>字符串
TemporalAccessor parse2 = dtf2.parse("2020-01-06T20:13:08.006");//解析:字符串—>日期
System.out.println(parse2);

//(3)根据模式字符串来创建DateTimeFormatter格式器
DateTimeFormatter dtf3 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(dtf3.format(date));
TemporalAccessor parse3 = dtf3.parse("2020-01-06T20:13:08.006");//解析:字符串—>日期
System.out.println(parse3);
}

系统相关类

System类简介

(1)System类代表系统,位于java.lang包,系统级的很多属性和控制方法都放置在该类的内部。

(2)由于该类的构造器是private的,所以无法创建该类的对象,也就是无法实例化该类。其内部的成员变量和成员方法都是static的,所以也可以很方便的进行调用

(3)System类内部包含in、out和err三个成员变量,分别代表标准输入流(键盘输入),标准输出流(显示器)和标准错误输出流(显示器)。

System类成员方法

方法简介
currentTimeMillis()返回当前的计算机时间
exit(int status)退出程序,其中status的值为0代表正常退出,非零代表异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。
gc()请求系统进行垃圾回收
getProperty(String key)获得系统中属性名为key的属性对应的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// java运行时的环境版本
String javaVersion = System.getProperty("java.version");
System.out.println(javaVersion);
// java的安装目录
String javaHome = System.getProperty("java.home");
System.out.println(javaHome);
// 操作系统的名称
String osName = System.getProperty("os.name");
System.out.println(osName);
// 操作系统的版本
String osVersion = System.getProperty("os.version");
System.out.println(osVersion);
// 用户的账户名称
String userName = System.getProperty("user.name");
System.out.println(userName);
// 用户的主目录
String userHome = System.getProperty("user.home");
System.out.println(userHome);
// 用户的当前工作目录
String userDir = System.getProperty("user.dir");
System.out.println(userDir);

Scanner类

Scanner 类是 Java 中提供的一个用于读取用户输入的工具类。它可以从不同的数据源(如键盘或文件)中获取用户输入,并将输入内容转换为程序中需要的不同类型的变量

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
public class ScannerExample {
public static void main(String[] args) {
// 创建一个 Scanner 对象,用于从标准输入中读取用户输入
Scanner scanner = new Scanner(System.in);

// 提示用户输入一个整数
System.out.print("请输入一个整数:");

// 使用 nextInt() 方法从键盘读取一个整数,并将其存储在变量 num 中
int num = scanner.nextInt();

// 输出用户输入的整数
System.out.println("您输入的整数是:" + num);

// 提示用户输入一个字符串
System.out.print("请输入一个字符串:");

// 使用 next() 方法从键盘读取一个字符串,并将其存储在变量 str 中
String str = scanner.next();

// 输出用户输入的字符串
System.out.println("您输入的字符串是:" + str);

// 关闭 Scanner 对象,释放资源
scanner.close();
}
}