常用类

常用类

1. System类

System 类位于 java.lang 包,代表当前 Java 程序的运行平台,系统级的很多属性和控制方法都放置在该类的内部。由于该类的构造方法是 private 的,所以无法创建该类的对象,也就是无法实例化该类。

System 类提供了一些类变量和类方法,允许直接通过 System 类来调用这些类变量和类方法。

System 类的成员变量

System 类有 3 个静态成员变量,分别是 PrintStream out、InputStream in 和 PrintStream err。

  1. PrintStream out

标准输出流。此流已打开并准备接收输出数据。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。

例如,编写一行输出数据的典型方式是:

System.out.println(data);
其中,println 方法是属于流类 PrintStream 的方法,而不是 System 中的方法。

  1. InputStream in

标准输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。

  1. PrintStream err

标准的错误输出流。其语法与 System.out 类似,不需要提供参数就可输出错误信息。也可以用来输出用户指定的其他信息,包括变量的值。
例 1

编写一个 Java 程序,使用本节介绍的 System 类实现从键盘输入字符并显示出来。 具体实现代码如下:

import java.io.IOException;
public class Test06 {
public static void main(String[] args) {
System.out.println("请输入字符,按回车键结束输入:");
int c;
try {
c = System.in.read(); // 读取输入的字符
while(c != '\r') { // 判断输入的字符是不是回车
System.out.print((char) c); // 输出字符
c = System.in.read();
}
} catch(IOException e) {
System.out.println(e.toString());
} finally {
System.err.println();
}
}
}

以上代码中,System.in.read() 语句读入一个字符,read() 方法是 InputStream 类拥有的方法。变量 c 必须用 int 类型而不能用 char 类型,否则会因为丢失精度而导致编译失败。

以上的程序如果输入汉字将不能正常输出。如果要正常输出汉字,需要把 System.in 声明为 InputStreamReader 类型的实例,最终在 try 语句块中的代码为:

InputStreamReader in = new InputStreamReader(System.in, "GB2312");
c = in.read();
while(c != '\r') {
System.out.print((char) c);
c = in.read();
}

如上述代码所示,语句 InputStreamReader in=new InputStreamReader(System.in,"GB2312") 声明一个新对象 in,它从 Reader 继承而来,此时就可以读入完整的 Unicode 码,显示正常的汉字。

System 类的成员方法

System 类中提供了一些系统级的操作方法,常用的方法有 arraycopy()、currentTimeMillis()、exit()、gc() 和 getProperty()。

1.arraycopy() 方法

该方法的作用是数组复制,即从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。该方法的具体定义如下:

public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
其中,src 表示源数组,srcPos 表示从源数组中复制的起始位置,dest 表示目标数组,destPos 表示要复制到的目标数组的起始位置,length 表示复制的个数。

例 2

下面的示例代码演示了 arraycopy() 方法的使用:

public class System_arrayCopy {
public static void main(String[] args) {
char[] srcArray = {'A','B','C','D'};
char[] destArray = {'E','F','G','H'};
System.arraycopy(srcArray,1,destArray,1,2);
System.out.println("源数组:");
for(int i = 0;i < srcArray.length;i++) {
System.out.println(srcArray[i]);
}
System.out.println("目标数组:");
for(int j = 0;j < destArray.length;j++) {
System.out.println(destArray[j]);
}
}
}
如上述代码,将数组 srcArray 中,从下标 1 开始的数据复制到数组 destArray 从下标 1 开始的位置,总共复制两个。也就是将 srcArray[1] 复制给 destArray[1],将 srcArray[2] 复制给 destArray[2]。这样经过复制之后,数组 srcArray 中的元素不发生变化,而数组 destArray 中的元素将变为 E、B、C、 H,

2. currentTimeMillis() 方法

该方法的作用是返回当前的计算机时间,时间的格式为当前计算机时间与 GMT 时间(格林尼治时间)1970 年 1 月 1 日 0 时 0 分 0 秒所差的毫秒数。一般用它来测试程序的执行时间。例如:

long m = System.currentTimeMillis();
上述语句将获得一个长整型的数字,该数字就是以差值表达的当前时间。

例 3

使用 currentTimeMillis() 方法来显示时间不够直观,但是可以很方便地进行时间计算。例如,计算程序运行需要的时间就可以使用如下的代码:

public class System_currentTimeMillis {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for(int i = 0;i < 100000000;i++) {
int temp = 0;
}
long end = System.currentTimeMillis();
long time = end - start;
System.out.println("程序执行时间" + time + "秒");
}
}
上述代码中的变量 time 的值表示代码中 for 循环执行所需要的毫秒数,使用这种方法可以测试不同算法的程序的执行效率高低,也可以用于后期线程控制时的精确延时实现。

3. exit() 方法

该方法的作用是终止当前正在运行的 Java 虚拟机,具体的定义格式如下:

public static void exit(int status)
其中,status 的值为 0 时表示正常退出,非零时表示异常退出。使用该方法可以在图形界面编程中实现程序的退出功能等。

4. gc() 方法

该方法的作用是请求系统进行垃圾回收,完成内存中的垃圾清除。至于系统是否立刻回收,取决于系统中垃圾回收算法的实现以及系统执行时的情况。定义如下:

public static void gc()

5.getProperty() 方法

该方法的作用是获得系统中属性名为 key 的属性对应的值,具体的定义如下:

public static String getProperty(String key)

属性名 属性说明
java.version Java 运行时环境版本
java.home Java 安装目录
os.name 操作系统的名称
os.version 操作系统的版本
user.name 用户的账户名称
user.home 用户的主目录
user.dir 用户的当前工作目录

2. Runtime类

Java Runtime 类是 Java 标准库中的关键类之一,它提供了对当前Java虚拟机实例的访问和控制,允许程序动态地修改和管理运行时环境。每个Java应用程序都有一个Runtime类实例,使得程序能够与其运行的环境相连接。
  Runtime类所在包为java.lang包,因此在使用的时候不需要进行导包。并且Runtime类被public修饰了,因此该类是可以被继承的。

2.1常用方法

程序中一般不能显式的主动实例化一个Runtime实例,而是通过Runtime.getRuntime()来获取当前程序的Runtime实例。通过getRuntime()获取到当前程序的运行环境之后,就可以做很多事了,比如说调用运行环境,执行一些cmd任务,或者外部的脚本等。由于Runtime类封装了虚拟机进程,因此,在程序中通常会通过该类的实例对象来获取当前虚拟机的相关信息。其常用方法和功能如下表所示:

2.2.1 基本方法

方法 描述
static Runtime getRuntime() 获取当前Java应用程序相关的运行时对象

2.2.2 行为控制类

方法 描述
void addShutdownHook(Thread hook) 注册新的虚拟机来关闭挂钩
boolean removeShutdownHook(Thread hook) 移除一个虚拟机关机任务,与addShutdownHook作用相反
void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的Java虚拟机
void gc() 调用该方法可以触发垃圾回收器的执行,以回收不再使用的对象占据的内存空间
System.gc()就是调用的Runtime的gc()函数
void halt(int status) 强行终止目前正在运行的Java虚拟机

2.2.3 系统信息类

方法 说明
int availableProcessors() 向 Java 虚拟机返回可用处理器的数目
long freeMemory() 返回当前Java虚拟机中的空闲内存量
long maxMemory() 返回当前Java虚拟机可以使用的最大内存量,即堆的最大容量
long totalMemory() 返回当前Java虚拟机当前已经使用的总内存量

注意:Runtime 类的 freeMemory、totalMemory、maxMemory三个方法反映的都是 Java 这个进程的内存情况,跟操作系统的内存根本没有关系。

2.2.4 exec

重载的exec()函数,可以执行指定的参数来达到执行cmd命令的目的。

方法 说明
exec(String command) 用于在操作系统中执行指定的命令,它创建一个新的进程并在其中执行给定的命令
exec(String[] cmdarray) 在单独的进程中执行指定命令和变量
exec(String[] cmdarray,String[] envp) 在指定环境的独立进程中执行指定命令和变量
exec(String[] cmdarray,String[] envp,File dir) 在指定环境和工作目录的独立进程中执行指定的命令和变量
exec(String command,String[] envp) 在指定环境的单独进程中执行指定的字符串命令
exec(String command,String[] envp,File dir) 在有指定环境和工作目录的独立进程中执行指定的字符串命令

2.2.5 其他方法

方法 说明
load(String filename) 加载作为动态库的指定文件名
loadLibrary(String libname) 加载具有指定库名的动态库
runFinalization() 运行挂起finalization的所有对象的终止方法
traceInstructions(on) 启用/禁用指令跟踪
traceMethodCalls(on) 启用/禁用方法调用跟踪

2.2.6 注意事项

这些方法允许Java应用程序与其运行时环境进行交互,获取内存使用情况、执行系统命令等操作。需要注意的是,在使用exec()方法执行系统命令时,可能需要处理输入流和输出流,以便与子进程进行通信,并正确处理可能的异常情况。在runtime执行大点的命令中,输入流和错误流会不断有流进入存储在JVM的缓冲区中,如果缓冲区的流不被读取被填满时,就会造成runtime的阻塞。所以在进行比如:大文件复制等的操作时,我们还需要不断的去读取JVM中的缓冲区的流,来防止Runtime的死锁阻塞

3. String类

3.1 字符串的添加

在Java中,String类是不可变的(immutable),这意味着一旦一个String对象被创建,它包含的内容就不能被改变。因此,你不能直接“添加”内容到一个已经存在的String对象上。但是,你可以通过几种方式来“创建”一个新的字符串,这个新字符串包含了原始字符串和其他内容。

1. 使用加号(+)操作符

最简单的方法是使用加号(+)操作符来连接字符串。这会在编译时由编译器优化,但理解其底层是创建了新的字符串对象是很重要的。

String original = "Hello, ";
String additional = "World!";
String result = original + additional; // 结果为 "Hello, World!"

2. 使用concat()方法

String类提供了一个concat()方法,可以用来连接两个字符串。与加号操作符相比,concat()方法只接受一个String类型的参数。

String original = "Hello, ";
String additional = "World!";
String result = original.concat(additional); // 结果为 "Hello, World!"

3. 使用StringBuilderStringBuffer

当需要频繁地修改字符串(例如,在循环中添加字符或字符串)时,使用StringBuilder(非线程安全)或StringBuffer(线程安全)是更好的选择。这两个类都是可变的,可以用来构建和修改字符串。

StringBuilder sb = new StringBuilder("Hello, ");
sb.append("World!");
String result = sb.toString(); // 结果为 "Hello, World!"

4. 使用String.format()方法

如果你需要按照特定的格式来构建字符串,String.format()方法是一个很好的选择。它可以让你使用占位符来指定值的插入位置。

String name = "World";
String greeting = String.format("Hello, %s!", name); // 结果为 "Hello, World!"

总结

尽管String对象本身是不可变的,但你可以通过上述方法来创建包含原始字符串和其他内容的新字符串。选择哪种方法取决于你的具体需求,例如字符串修改的频繁程度、是否需要格式化字符串等。对于简单的字符串拼接,加号操作符或concat()方法可能就足够了;而对于更复杂的场景,StringBuilder/StringBufferString.format()可能是更好的选择。

3.2 常见方法

1.构造方法:

使用常量串构造:通过直接赋值字符串常量来创建String对象。
直接new String对象:使用new关键字创建String对象。
使用字符数组进行构造:通过字符数组构造String对象。
String s1 = "hello bit";
String s2 = new String("hello bit");
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);

2.字符串比较:

使用==比较是否引用同一个对象。
使用equals(Object anObject)方法按照字典序比较字符串内容。
使用compareTo(String s)方法按照字典序进行比较。
使用compareToIgnoreCase(String str)方法进行忽略大小写比较。
String s1 = new String("hello");
String s2 = new String("world");
String s3 = s1;
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
System.out.println(s1.compareTo(s2)); // -15
System.out.println(s1.compareToIgnoreCase(s2)); // -15

3.字符串查找:

使用charAt(int index)方法返回指定位置的字符。
使用indexOf和lastIndexOf方法查找字符或子字符串的位置。
String s = "hello world";
System.out.println(s.charAt(1)); // 'e'
System.out.println(s.indexOf('o')); // 4
System.out.println(s.lastIndexOf('o')); // 7

4.字符串转化:

使用valueOf方法将数值、布尔值、对象等转化为字符串。
使用toUpperCase和toLowerCase方法进行大小写转换。
String s1 = String.valueOf(1234);
String s2 = String.valueOf(true);
String s3 = "hello";
System.out.println(s1); // "1234"
System.out.println(s2); // "true"
System.out.println(s3.toUpperCase());

3.3 正则表达式

一、什么是正则表达式

正则表达式是一组公式,是描述一种字符串匹配的格式。

正则表达式通常被用于判断语句中,用来检查某一字符串是否满足某一格式。。我们可以调用String中的matches(String regex)方法,判断字符串是否匹配给定的正则表达式,返回布尔值。

示例:

str.matches("[a-z]") 表示字符串是否是小写字母a-z中的某一字母

二、正则表达式元字符

正则表达式是含有一些具有特殊意义字符的字符串,这些特殊字符称为正则表达式的元字符。例如,"\d" 表示数0~9中的任何一个,"\d" 就是元字符。

元字符 正则表达式中的写法 意义
. "." 代表任意一个字符
\d "\d" 代表0~9的任何一个数字
\D "\D" 代表任何一个非数字字符
\s "\s" 代表空白字符,如“\t”、“\n”
\S "\S" 代表非空白字符
\w "\w" 代表可用做标识符的字符,但不包括“$”
\W "\W" 代表不可用于标识符的字符
\p{Lower} \p{Lower} 代表小写字母a~z
\p{Upper} \p{Upper} 代表大写字母A~Z
\p{ASCII} \p{ASCII} ASCII字符
\p{Alpha} \p{Alpha} 字母字符
\p{Digit} \p{Digit} 十进制数字,即0~9
\p{Alnum} \p{Alnum} 数字或字母字符
\p{Punct} \p{Punct} 标点符号:!"#$%'()+,-./:;<=>?@[]^_`{|}~
\p{Graph} \p{Graph} 可见字符:[\p{Alnum\p{Punct}}]
\p{Print} \p{Print} 可打印字符:[\p{Graph}\X20]
\p{Blank} \p{Blank} 空格或制表符:[\t]
\p{Cntrl} \p{Cntrl} 控制字符:[\x00-\x1F\x7F]

三、正则表达还是元字符使用方法

在正则表达式中可以使用方括号括起来若干个字符来表示一个元字符,该元字符可代表方括号中的任何一个字符。

例如,reg=“[abc]4”,这样字符串“a4”、“b4”、·“c4”都是和正则表达式匹配的字符串。除此之外还有很多格式。如:

[^456]:代表4、5、6之外的任何字符。

[a-r]:代表a~r中的任何一个字母。

[a-zA-Z]:可表示任意一个英文字母。

[a-e[g-z]]:代表ae,或gz中的任何一个字母(并运算)。

[a-o&&[def]]:代表字母d、e、f(交运算)。

[a-d&&[^bc]]:代表字母a、d(差运算)。

(ab)|(13)|(50):代表”ab”、”13”、”50”中任意一值。

四、正则表达式限定符

在正则表达式中允许使用限定修饰符来限定元字符出现的次数。例如,"A*"代表A可在字符串中出现0次或多次。

限定修饰符 意义 示例
? 0次或1次 A?

0次或多次 A*

一次或多次 A+
{n} 正好出现n次 A{2}
{n,} 至少出现n次 A{3,}
{n,m} 出现n次到m次 A{2,6}
正则表达式实例使用正则表达式来判断指定的变量是否是合法的E-mail地址。

设计思路:

[邮箱名] @ [服务器名称] [中间名] . [后缀]
合法字符串@ 合法字符串. 字符串(可无). 字符串(最大三个字符)

\w+ @ \w+ \.\w{2,3} \. \w{2,3}

五、正则表达式示例

验证字符串是不是大陆手机号,大陆手机号是11位,前三位分别是130、131、132、133、134、134、135、136、137、138、139、150、151、152、153、155、156、157、158、159、180、185、186、187、188、189等

代码:

import java.util.Scanner;

public class lianxi {

public static void main(String[] args) {

​	Scanner sc = new Scanner(System.in);
​	System.out.println("请输入手机号:");
​	String regex = "(13[0-9]|15[012356789]|18[056789])\\d{8}";

​	while (true) {
​		String phone = sc.nextLine();
​		if (phone.matches(regex)) {
​			System.out.println("您输入的是手机号");
​		} else {
​			System.out.println("您输入的不是手机号");
​		}
​	}
}

}

3.4 StringBuffer和StringBuilder

在 Java 编程中,字符串是使用非常频繁的数据类型。由于 String 类的实例是不可变的,对字符串的操作如拼接、修改等会产生大量的临时字符串对象,这可能会导致内存的浪费。为了优化这种情况,Java 提供了 StringBuffer 和 StringBuilder 类。这两个类提供了可变的字符串,使得字符串的操作更加灵活和高效。

一、StringBuffer 类

StringBuffer 类在 Java 中用于创建可变的字符串。当你需要对字符串内容进行修改时,使用 StringBuffer 可以避免生成多余的临时对象,从而提高性能。StringBuffer 是线程安全的,意味着它的方法是同步的,可以在多线程环境下安全地使用。

1.基本用法
public class StringBufferExample {
public static void main(String[] args) {
// 创建 StringBuffer 对象
StringBuffer sBuffer = new StringBuffer("初始字符串");

// 追加字符串
sBuffer.append(" - 追加的字符串");
System.out.println(sBuffer); // 输出: 初始字符串 - 追加的字符串

// 插入字符串
sBuffer.insert(0, "开始: ");
System.out.println(sBuffer); // 输出: 开始: 初始字符串 - 追加的字符串

// 替换字符串
sBuffer.replace(0, 3, "开端: ");
System.out.println(sBuffer); // 输出: 开端: 初始字符串 - 追加的字符串

// 删除字符串
sBuffer.delete(0, 4);
System.out.println(sBuffer); // 输出: 初始字符串 - 追加的字符串

// 反转字符串
sBuffer.reverse();
System.out.println(sBuffer); // 输出: 字符串的加追 - 字符串始初始
}
}
2.主要方法
append(String str): 追加字符串到当前 StringBuffer 对象的末尾。
insert(int offset, String str): 在指定位置插入字符串。
delete(int start, int end): 删除开始到结束位置的字符。
replace(int start, int end, String str): 使用给定的字符串替换当前字符串序列的子字符串。
reverse(): 将此字符序列用其反转形式取代。

二、StringBuilder 类

StringBuilder 类与 StringBuffer 类似,也是用于创建可变的字符串对象。不同之处在于,StringBuilder 不是线程安全的,它的方法不是同步的。因此,如果你的应用不是多线程的,那么使用 StringBuilder 可以获得比 StringBuffer 更好的性能。

1.基本用法
public class StringBuilderExample {
public static void main(String[] args) {
// 创建 StringBuilder 对象
StringBuilder sb = new StringBuilder("初始字符串");

// 追加字符串
sb.append(" - 追加的字符串");
System.out.println(sb); // 输出: 初始字符串 - 追加的字符串

// 插入字符串
sb.insert(0, "开始: ");
System.out.println(sb); // 输出: 开始: 初始字符串 - 追加的字符串

// 替换字符串
sb.replace(0, 3, "开端: ");
System.out.println(sb); // 输出: 开端: 初始字符串 - 追加的字符串

// 删除字符串
sb.delete(0, 4);
System.out.println(sb); // 输出: 初始字符串 - 追加的字符串

// 反转字符串
sb.reverse();
System.out.println(sb); // 输出: 字符串的加追 - 字符串始初始
}
}
StringBuilder 类的方法与 StringBuffer 类相似,不过它们不是同步的。

append(String str): 追加字符串到当前 StringBuilder 对象的末尾。
insert(int offset, String str): 在指定位置插入字符串。
delete(int start, int end): 删除。
replace(int start, int end, String str): 使用给定的字符串替换当前字符串序列的子字符串。
reverse(): 将此字符序列用其反转形式取代。

三、选择 StringBuffer 还是 StringBuilder?

多线程环境:如果你的应用运行在多线程环境中,并且需要对字符串内容进行修改,那么应该选择 StringBuffer。StringBuffer 中的大多数方法都是同步的,可以确保线程安全。
单线程环境:如果你的应用是单线程的,或者你确信字符串操作不会在多线程环境中使用,那么 StringBuilder 是更好的选择。因为其方法不是同步的,所以在性能上会比 StringBuffer 更优。

四、总结

StringBuffer 和 StringBuilder 都是为了解决字符串操作中的性能问题而设计的。它们提供了一种可变的字符串,允许进行各种字符串的修改操作,而不会产生大量的临时对象。在选择使用 StringBuffer 还是 StringBuilder 时,主要考虑是不是在多线程环境中使用。理解它们的不同之处和适用场景,可以帮助你在 Java 编程中更有效地处理字符串。

4. 日期时间类

一、Date类

  1. 基本概念

java.util 包提供了 Date 类来封装当前的日期和时间。 Date 类提供两个构造函数来实例化 Date 对象。

第一个构造函数使用当前日期和时间来初始化对象。

Date( )

第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。

Date(long millisec)

Date date2= new Date();
System.out.println(date2);

//参数表示1970-01-01 00:00:00到指定时间的毫秒数
Date date1 = new Date(14686531L);
System.out.println(date1);
2. #### 常见方法

  1. 2.1 日期比较

Java使用以下三种方法来比较两个日期:

使用 getTime() 方法获取两个日期(自1970年1月1日经历的毫秒数值),然后比较这两个值。
使用方法 before(),after() 和 equals()。例如,一个月的12号比18号早,则 new Date(99, 2, 12).before(new Date (99, 2, 18)) 返回true。
使用 compareTo() 方法,它是由 Comparable 接口定义的,Date 类实现了这个接口。

2.2 使用 SimpleDateFormat 格式化日期

因为使用Date类与字符串时间相互转化十分困难,因此,可以考虑通过Date类的其他子类来间接进行转换。DateFormat是日期/时间格式化子类的抽象类,它与以语言无关的方式格式化并解析日期或时间,是抽象类。所以使用其子类SimpleDateFormat实现日期和字符串的相互转换。

public static void main(String[] args) throws Exception {

// 定义输出格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 将字符串转化为日期
Date date = sdf.parse("2023-11-11 11:11:11");
System.out.println(date);

Date date = new Date();
// 将日期转化为字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
String str = sdf.format(date);
System.out.println(str);
}

二、DateFormat类

  1. 基本概念

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

格式化:按照指定的格式,从Date对象转换为String对象。(format)
解析:按照指定的格式,从String对象转换为Date对象。(parse)
2. #### 常见方法

2.1 构造方法
由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:

public SimpleDateFormat(String pattern):用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。

参数pattern是一个字符串,代表日期时间的自定义格式。

格式规则

常用的格式规则为:

标识字母(区分大小写) 含义
y 年
M 月
d 日
H 时
m 分
s 秒
创建SimpleDateFormat对象的代码如下:

import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class Demo02SimpleDateFormat {
public static void main(String[] args) {
// 对应的日期格式如:2023-11-11 11:11:11
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
}
2.2 DateFormat类常用方法
DateFormat类的常用方法有:

  1. public String format(Date date):将Date对象格式化为字符串。

format方法的代码如下:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把Date对象转换成String
*/
public class Demo03DateFormatMethod {
public static void main(String[] args) {
Date date = new Date();
// 创建日期格式化对象,在获取格式化对象时可以指定风格
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
String str = df.format(date);
System.out.println(str); // 2020年09月19日
}
}
2. public Date parse(String source):将字符串解析为Date对象。

parse方法的代码如下:

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
把String转换成Date对象
*/
public class Demo04DateFormatMethod {
public static void main(String[] args) throws ParseException {
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日");
String str = "2023年11月11日";
Date date = df.parse(str);
System.out.println(date); // Tue Dec 11 00:00:00 CST 2023
}
}

三、Calendar类

  1. 基本概念
    我们现在已经能够格式化并创建一个日期对象了,但是我们如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。

Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。

Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可。

java.util.Calendar是日历类,在Date后出现,替换掉了许多Date的方法。该类将所有可能用到的时间信息封装为静态成员变量,方便获取。日历类就是方便获取各个时间属性的。

  1. 常用方法
    2.1 构造方法
    Calendar为抽象类,由于语言敏感性,Calendar类在创建对象时并非直接创建,而是通过静态方法创建,返回子类对象,如下:

Calendar静态方法

public static Calendar getInstance():使用默认时区和语言环境获得一个日历
例如:

import java.util.Calendar;

public class Demo06CalendarInit {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
}
}
2.2 常用方法
根据Calendar类的API文档,常用方法有:

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对象。
Calendar类中提供很多成员常量,代表给定的日历字段:

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

  1. get/set方法

get方法用来获取指定字段的值,set方法用来设置指定字段的值。代码实例如下:

import java.util.Calendar;
public class Demo {
public static void main(String[] args) {
// 创建Calendar对象
Calendar cal = Calendar.getInstance();
// 获取年
int year = cal.get(Calendar.YEAR);
// 获取月
int month = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);

System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
}
}
import java.util.Calendar;

public class Demo07CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 设置年
cal.set(Calendar.YEAR, 2023);
// 获取年
int year = cal.get(Calendar.YEAR);
// 获取月
int month = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);

System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
}
}

  1. add方法

add方法可以对指定日历字段的值进行加减操作,如果第二个参数为正数则加上偏移量,如果为负数则减去偏移量。代码如:

import java.util.Calendar;

public class Demo08CalendarMethod {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
// 获取年
int year = cal.get(Calendar.YEAR);
// 获取月
int month = cal.get(Calendar.MONTH) + 1;
// 获取日
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "年" + month + "月" + dayOfMonth + "日");

// 使用add方法
cal.add(Calendar.DAY_OF_MONTH, 5); // 加5天
cal.add(Calendar.YEAR, -2); // 减2年
// 获取年
year = cal.get(Calendar.YEAR);
// 获取月
month = cal.get(Calendar.MONTH) + 1;
// 获取日
dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
System.out.println(year + "年" + month + "月" + dayOfMonth + "日");
}
}

  1. getTime方法:返回对应的Date对象

Calendar中的getTime方法并不是获取毫秒时刻,而是拿到对应的Date对象。

import java.util.Calendar;
import java.util.Date;

public class Demo {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
System.out.println(date);
}
}
注意事项:

西方星期的开始为周日(1)周一(2),中国开始为周一,因此可以-1使用。
在Calendar类中,月份的表示是以0-11代表1-12月(可以+1使用)。
日期是有大小关系的,时间靠后,时间越大。
总结
Java早期通过使用Date来计算时间,后来大部分Date类的方法都过时了,又转而希望用Calendar类代替Date类,然而Calendar类也是不尽如人意。因此专门在本文中对Java中Date类的基本概念、常见操作及相关类进行总结和归纳,方便日后的开发,希望对大家有所帮助。

5. Math类

/**
* 常量
*/
System.out.println("------>" + Math.E);//2.718281828459045
System.out.println("------>" + Math.PI);//3.141592653589793

/**
* Math.abs()计算绝对值
*/
System.out.println("------>" + Math.abs(-3));//3

/**
* 三角函数与反三角函数
* cos求余弦
* sin求正弦
* tan求正切
* acos求反余弦
* asin求反正弦
* atan求反正切
* atan2(y,x)求向量(x,y)与x轴夹角
* cosh计算值的双曲余弦
* sinh计算双曲正弦
* tanh计算双曲正切
*/
System.out.println("------>" + Math.acos(1));
System.out.println("------>" + Math.acos(-1));

/**
* Math.cbrt()开立方根
*/
System.out.println("------>" + Math.cbrt(-1));//-1.0
System.out.println("------>" + Math.cbrt(1));//1.0
System.out.println("------>" + Math.cbrt(0.5));//0.7937005259840998
System.out.println("------>" + Math.cbrt(5));//1.709975946676697

/**
* Math.sqrt(x)开平方
*/
System.out.println("------>" + Math.sqrt(4.0));//2.0

/**
* Math.hypot(x,y)求sqrt(xx+yy)在求两点间距离时有用sqrt((x1-x2)2+(y1-y2)2)
*/
System.out.println("------>" + Math.hypot(3.0, 4.0));//5.0

/**
* ceil天花板,向上取整,返回大的值
*/
System.out.println("------1>" + Math.ceil(7.2));//8.0
System.out.println("------2>" + Math.ceil(7.5));//8.0
System.out.println("------3>" + Math.ceil(7.6));//8.0
System.out.println("------4>" + Math.ceil(-7.2));//-7.0
System.out.println("------5>" + Math.ceil(-7.5));//-7.0
System.out.println("------6>" + Math.ceil(-7.6));//-7.0

/**
* floor地板,向下取整,返回小的值
*/
System.out.println("------1>" + Math.floor(7.2));//7.0
System.out.println("------2>" + Math.floor(7.5));//7.0
System.out.println("------3>" + Math.floor(7.6));//7.0
System.out.println("------4>" + Math.floor(-7.2));//-8.0
System.out.println("------5>" + Math.floor(-7.5));//-8.0
System.out.println("------6>" + Math.floor(-7.6));//-8.0

/**
* Math.cosh()返回 double 值的双曲线余弦。x 的双曲线余弦的定义是 (ex + e-x)/2,其中 e 是欧拉数
*/
System.out.println("------>" + Math.cosh(1));//1.543080634815244
System.out.println("------>" + Math.cosh(0));//1.0

/**
* exp(x) 返回e^x的值
* expm1(x) 返回e^x - 1的值
* pow(x,y) 返回x^y的值
* 这里可用的数据类型也只有double型
*/
System.out.println("------>" + Math.exp(2));//7.38905609893065
System.out.println("------>" + Math.expm1(2));//6.38905609893065
System.out.println("------>" + Math.pow(2.0, 3.0));//8.0

/**
* 对数
* Math.log(a) a的自然对数(底数是e)
* Math.log10(a) a 的底数为10的对数
* Math.log1p(a) a+1的自然对数
* 值得注意的是,前面其他函数都有重载,对数运算的函数只能传double型数据并返回double型数据
*/
System.out.println("------1>" + Math.log(Math.E));//1.0
System.out.println("------2>" + Math.log10(10));//1.0
System.out.println("------3>" + Math.log1p(Math.E - 1.0));//1.0

/**
* Math.max()求最大值
* Math.min()求最小值
*/
System.out.println("------1>" + Math.max(1, 2));//2
System.out.println("------2>" + Math.min(1, -2));//-2

/**
* Math.nextAfter()返回与第二个参数方向相邻的第一个参数的浮点数。
*/
System.out.println("------1>" + Math.nextAfter(-1, 2));//-0.99999994
System.out.println("------2>" + Math.nextAfter(1, 2));//1.0000001

/**
* Math.nextUp()返回与正无穷大方向相邻的 d的浮点值。
*/
System.out.println("------>" + Math.nextUp(1));//1.0000001
System.out.println("------>" + Math.nextUp(-1));//-0.99999994

/**
* Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范围是[0.0,1.0)的左闭右开区间,
* 返回值是一个伪随机选择的数,在该范围内(近似)均匀分布
*/
System.out.println("------>" + Math.random());//取值范围是[0.0,1.0)的随机数

/**
* Math.rint(x):x取整为它最接近的整数,如果x与两个整数的距离相等,则返回其中为偶数的那一个。
*/
System.out.println("------>" + Math.rint(3.5));//4.0
System.out.println("------>" + Math.rint(4.5));//4.0

System.out.println("------>" + Math.rint(3.1));//3.0
System.out.println("------>" + Math.rint(4.1));//4.0

System.out.println("------>" + Math.rint(3.7));//4.0
System.out.println("------>" + Math.rint(4.7));//5.0
/**
* Math.round(x):返回Math.floor(x+0.5),即“四舍五入”值。
*/
System.out.println("------>" + Math.round(3.5));//4
System.out.println("------>" + Math.round(4.5));//5

System.out.println("------>" + Math.round(3.1));//3
System.out.println("------>" + Math.round(4.7));//5

/**
* Math.scalb(double d, int scaleFactor),返回 f × 2scaleFactor,其舍入方式如同将一个正确舍入的浮点值乘以 float
* 值集合中的一个值
* 返回 d 和正无穷大之间与 d 相邻的浮点值 tatic double nextUp(double d)
*/
System.out.println("------1>" + Math.scalb(1.5d, 6));//96

/**
* Math.scalb(float f, int scaleFactor)
* 返回 f 和正无穷大之间与 f 相邻的浮点值 tatic float nextUp(float f)
* 返回第一个参数和第二个参数之间与第一个参数相邻的浮点数
*/
System.out.println("------2>" + Math.scalb(1.5f, 6));//96

/**
* Math.signum方法返回指定int值的符号函数(如果指定值为负,则返回-1;如果指定值为零,则返回 0;如果指定值为正,则返回1)。
*/
System.out.println("------1>" + Math.signum(10));//1.0
System.out.println("------2>" + Math.signum(-10));//-1.0
System.out.println("------3>" + Math.signum(0));//0.0

/**
* Math.toDegrees()将弧度转换角度
*/
System.out.println("------3>" + Math.toDegrees(1.57));//89.95437383553926

/**
* Math.toRadians()将角度转换为弧度
*/
System.out.println("------3>" + Math.toRadians(90));//1.5707963267948966

/**
* Math.ulp()
* 如果要理解什么是ulp(unit in the last place or unit of least precision (ULP))
* 先要了解在计算机中保存的数和我们在数学上认为的数是不一样的;
* 比方说2.0和3.0之间有多少个数,在数学中是无限的,但是在计算机中是有限的,
* 因为计算机需要用一堆字节来表示double或者float,但是因为计算机表示不了无限的数(因为没有无限内存)。
*
* 所以就有了ulp,假设在float 2.0和3.0之间有8,388,609个数,那么在2.0和3.0之间的数的ulp就是8,388,609/1.0约等于0.0000001。
*
* 你如果想知道某一个具体的double或float的先一个或者上一个数字是什么可以使用函数
* public static double nextAfter(float start, float direction)
* public static double nextAfter(double start, double direction)
*/

/**
* java8 Math新增方法
/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
/
*
* Math.addExact()求和,如果结果溢出取值范围,则引发异常。
*/
System.out.println("------>" + Math.addExact(1, 2));//3

/**
* Math.substractExact()方法返回两个参数之差,结果溢出时抛出ArithmeticException
*/
System.out.println("------>" + Math.subtractExact(100, 50));//50

/**
* Math.incrementExact() 方法 返回参数值加一,结果溢出时抛出ArithmeticException
*/
System.out.println("------>" + Math.incrementExact(100));//101

/**
* Math.decrementExact() 方法 返回参数值减一,结果溢出时抛出ArithmeticException
*/
System.out.println("------>" + Math.decrementExact(100));//99

/**
* Math.multiplyExact() 方法 返回两个参数之积,结果溢出时抛出ArithmeticException
*/
System.out.println("------>" + Math.multiplyExact(100, 5));//500

/**
* Math.negateExact() 方法 改变参数符号,结果溢出时抛出ArithmeticException
*/
System.out.println("------>" + Math.negateExact(100));//-100

/**
* Math.floorDiv(1,2)第一个参数除以第二参数,然后针对结果执行floor操作,返回小于或等于商的整数:
*/
System.out.println("------>" + Math.floorDiv(7, 3));//2
System.out.println("------>" + Math.floorDiv(-7, 3));//-3

/**
* Math.floorMod()
* 1、如果参数的符号相同,则floorMod和%运算符的结果是相同的。
* 2、如果参数的符号不同,则结果与%运算符不同。
*/
//如果参数的符号相同,则floorMod和%运算符的结果是相同的。
System.out.println("------1>" + Math.floorMod(4, 3));//1
System.out.println("------2>" + (4 % 3));//1

//如果参数的符号不同,则结果与%运算符不同。
System.out.println("------3>" + Math.floorMod(4, -3));//-2
System.out.println("------4>" + (4 % -3));//1

System.out.println("------5>" + Math.floorMod(-4, 3));//2
System.out.println("------6>" + (-4 % 3));//-1

System.out.println("------7>" + Math.floorMod(-4, -3));//-1
System.out.println("------8>" + (-4 % -3));//-1

/**
* Math.toIntExact(),long转int
*/
System.out.println("------1>" + Math.toIntExact(1));

/**
* Math.nextDown()返回与负无穷大方向相邻的 f的浮点值。
*/
System.out.println("------>" + Math.nextDown(1));//0.99999994
System.out.println("------>" + Math.nextDown(-1));//-1.0000001
}

6. 包装类

6.1 什么是包装类

在Java中,包装类(Wrapper Classes)是Java提供的一组类,它们将基本数据类型封装为对象。Java是一种面向对象的语言,但基本数据类型(如int, double, char等)并不是对象,它们只是简单的数据类型,不具备对象的特性,如继承、多态和方法等。为了能在对象世界中使用这些基本数据类型,Java为每种基本数据类型都提供了一个包装类。

6.2 包装类的特点

包装类(Wrapper Classes)在Java中扮演着重要角色,它们的特点主要体现在以下几个方面:

  1. 封装性

    • 包装类将基本数据类型封装为对象,使得基本数据类型能够具有对象的特性。这意味着包装类对象可以被当作对象一样处理,比如可以被赋值给对象变量,可以作为方法的参数传递,也可以作为方法的返回值。
  2. 提供额外功能

    • 每种包装类都提供了一系列静态和实例方法,这些方法为基本数据类型提供了额外的功能。例如,Integer类提供了将整数转换为字符串的toString(int i)方法,将字符串解析为整数的parseInt(String s)静态方法,以及比较两个整数大小的compareTo(Integer anotherInteger)实例方法等。
  3. 自动装箱和拆箱

    • 从Java 5(JDK 1.5)开始,引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)的概念。这意味着在基本数据类型和它们对应的包装类之间转换时,Java编译器会自动插入必要的代码,使得转换过程更加简便。例如,当你将一个基本数据类型赋值给一个包装类对象时,编译器会自动进行装箱操作;相反,当你将一个包装类对象赋值给一个基本数据类型变量时,编译器会自动进行拆箱操作。
  4. 缓存机制(针对某些包装类):

    • 为了提高性能,某些包装类(如IntegerByteShortCharacter,在Java 1.5及以后版本中)对于特定范围内的值提供了缓存机制。这意味着当创建这些范围内的包装类对象时,实际上是从缓存中获取已存在的对象,而不是创建一个新的对象。这个特性减少了对象的创建和销毁,提高了性能。但是,需要注意的是,超出缓存范围的包装类对象仍然会被正常创建。
  5. 支持泛型

    • 由于包装类是对象,因此它们可以被用作泛型类型参数。这使得我们可以在泛型集合(如ArrayList<Integer>)中存储基本数据类型,同时保留了类型安全。
  6. 与基本数据类型的关系

    • 包装类与它们对应的基本数据类型之间存在紧密的关系,但它们是完全不同的类型。因此,在比较包装类对象时,如果直接使用==操作符,那么比较的是对象的引用(即地址),而不是它们的值。为了比较包装类对象的值,应该使用equals()方法。
  7. 不可变性

    • 除了StringBuilderStringBuffer(尽管它们名字上类似于包装类,但实际上不是,它们是可变的字符序列类),大多数包装类(如IntegerDouble等)都是不可变的。一旦一个包装类对象被创建,它的值就不能被改变。这意呀着,每次当你需要修改一个包装类对象的值时,实际上都是创建了一个新的包装类对象。

6.3 基本数据类型与包装类

在Java中,基本数据类型(Primitive Types)和包装类(Wrapper Classes)是两个非常重要的概念,它们之间既有关联又有区别。

基本数据类型

基本数据类型是Java中最基本的数据类型,它们不是对象,而是直接在栈内存中分配空间。Java定义了八种基本数据类型,用于存储不同类型的值:

  • byte:8位,有符号的二进制补码整数
  • short:16位,有符号的二进制补码整数
  • int:32位,有符号的二进制补码整数(默认类型)
  • long:64位,有符号的二进制补码整数
  • float:32位,IEEE 754标准的单精度浮点数
  • double:64位,IEEE 754标准的双精度浮点数(默认浮点类型)
  • char:16位,Unicode字符
  • boolean:表示真值的true和假值的false

包装类

包装类(Wrapper Classes)是Java提供的一组类,它们将基本数据类型封装为对象。由于Java是一种面向对象的语言,而基本数据类型不是对象,因此Java提供了包装类以便在需要对象的地方使用基本数据类型。包装类都是不可变的,一旦创建,其值就不能被改变。

包装类包括:

  • Byte:对应byte
  • Short:对应short
  • Integer:对应int
  • Long:对应long
  • Float:对应float
  • Double:对应double
  • Character:对应char
  • Boolean:对应boolean

基本数据类型与包装类的关系

  1. 自动装箱与拆箱:Java 5(JDK 1.5)引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)机制,使得基本数据类型与包装类之间的转换更加简便。自动装箱是指将基本数据类型自动转换为包装类对象,自动拆箱则是指将包装类对象自动转换为基本数据类型。
  2. 用途差异:基本数据类型主要用于简单的数值计算和逻辑判断,而包装类则主要用于对象操作,如集合(如ListSet)中只能存储对象,因此需要用到包装类。
  3. 默认值:基本数据类型有默认值(如int的默认值是0),而包装类作为对象,其默认值是null,表示不指向任何对象。
  4. 内存分配:基本数据类型直接在栈内存中分配空间,而包装类对象在堆内存中分配空间,栈内存中存储的是对象的引用(即地址)。
  5. 方法调用:包装类提供了丰富的方法来操作数据,如Integer.parseInt(String s)用于将字符串转换为整数,而基本数据类型则没有这样的方法。
  6. 不可变性:包装类对象一旦创建,其值就不能被改变(除了StringBuilderStringBuffer,它们是可变的字符序列类,但不是包装类)。这意呀着,当你需要修改一个包装类对象的值时,实际上是创建了一个新的包装类对象。

综上所述,基本数据类型与包装类在Java中各有其用途和特点,它们之间通过自动装箱与拆箱机制相互转换,共同构成了Java语言的数据类型体系。

6.4 包装类共同点

Java中的包装类(Wrapper Classes)是将基本数据类型封装为对象的特殊类。这些包装类之间具有一些共同点,以下是对这些共同点的归纳:

  1. 封装性

    • 包装类将基本数据类型封装为对象,使得基本数据类型能够具有对象的特性。这意味着包装类对象可以被当作对象一样处理,比如可以被赋值给对象变量,可以作为方法的参数传递,也可以作为方法的返回值。
  2. 提供额外功能

    • 每种包装类都提供了一系列静态和实例方法,这些方法为基本数据类型提供了额外的功能。例如,Integer类提供了将整数转换为字符串的toString(int i)方法,将字符串解析为整数的parseInt(String s)静态方法,以及比较两个整数大小的compareTo(Integer anotherInteger)实例方法等。
  3. 重写Object方法

    • 所有包装类都重写了Object类的一些方法,如equals(Object obj)hashCode()。这些方法在包装类中被重写以提供基于值(而非引用)的比较和哈希码计算。这对于将包装类对象用作哈希表的键或进行对象比较时非常重要。
  4. 自动装箱和拆箱

    • 从Java 5(JDK 1.5)开始,引入了自动装箱和拆箱机制。这意味着在基本数据类型和它们对应的包装类之间转换时,Java编译器会自动插入必要的代码,使得转换过程更加简便。例如,Integer a = 10; 会被编译器自动转换为 Integer a = Integer.valueOf(10);,而 int b = a; 则会被转换为 int b = a.intValue();
  5. 缓存机制(针对部分包装类):

    • 为了提高性能,某些包装类(如IntegerByteShortCharacter)对于特定范围内的值提供了缓存机制。这意味着当创建这些范围内的包装类对象时,实际上是从缓存中获取已存在的对象,而不是创建一个新的对象。这个特性减少了对象的创建和销毁,提高了性能。
  6. 不可变性

    • 除了StringBuilderStringBuffer(尽管它们名字上类似于包装类,但实际上不是)之外,大多数包装类都是不可变的。一旦一个包装类对象被创建,它的值就不能被改变。这意呀着,每次当你需要修改一个包装类对象的值时,实际上都是创建了一个新的包装类对象。
  7. 实现Comparable接口

    • 除了Boolean包装类之外,其他所有包装类都实现了Comparable接口。这使得它们可以相互比较大小,并且可以使用在需要排序的集合(如TreeSet)或数组(如使用Arrays.sort())中。
  8. 提供静态常量

    • 大多数包装类都定义了一些静态常量,这些常量表示该类型的基本数据类型的最大值、最小值等。例如,Integer.MAX_VALUE表示int类型能表示的最大值。
  9. 与基本数据类型的紧密关系

    • 包装类与它们对应的基本数据类型之间存在紧密的关系,但它们是完全不同的类型。在比较包装类对象时,如果直接使用==操作符,那么比较的是对象的引用(即地址),而不是它们的值。为了比较包装类对象的值,应该使用equals()方法。

这些共同点使得包装类在Java编程中扮演着重要的角色,它们不仅提供了基本数据类型的对象封装,还通过提供额外的方法和特性,增强了Java语言的灵活性和功能。

6.5 自动装箱/拆箱

在Java中,自动装箱(Autoboxing)和自动拆箱(Unboxing)是Java 5(JDK 1.5)引入的两个特性,它们简化了基本数据类型与它们对应的包装类之间的转换过程。

自动装箱

自动装箱是指将基本数据类型(如intdouble等)自动转换为它们对应的包装类对象(如IntegerDouble等)的过程。这个过程是由Java编译器自动完成的,程序员不需要编写额外的代码。

示例

java复制代码

Integer num = 10; // 自动装箱,等同于 Integer num = Integer.valueOf(10);

在上面的例子中,虽然我们将一个基本数据类型int的值10直接赋值给了Integer类型的变量num,但实际上Java编译器在背后为我们调用了Integer.valueOf(int i)方法,将基本数据类型转换为了包装类对象。

自动拆箱

自动拆箱是指将包装类对象自动转换为它们对应的基本数据类型的过程。与自动装箱类似,这个过程也是由Java编译器自动完成的。

示例

Integer num = 10;  
int value = num; // 自动拆箱,等同于 int value = num.intValue();

在上面的例子中,虽然我们将一个Integer类型的对象num直接赋值给了基本数据类型int的变量value,但实际上Java编译器在背后为我们调用了num.intValue()方法,将包装类对象转换为了基本数据类型。

注意事项

  1. 性能考虑:虽然自动装箱和拆箱使得代码更加简洁,但它们可能会引入额外的性能开销。因为每次装箱和拆箱操作都可能涉及到对象的创建和销毁(尽管对于小范围的整数,Integer等包装类会使用缓存机制来减少这种开销)。
  2. 空指针异常:自动拆箱时,如果包装类对象为null,则会抛出NullPointerException。这是因为基本数据类型不能为null,而尝试从一个null的包装类对象中获取基本数据类型值是不可能的。
  3. 比较操作:在进行比较操作时,特别是与null或基本数据类型混合使用时,需要特别注意。例如,Integer num = null; if (num == 5) 这样的代码会抛出NullPointerException,因为numnull,而5是基本数据类型,它们之间不能直接比较。应该使用Integer.equals()方法来比较两个Integer对象,或者使用num != null && num == 5这样的表达式来避免空指针异常。
  4. 最佳实践:在性能敏感的代码区域,尽量避免不必要的装箱和拆箱操作。同时,在使用集合(如ListSet)时,如果元素类型可以是null,则应该优先考虑使用包装类,因为基本数据类型无法直接存储null值。
posted @ 2024-08-05 15:56  白11  阅读(66)  评论(0)    收藏  举报