1、String的本质

线程安全  

  打开String的源码,类注释中有这么一段话“Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings.Because String objects are immutable they can be shared.”。这句话总结归纳了String的一个最重要的特点:String是值不可变(immutable)的常量,是线程安全的(can be shared)。

不可继承

  String类使用了final修饰符,表明了String类的第二个特点:String类是不可继承的。

  在Java中,被final修饰的类是不允许被继承的,并且该类中的成员方法都默认为final方法。在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率。而从Java SE5/6开始,就渐渐摈弃这种方式了。因此在现在的Java SE版本中,不需要考虑用final去提升方法调用效率。只有在确定不想让该方法被覆盖时,才将方法设置为final。

值不可变,存储方式为字符数组

  1. private final char value[];
  2. private final int count;

  从final的修饰可以看出其不可变性

  其中的String类的 concat方法为起扩容,如果方法的参数长度等于0这返回this,否则并组成一个新的string,并紧随其后拼接参数后返回新的string

  我们看String类的concat方法。实现该方法第一步要做的肯定是扩大成员变量value的容量,扩容的方法重新定义一个大容量的字符数组buf。第二步就是把原来value中的字符copy到buf中来,再把需要concat的字符串值也copy到buf中来,这样子,buf中就包含了concat之后的字符串值。下面就是问题的关键了,如果value不是final的,直接让value指向buf,然后返回this,则大功告成,没有必要返回一个新的String对象。但是。。。可惜。。。由于value是final型的,所以无法指向新定义的大容量数组buf,那怎么办呢?“return new String(0, count + otherLen, buf);”,这是String类concat实现方法的最后一条语句,重新new一个String对象返回。这下真相大白了吧!

  String类其实是通过char数组来保存字符串的。

总结:String实质是字符数组,两个特点:1、该类不可被继承;2、不可变性(immutable)

 

  1. public String substring(int beginIndex, int endIndex) {
  2. if (beginIndex < 0) {
  3. throw new StringIndexOutOfBoundsException(beginIndex);
  4. }
  5. if (endIndex > count) {
  6. throw new StringIndexOutOfBoundsException(endIndex);
  7. }
  8. if (beginIndex > endIndex) {
  9. throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
  10. }
  11. return ((beginIndex == 0) && (endIndex == count)) ? this :
  12. new String(offset + beginIndex, endIndex - beginIndex, value);
  13. }
  14.  
  15. public String concat(String str) {
  16. int otherLen = str.length();
  17. if (otherLen == 0) {
  18. return this;
  19. }
  20. char buf[] = new char[count + otherLen];
  21. getChars(0, count, buf, 0);
  22. str.getChars(0, otherLen, buf, count);
  23. return new String(0, count + otherLen, buf);
  24. }
  25.  
  26. public String replace(char oldChar, char newChar) {
  27. if (oldChar != newChar) {
  28. int len = count;
  29. int i = -1;
  30. char[] val = value; /* avoid getfield opcode */
  31. int off = offset; /* avoid getfield opcode */
  32.  
  33. while (++i < len) {
  34. if (val[off + i] == oldChar) {
  35. break;
  36. }
  37. }
  38. if (i < len) {
  39. char buf[] = new char[len];
  40. for (int j = 0 ; j < i ; j++) {
  41. buf[j] = val[off+j];
  42. }
  43. while (i < len) {
  44. char c = val[off + i];
  45. buf[i] = (c == oldChar) ? newChar : c;
  46. i++;
  47. }
  48. return new String(0, len, buf);
  49. }
  50. }
  51. return this;

从上面的三个方法可以看出,无论是sub操、concat还是replace操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。

“对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。

 2、String的内存机制

JVM运行时,会将内存分为两个部分:堆和栈。堆中存放的是创建的对象,而栈中存放的方法调用过程的局部变量或引用。而设计Java字符串对象内存实现的时候,在堆中又开辟了一块很小的内存,称之为字符串常量池,专门用来存放特定的字符串对象。

创建Java字符串对象有两种常用的方式:

  1. String 引用变量名="字符串内容";
  2. String 应用变量名=new String(<参数序列>);

我们先来看看创建字符串对象的第一种方式内存如何分配的,代码如下:

  1. String s1="osEye.net";
  2. String s2="osEye.net";

如上图描述了引用对象的关系,以及内存的分配。Java实现的步骤如下:

  1. 查看字符串常量池中是否存在内容与“osEye.net”相同的字符串对象。
  2. 若没有,则新创建一个包含该内容的字符串对象,并让引用变量指向该对象。例如,创建字符串s1的时候,字符串常量池中没有,则创建一个新对象,并让引用s1指向该对象。
  3. 若已经存在包含该内容的字符串对象,则让字符串引用直接指向该对象。例如,在创建字符串s2的时候,字符串常量池中已经有包含该内容的对象了,所以引用s2直接指向已有的对象。

在来看看第二种创建字符串对象的方式:

  1. String s1="osEye.net";
  2. String s2=new String("osEye.net");

如上图描述了引用对象的关系,以及内存的分配。Java实现的步骤如下:

  1. 首先在堆(不是常量池)中创建一个包含指定内容的字符串对象,并将字符串引用指向该对象。例如上述代码中,使用new创建字符串s3,其会直接在堆中创建一个内容为“osEye.net”的字符串对对象,并将引用s3指向该对象。
  2. 去字符串常量池中查看,是否有包含该内容的对象。
  3. 若有,则将new出来的字符串对象与字符串常量池中内容相同的对象联系起来。例如,本例中s3所指向的对象与s1所指向的联系起来。
  4. 若没有,则在字符串常量池再创建一个包含该内容的字符串对象,并将堆中的对象与字符串常量池中新创建出来的对象联系起来。

我们知道,new出来的字符串对象和字符串常量池中的对象是有联系的,可以通过intern方法来查看,方法签名:

  1. public String intern()

此方法将指定的字符串引用在字符串常量池中对应的对象,若其指向的对象本身就在字符串常量池中,则直接将自己指向的对象返回;若该字符串引用指向的对象在堆中,则返回字符串常量池中与其联系的对象。实例如下:

  1. package net.oseye;
  2. public class ExceptionTest {
  3.  
  4. public static void main(String[] args) {
  5. String s1="osEye.net";
  6. String s2=new String("osEye.net");
  7.  
  8. if(s1==s2){
  9. System.out.println("字符串引用s1和字符串引用s2所指向的是同一个对象");
  10. }else{
  11. System.out.println("字符串引用s1和字符串引用s2所指向的不是同一个对象");
  12. }
  13. if(s1.intern()==s2.intern()){
  14. System.out.println("字符串引用s1和字符串引用s2在字符串常量池中联系的是同一个对象");
  15. }else{
  16. System.out.println("字符串引用s1和字符串引用s2在字符串常量池中联系的不是同一个对象");
  17. }
  18. }
  19. }

输出结果:

  1. 字符串引用s1和字符串引用s2所指向的不是同一个对象
  2. 字符串引用s1和字符串引用s2在字符串常量池中联系的是同一个对象

Java基础-关键字-String的更多相关文章

  1. Java基础之String、StringBuffer、StringBuilder浅析

    Java基础之String.StringBuffer.StringBuilder浅析 一.前言: 位于java.lang包下的String.StringBuilder.StringBuffer一般都是 ...

  2. Java基础之String中equals,声明方式,等大总结

    无论你是一个编程新手还是老手,提到String你肯定感觉特别熟悉,因为String类我们在学习java基础的时候就已经学过,但是String类型有我们想象的那么简单吗?其实不然,String类型的知识 ...

  3. 【Java基础】String 相关知识点总结

    String 相关知识点总结 字符串的不可变性 概述 String 被声明为 final,因此它不可继承 在 Java8 中,String 内部使用 char 数组存储数据 public final ...

  4. Java 基础之 String 类

    String String 被声明为 final,因此不能被继承.(Integer 等包装类也不能被继承) 在 java8 中,String 内部使用 char 数组 来存储数据 public fin ...

  5. JAVA基础关键字小结一

    基础部分总是看起来简单,若要明白原理和使用场景,需要慢慢的体会. 本文对常用的关键字如final,transient,volatile,static以及foreach循环的原理重新梳理了一遍. 一.f ...

  6. java基础-关键字-native

     一. 什么是Native Method    简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由 ...

  7. Java基础-关键字-final

    在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能被继 ...

  8. Java基础——字符串String

    String类 1. String类位于java.lang包中,使用时无需导包. 2. 创建字符串的两种方式: ① 直接指定(字面量声明):String str = "abc"; ...

  9. java基础(六)-----String性质深入解析

    本文将讲解String的几个性质. 一.String的不可变性 对于初学者来说,很容易误认为String对象是可以改变的,特别是+链接时,对象似乎真的改变了.然而,String对象一经创建就不可以修改 ...

随机推荐

  1. hdu-5895 Mathematician QSC(数学)

    题目链接: Mathematician QSC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Jav ...

  2. smarty初始化文件

    为了smarty初始化文件能方便其他目录下的脚步包含使用,应该将初始化文件里的引入smarty类及其他路径设置为全局路径,而不是相对路径 另外,如果模板里有文件的路径的引用,一定要是相对PHP脚本文件 ...

  3. Spring2.5与JDK8的集成问题

    Spring2.5不支持JDK8及其以上的版本,因为有一段校验JDK版本的代码,当JDK版本大于1.7之后,会识别成JDK1.4之前的.会报版本太低的错误. /* * Copyright 2002-2 ...

  4. codevs 1133 表达式的值

    1133 表达式的值 2011年NOIP全国联赛普及组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Descript ...

  5. POJ 3974 Palindrome

    D - Palindrome Time Limit:15000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Sub ...

  6. Managing the Lifecycle of a Service

    service的生命周期,从它被创建开始,到它被销毁为止,可以有两条不同的路径: A started service 被开启的service通过其他组件调用 startService()被创建. 这种 ...

  7. 加密方式&数字签名

    1,对称加密 2,混合加密 3.数字签名 4,带加密的数字签名

  8. C语言 百炼成钢17

    //题目49:老师将糖果分成若干份,让学生按任意次序领取,第一个领取的,得到1份加上剩余糖果的1/10, //第二个领取的,得到2份加上剩余糖果的1/10,第三个领取的,得到3份加上剩余糖果的1/10 ...

  9. windows 80端口被占用

    首先关了iis服务,或者把端口换了. 然后关了SQLServer Reporting Service. http://www.2cto.com/os/201505/399603.html

  10. use AP_VENDOR_PUB_PKG.Update_Vendor_Site_Public to u ORA-01722: invalid number in Package AP_VENDOR_PUB_PKG Procedure Update_Vendor_Site_Public

    ORA-01722: invalid number in Package AP_VENDOR_PUB_PKG Procedure Update_Vendor_Site_Public 发现此问题的经过: ...