Java文字列インタビューの質問と回答

1. 前書き

link:/java-string[_String_ class]は、Javaで最も広く使用されているクラスの1つであり、言語設計者に特別な扱いを促しました。 この特別な動作により、Javaインタビューで最もホットなトピックの1つになります。
このチュートリアルでは、_String_についての最も一般的なインタビューの質問をいくつか取り上げます。

2. _String_基礎

このセクションは、_String_内部構造とメモリに関する質問で構成されています。

Q1. Javaの文字列とは

Javaでは、a _String_は_byte_ values(またはJDK 9より前の_char_値)の配列によって内部的に表されます。
Java 8以前のバージョンでは、_String_はUnicode文字の不変の配列で構成されていました。 ただし、ほとんどの文字は、16ビット_(char_サイズ)の代わりに、8ビット(1 _byte)_で表現する必要があります。
メモリの消費とパフォーマンスを改善するために、Java 9ではlink:/java-9-compact-string[compact Strings]を導入しました。 これは、_String_に1バイト文字のみが含まれる場合、_Latin-1_エンコーディングを使用して表されることを意味します。 _String_に少なくとも1つのマルチバイト文字が含まれている場合、UTF-16エンコードを使用して、文字ごとに2バイトとして表されます。
CおよびC ++では、_String_も文字の配列ですが、Javaでは、独自のAPIを持つ別個のオブジェクトです。

*Q2. Java *で文字列オブジェクトを作成するにはどうすればよいですか?

https://docs.oracle.com/javase/8/docs/api/java/lang/String.html[_java.lang.String_] defines https://docs.oracle.com/javase/8/docs/api /java/lang/String.html#constructor.summary[_String_を作成する13の異なる方法]。 ただし、一般的には次の2つがあります。
  • _String_リテラルを介して:
    +

String s = "abc";
  • _new_キーワードを使用:
    +

String s = new String("abc");
Javaのすべての文字列リテラルは、_String_クラスのインスタンスです。

Q3. _String_はプリミティブ型ですか、派生型ですか?

_A String_は、状態と動作を持っているため、派生型です。 たとえば、_substring()_、_ indexOf()_、および___ equals()、__などのプリミティブにはないメソッドがあります。
しかし、私たちは皆非常に頻繁に使用するため、プリミティブのように感じる特別な特徴があります。
  • プリミティブのように文字列は呼び出しスタックに保存されませんが、
    これらは、 https://www.baeldung.com/java-string-pool [string pool] と呼ばれる特別なメモリ領域に保存されます**

  • プリミティブと同様に、文字列に対して + operatorを使用できます

  • また、プリミティブのように、のインスタンスを作成できます
    String withoutnew キーワードなし

Q4. 文字列が不変であることの利点は何ですか?

James Goslingのhttps://www.artima.com/intv/gosling313.html [インタビュー]によると、文字列はパフォーマンスとセキュリティを改善するために不変です。
そして実際には、https://www.baeldung.com/java-string-immutable [不変の文字列を持つことの利点]がいくつかあります。
  • 文字列プールは、作成された文字列が
    変更されない

  • コードは、文字列を別のメソッドに安全に渡すことができます*。
    その方法で変更することはできません

  • 不変*このクラスを自動的にスレッドセーフにします*

  • このクラスはスレッドセーフなので、同期する必要はありません*
    共通データ*。これにより、パフォーマンスが向上します。

  • 変更されないことが保証されているため、ハッシュコードは簡単に変更できます
    キャッシュ済み

Q5. 文字列はメモリにどのように保存されますか?

JVM仕様によると、_String_リテラルはhttps://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.5 [実行時定数プール]に格納されます。 JVMのhttps://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5.4 [メソッド領域]から割り当てられます。
*メソッド領域は論理的にはヒープメモリの一部ですが、仕様は場所、メモリサイズ、またはガベージコレクションポリシーを規定していません。*実装固有の場合があります。
クラスまたはインターフェイスのこのランタイム定数プールは、クラスまたはインターフェイスがJVMによって作成されるときに構築されます。

Q6. インターンされた文字列はJavaのガベージコレクションの対象ですか?

はい、プログラムからの参照がない場合、文字列プール内のすべての__String__sはガベージコレクションの対象となります。

Q7. 文字列定数プールとは何ですか?

link:/java-string-pool [文字列プール]は、_String_定数プールまたは_String_インターンプールとも呼ばれ、JVMが_String_インスタンスを格納する特別なメモリ領域です。
*割り当てられる文字列の頻度と数を減らすことにより、アプリケーションのパフォーマンスを最適化します*:
  • JVMは、特定の_String_の1つのコピーのみをプールに保存します

  • 新しい_String_を作成するとき、JVMはプール内で
    同じ値を持つ_String_

  • 見つかった場合、JVMはその_String_への参照を返します
    追加のメモリを割り当てる

  • 見つからない場合、JVMはそれをプールに追加(インターン)し、
    参照を返します

Q8. 文字列はスレッドセーフですか? どうやって?

文字列は不変であるため、実際に完全にスレッドセーフです。 不変であるクラスは、その不変性により、インスタンスが複数のスレッド間で変更されないことが保証されるため、スレッドセーフの対象となります。
*たとえば、スレッドが文字列の値を変更すると、既存の値を変更する代わりに、新しい_String_が作成されます。*

Q9. ロケールを指定することが重要なストリング操作はどれですか?

_Locale_クラスを使用すると、文化的なロケールを区別したり、コンテンツを適切にフォーマットしたりできます。
__String __classに関しては、strings_format_で文字列をレンダリングするとき、または下位または上位の文字列をレンダリングするときに必要です。
実際、これを忘れると、移植性、セキュリティ、使いやすさの問題が発生する可能性があります。

* Q10。 文字列の基になる文字エンコードとは何ですか?*

https://docs.oracle.com/javase/7/docs/api/java/lang/String.html[Ac__String'__s Javadocsによると] Java 8以前のバージョンでは、文字列はUTF-16に保存されます内部的にフォーマットします。
_char_データ型と_java.lang.Character_オブジェクトhttps://docs.oracle.com/javase/6/docs/api/java/lang/Character.html#unicode [元のUnicode仕様にも基づいています]固定幅の16ビットエンティティとして定義された文字。
JDK 9以降、1バイト文字のみを含む_Strings_は_Latin-1_エンコーディングを使用し、少なくとも1つのマルチバイト文字を含む_Strings_はUTF-16エンコーディングを使用します。

*3. String API *

このセクションでは、_String_ APIに関連するいくつかの質問について説明します。

* Q11。 Javaで2つの文字列を比較するにはどうすればよいですか? str1 == str2とstr1.Equals(str2)の違いは何ですか?*

2つの異なる方法でlink:/java-compare-strings [文字列を比較]できます。等号演算子(==)を使用する方法と_equals()_メソッドを使用する方法です。
どちらも互いにかなり異なります。
  • 演算子(str1 == str2参照の等価性をチェックします

  • メソッド(str1.equals(str2)字句の等価性をチェックします

    ただし、2つの文字列が字句的に等しい場合、__str1.intern()== str2.intern()__も_true_です。
    *通常、2つの_Strings_のコンテンツを比較するには、常に_String.equals_を使用する必要があります。*

* Q12。 Javaで文字列を分割するにはどうすればよいですか?*

_String_クラス自体は、正規表現の区切り文字を受け入れるlink:/string/split[the __String#split __method]を提供します。 _String [] _配列を返します。
String[] parts = "john,peter,mary".split(",");
assertEquals(new String[] { "john", "peter", "mary" }, parts);
  • _split_について注意すべき点の1つは、空の文字列を分割するときに、空でない配列を取得できることです。

assertEquals(new String[] { "" }, "".split(","));
もちろん、__ split __は、https://www.baeldung.com/java-split-string [Java _String_を分割]する多くの方法の1つにすぎません。

* Q13。 Stringjoinerとは何ですか?*

__https://www.baeldung.com/java-string-joiner [StringJoiner] __は、*色のリストを取り、それらをコンマ区切りの文字列として返す*ように、個別の文字列を1つに結合するためにJava 8で導入されたクラスです。 プレフィックスとサフィックスに加えて、区切り文字を指定できます。
StringJoiner joiner = new StringJoiner(",", "[", "]");
joiner.add("Red")
  .add("Green")
  .add("Blue");

assertEquals("[Red,Green,Blue]", joiner.toString());

* Q14。 String、Stringbuffer、Stringbuilderの違いは?*

文字列は不変です。 つまり、*その値を変更または変更しようとすると、Javaはまったく新しい_ * String *を作成します。 _
たとえば、文字列__str1 ___を作成後に追加すると、次のようになります。
String str1 = "abc";
str1 = str1 + "def";
次に、JVMは_str1_を変更する代わりに、まったく新しい_String_を作成します。
ただし、ほとんどの単純なケースでは、コンパイラは_StringBuilder_を内部的に使用し、上記のコードを最適化します。
しかし、ループのようなより複雑なコードの場合、*まったく新しい_String_が作成され、パフォーマンスが低下します*。 これは、_StringBuilder_と_StringBuffer_が便利な場所です。
link:/java-string-builder-string-buffer[_StringBuilder_およびJavaの_StringBuffer_]は、可変の文字シーケンスを保持するオブジェクトを作成します。* _StringBuffer_は同期されているため、スレッドセーフです。 。*
通常、__StringBuffer __の余分な同期は不要なので、_StringBuilder._を選択することでパフォーマンスを向上させることができます。

* Q15。 文字列ではなくChar []配列にパスワードを保存する方が安全なのはなぜですか?*

文字列は不変であるため、変更できません。 *この動作により、_Strings_が機密情報を保存するのに不適切になるように、その内容を上書き、変更、またはゼロにすることができなくなります。*
文字列のコンテンツを削除するには、ガベージコレクターに依存する必要があります。 さらに、Javaバージョン6以前では、文字列はPermGenに格納されていました。つまり、_String_が作成されると、ガベージコレクションは行われませんでした。
  • _char [] _配列を使用することで、その情報を完全に制御できます。* ガベージコレクターに依存することなく、それを変更または完全に消去できます。

    _String []を介して_char [] _を使用しても、情報は完全には保護されません。悪意のあるユーザーが機密情報にアクセスする機会を減らすための単なる対策です。

* Q16。 String’s Intern()メソッドは何をしますか?*

*メソッドlink:/string/intern[_intern()_]は、ヒープに_String_オブジェクトの正確なコピーを作成し、JVMが管理するmaintains__String ___constantプールに保存します。*
Javaは、文字列リテラルを使用して作成されたすべての文字列を自動的にインターンしますが、たとえば、_String str = new String(“ abc”)_などのnew演算子を使用して_String_を作成すると、Javaは他のオブジェクトと同様にヒープに追加します。
_intern()_メソッドを呼び出して、JVMに文字列プールがまだ存在しない場合は追加し、そのインターンされた文字列の参照を返すように指示することができます。
String s1 = "Baeldung";
String s2 = new String("Baeldung");
String s3 = new String("Baeldung").intern();

assertThat(s1 == s2).isFalse();
assertThat(s1 == s3).isTrue();

* Q17。 Javaで文字列を整数に、整数を文字列に変換するにはどうすればよいですか?*

link:/java-convert-string-to-int-or-integer[_String_を_Integer_に変換する]への最も簡単なアプローチは、_Integer#parseInt_を使用することです。
int num = Integer.parseInt("22");
逆を行うには、_Integer#toString_を使用できます。
String s = Integer.toString(num);

* Q18。 String.Format()とは何で、どのように使用できますか?*

link:/string/format[_String#format_]指定されたフォーマット文字列と引数を使用してフォーマットされた文字列を返します。
String title = "Baeldung";
String formatted = String.format("Title is %s", title);
assertEquals("Title is Baeldung", formatted);
また、ユーザーの__Localeを指定することを覚えておく必要があります。__Locale、default___オペレーティングシステムのデフォルトを受け入れるだけでいい場合を除きます:
Locale usersLocale = Locale.ITALY;
assertEquals("1.024",
  String.format(usersLocale, "There are %,d shirts to choose from. Good luck.", 1024))

* Q19。 文字列を大文字と小文字に変換するにはどうすればよいですか?*

_String_は、https://www.baeldung.com/string/to-upper-case [_String#toUpperCase_]を暗黙的に提供して、大文字を大文字に変更します。
ただし、Javadocは、正確性を確保するためにユーザーの_Locale_を指定する必要があることを思い出させます。
String s = "Welcome to Baeldung!";
assertEquals("WELCOME TO BAELDUNG!", s.toUpperCase(Locale.US));
同様に、小文字に変換するには、https://www.baeldung.com/string/to-lower-case [_String#toLowerCase_]を使用します。
String s = "Welcome to Baeldung!";
assertEquals("welcome to baeldung!", s.toLowerCase(Locale.UK));

* Q20。 文字列から文字配列を取得するにはどうすればよいですか?*

_String_は、JDK9より前の内部_char_配列のコピーを返す(およびJDK9 +で_String_を新しい_char_配列に変換する)_toCharArray_を提供します。
char[] hello = "hello".toCharArray();
assertArrayEquals(new String[] { 'h', 'e', 'l', 'l', 'o' }, hello);

* Q21。 Java文字列をバイト配列に変換する方法*

デフォルトでは、メソッドlink:/string/get-bytes[_String#getBytes()_]は、プラットフォームのデフォルトの文字セットを使用して文字列をバイト配列にエンコードします。
APIでは、文字セットを指定する必要はありませんが、https://www.baeldung.com/java-char-encoding [セキュリティと移植性を確保するために]:
byte[] byteArray2 = "efgh".getBytes(StandardCharsets.US_ASCII);
byte[] byteArray3 = "ijkl".getBytes("UTF-8");

4. _String_ベースのアルゴリズム

このセクションでは、__ String__sに関連するプログラミングの質問について説明します。

* Q22。 2つの文字列がJavaのアナグラムであるかどうかを確認するにはどうすればよいですか?*

アナグラムは、たとえば「car」や「arc」など、別の特定の単語の文字を並べ替えることによって形成される単語です。
まず、両方の_Strings_の長さが等しいかどうかを確認します。
次に、https://www.baeldung.com/java-sort-string-alphabetically [それらを_char [] _配列に変換し、並べ替えてから、等しいかどうかを確認します]。

* Q23。 文字列内の特定の文字の出現回数をカウントするにはどうすればよいですか?*

Java 8は、次のような集約タスクを実際に簡素化します。
long count = "hello".chars().filter(ch -> (char)ch == 'l').count();
assertEquals(2, count);
また、ループ、再帰、正規表現、外部ライブラリなど、https://www.baeldung.com/java-count-chars [lを数える]には他にも優れた方法がいくつかあります。

* Q24。 Javaで文字列を反転するにはどうすればよいですか?*

これを行うには多くの方法がありますが、最も簡単なアプローチは、_StringBuilder_(または_StringBuffer_)の_reverse_メソッドを使用することです。
String reversed = new StringBuilder("baeldung").reverse().toString();
assertEquals("gnudleab", reversed);

* Q25。 文字列がパリンドロームかどうかを確認するにはどうすればよいですか?*

link:/java-palindrome-substrings[palindrome]は、「マダム」、「レーダー」、「レベル」など、前方と後方を同じに読み取る文字のシーケンスです。
link:/java-palindrome [文字列が回文であるかどうかを確認する]ために、指定された文字列を一度に1文字ずつ、単一のループで前後に繰り返し始めることができます。 ループは最初の不一致で終了します。

5. 結論

この記事では、最も一般的な_String_インタビューの質問をいくつか取り上げました。
ここで使用されるすべてのコードサンプルは、https://github.com/eugenp/tutorials/tree/master/java-strings-2 [GitHubで入手可能]です。