字符串 String
在 Java
中,String
是一个引用类型,它本身是一个 class
。因此,比较两个字符串的内容是否相同时,必须使用 equals()
方法而不能用 ==
String str = "value";
var equals = "value".equals(str);
String 类是不可变类
String
对象被创建后,包含在这个对象汇总的字符序列是不可以更改的,直到这个对象被销毁。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;
private final byte coder;
public String() {
this.value = "".value;
this.coder = "".coder;
}
public String(String original) {
this.value = original.value;
this.coder = original.coder;
this.hash = original.hash;
}
}
在 Java 9 以前字符串采用 char[] 数组类保存字符,因此字符串的每个字符占 2 字节;而 Java 9 及之后的字符串采用 byte[] 数组加一个 encoding-flag 字段来保存字符,因此字符串的每个字符只占 1 字节。Java 9 及之后的字符串更加节省空间
StringBuilder
使用 +
拼接字符串非常简单,但是频繁使用 +
操作拼接可能存在 GC 效率隐患。如在循环中使用 +
拼接字符串,每次循环都会创建新的字符串对象,然后扔掉旧的字符串,整个循环过程中创建的大部分字符串都是临时对象,不仅浪费内存,还会影响 GC 效率。为了能高效拼接字符串,Java
标准库提供了 StringBuilder
,它是一个不可变对象,可以预分配缓冲区,往 StringBuilder
中新增字符时,不会创建新的临时对象。
StringBuilder 性能优于 “+”
TimeInterval timer = DateUtil.timer();
timer.start();
String str = "";
for (int i = 0; i < 100000; i++) {
str += i;
}
System.out.println("milliseconds = " + timer.interval()); // 2508 ms
timer.restart();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append(i);
}
var stB = sb.toString();
System.out.println("milliseconds = " + timer.interval()); // 3 ms
StringBuilder 支持链式操作
StringBuilder sb = new StringBuilder();
sb.append(1)
.append(2)
.append(3);
StringBuilder
类中的定义的 append()
方法会返回 this
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuilder>, CharSequence {
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
StringBuilder 是构造器 Builder 模式的典型设计
StringBuffer 是 StringBuilder 的线程安全版本
StringBuffer
和 StringBuilder
接口完全相同,一般只在要求线程安全拼接字符串时才使用
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuffer>, CharSequence {
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
}
包装类型
所有的包装类型都是不可变类。包装类型都是引用类型,比较包装类型是否相等必须使用 equals()
public final class Integer {
private final int value;
}
基本类型 | 对应的引用类型 |
---|---|
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 |
自动装箱 (Auto Boxing)
自动装箱和自动拆箱都是在编译期完成的(JDK>=1.5)
int i = 100;
Integer n = Integer.valueOf(i);
int x = n.intValue();
JavaBean
JavaBean
是一种符合命名规范的 class
,它通过 getter
和 setter
方法来定义属性;通常把一组对于的读方法和写方法称为属性,仅有 getter
的属性称为只读属性 read-only
,仅有 setter
的属性称为只写属性 write-only
// private 实例字段
private Type xyz;
// 读方法
public Type getXyz();
// 写方法
pubilc void setXyz(Type value);
读取 JavaBean 属性列表
Java
核心库提供 Introspector
类获取 JavaBean
的所有属性
import java.beans.*;
public class Main {
public static void main(String[] args) throws Exception {
BeanInfo info = Introspector.getBeanInfo(Person.class);
for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
System.out.println(pd.getName());
System.out.println(" " + pd.getReadMethod());
System.out.println(" " + pd.getWriteMethod());
}
}
}
枚举 Enum
当一个类的对象是有限且固定时推荐使用 enum
定义枚举类,枚举类是一个特殊的类,它一样可以有自己的成员变量、方法,可以实现一个或者多个接口,也可以定义构造器。
- 定义的
enum
类型总是继承自java.lang.Enum
,它被编译器编译为final class Xxx extends Enum { … }
。且无法被继承 - 只能定义除
enum
的实例,而无法通过new
操作符创建enum
的实例 - 定义的每个实例都是引用类型的唯一实例
enum
类型适用于switch
语句 ```java public enum SeasonEnum { /**春季 */ SPRING(1, “春季”),
/**
夏季 */ SUMMER(2, “夏季”),
/**
秋季 */ FALL(3, “秋季”),
/**
冬季 */ WINTER(4, “冬季”);
private final int value; private final String desc;
private SeasonEnum(int value, String desc) { this.value = value; this.desc = desc; }
@Override public String toString() { return “[“ + SeasonEnum.class + “]” + this.value + “:” + this.desc; } }
<a name="WpDw5"></a>
### enum 的比较
`enum` 定义的枚举类是引用类型。通常情况下,引用类型比较需要使用 `equals()` 方法,如果使用 `==` 比较,它比较的是两个引用类型的变量是否指向同一个对象。但是因为 `enum` 类型的每个常量在 `JVM` 中只有一个唯一实例,所以可以直接使用 `==` 比较
```java
var season = SeasonEnum.SPRING;
if(season == SeasonEnum.FALL){
// true
}
if(season.equals(SeasonEnum.FALL)){
// true
}
ordinal()
枚举定义的常量顺序从 0 开始计数。ordinal()
返回定义的常量的顺序,返回值随枚举定义顺序改变而改变
name() 和 toString()
判断枚举常量的名字,始终使用
name()
方法,绝不能使用toString()
方法
name()
返回枚举常量名称
String s = SeasonEnum.SPRING.name(); // SPRING
toString()
多用于覆写打印输出
SeasonEnum.FALL.toString(); // [class com.dev.SeasonEnum]3:秋季
eunm 实现单例模式
利用 enum 类型常量在 JVM 中只会存在一个唯一实例规则实现单例,此方法线程安全且不存在反序列化攻击
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("INSTANCE = " + INSTANCE);
}
}
BigInteger 和 BigDecimal
在 Java
中,由 CPU 原声提供的整型最大范围是 64
位 long
型长整数。超过 long
型整数用 java.math.BigInteger
来表示。BigInteger
内部用一个 int[]
数组来模拟一个非常大的整数
BigInteger bi = new BigInteger("1234567890");
System.out.println(bi.pow(5)); // 2867971860299718107233761438093672048294900000
Random 、ThreadLocalRandom 和 SecureRandom
Random
类专门用于生成一个伪随机数。伪随机数是指只要给定一个初始的种子,产生的随机数序列是完全一样的
Random r1 = new Random(50);
var i1 = r1.nextInt(); // -1160871061
Random r2 = new Random(50);
var i2 = r2.nextInt(); // -1160871061
为了避免两个 Random 对象产生相同的数字序列,通常使用当前时间作为 Random 对象的种子
Random random = new Random(System.currentTimeMillis());
ThreadLocalRandom
和 SecureRandom
都继承自 Random
类。ThreadLocalRandom
位于 java.util.concurrent
包中,它是 Random
的线程安全增强版,在并发访问环境下可以减少多线程资源竞争;SecureRandom
位于 java.security
包中,它是真随机数类
package java.util.concurrent
public class ThreadLocalRandom extends Random {}
package java.security
public class SecureRandom extends java.util.Random {}