重学java-8.String的基本概念

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/euzmin/article/details/89017787

String的基本概念

String的匿名对象

注意:字符串常量就是String的匿名对象。

public static void main(String[] args) {
		String a = "wow";
		System.out.println("wow".equals(a));//这里的"wow"就是一个匿名对象,由系统自动生成
	}

输出结果:
在这里插入图片描述

为了加深我们对String匿名对象的理解,我们再举个栗子:

public static void main(String[] args) {
		String a = new String("wow");//这里的申请了两段内存
		String b = "wow";
		System.out.println(a==b);
	}

输出结果:
在这里插入图片描述

之前在重学java-4.面对对象基本概念 的学习中,我们可以知道,每当new关键字出来,就代表堆中申请了一段内存。因而在这个例子中,第一个语句申请了 两段内存

一段是a指向的堆内存,一段是"wow"匿名对象。并且"wow"匿名对象在第一个语句结束之后,就会变成垃圾内存等待回收。

字符串的常量池

我对常量池的理解就是,为了节省各种申请空间的开销,java搞了个常量池,只要字符串在池里面,变量就可以直接引用这个字符串,不需要重复申请空间了。java7版本以后,字符串池也在堆中存储。

public static void main(String[] args) {
		String a = "wow";
		String b = "wow";
		System.out.println(a==b);
	}

输出结果:
在这里插入图片描述
是的,a与b所指向的内存一样,这就是因为"wow"在 编译的时候就已经放入了常量池中,后续的a与b实际上都是对常量池中"wow"的 共享

再看一个经典题目

public static void main(String[] args) {
		String a = new String("wow");//这里的"wow"入池
		String b = "wow";
		System.out.println(a==b);
	}

在这里插入图片描述

是的,正是上面匿名对象所举的例子。在这里"wow"匿名对象在 入池后 就变成了垃圾内存等待GC回收。a指向的是堆中new出来的内存,而b指向的是 之前的匿名对象"wow" 入池后的地址,因此输出为false。

如果想让a在堆中的内存在第一个语句中入池,则需要使用intern()类。

public static void main(String[] args) {
		String a = new String("wow").intern();
		String b = "wow";
		System.out.println(a==b);
	}

输出结果:
在这里插入图片描述
注意,intern()的用法是:“如果常量池中存在当前字符串, 就会直接返回 常量池中的 当前字符串. 如果常量池中没有此字符串, 会将此字符串放入常量池中后, 再返回常量池中的字符串”。

举个例子:

public static void main(String[] args) {
		String a = new String("wow");//匿名对象"wow"入池,a指向堆中的对象
		String c = a.intern();				//c指向常量池中的对象				
		String b = "wow";//b指向常量池中的对象
		System.out.println(a==b);
		System.out.println(b==c);
	}

输出结果:
在这里插入图片描述

String的不可变性

下面是String源码的截取

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

  

可以看到,在String中字符串以字符数组的形式存储(在java9之后,以byte的形式存储)。

value[]数组以关键字 final 修饰,且类中没有修改value[]数组的方法。可见String一旦定义则 不可改变

比如下面这个例子:

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

我们并不是将"hello"字符串改变成了"bye"字符串,而只是将a的引用由"hello"指向了"bye".之后"hello"将作为垃圾等待回收。

那么为什么将String设为不可变的呢?事实上,String不可变有以下5点好处:

1-字符串池

前面我们提到,字符串在直接赋值的时候,会先去字符串池里找,找不到再申请内存定义。

所以很多时候会有许多个句柄指向字符串池的同一个常量。如果字符串是可修改的,将字符串池中的常量修改后,所有指向该常量的字符串的值将全部改变。

举个例子:

public static void main(String[] args) {
		String a = "hello";//这个时候a与b指向同一个常量
		String b = "hello";
	}

我们看上面这个代码,a与b同时指向常量池中的"hello"字符串,如果String的值是可变的,当我们改变a值的时候,b值也会同时改变。当数据多的时候,我们将不敢修改或定义任何的字符串,因为会随时改变常量池中的内容。

2-hashcode的缓存

hash值是String的一个私有成员变量,因为它经常被使用(hashmap,hashset中),且String的不可变性,我们可以直接缓存hash值,节省了大量的运算。

3-利于其他数据结构的使用

比如我们把String放在set里,如果String是可修改的,很有可能导致set的结构混乱,无法进行去重与排序的操作。

4-安全性

String经常用于文件的传送与连接,如果String可以修改,可能会出现连接到另一个机器的情况等。

5-不可修改的对象天然是线程安全的

3、4、5本人接触较少,参考博客中的解释更为详细。总之,String的不可变性对效率与安全性都有好处。

String的基本操作

取出指定索引的字符

我们可以使用charAt()方法取出指定索引的字符,该方法源码如下:

public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

前面我们提到过,String在java8中内部存储方式是字符数组,因而上面的代码也不难理解。
举个使用的例子:

public static void main(String[] args) {
		String a = "abc";
		char b = a.charAt(1);
		System.out.println(b);
	}

输出结果:
在这里插入图片描述

字符数组与String的转换

举个简单的例子:

public static void main(String[] args) {
		String a = "abc";
		char b[] = a.toCharArray();
		System.out.println(b);
		String c = new String(b);
		System.out.println(c);
	}

输出结果:
在这里插入图片描述
object.toCharArray()的源码:

public char[] toCharArray() {
        // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }

可以发现,源码中使用的是System.arraycopy()方法,这个方法我们在 重学java-5.数组的基本概念 中有详细的介绍。

字符串的比较

举个例子说明4个方法的用法:

public static void main(String[] args) {
		String a = "abc";
		String b = "aBc";
		
		System.out.println(a.equals(b));
		System.out.println(a.equalsIgnoreCase(b));//忽略大小写
		
		//compare方法的返回值有三种情况:大于0表示表示a比b大,等于0表示a==b,小于0表示a比b小
		System.out.println(a.compareTo(b));
		System.out.println(a.compareToIgnoreCase(b));
	}

equalsIgnoreCase()方法中忽略大小写的实现用到了regionMatches()方法,本质是把所有字符转化为大写进行比较。

字符串的查找

举个例子:

public static void main(String[] args) {
		String a = "abcdefg";
		String b = "de";
		String c = "ab";
		String d = "fg";
		System.out.println(a.contains(b));
		
		//indexOf()找不到的时候返回-1
		System.out.println(a.indexOf(b));
		System.out.println(a.indexOf(b,4));//从第4个下标开始从前往后找
		
		System.out.println(a.lastIndexOf(b));
		System.out.println(a.lastIndexOf(b, 2));//从第2个下标开始从后往前找
		
		System.out.println(a.startsWith(c));
		System.out.println(a.startsWith(b,3));
		
		System.out.println(a.endsWith(d));

	}

输出结果:
在这里插入图片描述

字符串的替换

举个例子:

public static void main(String[] args) {
		String a = "aaaa";
		
		System.out.println(a.replaceAll("a", "A"));
		System.out.println(a.replaceFirst("a", "A"));
	}

在这里插入图片描述

字符串的截取与连接

public static void main(String[] args) {
		String a = "abcdefg";
		
		//截取
		System.out.println(a.substring(3));
		System.out.println(a.substring(3, 5));
		
		//连接
		System.out.println(a.substring(3).concat(a.substring(3,5)));//等价于+
	}

在这里插入图片描述

字符串的拆分

举个例子:

public static void main(String[] args) {
		//对特殊符号的拆分
		String ip = "192.168.1.111";
		String a[] = ip.split("\\.");//用\\转义,防止与正则表达式冲突
		System.out.println(Arrays.toString(a));
		
		//第二个参数limit表示将字符串拆分为几段
		String b[] = ip.split("\\.",2);
		System.out.println(Arrays.toString(b));
		
		//用空字符拆分
		String c[] = ip.split("");
		System.out.println(Arrays.toString(c));
	}

输出结果:
在这里插入图片描述
如果点开split源码我们可以发现,它是用list与String.substring()实现的。

另外,还有一个方法在拆分的时候经常使用。举个例子:

public static void main(String[] args) {
		
		String ip = "   192 168 1 111   ";
		
		String a[] = ip.split("\\.");
		//trim的作用是省去字符串两边的空格(中间的不省去
		String b[] = ip.trim().split("\\.");
		
		System.out.println(Arrays.toString(a));
		System.out.println(Arrays.toString(b));
		
		
	}

输出结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/euzmin/article/details/89017787
今日推荐