Java コア インタビューのトップ 50 の質問と回答。パート1
コレクション
25. Java のコレクションとは何を意味しますか?
コレクションは、オブジェクトを保存および操作するために設計されたフレームワークです。次の操作を実行するために使用されます。- 検索;
- 並べ替え。
- 操作;
- 追加;
- 削除。
java.util
パッケージに含まれています。
26. Collection フレームワークではどのようなクラスとインターフェイスが利用できますか?
インターフェース:- コレクション;
- リスト;
- セット;
- 地図;
- ソートされたセット;
- 分類された地図;
- 列。
- リスト:
- 配列リスト;
- リンクリスト;
- ベクター (非推奨)。
- セット:
- ハッシュセット;
- リンクされたハッシュセット;
- ツリーセット。
- マップ:
- ハッシュマップ
- ツリーマップ
- ハッシュテーブル (非推奨)
- リンクされたハッシュマップ
- 列
- 優先キュー。
27. コレクション内でソートおよび順序付けとは何を意味しますか?
順序付けられました:
これは、コレクションに保存される項目が、コレクションに追加された値に基づいていることを意味します。このようにして、コレクションの値を特定の順序で反復処理できます。言い換えれば、これは、コレクションの要素が、それに従って配置される独自の特定の順序を持っていることを意味します。わかりやすく説明すると、順序付けられていないコレクションは、その要素をランダムな順序で格納します。たとえば、セット。並べ替え:
これは、コレクション要素のデータに基づいて要素のグループがコレクションに分類されることを意味します。つまり、コレクションが順序付けされているだけでなく、要素の順序もその値に依存します。別の要素値で並べ替えると、この順序が変わる可能性があります。28. List インターフェイスにはどのようなコレクションがありますか? リストとはどのように連携していますか?
シート内の要素の値はインデックスに基づいており、インデックス順に並べられます。要素の繰り返しは許可されます (つまり、同じオブジェクトをコレクションに複数回追加しても問題ありません)。配列リスト:
最も一般的なコレクション。基本的に、これはサイズが動的に拡大する配列です。配列のサイズを管理する仕事はコレクションにあります。ほとんどの場合、これを使用する必要があることを理解することが重要です。特徴:- 高速検索と高速インデックス検索。
- コレクションはインデックス順に並べられますが、並べ替えられません。
- RandomAccess インターフェイスを実装します。
- 徐々にリストの真ん中に追加されます。
public class A {
public static void main(String[] args) {
ArrayList names = new ArrayList<>();
names.add("John");
names.add("John");
names.add("Roman");
names.add("Ivan");
}
}
>> 出力
[John, John, Roman, Ivan]
出力には、これらが反復可能な要素であることが示されています。記録された順に表示されます。他に何を読むべきですか?はい、たくさんの情報があります。JavaRush から離れる必要さえありません。
リンクされたリスト:
これは、各要素が前後の要素へのリンクを持つコレクションです。これらのリンクを使用すると、ある要素から別の要素に移動できます。要素を追加すると、前後の要素へのリンクが単に変更されます。- 要素は相互に接続されています。つまり、二重リンクリストが実装されています。
- 全体的な操作速度は ArrayList よりも著しく遅くなります。
- 配列の途中で多数の挿入や削除を行う場合に最適です。
- Queue および Deque list インターフェイスを実装しているため、それらが機能するメソッドがあります。
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("One");
linkedList.add("Two");
linkedList.add("Three");
29. Map コレクションとその実装について教えてください。
マップはキーと値のコレクションです。一意のキーと、その値に一致する値が存在します。キーの一意性を判断するために メソッドequals()
も使用されます。hashcode()
ハッシュマップ:
- ソートまたは順序付けされていない。
- 順序と並べ替えが重要ではない場合に使用されます。
- null キーをサポートします。
public class CollectionExample {
public static void main(String[] args) {
HashMap positions = new HashMap<>();
positions.put("junior", "Ivan");
positions.put("middle", "Roman");
positions.put("senior", "Vasily");
positions.put("team lead", "Anton");
positions.put("arthitect", "Andrew");
positions.put("senior", "John");
System.out.println(positions);
}
}
// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}
キーは常に一意であるため、先輩は 1 人だけ記録されます。
リンクされたハッシュマップ:
- 挿入順序を維持します。
- HashMap よりも遅い。
- 反復処理は HashMap よりも高速になることが期待されます。
public class CollectionExample {
public static void main(String[] args) {
LinkedHashMap<String, String> positions = new LinkedHashMap<>();
positions.put("junior", "Ivan");
positions.put("middle", "Roman");
positions.put("senior", "Vasily");
positions.put("team lead", "Anton");
positions.put("arthitect", "Andrew");
positions.put("senior", "John");
System.out.println(positions);
}
}
// вывод в консоль
// {junior=Ivan, middle=Roman, senior=John, team lead=Anton, arthitect=Andrew}
ツリーマップ:
キーの自然な順序に従ってエントリをソートし続けるマップ実装。マップの作成時にコンパレータがコンストラクタで提供されている場合は、コンパレータを使用してソートするのがよいでしょう。例:-
コンパレータなし
public class CollectionExample { public static void main(String[] args) { TreeMap<Integer, String> positions = new TreeMap<>(); positions.put(1, "Ivan"); positions.put(3, "Roman"); positions.put(2, "Vasily"); positions.put(10, "Anton"); positions.put(7, "Andrew"); positions.put(1, "John"); System.out.println(positions); } } // вывод в консоль // {1=John, 2=Vasily, 3=Roman, 7=Andrew, 10=Anton}
-
コンパレータ付
public class CollectionExample { public static void main(String[] args) { //используем реализацию Strategy Pattern'a и добавим компаратор: TreeMap<Integer, String> positions = new TreeMap<>(Comparator.reverseOrder()); positions.put(1, "Ivan"); positions.put(3, "Roman"); positions.put(2, "Vasily"); positions.put(10, "Anton"); positions.put(7, "Andrew"); positions.put(1, "John"); System.out.println(positions); } } // вывод в консоль // {10=Anton, 7=Andrew, 3=Roman, 2=Vasily, 1=John}
30. Set コレクションとその実装について教えてください。
セットは独自の要素のセットであり、これがその主な特徴です。つまり、Set では同じ要素を繰り返すことはできません。 ここで、追加されるオブジェクトにメソッドが実装されていることが重要ですequals
。
ハッシュセット:
- ソートまたは順序付けされていません。内部には、値のプレースホルダーを含む HashMap があります。自分で見て ;)
- hashCode を使用してオブジェクトを追加します。
- これは、一意のオブジェクトが必要で、その順序が重要ではない場合に使用する必要があります。
public class CollectionExample {
public static void main(String[] args) {
HashSet<String> positions = new HashSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [senior, middle, team lead, architect, junior]
ここでは、2 回追加された「ジュニア」要素が 1 つのインスタンスにのみ存在していることがわかります。また、追加するときと順序が異なります。
リンクされたハッシュセット:
- HashSet の順序付きバージョン。
- すべての要素の二重リンクリストをサポートします。
- 反復内で順序が必要な場合に使用します。
public class CollectionExample {
public static void main(String[] args) {
LinkedHashSet<String> positions = new LinkedHashSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [senior, middle, team lead, architect, junior]
ツリーセット:
- ソートされた 2 つのコレクションのうちの 1 つ。
- 赤と黒のツリー構造を使用し、要素が昇順であることを保証します。
- 内部的には、値のスタブを含む TreeMap です。そして、TreeSet の要素は TreeMap へのキーです (「;)」も参照してください。
public class CollectionExample {
public static void main(String[] args) {
TreeSet<String> positions = new TreeSet<>();
positions.add("junior");
positions.add("junior");
positions.add("middle");
positions.add("senior");
positions.add("team lead");
positions.add("architect");
System.out.println(positions);
}
}
// вывод в консоль
// [architect, junior, middle, senior, team lead]
例外
31. 例外とは何ですか?
例外とは、実行時に発生する可能性のある問題です。これは、何らかの理由により発生する例外的な状況です。例外継承図は次のようになります (暗記する必要があります ;)): この図は、一般に、すべての例外が例外とエラーの 2 つのグループに分けられることを示しています。エラー- JVM は、アプリケーションが意味をなさなくなるエラーを表示するために使用されます。たとえば、StackOverFlowError は、スタックがいっぱいでプログラムが実行できなくなったことを示します。 例外- コード内でプログラムによって生成される例外。チェックされているものとチェックされていないものを含め、さまざまな例外がありますが、重要なのは、それらが存在し、それらをキャッチでき、アプリケーションが機能し続けることができるということです。さらに、例外は、RuntimeException から継承する例外と、Exception の他の子孫に分類されます。この問題については十分な情報があります。チェック済み例外とチェックなし例外については以下で説明します。32. JVM は例外をどのように処理しますか?
使い方?どこかで例外がスローされるとすぐに、ランタイムは例外オブジェクト (ExcObj として示されます) を作成します。スローされた例外自体と例外が発生した場所など、作業に必要なすべての情報が保存されます。作成ExcObj
してランタイムに送信することは、「例外をスローする」ことに他なりません。 ExcObj
例外がスローされた場所にアクセスするために使用できるメソッドが含まれています。このメソッドのセットは呼び出しスタックと呼ばれます。次に、ランタイム システムは、例外を処理できるメソッドを呼び出しスタック内で探します。対応するハンドラーが見つかった場合、つまり、例外の型がハンドラー内の型と一致した場合は、すべて問題ありません。見つからない場合、ランタイムはすべてをデフォルトの例外ハンドラーに渡し、応答を準備して終了します。視覚的には次のようになります。
/**
* Пример, в котором показываются две опции — когда находится обработчик для исключения и когда нет.
*/
class ThrowerExceptionExample {
public static void main(String[] args) throws IllegalAccessException {
ThrowerExceptionExample example = new ThrowerExceptionExample();
System.out.println(example.populateString());
}
/**
* Здесь происходит перехват одного из возможных исключений — {@link IOException}.
* А вот второй будет пробрасываться дальше вверх по вызову.
*/
private String populateString() throws IllegalAccessException {
try {
return randomThrower();
} catch (IOException e) {
return "Caught IOException";
}
}
/**
* Здесь две опции: or бросается {@link IOException} or {@link IllegalAccessException}.
* Выбирается случайным образом.
*/
private String randomThrower() throws IOException, IllegalAccessException {
if (new Random().nextBoolean()) {
throw new IOException();
} else {
throw new IllegalAccessException();
}
}
}
私たちの場合、CallStack は概略的には次のようになります。
randomThrower() => populateString() => main(String[] args)
2 つのオプションがあります。どちらかの例外がランダムにスローされます。IOException の場合はすべて問題ありません。IOException が生成された場合、作業の結果は次のようになります"Caught IOException"
。ただし、ハンドラーがない 2 番目の例外が発生した場合、プログラムは次の出力で停止します。
Exception in thread "main" java.lang.IllegalAccessException
at ThrowerExceptionExample.randomThrower(CollectionExample.java:38)
at ThrowerExceptionExample.populateString(CollectionExample.java:24)
at ThrowerExceptionExample.main(CollectionExample.java:15)
33. プログラマは例外をどのように処理しますか?
上記の質問では、例外を処理するために特定のキーワードがすでに使用されていますが、ここでそれらについてさらに詳しく説明する必要があります。キーワードは何ですか?- 試す
- キャッチ
- 投げる
- 投げる
- ついに
try-catch-finally
は、例外を正しくキャッチして処理できるようにする構造です。try
- 時間は 1 回だけです。そこでロジックが発生します。catch
— 何らかのタイプの例外を受け取るブロック。例外は多数存在する可能性があります。たとえば、try ブロックは、互いに関係のないいくつかの例外をスローします。finally
- 「ついに」このブロック。これは、try、catch で何が行われたかに関係なく、どのような場合でも実行されるブロックです。
try {
// сюда передают тот code, который может вызвать исключение.
} catch (IOException e) {
// первый catch блок, который принимает IOException и все его подтипы(потомки).
// Например, нет file при чтении, выпадает FileNotFoundException, и мы уже соответствующе
// обрабатываем это.
} catch (IllegalAccessException e) {
// если нужно, можно добавить больше одного catch блока, в этом нет проблем.
} catch (OneException | TwoException e) {
// можно даже объединять несколько в один блок
} catch (Throwable t) {
// а можно сказать, что мы принимаем ВСЁ))))
} finally {
// этот блок может быть, а может и не быть.
// и он точно выполнится.
}
例の説明を注意深く読めば、すべてが明確になります)
34. Java でのスローとスロー
投げる
throw
新しい例外を明示的に作成する必要がある場合に使用されます。カスタム例外を作成してスローするために使用されます。たとえば、検証に関連する例外などです。通常、検証のために、 から継承しますRuntimeException
。例:
// пример пробрасывания исключения
throw new RuntimeException("because I can :D");
この構造は を継承するものによってのみ使用できることが重要ですThrowable
。つまり、次のように言うことはできません。
throw new String("How тебе такое, Илон Маск?");
次に、スレッドの作業が終了し、スレッドを処理できるハンドラーの検索が始まります。見つからない場合は、それを呼び出したメソッドに進み、対応するハンドラーが見つかるか、アプリケーションの実行が終了するまで、検索は呼び出しの行を遡っていきます。見てみよう:
// Пример, который демонстрирует работу throw
class ThrowExample {
void willThrow() throws IOException {
throw new IOException("Because I Can!");
}
void doSomething() {
System.out.println("Doing something");
try {
willThrow();
} catch (IOException e) {
System.out.println("IOException was successfully handled.");
}
}
public static void main(String args[]) {
ThrowExample throwExample = new ThrowExample();
throwExample.doSomething();
}
}
プログラムを実行すると、次の結果が得られます。
Doing something
IOException was successfully handled.
投げる
throws
— メソッドが 1 つ以上の例外をスローできるメカニズム。これらはカンマで区切って追加されます。それがいかに簡単でシンプルであるかを見てみましょう:
private Object willThrow() throws RuntimeException, IOException, FileNotFoundException
さらに、チェックされた例外とチェックされていない例外の両方が存在する可能性があることに注意することが重要です。もちろん、チェックされていない例外を に追加することはできませんthrows
が、マナーとしてはそうではありません。これらがチェック可能である場合は、それらを生成するメソッドを使用して、何らかの方法で処理する必要があります。次の 2 つのオプションがあります。
try-catch
適切かつ上記の継承例外を使用して記述します。throws
他の誰かがすでにこの問題を抱えているように、まったく同じ方法でそれを使用してください:D
35. Java のチェック済み例外とチェックなし例外
Java には、チェック済み例外とチェックなし例外の 2 種類があります。チェックされた例外:
これらはコンパイル時にチェックされる例外です。メソッド内の一部のコードが例外中にチェック例外をスローする場合、メソッドは を使用してそれを処理するtry-catch
か、さらに転送する必要があります。たとえば、パス "/users/romankh3/image.png" から画像を読み取り、それを更新します。何らかの方法で(私たちにとってこれは重要ではありません)、それを保存します。
class CheckedImageExample {
public static void main(String[] args) {
File imageFile = new File("/users/romankh3/image.png");
BufferedImage image = ImageIO.read(imageFile);
updateAndSaveImage(image, imageFile);
}
private static void updateAndSaveImage(BufferedImage image, File imageFile) {
ImageIO.write(image, "png", imageFile);
}
}
静的メソッドがIOExceptionImageIO.read()
をスローする ため、そのようなコードはコンパイルされません。IOException はチェックされ、それに応じて処理される必要があります。ImageIO.write()
ここには、すでに上で説明した 2 つのオプションがあります。 を使用するかtry-catch
、さらに進むかのいずれかです。より良く同化するために、私たちはあれやこれやを行います。つまり、updateAndSave
メソッド内で転送してから、メイン メソッドで使用しますtry-catch
。
class CheckedImageExample {
public static void main(String[] args) {
File imageFile = new File("/users/romankh3/image.png");
try {
BufferedImage image = ImageIO.read(imageFile);
updateAndSaveImage(image, imageFile);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void updateAndSaveImage(BufferedImage image, File imageFile) throws IOException {
ImageIO.write(image, "png", imageFile);
}
}
未チェックの例外:
これらは、コンパイル段階ではチェックされない例外です。つまり、メソッドは RuntimeException を生成する可能性がありますが、コンパイラーはそれを何らかの方法で処理するようにユーザーに通知しません。以下に示すように、RuntimeException と Error から継承したものはすべてチェックされていません。 次の Java プログラムについて考えてみましょう。コードは正常にコンパイルされますが、実行時に例外がスローされますArrayIndexOutOfBoundsException
。ArrayIndexOutOfBoundsException
これは未チェック例外であるため、コンパイラはコンパイルを許可します。配列に関する一般的な状況としては、次のようなものが考えられます。
class CheckedImageExample {
public static void main(String[] args) {
int[] array = new int[3];
array[5] = 12;
}
}
結果は次のようになります。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at main(CheckedImageExample.java:12)
ところで、Java では誰も短い名前を付けないことにもう気づきましたか? 大きければ大きいほどいい。Spring Framework はこの点で非常に成功しました。BeanFactoryPostProcessor クラスを取得するだけです) ))
36. Try-with-リソースとは何ですか?
これは、すべてのリソースを正しく閉じる必要があるメカニズムです。どういうわけか明確ではありませんよね?) まず第一に、リソースとは何ですか... リソースはオブジェクトであり、操作した後はそれを閉じる必要があります。つまり、 を呼び出す必要がありますclose()
。リソースは、インターフェイスを実装するすべてのオブジェクトでありAutoClosable
、さらにインターフェイスを実装するオブジェクトですCloseable
。InputStream
すべてはリソースでありOutpuStream
、正しく適切にリリースされる必要があることを理解することが重要です。try-with-resource
まさにこれが、この構造を使用する必要がある理由です。以下にその様子を示します。
private void unzipFile(File zipFile) throws IOException {
try(ZipInputStream zipOutputStream = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry zipEntry = zipOutputStream.getNextEntry();
while (zipEntry != null) {
}
}
}
private void saveZipEntry(ZipEntry zipEntry) {
// логика сохранения
}
この例では、リソースは ですZipInputStream
。作業後は、リソースを閉じる必要があります。そして、メソッドの呼び出しについて考えないようにするためにclose()
、例に示すように、単に try ブロック内でこの変数を定義し、このブロック内で必要なすべての処理を行います。この例は何をするのでしょうか? zip アーカイブを解凍します。これを行うには、「om」を使用する必要がありますInputStream
。複数の変数をセミコロンで区切って定義できます。どうしたの?finally
しかし、ブロックを使用することはできる、とあなたは言うかもしれません。このアプローチの問題点について詳しく説明した記事は次のとおりです。また、この設計の使用を怠った人に降りかかる可能性のある失敗のリスト全体についても説明します。読むことをお勧めします ;)最後の部分には、マルチスレッドのトピックに関する質問と回答があります。 私の GitHub プロフィール
GO TO FULL VERSION