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

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

已在 Random-ZH 群组中发布
各位软件工程师,女士们先生们大家好!我们来谈谈面试问题。关于您需要准备什么以及您需要了解什么。这是从头开始重复或研究这些要点的绝佳理由。 前 50 个 Java 核心面试问题和答案。 第 1 - 1 部分我收集了相当广泛的有关 OOP、Java 语法、Java 中的异常、集合和多线程的常见问题,为了方便起见,我将其分为几个部分。 重要的:我们只会讨论 Java 版本 8 之前的版本。这里不会考虑 9、10、11、12、13 中的所有创新。欢迎任何关于如何改进答案的想法/评论。祝阅读愉快,走吧!

Java面试:OOP问题

1.Java有什么特点?

回答:
  1. 面向对象编程的概念:

    1. 面向对象;
    2. 遗产;
    3. 封装;
    4. 多态性;
    5. 抽象。
  2. 跨平台: Java程序无需任何修改就可以在任何平台上运行。您唯一需要的是已安装的 JVM(java 虚拟机)。

  3. 高性能: JIT(Just In Time编译器)允许高性能。JIT 将字节码转换为机器码,然后 JVM 开始执行。

  4. 多线程:称为 的执行线程Thread。JVM 创建一个名为 的线程main thread。程序员可以通过继承 Thread 类或实现接口来创建多个线程Runnable

2.什么是继承?

继承意味着一个类可以继承(“扩展”)另一个类。这样您就可以重用您继承的类中的代码。现有类称为superclass,正在创建的类称为subclass。他们还说parentchild
public class Animal {
   private int age;
}

public class Dog extends Animal {

}
其中Animalparent,并且Dog- child

3.什么是封装?

这个问题在 Java 开发人员面试中经常出现。封装是使用访问修饰符、getter 和 setter 来隐藏实现。这样做是为了在开发人员认为有必要的地方关闭外部使用的访问。生活中一个容易理解的例子是汽车。我们无法直接了解发动机的运行情况。对于我们来说,工作就是将钥匙插入点火开关并启动发动机。幕后将发生什么流程与我们无关。此外,我们对这项活动的干扰可能会导致不可预测的情况,因此我们可能会损坏汽车并伤害自己。编程中也发生同样的事情。维基百科上有很好的描述。JavaRush上也有一篇关于封装的文章。

4.什么是多态性?

多态性是程序能够以相同的接口相同地使用对象,而无需了解该对象的特定类型。正如他们所说,一个接口 - 多种实现。通过多态性,您可以根据不同类型的对象的共同行为来组合和使用它们。例如,我们有一个 Animal 类,它有两个后代 - Dog 和 Cat。通用 Animal 类有一个共同的行为 - 发出声音。当我们需要将 Animal 类的所有后代放在一起并执行“发出声音”方法时,我们可以使用多态性的可能性。它将如下所示:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
所以多态性对我们有帮助。此外,这也适用于多态(重载)方法。 使用多态性的实践

面试问题 - Java 语法

5. Java中的构造函数是什么?

以下特征有效:
  1. 创建新对象时,程序会使用适当的构造函数来执行此操作。
  2. 构造函数就像一个方法。它的奇特之处在于没有返回元素(包括void),并且它的名称与类的名称相同。
  3. 如果没有明确编写构造函数,则会自动创建一个空构造函数。
  4. 构造函数可以被重写。
  5. 如果创建了带参数的构造函数,但还需要一个不带参数的构造函数,则需要单独编写它,因为它不会自动创建。

6. 哪两个类不是继承自Object?

不要被挑衅所迷惑,不存在这样的类:所有直接或通过祖先的类都是从 Object 类继承的!

7.什么是局部变量?

Java 开发人员面试中的另一个常见问题。局部变量是在方法内部定义的变量,并且在方法执行之前一直存在。一旦执行结束,局部变量将不复存在。下面是一个在 main() 方法中使用 helloMessage 局部变量的程序:
public static void main(String[] args) {
   String helloMessage;
   helloMessage = "Hello, World!";
   System.out.println(helloMessage);
}

8.什么是实例变量?

实例变量是在类内部定义的变量,它一直存在到对象存在的那一刻。一个例子是 Bee 类,它有两个变量 nectarCapacity 和 maxNectarCapacity:
public class Bee {

   /**
    * Current nectar capacity
    */
   private double nectarCapacity;

   /**
    * Maximal nectar that can take bee.
    */
   private double maxNectarCapacity = 20.0;

  ...
}

9. 什么是访问修饰符?

访问修饰符是一种允许您自定义对类、方法和变量的访问的工具。有以下修饰符,按访问权限增加的顺序排列:
  1. private- 用于方法、字段和构造函数。访问级别只是声明它的类。
  2. package-private(default)- 可用于课程。仅在声明了类、方法、变量、构造函数的特定包中访问。
  3. protectedpackage-private—对于那些从带有修饰符的类继承的类,具有与 + 相同的访问权限protected
  4. public- 也用于课程。整个应用程序的完全访问权限。
  5. 前 50 个 Java 核心面试问题和答案。 第 1 - 2 部分

10.什么是重写方法?

当子类想要更改父类的行为时,就会发生方法重写。如果你想要执行父方法中的内容,你可以在子方法中使用像 super.methodName() 这样的构造,它将完成父方法的工作,然后才添加逻辑。需满足的要求:
  • 方法签名必须相同;
  • 返回值应该是相同的。

11.什么是方法签名?

前 50 个 Java 核心面试问题和答案。 第 1 - 3 部分方法签名是方法名称和方法接受的参数的集合。方法签名是重载方法时方法的唯一标识符。

12.什么是方法重载?

方法重载是多态性的一个属性,通过更改方法签名,您可以为相同的操作创建不同的方法:
  • 相同的方法名称;
  • 不同的论点;
  • 可能有不同的返回类型。
例如,可以按如下方式重载相同的add()内容ArrayList,并将根据传入参数以不同的方式执行加法:
  • add(Object o)- 简单地添加一个对象;
  • add(int index, Object o)— 将对象添加到特定索引;
  • add(Collection<Object> c)— 添加对象列表;
  • add(int index, Collection<Object> c)— 从某个索引开始添加对象列表。

13.什么是接口?

Java 中没有实现多重继承,因此为了解决这个问题,添加了我们所知道的接口;) 很长一段时间,接口只有方法而没有实现它们。作为这个答案的一部分,我们将讨论它们。例如:

public interface Animal {
   void makeSound();
   void eat();
   void sleep();
}
由此得出一些细微差别:
  • 接口中的所有方法都是公共的、抽象的;
  • 所有变量都是public static final;
  • 类并不继承它们(扩展),而是实现它们(实现)。此外,您可以实现任意数量的接口。
  • 实现接口的类必须提供该接口所具有的所有方法的实现。
像这样:
public class Cat implements Animal {
   public void makeSound() {
       // method implementation
   }

   public void eat() {
       // implementation
   }

   public void sleep() {
       // implementation
   }
}

14. 接口中的默认方法是什么?

现在我们来谈谈默认方法。为了什么,为了谁?添加这些方法是为了让一切“既是你的,也是我们的”。我在说什么?是的,一方面,有必要添加新功能:lambdas、Stream API,另一方面,有必要保留 Java 闻名的东西——向后兼容性。为此,有必要在界面中引入现成的解决方案。这就是我们如何使用默认方法的。也就是说,默认方法是接口中具有关键字 的实现方法default。例如,众所周知的stream()方法Collection。看看吧,这个界面并不像看起来那么简单;)。forEach()或者也是来自 的同样众所周知的方法Iterable。在添加默认方法之前它也不存在。顺便说一句,您还可以在JavaRush上阅读有关此内容的内容。

15. 那么如何继承两个相同的默认方法呢?

根据之前关于默认方法是什么的答案,您可以问另一个问题。如果你可以在接口中实现方法,那么理论上你可以用相同的方法实现两个接口,如何做到这一点?有两个不同的接口具有相同的方法:
interface A {
   default void foo() {
       System.out.println("Foo A");
   }
}

interface B {
   default void foo() {
       System.out.println("Foo B");
   }
}
并且有一个类实现了这两个接口。foo()为了避免不确定性并编译代码,我们需要重写类中的方法,并且我们可以简单地调用其中任何接口的C方法-或。但究竟如何选择具体的接口方法呢?有一个这样的结构: foo()ABАВA.super.foo()
public class C implements A, B {
   @Override
   public void foo() {
       A.super.foo();
   }
}
或者:
public class C implements A, B {
   @Override
   public void foo() {
       B.super.foo();
   }
}
因此,foo()类方法将使用接口中的C默认方法或接口中的方法。 foo()Afoo()B

16.什么是抽象方法和类?

Java 有一个保留字abstract,用于表示抽象类和方法。首先,一些定义。abstract抽象方法是在抽象类中使用关键字创建的没有实现的方法。也就是说,这是一个像接口中的方法,只是添加了一个关键字,例如:
public abstract void foo();
抽象类是一个也有abstract这个词的类:
public abstract class A {

}
抽象类有几个特点:
  • 不能在其基础上创建对象;
  • 它可以有抽象方法;
  • 它可能没有抽象方法。
需要抽象类来概括某种抽象(抱歉是同义反复),这种抽象在现实生活中并不存在,但它包含许多常见的行为和状态(即方法和变量)。生活中的例子已经足够多了。一切都在我们周围。它可以是“动物”、“汽车”、“几何图形”等等。

17. String、String Builder 和 String Buffer 有什么区别?

这些值String存储在常量字符串池中。一旦创建了一行,它将出现在该池中。并且将无法删除它。例如:
String name = "book";
...变量将引用字符串池常量字符串池 前 50 个 Java 核心面试问题和答案。 第 1 - 4 部分 如果将变量名称设置为不同的值,您将得到以下结果:
name = "pen";
常量字符串池 前 50 个 Java 核心面试问题和答案。 第 1 - 5 部分所以这两个值会保留在那里。 字符串缓冲区:
  • String存储在堆栈中。如果该值发生变化,则新值将被旧值替换;
  • String Buffer同步,因此线程安全;
  • 由于线程安全的原因,运行速度还有很多不尽如人意的地方。
例子:
StringBuffer name = "book";
前 50 个 Java 核心面试问题和答案。 第 1 - 6 部分一旦 name 的值发生变化,堆栈上的值就会发生变化: 前 50 个 Java 核心面试问题和答案。 第 1 - 7 部分StringBuilder 与 完全相同StringBuffer,只是它不是线程安全的。因此,它的速度明显高于StringBuffer

18. 抽象类和接口有什么区别?

抽象类:
  • 抽象类有一个默认构造函数;每次创建该抽象类的子类时都会调用它;
  • 包含抽象方法和非抽象方法。总的来说,它可能不包含抽象方法,但仍然是一个抽象类;
  • 从抽象类继承的类必须仅实现抽象方法;
  • 抽象类可以包含实例变量(参见问题#5)。
界面:
  • 没有构造函数,无法初始化;
  • 只应该添加抽象方法(不包括默认方法);
  • 实现接口的类必须实现所有方法(不包括默认方法);
  • 接口只能包含常量。

19. 为什么访问数组中的元素需要 O(1)?

这个问题实际上来自上次采访。后来我才知道,问这个问题是为了看看一个人是怎么想的。显然,这些知识没有什么实际意义:只要知道这个事实就足够了。首先,我们需要澄清 O(1) 是当操作在恒定时间内发生时算法的时间复杂度的指定。也就是说,这个指定是最快执行的。要回答这个问题,我们需要了解一下我们对数组了解多少?要创建数组int,我们必须编写以下内容:
int[] intArray = new int[100];
从这段录音中可以得出几个结论:
  1. 创建数组时,它的类型是已知的。如果类型已知,那么数组的每个单元的大小就很清楚了。
  2. 数组的大小是已知的。
由此可见:要了解要写入哪个单元格,您只需计算要写入哪个内存区域即可。对于汽车来说,这再简单不过了。该机器具有已分配内存的起始位置、多个元素和单个单元大小。由此可以清楚地看出,记录空间将等于数组的起始位置+单元格的大小乘以其大小。

如何以 O(1) 的速度访问 ArrayList 中的对象?

这个问题紧接着上一个问题。确实,当我们使用数组并且其中有基元时,我们在创建该类型时提前知道该类型的大小是多少。但是,如果有一种如图所示的方案: 前 50 个 Java 核心面试问题和答案。 第 1 - 8 部分我们想要创建一个包含 A 类型元素的集合,并添加不同的实现 - B、C、D:
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
在这种情况下,您如何了解每个单元格的大小,因为每个对象都会不同,并且可能具有不同的附加字段(或完全不同)。该怎么办?这里提出这个问题,是为了混淆视听。我们知道,其实集合并不存储对象,而只是存储这些对象的链接。所有链接都具有相同的大小,这是已知的。因此,这里计算空间的方式与上一个问题相同。

21. 自动装箱和拆箱

历史背景:自动装箱和自动拆箱是 JDK 5 的主要创新之一。 自动装箱是从原始类型自动转换为适当的包装类的过程。 自动拆箱- 与自动装箱完全相反 - 将包装类转换为基元。但如果存在包装器值null,则在解包期间将引发异常NullPointerException

匹配原语 - 包装器

原始 类包装器
布尔值 布尔值
整数 整数
字节 字节
字符 特点
漂浮 漂浮
长的 长的
短的 短的
双倍的 双倍的

自动打包发生:

  • 当为原语分配对包装类的引用时:

    Java 5 之前:

    //manual packaging or how it was BEFORE Java 5.
    public void boxingBeforeJava5() {
       Boolean booleanBox = new Boolean(true);
       Integer intBox = new Integer(3);
       // and so on to other types
    }
    
    после Java 5:
    //automatic packaging or how it became in Java 5.
    public void boxingJava5() {
       Boolean booleanBox = true;
       Integer intBox = 3;
       // and so on to other types
    }
  • 当将原语作为参数传递给需要包装器的方法时:

    public void exampleOfAutoboxing() {
       long age = 3;
       setAge(age);
    }
    
    public void setAge(Long age) {
       this.age = age;
    }

自动解包发生:

  • 当我们将一个原始变量分配给包装类时:

    //before Java 5:
    int intValue = new Integer(4).intValue();
    double doubleValue = new Double(2.3).doubleValue();
    char c = new Character((char) 3).charValue();
    boolean b = Boolean.TRUE.booleanValue();
    
    //and after JDK 5:
    int intValue = new Integer(4);
    double doubleValue = new Double(2.3);
    char c = new Character((char) 3);
    boolean b = Boolean.TRUE;
  • 在进行算术运算的情况下。它们仅适用于原始类型;为此您需要对原始类型进行解包。

    // Before Java 5
    Integer integerBox1 = new Integer(1);
    Integer integerBox2 = new Integer(2);
    
    // for comparison it was necessary to do this:
    integerBox1.intValue() > integerBox2.intValue()
    
    //в Java 5
    integerBox1 > integerBox2
  • 当传递给接受相应原语的方法中的包装器时:

    public void exampleOfAutoboxing() {
       Long age = new Long(3);
       setAge(age);
    }
    
    public void setAge(long age) {
       this.age = age;
    }

22.final关键字是什么以及在哪里使用它?

该关键字final可用于变量、方法和类。
  1. 最终变量不能重新分配给另一个对象。
  2. 最终类是不育的))它不能有继承人。
  3. 最终方法不能在祖先上被重写。
我们已经讨论了顶部内容,现在让我们更详细地讨论它。

最终变量

;Java 为我们提供了两种创建变量并为其赋值的方法:
  1. 您可以声明一个变量并稍后对其进行初始化。
  2. 您可以声明一个变量并立即分配它。
在这些情况下使用最终变量的示例:
public class FinalExample {

   //final static variable, which is immediately initialized:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";

   //final is a variable that is not initialized, but will only work if
   //initialize this in the constructor:
   final long creationTime;

   public FinalExample() {
       this.creationTime = System.currentTimeMillis();
   }

   public static void main(String[] args) {
       FinalExample finalExample = new FinalExample();
       System.out.println(finalExample.creationTime);

       // final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
//    FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";

       // final field Config.creationTime cannot be assigned
//    finalExample.creationTime = 1L;
   }
}

Final 变量可以被视为常量吗?

由于我们无法为最终变量分配新值,因此这些似乎是常量变量。但这只是乍一看。如果变量引用的数据类型是 immutable,那么是的,它是一个常量。但是,如果数据类型mutable是可变的,则使用方法和变量就可以更改变量引用的对象的值final,在这种情况下,它不能称为常量。因此,该示例表明某些最终变量确实是常量,但有些则不是,并且它们是可以更改的。
public class FinalExample {

   //immutable final variables:
   final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
   final static Integer FINAL_EXAMPLE_COUNT  = 10;

   // mutable filter variables
   final List<String> addresses = new ArrayList();
   final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}

局部最终变量

final一个变量在方法内部创建时,它被称为local final变量:
public class FinalExample {

   public static void main(String[] args) {
       // This is how you can
       final int minAgeForDriveCar = 18;

       // or you can do it this way, in the foreach loop:
       for (final String arg : args) {
           System.out.println(arg);
       }
   }

}
final我们可以在扩展循环中 使用关键字for,因为完成循环迭代后,for每次都会创建一个新变量。但这不适用于普通的 for 循环,因此下面的代码将引发编译时错误。
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
   System.out.println(args[i]);
}

最后一堂课

您不能扩展声明为 的类final。简而言之,没有任何类可以继承这个类。finalJDK 中的类的一个很好的例子是String. 创建不可变类的第一步是将其标记为final,以便它无法扩展:
public final class FinalExample {
}

// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}

最终方法

当一个方法被标记为final时,它被称为final方法(逻辑上的,对吧?)。Final 方法不能在后代类中被重写。顺便说一句,Object 类中的方法 - wait() 和 notification() - 是最终方法,因此我们没有机会重写它们。
public class FinalExample {
   public final String generateAddress() {
       return "Some address";
   }
}

class ChildOfFinalExample extends FinalExample {

   // compile error here
   @Override
   public String generateAddress() {
       return "My OWN Address";
   }
}

在 Java 中如何以及在何处使用 Final

  • 使用final关键字定义一些类级别的常量;
  • 当您不希望对象被修改时,为对象创建最终变量。例如,我们可以用于记录目的的特定于对象的属性;
  • 如果您不想延长该课程,请将其标记为最终课程;
  • 如果你需要创建一个不可变的<类,你需要将其设为final;
  • 如果您希望方法的实现在其后代中不发生更改,请将该方法指定为final。这对于确保实施不会改变非常重要。

23.什么是可变不可变?

可变的

可变对象是其状态和变量在创建后可以更改的对象。例如StringBuilder、StringBuffer等类。例子:
public class MutableExample {

   private String address;

   public MutableExample(String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   // this setter can change the name field
   public void setAddress(String address) {
       this.address = address;
   }

   public static void main(String[] args) {

       MutableExample obj = new MutableExample("first address");
       System.out.println(obj.getAddress());

       // update the name field, so this is a mutable object
       obj.setAddress("Updated address");
       System.out.println(obj.getAddress());
   }
}

不可变的

不可变对象是指在创建对象后其状态和变量不能更改的对象。为什么 HashMap 没有一个很好的键,对吧?)例如 String、Integer、Double 等等。例子:
// make this class final so no one can change it
public final class ImmutableExample {

   private String address;

   ImmutableExample (String address) {
       this.address = address;
   }

   public String getAddress() {
       return address;
   }

   //remove the setter

   public static void main(String[] args) {

       ImmutableExample obj = new ImmutableExample("old address");
       System.out.println(obj.getAddress());

       // Therefore, do not change this field in any way, so this is an immutable object
       // obj.setName("new address");
       // System.out.println(obj.getName());

   }
}

24.如何编写不可变类?

弄清楚什么是可变对象和不可变对象后,下一个问题就很自然了——怎么写?要编写一个不可变的不可变类,您需要遵循简单的步骤:
  • 使课程成为最终的。
  • 将所有字段设为私有并只为它们创建 getter。当然,不需要设置器。
  • 将所有可变字段设为最终值,以便该值只能设置一次。
  • 通过构造函数初始化所有字段,执行深复制(即复制对象本身、其变量、变量的变量等)
  • 在 getter 中克隆可变变量对象以仅返回值的副本,而不是对实际对象的引用。
例子:
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {

   private final int age;

   private final String name;

   private final HashMap<String, String> addresses;

   public int getAge() {
       return age;
   }


   public String getName() {
       return name;
   }

   /**
    * Clone the object before returning it.
    */
   public HashMap<String, String> getAddresses() {
       return (HashMap<String, String>) addresses.clone();
   }

   /**
    * In the constructor, deep copy the mutable objects.
    */
   public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
       System.out.println("Performing a deep copy in the constructor");
       this.age = age;
       this.name = name;
       HashMap<String, String> temporaryMap = new HashMap<>();
       String key;
       Iterator<String> iterator = addresses.keySet().iterator();
       while (iterator.hasNext()) {
           key = iterator.next();
           temporaryMap.put(key, addresses.get(key));
       }
       this.addresses = temporaryMap;
   }
}
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION