声明:
本博客是本人在学习《Java 编程的逻辑》后整理的笔记,旨在方便复习和回顾,并非用作商业用途。
本博客已标明出处,如有侵权请告知,马上删除。
开头语
本章介绍 Java 编程中一些常用的基础类,探讨它们的用法、应用和实现原理,这些类有:
- 各种包装类
- 文本处理的类 String 和 StringBuilder
- 数组操作的类 Arrays
- 日期和时间处理
- 随机
7.1 包装类
Java 有八种基本类型,每种基本类型都有一个对应的包装类。
包装类是什么呢?它是一个类,内部有一个实例变量,保存对应的基本类型的值,这个类一般还有一些静态方法、静态变量和实例方法,以方便对数据进行操作。
Java 中,基本类型和对应的包装类如表 7-1 所示:
包装类也都很好记,除了 Integer 和 Character 外,其他类名称与基本类型基本一样,只是首字母大写。
包装类有什么用呢?Java 中很多代码(比如后续文章介绍的集合类)只能操作对象,为了能操作基本类型,需要使用其对应的包装类,另外,包装类提供了很多有用的方法,可以方便对数据的操作。
下面先介绍各个包装类的基本用法及其共同点,然后重点介绍 Integer 和 Character。
7.1.1 基本用法
我们先来看各个基本类型和其包装类是如何转换的,我们直接看代码:
Boolean
boolean b1 = false;
Boolean bObj = Boolean.valueOf(b1);
boolean b2 = bObj.booleanValue();
Byte
byte b1 = 123;
Byte byteObj = Byte.valueOf(b1);
byte b2 = byteObj.byteValue();
Short
short s1 = 12345;
Short sObj = Short.valueOf(s1);
short s2 = sObj.shortValue();
Integer
int i1 = 12345;
Integer iObj = Integer.valueOf(i1);
int i2 = iObj.intValue();
Long
long l1 = 12345;
Long lObj = Long.valueOf(l1);
long l2 = lObj.longValue();
Float
float f1 = 123.45f;
Float fObj = Float.valueOf(f1);
float f2 = fObj.floatValue();
Double
double d1 = 123.45;
Double dObj = Double.valueOf(d1);
double d2 = dObj.doubleValue();
Character
char c1 = 'A';
Character cObj = Character.valueOf(c1);
char c2 = cObj.charValue();
这些代码结构是类似的,每种包装类都有一个静态方法 valueOf(),接受基本类型,返回引用类型,也都有一个实例方法 xxxValue() 返回对应的基本类型。
将基本类型转换为包装类的过程,一般称为"装箱",而将包装类型转换为基本类型的过程,则称为"拆箱"。装箱/拆箱写起来比较啰嗦,Java 1.5 以后引入了自动装箱和拆箱技术,可以直接将基本类型赋值给引用类型,反之亦可,如下代码所示:
Integer a = 100;
int b = a;
自动装箱/拆箱是 Java 编译器提供的能力,背后,它会替换为调用对应的 valueOf()/xxxValue(),比如说,上面的代码会被 Java 编译器替换为:
Integer a = Integer.valueOf(100);
int b = a.intValue();
每种包装类也都有构造方法,可以通过 new 创建,比如说:
Integer a = new Integer(100);
Boolean b = new Boolean(true);
Double d = new Double(12.345);
Character c = new Character('马');
那到底应该用静态的 valueOf 方法,还是使用 new 呢?一般建议使用 valueOf。new 每次都会创建一个新对象,而除了 Float 和 Double 外的其他包装类,都会缓存包装类对象,减少需要创建对象的次数,节省空间,提升性能,后续我们会分析其具体代码。
7.1.2 共同点
各个包装类有很多共同点,比如,都重写了 Object 中的一些方法,都实现了 Comparable 接口,都有一些与 String 有关的方法,大部分都定义了一些静态常量,都是不可变的。下面具体介绍。
7.1.2.1 重写 Object 方法
所有包装类都重写了 Object 类的如下方法:
boolean equals(Object obj)
int hashCode()
String toString()
我们逐个来看下。
equals
equals 用于判断当前对象和参数传入的对象是否相同,Object 类的默认实现是比较地址,对于两个变量,只有这两个变量指向同一个对象时,equals 才返回 true,它和比较运算符(==)的结果是一样的。
但,equals 应该反映的是对象间的逻辑相等关系,所以这个默认实现一般是不合适的,子类需要重写该实现。所有包装类都重写了该实现,实际比较用的是其包装的基本类型值,比如说,对于 Long 类,其 equals 方法代码是:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
对于 Float,其实现代码为:
public boolean equals(Object obj) {
return (obj instanceof Float)
&& (floatToIntBits(((Float)obj).value) == floatToIntBits(value));
}
Float 有一个静态方法 floatToIntBits(),将 float 的二进制表示看做 int。需要注意的是,只有两个 float 的二进制表示完全一样的时候,equals 才会返回 true。在 2.2 节的时候,我们提到小数计算是不精确的,数学概念上运算结果一样,但计算机运算结果可能不同,比如说,看下面代码:
Float f1 = 0.01f;
Float f2 = 0.1f*0.1f;
System.out.println(f1.equals(f2));
System.out.println(Float.floatToIntBits(f1));
System.out.println(Float.floatToIntBits(f2));
输出为:
false
1008981770
1008981771
也就是,两个浮点数不一样,将二进制看做整数也不一样,相差为 1。
Double 的 equals 方法与 Float 类似,