JavaRush /Java 博客 /Random-ZH /前 50 个 Java 核心面试问题和答案。第2部分
Roman Beekeeper
第 35 级

前 50 个 Java 核心面试问题和答案。第2部分

已在 Random-ZH 群组中发布
前 50 个 Java 核心面试问题和答案。第1部分 前 50 个 Java 核心面试问题和答案。 第 2 - 1 部分

收藏

25. Java中集合是什么意思?

集合是一个旨在存储和操作对象的框架。用于执行以下操作:
  • 搜索;
  • 排序;
  • 操纵;
  • 添加;
  • 删除。
Collection 框架的所有类和接口都在java.util包中。

26. Collection框架中有哪些类和接口可用?

接口:
  • 收藏;
  • 列表;
  • 放;
  • 地图;
  • 排序集;
  • 排序地图;
  • 队列。
课程:
  • 列表:
    1. 数组列表;
    2. 链表;
    3. 矢量(已弃用)。
  • 套:
    1. 哈希集;
    2. 链接哈希集;
    3. 树集。
  • 地图:
    1. 哈希映射
    2. 树形图
    3. 哈希表(已弃用)
    4. 链接哈希映射
  • 队列
    1. 优先队列。

27.集合中排序和排序是什么意思?

订购:

这意味着存储在集合中的项目基于添加到集合中的值。这样我们就可以按照特定的顺序迭代集合中的值。换句话说,这意味着集合的元素有自己特定的排列顺序。为了更好地理解,未排序的集合以随机顺序存储其元素。例如,设置。

排序:

这意味着根据集合元素的数据将一组元素排序到集合中。也就是说,不仅集合是有序的,而且元素的顺序也取决于它们的值。如果您按不同的元素值排序,则此顺序可能会更改。

28. 有哪些集合具有List接口?你如何与列表一起工作?

工作表中元素的值基于它们的索引——它们按索引排序。元素重复是允许的(也就是说,你可以多次将同一个对象添加到集合中就可以了)。

数组列表:

最常见的集合。本质上,它是一个大小动态扩展的数组。管理数组大小的工作由集合负责。对于我们来说,重要的是要了解,在大多数情况下,这就是我们需要使用的。特点:
  • 快速搜索和快速索引搜索;
  • 集合按索引排序,但未排序;
  • 实现随机访问接口;
  • 慢慢地添加到列表的中间。
例子:
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:

链接列表:

这是一个集合,其中每个元素都有到上一个和下一个元素的链接。这些链接允许您从一个元素移动到另一个元素。添加元素时,前一个和下一个元素的链接只会发生变化: 前 50 个 Java 核心面试问题和答案。 第 2 - 2 部分
  • 元素之间相互连接,即实现了双向链表;
  • 整体运行速度明显低于ArrayList;
  • 对于在数组中间进行大量插入和删除的绝佳选择;
  • 实现了 Queue 和 Deque 列表接口,因此有它们的工作方法。
例子:
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("One");
linkedList.add("Two");
linkedList.add("Three");

29. 请介绍一下 Map 集合及其实现?

Map 是一个键值集合。有一个唯一的键和一个与该值匹配的值。equals()方法还用于hashcode()确定密钥的唯一性。

哈希映射:

  • 未分类或排序;
  • 如果顺序和排序不重要则使用;
  • 支持空键。
例子:
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}
密钥始终是唯一的,因此只记录了一位高级人员。

链接哈希映射:

  • 维护插入顺序;
  • 比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}

树形图:

一种映射实现,可以根据键的自然顺序对条目进行排序,或者更好的是,如果创建映射时在构造函数中提供了比较器,则使用比较器。例子:
  1. 不带比较器

    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}
  2. 带比较器

    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}
我们看到按升序排序是作为标准实现的,但是可以通过向构造函数添加比较器来更改这一点。TreeMap在这里得到了很好的描述。

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]
在这里您可以看到添加了两次的“junior”元素仅出现在一个实例中。而且顺序和添加时不一样。

链接哈希集:

  • 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]

树集:

  • 两个已排序集合之一;
  • 采用红黑树结构,并保证元素按升序排列;
  • 在底层,它是一个带有值存根的 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.什么是异常?

异常是运行时可能发生的问题。这是由于某种原因而出现的特殊情况。异常继承图如下所示(您需要熟记在心;)): 前 50 个 Java 核心面试问题和答案。 第 2 - 3 部分该图显示,一般来说,所有异常都分为两组 -异常错误。 错误- 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)
有两种选择:随机抛出一个或另一个异常。对于 IOException 一切正常,如果生成它,那么工作结果将是:"Caught IOException"。但是,如果出现第二个没有处理程序的异常,程序将停止并显示以下输出:

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. 程序员如何处理异常?

在上面的问题中,某些关键字已经用于处理异常;现在我们需要更详细地讨论它们。关键词是什么?
  • 尝试
  • 抓住
  • 投掷
  • 最后
需要注意的是,catch、throw 和 throws 只能与 java.lang.Throwable 一起使用。这不适用于其他类型。现在我们将讨论 try、catch 和finally。
  • try-catch-finally是一个允许您正确捕获和处理异常的构造。
  • try- 只能有一次,即逻辑发生的时刻;
  • 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 和 throws

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- 一种方法可以抛出一个或多个异常的机制。它们以逗号分隔添加。让我们看看它是多么容易和简单:
private Object willThrow() throws RuntimeException, IOException, FileNotFoundException
此外,值得注意的是,可以存在受控异常和非受控异常。当然,未经检查的异常可能不会被添加到 中throws,但出于礼貌,否则会这样说。如果这些是可检查的,那么使用生成它们的方法,您需要以某种方式处理它。有两种选择:
  1. try-catch用适当的和上面的继承异常来编写。
  2. throws以完全相同的方式使用它,以便其他人已经遇到这个问题:D

35. Java中的受检异常和非受检异常

Java中有两种类型的异常——受控异常和非受控异常。

检查异常:

这些是在编译时检查的异常。如果方法中的某些代码在异常期间抛出已检查的异常,则该方法必须使用 处理它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);
   }
}
此类代码将无法编译,因为静态方法ImageIO.read()会抛出ImageIO.write()IOException,该异常会被检查并必须进行相应处理。这里有两个选项我们已经在上面讨论过:要么使用try-catch,要么进一步转发。为了更好地同化,我们会做这做那。也就是说,updateAndSave我们只需在方法中转发它,然后在 main 方法中使用它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 的内容都取消选中。 前 50 个 Java 核心面试问题和答案。 第 2 - 4 部分考虑以下 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. 什么是资源尝试?

这是一种必须正确关闭所有资源的机制。有点不清楚,对吧?)首先,什么是资源……资源是一个对象,使用后需要关闭它,即调用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 存档。为此,您需要使用InputStream'om. 您可以定义多个变量;它们之间用分号分隔。有什么问题?但您finally可能会说,您可以使用块。这里有一篇文章详细介绍了这种方法的问题。它还描述了忽略使用此设计的人可能遇到的失败的完整列表。我建议阅读它;)最后一部分有关于多线程主题的问题/答案。 我的 GitHub 个人资料
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION