JAVA学习(三)| JAVA核心类
字符串与编码⁍
在Java中,String
是一个引用类型,它本身也是一个class
。
- 不可变:Java字符串的一个重要特点就是字符串不可变。这种不可变性是通过内部的
private final char[]
字段,以及没有任何修改char[]
的方法实现的。
String (Java SE 11 & JDK 11 ) (runoob.com)
字符串比较⁍
必须使用equals()
方法而不能用==
。要忽略大小写比较,使用equalsIgnoreCase()
方法。
1 |
|
搜索/提取子串⁍
1 |
|
去除首尾空白字符⁍
使用trim()
方法可以移除字符串首尾空白字符。空白字符包括空格,\t
,\r
,\n
(其代码点小于或等于 'U+0020'
(空格字符)的任何字符。):
1 |
|
注意:trim()
并没有改变字符串的内容,而是返回了一个新字符串。
判断字符串是否为空/空白⁍
- 是否为空:
string.isEmpty()
- 是否为空白:
string.isBlank()
1 |
|
替换子串⁍
s.replace(A,B)
把所有A换成B
- 正则表达式
分割字符串⁍
split()
方法,并且传入的也是正则表达式:
1 |
|
拼接字符串⁍
静态方法join()
,它用指定的字符串连接字符串数组:
1 |
|
格式化字符串⁍
formatted()
方法和format()
静态方法,可以传入其他参数,替换占位符,然后生成新的字符串:
1 |
|
如果不确定用啥占位符,那就始终用%s
,因为%s
可以显示任何数据类型。要查看完整的格式化语法,请参考JDK文档。
类型转换⁍
- 转为字符串
要把任意基本类型或引用类型转换为字符串,可以使用静态方法valueOf()
。这是一个重载方法,编译器会根据参数自动选择合适的方法
1 |
|
-
转为
int
:Integer.parseInt("123")
-
转为
boolean
:Boolean.parseBoolean("true")
-
要特别注意,
Integer
有个getInteger(String)
方法,它不是将字符串转换为int
,而是把该字符串对应的系统变量转换为Integer
:1
Integer.getInteger("java.version"); // 版本号,11
转换为char[]⁍
String
和char[]
类型可以互相转换,方法是:
1 |
|
修改外部的char[]
数组不会影响String
实例内部的char[]
数组,因为这是两个不同的数组。
从String
的不变性设计可以看出,如果传入的对象有可能改变,我们需要复制而不是直接引用。
1 |
|
字符编码⁍
在Java中,char
类型实际上就是两个字节的Unicode
编码。如果我们要手动把字符串转换成其他编码,可以这样做:
1 |
|
注意:转换编码后,就不再是char
类型,而是byte
类型表示的数组。
如果要把已知编码的byte[]
转换为String
,可以这样做:
1 |
|
始终牢记:Java的String
和char
在内存中总是以Unicode编码表示。
StringBuilder⁍
可以直接用+
拼接字符串。但是,在循环中,每次循环都会创建新的字符串对象,然后扔掉旧的字符串。这样,绝大部分字符串都是临时对象,不但浪费内存,还会影响GC效率。
提供了StringBuilder
,它是一个可变对象,可以预分配缓冲区,这样,往StringBuilder
中新增字符时,不会创建新的临时对象:
1 |
|
对于普通的字符串
+
操作,并不需要我们将其改写为StringBuilder
,因为Java编译器在编译时就自动把多个连续的+
操作编码为StringConcatFactory
的操作。在运行期,StringConcatFactory
会自动把字符串连接操作优化为数组复制或者StringBuilder
操作。
- 方法 StringBuilder (Java SE 11 & JDK 11 ) (runoob.com)
StringBuffer
是StringBuilder
的线程安全版本,现在很少使用。- 用
toString()
转换成字符串
链式操作⁍
进行链式操作的关键是,定义的
append()
方法会返回this
,这样,就可以不断调用自身的其他方法。
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;
}
}
StringJoiner⁍
import java.util.StringJoiner;
用分隔符拼接数组
可以指定开头结尾
1 |
|
在不需要指定“开头”和“结尾”的时候,用静态方法String.join()
更方便
数据类型⁍
Java的数据类型分两种:
- 基本类型:
byte
,short
,int
,long
,boolean
,float
,double
,char
- 引用类型:所有
class
和interface
类型
引用类型可以赋值为null
,表示空,但基本类型不能赋值为null
通过包装类,将一个基本类型视为引用类型
基本类型 | 对应的引用类型 |
---|---|
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 |
自动装/拆箱⁍
直接把int
变为Integer
的赋值写法,称为自动装箱(Auto Boxing),反过来,把Integer
变为int
的赋值写法,称为自动拆箱(Auto Unboxing)。
注意:自动装箱和自动拆箱只发生在编译阶段,目的是为了少写代码。
装箱和拆箱会影响代码的执行效率,并且,自动拆箱执行时可能会报NullPointerException
int
和Integer
互相转换一般写为
1 |
|
不变类⁍
引用类型特性:创建即不变,只能用equals()
比较
但因为Java标准库的
Integer
内部有缓存优化,编译器把Integer x = 127;
自动变为Integer x = Integer.valueOf(127);
,为了节省内存,Integer.valueOf()
对于较小的数,始终返回相同的实例,因此,==
比较“恰好”为true
。
创建Integer
对象⁍
- 方法1:
Integer n = new Integer(100);
- 方法2:
Integer n = Integer.valueOf(100);
//更好,静态工厂方法,它尽可能地返回缓存的实例以节省内存。把内部优化留给Integer
的实现者去做
进制转换⁍
1 |
|
输出都是String
静态变量⁍
1 |
|
处理无符号整型⁍
在Java中,并没有无符号整型(Unsigned)的基本数据类型。
无符号整型和有符号整型的转换在Java中就需要借助包装类型的静态方法完成。
例如:Byte.toUnsignedInt(-1)
//输出255。byte是有符号整型,范围是-128
~+127
,但如果把byte
看作无符号整型,它的范围就是0
-255
。
类似的,可以把一个short
按unsigned转换为int
,把一个int
按unsigned转换为long
。
JavaBean⁍
在Java中,有很多class
的定义都符合这样的规范:
- 若干
private
实例字段; - 通过
public
方法来读写实例字段。
如果读写方法符合以下这种命名规范:
1 |
|
那么这种class
被称为JavaBean
通常把一组对应的读方法(getter
)和写方法(setter
)称为属性(property
)。例如,name
属性:
- 对应的读方法是
String getName()
- 对应的写方法是
setName(String)
只有getter
的属性称为只读属性(read-only)
只有setter
的属性称为只写属性(write-only)。
boolean
字段比较特殊,它的读方法一般命名为isXyz()
:
通过IDE,可以快速生成
getter
和setter
。IDEA中右键-生成。
使用Introspector.getBeanInfo()
可以获取属性列表。
枚举类enum⁍
为了让编译器能自动检查某个值在枚举的集合内,并且,不同用途的枚举需要不同的类型来标记,不能混用,我们可以使用enum
来定义枚举类
1 |
|
enum
常量本身带有类型信息,即Weekday.SUN
类型是Weekday
,编译器会自动检查出类型错误。- 不可能引用到非枚举的值,因为无法通过编译。
- 不同类型的枚举不能互相比较或者赋值,因为类型不符。
- 是引用类型,但可以用
==
比较,因为enum
类型的每个常量在JVM中只有一个唯一实例
enum类型⁍
通过enum
定义的枚举类,和其他的class
有什么区别?
答案是没有任何区别。enum
定义的类型就是class
,只不过它有以下几个特点:
- 定义的
enum
类型总是继承自java.lang.Enum
,且无法被继承; - 只能定义出
enum
的实例,而无法通过new
操作符创建enum
的实例; - 定义的每个实例都是引用类型的唯一实例;
- 可以将
enum
类型用于switch
语句。
实例方法:⁍
name()
:返回常量名ordinal()
:返回定义的常量的顺序,从0开始计数类.values()
:将枚举类转变为一个枚举类型的数组
可以定义private
的构造方法,并且,给每个枚举常量添加字段
1 |
|
覆写toString()
的目的是在输出时更有可读性。
注意:判断枚举常量的名字,要始终使用name()方法,绝不能调用toString()!
枚举类可以应用在switch
语句中。
记录类⁍
从Java 14开始,引入了新的Record
类。
1 |
|
record
用final
修饰class以及每个字段外,编译器还自动为我们创建了构造方法,和字段名同名的方法,以及覆写toString()
、equals()
和hashCode()
方法。
构造方法⁍
可以编写Compact Constructor对参数进行验证(不写参数)
1 |
|
静态方法⁍
仍然可以添加静态方法。一种常用的静态方法是of()
方法,用来创建Point
:
1 |
|
JDK15等使用record关键字,需要编译器打开–enable-preview
IDEA 点击File -> Project Structure -> Project Settings -> Project选项卡,选择Project language level中的15 (Preview)这一项,然后点击OK。
BigInteger⁍
import java.math.BigInteger
java.math.BigInteger
就是用来表示任意大小的整数。BigInteger
内部用一个int[]
数组来模拟一个非常大的整数
对BigInteger
做运算的时候,只能使用实例方法
将BigInteger
转换成基本类型时可使用longValueExact()
等方法保证结果准确。(如果超出了long
型的范围,会抛出ArithmeticException
。)
BigInteger
和Integer
、Long
一样,也是不可变类,并且也继承自Number
类。因为Number
定义了转换为基本类型的几个方法:
- 转换为
byte
:byteValue()
- 转换为
short
:shortValue()
- 转换为
int
:intValue()
- 转换为
long
:longValue()
- 转换为
float
:floatValue()
- 转换为
double
:doubleValue()
因此,通过上述方法,可以把BigInteger
转换成基本类型。如果BigInteger
表示的范围超过了基本类型的范围,转换时将丢失高位信息,即结果不一定是准确的。如果需要准确地转换成基本类型,可以使用intValueExact()
、longValueExact()
等方法,在转换时如果超出范围,将直接抛出ArithmeticException
异常。
- 如果
BigInteger
的值甚至超过了float
的最大范围(3.4x1038),那么返回的float是Infinity
常见方法(运算)⁍
BigInteger (Java SE 11 & JDK 11 ) (runoob.com)
add, subtract, multiply, divide(divideAndRemainder(BigInteger val),)
变量和类型 | 方法 | 描述 |
---|---|---|
BigInteger |
add(BigInteger val) |
返回一个值为 (this + val) 的BigInteger。 |
BigInteger |
subtract(BigInteger val) |
返回值为 (this - val) 的BigInteger。 |
BigInteger |
multiply(BigInteger val) |
返回值为 (this * val) 的BigInteger。 |
BigInteger |
divide(BigInteger val) |
返回值为 (this / val) 的BigInteger。 |
BigInteger[] |
divideAndRemainder(BigInteger val) |
返回两个BigIntegers的数组,其中包含 (this / val) 后跟 (this % val) 。 |
BigInteger |
mod(BigInteger m) |
返回一个值为 (this mod m 的BigInteger。 |
BigInteger |
pow(int exponent) |
返回值为 (thisexponent) 的BigInteger。 |
BigDecimal⁍
import java.math.BigDecimal;
BigDecimal
可以表示一个任意大小且精度完全准确的浮点数。
BigDecimal (Java SE 11 & JDK 11 ) (runoob.com)
-
用
scale()
表示小数位数, -
stripTrailingZeros()
方法,可以将一个BigDecimal
格式化为一个相等的,但去掉了末尾0的BigDecimal
-
用
setScale()
方法设置精度1
2
3
4import java.math.RoundingMode;
//...
BigDecimal d2 = d1.setScale(4, RoundingMode.HALF_UP); // 四舍五入,123.4568
BigDecimal d3 = d1.setScale(4, RoundingMode.DOWN); // 直接截断,123.4567 -
对
BigDecimal
做加、减、乘时,精度不会丢失,但是做除法时,存在无法除尽的情况,这时,就必须指定精度以及如何进行截断:1
2BigDecimal d3 = d1.divide(d2, 10, RoundingMode.HALF_UP); // 保留10位小数并四舍五入
BigDecimal d4 = d1.divide(d2); // 报错:ArithmeticException,因为除不尽 -
必须使用
compareTo()
方法来比较大小,它根据两个值的大小分别返回负数、正数和0
,分别表示小于、大于和等于。- 不要使用equals()! 在比较两个
BigDecimal
的值是否相等时,要特别注意,使用equals()
方法不但要求两个BigDecimal
的值相等,还要求它们的scale()
相等
- 不要使用equals()! 在比较两个
常用工具类⁍
Math⁍
顾名思义,Math
类就是用来进行数学计算的,它提供了大量的静态方法来便于我们实现数学计算:
计算ex次方:
1 |
|
计算以e为底的对数:
1 |
|
计算以10为底的对数:
1 |
|
三角函数:
1 |
|
Math还提供了几个数学常量:
1 |
|
生成一个随机数x,x的范围是0 <= x < 1
:
1 |
|
生成一个区间在[MIN, MAX)
的随机数
1 |
|
Random⁍
import java.util.Random;
Random
用来创建伪随机数。所谓伪随机数,是指只要给定一个初始的种子,产生的随机数序列是完全一样的。
要生成一个随机数,可以使用nextInt()
、nextLong()
、nextFloat()
、nextDouble()
:
1 |
|
在创建Random
实例时指定一个种子,就会得到完全确定的随机数序列Random r = new Random(12345);
SecureRandom⁍
import java.security.SecureRandom;
有伪随机数,就有真随机数。实际上真正的真随机数只能通过量子力学原理来获取,而我们想要的是一个不可预测的安全的随机数,SecureRandom
就是用来创建安全的随机数的:
1 |
|
SecureRandom
无法指定种子,它使用RNG(random number generator)算法。JDK的SecureRandom
实际上有多种不同的底层实现,有的使用安全随机种子加上伪随机数算法来产生安全的随机数,有的使用真正的随机数生成器。实际使用的时候,可以优先获取高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器: