大家好!今天我继续回顾 Java 开发者面试问题。 探索 Java 开发人员职位面试中的问题和答案。 第 4 - 1 部分

29. return 可以在构造函数中使用吗?

是的,但只有return关键字右侧没有值。您可以使用返回;作为构造函数中的辅助语句,用于紧急终止(中断)进一步代码的执行并完成对象的初始化。例如,假设我们有一个Cat类,如果一只Cat无家可归(isHomeless = true,那么我们想要终止初始化并且不填写其他字段(毕竟,它们对我们来说是未知的,因为猫无家可归) :
public Cat(int age, String name, boolean isHomeless) {
   if (isHomeless){
       this.isHomeless = isHomeless;
       return;
   }
   this.isHomeless = isHomeless;
   this.age = age;
   this.name = name;
}
但如果我们谈论的是具体值,那么return关键字就不能返回特定值,因为:
  • 当你声明一个构造函数时,你不会有像返回类型这样的东西;
  • 通常,构造函数在实例化期间被隐式调用;
  • 构造函数不是方法:它是一个单独的机制,其唯一目的是初始化实例变量,即我们使用 new运算符来创建对象。
探索 Java 开发人员职位面试中的问题和答案。 第 4 - 2 部分

30. 构造函数可以抛出异常吗?

构造函数处理异常的方式与方法相同。方法允许我们通过在方法头中写入throws <ExceptionType>来抛出异常。构造函数允许我们做同样的事情。当我们继承并定义子类的构造函数时,我们可以扩大异常类型 - 例如,IOException -> Exception(但反之则不然)。让我们使用Cat类的构造函数作为构造函数抛出异常的示例。假设当我们创建一个对象时,我们想从控制台输入名称和年龄:
public Cat() throws IOException {
   BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
   this.name = reader.readLine();
   this.age = Integer.parseInt(reader.readLine());
}
由于reader.readLine()抛出 IOException,我们将其作为可能抛出的异常写入标头中。

31. 类头的元素是什么?写一个例子

为了说明构成类头的元素,让我们看一个小模式:
  • 强制元素出现在括号 <> 中
  • 可选元素位于 {} 中
{访问修饰符}{静态}{final}{抽象}<类名>{父类的继承}{接口的实现}所以,我们有:{ 访问 修饰符} - 只有公共默认访问修饰符可用于班级。 {static}static修饰符表明该类是静态的;它仅适用于内部类(其他类中的类)。 {final} — 当然,这是最终修饰符,它使类不可继承(开箱即用的示例是String)。 {abstract}抽象修饰符,表示该类可能具有未实现的方法。该修饰符与最终修饰符冲突。类头只能有其中之一,因为abstract修饰符意味着该类将被继承并且其抽象元素将被实现。但final表示这是该类的最终版本,并且不能被继承。实际上,同时使用这两个修饰符是荒谬的。编译器不会让我们这样做。 <class>是一个强制关键字,表示类声明。 <class name>是一个简单的类名,它成为特定 Java 类的标识符。完全限定的类名由限定的包名称加上“.”组成。加上简单的类名。 {父类的继承}是使用extends关键字指示父类(如果有)。例如,...扩展 ParentClass{接口的实现} — 该类使用implements关键字实现的接口列表(如果有)。例如: ...实现 FirstInterface、SecondInterface ... 作为示例,请考虑Lion类的类标题,该类继承Cat并实现WildAnimal接口:
public final class Lion extends Cat implements WildAnimal
探索 Java 开发人员职位面试中的问题和答案。 第 4 - 3 部分

32. 方法头的元素是什么?写一个例子

在考虑构成方法头的元素时,让我们再次考虑一个小模式:
  • 强制元素出现在括号 <> 中
  • 可选元素位于 {} 中
{访问修饰符}{static}{abstract}{final}{synchronized} {native} <返回值><方法名称> <(>{方法参数}<}>{抛出异常} {访问修饰符} — 所有访问 修饰符都是可用于方法 — publicprotecteddefaultprivate{static}static修饰符,表示该方法是静态的,因此与类相关联,而不是对象。 {abstract}抽象修饰符,表示该方法没有实现(主体)。为了正确工作,声明该方法的类还必须具有abstract修饰符。如在类头中,该修饰符与final修饰符冲突,也与static修饰符冲突,因为抽象方法意味着重写后代中方法,静态 方法不能被 重写。从不同的线程同时访问它。如果该方法不是静态的,则该方法对于对象的 this 互斥锁是关闭的。如果该方法是静态的,那么它对于当前类的互斥锁是关闭的。 {native}native修饰符指示该方法是用另一种编程语言编写的。 <return type> — 方法必须返回的值的类型。如果该方法不返回任何内容,则返回 void<方法名称> ——方法名称的名称,即它在系统中的标识符。 {方法参数} — 方法接受的参数:它们是实现其功能所必需的。 {抛出的异常}抛出 <ExceptionType> — 此方法可以抛出的已检查异常的列表。我将提供以下内容作为方法标头的示例:
public static void main(String[] args) throws IOException

33. 如果基类中尚未定义默认构造函数(但定义了不同的构造函数),则在子类中创建默认构造函数

我不确定我完全理解这个问题,但这也许意味着我们在父类中有一些像这样的构造函数:
public Cat(int age, String name) {
   this.age = age;
   this.name = name;
}
在这种情况下,在父类中,我们肯定需要定义一个构造函数来初始化父类(即调用父构造函数):
public class Lion extends Cat {

   public Lion(int age, String name) {
       super(age, name);
   }
}
探索 Java 开发人员职位面试中的问题和答案。 第 4 - 4 部分

34.什么时候使用this关键字?

在 Java 中,有两种不同的含义。1. 它是对当前对象的引用,例如this.age = 9。也就是说,this指的是使用它的对象以及带有this 的代码所指的对象。主要目的是提高代码可读性并避免歧义。例如,如果实例字段和方法参数具有相同的名称:
public void setName(String name) {
   this.name = name;
}
也就是说,this.name是对象的字段,而name是方法参数。this引用不能在静态方法中使用。2. 在构造函数中,this可以像方法一样被调用,例如this(value)。在这种情况下,它将调用同一类的另一个构造函数。基本上,您可以在创建对象的过程中调用两个构造函数:
public Cat(int age, String name) {
   this(name);
   this.age = age;
}

public Cat(String name) {
   this.name = name;
}
当调用第一个构造函数创建Cat对象时,两个实例字段都将成功初始化。这里有一些细微差别:
  1. this()仅适用于构造函数。
  2. 对另一个构造函数的引用必须位于构造函数块(主体)的第一行。这意味着构造函数不能调用其类的多个(其他)构造函数。
探索 Java 开发人员职位面试中的问题和答案。 第 4 - 5 部分

35.什么是初始化器?

据我了解,这个问题是关于普通和静态初始化块的。让我们首先记住什么是初始化。初始化是字段的创建、激活、准备和定义。准备程序或组件以供使用。你会记得,当你创建一个对象时,类变量可以在声明时立即初始化:
class Cat {
   private int age = 9;
   private String name = "Tom";
或者通过构造函数事后设置:
class Cat {
   private int age;
   private String name;

   public Cat(int age, String name) {
       this.age = age;
       this.name = name;
   }
但还有另一种方法:您可以使用初始化块设置实例变量,该初始化块在类中 采用大括号{} 的形式,没有名称(如无名方法或构造函数):
class Cat {
   private int age;
   private String name;

   {
       age = 10;
       name = "Tom";
   }
初始化块是创建对象时加载的一段代码。此类块通常用于执行加载类时所需的某些复杂计算。这些计算的结果可以设置为变量的值。除了普通的初始化块之外,还有静态的初始化块。它们看起来相同,但在左大括号前面 有static关键字:
class Cat {
   private static int age;
   private static String name;

   static{
       age = 10;
       name = "Tom";
   }
此块与前一个块相同。但是,如果普通对象在初始化每个对象时执行,则静态对象仅在类加载时执行一次。通常,某些复杂的计算是在静态块中执行的,用于初始化静态类变量。同样的限制也适用于静态方法的静态块:不能使用非静态数据,例如静态块中 对当前对象 ( this ) 的引用。探索 Java 开发人员职位面试中的问题和答案。 第 4 - 6 部分现在我们可以查看类(及其父类)的初始化顺序,以便更好地理解何时调用初始化块。

36.给定一个扩展Parent的公共Child类,写出该对象的初始化顺序

当加载Child类时,初始化顺序如下:
  1. 类的静态类字段。
  2. 类的静态初始化块。
  3. Сchild类的静态字段。
  4. Child类的静态初始化块。
  5. 类的非静态字段。
  6. 类的非静态初始化块。
  7. 类构造函数。
  8. Сhild类的非静态字段。
  9. Сhild类的非静态初始化块。
  10. Сchild类的构造函数。
探索 Java 开发人员职位面试中的问题和答案。 第 4 - 7 部分

37.你知道类(对象)之间有哪些关系?

Java 中有两种变量:原始类型和对成熟对象的引用。
  • IS-A关系
OOP 的 IS-A 原则基于类继承或接口实现。例如,如果Lion类继承Cat,那么我们说LionCat
Lion IS-A Cat
(但不是每只猫都是狮子)接口也存在同样的情况。如果Lion类实现了WildAnimal接口,那么它们也存在关系:
Lion IS-A WildAnimal
  • HAS-A关系
这种类型的关系是一个类使用其他类的关系,也称为“关联”。关联是一个类引用另一个类(或相互引用)。例如,Car类可以引用Passenger类,这将构成以下关系:
Car HAS-A Passenger
反之亦然:如果Passenger引用了Car,那么关系如下:
Passenger HAS-A Car

38.你知道哪些关联对象关系?

聚合和组合只不过是关联的特殊情况。 聚合是一种关系,其中一个对象是另一个对象的一部分。例如,乘客可能位于汽车内。更重要的是,可能有多名乘客,或者根本没有乘客(如果我们谈论的是特斯拉,可能没有司机)。例如:
public class Car {
   private List passengers = new ArrayList<>();

 void setPassenger(Passenger passenger) {
     passengers.add(passenger);
 }

   void move() {
       for (Passenger passenger : passengers) {
           System.out.println("Transporting passenger - " + passenger.toString());
       }
       passengers.clear();
   }
}
换句话说,乘客数量(任何数量)对我们来说并不重要:Car类的功能不依赖于此。聚合还意味着当另一个对象使用一个对象时,第一个对象可以被其他对象使用。例如,同一个学生可能同时参加针织俱乐部和摇滚乐队,并同时参加西班牙语课程。正如你可以想象的,聚合是类之间更松散的关联关系。 组合是一种更紧密的关系,其中一个对象不仅是另一个对象的一部分,而且一个对象的工作非常依赖于另一个对象。例如,汽车有发动机。发动机可以在没有汽车的情况下存在,但离开汽车就毫无用处。没有发动机汽车就无法工作:
public class Car {
   private Engine engine;

   public Car(Engine engine) {
       this.engine = engine;
   }

   void startMoving() {
       engine.start();
           ...
   }
组合还意味着当另一个对象使用一个对象时,第一个对象不能属于任何其他对象。回到我们的例子,一台发动机只能属于一辆汽车,不能同时属于两辆或更多汽车。我想今天的内容就足够了,所以我们就到此为止。