色々と間違っていますが、細かいところを理解するにはJavaやプログラミング一般についてもっと詳しくならないと難しいです。取りあえず解説を書いてみましたが、以下は、ある程度の知識が無いと理解できないかも知れません。
まず、Javaの変数には大きく分けてプリミティブ型と参照型の二種類があります。この二つはそれぞれをわけて考える必要があります。
プリミティブ型とはint
やdouble
といった基本的なベースになる型です。この型で変数が宣言された場合、その型のサイズ分のメモリ領域が確保されます(ローカル変数の場合はスタックに積まれ、インスタンス変数の場合はそのインスタンス作成時にヒープに確保されますが、この話はひとまず置いておきます。)。サイズは型によって決まっており、int
であれば4バイト、double
であれば8バイトと言ったようにです。
参照型はint[]
のような配列の型またはString
のようなクラスのオブジェクトの型です。この型では変数が宣言された場合、実際のオブジェクトを参照するために必要な分のメモリ領域が確保されます。実際の所では、Cで言えばポインタと呼ばれるもので実装されており、64bitアーキテクチャ向けの実装では8バイトです(以下の文章では、64bitの環境を前提とし、8バイトを確保する物として扱います。)。その変数の中身は、配列またはオブジェクトを指し示す値が入っています。また、null
という特殊な値も入れることができ、どのオブジェクトも指し示さないことを表す値です。
ここで注意して欲しいのは、参照型の変数の中に配列やオブジェクトが入っているわけでは無いことです。変数の中はそれらを参照する値(しばしば参照値と言われます)であって、オブジェクトそのものではないのです。では、配列やオブジェクトはどのタイミングできるのかというと、new演算子を使ったときに、配列ならその時指定されたサイズのメモリ領域が確保され、クラスのオブジェクトならクラスのインスタンス変数全てを確保できる分のメモリ領域が確保されます。サイズが10のintの配列なら4×10=40バイト、intを2つdobuleを1つStringを2つインスタンス変数として持つクラスのオブジェクトなら4×2+8×1+8×2=32バイト、と言う形です。(さらに配列の長さやオブジェクトの情報を管理するための領域が確保されています。)
これを踏まえて
が何をしているのかを考えます。最初にa
の為に確保されるメモリはどれぐらいかというと、参照値を入れる分だけであるため8バイトです。では、"hello"はどこに確保されるのでしょうか?この"hello"は文字列リテラルという物で、実際の所はStirngをnewする処理をしています。上のコードは
Java
1char[] data = {'h', 'e', 'l', 'l', 'o'};
2String a = new String(data)
と同じです(参考)。newでオブジェクトのインスタンス変数分のメモリが確保されるという話でした。では、Stringのオブジェクトのインスタンス右辺数に"hello"の分のメモリが確保されるのかというと、実は違うのです。銅なのかというと、実際のコードを見る方がわかりやすいでしょう。
String.javaのコードはString
の実際の実装です(OpenJDK 17の物ですが)。"hello"のデータがあるのはprivate final byte[] value;
と宣言されたインスタンス変数です。このインスタンス変数は参照型であるため、String
としては8バイト分しかメモリは用意されません。でも問題ありません。このvalue
は、"hello"のデータが保存された配列を示す参照にすぎないから、8バイトで十分なのです。実際の"hello"のデータはここでStringUTF16.compress()
またはStringUTF16.toBytes()
で作成されたものへvalue
が指し示すようになります(参照型への代入は指し示す物の変更です)。さらに、StringUTF16.javaをみればわかるように、Latin1に収まるなら文字数の同じバイト数のbyte配列を作成してLatin1として文字が入れ(StringUTF16.compress()
)、収まらないなら文字数の2倍のバイト数(コードではlen << 1
)のbyte配列を作成してUTF16として文字を入れ(StringUTF16.toBytes()
)、それがvalue
に代入される(value
さ指し示す先がそれらのbyte配列になる)と言うことです。"hello"はLatin1に収まるため、5バイトのbyte配列が作成されることになります。
つまり、"hello"という文字が実際に持っているデータというのは、変数a
(8バイト)が指し示す先のStringオブジェクトが持つインスタンス変数value
(8バイト)が指し示す先のサイズが5のbyte配列にあるということです。
そこから
は何をしているのかです。"add text"
というのは、これまたnew String(...)
としているだけですので、"add text"の入ったbyte配列を指し示すインスタンス変数value
を持つ全く新しいStringオブジェクトになります。これが、a
に代入されるのですが、参照型への代入は、参照値を入れ替えることになり、言ってしまえば、参照先の差し替えてです。a
が持もつ値というのは、"hello"のデータを持っているStringオブジェクトを指し示す参照値から、"add text"のデータを持っているStringオブジェクトを指し示す参照値に変わると言うことです。
"hello"のデータを持っているStringオブジェクトはどうなるのかというと、どこからも参照されなくなりますので、しばらくしたらガーベージコレクターによって回収され、確保していたメモリは解放されることになります。
より詳しくはJava言語仕様書やJava VM仕様書を読んでください。ただ、一定上のプログラミングやコンピューターの知識がないと、ちょっと理解するのは難しいかと思います。CやC++のような自分でメモリ管理が否が応でも必要な言語なら、これらを理解していないと簡単なプログラムですら作るのは難しいですが、Javaのようなガベージコレクション前提の言語では、メモリ使用量の改善が必要になるなど特殊な状況にならない限り、メモリがどのように確保されるかどうかは、ほとんどの場合で必要とされない知識だと思います。トップレベルのプログラマーになるなら必要ですが、たぶん、CやC++等を触った後で無いと難しいでしょう。配列のメモリ確保の実際の動作とかを調べるにはそれこそC/C++で書かれたネイティブなコードをみないといけなくなりますから。