こんにちは、みなさん!プログラミングには落とし穴がたくさんあります。そして、つまずいたり、つま先をぶつけたりする原因にならない話題はほとんどありません。これは特に初心者に当てはまります。足の指を救う唯一の方法は学ぶことです。特に、最も基本的なトピックを深く掘り下げる必要があります。今日は、Java 開発者向けの面接で最もよく聞かれる質問のレビューを続けます。これらの面接の質問は、基本的なトピックをカバーするのに優れています。このリストには、一般的な問題に別の方法でアプローチできる、あまり標準的ではない質問も含まれていることに注意してください。

62. 文字列プールとは何ですか?なぜ必要ですか?
Java プログラムで使用できるメモリの一部はヒープ (これについては後で説明します) と呼ばれ、ヒープの一部は String poolと呼ばれます。文字列値を保存するためのものです。つまり、たとえば次のように二重引用符を使用して文字列を作成する場合:
String str = "Hello world";
JVM は、文字列プールに指定された値がすでにあるかどうかを確認します。存在する場合、str変数にはプール内のその値への参照が割り当てられます。そうでない場合は、新しい値がプール内に作成され、その値への参照がstr変数に割り当てられます。例を考えてみましょう。
String firstStr = "Hello world";
String secondStr = "Hello world";
System.out.println(firstStr == secondStr);
trueと画面に表示されます。== は参照を比較し、これら 2 つの変数は文字列プール内の同じ値を指すことに注意してください。これは、メモリ内に多数の同一のStringオブジェクトが生成されることを回避するのに役立ちます。これができるのは、思い出していただけると思いますが、Stringは不変クラスであるため、同じ値への参照が複数あっても問題はありません。これで、1 か所の値を変更すると他のいくつかの参照が変更されるという状況は発生しなくなりました。それでも、 new を使用して文字列を作成すると、次のようになります。
String str = new String("Hello world");
次に、別のオブジェクトがメモリ内に作成され、指定された文字列値が保存されます (その値がすでに文字列プールにあるかどうかは関係ありません)。この主張を確認するには、次の点を考慮してください。
String firstStr = new String("Hello world");
String secondStr = "Hello world";
String thirdStr = new String("Hello world");
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
false を示す 2 行が表示されます。これは、3 つの別々の文字列があることを意味します。基本的に、二重引用符を使用して文字列を作成する必要があるのはこのためです。ただし、 newキーワードを使用してオブジェクトを作成する場合でも、文字列プールに値を追加 (または参照を取得) することは可能です。これを行うには、String クラスのintern()メソッドを使用します。このメソッドにより、文字列プールに値を作成するか、すでに存在する場合はその値への参照を取得することができます。以下に例を示します。
String firstStr = new String("Hello world").intern();
String secondStr = "Hello world";
String thirdStr = new String("Hello world").intern();
System.out.println(firstStr == secondStr);
System.out.println(firstStr == thirdStr);
System.out.println(secondStr == thirdStr);
このコードはコンソールに true を3 回出力します。これは、3 つの変数すべてがメモリ内の同じ文字列を参照していることを示しています。
63. 文字列プールではどのような GoF デザイン パターンが使用されていますか?
ストリング プールでは、GoFデザイン パターンはフライウェイトパターンです。ここで別のデザイン パターンに気づいた場合は、コメントで共有してください。ここではフライウェイトのデザインパターンについてお話します。これは、プログラム内のさまざまな場所で一意のインスタンスとしてそれ自体を表すオブジェクトが、実際には一意ではない構造設計パターンです。フライウェイトは、各オブジェクトに同一のデータを保存するのではなく、オブジェクトの共有状態を保存することでメモリを節約します。要点を理解するために、この基本的な例を考えてみましょう。従業員インターフェースがあるとします。
public interface Employee {
void work();
}
そして、 Lawyerクラス などの実装がいくつかあります。
public class Lawyer implements Employee {
public Lawyer() {
System.out.println("A lawyer was hired.");
}
@Override
public void work() {
System.out.println("Settling legal issues...");
}
}
そして会計士クラス:
public class Accountant implements Employee {
public Accountant() {
System.out.println("An accountant was hired.");
}
@Override
public void work() {
System.out.println("Keeping accounting records...");
}
}
メソッドは完全に任意です。この例では、メソッドが実行されていることを確認するだけで済みます。コンストラクターについても同様です。新しいオブジェクトがいつ作成されるかは、コンソール出力に表示されます。また、要求された従業員を復帰させることを任務とする人事部門もあります。その従業員がまだスタッフにいない場合、その従業員は雇用され、人事部門は新しい従業員を復帰させます。
public class HumanResourcesDepartment {
private Map<String, Employee> currentEmployees = new HashMap<>();
public Employee getEmployee(String type) throws Exception {
Employee result;
if (currentEmployees.containsKey(type)) {
result = currentEmployees.get(type);
} else {
switch (type) {
case "Accountant":
result = new Accountant();
currentEmployees.put(type, result);
break;
case "Lawyer":
result = new Lawyer();
currentEmployees.put(type, result);
break;
default:
throw new Exception("This employee is not on the staff!");
}
}
return result;
}
}
したがって、ロジックは単純です。目的のオブジェクトが存在する場合は、それを返します。そうでない場合は、それを作成し、ストレージ (キャッシュのようなもの) に入れて返します。それでは、すべてがどのように機能するかを見てみましょう。
public static void main(String[] args) throws Exception {
HumanResourcesDepartment humanResourcesDepartment = new HumanResourcesDepartment();
Employee empl1 = humanResourcesDepartment.getEmployee("Lawyer");
empl1.work();
Employee empl2 = humanResourcesDepartment.getEmployee("Accountant");
empl2.work();
Employee empl3 = humanResourcesDepartment.getEmployee("Lawyer");
empl1.work();
Employee empl4 = humanResourcesDepartment.getEmployee("Accountant");
empl2.work();
Employee empl5 = humanResourcesDepartment.getEmployee("Lawyer");
empl1.work();
Employee empl6 = humanResourcesDepartment.getEmployee("Accountant");
empl2.work();
Employee empl7 = humanResourcesDepartment.getEmployee("Lawyer");
empl1.work();
Employee empl8 = humanResourcesDepartment.getEmployee("Accountant");
empl2.work();
Employee empl9 = humanResourcesDepartment.getEmployee("Lawyer");
empl1.work();
Employee empl10 = humanResourcesDepartment.getEmployee("Accountant");
empl2.work();
}
コンソールに表示される内容は次のとおりです。
弁護士が雇われた。法的問題の解決... 会計士が雇われました。会計記録の保管… 法的問題の解決… 会計記録の保管… 法的問題の解決… 会計記録の保管… 法的問題の解決… 会計記録の保管… 法的問題の解決… 会計の保管記録…
ご覧のとおり、2 つのオブジェクトだけを作成し、それらを何度も再利用しました。この例は非常に単純ですが、この設計パターンがどのようにリソースを節約できるかを示しています。お気づきかと思いますが、このパターンのロジックは、保険プールのロジックと非常によく似ています。 
64. 文字列を複数の部分に分割するにはどうすればよいですか? 関連するコードの例を示します
明らかに、この質問は分割方法に関するものです。Stringクラスには、このメソッドの 2 つのバリエーションがあります。
String split(String regex);
そして
String split(String regex);
regexパラメータは区切り文字です。文字列を文字列の配列に分割するために使用される正規表現です。次に例を示します 。
String str = "Hello, world it's Amigo!";
String[] arr = str.split("\\s");
for (String s : arr) {
System.out.println(s);
}
コンソールには以下が表示されます。
こんにちは、世界よ、アミーゴです!
したがって、文字列は区切り文字としてスペースを使用して文字列の配列に分割されました (正規表現"\\s"の代わりに、通常の文字列表現" "を使用することもできました)。2 番目のオーバーロードされたバリアントには、追加の制限パラメータがあります。 limit は、結果として得られる配列の最大許容サイズです。つまり、文字列が最大許容数の部分文字列に分割されると、分割は停止し、最後の要素には分割されなかった文字列の「残り物」が含まれることになります。例:
String str = "Hello, world it's Amigo!";
String[] arr = str.split(" ", 2);
for (String s : arr) {
System.out.println(s);
}
コンソール出力:
こんにちは、世界よ、アミーゴです!
見てわかるように、 limit = 2 でなければ、配列の最後の要素は 3 つの部分文字列に分割される可能性があります。
65. パスワードを保存する場合、文字列よりも文字配列の方が優れているのはなぜですか?
パスワードを保存するときに文字列よりも配列を優先する理由はいくつかあります。1. 文字列プールと文字列の不変性。
配列 ( char[] ) を使用する場合、作業が終了したらデータを明示的に消去できます。また、配列を必要なだけ上書きして、ガベージ コレクションの前であってもシステムからパスワードを削除することもできます (いくつかのセルを無効な値に変更するだけで十分です)。対照的に、Stringは不変クラスです。これは、 Stringオブジェクトの値を変更したい場合、新しい値を取得しますが、古い値は文字列プールに残ることを意味します。パスワードを含む文字列を削除したい場合は、ガベージ コレクターがその値を文字列プールから削除する必要があるため、複雑な作業に直面しますが、その文字列は長期間そこに残る可能性があります。つまり、データを安全に保存するという点では、Stringはchar配列より劣ります。2.文字列値をコンソール (またはログ) に出力すると、次の結果が得られます。
String password = "password";
System.out.println("Password - " + password);
コンソール出力:
パスワード - パスワード
そして、配列をコンソールに出力すると、次のようになります。
char[] arr = new char[]{'p','a','s','s','w','o','r','d'};
System.out.println("Password - " + arr);
コンソールには理解できない意味不明な内容が表示されます。
パスワード - [C@7f31245a]
実際、それは意味不明ではありません。表示されている内容を理解する方法は次のとおりです。 [Cはクラス名 -文字配列、 @は区切り文字、 7f31245aは 16 進数のハッシュ コードです。
3. 公式の Java Cryptography Architecture (JCA) リファレンス ガイドでは、パスワードをStringではなくchar[]に保存することが明示的に記載されています。
「パスワードを収集して、 java.lang.String型のオブジェクトに保存するのは論理的だと思われます。ただし、注意点があります。String型のオブジェクトは不変です。つまり、変更 (上書き) できるメソッドが定義されていません」または、使用後にStringの内容をゼロにします。この機能により、 Stringオブジェクトはユーザー パスワードなどのセキュリティ上の機密情報を保存するのに適さなくなります。代わりに、常にセキュリティ上の機密情報を収集して char 配列に保存する必要があります。」
列挙型
66. JavaのEnumについて簡単に説明してください
Enumは列挙の略で、共通の型によって結合された文字列定数のセットです。enumキーワードを使用して宣言します。これは、いくつかの学校キャンパスで enum : 許可されたロールを使用した例です。
public enum Role {
STUDENT,
TEACHER,
DIRECTOR,
SECURITY_GUARD
}
大文字で書かれた単語は列挙定数です。これらはnew演算子を使用せずに、簡略化された方法で宣言されます。列挙型を使用すると、名前のエラーや混乱を避けることができるため、作業がはるかに簡単になります (リストは有効な値のみを定義するため)。私にとって、スイッチ
構造では非常に便利です。
67. Enum はインターフェイスを実装できますか (implements キーワードを使用)?
はい。結局のところ、列挙型は単なる受動的なセット (学校のキャンパスでの役割など) 以上のものを表す必要があります。Java では、より複雑なオブジェクトを表すことができるため、追加の機能を追加する必要がある場合があります。これにより、実装されたインターフェイス タイプが必要な場所で enum値を置き換えることにより、ポリモーフィズムを利用することもできます。68. Enum はクラスを拡張できますか (extends キーワードを使用)?
いいえ、できません。列挙型はデフォルトのEnum<T>クラス ( Tは列挙型)のサブクラスであるためです。これは、Java 言語のすべての列挙型に共通の基本クラスにすぎません。enumからクラスへの変換は、コンパイル時に Java コンパイラーによって行われます。拡張子はコード内で明示的に示されていませんが、常に暗黙的に示されています。69. オブジェクトのインスタンスなしで Enum を作成することは可能ですか?
この質問は少し複雑で、完全に理解できているかどうかわかりません。2 つの解釈があります: 1.値のない列挙型を使用できますか? はい、もちろんですが、それは空のクラスのようなものになります - 無意味です。
public enum Role {
}
そして、次のように呼び出すと、
var s = Role.values();
System.out.println(s);
コンソールに次の情報が表示されます。
[Lflyweight.Role;@9f70c54
( Role値 の空の配列) 2. new演算子を使用せずに列挙型を作成することは可能ですか? はい、もちろん。上で述べたように、列挙値は静的な値であるため、列挙値にはnew演算子を使用しません。
70. Enum の toString() メソッドをオーバーライドできますか?
はい、もちろんtoString()メソッドをオーバーライドして、 toStringメソッドが呼び出されたとき(たとえば、コンソールやログに出力するために 列挙型を通常の文字列に変換するとき) に列挙型を表示する方法を定義できます。
public enum Role {
STUDENT,
TEACHER,
DIRECTOR,
SECURITY_GUARD;
@Override
public String toString() {
return "Selected role - " + super.toString();
}
}
今日はここまでです。次のパートまで!
GO TO FULL VERSION