JavaSE——包装类、String、StringBuffer和StringBuilder

目录

一、包装类

(一)八大包装类

(二)装箱与拆箱

(三)包装类型和String类型的相互转换

(四)Integer和Character类常用方法

(五)Integer类面试题 

二、String类

(一)String类对象的理解

(二)String类对象的创建

(三)String测试题

1.题目1

2.题目2

3.题目3

(四)String字符串特性——常量相加看池,变量相加看堆

1.题目1

2.题目2

3.题目3(重要)

4.题目4

(五)String常用方法

三、StringBuffer类——多线程

(一)StringBuffer基本介绍

(二)StringBuffer构造器的使用

(三)String和StringBuffer相互转换

1.String转换为StringBuffer

2.StringBuffer转为String

(四)StringBuffer常用方法

(五)StringBuffer实现动态分割价格

四、StringBuilder类——单线程

(一)StringBuilder基本介绍

(二)String、StringBuffer和StringBuilder比较

相同点:

不同点:

使用原则总结:       


一、包装类

(一)八大包装类

针对八种基本数据类型相应的引用类型—包装类,有了类的特点,就可以调用类中的方法。

byteshortintlongfloatdoublecharbooleanvoid
ByteShortIntegerLongFloatDoubleCharacterBooleanVoid

包装类结构图: 

(二)装箱与拆箱

装箱:jdk5之前就是基本数据类型手动转为包装类。

拆箱:jdk5之前就是包装类手动转为基本数据类型。

自动装箱:jdk5之后就是基本数据类型自动转为包装类,底层调用的是valueOf方法,比如Integer.valueOf()。 

自动拆箱:jdk5之后就是包装类自动转为基本数据类型,底层调用xxxValue()方法。

以int <—> Integer为例:

jdk5之前:手动装箱和拆箱

// 手动装箱 int->Integer
int n1 = 10;

Integer integer = new Integer(n1);
// 或者
Integer integer1 = Integer.valueOf(n1);


// 手动拆箱 Integer->int
int i = integer.intValue();

jdk5之后:自动装箱和拆箱

int n2 = 100;
// 自动装箱
Integer integer2 = n2;
// 自动装箱底层源码
public static Integer valueOf(int i) {
    if (i >= Integer.IntegerCache.low && i <= Integer.IntegerCache.high)
        return Integer.IntegerCache.cache[i + (-Integer.IntegerCache.low)];
    return new Integer(i);
}


// 自动拆箱
int n3 = integer2;

// 自动拆箱底层源码
public int intValue() {
    return value;
}

自动装箱与自动拆箱练习: 

Object obj1 = true ? new Integer(1) : new Double(2.0);
System.out.println(obj1); // 1.0
// 三元运算符是一个整体,这里精度最高的是Double,会提升优先级

Object obj2;
if (true) {
    obj2 = new Integer(1);
} else {
    obj2 = new Double(2.0);
}
System.out.println(obj2);

(三)包装类型和String类型的相互转换

// 包装类->String
Integer i = 100;
// 方式一:这种没有改变i的类型
String str1 = i + "";
// 方式二
String str2 = i.toString();
// 方式三
String str3 = String.valueOf(i);

// String->包装类
String str4 = "12345";
// 使用自动装箱
Integer i1 = Integer.parseInt(str4);
// 构造器
Integer integer = new Integer(str4);

(四)Integer和Character类常用方法

System.out.println(Integer.MIN_VALUE);//返回最小值
System.out.println(Integer.MAX_VALUE);//返回最大值

System.out.println(Character.isDigit('a'));//判断是不是数字
System.out.println(Character.isLetter('a'));//判断是不是字母
System.out.println(Character.isUpperCase('a'));//判断是不是大写
System.out.println(Character.isLowerCase('a'));//判断是不是小写
System.out.println(Character.isWhitespace('a'));//判断是不是空格
System.out.println(Character.toUpperCase('a'));//转成大写
System.out.println(Character.toLowerCase('A'));//转成小写

(五)Integer类面试题 

Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);  //False

//所以,这里主要是看范围 -128 ~ 127 就是直接返回
/*
//1. 如果i在 IntegerCache.low(-128)~IntegerCache.high(127),就直接从数组返回
//2. 如果不在 -128~127,就直接 new Integer(i)
底层源码:
 public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
 */
Integer m = 1; //底层 Integer.valueOf(1); -> 阅读源码
Integer n = 1;//底层 Integer.valueOf(1);
System.out.println(m == n); //T

//所以,这里主要是看范围 -128 ~ 127 就是直接返回,否则,就new Integer(xx);
Integer x = 128;//底层Integer.valueOf(1);
Integer y = 128;//底层Integer.valueOf(1);
System.out.println(x == y);//False

二、String类

(一)String类对象的理解

1.String对象用于保存字符串,也就是一组字符序列
2."jack"字符串常量, 双引号括起的字符序列
3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
4.String类有很多构造器,构造器可以进行重载

   常用的有:

String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
String s5 = new String(byte[] b);

5.String类实现了接口Serializable【String可以串行化:在网络间传输】
                           接口Comparable[String对象可以比较大小]
6.String是final修饰的类,不能被其他的类继承
7.String有属性private final char value[];用于存放字符串内容
8.一定要注意:value 是一个final类型,不可以修改:即value不能指向新的地址,但是单个字符内容是可以变化

public static void main(String[] args) {
	String name = "jack";
	name = "tom";
	final char[] value = {'a','b','c'};
	char[] v2 = {'t','o','m'};
	value[0] = 'H';
	//value = v2; value是final修饰的,不可以修改value地址
}

(二)String类对象的创建

两种创建String对象的区别

方式一:String s1 = "hello";
方式二:String s2 = new String("hello");

方式一:先在字符串常量池查看是否有"hello"数据空间,如果有,直接指向;

如果没有则重新创建,然后指向。s1最终指向常量池的空间地址

方式二:先在堆中创建空间,里面有value属性,指向字符串常量池的"hello"空间。如果常量池没有"hello",则重新创建,如果有,直接通过value指向。最终指向的是堆中的空间地址。

String a = "abc";
String b = "abc";
System.out.println(a.equals(b)); // true 重写了equals方法,比较内容
System.out.println(a == b); // true 两个变量指向常量池同一个地址

(三)String测试题

1.题目1

        当调用intern方法时,如果字符串常量池中,已经包含一个等于此String对象的字符串(用equals(Object)方法确定),则返回池中的字符串。否则,将此String对象添加到池中,并返回此String对象的引用。

        b.intern()方法最终返回的是常量池的地址(对象)。

public static void main(String[] args) {
        String a = "hello";
        String b = new String("hello");

        System.out.println(a.equals(b)); // equals是被重写过的,判断内容  true
        System.out.println(a == b); // 比较地址 false
        System.out.println(a == b.intern()); // 比较地址,都指向常量池中同一个地址 true
        System.out.println(b == b.intern()); // b指向堆的地址  b.intern()指向常量池中的地址 false
    }

2.题目2

public static void main(String[] args) {
    String s1 = "zhangsan";
    String s2 = "java";
    String s4 = "java";
    String s3 = new String("java"); // 堆
    System.out.println(s2 == s3); // 比较地址 false
    System.out.println(s2 == s4); // 都指向常量池中的同一个地址 true
    System.out.println(s2.equals(s3)); // 比较内容 true
    System.out.println(s1 == s2); // 比较内容 内容不一样 false
}

3.题目3

public static void main(String[] args) {
    Person p1 = new Person();
    p1.name = "zhangsan";
    Person p2 = new Person();
    p2.name = "zhangsan";

    System.out.println(p1.name.equals(p2.name)); // 比较内容 true
    System.out.println(p1.name == p2.name); // 都指向常量池同一个地址  true
    System.out.println(p1.name == "zhangsan"); // 常量zhangsan在常量池中  true

    String s1 = new String("lisi");
    String s2 = new String("lisi");
    System.out.println(s1 == s2); // 对象地址不同 false
}

内存图: 

(四)String字符串特性——常量相加看池,变量相加看堆

String是一个final类,代表不可变的字符序列。

字符串是不可变的。一个字符串对象一旦被分配,其内容是不可变的。

1.题目1

String s1 = "zhangsan";
s1 = "hello";

上述代码的内存图:

        首先创建"zhangsan"对象,s1指向"zhangsan",然后又来了一个值,创建新的"hello"对象,s1重新指向"hello",所以一共创建了2个对象。

2.题目2

String a = "hello" + "abc";创建了几个对象?

编译器可以对上述代码做一个优化,判断创建的常量池对象,是否有引用指向。

String a = "hello" + "abc"; => String a = "helloabc";

如果单独创建"hello"和"abc",没有任何指向,那么就浪费空间,没有意义。因此,上述代码只创建了一个对象。

3.题目3(重要)

public static void main(String[] args) {
    String a = "hello"; //创建 a对象
    String b = "abc";//创建 b对象

    //1. 先创建一个 StringBuilder sb = new StringBuilder();
    //2. 执行 sb.append("hello");
    //3. sb.append("abc");
    //4. String c = sb.toString();
    // 最后其实是 c 指向堆中的对象(String) value[] -> 池中 "helloabc"
    String c = a + b;
    String d = "helloabc";
    System.out.println(c == d);//真还是假? 是false
    String e = "hello" + "abc";//直接看池, e指向常量池
    System.out.println(d == e);//真还是假? 是true
}

上述代码debug  step into源码步骤:

第一步:

第二步:

第三步:

第四步:

注意:这里要用JDK8,如果JDK8仍然无法实现上述debug效果,在JDK8的基础上进行下图操作即可。

4.题目4

String s1 = "hspedu";  //s1 指向池中的 “hspedu”
String s2 = "java"; // s2 指向池中的 “java”
String s5 = "hspedujava"; //s5 指向池中的 “hspedujava”
String s6 = (s1 + s2).intern();//s6 指向池中的   “hspedujava”
System.out.println(s5 == s6); // true
System.out.println(s5.equals(s6));// true

(五)String常用方法

// 1. equals 比较内容是否相同,区分大小写
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));

// 2.equalsIgnoreCase 忽略大小写的判断内容是否相等
String username = "johN";
if ("john".equalsIgnoreCase(username)) {
    System.out.println("Success!");
} else {
    System.out.println("Failure!");
}

// 3.length 获取字符的个数,字符串的长度
System.out.println("天安门广场".length()); // 5

// 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1
String s1 = "wer@terwe@g";
int index = s1.indexOf('@');
System.out.println(index);// 3
System.out.println("weIndex=" + s1.indexOf("we"));// 0

// 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1
s1 = "wer@terwe@g@";
index = s1.lastIndexOf('@');
System.out.println(index);// 11
System.out.println("ter的位置=" + s1.lastIndexOf("ter"));// 4

// 6.substring 截取指定范围的子串
String name = "hello,张三";
//name.substring(6) 从索引6开始截取后面所有的内容
System.out.println(name.substring(6));// 截取后面的字符
//name.substring(0,5)表示从索引0开始截取,截取到索引 5-1=4位置 [包头不包尾]
System.out.println(name.substring(2, 5));// llo

// 7.toUpperCase转换成大写
String s = "heLLo";
System.out.println(s.toUpperCase());//HELLO

// 8.toLowerCase
System.out.println(s.toLowerCase());//hello

// 9.concat拼接字符串
String s1 = "张三";
s1 = s1.concat("李四").concat("王五").concat("赵六");
System.out.println(s1);// 张三李四王五赵六

// 10.replace 替换字符串中的字符
s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
// 在s1中,将 所有的 林黛玉 替换成薛宝钗
// s1.replace()方法执行后,返回的结果才是替换过的
// 对s1没有任何影响
String s11 = s1.replace("宝玉", "jack");
System.out.println(s1);// 宝玉 and 林黛玉 林黛玉 林黛玉
System.out.println(s11);// jack and 林黛玉 林黛玉 林黛玉

// 11.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String[] split = poem.split(",");
for (int i = 0; i < split.length; i++) {
    System.out.println(split[i]);
}

poem = "E:\\aaa\\bbb";
split = poem.split("\\\\");
for (int i = 0; i < split.length; i++) {
    System.out.println(split[i]);
}

// 12.toCharArray 转换成字符数组
s = "happy";
char[] chs = s.toCharArray();
for (int i = 0; i < chs.length; i++) {
    System.out.println(chs[i]);
}

// 13.compareTo 比较两个字符串的大小
// 如果前者大,则返回正数;后者大,则返回负数;如果相等,返回0
// (1) 如果长度相同,并且每个字符也相同,就返回 0
// (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小
//     就返回 if (c1 != c2) {
//                return c1 - c2;
//            }
// (3) 如果前面的部分都相同,就返回 str1.len - str2.len
String a = "jcc";// len = 3
String b = "jack";// len = 4
System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2的值

// 14.format 格式字符串
// 占位符有:%s 字符串  %c 字符型  %d 整型  %.2f 浮点型(保留2位小数)
String name = "john";
int age = 10;
double score = 98.5 / 3;
char gender = '男';
// 将所有的信息都拼接在一个字符串.
String info = "姓名:" + name + ",年龄:" + age + ",成绩:" + score + ",性别:" + gender;
System.out.println("info=" + info);

String info2 = String.format("姓名:%s,年龄:%d,成绩:%.2f,性别:%c", name, age, score, gender);
System.out.println("info2=" + info2);

三、StringBuffer类——多线程

(一)StringBuffer基本介绍

        StringBuffer属于java.lang包,它代表可变的字符序列,可以对字符串内容进行增删。很多方法与String相同,但StringBuffer是可变长度的。StringBuffer是一个容器。

  1. StringBuffer 的直接父类是 AbstractStringBuilder
  2. StringBuffer 实现了 Serializable,即StringBuffer的对象可以串行化
  3. 在父类中AbstractStringBuilder 有属性 byte[] value,不是final
  4. 该 value 数组存放字符串内容,真正存放在堆中,而不是字符串常量池
  5. StringBuffer 是一个 final类,不能被继承
  6. 因为StringBuffer 字符内容是存在 byte[] value,所以在变化(增加/删除)
  7. 不用每次都更换地址(只有放不下了才会创建新对象), 所以效率高于String
  8. 初始化大小是16

StringBuffer的类继承关系图:

(二)StringBuffer构造器的使用

注意:StringBuffer在JDK8是char[] value;在JDK11是byte[] value

//1. 创建一个 大小为16的 byte[] ,用于存放字符内容
StringBuffer stringBuffer = new StringBuffer();

//2 通过构造器指定 byte[] 大小
StringBuffer stringBuffer1 = new StringBuffer(100);

//3. 通过 给一个String 创建 StringBuffer, byte[] 大小就是 str.length() + 16
StringBuffer hello = new StringBuffer("hello");

(三)String和StringBuffer相互转换

1.String转换为StringBuffer

// 方式一:使用StringBuffer构造器
// 注意:返回的才是StringBuffer对象,对str本身没有影响
String str = "hello";
StringBuffer stringBuffer = new StringBuffer(str);

// 方式二:使用append方法
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = stringBuffer1.append(str);

2.StringBuffer转为String

// 方式一:使用StringBuffer的toString()方法
StringBuffer stringBuffer3 = new StringBuffer("world");
String str1 = stringBuffer3.toString();

// 方式二:使用String构造器
StringBuffer stringBuffer4 = new StringBuffer("world");
String str2 = new String(stringBuffer4);

(四)StringBuffer常用方法

StringBuffer s = new StringBuffer("hello");
//增
s.append(','); // "hello,"
s.append("张三丰"); // "hello,张三丰"
s.append("赵敏").append(100).append(true).append(10.5);
System.out.println(s); // "hello,张三丰赵敏100true10.5"

//删
/*
 * 删除索引为>=start && <end 处的字符
 * 解读: 删除11~14的字符 [11, 14)  包头不包尾
 */
s.delete(11, 14);
System.out.println(s);//"hello,张三丰赵敏true10.5"

// 改
// 使用 周芷若 替换 索引9-11的字符 [9,11)
s.replace(9, 11, "周芷若");
System.out.println(s);//"hello,张三丰周芷若true10.5"

//查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
int indexOf = s.indexOf("张三丰1");
System.out.println(indexOf);// -1

//插
//老韩解读,在索引为9的位置插入 "赵敏",原来索引为9的内容自动后移
s.insert(9, "赵敏");
System.out.println(s);//"hello,张三丰赵敏周芷若true10.5"

//长度
System.out.println(s.length());//22
System.out.println(s); // hello,张三丰赵敏周芷若true10.5

(五)StringBuffer实现动态分割价格

假如价格如下:99945176841654868.56,需要使用“,”号分割,每3位一个“,”号

public static void main(String[] args) {
    String price = "99945176841654868.56";
    StringBuffer sb = new StringBuffer(price);
    int index = sb.lastIndexOf(".");
    for (int i = index - 3; i > 0; i -= 3) {
        sb.insert(i, ",");
    }
    System.out.println(sb);
}

四、StringBuilder类——单线程

(一)StringBuilder基本介绍

        StringBuilder是一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步(StringBuilder不是线程安全)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,单线程的时候推荐使用StringBuilder,它比StringBuffer快。

        在StringBuilder上的主要操作是append和insert方法,可重载这些方法,以接受任意类型的数据。

证明如下:

  1. StringBuilder 继承 AbstractStringBuilder 类
  2. 实现了Serializable接口,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)
  3. StringBuilder 是final修饰的类,不能被继承
  4. StringBuilder对象字符序列仍然是存放在其父类 AbstractStringBuilder的 byte[] value(此处为JDK11,JDK8中是char[] value);因此,字符序列存放在堆中
  5. StringBuilder的方法,没有做互斥的处理,即没有synchronized关键字,因此在单线程的情况下推荐使用StringBuilder

(二)String、StringBuffer和StringBuilder比较

相同点:

1)都是对字符串进行操作

2)三者的类都被final修饰

3)StringBuffer和StringBuilder,均代表可变的字符串。都实现了Serializable和Comparable,可以序列化,实现在网络间的传输,并且都继承了AbstractStringBuilder类,二者调用的方法相同。JDK11中的value是byte[],JDK8中的value是char[]。

不同点:

1)String代表不可变字符序列,byte[] value 被final修饰,效率低,但是复用率高(如果内容相同,都指向常量池中同一个地址);

        String效率低的原因:创建一个字符串,后面又对其进行拼接,原来的字符串被丢弃。如果多次执行这些改变字符串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大地影响程序性能。因此,如果我们对String做大量修改,不要使用String

2)StringBuffer代表可变字符序列,byte[] value没有被final修饰,效率较高(增删)、线程安全

3)StringBuilder代表可变字符序列,byte[] value没有被final修饰,效率最高、线程不安全

使用原则总结:       

1)如果字符串存在大量的修改操作,一般使用StringBuffer或StringBuilder

2)如果字符串存在大量的修改操作,并存在单线程的情况,使用StringBuilder

3)如果字符串存在大量的修改操作,并存在多线程的情况,使用StringBuffer

4)如果字符串很少修改,被多个对象引用,使用String,比如配置信息等

5)StringBuffer和StringBuilder方法的使用一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值