JavaRush /Java 博客 /Random-ZH /Java 开发人员访谈问答分析。第 14 部分

Java 开发人员访谈问答分析。第 14 部分

已在 Random-ZH 群组中发布
焰火!世界在不断变化,我们也在不断变化。以前,要成为一名Java开发人员,了解一点Java语法就足够了,剩下的就来了。随着时间的推移,成为 Java 开发人员所需的知识水平显着提高,竞争也不断加剧,这继续将所需知识的下限推高。 Java 开发人员访谈问答分析。 第 14 - 1 部分如果你真的想成为一名开发者,你需要理所当然地做好充分的准备,才能在像你这样的初学者中脱颖而出,我们今天要做的就是继续分析这250+个问题。在之前的文章中,我们讨论了所有初级级别的问题,今天我们将讨论中级级别的问题。虽然我注意到这些不是100%的中级问题,但你可以在初级面试中遇到大部分问题,因为正是在这样的面试中,对你的理论基础进行了详细的探究,而对于中学生来说问题更侧重于探究他的经历。 Java 开发人员访谈问答分析。 第 14 - 2 部分但是,事不宜迟,让我们开始吧。

中间

是常见的

1. 与过程式/函数式编程相比,OOP 的优点和缺点是什么?

Juinior的题分析中有这个问题,所以我已经回答了。在本文的 这一部分(问题 16 和问题 17)中查找这个问题及其答案。

2. 聚合与组合有何不同?

在 OOP 中,对象之间存在多种类型的交互,统一在“Has-A 关系”的一般概念下。这种关系表明一个对象是另一个对象的组件。同时,这种关系有两种子类型: 组合- 一个对象创建另一个对象,另一个对象的生命周期取决于创建者的生命周期。 聚合——一个对象在构造过程中接收到另一个对象的链接(指针)(在这种情况下,另一个对象的生命周期不依赖于创建者的生命周期)。为了更好地理解,我们来看一个具体的例子。我们有一个特定的汽车类 - Car,它又具有类型的内部字段 - Engine乘客列表 - List<Passenger>,它还有一个启动移动的方法 - startMoving()
public class Car {

 private Engine engine;
 private List<Passenger> passengers;

 public Car(final List<Passenger> passengers) {
   this.engine = new Engine();
   this.passengers = passengers;
 }

 public void addPassenger(Passenger passenger) {
   passengers.add(passenger);
 }

 public void removePassengerByIndex(Long index) {
   passengers.remove(index);
 }

 public void startMoving() {
   engine.start();
   System.out.println("Машина начала своё движение");
   for (Passenger passenger : passengers) {
     System.out.println("В машине есть пассажир - " + passenger.getName());
   }
 }
}
在这种情况下,Composition是CarEngine之间的连接,因为汽车的性能直接取决于引擎对象的存在,因为如果engine = null,那么我们将收到一个NullPointerException。反过来,如果没有机器,引擎就无法存在(为什么我们需要没有机器的引擎?),并且不能在一个时间点属于多个机器。这意味着,如果我们删除Car对象,将不再有对Engine对象的引用,并且它很快就会被垃圾收集器删除。正如您所看到的,这种关系非常严格(强)。 聚合是CarPassenger之间的连接,因为Car的性能绝不取决于Passenger类型的对象及其数量。他们可以离开汽车 - removePassengerByIndex(Long index)或输入新的汽车 - addPassenger(Passenger乘客),尽管如此,汽车将继续正常运行。反过来,Passenger对象可以在没有Car对象的情况下存在。如您所知,这种联系比我们在构图中看到的要弱得多。 Java 开发人员访谈问答分析。 第 14 - 3 部分但这还不是全部,通过聚合连接到另一个对象的对象也可以在同一时间点与其他对象具有给定的连接。例如,您作为一名 Java 学生,同时注册了英语、OOP 和对数课程,但同时您并不是其中至关重要的一部分,没有这些课程就不可能正常运作(例如一位老师)。

3. 您在实践中使用过哪些 GoF 模式?举例说明。

这个问题我之前已经回答过,所以就留下分析链接,看第一个问题。我还发现了一篇关于设计模式的精彩备忘单文章,我强烈建议您保留它。

4.什么是代理对象?举例说明

代理是一种结构设计模式,允许您替换特殊的替代对象,或者换句话说,代理对象,而不是真实的对象。这些代理对象拦截对原始对象的调用,允许在调用传递给原始对象 之前之后插入一些逻辑。Java 开发人员访谈问答分析。 第 14 - 4 部分使用代理对象的示例:
  • 作为远程代理 - 当我们需要需要在本地表示的远程对象(不同地址空间中的对象)时使用。在这种情况下,代理将处理连接创建、编码、解码等,而客户端将使用它,就好像它是位于本地空间的原始对象一样。

  • 作为虚拟代理 - 在需要资源密集型对象时使用。在这种情况下,代理对象就像实际还不存在的真实对象的图像一样。当真正的请求(方法调用)发送到该对象时,才会加载原始对象并执行该方法。这种方法也称为惰性初始化;它可以非常方便,因为在某些情况下原始对象可能没有用,那么创建它就不需要任何成本。

  • 作为安全代理 - 当您需要根据客户端权限控制对某些对象的访问时使用。也就是说,如果缺少访问权限的客户端尝试访问原始对象,代理将拦截它并且不允许它。

让我们看一个虚拟代理的示例:我们有一些处理程序接口:
public interface Processor {
 void process();
}
其实现使用了太多资源,但同时可能不会在每次启动应用程序时都使用:
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
代理类:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
让我们在main中运行它:
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
我注意到许多框架都使用代理,对于Spring来说这是一个关键模式(Spring 内外都与它缝合在一起)。请在此处阅读有关此模式的更多信息。 Java 开发人员访谈问答分析。 第 14 - 5 部分

5. Java 8 宣布了哪些创新?

Java 8带来的创新如下:
  • 添加了功能接口,了解一下这是什么样的野兽

  • Lambda 表达式与函数式接口密切相关,请在此处阅读有关其使用的更多信息。

  • 添加了Stream API以方便处理数据集合,请在此处阅读更多内容。

  • 添加了方法的链接

  • forEach()方法已添加到Iterable接口中。

  • java.time包中添加了新的日期和时间API,详细分析见这里

  • 改进了并发 API

  • 添加一个可选包装类,用于正确处理空值,您可以在此处找到有关此主题的优秀文章。

  • 添加接口使用静态默认方法的能力(本质上,这使 Java 更接近多重继承),更多详细信息请参见此处

  • Collection(removeIf(), spliterator())类添加了新方法。

  • 对 Java Core 的细微改进。

Java 开发人员访谈问答分析。 第 14 - 6 部分

6. 什么是高内聚和低耦合?举例说明。

高内聚高内聚是指某个类包含彼此密切相关并为其目的组合的元素的概念。例如,User类中的所有方法都应该代表用户行为。如果类包含不相关的元素,则该类的内聚性较低。例如,包含电子邮件地址验证方法的 User类:
public class User {
private String name;
private String email;

 public String getName() {
   return this.name;
 }

 public void setName(final String name) {
   this.name = name;
 }

 public String getEmail() {
   return this.email;
 }

 public void setEmail(final String email) {
   this.email = email;
 }

 public boolean isValidEmail() {
   // некоторая логика валидации емейла
 }
}
用户类可能负责存储用户的电子邮件地址,但不负责验证它或发送电子邮件。因此,为了实现高一致性,我们将验证方法移至单独的实用程序类中:
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
我们根据需要使用它(例如,在保存用户之前)。 低耦合低耦合是描述软件模块之间低相互依赖性的概念。从本质上讲,相互依赖是指改变一方就需要改变另一方。如果两个类密切相关,则它们具有强耦合(或紧耦合)。例如,两个具体类存储彼此的引用并调用彼此的方法。松散耦合的类更容易开发和维护。由于它们彼此独立,因此可以并行开发和测试。而且,它们可以更改和更新,而不会相互影响。让我们看一个强耦合类的示例。我们有一些学生班:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
其中包含课程列表:
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
每节课都包含一个参加学生的链接。非常强大的抓地力,你不觉得吗?你怎样才能减少它?首先,我们确保学生拥有的不是科目列表,而是其标识符列表:
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
其次,课程班级不需要了解所有学生,所以我们把他们的名单全部删除:
public class Lesson {
 private Long id;
 private String name;
}
所以事情变得容易多了,联系也变得弱了,你不觉得吗? Java 开发人员访谈问答分析。 第 14 - 7 部分

面向对象编程

7. Java中如何实现多重继承?

多重继承是面向对象概念的一项特征,其中一个类可以从多个父类继承属性。当超类和子类中存在具有相同签名的方法时,就会出现问题。当调用方法时,编译器无法确定应该调用哪个类方法,甚至在调用优先的类方法时也是如此。因此,Java不支持多重继承!但是有一种漏洞,我们接下来会讲到。正如我之前提到的,随着 Java 8 的发布,接口中添加了拥有默认方法的功能。如果实现接口的类没有重写这个方法,那么就会使用这个默认实现(没有必要重写默认方法,比如实现一个抽象方法)。在这种情况下,可以在一个类中实现不同的接口并使用它们的默认方法。让我们看一个例子。我们有一些传单界面,带有默认的Fly()方法:
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
walker 接口,具有默认的walk()方法:
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
Swimmer 接口,带有Swim()方法:
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
好吧,现在让我们在一个鸭子类中实现所有这些:
public class Duck implements Flyer, Swimmer, Walker {
}
让我们运行鸭子的所有方法:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
在控制台中我们将收到:
我去!!!我在飞!!!我在游泳!!!
这意味着我们已经正确地描述了多重继承,尽管事实并非如此。 Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 14 - 8我还注意到,如果一个类实现的接口的默认方法具有相同的方法名称和相同的参数,那么编译器将开始抱怨不兼容性,因为它不知道真正需要使用哪个方法。有几种出路:
  • 重命名接口中的方法,使它们彼此不同。
  • 在实现类中重写此类有争议的方法。
  • 从实现这些有争议的方法的类继承(然后您的类将准确地使用其实现)。

8.final、finally 和 Finalize() 方法之间有什么区别?

Final是一个关键字,用于对类、方法或变量进行约束,约束的含义是:
  • 对于变量 - 初始初始化后,不能重新定义该变量。
  • 对于方法来说,该方法不能在子类(继承类)中被重写。
  • 对于一个类——该类不能被继承。
finally是代码块之前的关键字,在处理异常时与try块结合使用,并与 catch 块一起使用(或可互换)。无论是否抛出异常,该块中的代码在任何情况下都会执行。在本文的这一部分中,在问题 104 中,讨论了该块不会被执行的特殊情况。 Finalize()是Object类的一个方法,在每个对象被垃圾收集器删除之前调用,该方法会被调用(最后),用于清理占用的资源。有关每个对象继承的Object类的方法的更多信息,请参阅本文这部分中的问题 11 。好了,今天我们就到此结束。下一部分见! Разбор вопросов и ответов с собеседований на Java-разработчика. Часть 14 - 9
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION