[JAVA SE] 「文字列」とそれに対応する基礎となるソースコードの詳細な分析

親切なヒント

皆さんこんにちは、Cbiltpsです 私のブログでは、わかりにくい文章言葉で表現しにくい要点などがある場合は、画像を載せていますなので、写真付きのブログはとても重要です

コードのコメントには多くの知識が含まれているため、コードのコメントも非常に重要です。

この記事では、まずString の簡単なルールをいくつか説明します。

それでは、この記事の補足として、String の不変性について別の記事を書きます

したがって、この記事と次の記事を大きなクラスとして扱うことができます: String class !

序章

実際、このセクションの内容は大学ではほとんど教えられておらず、深く学ぶ人もほとんどいません。

しかし、自分自身を改善しStringの最下層がどのように実現されるかを理解するために、今日の記事があります。

この章の内容は、完全に内なる力の育成と捉えていただいて、百害あって一利なし!

今日描いた記憶マップはさらに頭が熱くなりますので、ぜひじっくり見てください!

この章と次の章のハイライト

  • Stringクラスについて知る
  • 文字列定数プールについて知る
  • String クラスの基本的な使い方を理解する
  • String クラスの一般的な操作に精通している
  • StringBufferStringBuilderを理解する

本文の始まり


1. 文字列の詳細な分析


1.1 文字列を理解する

C 言語の没有文字列型inC++とin は! とJava呼ばれます。String

次の 2 つの質問を理解するだけです。

とは字符串:二重引用符を使用します。複数の文字を使用" "できます。これは文字列定数です

とは字符:一重引用符で囲むことできる文字は1 つだけです。これは文字定数です' '

注意: Java には字符串以\0结尾引数がありません。

その直後に、String ソース コードを直接開き、内部のコードを確認すると、それが最終的に変更されており継承できないことがわかります。
ここに画像の説明を挿入

1.2 文字列の作成

//方式1:
String str = "祥子";//字符串常量
//方式2:
String str2 = new String("花花");//调用构造方法定义
//方式3:把数组变成字符串
char[] chars = {
    
    'a', 'b', 'c'};
String str3 = new String(chars);
System.out.println(str3);

公式ドキュメントにはまだ多くのString 構築メソッドが記載されています。ご覧ください。ブロガーはほとんどの例のみを紹介しました。

1.3 文字列コードのメモリレイアウト

まずサンプルコードを書きます。

public class Main {
    
    

     public static void main(String[] args) {
    
    
        String str = "abcdef";
        String str2 = str;
        System.out.println(str);
        System.out.println(str2);
        /*打印出来是一样的!画一下内存图!*/
        
        /*其实这里的str和str2两个引用都是指向"abcdef"的,然后想一个问题,可以用str2修改"abcdef"吗?
        答案是不可以的,因为:它是字面值常量不能修改的!*/
        
        System.out.println("==============");
        str = "hello";//但是这里的修改是修改的指向!
        System.out.println(str);
        System.out.println(str2);
        /*打印出来不一样!画一下内存图!*/
    }
}

ここに画像の説明を挿入

上の図も同じ理由で印刷し、変更後のメモリ マップを確認します (変更点)。

ここに画像の説明を挿入

次に、コードを書き換えます。

public class Main {
    
    

    public static void func(String s,char[] array) {
    
    
        s = "xiangzi";
        array[0] = 'p';
    }
    
    public static void main(String[] args) {
    
    
        String str = "abcdef";
        char[] chars = {
    
    'b','i','t'};
        func(str,chars);
        System.out.println(str);
        System.out.println(Arrays.toString(chars));
    }
    /*上面的问题,一定要画草图了解一下!里面牵扯较复杂的指向问题!*/
    /*所以说,不是 传引用 就是可以改变实的值!你要看这个引用到底干啥了!!*/
}

上記のコードのメモリ マップとポインティングの変更については、次の図を参照してください。

ここに画像の説明を挿入

したがって、実際の値を変更すること传引用は可能です。このリファレンスが何をしているのか見てみる必要があります。


2. 文字列比較が等しい


2.1 内容の比較

コンテンツの比較では、比較に平等メソッドを使用します。使い方は、ソース コードまたはドキュメントにアクセスするだけでわかります

では、equals メソッドで注意すべき点について説明します。

 public static void main(String[] args) {
    
    
        String str1 = null;
        /*使用equals的时候要注意:一定要预防空指针异常!*/
        String str2 = "11";//如果想要修改的话,就必须通过反射修改
//        System.out.println(str1.equals(str2));//这样子就是空指针异常
        System.out.println(str2.equals(str1));//这里的str2不是空指针,就不会报错的
    }

2.2 アドレスの比較

コードに直接移動します (ナレッジ ポイントと拡張コンテンツを含む)。

public class Main {
    
    
    public static void main4(String[] args) {
    
    
        String str1 = "hello";
        String str2 = new String("hello");
        System.out.println(str1 == str2);
        /*其实这里比较的不是内容,而是地址! 地址不一样,运行的结果就是false!*/

   /**然后下面做一点铺垫(涉及到JVM的知识):
    *
    * Class文件常量池:Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量
    * 池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加
    * 载后存放到方法区的运行时常量池中。
    *
    * 运行时常量池:当程序把编译好的字节码文件加载到JVM当中后,会生成一个运行时常量池(在方法区,就是从磁盘到内存),
    * Class文件常量池会变成运行时常量池。
    *
    * 字符串常量池:本质是一个哈希表(StringTable),JDK1.8开始放在了堆里面,里面存的是驻留字符串的引用,
    * 在堆中的字符串实例被这个哈希表引用之后就等同被赋予了”驻留字符串”的身份,
    * 在JVM中字符串常量池被所有类共享。
    *
    * 什么是哈希表:其实就是一个数据结构,描述和组织数据的一种方式,非常的块!
    *  如果你想知道StringTable如何实现的,可以直接去看JVM的源代码,但是使用C++写的!
    */

上記のコードのメモリ マップを見てください。

ここに画像の説明を挿入

次に、次のコード部分を見てください。

public static void main(String[] args) {
    
    
        String str1 = "hello";
        String str2 = "hello";
        System.out.println(str1 == str2);
        /*这里肯定就是true了!*/
    }

そのメモリマップは次のようになります。

ここに画像の説明を挿入

ここに絵を描くと、str2の右側のアドレスが0x334になっていますが、変更するのが面倒なので読むときは注意してください!

上記の知識ポイントに関して、確認したい場合は、逆アセンブリ コードを開いて表示できます。

ここに画像の説明を挿入
この時点で、文字列がどのように作成されるかがわかりました。ソース コードがどのように実装されるかを見てみましょう。

JVM のソース コードには次のものが表示されます。

要素の配置方法。

StringTable の作成方法。

データがどのようにハッシュに変換されるかなど。

これらはすべて C++ で書かれているため、理解できないブロガーもいます。

JAVA が巨人の肩の上に立っているのを見てください!

ここに画像の説明を挿入

理解を強化するために、引き続き下を見てください。

public class Main {
    
    
    public static void main(String[] args) {
    
    
        String str1 = "hello";
        String str2 = "he" + "llo";//此时 他两都是常量,编译的时候,就已经确定好了是"hello"
        String str3 = "he";
        String str4 = str3 + "llo";//此时str3是一个变量 -> 编译的时候,不知道是啥? str4不是一个完整的对象!
        /*拼接的hello并不是在常量池里面,拼接的是一个单独的对象!所以运行结果就是false!*/
        System.out.println(str1 == str4);
    }
}

上記のコードでは、str2は「hello」です。以下の逆アセンブリ コードを参照してください。

ここに画像の説明を挿入

メモリグラフを見てください。

ここに画像の説明を挿入

次の例はさらに厄介です (重要)。

public static void main(String[] args) {
    
    
        String str1 = "11";
        String str2 = new String("1") + new String("1");//这里创建的对象是StringBuilder对象!
        System.out.println(str1 == str2);
    }

メモリ マップに直接移動します。

ここに画像の説明を挿入

さらに、上の図を確認するために逆アセンブリ コードを確認することも同じです。

ここに画像の説明を挿入

この場合は次のようになります。

//代码1
public static void main(String[] args) {
    
    
        String str2 = new String("1")+new String("1");
        String str1 = "11";
        System.out.println(str1 == str2);//如果反过来依旧是false
    }

以下をご覧ください。

//代码2
public static void main(String[] args) {
    
    
        String str2 = new String("1")+new String("1");
        str2.intern();//手动入池
        String str1 = "11";
        System.out.println(str1 == str2);//这里就是true了
    }

ここまで来たらもう描かない、ほとんど役に立たない、もう描きたくない…。

実際、その原理は次のように簡単に説明されています。

コード 1は上記のデモと同じコードです。

コード 2 は、スペル「11」を手動でプールに入れます。

次に、文字列を作成するときに、この文字列への直接のポイントがあるかどうかを確認します。

文字列比較の問題はこれで終わりです、みなさん、よく見て勉強してください!

本文の終わり

本当はここでも書けるのですが、もっと網羅的に書きたいので、

ただし、長すぎるのは良い解決策ではありません。

String については、後で記事を書きます

それは、文字列を不変にし変更可能にすることです。

ぜひ楽しみにしていてください!いいねと注目をありがとうございます!

おすすめ

転載: blog.csdn.net/Cbiltps/article/details/122535985