CodeGym/Java 博客/随机的/探索 Java 开发人员职位面试中的问题和答案。第12部分
John Squirrels
第 41 级
San Francisco

探索 Java 开发人员职位面试中的问题和答案。第12部分

已在 随机的 群组中发布
个会员
你好!知识就是力量。您在第一次面试中掌握的知识越多,您就会越有信心。 探索 Java 开发人员职位面试中的问题和答案。 第 12 - 1 部分如果你有一个充满知识的大脑,你的面试官会发现很难让你困惑,并且很可能会给你带来惊喜。因此,言归正传,今天我们将通过复习 Java 开发人员的问题来继续加强您的理论基础。

103. 继承过程中的异常检查适用哪些规则?

如果我正确理解这个问题,他们会询问继承过程中处理异常的规则。相关规则如下:
  • 后代/实现中的重写或实现的方法不能抛出在层次结构中比超类/接口方法中的异常更高的已检查异常。
例如,假设我们有一些Animal接口,其方法会抛出IOException
public interface Animal {
   void speak() throws IOException;
}
在实现该接口时,我们无法暴露更通用的可抛出异常(例如ExceptionThrowable),但我们可以用子类替换现有异常,例如FileNotFoundException
public class Cat implements Animal {
   @Override
   public void speak() throws FileNotFoundException {
// Some implementation
   }
}
  • 子类构造函数的throws子句必须包含为创建对象而调用的超类构造函数抛出的所有异常类。
假设Animal类的构造函数抛出了很多异常:
public class Animal {
  public Animal() throws ArithmeticException, NullPointerException, IOException {
  }
然后子类构造函数也必须抛出它们:
public class Cat extends Animal {
   public Cat() throws ArithmeticException, NullPointerException, IOException {
       super();
   }
或者,与方法一样,您可以指定不同的、更一般的异常。在我们的例子中,我们可以指示Exception,因为它更通用,并且是超类中指示的所有三个异常的共同祖先:
public class Cat extends Animal {
   public Cat() throws Exception {
       super();
   }

104. 你能写一些不执行finally块的代码吗?

首先,让我们记住什么是finally。之前,我们研究了异常捕获机制:try块指定将捕获异常的位置,catch块是捕获相应异常时将调用的代码。由finally关键字标记的第三个代码块可以替换catch 块或位于catch 块之后。该块背后的想法是,无论trycatch块中发生什么(无论是否存在异常),它的代码总是会被执行。不执行该块的情况并不常见,并且属于异常情况。最简单的例子是在finally块之前调用System.exit(0),从而终止程序:
try {
   throw new IOException();
} catch (IOException e) {
   System.exit(0);
} finally {
   System.out.println("This message will not be printed on the console");
}
还有一些其他情况导致finally块不会运行:
  • 例如,严重系统错误导致的程序异常终止,或者导致应用程序崩溃的错误(例如,当应用程序堆栈溢出时发生StackOverflowError )。

  • 另一种情况是守护线程进入try-finally块,但随后程序的主线程终止。毕竟,守护线程用于非高优先级或强制性的后台工作,因此应用程序不会等待它们完成。

  • 最愚蠢的例子是trycatch块内的无限循环 - 一旦进入,线程将永远卡在那里:

    try {
       while (true) {
       }
    } finally {
       System.out.println("This message will not be printed on the console");
    }
这个问题在初级开发人员面试中很常见,因此最好记住一些特殊情况。 探索 Java 开发人员职位面试中的问题和答案。 第 12 - 2 部分

105. 编写一个在单个 catch 块中处理多个异常的示例。

1)我不确定这个问题是否正确提出。 据我了解,这个问题指的是几个catch块和一个try
try {
  throw new FileNotFoundException();
} catch (FileNotFoundException e) {
   System.out.print("Oops! There was an exception: " + e);
} catch (IOException e) {
   System.out.print("Oops! There was an exception: " + e);
} catch (Exception e) {
   System.out.print("Oops! There was an exception: " + e);
}
如果在try块 中引发异常,则关联的catch块将尝试按从上到下的顺序捕获该异常。一旦异常与其中一个catch块匹配,任何剩余的块将不再能够捕获和处理它。这一切都意味着在catch块集中,较窄的异常排列在较通用的异常之上。例如,如果我们的第一个catch块捕获了Exception类,那么任何后续块都不会捕获已检查的异常(也就是说,具有Exception子类的剩余块将完全无用)。 2)或者也许这个问题问得正确。 在这种情况下,我们可以按如下方式处理异常:
try {
  throw new NullPointerException();
} catch (Exception e) {
   if (e instanceof FileNotFoundException) {
       // Some handling that involves a narrowing type conversion: (FileNotFoundException)e
   } else if (e instanceof ArithmeticException) {
       // Some handling that involves a narrowing type conversion: (ArithmeticException)e
   } else if(e instanceof NullPointerException) {
       // Some handling that involves a narrowing type conversion: (NullPointerException)e
   }
使用catch捕获异常后,我们尝试使用instanceof运算符来发现其特定类型,该运算符检查对象是否属于某种类型。这使我们能够自信地执行缩小类型转换,而不必担心负面后果。我们可以在相同的情况下应用任一方法。我对这个问题表示怀疑只是因为我不认为第二种选择是一个好方法。根据我的经验,我从未遇到过这种情况,并且第一种涉及多个 catch 块的方法很普遍。

106.哪个运算符可以让你强制抛出异常?写一个例子

我已经在上面的示例中多次使用过它,但我将再次重复它:throw关键字。手动抛出异常的示例:
throw new NullPointerException();

107. main方法可以抛出异常吗?如果是这样,那么它去哪里了?

首先,我要说明的是,main方法只不过是一个普通的方法。是的,它由虚拟机调用来开始执行程序,但除此之外,还可以从任何其他代码调用它。这意味着它也遵循有关在throws关键字后指示已检查异常的通常规则:
public static void main(String[] args) throws IOException {
因此,它可以抛出异常。当main被调用作为程序的起点(而不是通过其他方法)时,它抛出的任何异常都将由UncaughtExceptionHandler 处理。每个线程都有一个这样的处理程序(即每个线程中有一个这样的处理程序)。如有必要,您可以创建自己的处理程序并通过调用 public static void main(String[] args) throws IOException {setDefaultUncaughtExceptionHandler 方法来设置它 public static void main(String[] args) throws IOException {Thread 对象。

多线程

探索 Java 开发人员职位面试中的问题和答案。 第 12 - 3 部分

108. 您知道哪些在多线程环境中工作的机制?

Java中多线程的基本机制是:
  • Synchronized关键字,是线程进入时锁定方法/块的一种方式,阻止其他线程进入

  • 易失性关键字确保对不同线程访问的变量的访问一致。也就是说,当将此修饰符应用于变量时,分配和读取该变量的所有操作都将成为原子操作。换句话说,线程不会将变量复制到本地内存并更改它。它们会改变它的原始值。

  • Runnable——我们可以在某个类中实现这个接口(它由一个run()方法组成):

    public class CustomRunnable implements Runnable {
       @Override
       public void run() {
           // Some logic
       }
    }

    一旦我们创建了该类的对象,我们就可以通过将对象传递给Thread构造函数然后调用start()方法来启动一个新线程:

    Runnable runnable = new CustomRunnable();
    new Thread(runnable).start();

    start方法在单独的线程上运行已实现的run()方法。

  • Thread — 我们可以继承这个类并重写它的run方法:

    public class CustomThread extends Thread {
       @Override
       public void run() {
           // Some logic
       }
    }

    我们可以通过创建此类的对象然后调用start()方法来启动一个新线程:

    new CustomThread().start();

  • 并发性——这是用于在多线程环境中工作的工具包。

    它包括:

    • 并发集合——这是为在多线程环境中工作而显式创建的集合的集合。

    • 队列——多线程环境的专用队列(阻塞和非阻塞)。

    • 同步器——这些是在多线程环境中工作的专用实用程序。

    • 执行器——创建线程池的机制。

    • ——比标准同步机制(synchronized、wait、notify、notifyAll)更灵活的线程同步机制。

    • 原子- 针对多线程优化的类。他们的每个操作都是原子的。

109. 告诉我们线程之间的同步。wait()、notify()、notifyAll() 和 join() 方法的用途是什么?

线程之间的同步与synchronized关键字有关。该修饰符可以直接放置在块上:
synchronized (Main.class) {
   // Some logic
}
或者直接在方法签名中:
public synchronized void move() {
   // Some logic }
正如我之前所说,同步是一种一旦一个线程进入就将块/方法锁定到其他线程的机制。让我们将代码块/方法视为一个房间。一些线程接近房间,进入房间,并用钥匙锁上门。当其他线程接近房间时,它们会看到门被锁住并在附近等待,直到房间可用。一旦第一个线程完成了房间中的事务,它就会打开门,离开房间,并释放钥匙。我多次提到某个键是有原因的——因为类似的东西确实存在。这是一个具有忙碌/空闲状态的特殊对象。Java中的每个对象都有这样一个对象,因此当我们使用synchronized块时,我们需要使用括号来指示互斥体将被锁定的对象:
Cat cat = new Cat();
synchronized (cat) {
   // Some logic
}
我们还可以使用与类关联的互斥锁,就像我在第一个示例 ( Main.class ) 中所做的那样。毕竟,当我们在方法上使用同步时,我们没有指定要锁定的对象,对吗?在这种情况下,对于非静态方法,将被锁定的互斥量是this对象,即类的当前对象。对于静态方法,与当前类 ( this.getClass(); )关联的互斥锁被锁定。 wait()是一种释放互斥锁并将当前线程置于等待状态的方法,就像附加到当前监视器(类似于锚点)一样。因此,只能从同步块或方法中调用该方法。否则,它会等待什么并释放什么?)。另请注意,这是Object类的方法。好吧,不是一个,而是三个:
  • wait()将当前线程置于等待状态,直到另一个线程调用该对象的notify()notifyAll()方法(我们稍后会讨论这些方法)。

  • wait(long timeout)将当前线程置于等待状态,直到另一个线程调用该对象的notify()notifyAll()方法或者timeout指定的时间间隔到期。

  • wait(long timeout, int nanos)与前面的方法类似,但这里nanos允许您指定纳秒(更精确的超时)。

  • notify()可以让您唤醒一个在当前同步块上等待的随机线程。再次强调,这个方法只能在同步块或方法中调用(毕竟在其他地方就没有人会被唤醒)。

  • notifyAll()唤醒当前监视器上等待的所有线程(也仅在同步块或方法中使用)。

110.我们如何停止一个线程?

这里首先要说的是,当run()运行完成时,线程会自动终止。但有时我们想在方法完成之前提前终止线程。那么我们该怎么办?也许我们可以在Thread对象上使用stop()方法?没有!该方法已被弃用,可能会导致系统崩溃。 嗯,然后呢?有两种方法可以做到这一点: 首先,使用其内部布尔标志。让我们看一个例子。我们实现了一个线程,该线程应该在屏幕上显示某个短语,直到线程完全停止: 探索 Java 开发人员职位面试中的问题和答案。 第 12 - 4 部分
public class CustomThread extends Thread {
private boolean isActive;

   public CustomThread() {
       this.isActive = true;
   }

   @Override
   public void run() {
       {
           while (isActive) {
               System.out.println("The thread is executing some logic...");
           }
           System.out.println("The thread stopped!");
       }
   }

   public void stopRunningThread() {
       isActive = false;
   }
}
调用stopRunningThread()方法将内部标志设置为 false,从而导致run()方法终止。让我们在main中调用它:
System.out.println("Program starting...");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// As long as our main thread is asleep, our CustomThread runs and prints its message on the console
thread.stopRunningThread();
System.out.println("Program stopping...");
结果,我们会在控制台中看到类似这样的内容:
程序开始... 线程正在执行一些逻辑... 线程正在执行一些逻辑... 线程正在执行一些逻辑... 线程正在执行一些逻辑... 线程正在执行一些逻辑...线程正在执行一些逻辑...程序停止...线程停止!
这意味着我们的线程启动了,在控制台上打印了几条消息,然后成功停止。请注意,显示的消息数量因启动而异。有时辅助线程可能根本不显示任何内容。具体行为取决于主线程休眠的时间。它休眠的时间越长,辅助线程无法显示任何内容的可能性就越小。如果睡眠时间为 1 毫秒,您几乎看不到这些消息。但如果将其设置为 20 毫秒,则几乎总是会显示消息。当睡眠时间很短时,线程根本没有时间启动并完成其工作。相反,它会立即停止。第二种方法是使用Thread对象的 Interrupted ( )方法。它返回内部中断标志的值,默认为false 。或者它的Interrupt()方法,该方法将此标志设置为true(当标志为true时,线程应该停止运行)。让我们看一个例子:
public class CustomThread extends Thread {

   @Override
   public void run() {
       {
           while (!Thread.interrupted()) {
               System.out.println("The thread is executing some logic...");
           }
           System.out.println("The thread stopped!");
       }
   }
}
在main 中运行:
System.out.println("Program starting...");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Program stopping...");
运行的结果与第一种情况相同,但我更喜欢这种方法:我们编写了更少的代码并使用了更多现成的标准功能。好了,今天就到此为止!
评论
  • 受欢迎
你必须先登录才能发表评论
此页面还没有任何评论