字符串
java.lang.String :字符串类,并且该类加final修饰;
底层就是char数组 private final char value [];
所以字符串有很多的特性就是数组的特性
# 1.字符串一旦创建不能更改;
2.为了提升字符串的访问效率,java中提出了字符串常量池,相当于一个缓存区;
引用类型对象应该保存在堆内存,但字符串不同,保存在字符串常量池中;
3.在程序的执行过程中,如果程序要用到某个字符串;虚拟机会先前常量池中搜索,是否存在这个字符串;
如果有就直接指向改字符串,如果没有就新建一个字符串对象,并指向这个字符串常量池的地址;
因为String一旦创建不可更改,所以不要频繁拼接字符串
因为效率比较低,还浪费空间,并且垃圾回收也会有一定问题
java.lang.StringBuffer java.lang.StringBuilder
1:StringBuffer和StringBuilder是什么?
一个可变的字符串缓冲区
2:原理?
预先在内存中申请一块空间,可以容纳字符序列(字符数组)
如果 预留空间不够,会进行自动扩容
底层都是char[] ,并且默认初始化容量是16个字符
3:String,StringBuffer,StringBuilder最大的区别?
①String不可变字符序列,而StringBuilder和StringBuffer是可变字符序列
②StringBuffer是线程安全,在多线程环境下,不会出现问题,所以效率低,一般常用于类中
③StringBuilder是非线程安全,在多线程环境下可能出现问题,效率高,一般用于方法中
4 :如何选择StringBuilder和StringBuffer
多线程环境下,是否有可能出现多个线程同时操作同一个数据的可能(增,删,改);
String 不可以任意拼接的字符串
String s2 = “a” + “b”; 这种写法,在编译阶段,就会把+ 去掉;变成ab;
“a” +“b” a和b都是字面量,需要在编译阶段说明临时空间,所以需要通过值确定类型
编译时看到是两个字符串相加,就直接把+省略,保存ab
s1 == s2 是同一个内存地址 所以相等 true
String s3 = a + b;
a和b是变量,编译阶段是不确定变量的值的
在运行的时候,由于是两个字符串变量相加,新建了一个对象,在堆内存用来存它的值;
自动创建一个StringBuffer对象,然后把两个变量拼接到一起,最终转换为String类型
而是以 return new String(value, 0, count); 所以 s3是指向堆内存的
包装类 : 封装了基本类型的操作,更方便我们使用
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
boolean -- java.lang.Boolean
既然有了基本类型,为什么还要使用包装类呢?
方便;为了理论上的完整(面向对象);
想要创建一个方法,该方法可以接收任何类型?
Object ,因为Object是所有类的祖类.
由于多态的原因,可以接收任何对象.
基本类型并不是Object的子类,怎么接收?
包装类 , 可以把基本类型转换为对应的包装类类型,
而包装类也是个类,也是Object的子类.
java1.5新特性
自动装箱:把 基本数据类型 自动转换为 对应的包装类
自动拆箱:把 包装类 自动转换为 基本数据类型
都是在编译时完成的;
深入理解自动装箱和自动拆箱
1.都是编译时的概念,和运行时无关
2.装箱的时候,会在编译时 自动把赋值 操作 变成 Integer.valueOf(222)
3.String , Integer,Double 等 八种包装类 和String 都覆写了toString(),equals() , hashCode() 方法
4. Integer.valueOf(xxx) : 方法实现
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
5.valueOf : 把基本类型转换为Integer类型
理解:一个大箱子,放的下就直接放进来用,有就直接拿走;
放不下东西了就放个信息,告诉去哪里拿它;
(箱子的大小)
里面初始化了一个整型常量池,值的范围是在 -128~127之间
就是一个Integer[] 数组 有 256个对象,对象中的int值 分别为 -128,-127…126,127 下标 是 0~ 255
(源码)
在 private static class IntegerCache 类中 是 Integer中的一个静态内部类
三个变量
static final int low = -128;
static final int high;
static final Integer cache[];
并且在 static 代码块中 对这个数组进行了初始化操作
如果 值 在 -128~127之间 就直接去这个缓存数组中找对应的对象即可,不用再重新创建Integer对象
return IntegerCache.cache[i + (-IntegerCache.low)];
// IntegerCache.low 是 -128
// (-IntegerCache.low) : 就是 128 负负得正
// 加上 我们要添加的数据,就能得到 对应的值所在缓存数组中的下标索引
// 把该对象返回
那么这样的话,如果是通过自动装箱或者是通过valueOf方法赋值的两个值都符合该范围,那么 这两个变量保存的内存地址是一致的,则使用 == 也是true
如 Integer i1 = 123; Integer i2 = 123; 或者 Integer i3 = Integer.valueOf(123); 他们的内存地址是相等的,找到的是同一个对象
(箱子不够大的时候)
反之 , 就等于new Integer(xxx)
return new Integer(i); 既然是new 那么 堆内存会重新创建新的对象,那么尽管初始化的int值一样,但是内存地址也不同
所以 使用 == 也是false,此时 应该使用equals 来比较是否相等
如 Integer i4 = Integer.valueOf(128); Integer i5 = Integer.valueOf(128); 或者是 Integer i6 = new Integer(1)