この章の内容
親切なヒント
皆さんこんにちは、Cbiltpsです 私のブログでは、わかりにくい文章や言葉で表現しにくい要点などがある場合は、画像を載せています。なので、写真付きのブログはとても重要です!!!
コードのコメントには多くの知識が含まれているため、コードのコメントも非常に重要です。!!
この記事では、まずString の簡単なルールをいくつか説明します。
それでは、この記事の補足として、String の不変性について別の記事を書きます。
したがって、この記事と次の記事を大きなクラスとして扱うことができます: String class !
序章
実際、このセクションの内容は大学ではほとんど教えられておらず、深く学ぶ人もほとんどいません。
しかし、自分自身を改善し、Stringの最下層がどのように実現されるかを理解するために、今日の記事があります。
この章の内容は、完全に内なる力の育成と捉えていただいて、百害あって一利なし!
今日描いた記憶マップはさらに頭が熱くなりますので、ぜひじっくり見てください!!!
この章と次の章のハイライト
- Stringクラスについて知る
- 文字列定数プールについて知る
- String クラスの基本的な使い方を理解する
- String クラスの一般的な操作に精通している
- StringBufferとStringBuilderを理解する
本文の始まり
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 については、後で記事を書きます。
それは、文字列を不変にして変更可能にすることです。
ぜひ楽しみにしていてください!いいねと注目をありがとうございます!