Java----字符串(String)

一、什么是字符串?

定义:字符串是一个多个字符的序列

在java中用char数组来表示字符串,我们可以从源码看到:java对char数组进行了封装,并用String类型来表达字符串,也就是说java程序中所有字符串的文字都被成为此类的对象。

#java 8源码
/** The value is used for character storage. */
private final char value[];

看了字符串的定义之后,让我们来看一下

1.字符串的赋值方式?

2.两种赋值方式的区别

3.解释为什么String是不可变的?

二、赋值方式

1.直接赋值

        顾名思义,直接赋值就如下所示:

String s1="abc";

2.new

String s2=new String("abc");

可以看到这两种方式,都可以创建一个字符串,那么我们现在来解释一下两种赋值方式的区别。

区别:直接赋值会创建一个对象或者不创建对象,而new的方式至少会创建一个都对象

        在说明这个区别之前,我们要了解几个概念:

        1.堆内存:存储对象或者数组,new来创建的,都存在堆内存中。

        2.:方法运行时使用的内存,比如main方法运行,进入方法栈中执行。

        3.常量池:我们在开发中,会经常大量使用String类型,java对其进行了性能优化。

看下面一段代码:

String a1=new String("dog");

String a2=new String("dog");

System.out.println(a1==a2); //运行结果false

这段代码可以看出,按照常规方式创建字符串对象的时候,即使字符串内容完全一致,也会创建多个对象,想象一下如果要使用几万个字符串,都要创建对象的话,那么会极大的浪费资源,所以就引入了字符串常量池这个概念:

当我们用字面量创建字符串时,字符串常量池会将其对象引用进行保存,后面如果创建重复的字面量,就会直接将字符串常量池的引用进行返回。


public class demo11 {
    public static void main(String[] args) {
        String s1="abc";
        String s2="abc";
        System.out.println(s1==s2);//结果为true
    }
}

上图中代码可以看出对s1和s2使用直接赋值的方式,字面量都是"abc"。直接赋值时,系统都会检查该字符串在串池是否存在,不存在的话则创建新的,存在的话,可以看到字符串常量值"abc"的地址值返回。这也就解释了直接赋值的方式会创建一个或者不会创建对象

那么对于new的方式,直接看内存原理图和代码就很好理解了。

public class demo98{
    public static void main(String[] args) {
          String s1=new String("abc");
          String s2=new String("abc");
          System.out.println(s1==s2);//运行结果为false

    }
}

        从内存图可以看出,当s1创建字符串对象时,发现常量池中并没有”abc"这个字符串这个对象,它就会在常量池中创建一个对象还会在堆上创建一个字符串s1并指向常量池,而且将s1的地址值返回,这时就是创建了两个对象。当s2创建对象的时候,也会去检查常量池中是否有该字符串,若有的话,直接在堆中创建一个新的String对象,这个对象也会指向常量池中的"abc",然后把这个新的String的地址返回,这时就是创建了一个对象

那么,可能有人会说,常量池到底有没有我们也不知道啊,你这样说,也没有依据啊,那我们就来看一下字节码文件,通过javap -v demo98.class进行编译查看结果

Constant pool:
   #1 = Methodref          #8.#21         // java/lang/Object."<init>":()V
   #2 = Class              #22            // java/lang/String
   #3 = String             #23            // abc
   #4 = Methodref          #2.#24         // java/lang/String."<init>":(Ljava/lang/String;)V
   #5 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = Methodref          #27.#28        // java/io/PrintStream.println:(Z)V
   #7 = Class              #29            // stringDemo/demo98
   #8 = Class              #30            // java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               StackMapTable
  #16 = Class              #31            // "[Ljava/lang/String;"
  #17 = Class              #22            // java/lang/String
  #18 = Class              #32            // java/io/PrintStream
  #19 = Utf8               SourceFile
  #20 = Utf8               demo98.java
  #21 = NameAndType        #9:#10         // "<init>":()V
  #22 = Utf8               java/lang/String
  #23 = Utf8               abc
  #24 = NameAndType        #9:#33         // "<init>":(Ljava/lang/String;)V
  #25 = Class              #34            // java/lang/System
  #26 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #27 = Class              #32            // java/io/PrintStream
  #28 = NameAndType        #37:#38        // println:(Z)V
  #29 = Utf8               stringDemo/demo98
  #30 = Utf8               java/lang/Object
  #31 = Utf8               [Ljava/lang/String;
  #32 = Utf8               java/io/PrintStream
  #33 = Utf8               (Ljava/lang/String;)V
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               println
  #38 = Utf8               (Z)V

 我们可以看到,常量池会把abc存进去,供之后复用。

        到这里,还有一个问题就是那么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

final:

        .final修饰的变量表示变量的值不可改变,被final修饰过的变量就是常量

        .final修饰方法表示此方法不可以被重写

        .find修饰类表示此类不能被继承

1.第一点:可以看到String这个类被final修饰,表示该类不能被继承,从而杜绝了子类覆盖父类行为的过程

2.第二点:就是String字符串底层存储的实现也是用char value[]这个数组,这个数组也被final修饰,但是只说明它不能指向新的数组,不代表数组本身的数据不可被改变,真正不可变的原因是他还被private修饰,并且String并没有暴露提供任何修改字符数组的方法。

综上两点,说明String时不可变的。

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值