字符串 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 {@Stableprivate 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 mstimer.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@HotSpotIntrinsicCandidatepublic 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@HotSpotIntrinsicCandidatepublic 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` 中只有一个唯一实例,所以可以直接使用 `==` 比较```javavar 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(); // -1160871061Random 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.concurrentpublic class ThreadLocalRandom extends Random {}package java.securitypublic class SecureRandom extends java.util.Random {}
