前 50 个 Java 核心面试问题和答案。第1部分
收藏
25. Java中集合是什么意思?
集合是一个旨在存储和操作对象的框架。用于执行以下操作:- 搜索;
- 排序;
- 操纵;
- 添加;
- 删除。
java.util
包中。
26. Collection框架中有哪些类和接口可用?
接口:- 收藏;
- 列表;
- 放;
- 地图;
- 排序集;
- 排序地图;
- 队列。
- 列表:
- 数组列表;
- 链表;
- 矢量(已弃用)。
- 套:
- 哈希集;
- 链接哈希集;
- 树集。
- 地图:
- 哈希映射
- 树形图
- 哈希表(已弃用)
- 链接哈希映射
- 队列
- 优先队列。
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:
链接列表:
这是一个集合,其中每个元素都有到上一个和下一个元素的链接。这些链接允许您从一个元素移动到另一个元素。添加元素时,前一个和下一个元素的链接只会发生变化:- 元素之间相互连接,即实现了双向链表;
- 整体运行速度明显低于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}
树形图:
一种映射实现,可以根据键的自然顺序对条目进行排序,或者更好的是,如果创建映射时在构造函数中提供了比较器,则使用比较器。例子:-
不带比较器
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]
在这里您可以看到添加了两次的“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.什么是异常?
异常是运行时可能发生的问题。这是由于某种原因而出现的特殊情况。异常继承图如下所示(您需要熟记在心;)): 该图显示,一般来说,所有异常都分为两组 -异常和错误。 错误- 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. 程序员如何处理异常?
在上面的问题中,某些关键字已经用于处理异常;现在我们需要更详细地讨论它们。关键词是什么?- 尝试
- 抓住
- 扔
- 投掷
- 最后
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
,但出于礼貌,否则会这样说。如果这些是可检查的,那么使用生成它们的方法,您需要以某种方式处理它。有两种选择:
try-catch
用适当的和上面的继承异常来编写。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 的内容都取消选中。 考虑以下 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 个人资料
GO TO FULL VERSION