<Java>12 常用类
本文最后更新于:2023年6月27日 上午
12 常用类
12.1 包装类
包装类(Wrapper):针对 八种基本数据类型 相应的 引用类型
有了类的特点,就可以调用类中的方法
基本数据类型 | 包装类 | 父类 |
---|---|---|
boolean | Boolean | Object |
char | Character | Object |
int | Integer | Number |
float | Float | Number |
double | Double | Number |
long | Long | Number |
short | Short | Number |
byte | Byte | Number |
void | Void | Object |
12.1.1 装箱和拆箱
-
手动装箱和拆箱(JDK 5 以前)
int n1 = 100; Integer integer = new Integer(n1); // 手动装箱 Integer integer2 = Integer.valueOf(n1); // 手动装箱 int i = integer.intValue(); // 手动拆箱
-
自动装箱和拆箱(JDK 5 以后)
n2 = 200; Integer integer3 = n2; // 自动装箱 int j = integer3; // 自动拆箱
虽然可以自动装箱、拆箱,但使用 == 直接比较两个包装类时,仍然是比较其地址。以下比较通常会失败:
Integer ia = 1000; Integer ib = 1000; System.out.print(ia == ib); // false
但,Java 实现仍有可能使其成立。Byte、Boolean 以及 Short、Integer 中 [-128, 127] 间的值已被包装到固定的对象中。对他们的比较可以成功。
Integer ia = 127; Integer ib = 127; System.out.print(ia == ib); // true
由此可见,使用 == 直接比较两个包装类会带来不确定性。尽量使用 equals 方法对包装类进行比较。
装箱与拆箱是 编译器 的工作。在生成可执行的字节码文件时,编译器已经插入了必要的方法调用。
12.1.2 包装类和 String
的相互转换
-
包装类转
String
:>Integer integer = 100; >String str1 = integer + ""; //方法1(自动拆箱) >String str2 = integer.toString(); //方法2(toString方法) >String str3 = String.valueOf(integer); //方法3(自动拆箱)
-
String
转包装类:String str4 = "100"; Integer integer2 = Integer.parseInt(str4); //方法1(自动装箱) Integer integer3 = new Integer(str4); //方法2(构造器)
12.1.3 包装类的常用方法
-
Integer.MIN_VALUE
:返回最大值 -
Double.MAX_VALUE
:返回最小值 -
byteValue()
、doubleValue()
、floatValue()
、intValue()
、longValue()
按各种基本数据类型返回该对象的值
-
Character.isDigit(int)
:判断是不是数字Character.isLetter(int)
:判断是不是字母Character.isUpperCase(int)
:判断是不是大写字母Character.isLowerCase(int)
:判断是不是小写字母Characher.isWhitespace(int)
:判断是不是空格 -
Character.toUpperCase(int)
:转成大写字母Character.toLowerCase(int)
:转成小写字母 -
Integer.parseInt(string)
:将 String 内容转为 intDouble.parseDouble(string)
-
Integer.toBinaryString(int)
:将数字转为 2 进制表示的字符串Integer.toHexString(int)
:将数字转为 16 进制表示的字符串Integer.toOctalString(int)
:将数字转为 8 进制表示的字符串特别地,浮点数类型的包装类只有转成 16 进制的方法。而 Short、Byte 及其他包装类无此方法
-
int Integer.bitCount(i int)
:统计指定数字的二进制格式中 1 的数量
12.1.4 strictfp 关键字
由于不同处理器对于浮点数寄存采取不同策略(有些处理器使用 64 位寄存 double,有些则是 80 位),对于浮点数的运算在不同平台上可能出现不同结果。
使用 strictfp 关键字标记的方法或类中,所有指令都会使用严格统一的浮点数运算。
比如,把 main 方法标记为 strictfp
public static strictfp void main(String[] args) {
double ᓚᘏᗢ = 1 / 13.97;
System.out.println(ᓚᘏᗢ);
}
12.2 String
类
-
String
对象用于保存字符串,也就是一组字符序列 -
字符串常量对象是用双引号扩起的字符序列。例如
"你好"
-
字符串的字符使用 Unicode 字符编码。一个字符(不论字母汉字)占 2 字节
-
常用构造器:
-
String str1 = new String();
-
String str2 = new String(String original);
-
String str3 = new String(char[] a);
-
String str4 = new String(char[] a, int startIndex, int count);
这句意思是:
char[]
从startIndex
起的count
个字符
-
-
String
实现了接口Serializable
和Comparable
,可以 串行化和 比较大小串行化:即,可以被网络传输,也能保存到文件
-
String
是final
类,不能被继承 -
String
有属性private final char[] value;
用于存放字符串内容。value
是final
属性。其在栈中的地址不能修改,但堆中的内容可以修改。
12.2.1 String
构造方法
-
直接指定
String str1 = "哈哈哈";
该方法:先从常量池看是否有
"哈哈哈"
数据空间。有的场合,指向那个空间;否则重新创建然后指向。这个方法,
str1
指向 常量池中的地址。 -
构造器
String str2 = new String("嘿嘿嘿");
该方法:先在堆中创建空间,里面维护一个
value
属性,指向 或 创建后指向 常量池的"嘿嘿嘿"
空间。这个方法,
str2
指向 堆中的地址
12.2.2 字符串的特性
-
常量相加,看的是池
String str1 = "aa" + "bb"; //常量相加,看的是池
上例由于构造器自身优化,相当于
String str1 = "aabb";
-
变量相加,是在堆中
String a = "aa"; String b = "bb"; String str2 = a + b; //变量相加,是在堆中
上例的底层是如下代码
StringBuilder sb = new StringBuilder(); sb.append(a); sb.append(b); str2 = sb.toString(); //sb.toString():return new String(value, 0, count);
12.2.3 String
的常用方法
以下方法不需死记硬背,手熟自然牢记
-
boolean equals(String s)
:区分大小写,判断内容是否相等boolean equalsIgnoreCase(String s)
:判断内容是否相等(忽略大小写) -
boolean empty()
:返回是否为空 -
int charAt(int index)
:获取某索引处的字符(代码单元)。必须用
char c = str.charAt(15);
,不能用char c = str[15];
int codePointAt(int index)
int length()
:获取字符(代码单元)的个数—— 代码单元,见 [2.6.2.4 字符本质与编码表]
IntStream codePoints()
:返回字符串中全部码点构成的流long codePoints().count()
:返回真正长度(码点数量)—— 流,见 [27.4 Stream API]
-
int indexOf(String str)
:获取字符(串)在字符串中第一次出现的索引。如果找不到,返回 -1int indexOf(int char)
参数也可以传入一个 int。由于自动类型转换的存在,也能填入 charint indexOf(String str, int index)
:从 index 处(包含)开始查找指定字符(串)int lastIndexOf(String str)
:获取字符在字符串中最后一次出现的索引。如果找不到,返回 -1 -
String substring(int start, int end)
:返回截取指定范围 [start, end) 的 新 字符串String substring(int index)
:截取 index(包含)之后的部分 -
String trim()
:返回去前后空格的新字符串 -
String toUperCase()
:返回字母全部转为大写的新字符串String toLowerCase()
:返回字母全部转为小写的新字符串 -
String concat(String another)
:返回拼接字符串 -
String replace(char oldChar, char newChar)
:替换字符串中的元素String str1 = "Foolish cultists"; String str2 = str1.replace("cultists", "believers"); //str1不变,str2为改变的值
-
String[] split(String regex)
:分割字符串。对于某些分割字符,我们需要转义
String str1 = "aaa,bbb,ccc"; String[] strs1 = str1.split(","); //这个场合,strs = {"aaa", "bbb", "ccc"};4 String str2 = "aaa\bbb\ccc"; String[] strs2 = str2.split("\\"); //"\" 是特殊字符,需要转义为 "\\"
-
int compareTo(String another)
:按照字典顺序比较两个字符串(的大小)。返回出现第一处不同的字符的编号差。前面字符相同,长度不同的场合,返回那个长度差。
String str1 = "ccc"; String str2 = "ca"; String str3 = "ccc111abc"; int n1 = str1.compareTo(str2); //此时 n1 = 'c' - 'a' = 2 int n2 = str1.compareTo(str3); //此时 n2 = str1,length - str3.length = -6 int n3 = str1.compareTo(str1); //此时 n3 = 0
-
char[] toCharArray()
:转换成字符数组byte[] getBytes()
:字符串转为字节数组 -
String String.format(String format, Object... args)
:(静态方法)格式字符串String name = "Roin"; String age = "1M"; String state = "computer"; String formatStr = "I am %s, I am %s old, I am a %s"; String str = String.format(formatStr, name, age, state); //其中 %s 是占位符。此时,str = "I am Roin, I am 1M old, I am a computer"; //%s 表示字符串替换;%d 表示整数替换;#.2f 表示小数(四舍五入保留2位)替换;%c 表示字符替换
-
String join(deli, ele...)
:拼接字符串(ele...
),以deli
间隔。 -
boolean startsWith(str)
:测试 str 是否为当前字符串的前缀 -
String repeat(int n)
:返回该字符串重复 n 次的结果
12.3 StringBuffer
类
java.lang.StringBuffer
代表可变的字符序列。可以对字符串内容进行增删。很多方法和
String
相同,但StringBuffer
是可变长度。同时,StringBuffer
是一个容器
StringBuffer
的直接父类是AbstractStringBuffer
StringBuffer
实现了Serialiazable
,可以串行化- 在父类中,
AbstractStringBuffer
有属性char[] value
不是final
StringBuffer
是一个final
类,不能被继承
String
对比 StringBuffer
String
保存字符串常量,其中的值不能更改。每次更新实际上是更改地址,效率较低StringBuffer
保存字符串变量,里面的值可以更改。每次更新是更新内容,不用每次更新地址。
12.3.1 StringBuffer
构造方法
-
无参构造
StringBuffer strb1 = new StringBuffer();
创造一个 16 位容量的空
StringBuffer
-
传入字符串构造
String str1 = "abcabc"; StringBuffer strb2 = new StringBuffer(str1);
(上例)创造一个 str1.length + 16 容量的
StringBuffer
-
指定容量构造
StringBuffer strb3 = new StringBuffer(3);
(上例)创造一个 3 容量的空
StringBuffer
12.3.2 String
和 StringBuffer
的转换
-
转
StringBuffer
String str1 = "abcabc"; StringBuffer strb1 = new StringBuffer(str1); //方法1(构造器) StringBuffer strb1 = new StringBuffer(); strb1 = strb1.append(str1); //方法2(先空再append)
-
转
String
String str2 = strb1.toString(); //方法1(toString) String str3 = new String(strb1); //方法2(构造器)
12.3.3 StringBuffer
的常用方法
-
append(char c)
:增加append(String s)
参数也能是字符串特别的,
append(null);
的场合,等同于append("null");
-
delete(start, end)
:删减 [start, end) 的内容 -
replace(start, end, string)
:将 start 与 end 间的内容替换为 string -
indexOf
:查找指定字符串第一次出现时的索引。没找到的场合返回 -1 -
insert
:在指定索引位置之前插入指定字符串 -
length()
:返回字符长度capacity()
:返回当前的容量String 类对象分配内存时,按照对象中所含字符个数等量分配。
StringBuffer 类对象分配内存时,除去字符所占空间外,会另加 16 字符大小的缓冲区。
对于
length()
方法,返回的是字符串长度。对于capacity()
方法,返回的是 字符串 + 缓冲区 的大小。
12.4 StringBuilder
类
一个可变的字符序列。此类提供一个与
StringBuffer
兼容的 API,但不保证同步(有线程安全问题)。该类被设计成StringBuffer
的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先使用该类。因为在大多数实现中,它比起StringBuffer
要快。在
StringBuilder
是的主要操作是append
和insert
方法。可以重载这些方法,以接受任意类型的数据。
-
StringBuilder
也继承了AbstractStringBuffer
-
StringBuilder
也实现了Serialiazable
,可以串行化 -
仍然是在父类中有属性
char[] value
,而且不是final
-
StringBuilder
也是一个final
类,不能被继承 -
StringBuilder
的方法,没有做互斥的处理(没有synchronize
),故而存在线程安全问题
12.4.1 String
、StringBuffer
、StringBuilder
的对比
-
StringBuilder
和StringBuffer
类似,均代表可变字符序列,而且方法也一样 -
String
:不可变字符序列,效率低,但复用率高 -
StringBuffer
:可变字符序列,效率较高,线程安全 -
StringBuilder
:可变字符序列,效率最高,存在线程安全问题 -
String
为何效率低:String str1 = "aa"; //创建了一个字符串 for(int n = 0; n < 100; n++){ str1 += "bb"; //这里,原先的字符串被丢弃,创建新字符串 } //多次执行后,大量副本字符串留在内存中 //导致效率降低,也会影响程序性能
如上,对
String
大量修改的场合,不要使用String
12.5 Math
类
-
Math.multiplyExact(int n1, int n2)
:进行乘法运算,返回运算结果通常的乘法
n1 * n2
在结果大于那个数据类型存储上限时,可能返回错误的值。使用此方法,结果大于那个数据类型存储上限时,会抛出异常
Math.addExact(int n1, int n2)
:加法Math.subtractExact(int n1, int n2)
:减法Math.incrementExact(int n1)
:自增Math.decrementExact(int n1)
:自减Math.negateExact(int n1, int n2)
:改变符号 -
Math.abs(n)
:求绝对值,返回 |n1| -
Math.pow(n, i)
:求幂,返回 n3 ^ i -
Math.ceil(n)
:向上取整,返回 >= n3 的最小整数(转成double) -
Math.floor(n)
:向下取整,返回 <=n4 的最小整数(转成double) -
Math.floorMod(int n1, int n2)
:返回 n1 除以 n2 的余数n1 % n2
的场合,返回的可能是负数,而不是数学意义上的余数 -
Math.round(n)
:四舍五入,相当于Math.floor(n5 + 0.5)
-
Math.sqrt(n)
:求开方。负数的场合,返回NaN
-
Math.random()
:返回一个 [0, 1) 区间的随机小数 -
Math.sin(n)
:正弦函数Math.cos(n)
:余弦函数Math.tan(n)
、Math.atan(n)
、Math.atan2(n)
要注意,上述方法传入的参数是 弧度值。
要得到一个角度的弧度值,应使用:
Math.toRadians(n)
-
Math.exp(n)
:e 的 n 次幂Math.log10(n)
:10 为底的对数Math.log()
:自然对数 -
Math.PI
:圆周率的近似值Math.E
:e 的近似值
12.6 Arrays
类
-
Arrays.toString()
:返回数组的字符串形式int[] nums = {0, 1, 33}; String str = Array.toString(nums); //此时,str = "[0, 1, 33]"
特别的,输入为 null 时返回 “null”
-
Arrays.sort(arr)
:排序因为数组是引用类型,使用 sort 排序后,会直接影响到实参。
默认(自然排序)从小到大排序。
Arrays.sort(arr, Comparator c)
:按照传入的比较器决定排序方法Integer[] nums; ... Comparator<Integer, Integer> c = new Comparator<Integer, Integer>(){ @Override public int compare(Integer o1, Integer o2){ return n2 - n1; // 这个场合,变成从大到小排序 } } Arrays.sort(nums, c);
-
Arrays.binarySearch(array, num)
:通过二分搜索法查找。前提是必须先排序。找不到的场合,返回 - (low + 1)。即,其应该在的位置的负值
Integer[] nums2 = {-10, -5, -2, 0, 4, 5, 9}; int index = Arrays.binarySearch(nums2, 7); // 此时 index = -7 // 如果 7 存在,应该在第 7 个位置
-
Arrays.copyOf(arr, n)
:从arr
中,复制 n 个元素(成为新的数组)。n > arr.length 的场合,在多余的位置添加
null
。n < 0 的场合,抛出异常。该方法的底层使用的是
System.arraycopy
-
Arrays.fill(arr, o)
:用 o 填充num
的所有元素。 -
Arrays.equals(arr1, arr2)
:比较两个数组元素是否完全一致(true
/false
) -
Arrays.asList(a, b, c, d)
:将输入数据转成一个List
集合
12.7 System
类
-
System.exit(0)
:退出当前程序。0 表示一个状态,正常状态是 0 -
System.arraycopy(arr, 0, newArr, 0 ,3)
:复制数组元素。上例是:arr 自下标 0 起开始,向 newArr 自下标 0 开始,依次拷贝 3 个值
这个方法比较适合底层调用。我们一般使用
Arrays.copyOf
来做 -
System.currentTimeMillis
:返回当前时间距离 1970 - 1 - 1 的毫秒数 -
System.gc
:运行垃圾回收机制
12.8 BigInteger
和 BigDecimal
类
BigInteger
:适合保存更大的整数
BigDecimal
:适合保存精度更大的浮点数
//用引号把大数变成字符串
BigInteger bigNum = new BigInteger("100000000000000000000000");
构造方法:
new BigInteger(String intStr)
:通过一个字符串构建大数BigInteger BigInteger.valueOf(1)
:通过静态方法,让整数类型转成大数
另外,在对 BigInteger
和 BigDecimal
进行加减乘除的时候,需要使用对应方法
不能直接用 +
-
*
/
常用方法:
-
BigInteger add(BigInteger)
:加法运算。返回新的大数 -
BigInteger subtract(BigInteger)
:减法 -
BigInteger multiply(BigInteger)
:乘法 -
BigInteger divide(BigInteger)
:除法运算该方法可能抛出异常。因为可能产生是无限长度小数。
解决方法(保留分子精度):
bigDecimal.divide(bD3, BigDecimal.ROUND_CELLING)
-
一些常量:
BigInteger.ONE
、BigInteger.ZERO
、BigInteger.TEN
分别是 1、0、10one 就是英文的 1,zero 就是英文的 0……这个大家都懂的吧?
12.9 日期类
12.9.1 第一代日期类
Date:精确到毫秒,代表特定瞬间。这里的是 java.util.Date
SimpleDateFormat:格式和解析日期的类
-
Date d1 = new Date();
:调用默认无参构造器,获取当前系统时间。默认输出日期格式是国外的格式,因此通常需要进行格式转换
SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH.mm.ss"); String dateFormated = sdf.(d1); //日期转成指定格式。
-
通过指定毫秒数得到时间:
Date d2 = new Date(10000000000);
-
把一个格式化的字符串转成对应的 Date:
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH点mm分 E"); Date d3 = sdf2.parse("2021年12月22日 00点03分 星期三");
这个场合,给定的字符串格式应和
sdf2
格式相同,否则会抛出异常
12.9.2 第二代日期类
Calendar:构造器是私有的,要通过 getInstance 方法获取实例
-
Calendar 是一个抽象类,其构造器私有
Calendar c1 = Calendar.genInstance(); //获取实例的方法
-
提供大量方法和字段提供给程序员使用
-
c1.get(Calendar.YEAR)
:获取年份数 -
c1.get(Calendar.MONTH)
:获取月份数特别的,实际月份是 返回值 +1。因为 Calendar 的月份是从 0 开始编号的
-
c1.get(Calendar.DAY_OF_MONTH)
:获取日数 -
c1.get(Calendar.HOUR)
:获取小时数(12小时制)c1.get(Calendar.HOUR_OF_DATE)
:获取小时数(24小时制) -
c1.get(Calendar.MINUTE)
:获取分钟数 -
c1.get(Calendar.SECOND)
:获取秒数
Calendar 没有专门的格式化方法,需要程序员自己组合来显示
-
12.9.3 第三代日期类
JDK 1.0 加入的 Date 在 JDK 1.1 加入 Calendar 后已被弃用
然而,Calendar 也存在不足:
- 可变性:像日期和实际这样的类应该是不可改变的
- 偏移性:年份都是从 1900 年开始,月份都是从 0 开始
- 格式化:只对 Date 有用,对 Calendar 没用
- 其他问题:如不能保证线程安全,不能处理闰秒(每隔 2 天多 1 秒)等
于是,在 JDK 8 加入了以下新日期类:
- LocalDate:只包含 日期(年月日),可以获取 日期字段
- LocalTime:只包含 时间(时分秒),可以获取 时间字段
- LocalDateTime:包含 日期 + 时间,可以获取 日期 + 时间字段
- DateTimeFormatter:格式化日期
- Instant:时间戳
-
使用
now()
方法返回当前时间的对象LocalDateTime ldt = LocalDateTime.now(); //获取当前时间
-
获取各字段方法:
-
ldt.getYear();
:获取年份数 -
ldt.getMonth();
:获取月份数(英文)ldt.getMonthValue();
:获取月份数(数字) -
ldt.getDayOfMonth();
:获取日数 -
LocalDateTime ldt2 = ldt.plusDays(100);
:获取 ldt 时间 100 天后的时间实例 -
LocalDateTime ldt3 = ldt.minusHours(100);
:获取 ldt 时间 100 小时前的时间实例 -
…
-
-
格式化日期:
DateTimeFormatter dtf = new DateTimeFormatter("yyyy.MM.dd HH.mm.ss"); String date = dtf.format(ldt); //获取格式化字符串
-
Instant
和Date
类似-
获取当前时间戳:
Instant instant = Instant.now();
-
转换为
Date
:Date date = Date.form(instant);
-
由
Date
转换:Instant instant = date.toInstant;
-
12.10 泛型
泛型(generic):又称 参数化类型。是JDK 5 出现的新特性。解决数据类型的安全性问题。
在类声明或实例化时只要制定好需要的具体类型即可。
举例说明:
Properties<Person> prop = new Properties<Person>();
上例表示存放到
prop
中的必须是Person
类型。如果编译器发现添加类型不符合要求,即报错。
遍历时,直接取出
Person
而非Object
-
编译时,检查添加元素的类型。可以保证如果编译时没发出警告,运行就不会产生 ClassCastException 异常。提高了安全性,使代码更加简洁、健壮。
-
也减少了转换的次数,提高了效率。
-
泛型的作用是:可以在类声明是通过一个标识表示类中某个属性的类型,或某个方法返回值的类型,或参数类型。
class P<E> { E e; //E 表示 e 的数据类型,在定义 P类 时指定。在编译期间即确认类型 public P(E e){ //可作为参数类型 this.e = e; } public E doSth(){ //可作为返回类型 return this.e; } }
实例化时指定 E 的类型,编译时上例所有 E 会被编译器替换为那个指定类型
#使用方法:
-
声明泛型:
interface InterfaceName<T> {...} class ClassName<A, B, C, D> {...}
上例 T、A、B、C、D 不是值,而是类型。可以用任意字母代替
-
实例化泛型:
List<String> strList = new ArrayList<String>(); Iterator<Integer> iterator = vector.interator<Integer>();
类名后面指定类型参数的值
注意细节:
-
泛型只能是引用类型
-
指定泛型具体类型后,可以传入该类型或其子类类型
-
在实际开发中往往简写泛型
List<String> strList = new ArrayList<>();
编译器会进行类型推断,右边
< >
内容可以省略 -
实例化不写泛型的场合,相当于默认泛型为
Object
#自定义泛型类 · 接口:
class Name<A, B...> {...} //泛型标识符 可有多个,一般是单个大写字母表示
这就是自定义泛型啊
-
普通成员可以使用泛型(属性、方法)
-
泛型类的类型,是在创建对象时确定的。
因此:静态方法中不能使用类的泛型;使用泛型的数组,也不能初始化。
-
创建对象时不指定的场合,默认 Object。建议还是写上
<Object>
,大气,上档次 -
自定义泛型接口
interface Name<T, R...> {...}
泛型接口,其泛型在 继承接口 或 实现接口 时确定。
#自定义泛型方法:
修饰符 <T, R...> 返回类型 方法名(形参) {...}
-
可以定义在普通类中,也可以定义在泛型类中
-
当泛型方法被调用时,类型会确定
-
以下场合
Class C<T> { public void cMethord(T t){ } }
没有
< >
,不是泛型方法,而是使用了泛型的普通方法
#泛型继承:
- 泛型不具有继承性
<?>
:支持任意泛型类型<? extends A>
:支持 A 及 A的子类,规定了泛型的上限<? super B>
:支持 B 及 B 的父类,规定了泛型的下限