并非所有的访谈都将重点放在算法和数据结构上—通常,访谈通常只侧重于您声称是专家的语言或技术。在此类访谈中,通常没有任何“陷阱”问题,而是它们要求您利用内存和使用该语言的经验–换句话说,它们测试您对编程语言的了解。
但是,忘记像Java这样的语言的所有来龙去脉很容易,因为简单地说,我们不会处理诸如“ JVM管理哪种内存?”这样的问题。 和“用示例描述多态”。 以一天为周期。
这篇文章概述了Java访谈中提出的一些常见问题。 由于特定于Java的问题可能相差很大,因此本文旨在指导您思考各种不同类型的问题以及应该准备的主题。
今天,我们将讨论与以下内容有关的面试问题和答案:
- Java生态系统
- Java类
- 介面
- 遗产
- 多线程
- 内存管理
- 馆藏
- 异常处理
- 序列化
- 辛格尔顿
让我们开始吧!
问题1:Java独立于平台意味着什么?
Java按照一次编写即可在任何地方运行的原则工作。 编写Java程序后,它将被编译为字节码,然后可以在任何Java虚拟机或JVM上运行。
字节码的编译是Java互操作性背后的魔力。 不同的操作系统和硬件体系结构具有为自己定制的JVM,并且所有JVM都可以运行相同的字节码。 因此,如果您在Linux上编写Java程序,它将在为Windows操作系统设计的JVM上无缝运行,从而使代码与基础硬件和操作系统无关。
Q2:解释JRE,JDK和JVM的概念
- JRE(Java运行时环境)包括Java虚拟机和标准Java API(核心类和支持文件)。 JRE包含的内容足以执行Java应用程序,但不足以对其进行编译。
- JDK(Java开发工具包)是JRE加上Java编译器,以及一组用于编译和调试代码的其他工具。 JRE由Java平台库,Java虚拟机(JVM),Java插件和Java Web Start组成,以运行Java应用程序。 JRE作为独立版本不包含编译器和调试工具。 如果需要开发Java程序,则需要完整的Java SDK。 JRE不足以进行程序开发。 只有完整的Java SDK包含Java编译器,该编译器会将您的.java源文件转换为字节码.class文件。
- JVM(Java虚拟机)是规范的实现,详细描述了JVM的预期行为。 任何符合JVM规范的实现都应该能够运行编译为Java字节码的代码,而与最初编写该代码的语言无关。 在Java编程语言中,所有源代码都首先以纯文本文件编写,扩展名为.java。 然后,这些源文件由javac编译器编译为.class文件。 .class文件不包含处理器本身的代码。 相反,它包含字节码-Java虚拟机的机器语言。 然后,Java启动器工具将使用Java虚拟机的实例运行您的应用程序。
问题3:如何在Java中将实体包标记为私有?
没有私有包的显式修饰符。 在没有任何修饰符的情况下,类或成员变量是程序包私有的。 标记为包私有的成员仅在其自己的包中可见。 考虑下面的类。
私有包是私有的一种较宽泛的形式。 关于package-private的一件好事是,您可以使用它来授予对单元测试类本来认为私有的方法的访问权。 因此,如果您使用没有其他用途的帮助程序类,而是为了帮助您的公共类完成客户端需要做的事情,则有必要将它们打包为私有,这是很有意义的,因为您希望使库用户尽可能简单。
有哪些替代方案?
Object类提供了一个回调方法finalize(),当该对象变为垃圾时可以在该对象上调用该方法。 对象的finalize()实现没有任何作用-您可以覆盖finalize()进行清理,例如释放资源。
finalize()方法可由系统自动调用,但是何时调用,即使调用,也不确定。 因此,您不应依赖此方法为您进行清理。 例如,如果您在执行I / O之后没有在代码中关闭文件描述符,并且您希望finalize()为您关闭它们,则文件描述符可能用完了。
以下是一些替代方案:
- try-with-resources惯用法可以用来清理对象。 这需要实现AutoCloseable接口。
- 垃圾回收对象时使用PhantomReference进行清理
- 使用Cleaner类执行清除操作。
- 实现close()方法,该方法进行清理并记录该方法的调用。
Q5:是否可以按下面的代码片段所示更改最终数组的内容?
final int[] array = new int[5];
array[0] = 1;
它可能看起来违反直觉,但实际上我们可以更改数组的内容,即使它被标记为final。 数组变量指向内存中放置数组内容的特定起始位置。 位置或内存地址无法更改。 例如,以下代码将无法编译:
final int[] array = new int [5]
array = new int[10];
但是,以下代码将起作用。
public class FinalArrayExample {final int[] array = new int[5];// allowedvoid changeArrayContents(int i, int val) {array[i] = val;}// not allowed and will not compile/*void changeArray() {array = new int [10]}*/}
什么时候应该使用其中一个?
抽象类不能被实例化,但是可以被子类化。 抽象类通常包含抽象和非抽象方法,子类被迫为其提供实现。
接口是完全“抽象类”,用于将相关方法与空主体组合在一起。
以下是抽象类和接口之间的四个主要区别:
- 抽象类可以具有最终变量,静态变量或类成员变量,而接口只能具有默认为final和static的变量。
- 抽象类可以具有静态,抽象或非抽象方法。 接口可以具有静态,抽象或默认方法。
- 抽象类的成员可以具有对私有,受保护或公共的不同可见性。 而在接口中,所有方法和常量都是公共的。
- 一个类只能扩展另一个类,但是可以实现多个接口。 同样,一个接口可以扩展多个接口。 接口从不实现类或接口。
当子类共享状态或使用通用功能时,请使用抽象类。 或者,您需要声明非静态,非最终字段,或者需要除public之外的访问修饰符。
如果您期望不相关的类将实现您的接口,请使用接口。 例如,接口Comparable和Cloneable由许多不相关的类实现。 接口还用于需要多重继承类型的实例。
你能给个例子吗?
多态是在编程中针对不同的基础形式或数据类型呈现相同接口的能力。 多态是指您可以将对象视为某物的通用版本,但是当您访问它时,代码将确定其确切的类型并调用关联的代码。 这意味着多态性使您的代码可以与不同的类一起使用,而无需知道它正在使用哪个类。
多态性用于使应用程序更具模块化和可扩展性。 您可以创建根据需要选择的可互换对象,而不必使用混乱的条件语句来描述不同的操作过程。 这是多态性的基本目标。
多态的经典示例是Shape
类。 我们从父类Shape
派生Circle
, Triangle
和Rectangle
类,它公开了抽象方法draw()。 派生类为draw()
方法提供了其自定义实现。 现在,通过在每个对象上调用draw()
方法,很容易呈现所有包含在同一数组中的不同类型的形状。 这使我们drawTriangle()
为每种形状创建单独的绘制方法,例如drawTriangle()
, drawCircle()
等。
Q8:主要方法可以重载吗?
是的,主要方法是静态方法,可以重载。 但是,即使您指定一个或两个命令行参数,当JVM启动您的类时,也只会使用public static void main(String[] args)
。 但是,可以以编程方式调用main方法的重载版本。
问题9:如何在每次调用调用时将多个参数传递给方法?
我们可以使用varargs功能将可变数量的参数传递给方法。 下面是将相同类型的多个参数传递给方法的示例。
public void childrenNames(string... names) {for(int i= 0; i < names.length; i++)system.out.println(names[i]);}
- 类型名称后跟三个点,一个空格,然后是变量名称。
- varargs变量被视为数组。
- varargs变量必须出现在方法签名的最后。
- 由于上述原因,方法签名中只能有一个变量。
可以按以下方式调用上述方法: 调用Varargs方法
childrenNames();
childrenNames("Jane");
childrenNames("Jane", "Tom", "Peter");
Q10:信号量可以用作互斥量吗?
如果信号量可以发出的许可数设置为1,则它有可能充当互斥量。但是,两者之间最重要的区别是,对于互斥量,同一线程必须调用获取并随后释放。互斥锁,而在二进制信号量的情况下,不同的线程可以在信号量上调用获取和释放。
这将我们引向“所有权”的概念。 互斥锁由获取它的线程拥有,直到它释放为止,而对于信号量则没有所有权的概念。
需要多线程复习吗? 请查看本文“ Java多线程和并发:进行高级工程访谈时应了解的知识”。
Q11:解释可外部化的界面
Serializable接口为我们的类对象提供了自动序列化功能。 另一方面,Externalizable接口提供了一种实现自定义序列化机制的方法。 实现Externalizable接口的类负责保存和恢复其自己实例的内容。
Externalizable接口扩展了Serializable接口,并提供了两种方法来序列化和反序列化对象: writeExternal()
和readExternal()
。
问题12:如果一个代码块引发多个异常,该如何处理?
一段代码片段引发的多种类型的异常可以由多个catch块子句后跟try块来处理。 下面是一个示例异常处理片段:
oid process(int val) {try {if (val == 1)//checked exceptionthrow new FileNotFoundException();if (val == 2)// runtime exceptionthrow new NullPointerExxception();if (val == 3)// error exceptionthrow new StackOverflowError} catch (RuntimeException re) {// catches all unchecked exceptions} catch (Exception e) {// catches all checked exceptions} catch (Error err) {// catches all errors}}
问题13:如果要使用集合,如何确定HashSet和TreeSet之间?
最初,您可能希望使用HashSet,因为它会给您带来更好的时间复杂度,但不能保证该集的迭代顺序。 特别是,它不能保证顺序会随着时间的推移保持恒定。
因此,如果您想保持顺序,最好使用TreeSet,因为它以升序而不是按插入顺序存储键。 它不是线程安全的。 但是,请记住,TreeSet不是线程安全的,而HashSet是线程安全的。
问题14:可以通过哪些方法来改善Java应用程序的内存占用?
您可以采取以下三个关键步骤来改善内存占用量:
- 限制局部变量的范围。 每次弹出堆栈顶部的作用域时,该作用域中的引用都会丢失,这会使对象有资格进行垃圾回收。
- 不需要时将变量引用显式设置为null。 这将使对象有资格进行垃圾回收。
- 避免终结器。 它们会降低程序性能,并且不能保证任何事情。
问题15:实现单例类的最佳方法是什么?
根据乔什·布洛赫(Josh Bloch)实现单例的最佳方法是为单例使用枚举类型。 因为Java确保仅创建一个枚举的单个实例,所以通过枚举实现的单例类可以免受反射和序列化攻击。
class Demonstration {public static void main( String args[] ) {Superman superman = Superman.INSTANCE;superman.fly();}
}enum Superman {INSTANCE;private final String name = "Clark Kent";private String residence = "USA";public void fly() {System.out.println("I am flyyyyinggggg ...");}
}
精通
这篇文章涵盖了很多有关Java编程语言的内容,从Java生态系统(问题1)到多线程(问题10)和异常(问题12)不等。 这些是您可以期望的Java面试问题的类型。 最好使用上面概述的材料作为您要学习的主题和可能遇到的问题类型的指南。
但是,这里的材料只是划伤表面。 还有更多的概念需要重新研究或探索,例如面向对象的编程,静态变量和方法重载。
学习愉快! 家
翻译自: https://www.javacodegeeks.com/2019/12/java-interview-prep-15-java-interview-questions.html