【Java】字符串分类和概述(String类分析,包含两个面试题,字符串比较,编译器优化)

1. 字符

字符序列:把多个字符按照一定得顺序排列起来.
字符串:把多个字符串串联起来(好比羊肉串).

2. 字符串的分类:

(1)不可变的字符串(String):

当前对象创建完毕之后,该对象的内容(字符序列)是不能改变的,一旦内容改变就是一个新的对象。

(2)可变的字符串(StringBuilder/StringBuffer):
当对象创建完毕之后,该对象的内容可以发生改变,当内容发生改变的时候,对象保持不变.

我们看下String类源码:其实String就是char数组的封装!
在这里插入图片描述

字符串的本质(底层是其实就是char[])
char表示一个字符,数组表示同一种类型的多个数据如何理解char[]:

String  str = “ABCDEFG”;    //定义一个字符串对象,等价于
char[] cs = new char[]{'A','B','C','D','E','F','G'};

3. String类分析(不可变字符串)

String类表示不可变字符串,当前对象创建完毕之后,该对象的内容(字符序列)是不能改变的,一旦内容改变就是一个新的对象。

	String str = "hello";
	str  = "world"; // 此时的str已经不再是之前的str了,引用地址发生了变化

在内存中:
在这里插入图片描述

3.1 String对象的创建(两种方式)

(1)直接赋值一个字面量

	String str = ”ABCD“;

(2)通过构造器创建

String str = new Sting("ABCD");

3.2 创建String对象两种方式的区别?(面试题1)

我们首先看一个概念:

常量池: 专门存储常量的一个地方,都指的方法区中。其实就是一个缓存区。
(1)编译常量池:把字节码加载在JVM的时候,存储的是字节码的相关信息(不研究)
(2)运行常量池:存储常量数据(研究)

    String str1 = "hello";
    String str2 = new String("hello");

以上两种创建String对象的方式有什么区别,我们来看一下它们的在内存中的分配。
在这里插入图片描述

String str1 = "hello"; // 最多创建一个String对象,最少不创建对象。

  1. 其实就是在创建str1对象前,先判断常量池中有hello,如果有就str直接引用,此时不创建对象。
  2. 如果没有就创建对象,再引用。

String str2 = new String("hello"); // 最多创建两个String对象,最少创建一个String对象。new关键字绝对会在堆空间中内存区域,所以至少创建一个String对象。

  1. 先判断常量池中有"hello"对象,如果没有就在常量池中创建,然后在堆空间中创建String对象。再引用常量池中的"hello";
  2. 如果常量池中存在"hello"对象,在堆空间中先创建String对象,再引用常量池中的"hello"。

因此:

	String str1 = "hello";
	String str2 = new String("hello");
	System.out.println(str1 == str2); // false
	System.out.println(str1.equals(str2)); // true

3.3 String对象的空值:

(1)引用为空(null):没有初始化,也就是说没有给对象分配内存空间;

	String str = null;

(2)内容为空字符串,已经初始化,分配了内存空间,不过内容为空;

	String = "";

3.4 判断字符串为空的方法

(1)引用不能为空(null);
(2)字符内容不能为空字符串("");

    public static boolean isNull(String str) {

        if (str != null && !str.equals("")) {
            return false;
        }
        return true;
    }
}

3.5 字符串的比较操作

基本数据类型的比较使用" == " ,而引用数据类型的使用 " == "比较的是内存地址;
(1)使用 " == “,只能比较内存地址;
(2)使用equals方法,在Object类中和” == "相同,建议子类覆盖该方法比较自己的内容。String类覆盖了equals方法,比较的是字符内容

下面是String类中equals方法:
(1)先使用" == "比较内存地址是否相同,相同说明,属于同一对象,那么内容肯定相等。
(2)先校验是否属于String类,再校验两个字符串长度是否一致。然后逐字符比较,有一个字符不同就返回false。
在这里插入图片描述

3.5 字符串的比较练习(面试题2)

说说下面String对象,彼此之间是否相等.

	String str1 = "ABCD";
	String str2 = "A" + "B" + "C" + "D";
	String str3 = "AB" + "CD";
	String str4 = new String("ABCD");
	String temp = "AB";
	String str5 = temp + "CD"; 
	String str6 = getXx() + "CD";
    System.out.println(str1 == str2); // true
    System.out.println(str1 == str3); // true
    System.out.println(str1 == str4); // false
    System.out.println(str1 == str5); // false
    System.out.println(str1 == str6); // false
	private static String getXx() {
		return "AB";
	}

首先需要明确的是,使用equals方法比较str1-str6内容时,都是相等的,我们不考虑这个情况,只比较它们的内存地址。

下面我们逐个分析原因:

(1) 从打印的结果可知,str1==str2==str3,从之前我们学习的知识可知,随着字符串的改变,每次都会创建不同的String对象,为什么会出现这种情况。我们看下编译前和编译后的代码就明白了。

编译前的代码:
在这里插入图片描述

编译后的代码:
在这里插入图片描述
编译之后的代码存在编译优化操作
编译器发现str2其实就是四个常量连在一起,就是"ABCD",所以编译器将String str2 = "A" + "B" + "C" + "D";优化成了String str2 = "ABCD"; str3也是如此。

(2)str1 == str4 // false 这个原因我们已经在3.2中进行了证明

(3)str1 == str5 // fasle 在代码编译时,编译器确定不了temp变量的值是多少,仅检查语法是否正确,运行时期才知道temp是什么,所以在编译时无法对str5进行优化,因此又创建了str5对象。
(4)str1 == str6 //false 在代码编译时,是不会调用getXx()方法,运行时才调用该方法,才知道getXx()返回的值是多少。

3.6 字符串比较总结

(1)单独使用""引号创建的字符串都是直接量,编译时期就已经存在于常量池中
如:String str1 = "ABCD";

(2)使用new String("")创建的对象会存储到堆内存中,只有运行时才创建
如:String str4 = new String("ABCD");

(3)使用只包含直接量的字符串链接符:“aa”+“bb”,创建的也是直接量,编译器会优化,存在与常量池中。
String str2 = "A" + "B" + "C" + "D";
String str3 = "AB" + "CD";

(4)使用包含String直接量(无final修饰符)的字符串表达式:“aa”+str1穿件的对象运行时期才创建,存储于堆中;
如:
String temp = "AB";
String str5 = temp + "CD";

【通过变量、调用方法去连接的字符串,都只能在运行时期才能确定变量的值和方法的返回值,不存在编译优化】
如:

String temp = "AB";
String str5 = temp + "CD";

或:

String str6 = getXx() + "CD";
private static String getXx() {
    return "AB";
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值