常用类
★第十三章 常用类
包装类
针对八种基本数据类型的相应的引用类型就是包装类。
有了类的特点,就可以调用类中的方法。
基本数据类型 包装类 boolean Boolean char Character byte Byte short Short int Integer long Long float Float double Double Byte、Short、Integer、Long、Float、Double 都是 Number 的子类。
装箱和拆箱
装箱:基本数据类型 -> 包装类型
拆箱:包装类型 -> 基本数据类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16package com.f.chapter13;
public class Integer01 {
public static void main(String[] args) {
//演示 int <---> Integer 的装箱与拆箱
//jdk5以前用的是手动装箱和手动拆箱的方式
int n1 = 100;
Integer integer1 = new Integer(n1); //手动装箱方式1
Integer integer2 = Integer.valueOf(n1); //手动装箱方式2
int n2 = integer1.intValue(); //手动拆箱方式
//jdk5以后可以用自动装箱和自动拆箱
Integer integer3 = n2; //自动装箱:底层使用的就是 Integer.valueOf(n2)
int n3 = integer3; //自动拆箱:底层使用的就是 intValue() 方法
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.f.chapter13;
public class String01 {
public static void main(String[] args) {
//包装类(以Integer为例)和 String 的相互转换
//Integer -> String
Integer i1 = 100; //自动装箱
String s1 = i1 + ""; //方式1
String s2 = i1.toString(); //方式2
String s3 = String.valueOf(i1); //方式3
//String -> Integer
Integer i2 = Integer.parseInt(s1); //方式1 parseInt方法返回的是 int 类型数据,所以这里还用到了自动装箱,将 int 装箱为 Integer
Integer i3 = Integer.valueOf(s1); //方式2
}
}
包装类常用方法(Integer、Character)
Integer 类常用方法:
1
2
3
4
5
6
7
8package com.f.chapter13;
public class IntegerMethod {
public static void main(String[] args) {
System.out.println(Integer.MIN_VALUE); //Integer的最小值
System.out.println(Integer.MAX_VALUE); //Integer的最大值
}
}Character 类常用方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.f.chapter13;
public class CharacterMethod {
public static void main(String[] args) {
System.out.println(Character.isDigit('1')); //判断是不是数字
System.out.println(Character.isLetter('a')); //判断是不是字母
System.out.println(Character.isUpperCase('A')); //判断是不是大写
System.out.println(Character.isLowerCase('a')); //判断是不是小写
System.out.println(Character.isWhitespace(' ')); //判断是不是空格
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
}
}
★Integer创建机制
看如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.f.chapter13;
/**
* @author fzy
* @date 2023/4/23 10:26
*/
public class Integer02 {
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //false
Integer m = 1;
Integer n = 1;
System.out.println(m == n); //true
Integer x = 128;
Integer y = 128;
System.out.println(x == y); //false
}
}发现输出分别为 false、true、false。输出结果与 Integer 的创建机制有关。
对于第一种情况,因为是 new 了两个对象,这两个对象并不相同,所以为 false。
对于第二种和第三种情况,前面已经说过,这里用到了自动装箱:底层使用的就是 Integer.valueOf() 方法,所以看源码:
1
2
3
4
5public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}发现:
- 如果
i >= IntegerCache.low && i <= IntegerCache.high
,那么就从IntegerCache.cache[]
数组中取出相应的 Integer 对象。 - 否则,直接 new 一个 Integer 对象并返回。
而源码中设定
IntegerCache.low = -128
,IntegerCache.high = 127
,所以:- 对于第二种情况,因为 -128 < 1 < 127,所以都返回的是
IntegerCache.cache[]
中的 Integer 对象,因此 m 和 n 指向的是同一个 Integer 对象,结果为 true。 - 对于第三种情况,因为 128 > 127,所以都是直接 new 了一个 Integer 对象,x 和 y 指向的是不同的 Integer 对象,结果为 false。
- 如果
★String
String结构剖析

String 对象用于保存字符串,也就是一组字符序列。
字符串常量对象是用双引号括起来的字符序列,例如:“你好”、“boy”、“12345” 等。
字符串的字符使用 Unicode 字符编码,一个字符(不论是字母还是汉字)占两个字节。
String 类较为常用的构造方法:
1
2
3
4
5String 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);String 类实现了接口 Serializable(String 可以串行化,可以在网络传输),实现了接口 Comparable(String 对象可以比较大小)。
String 是 final 类,不能被其他的类继承。
String 有属性
private final char value[];
,用于存放字符串内容,注意value[]
是final
类型,不可以修改(即 value 不能指向新的地址,但是它的单个字符内容是可以变化的)。1
2
3
4
5
6
7
8
9
10package com.f.chapter13;
public class StringMethod03 {
public static void main(String[] args) {
String s = new String("john");
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s)));
s = new String("tom");
System.out.println(String.class.getName() + "@" + Integer.toHexString(System.identityHashCode(s)));
}
}首先变量 s 指向了堆中的一个 String 对象,该 String 对象中的 value 指向了常量池中的字符串 “john”;
然后在
s = new String("tom");
创建了一个新的 String 对象,变量 s 变为指向这个新的 String 对象,该新的 String 对象中的 value 指向了常量池中的字符串 “tom”,亦即,第一个 String 对象中的 value 指向的地址并没有发生改变,不可以修改。
★★★String创建剖析
创建 String 对象的两种方式:
直接赋值:
String s = "hello";
先从常量池中查看是否有 ”hello“ 数据空间,如果有,则直接指向;如果没有则重新创建,然后指向。s 最终指向的是常量池的空间地址。
调用构造器:
String s = new String("hello");
先在堆中创建空间,里面维护了 value 属性,value 指向常量池的 ”hello“ 空间,如果常量池中没有 ”hello“ 则重新创建,如果有则直接通过 value 指向,最终指向的是堆中的空间地址。
★String对象特性
String 是一个 final 类,代表不可变的字符序列。
字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。
字符串对象 ”hello“ 的内容并没有变化,而是让字符串变量 s1 指向了新的字符串对象 ”haha“,所以是创建了两个字符串对象。
下面的例子中,只在常量池中创建了一个字符串对象,因为编译器做了一个优化。
下面的例子中,在常量池中创建了三个字符串对象,但是需要注意 c 指向的是堆中的对象,而不是常量池中的对象。
注意:一个变量加一个字符串常量,如
String s = "hello,";
String s2 = s + "world!";
s2 的创建过程和两个变量相加的创建过程是一样的,即最终结果为:s2 指向堆中的对象,堆中对象的
value[]
指向常量池中的"hello,world!"
String常用方法
intern()
:当调用 intern 方法时,如果常量池中已经包含一个内容等于此 String 对象的字符串(用equals(Object)
方法确定),则返回常量池中的字符串地址;否则,将此 String 对象添加到池中,并返回此 String 对象的引用地址。1
2
3
4
5
6
7
8
9
10package com.f.chapter13;
public class String02 {
public static void main(String[] args) {
String s1 = new String("1") + new String("1"); //常量池中生成”1“,s1指向堆中的对象,堆中对象的value内容为”11“
s1.intern(); //常量池中没有”11“,所以将s1的地址添加到常量池中
String s2 = "11"; //因为常量池中存在”11“的引用s1,所以s2指向常量池中s1的地址
System.out.println(s1 == s2); //返回true
}
}s1.equals(s2)
:判断内容是否相等(区分大小写)。s1.equalsIgnoreCase(s2)
:判断内容是否相等(忽略大小写)。s1.length()
:获取字符串的长度。s1.indexOf(s2)
:获取字符在字符串中第一次出现的索引,索引从 0 开始,如果找不到,返回 -1。s1.lastIndexOf(s2)
:获取字符在字符串中最后一次出现的索引,索引从 0 开始,如果找不到,返回 -1。substring
:截取指定范围的字串。s1.trim()
:去除前后空格。s1.charAt(index)
:获取某索引处的字符,注意不能使用 Str[index] 这种方式。s1.toUpperCase()
:转换成大写。s1.toLowerCase()
:转换成小写。s1.replace(oldChar,newChar)
:替换字符串中的内容,但需要注意的是,replace
方法执行后,返回的结果才是替换过的,而对原来的字符串对象没有影响,从源码可以看到,是返回了一个新的字符串对象:return new String(buf, true);
。1
2
3
4
5
6
7
8
9
10package com.f.chapter13;
public class StringMethod01 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = s1.replace("he","ol");
System.out.println(s1); //s1指向的常量池中的 “hello”对象 并没有发生变化
System.out.println(s2); //是在堆中创建了一个新的 String 对象,对象的 value 指向常量池中的 “olllo”
}
}输出结果为
1
2hello
olllos1.concat(s2)
:拼接字符串。和replace
类似,会返回一个新的字符串对象,而对原来的字符串对象无影响。s1.split("ch")
:分割字符串,返回结果为字符串数组。对于某些分割字符,我们需要转义,比如 “|” “\” 等。s1.toCharArray()
:将字符串转换为字符数组。s1.compareTo(s2)
:比较两个字符串的大小,如果前者大,则返回正数;后者大,则返回负数;如果相等,则返回 0。- 如果长度相同,并且每个字符也相同,就返回 0
- 如果长度不相同,但是在进行比较时,不能区分大小(对应字符都一样),就返回
len1 - len2
- 如果长度相同或者不相同,但是在进行比较时,可以区分大小,就返回
c1 - c2
看源码即可理解:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}String.format()
:格式化字符串。1
2
3
4
5
6
7
8
9
10
11
12package com.f.chapter13;
public class StringMethod02 {
public static void main(String[] args) {
String name = "小明";
int age = 10;
double score = 100 / 3;
String formatStr = "我的姓名是%s 年龄是%d 成绩是%.2f";
String info = String.format(formatStr, name, age, score);
System.out.println(info);
}
}%s
、%d
、%.2f
等称为占位符,这些占位符由后面的变量来替换。
★StringBuffer
java.lang.StringBuffer
代表可变的字符序列,可以对字符串的内容进行增删。StringBuffer 的很多方法和 String 相同,但是 StringBuffer 是可变长度的。 StringBuffer 是一个容器。
StringBuffer结构剖析

- StringBuffer 的直接父类是 AbstractStringBuilder。
- StringBuffer 实现了 Serializable,即 StringBuffer 的对象可以串行化。
- 在父类 AbstractStringBuilder 中,有属性
char[] value;
,value 数组不是 final 类型(和 String 不同)。该 value 数组存放字符串内容,且存放在堆中(而非常量池中)。 - StringBuffer 是 final 类,不能被继承。
★注意:String 保存的是字符串常量,value 的地址不能修改,每次 String 类的更新实际上就是创建新的 String 对象,然后让 String 变量的地址更改为新的 String 对象的地址,效率较低(
private final char value[];
);StringBuffer 保存的是字符串变量,里面的值可以更改,每次 StringBuffer 的更新实际上可以更新内容,而不用更新地址,效率高(
char[] value;
)。
StringBuffer和String的转换
StringBuffer的构造器
StringBuffer()
:构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符。StringBuffer(CharSequence seq)
:public java.lang.StringBuilder(CharSequence seq)
构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。StringBuffer(int capacity)
:构造一个不带字符,但具有指定初始容量的字符串缓冲区。即对 char[] 大小进行指定。StringBuffer(String str)
:构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。char[] 大小为str.length() + 16
。
String 和 StringBuffer 的转换:
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
32package com.f.chapter13;
/**
* @author fzy
* @date 2023/4/23 10:18
*/
public class StringAndStringBuffer {
public static void main(String[] args) {
//String -> StringBuffer
String str = new String("Tom");
//方式1:使用 StringBuffer 的构造器
StringBuffer stringBuffer1 = new StringBuffer(str);
//方式2:使用 StringBuffer 的 append 方法
StringBuffer stringBuffer2 = new StringBuffer();
stringBuffer2.append(str);
//StringBuffer -> String
StringBuffer stringBuffer3 = new StringBuffer("Jerry");
//方式1:使用 StringBuffer 的 toString 方法
String s1 = stringBuffer3.toString();
//方式2:使用 String 的构造器
String s2 = new String(stringBuffer3);
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
System.out.println(stringBuffer1 == stringBuffer2); //false
System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2); //false
}
}
StringBuffer常用方法
增:
s.append()
,在 StringBuffer 对象中增加内容。删:
s.delete(start, end)
,删除索引为[start, end)
处的字符。改:
s.replace(start, end, "str")
,使用 ”str“ 替换索引为[start, end)
处的字符。查:
s.indexOf("str")
,查找指定的子串在字符串中第一次出现的索引,如果找不到则返回 -1。插:
s.insert(index, "str")
,在索引为 index 的位置插入字符串 “str”,原来索引为 index 的内容自动后移。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
32package com.f.chapter13;
/**
* @author fzy
* @date 2023/4/24 10:21
*/
public class StringBufferMethod {
public static void main(String[] args) {
//增:append
StringBuffer s = new StringBuffer("hello"); //hello
s.append(","); //hello,
s.append("world!").append(100).append(true).append(1.1); //hello,world!100true1.1
System.out.println(s); //输出 "hello,world!100true1.1"
//删:delete
s.delete(12, 15); //删除 "100"
System.out.println(s); //输出 "hello,world!true1.1"
//改:replace
if (s.indexOf("true") != -1) {
s.replace(s.indexOf("true"), s.indexOf("true") + "true".length(), "false");
System.out.println(s); //输出 "hello,world!false1.1"
}
//查:indexOf
System.out.println(s.indexOf("false")); //输出 12
//插:insert
s.insert(s.indexOf("false"), "true, ");
System.out.println(s); //输出 "hello,world!true, false1.1"
}
}
★StringBuilder
java.lang.StringBuilder
表示一个可变的字符序列,此类提供一个与 StringBuffer 兼容的 API,但不保证同步(StringBuilder 不是线程安全的)。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。- 在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。
StringBuilder结构剖析

- StringBuilder 的直接父类是 AbstractStringBuilder。
- StringBuilder 实现了 Serializable,即 StringBuilder 的对象可以串行化。
- 在父类 AbstractStringBuilder 中,有属性
char[] value;
,value 数组不是 final 类型(和 String 不同)。该 value 数组存放字符串内容,且存放在堆中(而非常量池中)。 - StringBuilder 是 final 类,不能被继承。
- StringBuilder 的方法没有做互斥处理,既没有
synchronized
关键字,因此只在单线程的情况下使用 StringBuilder。
★String、StringBuffer、StringBuilder对比
StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,而且方法也一样。
String
:不可变字符序列,效率低,但是复用率高。StringBuffer
:可变字符序列,效率较高,线程安全。StringBuilder
:可变字符序列,效率最高,线程不安全。String 使用注意:
String s = "a"; //创建了一个字符串
s += "b"; //实际上原来的 "a" 字符串对象已经丢弃了,现在又产生了一个字符串 s+"b" (也就是 "ab" )
如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。
如果这样的操作放到循环中,会极大影响程序的性能 => 结论 : 如果我们对 String 做大量修改,不要使用 String。
String、StringBuffer、StringBuilder 使用原则:
- 如果字符串存在大量的修改操作,一般使用
StringBuffer
或StringBuilder
。- 如果字符串存在大量的修改操作,并在单线程的情况,则使用
StringBuilder
。 - 如果字符串存在大量的修改操作,并在多线程的情况,则使用
StringBuffer
。
- 如果字符串存在大量的修改操作,并在单线程的情况,则使用
- 如果我们字符串很少修改,被多个对象引用,则使用
String
,比如配置信息等。
- 如果字符串存在大量的修改操作,一般使用