Java 细节汇总(2)-String 的最大长度
文章目录1. String 的最大长度2. Integer 的缓冲池1. String 的最大长度从源码来看,String 其实就是由 char 数组实现,根据以下构造方法推断,count 是 int 类型,那么char value[]中最多应该可以保存 Integer.MAX_VALUE个字符,也就是2147483647字符但是实际编译的时候,String 中最多可以有65534个字符,如...
1. String 的最大长度
从源码来看,String 其实就是由 char 数组实现,根据以下构造方法推断,count 是 int 类型,那么char value[]中最多应该可以保存 Integer.MAX_VALUE
个字符,也就是2147483647
个字符,这也是运行期的最大限制
但是编译的时候,String 中最多可以有65534个字符,如果超过这个数,就会在
编译期报错
。这是因为 字符串字面量直接定义String 的时候,会把字符串在常量池 中存储一份,65534 其实是常量池的限制。常量池理论上允许的最大长度是 2^16=65536,而 java class 文件使用一种变体 UTF-8 格式来存放字符,null 值使用两个字符来表示,因此只剩下 65536- 2 = 65534个字符。由此可知所有需要保存在常量池中的数据,长度最大不能超过 65535,这当然包括了字符串定义
public String(char value[], int offset, int count) {......}
2. Integer 的缓冲池
根据自动拆装箱规则 Integer a=1 等价于 Integer a = Integer.valueOf(1),而Integer已经缓存了数值为 [-128,127] 的Integer对象,JVM会直接在对象缓冲池找到该值的引用
- Integer a=1 声明一个Integer对象时,JVM首先在Integer对象的缓存池中查找是否有值为1的对象,有则直接返回该对象的引用;没有则 new 一个 Integer 对象,返回该对象的引用地址
- Java 中 == 比较的是两个对象的内存地址,a 和 b 引用的是同一个对象,所以 a == b 结果为true;c 和 d 超出了缓存的范围,需分别生成新的 Integer对象,故 c == d 结果为false
Integer a=1;
Integer b=1;
Integer c=200;
Integer d=200;
System.out.println(a==b);//true
System.out.println(c==d);//false
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[]; // 缓存Integer对象的数组
static {
······
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
3. 顺序栈和链栈的区别
- 顺序栈
基于数组结构,数组中的元素在内存中的存储位置是连续的,且编译器要求我们在编译期就要确定数组的大小,这样对内存的使用效率并不高,一来无法避免因数组空间用光而引起的溢出问题,二是在系统将内存分配给数组后,这些内存对于其他任务就不可用,可能造成浪费- 链栈
基于链表结构,链表中的元素存储在不连续的地址,由于是动态申请内存,故可以非常小的内存空间开始,另外当某个项不使用时也可将内存返还给系统
4. 访问修饰符的访问权限
Java 中继承为单继承,但是继承关系可传递,子类可无条件向上转型,例如任何类对象都可以转化为 Object 类对象
Object o = new String(); // 无条件向上转型
访问权限 | 类 | 同一包 | 同一包中的子类 | 不同包中的子类 | 不同包 |
---|---|---|---|---|---|
public | 允许 | 允许 | 允许 | 允许 | 允许 |
protected | 允许 | 允许 | 允许 | 允许 | 不允许 |
default | 允许 | 允许 | 允许 | 不允许 | 不允许 |
private | 允许 | 不允许 | 不允许 | 不允许 | 不允许 |
5. 隐式类型转换
如下s1 被声明为 short 类型,而 1 的默认类型为 int ,精度要比 short 高,因此不能直接将 int 类型转型为 short 类型
short s1 = 1;
s1 = s1 + 1; // 会在编译时错误,不兼容的类型: 从int转换到short可能会有损失
使用 += 或者 ++ 运算符可以执行隐式类型转换,不会有异常
s1 += 1;
s1++;
上面的语句相当于将 s1 + 1 的计算结果进行了向下转型:
s1 = (short) (s1 + 1);
6. 迭代器并发修改异常
使用迭代器遍历集合元素时,只能使用 Iterator 提供的相关操作方法增删元素,如果通过 Collection 本身的增删方法来修改集合,则会出现并发修改异常 java.util.ConcurrentModificationException
注意,增强for循环的内部其实就是用 Iterator 来实现的,故也需要注意该问题,以下使用会报并发修改异常
for(Integer i:list) {
if (i == 1) list.remove(0); // java.util.ConcurrentModificationException
}
以 ArrayList 为例,根据源码来看,在该问题中最重要的两个属性是 modCount 及 expectedModCount
- modCount 属性广泛存在于各个集合类中,是集合类自身的成员变量,主要用于标记 集合的修改次数,也可以理解为标记集合对象的版本号
- expectedModCount 为迭代器保存的集合版本号,迭代器增删集合中元素时其实还是调用集合自身的相关方法,因此两个版本号不会出现不一致。但是当直接使用集合自身增删方法时,只会修改集合自身的版本号,不会修改迭代器保存的集合版本号。因此迭代器遍历元素时就会发现集合的版本与自己期望的版本号不一致,也就是数据已经发生了变化,会产生数据安全问题,故抛出异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
更多推荐
所有评论(0)