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
28public 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实际上有多种不同的底层实现,有的使用安全随机种子加上伪随机数算法来产生安全的随机数,有的使用真正的随机数生成器。实际使用的时候,可以优先获取高强度的安全随机数生成器,如果没有提供,再使用普通等级的安全随机数生成器:





