一 字符串创建、判断相等/为空

1.1 创建字符串:双引号与new

字符串对象的创建很特殊,除了像一般对象那样通过new创建对象外,还能用双引号直接为字符串引用变量指定字符串对象的值。其实,考虑到字面值常量的概念,双引号的字符串内容其实也可以理解为一个引用(变量),只不过这个引用(变量)的名称和其指向的对象代表的值一样。如例子所示,双引号字符串可以像引用变量一样直接调用字符串类的方法,可见其确实是一个引用(变量)

System.out.println("abc".length());		// 3

引号创建,意味着在是在程序中写死的,在程序运行之前其对应的字符串对象就是确定的了。引号创建的字符串对象就是常量,会在程序加载时创建(猜的),而且被存放到JVM维护的字符串实例池

new创建,则字符串对象是在程序运行时创建的,和普通对象一样,保存在中。

引号创建时,JVM会先在字符串实例池中是否已存在,如果已存在直接返回地址,不重复创建,所以多次创建实际为同一个对象new创建时,总是会在堆中开辟新的内存区域,多次创建为不同对象。引号创建和new创建的字符串对象所在的内存区域都不同,二者自然不是同一个对象。

接中博客说:new创建字符串对象时,也会先在字符串实例池中查找,如果没有,也会同时在字符串实例池中创建。那么下面这种情况也是吗?

String str1 = "abc";
        String str2 = str1.substring(0,1);
        System.out.println(str2);

str2也会在字符串实例池中创建吗?

总结:
在Java中与字符串相关的内存区域有三个:字符串实例池、堆、栈。代码中用双引号写明的字符串对象都存放在字符串实例池,new创建的字符串对象放在堆,字符串对象的引用放在栈。

补充:

  1. 考虑到字面值常量概念,那字符串对象存在的两种创建方式、判断值相等得用equals等问题其实也不算特殊。因为所有内置类型都存在字面值常量,所以其他内置对象也存在一样的问题,如下面例子所示。
  2. 之所以对其他内置类型感觉没这么明显,是因为:从理解上看,String对标的应该是Integer、Character这些对象;但在操作上,Java中为这些内置数据类型提供了其他关键字,如:int、char,所以Integer、Character就用的少了,问题就不突出了。但是字符串对应的只有String一个关键字。【int类型其实比String更复杂,具体见下面例子】
  3. 判断Integer对象的值是否相等使用的依然是equals(),可见equals相当重要。equals可是从Object中继承的方法。
  4. 字面值常量:1、2、‘a’、‘b’…保存在哪?常量池,具体参考以下博文:

Integer i = new Integer(1);
        Integer i1 = 1;

        Character ch = new Character('a');
        Character ch1 = 'a';

        String str = new String("abc");
        String str1 = "abc";

        System.out.println(i==i1);				// false
        System.out.println(ch==ch1);			// false
        System.out.println(str==str1);			// false

        System.out.println(i.equals(i1));		// true
        System.out.println(ch.equals(ch1));		// true
        System.out.println(str.equals(str1));	// true

再补充一个int的例子,这才叫跌破眼镜

Integer i = new Integer(1);
        Integer j = 1;
        int k =1;

        System.out.println(i==j);	// false
        System.out.println(i==k);	// true
        System.out.println(j==k);	// true

1. 注意看,i和j不等,这在上面已经分析过了;但是二者竟然都能和k相等;注意看,是对象相等。
2. 换句话说:k==i、k==j,推不出i==j,怎么解释?
3. 个人理解这是操作符重载,==操作符在涉及int、char...时,不再表示对象相等,而是表示值相等。
4. 可见基本数据类型int、char等真是极其特殊,只用考虑值,不做对象考虑。
5. int i=1 和 int j=3-2,是等价的,没有区别,Integer i=1 和 Integer j=3-2,也是等价的,是同一个对象

1.2 字符串对象判断相等/为空:

字符串判断相等/为空这种说法本身是不明确的,应该说判断:字符串对象值相等、字符串对象相等、字符串对象值为空、字符串对象为空。

等号运算符"==",用于判断对象相等,即同一块内存单元。equals()方法,用于判断对象的值相等,所有类都存在equals方法(继承自Object)。

判断字符串对象相等:==
        String str1 = "abc";
        String str2 = "abc";
        System.out.println(str1 == str2);		// true
        
判断字符串对象值相等:equals
        String str1 = "abc";
        String str2 = new String("abc");
        System.out.println(str1.equals(str2));	// true

判断字符串对象为空:== null
【注意null对象也能进行== 运算】
		String str1 = null;
        System.out.println(str1==null);			// true
        
判断字符串对象值为空: equals("")、length()==0
【确定字符串对象不为null的情况下才能按以下写法】
		String str1 = "";
        System.out.println(str1.equals(""));
        System.out.println(str1.length()==0);
【不确定是否为null,更稳妥的写法如下:】
		String str1 = "";
        System.out.println("".equals(str1));	// true
        System.out.println(str1!==null && str1.length()==0); // true

综上所述:
1.与字符串对象本身相关的判断,都用==,且只能用==
2.与字符串对象值相关的判断,都用equals(),判断值为空也可以使用length()

补充:
什么叫对象为空?什么叫对象没有初始化?有什么区别?

  1. 对象为空并不是说对象没有值,而是说对象为空值,也即对象的值为null。这个null值,可以是手动赋的,也可以是第三方函数的返回结果,也可能是静态变量由JVM默认初始化的。
  2. 对象没有初始化说的是对象没有值,局部变量定义后没有赋初值就会出现这个问题。
  3. 调用没有初始化的对象是编译时问题,在IDE中就会报红;调用为空值的对象是运行时的问题,程序运行时才会报错。【空指针异常】

二 字符串常量和字符串变量

字符串变量变量的说法是错误的,应该叫字符串缓冲变量,为了好记,将错就错

2.1 String、StringBuffer、StringBuilder的区别

  1. String变量不应该叫字符串对象,而应该叫字符串对象的引用变量。实际上,创建任意对象时,都不应该把XXX叫做XXX对象,而应该叫做XXX对象的引用变量。对象存放在堆,引用变量存放在栈。
  2. 字符串对象(不是指引用变量)是一个常量,一旦创建值就不能改变,也就是所对应的内存单元的值不能改变,长时间不使用,会被垃圾回收;
  3. 对字符串对象的各种改变实际上是创建了新的字符串对象,分配了新的内存单元,并让原来的引用变量指向这个新的字符串对象。

根据上面的叙述,显而易见,程序中要频繁修改字符串对象的值时,使用字符串对象是很不利的。此时,应该使用字符串缓冲对象StringBuffer、StringBuilder,这两个(指向的对象)是可变的,不会产生新的对象,可在修改完成后再为String对象。【不仅比String占内存小,执行的也更快】

StringBuffer和StringBuilder的区别在于:
二者方法和功能完全是等价的,区别在于:StringBuffer是线程安全的,有同步机制;StringBuilder不是线程安全的,但因此更快。单线程时优先使用StringBuilder,多线程时使用StringBuffer。

2.2 StringBuffer、StringBuilder

二者API一致,且访问对象的操作与String对象也类似,主要是使用修改操作,包括:append、insert、delete、replace、reverse

StringBuilder stringBuilder = new StringBuilder("123");
        stringBuilder.append("456");		// 123456
        stringBuilder.insert(0,"abc");		// abc123456
        stringBuilder.delete(0,2);			// c123456
        stringBuilder.replace(0,1,"xyz");	// xyz123456
        stringBuilder.reverse();			// 654321zyx
        System.out.println(stringBuilder.toString());

注意:
1.String类没有reverse方法,StringBuilder才有。在字符串表示大数时,可能涉及reverse操作,因为数值高位在左边,而数组高位在右边。
2.append(),数可以是string,也可以是char

三 字符串常用操作

3.1 字符串与字符数组转换

String s = "123";
char[] chars = s.toCharArray();
String str = String.valueOf(chars);

注意:
若用字符串表示数值,在为字符数组时要特别注意,数值高位在数组低位

3.2 字符串截取

四 正则表达式