zoukankan      html  css  js  c++  java
  • 详解String类

    摘录一些很经典的代码段。个人觉得特别有代表性,能更深入的理解String以及StringBuilder。值得看看。参考了不少别人的资料,但都是自己仔细总结的。期待大家为我纠正错误之处。
    ====================================================================================
                             String
    一.String的内存解析。
    这是刚开始接触java的同学相对较多遇到问题的地方吧。先看两个语句:
    String str0 = "java";
    String str1 = new String("hello java!");
    两种声明字符串的方式。前一种,并没有在堆中分配内存。只是将"java"保存在常量池中。第二种方式,是将"hello java!"保存在常量池中,new操作所开辟的堆空间里会复制一份"hello java!"。然后讲str1指向new后的堆空间。这
    就是为什么说第二个语句实际上是两个对象。如图:

    ---------------------------------------------------------------------------------------------------------------------------------------------------
    二.String是不可变的。
    刚看到String是不可变的时候,有点不明白。后来查看一些资料,再看看String类的源代码,就明白了。这也解决了另外一个常被问到的面试题: 问,String类可以被继承吗?先来看看String的源码,String类的声明是这样的:public final class String......这样是不是就有答案了。至于String是不可变的原因是,从源码可以看出,String的成员变量差不多都是被final修饰 的。再看两个语句:
    String str = "java";
    str = "hello";
    String的不可变是指,虚拟机并没有改变原来的对象"java",而是新创建了一个对象"hello",再让str指向这个新的对象"hello"。
    ----------------------------------------------------------------------------------------------------------------------------------------------------
    三.String相关的几段经典代码。
    *****代码段一 
            String str0 = "ab";  
            String str1 = "a" + "b";  
            System.out.println(str0 == str1); 
    *****代码段二 
            String str0 = "ab";  
            String str1 = "b";  
            String str2 = "a" + str1;  
            System.out.println(str0 == str2);   
    *****代码段三
          String str = "java";
          String str1 = new String("java");
          System.out.println(str==str1.intern());
    代码段一,输出的结果是true,代码段二的结果是false。
    对于以上两段代码的说明:
    1.当使用String str = "java";方式来创建对象时,java运行时会拿这个"java"去String的常量池里去找是否有这个字符串,如果有,直接将常量池中地址给str。如果没有,讲"java"加入常量池。
    2.当使用String str = new String("java");来创建对象时,同样会拿"java"去常量池中找,如果没有,则添加。并复制一份到new的堆空间,将堆空间地址给str。如果有,直接复制一份到new的堆空间,将堆空间地址给str。
    3.使用常量来串联表达式时,不会在堆空间新建对象。只是在常量池中检查,有则直接指向,没有则在池中创建再被指向。如代码段一String str1 = "a" + "b";因为只在常量池中操作,所以返回true。
    4.使用带变量表达式创建String,则不仅检查常量池,还会在堆中新建对象。如代码段二。返回false。
    5.关于字符串常量池,还有一个intern()方法。以前没接触毕老师视频的时候,因为不知道字符串有个常量池,所以和一同学不理解编程中遇到的现象。 为此查了不少资料才搞明白。当时在一资料上看到这个方法。印象比较深,这个方法的意思就是将常量池的字符串进行比较(我是这么理解的)。如代码段三。
    -------------------------------------------------------------------------------------------------------------------------------------------------------
    四.关于一道面试题的多种说法解析。

        public class Example  
        {  
            String str = new String("good");  
            char[] ch = {'a','b','c'};  
            public static void main(String[] args)  
            {  
                Example ex = new Example();  
                ex.change(ex.str, ex.ch);  
                System.out.print(ex.str+"......and.....");  
                System.out.print(ex.ch);  
            }  
          
            public void change(String str,char ch[])  
            {  
                str = "test ok";  
                ch[0]='g';  
            }  
        }  
    


    一些理解:
    1.字符串是不可变的;
    2.值传递与引用传递的不同(事实上,java中只有值传递);
    3.成员变量与局部变量区别;
    经过验证,在程序change函数里的str前面加上this。就会出现不一样的结果。查阅许多资料后,理解是这样的:
    1.字符串是不可变的。这话没有错。如第二部分的解说。也许也有些类似以上的题目,被解说为字符串是不可变的。我觉得这种说法,不太合适。这里的字符串不 可变体现在,当change函数里str前加上this时,change被调用后,str指向了"test ok",原来的"good"暂时还在,只不过str不再指向它了。这就是说"good"是不可变的。我将程序稍微调整如代码段四。运行结果说明了所有问 题。
    ******代码段四

        //相比前面代码增加了成员变量num,以及change函数增加同名形参。  
        public class Example  
        {  
                  String str = new String("good");  
                  char[] ch = {'a','b','c'};  
                  int num = 9;  
                  public static void main(String[] args)  
                  {  
                          Example ex = new Example();  
                          ex.change(ex.str, ex.ch,ex.num);  
                          System.out.print(ex.str+"......and.....");  
                          System.out.println(ex.ch);  
                          System.out.println(ex.num);  
                  }  
                  public void change(String str,char ch[],int num)  
                  {  
                             str = "test ok";  
                             ch[0]='g';  
                             num = 11;  
                  }  
        }  
    

    2.关于值传递和引用传递的说法。查过资料说,String是对象类型,但有值传递的特征。我的理解是,同样如代码段四。这里是不是可以这么理解,具有值传递特征的变量才会出现局部变量和成员变量的问题。事实上,java中只有值传递。
    3.如代码段五。这一段不需要任何解释了。结合上面一条说法,大家可以自己思考下。
    ******代码段五

    class Person  
    {  
             String name;  
             int age;  
             Person(String name,int age)  
             {  
                        this.name = name;  
                        this.age = age;  
             }  
    } 
    

    最后,我觉得可以这样分析这道面试题(有错之处欢迎跟帖纠正):
    因为String类以及Stirng类几乎所有成员变量都被final修饰,所以String是不可变的,因此String虽然是对象,但有值传递的特征。在java中,当函数的形参与成员变量同名时,可以使用this来区分。这里change函数里没有添加this,所以结果为 good gbc。

    ===========================================================================================
            String、StringBuilder、StringBuffer

    StringBuilder是1.5才出现的。与StringBuffer有至少两个不一样的地方。第一,名字不一样(废话)。第 二,StringBuilder是线程非安全的。也就是说,频繁的操作中不需要判断同步锁,在单线程情况下,可以大大提高效率。帮助文档里是这样介绍的: 一个可变的字符序列。此类提供一个与StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。我打开两份帮助文档,分别找到StringBuilder和StringBuffer,一一比对,从上到下,每一个方法都是一模一样的。所以,再没 什么好说的了。
    既然官方文档说字符串缓冲区被单个线程使用的情况很普遍,不如介绍下StringBuilder里的一部分方法实在:
    在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符添加或插入到字符串生成器中。 append 方法始终将这些字符添加到生成器的末端;而 insert 方法则在指定的点添加字符。其它的方法,都参照帮助文档吧。之所以前面加个String,是这里有必要说明一下String与StringBuilder 的区
    别(StringBuffer是一样的了)。如前文叙述,String是不可变的。通过以下两个语句来看看String是怎样完成字符串的串联操作的。
    String str = new String("hello");
    str += " java";
    1.第一句,首先,检查常量池是否有"hello",没有则创建"hello",再将"hello"复制到new的堆空间,让str指向堆中的"hello"。
    2.第二句,在常量池中创建" java"。但没有指向。然后创建一个StringBuilder对象(1.5前是StringBuffer),调用append(),将字符串串联
    后,通过StringBuilder.toString()转化为String。也即使说,String的串联操作是通过StringBuilder完成 的。而且整个过程中创建了四个对象。创建对象以及频繁操作后多余的对象要被gc回收,都是非常耗费时间的。所以说StringBuilder效率高。可以 自己试着写小程序来比较运行时间。(差距是非常大的)。

  • 相关阅读:
    Django【进阶篇-缓存类型】
    深度剖析Kubernetes API Server三部曲
    深度剖析Kubernetes API Server三部曲
    深度剖析Kubernetes API Server三部曲
    Istio技术与实践03:最佳实践之sidecar自动注入
    原来你是这样的PaaS!
    5分钟APIG实战: 使用Rust语言快速构建API能力开放
    Log4J日志配置详解
    cookie是如何保存到客户端,又是如何发送到服务端
    session cookie
  • 原文地址:https://www.cnblogs.com/congyue/p/3083782.html
Copyright © 2011-2022 走看看