Java进阶_01_常用类


本文介绍Java中常用的几个类。

1. 数组

1.1 数组简介

Java中的数组属于引用数据类型,不是基本数据类型,它的父类是Object。因为其是引用数据类型,所以数组对象存储在堆内存中。另外数组中如果存储的是引用数据类型数据的话,实际上存储的是其引用(内存地址)。数组一旦创建,长度不可变。

数组分为一维数组、二维数组、三维数组、多维数组…。所有的数组对象都有length属性,用来获取数组中元素的个数。Java中的数组要求数组中元素的类型统一,比如int类型数组只能存储int类型。数组中首元素的内存地址作为整个数组对象的内存地址。可以把int[]整体看做是一个具体的引用数据类型。

1.2 数组语法

和基本数据类型一样,数组作为引用数据类型也需要声明和初始化。

声明数组的语法格式如下:

1
2
3
4
5
6
7
8
数据类型[] 数组名;
// 也可 数据类型 数组名[]; // 不推荐这不是Java风格

// 如下
int[] array1;
double[] array2;
boolean[] array3;
String[] array4;

初始化数组的语法格式如下:

1
2
3
4
5
// 静态初始化一维数组
int[] array = {100, 2100, 300, 55}; // 元素个数就是大括号中的个数

// 动态初始化一维数组
int[] array = new int[5]; // 这里的5表示数组的元素个数,数组元素默认为0.

静态初始化例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ArrayTest01 {

public static void main(String[] args) {
int[] array_01 = {10, 20, 55, 67, 87};

System.out.println(array_01[0]);
System.out.println(array_01[1]);

System.out.println("-----------");

System.out.println(array_01.length);

System.out.println("-----------");

for(int i=0; i< array_01.length; i++){
System.out.println(array_01[i]);
}

System.out.println("-----------");
array_01[2] += 55;
System.out.println(array_01[2]);
}
}

动态初始化例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ArrayTest02 {
public static void main(String[] args) {
int[] array_01 = new int[5];

for(int i=0; i<array_01.length; i++){
System.out.println(array_01[i]);
}

String[] strs = new String[5];

for(int i=0; i< strs.length; i++){
System.out.println(strs[i]);
}

}
}

1.3 数组扩容

在Java语言中,数组长度一旦确定不可变,那么数组满了需要扩容,该怎么做呢?先创建一个大容量数组,然后将小容量数组中的数据一个个拷贝到大数组中。数组拷贝方法system.arraycopy()

1
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

将src数组中srcPos位置开始,拷贝长度为length,将其拷贝到数组dest中,起始位置是destPos

所以数组扩容效率较低,因为涉及到拷贝的问题。因此在以后的开发中要注意:尽可能少的进行数组拷贝,可以在创建数组对象的时候预估计一下多长合适,最好预估准确。

1.4 二维数组

上面提到的数组是一维数组,数组元素可以存储引用数据类型,那么当然也可以存储一维数组。这样形成的就是二维数组,二维数组其实就是特殊的一维数组。同理,三维数组就是特殊的二维数组。

其初始化语法如下:

1
2
3
4
5
// 静态初始化
int[][] array = {{1,2,3}, {4,5,6}, {7,8,910}, {0,1}}

// 动态初始化
int[][] array = new int[4][5];

其中第一个中括号用于索引外层元素,第二个中括号用于索引指定外层数组元素中的内层数组元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ArrayTest04 {
public static void main(String[] args) {
int[][] array_01= { {1,2,3,4,5},
{6,5,4,3},
{2,10,6},
};

for (int i = 0; i < array_01.length; i++) {
for (int j = 0; j < array_01[i].length; j++) {
System.out.print(array_01[i][j] + ",");
}

System.out.println();
}
}
}

2. String类

2.1 String基本介绍

该类是java.lang.String类,表示字符串,属于引用数据类型。在Java中随便使用双引号括起来的都是String对象,例如“abc”、“hello world”等等,这些都是String对象。Java中规定,双引号括起来的字符串是不可变的,也就是说“abc”自出生到最终死亡都不可变,这个对象不会变成“abcd”。在JDK中双引号括起来的字符串,例如“abc”、“def”都是直接存储在方法区中的“字符串常量池”当中的,(感觉有点像字面值),注意,垃圾回收器是不会释放常量的。

为什么SUN公司把字符串存储在一个“字符串常量池”当中呢?因为字符串在实际的开发中使用太频繁。为了执行效率,所以把字符串放到了方法区的字符串常量池当中。因为字符串无法改变,所以后续用到相同字符串的时候,直接用该地址即可,无需再次创建。如下代码所示,字符串拼接会产生新的字符串对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class StringTest01 {

public static void main(String[] args) {
// 注意,下面的两个“abcdef”是同一个String对象,最终生成三个对象,“abcdef”,"xy","abcdefxy"。
String s1 = "abcdef";
String s2 = "abcdef" + "xy";

// 分析:这是使用new的方式创建的字符串对象。这个代码的“xy”是从哪里来的?
// 凡是双引号括起来的都在字符串常量池中有一份。
// new对象的时候一定在堆内存当中开辟空间。
String s3 = new String("xy");
}
}

adv_001.png (902×456) (gitee.io)

所以String对象的两种创建形式在内存中是不一样的,一种是直接指向字符串常量池,另一种是指向堆内存,然后堆内存再指向字符串常量池。所以,用双等号“==”来判断两个String对象的内容是否相等是不保险的,如果对象是在堆中,则二者内存地址不一样,就会被认为不相等,一般用String类重写后的equals方法来比较二者是否相等。

2.2 String构造方法

最常用的构造方法有如下几种:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 直接将字符串赋值
String s1 = "hello world!";

// 2. 传入byte数组,将值作为ASCII码转换为对应的字符
byte[] bytes = {97, 98, 99};
String s2 = new String(bytes);

// 3. 将数组特定下标范围作为参数传入,参数分别是:数组、起始位置、长度
String s3 = new String(bytes, 1, 2);

// 4. char数组
char[] chars = {'a', 'b', 'c'};
String s4 = new String(chars);
String s5 = new String(chars, 1, 2);

// 5. 直接传入字符串
String s6 = new String("abc");

2.3 String常用方法

方法 功能
public char charAt(int index) 返回字符串中指定位置的字符
public int compareTo(String anotherString) 按字典顺序比较两个字符串,返回0表示两个字符串相等,<0表示自身小于参数字符串,>0表示自身大于参数字符串(以ASCII码序号比较)
public boolean contains(CharSequense s) 判断字符串是否包含参数中的字符串
public boolean endsWith(String suffix) 判断字符串是否以某个子串结尾。(同理,肯定有startsWith()方法)
public boolean equals(Object anObject) 判断两个字符串是否相等(值是否相等,并不是内存地址)
public boolean equalsIgnoreCase(String anotherString) 判断两个字符串是否相等,忽略大小写。
public byte[] getBytes() 将字符串对象转换成byte数组。
public int indexOf(String str) 返回参数字符串在自身字符串中第一次出现的索引。
public boolean isEmpty() 判断某个字符串是否为空。
public int length() 返回字符串的长度。
public String replace(CharSequence target, CharSequence replacement) 将自身字符串中的target子串替换为replacement子串。
public String[] split(String regex) 将自身字符串按照正则表达式或指定字符串分割,返回String类型数组
public String substring(int beginIndex, int endIndex) 截取自身字符串,下标从beginIndex,到endIndex,右开区间。
public char[] toCharArray() 将自身字符串转换为一个字符数组。
public String trim() 去掉字符串前后空白。
public static String valueOf() 静态方法,将参数“非字符串”转换为字符串,参数可以是任何数据类型,包括引用数据类型,调用其toString()方法。

注意,equals()方法和compareTo()方法有什么区别呢?本质上没有区别,都是比较的是字符串的值,无非就是返回结果的不同,或者说compareTo可以比较出具体的大小。另外,似乎旧版本的JDK,equals()方法内部就是调用的是compareTo()方法。

注意,valueOf()方法就是将其他数据类型的数据转换为字符串类型数据,此时可以查看System.out.println()方法的源码,它就是调用了valueOf方法。实际上显示在控制台上的东西都是字符串。

2.4 简单案例

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
public class StringTest01 {

public static void main(String[] args) {
String s1 = "abcdef";
String s2 = "abcdef" + "xy";

System.out.println("构造方法:");
String s3 = "hello world";
System.out.println(s3);

byte[] bytes = {97, 98, 99};
String s4 = new String(bytes);
System.out.println(s4);

String s5 = new String(bytes, 1, 2);
System.out.println(s5);

char[] chars = {'a', 'b', 'c'};
String s6 = new String(chars);
System.out.println(s6);

System.out.println("charAt()方法:");
System.out.println("我是中国人".charAt(2));

System.out.println("compareTo()方法:");
System.out.println("xyz".compareTo("yxz"));

System.out.println("contains()方法:");
System.out.println("helloworld.java".contains(".java"));

System.out.println("endsWith()方法:");
System.out.println("helloworld.java".contains(".java"));

// String aa = "qwer";
// String bb = "qwer";
// System.out.println(aa.equals(bb));
// System.out.println(aa.compareTo(bb));

System.out.println("equals()方法:");
System.out.println("asdf".equals("asdf"));

System.out.println("equalsIgnoreCase()方法:");
System.out.println("asdf".equalsIgnoreCase("ASdf"));

System.out.println("getBytes()方法:");
byte[] bytes1 = "asdf".getBytes();
for (int i = 0; i < bytes1.length; i++) {
System.out.print(bytes1[i] + " ");
}
System.out.println();

System.out.println("indexOf()方法:");
System.out.println("asdfqwer".indexOf("df"));

System.out.println("isEmpty()方法:");
System.out.println("".isEmpty());
System.out.println("asdf".isEmpty());

System.out.println("length()方法:");
System.out.println("asdf".length());

System.out.println("replace()方法:");
System.out.println("asdfqwerzxcfv".replace("qwer", "vcbn"));

System.out.println("split()方法:");
String[] aaaa = "1980-12-12".split("-");
for (int i = 0; i < aaaa.length; i++) {
System.out.print(aaaa[i] + " ");
}
System.out.println();

System.out.println("substring()方法:");
System.out.println("http://www.baidu.com".substring(11, 16));

System.out.println("toCharArray()方法:");
char[] chars1 = "hello world".toCharArray();
for (int i = 0; i < chars1.length; i++) {
System.out.print(chars1[i] + " ");
}
System.out.println();

System.out.println("trim()方法:");
System.out.println(" hello world ".trim());

System.out.println("valueOf()方法:");
System.out.println(String.valueOf(123));
System.out.println(String.valueOf('c'));
System.out.println(String.valueOf(true));

}
}

3. StringBuffer类

上面提到过,字符串一旦在字符串常量池中出现,那么就不会改变,此时如果拼接字符串,就会生成新的字符串常量,这样会占用大量的方法区内存,造成内存空间的浪费。

所以如果需要大量的字符串拼接操作,此时可以用Java提供的StringBuffer类和StringBuilder类,二者均在java.lang包下。

其中StringBuffer是线程安全的,StringBuilder是线程不安全的。是否线程安全参考后续的多线程编程。

3.1 StringBuffer类

3.1.1 构造方法

构造方法名 作用
StringBuffer() 构造一个其中不带字符的字符串缓冲区,初始容量为16个字符。
StringBuffer(CharSequence seq) 构造一个字符串缓冲区,包含指定的seq字符序列。
StringBuffer(int capacity) 构造一个不带字符,但具有指定初始容量的字符串缓冲区。
StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串。

通过源码可以发现,底层开辟了byte数组,后续将字符对应的ASCII码保存到数组中。并且当数组存储满了之后,可以自动扩容,而String类底层也是数组,但是final修饰,即不可变,无论是长度还是内容

注意,StringBuffer节省空间只是节省的是中间产物,比如”a”连续追加拼接到“z”,最终生成的对象是26个单个字符和长度为26的字符串,而String类则除了上述对象,还会生成“ab”、“abc”等类似的中间产物对象。

另外,在创建StringBuffer的时候尽可能给定一个初始化容量,最好减少底层的扩容次数,预估一下,给一个大一些的初始化容量。

注意,尽管会初始化长度,但是这个长度并不是实际字符串的长度。

3.1.2 常用方法

  • apend()

    主要用到的就是这个方法,用于字符串的拼接,并且,如果初始化设置的容量不够就会自动进行扩容。

3.2 StringBuilder类

和StringBuilder类类似,只不过该类中的方法没有synchronized关键字修饰,线程不安全。

3.3 简单案例

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
public class StringBufferTest01 {
public static void main(String[] args) {

System.out.println("StringBuffer: ");
// 默认容量为16
StringBuffer sb = new StringBuffer();

sb.append("a");
sb.append("b");
sb.append("d");
sb.append("e");
sb.append("h");
sb.append("jk");

System.out.println(sb);
System.out.println(sb.length());

System.out.println("StringBuilder: ");
StringBuilder sbPlus = new StringBuilder();
sbPlus.append("Hello world");
System.out.println(sbPlus);
System.out.println(sbPlus.length());

}
}

4. Object类

4.1 Object类概述

Object类是Java类库所有类的根类,这个类中的方法需要研究一下,因为这些方法是所有子类通用的,任何一个类都默认继承Object,即使没有直接继承,最终也会间接继承。

4.2 Object常用方法

4.2.1 toString()

源码如下:

1
2
3
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

可以看到,源码方法的实现默认是类名@对象内存地址的十六进制。那么该方法的作用是什么呢?通过调用这个方法可以将一个“Java对象”转换成“字符串表示形式”。建议所有子类都重写这个方法,自定义想要显示的内容。

另外,如果System.out.println()中的参数是一个引用,那么就会自动调用该引用的toString()方法。

4.2.2 equals()

源码如下:

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

可以看到,源码方法的实现默认是用双等号,比较对象(引用数据类型)的内存地址是否相等。一般情况下,我们判断对象是否相等指的是其变量的值是否相等,并不是判断内存地址,所以Object中的equals()方法无法满足要求,一般情况下子类需要重写该方法,即自定义对象相等的判断标准。

注意,为了后续哈希表以及其他数据结构中的需要,如果一个类的equals方法重写了,那么hashCode()方法必须重写。并且equals方法返回如果是true,hashCode()方法返回的值必须一样。

4.2.3 finalize()

源码如下:

1
protected void finalize() throws Throwable { }

注意,这个方法protected关键字修饰的,只有一个方法体,里面没有代码。这个方法不需要程序员手动调用,JVM的垃圾回收器负责调用这个方法

finalize()方法实际上是SUN公司为Java程序员准备的一个时机:垃圾回收时机。如果希望在对象销毁时机执行一段代码的话,这段代码要写到finalize()方法体当中。有点类似静态代码块,在指定实际执行执行的代码片段。

另外:System.gc()方法可以建议启动垃圾回收器,即不让垃圾回收器按照原始设定启动,一定程序上建议它启动,即启动概率高了一些。

不过JDK9开始似乎已经弃用这个方法。

4.2.4 hashCode()

源码如下:

1
public native int hashCode();

该方法不是抽象方法,有native修饰,底层调用C++程序。作用就是将一个Java对象的内存地址经过哈希算法,得到一个值。所以hashCode()方法的执行结果可以等同看做一个Java对象的内存地址。

4.2.5 clone()

源码如下:

1
protected native Object clone() throws CloneNotSupportedException;

底层调用C++,用于克隆对象,分为深克隆和浅克隆,此处略。

4.3 简单案例

简单案例如下

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
class Animal{
private int height;
private int weight;

public Animal(){}

public Animal(int height, int weight){
this.height = height;
this.weight = weight;
}

public boolean equals(Object obj){
if(obj == null || !(obj instanceof Animal)){
return false;
}

if(this == obj){
return false;
}

Animal animal = (Animal)obj;
if(this.height == animal.height && this.weight == animal.weight){
return true;
}

return false;
}

public String toString(){
return "height: " + this.height + ", weight: " + this.weight;
}

protected void finalize() throws Throwable{
System.out.println(this.hashCode() + "即将被销毁");
}
}

public class ObjectTest01 {

public static void main(String[] args) {
Object obj;

System.out.println();

String sss = "asdf";

System.out.println("测试finalize()方法:");
for (int i = 0; i < 10000000; i++) {
Animal a = new Animal();

// 将其为null,这样堆内存中没有引用指向该对象,那么就被垃圾回收器回收,执行finalize()方法。
a = null;
}

System.out.println("测试hashCode()方法:");
Animal aa = new Animal();
System.out.println(aa.hashCode());

System.out.println("测试toString()方法:");
Animal bb = new Animal(123, 234);
System.out.println(bb); // 直接输出引用,会自动调用所指对象的toString()方法。
System.out.println(bb.toString());

System.out.println("测试equals()方法:");
Animal cc = new Animal(123, 234);
System.out.println(cc.equals(bb));
}
}

5. Scanner类

前面提到过,程序本质上说是对数据的处理。而前面几篇文章中用到的数据都是我们在编写的时候提前写好的,这些数据是“静态”的,很多时候需要用户输入一些数据,那么用户如何输入数据呢,程序如何获取到这些数据呢?

这里简单介绍一个类,用于获取用户控制台的输入,先记住基本语法,具体的原理可参考Java进阶-05-IO流。该类是java.util.Scanner类,创建对象时的参数为System.in,主要有nextInt()hasNext()next()nextLine()等方法,注意,回车符号可能会存储在缓冲区中,所以上述方法尽量单独测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.Scanner;

public class ScannerTest01 {

public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
System.out.print("请输入整数:");
int argsByUser_01 = scan.nextInt();
System.out.println("int:" + argsByUser_01);

/*
String argsByUser_02 = scan.next();
System.out.println("String:" + argsByUser_02);
*/

/*
String argsByUser_03 = scan.nextLine();
System.out.println("String Line:" + argsByUser_03);
*/
}
}

6. Arrays类

这个类是数组工具类,里面提供了针对数组的很多方法,比如快速排序、冒泡排序、二分查找、计算哈希值等等,方法基本上都是静态的,可以直接调用。主要用到的方法是查找和排序

简单代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.Arrays;

public class ArraysTest01 {

public static void main(String[] args) {
int[] array = {23, 123, 0, 6, 1, 534};

Arrays.sort(array);

for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
System.out.println("============");

int index = Arrays.binarySearch(array, 123);
System.out.println(index);
}
}

7. 包装类

7.1 包装类基本介绍

Java为8种基本数据类型又对应准备了8种包装类型。8种包装类型属于引用数据类型,父类是Object。有了基本数据类型,为什么要提供对应的包装类呢?

有时候有这种需求,方法的参数是Object类型,这种方法参数实现了多态,可以传入任何引用数据类型,但是此时却不能传入基本数据类型(现在有了自动装箱功能,所以目前测试不出来),这样可能就不会满足需求,基本数据类型没有父类。包装类的作用就是为了方便编程。

那么此时怎么做呢?可以将基本数据类型封装成引用数据类型,即创建一个类,里面的属性是某一个基本数据类型变量,继承Object类。Java中提供了这种封装基本数据类型的包装类。基本数据类型和包装类型的对应如下所示:

基本数据类型 包装类型
byte java.lang.Byte(父类Number)
short java.lang.Short(父类Number)
int java.lang.Integer(父类Number)
long java.lang.Long(父类Number)
float java.lang.Float(父类Number)
double java.lang.Double(父类Number)
boolean java.lang.Boolean(父类Object)
char java.lang.Character(父类Object)

八种包装类型中,6个类的父类是Number类,2个类的父类是Object。下面先介绍一下java.lang.Number

7.2 Number类

Number类是一个抽象类,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class Number implements Serializable {
private static final long serialVersionUID = -8742448824652078965L;

public Number() {
}

public abstract int intValue();

public abstract long longValue();

public abstract float floatValue();

public abstract double doubleValue();

public byte byteValue() {
return (byte)this.intValue();
}

public short shortValue() {
return (short)this.intValue();
}
}

提供的抽象方法可以看到,均是将数值转换指定的基本数据类型值,也可认为是将引用数据类型转换为基本数据类型。这里引出两个概念:

  • 装箱:将基本数据类型转换成引用数据类型。
  • 拆箱:将引用数据类型转换成基本数据类型。

所以,包装类构造方法用于装箱,其继承的Number类中的部分方法用于拆箱。但是有了自动装箱和自动拆箱,实际上Number中的方法就不需要显式调用了。

7.3 java.lang.Integer类

7.3.1 构造方法

该类的构造方法在JDK9之后弃用了,但是也可以用。除了构造方法,可以直接用对应的基本数据类型数值赋值该引用(自动装箱)。另外,包装类也提供了最大值最小值等常量。

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
      System.out.println("Integer类型:");
Integer intss = new Integer(12); // 手动装箱
Integer intsss = new Integer("123");
Integer ints = 12; // 自动装箱
System.out.println(intss);
System.out.println(ints);
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);

System.out.println("Double类型:");
Double doubless = new Double(3.23);
Double doubles = 3.23;
System.out.println(doubless);
System.out.println(doubles);
System.out.println(Double.MAX_VALUE);
System.out.println(Double.MIN_VALUE);

System.out.println("Boolean类型:");
Boolean booleanss = new Boolean(true);
Boolean booleans = true;
System.out.println(booleanss);
System.out.println(booleans);

System.out.println("Character类型:");
Character characterss = new Character('c');
Character characters = 'c';
System.out.println(characterss);
System.out.println(characters);
System.out.println(Character.MAX_VALUE);
System.out.println(Character.MIN_VALUE);

7.3.2 自动装箱和自动拆箱

在JDK1.5之后,对基本数据类型及其包装类支持自动装箱和自动拆箱了。

自动装箱指的将基本数据类型自动转换成包装类。自动拆箱指的是将包装类自动转换成基本数据类型。涉及到加减乘除等运算的时候会触发自动装箱和拆箱机制,双等号判断的时候JDK旧版本不会触发,最新版本可能会触发。

1
2
3
4
Integer x = 600;    // 自动装箱
int y = x; // 自动拆箱

int z = x + 1; // 自动拆箱,x转换成int,然后加法操作

注意,在[-128, 127]之间,这些数值用的比较多,所以Java为了提高程序的执行效率,将这些数值之间的所有包装对象提前创建好,放到了一个方法区的“整数型常量池”当中了,目的是只要这个区间的数据不需要再new了,和字符串常量池一样,直接取。所以此时双等号判断会为true,不要认为双等号这里采用了拆箱。

1
2
3
4
5
6
7
Integer aa = 134;
Integer bb = 134;
System.out.println(aa == bb); // false

Integer cc = 123;
Integer dd = 123;
System.out.println(cc == dd); // true

参考Integer类源码中的静态内部类,如下所示,自动创建 [-128, 127]之间的缓存对象。

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

private IntegerCache() {
}

static {
int h = 127;
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
int size;
if (integerCacheHighPropValue != null) {
try {
size = Integer.parseInt(integerCacheHighPropValue);
size = Math.max(size, 127);
h = Math.min(size, 2147483518);
} catch (NumberFormatException var6) {
}
}

high = h;
VM.initializeFromArchive(Integer.IntegerCache.class);
size = high - -128 + 1;
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = -128;

for(int k = 0; k < c.length; ++k) {
c[k] = new Integer(j++);
}

archivedCache = c;
}

cache = archivedCache;

assert high >= 127;

}
}

7.3.3 Integer常用方法

  1. public int intValue()

    手动拆箱,即返回Int类型的对应值。

  2. public static int parseInt(String s)

    将字符串转换成int类型,前提是字符串必须是数字型字符串,相当于python中的eval()函数。

  3. public static Integer valueOf(int i)

    将int类型数据转换成对应的Integer类型数据。

  4. public static Integer valueOf(String s)

    将String类型数据转换成对应的Integer类型数据。前提是String类型数据必须是对应的数值所形成的数据,如”1223”等可以转换成Integer类型的字符串。

7.3.4 String、int、Integer三种类型互相转换

adv_002.png (846×450) (gitee.io)

7.4 补充

Integer类型的相关方法如上所述,其他七种包装类型也是相类似的方法。一般情况下采用自动装箱即可。

8. 其他工具类

8.1 Date类

该类是Java提供的日期类,提供对日期的一些常见操作,该类全称是java.util.Date,可以获取当前日期等相关操作。

另外,由于日期类的输出和中国人日常格式不同,所以java.text.SimpleDateFormat类提供了对日期的格式操作,按照指定格式格式化日期对象并返回字符串。同时,该类也提供了将字符串转换成日期对象的方法。

System.currentTimeMillis()方法可以获取系统当前时间距离1970年1月1日之间的毫秒数,那么统计程序运行时长可以两次调用该方法,取差值即可。

8.2 Random类

java.util.Random类是Java提供的随机数类,提供随机数的相关操作。

8.3 enum关键字

java.lang.Enum类是Java提供的抽象类:枚举类enum关键字意味着所创建的类型都是java.lang.Enum类的子类。

枚举类型有什么用处呢?定义变量的时候,因为变量可以随意取值,也就是有无限种取值情况,但是有时候,我们其实只需要其中几种取值,那么此时,如果单纯的只定义这几种取值,比如value的取值只有0、1、2三种,有可能会写错,并且程序运行也没什么问题。可以限定value的取值,这时候就不会写错。

枚举就是一枚一枚可以列举出来的,才建议使用枚举类型,如果取值只有两种情况,建议使用布尔类型,超过两种并且还可以列举出来,建议使用枚举类型,比如颜色、四季、星期等等

和interface、class一样,作为关键字,如enum Result{},可以单独写入一个文件。枚举编译之后也生成class文件,枚举也是一种引用数据类型,枚举中的每一个值可以看做是常量

注意,不能实例化,只能采用类名.常量的形式调用。

语法如下所示:

1
2
3
enum 枚举类型名{
枚举值1, 枚举值2, 枚举值3...
}

可以将枚举看成一个类,枚举值就是其中的静态常量,例子如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class EnumTest01 {

public static void main(String[] args) {
System.out.println(Result.SUCCESS);
System.out.println(Result.FAIL);
}
}


enum Result{
// 二者可以看做是“常量”
SUCCESS, FAIL
}

补充一个IDEA中的提示文件,在选中包的情况下,按ALT+Insert弹出可创建的文件类型,就包含Enum类型。

adv_003.png (426×240) (gitee.io)

8.4 DecimalFormat和BigDecimal类

java.text.DecimalFormat是Java提供的专门负责数字格式化的类,其中**#代表任意数字,’,’代表千分位,’.’代表小数点**。

java.math.BigDecimal是Java提供的大数据类型,精度极高,是引用数据类型,专门用在财务软件当中。财务软件中double类型是不够的,需要用到BigDecimal。

9. 补充

9.1 java.util包

可以看到,上述的很多类都是在java.util包下,这个包是Java提供的工具包,里面封装了很多Java提供的工具类,比如常见的数据结构以及其他类。

9.2 java.lang包

该包下的直接子类不需要导入,Java会自动导入。著名的SystemObject等类都在此包下。

10. 备注

参考B站《动力节点》。


文章作者: 浮云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 浮云 !
  目录