【Java基础复习1】基本数据类型、包装类型、缓存池、String等
8种基本数据类型
- byte/8bit: 取值范围为-128~127,占用1个字节
- short/16bit: 取值范围为-32768~32767,占用2个字节
- int/32bit: 占用4个字节(-2的31次方到2的31次方-1)
- float/32bit: 占用4个字节 (-3.40292347E+38~3.40292347E+38)
- long/64bit: 占用8个字节(-2的63次方到2的63次方-1)
- double/64bit: 占用8个字节,IEEE754
- char/16bit:占用2个字节
- boolean/true、false,可以使用 1 bit 来存储,但是具体大小没有明确规定。JVM 会在编译时期将 boolean 类型的数据转换为 int,使用 1 来表示 true,0 表示 false。JVM 支持 boolean 数组,但是是通过读写 byte 数组来实现的。
包装类型
所谓包装类,就是能够直接将简单类型的变量表示为一个类,在执行变量类型的相互转换时,我们会大量使用这些包装类。
包装类都为final 不可继承;包装类型都继承了Number抽象类
public final class **Integer** extends **Number** implements Comparable<Integer>
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
/* 手动装箱 /
Integer integer1 = Integer.valueOf(12);
/ 手动拆箱 */
int intValue1 = integer1.intValue();
/* 自动装箱 /
Integer integer3 = 12;
/ 自动拆箱 */
int intValue2 = integer1;
以下用途:
- 作为基本数据类型对应的类类型,提供了一系列实用的对象操作,如类型转换,进制转换等
- 集合不允许存放基本数据类型,故常用包装类
- 包含了每种基本类型的相关属性,如最大值,最小值,所占位数等
new Integer(123)与Integer.valueOf(123)的区别
-
new的对象都在堆里面,每次都会新建一个对象
-
Integer.valueOf(123)会使用缓存池里的对象,多次调用会取得同一个对象的引用
valueOf() 方法的实现比较简单,就是先判断值是否在缓存池中,如果在的话就直接返回缓存池的内容。下面是具体的实现代码。
简单的理解,Integer 类的装箱操作,会调用 valueOf(),并开辟一块新的堆内存
无论是手动装箱,还是自动装箱,都会调用 valueOf(),只是隐藏了这部分
若装箱后的 Integer 对象存在于 Integer 的缓存池中,则不会创建新对象,而是直接引用自缓存池 IntegerCache
缓存池
包装类中,存在缓存池的设置,避免对象的重复创建,以 Integer 为例
在 Integer 类中,存在一个私有静态内部类 private static class IntegerCache {}
,如下图:
cache[],也就是Integer常量池,常量池的大小为1 Byte = 8 Bits(-128~127)
这是Integer缓存池的具体实现源码
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
int h = 127;
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
为啥把缓存设置为[-128,127]区间?
性能和资源之间的权衡。 在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 - 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax= 来指定这个缓冲池的大小。
String
String类被声明为final,因此不可以被继承。
下图是java8中的String实现,String内部使用char数组来存储数据;除此之外还有一个hash成员变量,是该String对象的哈希值的缓存
但在Java9之后,使用字节数组来存储了:private final byte[] value;
;成员变量 coder 的声明:private final byte coder;
使用 coder 来标识使用了哪种编码。
对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象
在Java中不可能直接操作对象本身,所有的对象都由一个引用指向,必须通过这个引用才能访问对象本身,包括获取成员变量的值,改变对象的成员变量,调用对象的方法等。
什么是不可变的对象?
如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的。不能改变状态的意思是,不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变。
不可变的好处:
- 可以缓存 hash 值。以String作为HashMap的key,String的不可变保证了hash值的不可变。
- 字符串常量池 String Pool 的需要。java 8 字符串常量池放置于方法区中。如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
- 安全性。String 经常作为参数,String 不可变性可以保证参数不可变。如网络传输
- 线程安全。并发场景下,多个线程同时读一个资源,不会引发竟态条件。只有对资源做写操作才有危险。不可变对象不能被写,所以线程安全。
String, StringBuffer and StringBuilder
- String是不可变的,底层使用char[]存储
- StringBuffer—可变的字符序列,线程安全,效率低,底层使用char[]存储,所有的方法都是synchronized同步的
- StringBuilder—可变的字符序列;jdk5.0新增,线程不安全的,效率高,底层使用char[]存储
- 效率从高到低:StringBuilder>StringBuffer>String
AbstractStringBuilder 扩容问题:如果要添加的数据底层数组盛不下了,就需要扩容底层的数组,默认情况下扩容为原来的2倍+2,同时将原有数组中的元素复制到新的数组中
- 在使用StringBuilder的时候,append()之后,我们一般会在后面在加上一个分隔符,例如逗号,也就是再加上一个char,而char在java中占2个字节,避免了因为添加分隔符而再次引起扩容。
**
开发中建议使用**:StringBuffer(int capacity) 或 StringBuilder(int capacity)//避免后续再进行扩容;
对于三者使用的总结:
操作少量的数据: 适用String
单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix