JAVA核心类
JAVA核心类
字符串
字符串不可变性
概念: 在 Java
中,字符串(String
)是不可变的,这意味着一旦创建,字符串的内容无法更改。任何对字符串的修改都会创建一个新的字符串对象,而原字符串保持不变。
示例:
1 | public class Main { |
解释:
- 首先创建的
s
对象内容为"Hello"
。 - 调用
s.toUpperCase()
方法后,它并未修改原字符串s
,而是返回了一个新字符串"HELLO"
并将s
的引用指向这个新对象。原始的"Hello"
字符串对象仍然存在于内存中,直至被垃圾回收。
字符串比较
概念: 在比较字符串时,==
比较的是两个引用是否指向同一个对象,而 equals()
方法比较的是两个字符串的内容是否相同。
示例:
1 | public class Main { |
解释:
- 在第一个例子中,
s1
和s2
都是通过字符串字面量赋值的,它们指向常量池中的同一个字符串对象,因此s1 == s2
返回true
。 - 在第三个例子中,
s3
是通过方法toLowerCase()
创建的,它虽然内容与s1
相同,但引用的是不同的对象,所以s1 == s3
返回false
。然而,由于s1
和s3
的内容相同,s1.equals(s3)
返回true
。
子字符串操作
概念: Java 提供了一些方法来操作字符串,比如提取子串、搜索子串等。
示例:
1 | public class Main { |
解释:
substring(7, 12)
提取从索引 7 到 11 的字符形成新的子串"World"
。indexOf("World")
返回子串"World"
在字符串中的起始位置(索引 7)。
字符串的常用方法
- 替换:
replace()
可以用来替换字符串中的字符或子串。 - 分割:
split()
可以根据指定的正则表达式分割字符串,返回字符串数组。 - 拼接:
join()
用来将多个字符串拼接在一起,使用指定的分隔符。 - 格式化:
format()
用来创建带占位符的格式化字符串。
示例:
1 | public class Main { |
其他:
1. 字符串池(String Pool)
在Java中,字符串池是一个特殊的内存区域,用于存储字符串字面量。Java编译器会将相同的字符串字面量存储到字符串池中,以减少内存消耗。当你创建一个字符串字面量时,JVM会首先检查字符串池中是否已经存在相同内容的字符串。如果存在,则直接引用这个字符串;否则,会在池中创建一个新的字符串对象。
1
2
3 String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true,因为s1和s2引用同一个字符串池中的对象然而,当使用
new
关键字创建字符串时,它会在堆上创建一个新的字符串对象,而不会使用字符串池。
1
2
3 String s1 = new String("Hello");
String s2 = "Hello";
System.out.println(s1 == s2); // false,因为s1是一个新的对象2. 字符串的不可变性
字符串的不可变性不仅仅是因为它的内部
char[]
是final
,更深层次的原因包括:
- 线程安全:不可变对象是天然的线程安全的,不需要额外的同步机制。
- 字符串池的高效利用:由于字符串是不可变的,所以字符串池可以安全地被共享。
- 性能优化:不可变对象可以更好地被编译器和JVM优化,比如在哈希表中作为键使用。
3. StringBuilder 和 StringBuffer
在需要对字符串进行大量修改时,
StringBuilder
和StringBuffer
是更好的选择,因为它们是可变的。与String
不同,StringBuilder
和StringBuffer
在修改内容时不会创建新的对象,而是直接在原对象上进行操作。
- StringBuilder:非线程安全,适合单线程环境,性能较好。
- StringBuffer:线程安全,适合多线程环境,性能稍差。
1
2
3 StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb.toString()); // "Hello World"4. 字符串的哈希码
String
类的hashCode()
方法是根据字符串的内容计算的,符合以下公式: \(\text{hashCode} = s[0] \times 31^{(n-1)} + s[1] \times 31^{(n-2)} + \ldots + s[n-1]\)其中,
s[i]
是字符串的第i
个字符,n
是字符串的长度。由于这个公式的实现,字符串的哈希码在哈希表中有很好的分布特性。
1
2 String s = "Hello";
System.out.println(s.hashCode()); // 696096505. 字符串的比较性能
由于
String
类的equals()
方法会逐字符比较两个字符串的内容,因此对于长字符串的比较,性能可能会受到影响。为此,可以在性能关键的部分使用compareTo()
方法,它不仅比较内容,还返回一个整数表示字符串的相对顺序。
1
2
3 String s1 = "Hello";
String s2 = "World";
System.out.println(s1.compareTo(s2)); // -15,表示s1小于s26. 字符串的常见陷阱
空字符串与
null
:调用null
对象的任何方法都会抛出NullPointerException
,所以在操作字符串前应该确保它不是null
。
1
2
3
4 String s = null;
if (s != null && s.isEmpty()) {
System.out.println("Empty String");
}拼接效率:频繁的字符串拼接会导致性能问题,因为每次拼接都会创建新的字符串对象。可以考虑使用
StringBuilder
或StringBuffer
来优化。
1
2
3
4 String result = "";
for (int i = 0; i < 1000; i++) {
result += "a"; // 每次循环都会创建一个新的字符串对象
}
StringBuilder
类是一个可变的字符串对象,适合高效的字符串拼接。
StringBuilder
的优势:
- 可变性:
StringBuilder
的内容可以被修改,不会每次修改时都创建新的对象。- 预分配缓冲区:可以指定初始容量,避免了频繁的内存分配。
- 链式操作:支持方法链调用,如
append
和insert
。例子:
1
2
3
4
5
6 StringBuilder sb = new StringBuilder(1024);
for (int i = 0; i < 1000; i++) {
sb.append(',');
sb.append(i);
}
String s = sb.toString();在这个例子中,
StringBuilder
用于高效地拼接多个字符串,而不会在每次循环中创建新的字符串对象。链式操作示例:
1
2
3
4
5
6
7
8
9
10 public class Main {
public static void main(String[] args) {
var sb = new StringBuilder(1024);
sb.append("Mr ")
.append("Bob")
.append("!")
.insert(0, "Hello, ");
System.out.println(sb.toString());
}
}通过链式调用,
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 public class Main {
public static void main(String[] args) {
Adder adder = new Adder();
adder.add(3)
.add(5)
.inc()
.add(10);
System.out.println(adder.value());
}
}
class Adder {
private int sum = 0;
public Adder add(int n) {
sum += n;
return this;
}
public Adder inc() {
sum ++;
return this;
}
public int value() {
return sum;
}
}这个例子演示了如何实现一个支持链式操作的类,类似于
StringBuilder
的设计模式。编译器优化:
- Java编译器会将多个连续的
+
操作优化为StringConcatFactory
操作,通常在运行时通过StringBuilder
实现高效拼接。
StringBuffer
的历史:
StringBuffer
是StringBuilder
的线程安全版本,但由于同步的开销,现在一般推荐使用StringBuilder
,除非在多线程环境中需要线程安全的操作。7. 字符串的编码转换
在国际化应用中,字符串的编码转换是一个重要问题。Java的
String
类提供了多种方法进行字符编码的转换。需要注意的是,编码转换时要使用正确的字符集,否则可能会导致乱码。
1
2 byte[] utf8Bytes = "Hello".getBytes(StandardCharsets.UTF_8);
String utf8String = new String(utf8Bytes, StandardCharsets.UTF_8);8. 字符串的内存管理
Java字符串池的内存管理是通过垃圾收集机制实现的。字符串池中的字符串在JVM退出时会被释放,但是在运行时它们会一直保留在内存中。因此,在处理大量不同的字符串时,要注意内存的使用,可以通过手动调用
intern()
方法将字符串放入池中,以便重复使用。
1
2
3 String s1 = new String("Hello").intern();
String s2 = "Hello";
System.out.println(s1 == s2); // true
字符串拼接后话之StringJoiner:
StringJoiner
是 Java 8
引入的一个类,用于在不需要显式使用 StringBuilder
的情况下拼接字符串。它提供了一种简洁的方式来拼接多个字符串,并可以灵活地指定分隔符、前缀和后缀。
主要特性
- 分隔符:
StringJoiner
允许指定一个分隔符,这个分隔符会被插入到拼接的每两个元素之间。
- 前缀和后缀:
- 可以指定一个前缀和一个后缀,前缀会加在拼接结果的开头,后缀会加在拼接结果的结尾。
构造方法
基本构造方法:
1
StringJoiner(CharSequence delimiter)
- 创建一个
StringJoiner
,使用指定的分隔符。
- 创建一个
带前缀和后缀的构造方法:
1
StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
- 创建一个
StringJoiner
,使用指定的分隔符、前缀和后缀。
- 创建一个
主要方法
add()
:用于向
StringJoiner
中添加元素。示例:
1
2StringJoiner sj = new StringJoiner(", ");
sj.add("Bob").add("Alice").add("Grace");
toString()
:返回拼接后的字符串结果。
示例:
1
String result = sj.toString(); // 结果: "Bob, Alice, Grace"
示例代码
简单用法:
1
2
3
4
5
6
7
8
9
10
11import java.util.StringJoiner;
public class Main {
public static void main(String[] args) {
StringJoiner sj = new StringJoiner(", ");
sj.add("Bob");
sj.add("Alice");
sj.add("Grace");
System.out.println(sj.toString()); // 输出: Bob, Alice, Grace
}
}带前缀和后缀的用法:
1
2
3
4
5
6
7
8
9
10
11import java.util.StringJoiner;
public class Main {
public static void main(String[] args) {
StringJoiner sj = new StringJoiner(", ", "Hello ", "!");
sj.add("Bob");
sj.add("Alice");
sj.add("Grace");
System.out.println(sj.toString()); // 输出: Hello Bob, Alice, Grace!
}
}
注意事项
- 空
StringJoiner
:- 如果
StringJoiner
中没有添加任何元素,它的toString()
方法会返回包含前缀和后缀的字符串,且中间不会有分隔符。
- 如果
- 线程安全:
StringJoiner
不是线程安全的,如果需要在多线程环境中使用,应该采取相应的同步措施。
包装类型
Java中的基本类型和引用类型
在Java中,数据类型分为基本类型和引用类型:
- 基本类型:包括
byte
、short
、int
、long
、boolean
、float
、double
、char
。 - 引用类型:包括所有
class
和interface
类型。
基本类型不能赋值为 null
,但引用类型可以。例如:
1 | String s = null; // 合法 |
包装类(Wrapper Classes)
为了将基本类型视为对象(引用类型),Java 提供了包装类。每种基本类型都有一个对应的包装类:
基本类型 | 对应的引用类型 |
---|---|
boolean |
java.lang.Boolean |
byte |
java.lang.Byte |
short |
java.lang.Short |
int |
java.lang.Integer |
long |
java.lang.Long |
float |
java.lang.Float |
double |
java.lang.Double |
char |
java.lang.Character |
自定义 Integer 类示例:
1 | public class Integer { |
使用 Java 标准库中的包装类:
1 | public class Main { |
自动装箱(Auto Boxing)和自动拆箱(Auto Unboxing)
- 自动装箱:基本类型转换为包装类对象。
- 自动拆箱:包装类对象转换为基本类型。
示例代码:
1 | public class Main { |
注意:自动拆箱可能会导致
NullPointerException
。
1 | public class Main { |
不变类
所有包装类型都是不变类(Immutable
Classes)。例如,Integer
类的关键部分如下:
1 | public final class Integer { |
由于 Integer
是不变的,因此其对象一旦创建便不可更改。
比较两个 Integer
实例:
1 | public class Main { |
由于 Integer
类内部有缓存机制,==
比较的结果可能依赖于数值范围,建议使用 equals()
方法进行比较。
最佳实践
- 使用静态工厂方法:例如
Integer.valueOf()
方法比new Integer()
更推荐,因为它可能利用缓存来节省内存。
1 | Integer n1 = new Integer(100); // 不推荐 |
- 进制转换:
Integer
类提供了将整数转换为不同进制表示的静态方法。
示例代码:
1 | public class Main { |
处理无符号整型
Java 不支持原生无符号整型,但包装类提供了处理无符号整型的方法。
示例代码:
1 | public class Main { |
- 使用自动装箱和自动拆箱简化基本类型与包装类之间的转换。
- 了解和使用不变类的特性,尤其在比较对象时。
- 优先使用静态工厂方法来创建实例。
- 熟悉进制转换和处理无符号整型的工具和方法。
JavaBean
在Java中,JavaBean
是一种遵循特定规范的Java类,旨在封装数据并提供对这些数据的访问。这种规范主要用于数据传递和工具支持,尤其在图形用户界面(GUI)设计和其他需要自动生成代码的场景中非常有用。
JavaBean规范
一个类要成为JavaBean,通常需要满足以下条件:
- 无参数构造函数:必须有一个无参数的构造函数。
- 私有属性:所有的属性(字段)应为
private
,以确保封装性。 - 公有getter和setter方法:对于每个属性,必须提供公有的getter和setter方法,方法名遵循特定的命名规范。
- 可序列化:通常,JavaBean实现
java.io.Serializable
接口,使其能够进行序列化和反序列化(虽然这不是强制要求,但在很多应用场景中是常见的实践)。
JavaBean命名规范
getter方法:用于获取属性值,方法签名为
public Type getXyz()
,其中Type
是属性的类型,Xyz
是属性名的首字母大写形式。1
public String getName() { return this.name; }
setter方法:用于设置属性值,方法签名为
public void setXyz(Type value)
,其中Type
是属性的类型,Xyz
是属性名的首字母大写形式。1
public void setName(String name) { this.name = name; }
boolean属性:对于
boolean
类型的属性,getter方法通常以is
开头,如public boolean isXyz()
。1
public boolean isChild() { return age <= 6; }
示例:Person类
下面是一个符合JavaBean规范的Person
类示例:
1 | public class Person { |
JavaBean的作用
- 数据传递:JavaBean常用于封装一组相关的数据,方便在不同组件或系统之间传递数据。
- 工具支持:JavaBean被许多IDE(如Eclipse、IntelliJ IDEA)和框架(如Spring)广泛支持,工具可以自动生成代码、分析属性,简化开发过程。
- GUI设计:在图形用户界面设计中,JavaBean可以被可视化工具(如Java Beans Development Kit (BDK))用来生成界面组件和事件处理代码。
使用Introspector枚举JavaBean属性
Introspector
类可以用来枚举JavaBean的所有属性。以下是一个示例代码,展示如何使用Introspector
来获取Person
类的属性及其getter和setter方法:
1 | import java.beans.BeanInfo; |
在运行上述代码后,控制台将显示Person
类的属性名称及其对应的getter和setter方法。
- JavaBean是一个封装数据的标准Java类,符合特定的规范。
- 它通过私有属性和公有的getter、setter方法来实现数据封装。
- JavaBean广泛应用于数据传递和图形用户界面设计,工具支持和自动生成代码是其主要应用场景。
- 通过
Introspector
可以方便地获取JavaBean的属性和方法信息。
示例:
1. 简单的Person
JavaBean
这是一个基本的JavaBean,包含一些常见的属性和方法。
1 | public class Person { |
2. Book
JavaBean
展示了一个包含基本数据类型和对象类型属性的JavaBean。
1 | public class Book { |
3. Employee
JavaBean
展示了如何处理布尔属性和计算属性。
1 | public class Employee { |
4. Car
JavaBean
展示了如何使用JavaBean来封装复杂数据类型和计算属性。
1 | public class Car { |
5. Address
JavaBean
展示了如何封装对象类型属性和使用嵌套的JavaBean。
1 | public class Address { |
使用示例
可以在其他类中创建这些JavaBean的实例,并使用其getter和setter方法来操作数据:
1 | public class Main { |
枚举类型
在Java中,使用enum
来定义枚举类型比使用static final
常量更加安全和灵活。
基本定义
定义枚举类型 使用
enum
关键字定义枚举类,枚举常量的定义以逗号分隔。1
2
3public enum Weekday {
SUN, MON, TUE, WED, THU, FRI, SAT;
}使用枚举 枚举类型的常量可以直接使用
==
比较,因为它们是唯一的实例。1
2
3
4
5
6Weekday day = Weekday.SUN;
if (day == Weekday.SAT || day == Weekday.SUN) {
System.out.println("Work at home!");
} else {
System.out.println("Work at office!");
}
优势
类型安全 枚举类型提供了编译时的类型检查,避免了错误的值被使用。
1
2
3int day = 1; // 错误:不能赋值给Weekday
if (day == Weekday.SUN) { // 编译错误
}不可继承 枚举类自动继承自
java.lang.Enum
,并且不能被继承。自定义字段和方法 可以为枚举添加字段、构造方法和自定义方法。
1
2
3
4
5
6
7
8
9public enum Weekday {
MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);
public final int dayValue;
private Weekday(int dayValue) {
this.dayValue = dayValue;
}
}覆写
toString()
可以覆写toString()
方法来提供自定义的输出格式。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public enum Weekday {
MON(1, "Monday"), TUE(2, "Tuesday"), WED(3, "Wednesday"),
THU(4, "Thursday"), FRI(5, "Friday"), SAT(6, "Saturday"), SUN(0, "Sunday");
public final int dayValue;
private final String dayName;
private Weekday(int dayValue, String dayName) {
this.dayValue = dayValue;
this.dayName = dayName;
}
public String toString() {
return this.dayName;
}
}
枚举在switch
语句中的应用
1 | public class Main { |
enum
提供了类型安全、不可继承和更加结构化的常量定义。- 枚举实例是唯一的,可以添加字段、方法和构造函数。
- 使用
switch
语句时,枚举类型比int
和String
更安全且易于维护。
不变类和 record
在Java中,不变类(Immutable
Class)是指创建后其状态不能被修改的类。这种设计可以提高程序的安全性和可靠性。String
和 Integer
等类都是不变类。Java 14 引入了
record
,使得定义不变类更加简洁和高效。
不变类的定义
一个不变类通常具有以下特点:
- 类本身使用
final
修饰,防止被继承。 - 所有字段都使用
final
修饰,确保创建实例后字段值不变。 - 必须正确覆写
equals()
和hashCode()
方法,以保证在集合中的正确行为。
例如,定义一个不变类 Point
:
1 | public final class Point { |
equals()
和hashCode()
方法在 Java 中用于对象比较和哈希处理,它们在集合类和数据结构中的作用非常重要。
equals()
方法
- 作用:用于比较两个对象是否相等。
- 签名:
public boolean equals(Object obj)
- 实现:通常,
equals()
方法会根据对象的状态(字段值)来判断两个对象是否相等。例如,如果两个Point
对象的x
和y
坐标都相同,则它们是相等的。重写
equals()
的基本原则:
- 自反性:对于任何非
null
引用x
,x.equals(x)
应返回true
。- 对称性:对于任何非
null
引用x
和y
,如果x.equals(y)
为true
,则y.equals(x)
也应为true
。- 传递性:对于任何非
null
引用x
、y
和z
,如果x.equals(y)
为true
,且y.equals(z)
为true
,则x.equals(z)
也应为true
。- 一致性:对于任何非
null
引用x
和y
,多次调用x.equals(y)
应返回相同的结果,只要x
和y
的状态没有改变。- 对
null
的比较:对于任何非null
引用x
,x.equals(null)
应返回false
。示例:
1
2
3
4
5
6
7
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Point point = (Point) o;
return x == point.x && y == point.y;
}
hashCode()
方法
- 作用:用于返回对象的哈希码,这是一种用于快速查找和存储对象的整数值。
- 签名:
public int hashCode()
- 实现:
hashCode()
方法生成一个整数值,该值通常基于对象的状态。如果两个对象通过equals()
方法比较相等,那么它们的hashCode()
方法也应返回相同的哈希码。重写
hashCode()
的基本原则:
- 一致性:对于任何非
null
引用x
,只要对象的状态没有变化,调用x.hashCode()
多次应该返回相同的结果。- 相等性:如果两个对象通过
equals()
方法比较相等,那么它们的hashCode()
方法必须返回相同的值。示例:
1
2
3
4
public int hashCode() {
return Objects.hash(x, y);
}为什么需要重写
equals()
和hashCode()
在 Java 集合框架中,例如
HashSet
和HashMap
,equals()
和hashCode()
方法用于判断对象的相等性和存储位置。HashSet
使用hashCode()
来确定对象存储的位置,然后使用equals()
方法来检查是否存在重复对象。如果不正确重写这两个方法,可能会导致集合行为异常,例如无法正确查找或删除对象。
equals()
用于比较对象的相等性。hashCode()
用于生成对象的哈希码,帮助快速存储和查找对象。- 在定义不变类时,正确重写
equals()
和hashCode()
方法非常重要,以确保对象在集合中的正确行为。
record
的简化
record
是 Java 14
引入的一种新特性,用于简化不变类的定义。使用 record
可以一行代码定义一个不变类,并自动生成构造方法、访问器方法、toString()
、equals()
和 hashCode()
方法。
定义 record
使用 record
关键字定义 Point
类:
1 | public record Point(int x, int y) {} |
这个定义相当于:
1 | public final class Point extends Record { |
Compact Constructor
如果需要在 record
的构造方法中添加参数验证,可以使用
Compact Constructor:
1 | public record Point(int x, int y) { |
编译器将自动生成带有验证的构造方法:
1 | public final class Point extends Record { |
静态方法
可以为 record
添加静态工厂方法:
1 | public record Point(int x, int y) { |
这使得创建 Point
实例变得更加灵活:
1 | var p1 = Point.of(); // 创建 (0, 0) |
- 不变类的特点是使用
final
修饰类和字段,确保实例状态不可修改。 - 使用
record
可以更简洁地定义不变类,同时自动生成常用方法。 record
支持 Compact Constructor,用于添加参数验证逻辑。- 可以为
record
添加静态工厂方法,简化对象创建。
BigInteger
在 Java 中,BigInteger
是一个用于表示任意精度整数的类,它提供了超过 long
类型所能表示的整数范围的功能。BigInteger
是不可变的,并且继承自 Number
类,这使得它可以转换为基本数据类型如
byte
、short
、int
、long
、float
和 double
。
BigInteger
的基本使用
创建
BigInteger
对象:1
BigInteger bi = new BigInteger("1234567890");
执行运算:
加法:
1
2
3BigInteger i1 = new BigInteger("1234567890");
BigInteger i2 = new BigInteger("12345678901234567890");
BigInteger sum = i1.add(i2); // 结果是 12345678902469135780乘法:
1
BigInteger product = i1.multiply(i2);
指数运算:
1
BigInteger power = bi.pow(5); // 结果是 2867971860299718107233761438093672048294900000
转换为
long
类型:1
2BigInteger i = new BigInteger("123456789000");
long longValue = i.longValue(); // 123456789000
BigInteger
的转换方法
BigInteger
继承自 Number
类,提供了一些方法用于将其转换为基本数据类型:
转换为
byte
:1
byte byteValue = bi.byteValue();
转换为
short
:1
short shortValue = bi.shortValue();
转换为
int
:1
int intValue = bi.intValue();
转换为
long
:1
long longValue = bi.longValue();
转换为
float
:1
float floatValue = bi.floatValue();
转换为
double
:1
double doubleValue = bi.doubleValue();
精确转换与溢出
对于超出基本数据类型范围的 BigInteger
对象,使用
longValueExact()
、intValueExact()
等方法时,会抛出
ArithmeticException
,如果值超出基本类型的范围。
转换时溢出示例:
1
2
3
4BigInteger bigValue = new BigInteger("123456789000");
long longValueExact = bigValue.longValueExact(); // 123456789000
BigInteger hugeValue = new BigInteger("12345678900000000000000000000000000000000000000000000000000000000000000000000000000000000000");
long overflowValue = hugeValue.longValueExact(); // java.lang.ArithmeticException: BigInteger out of long range
BigInteger
转换为 float
和 double
当 BigInteger
的值超出 float
和
double
的最大表示范围时,结果可能会出现溢出或不准确:
转换为
float
:1
2
3BigInteger hugeValue = new BigInteger("999999").pow(99);
float floatValue = hugeValue.floatValue();
System.out.println(floatValue); // Infinity由于
float
类型的最大值约为 \(3.4 \times 10^{38}\),当BigInteger
的值超出这个范围时,floatValue()
方法会返回Infinity
。转换为
double
:1
2double doubleValue = hugeValue.doubleValue();
System.out.println(doubleValue); // Infinitydouble
类型的最大值约为 \(1.8 \times 10^{308}\),当BigInteger
的值超出这个范围时,doubleValue()
方法也会返回Infinity
。
BigInteger
提供了对任意大小整数的支持,适用于需要超出long
范围的整数计算。- 使用
BigInteger
时,操作是通过实例方法进行的,且结果不会受到固定大小限制。- 在转换
BigInteger
为基本数据类型时,如果数值超出范围,可能会出现数据丢失或溢出。- 对于极大的
BigInteger
,在转换为float
或double
时,可能会返回Infinity
或不准确的结果。
BigDecimal
BigDecimal
是 Java
中用于表示任意精度的浮点数的类,特别适合处理需要精确计算的场景,如金融计算。与
BigInteger
类似,BigDecimal
提供了比
double
和 float
更高的精度,并且不受浮点数计算中常见的精度损失问题的影响。
BigDecimal
的基本操作
创建 BigDecimal
对象
你可以通过构造函数创建 BigDecimal
对象:
1 | BigDecimal bd = new BigDecimal("123.4567"); |
使用 String
构造函数是推荐的方式,因为它避免了浮点数转换中的精度损失。
基本运算
BigDecimal
提供了加法、减法、乘法和除法等方法。操作是通过实例方法进行的。
加法:
1
2
3BigDecimal d1 = new BigDecimal("123.45");
BigDecimal d2 = new BigDecimal("67.89");
BigDecimal sum = d1.add(d2); // 191.34减法:
1
BigDecimal difference = d1.subtract(d2); // 55.56
乘法:
1
BigDecimal product = d1.multiply(d2); // 8384.2355
除法(需要指定精度和舍入模式):
1
2
3BigDecimal d3 = new BigDecimal("123.456");
BigDecimal d4 = new BigDecimal("23.456789");
BigDecimal quotient = d3.divide(d4, 10, RoundingMode.HALF_UP); // 5.2644664375如果除法不能精确除尽,需要指定舍入模式,否则会抛出
ArithmeticException
:1
2
3BigDecimal d5 = new BigDecimal("1");
BigDecimal d6 = new BigDecimal("3");
BigDecimal quotient2 = d5.divide(d6, 10, RoundingMode.HALF_UP); // 0.3333333333求余数:
1
2
3BigDecimal[] result = d3.divideAndRemainder(d4);
BigDecimal quotientResult = result[0]; // 商
BigDecimal remainder = result[1]; // 余数
BigDecimal
的 scale
和 stripTrailingZeros
方法
获取
scale
:scale
表示小数点后的位数。1
2BigDecimal d1 = new BigDecimal("123.45");
System.out.println(d1.scale()); // 2scale
的值可能为负数,表示整数部分末尾的零。例如:1
2BigDecimal d3 = new BigDecimal("1234500");
System.out.println(d3.scale()); // 0去除末尾的零:
stripTrailingZeros()
方法将BigDecimal
对象格式化为一个相等的BigDecimal
,但去掉了末尾的零。1
2
3
4BigDecimal d1 = new BigDecimal("123.4500");
BigDecimal d2 = d1.stripTrailingZeros();
System.out.println(d1.scale()); // 4
System.out.println(d2.scale()); // 2如果
BigDecimal
是整数,stripTrailingZeros()
会返回负数的scale
值,表示末尾的零:1
2
3
4BigDecimal d3 = new BigDecimal("1234500");
BigDecimal d4 = d3.stripTrailingZeros();
System.out.println(d3.scale()); // 0
System.out.println(d4.scale()); // -2
BigDecimal
的精度设置
设置精度和舍入模式:
setScale()
方法可以用来设置BigDecimal
的精度,并指定舍入模式。1
2
3BigDecimal d1 = new BigDecimal("123.456789");
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567
比较 BigDecimal
对象
equals()
方法:equals()
方法不仅要求值相等,还要求scale
相等:1
2
3BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.equals(d2)); // false, 因为 scale 不同compareTo()
方法: 使用compareTo()
方法比较BigDecimal
对象,它只比较数值而忽略scale
:1
2
3BigDecimal d1 = new BigDecimal("123.456");
BigDecimal d2 = new BigDecimal("123.45600");
System.out.println(d1.compareTo(d2)); // 0, 因为数值相等compareTo()
返回的值:0
表示相等- 负数表示小于
- 正数表示大于
内部表示
BigDecimal
内部使用 BigInteger
来表示整数部分,使用 scale
来表示小数点位置:
1 | public class BigDecimal extends Number implements Comparable<BigDecimal> { |
BigDecimal
提供了精确的浮点数运算,避免了传统浮点数计算的精度问题。scale
表示小数点后的位数,可能是负数,表示整数部分的零。- 使用
stripTrailingZeros()
方法可以去除末尾的零。- 在进行除法运算时,必须处理精度和舍入模式。
- 使用
compareTo()
进行数值比较,避免了equals()
方法的scale
问题。
Math 类
Math
类提供了许多静态方法,用于执行各种数学计算。常用方法包括:
绝对值:
1
2Math.abs(-100); // 100
Math.abs(-7.8); // 7.8最大值和最小值:
1
2Math.max(100, 99); // 100
Math.min(1.2, 2.3); // 1.2幂运算:
1
Math.pow(2, 10); // 1024
平方根:
1
Math.sqrt(2); // 1.414...
指数:
1
Math.exp(2); // 7.389...
对数:
1
2Math.log(4); // 1.386...
Math.log10(100); // 2三角函数:
1
2
3
4
5Math.sin(3.14); // 0.00159...
Math.cos(3.14); // -0.9999...
Math.tan(3.14); // -0.0015...
Math.asin(1.0); // 1.57079...
Math.acos(1.0); // 0.0常量:
1
2double pi = Math.PI; // 3.14159...
double e = Math.E; // 2.7182818...生成随机数:
1
Math.random(); // 0 <= x < 1
要生成区间 [MIN, MAX) 的随机数:
1
2
3
4
5double x = Math.random();
double min = 10;
double max = 50;
double y = x * (max - min) + min; // y 的范围是 [10, 50)
long n = (long) y; // [10, 50) 的整数
1. 计算一个数的绝对值
有时你需要计算一个数的绝对值,例如在处理距离或误差时:
1
2
3
4
5
6
7
8
9
10
11
12 public class AbsoluteValueExample {
public static void main(String[] args) {
int negativeValue = -25;
double positiveValue = 15.75;
int absInt = Math.abs(negativeValue);
double absDouble = Math.abs(positiveValue);
System.out.println("Absolute value of -25: " + absInt);
System.out.println("Absolute value of 15.75: " + absDouble);
}
}2. 计算一个数的平方根
如果你需要计算一个数的平方根,例如在几何计算中:
1
2
3
4
5
6
7
8
9 public class SquareRootExample {
public static void main(String[] args) {
double number = 16;
double sqrt = Math.sqrt(number);
System.out.println("Square root of 16: " + sqrt);
}
}3. 计算最大值和最小值
有时你需要找到一组数中的最大值或最小值,例如在统计分析中:
1
2
3
4
5
6
7
8
9
10
11
12 public class MaxMinExample {
public static void main(String[] args) {
int a = 10, b = 20;
double x = 5.5, y = 2.2;
int maxInt = Math.max(a, b);
double minDouble = Math.min(x, y);
System.out.println("Maximum of 10 and 20: " + maxInt);
System.out.println("Minimum of 5.5 and 2.2: " + minDouble);
}
}4. 生成区间内的随机数
如果你需要生成指定范围内的随机数,例如用于模拟或测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 public class RandomNumberExample {
public static void main(String[] args) {
double min = 1.0;
double max = 10.0;
double random = Math.random();
double rangeRandom = random * (max - min) + min;
System.out.println("Random number between 1 and 10: " + rangeRandom);
// 生成范围内的整数
long integerRandom = (long) rangeRandom;
System.out.println("Random integer between 1 and 10: " + integerRandom);
}
}5. 计算对数和指数
如果你需要计算对数或指数,例如在科学计算中:
1
2
3
4
5
6
7
8
9
10
11 public class LogExpExample {
public static void main(String[] args) {
double number = 10;
double logValue = Math.log(number);
double expValue = Math.exp(number);
System.out.println("Natural logarithm of 10: " + logValue);
System.out.println("Exponent of 10: " + expValue);
}
}6. 使用三角函数
如果你需要计算三角函数的值,例如在图形学中:
1
2
3
4
5
6
7
8
9
10
11
12
13 public class TrigonometricFunctionsExample {
public static void main(String[] args) {
double angle = Math.PI / 4; // 45 degrees in radians
double sinValue = Math.sin(angle);
double cosValue = Math.cos(angle);
double tanValue = Math.tan(angle);
System.out.println("Sine of 45 degrees: " + sinValue);
System.out.println("Cosine of 45 degrees: " + cosValue);
System.out.println("Tangent of 45 degrees: " + tanValue);
}
}
HexFormat 类
HexFormat
用于处理 byte[]
和十六进制字符串的转换。
将
byte[]
转换为十六进制字符串:1
2
3
4
5import java.util.HexFormat;
byte[] data = "Hello".getBytes();
HexFormat hf = HexFormat.of();
String hexData = hf.formatHex(data); // 48656c6c6f定制转换格式:
1
2HexFormat hf = HexFormat.ofDelimiter(" ").withPrefix("0x").withUpperCase();
hf.formatHex("Hello".getBytes()); // 0x48 0x65 0x6C 0x6C 0x6F从十六进制字符串转换为
byte[]
:1
byte[] bs = HexFormat.of().parseHex("48656c6c6f");
Random 类
Random
用于生成伪随机数。可以指定种子来生成确定的随机序列。
生成随机数:
1
2
3
4
5
6Random r = new Random();
r.nextInt(); // 2071575453
r.nextInt(10); // [0, 10)
r.nextLong(); // 8811649292570369305
r.nextFloat(); // [0, 1)
r.nextDouble(); // [0, 1)指定种子:
1
2
3
4Random r = new Random(12345);
for (int i = 0; i < 10; i++) {
System.out.println(r.nextInt(100));
}
SecureRandom 类
SecureRandom
用于生成安全的随机数,通常用于加密应用。
生成安全随机数:
1
2SecureRandom sr = new SecureRandom();
System.out.println(sr.nextInt(100));获取高强度安全随机数生成器:
1
2
3
4
5
6
7
8
9
10
11
12
13import java.security.SecureRandom;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
SecureRandom sr;
try {
sr = SecureRandom.getInstanceStrong(); // 高强度生成器
} catch (NoSuchAlgorithmException e) {
sr = new SecureRandom(); // 普通生成器
}
byte[] buffer = new byte[16];
sr.nextBytes(buffer);
System.out.println(Arrays.toString(buffer));
SecureRandom
使用操作系统提供的安全随机种子,确保生成的随机数不可预测。在密码学中,使用
SecureRandom
是确保安全的最佳实践。