你见过老外的 Java 面试题吗(下)?

前言

上一篇文章总结了 老外常见的 Java 面试题上,如果有感兴趣的同学可以点击查看,接下来补上下半部。

正文

finalize 方法调用了多少次

关于 finalize 总结了以下几点:
  • finalize() 方法由垃圾收集器调用。对于每个对象,垃圾收集器只调用 finalize() 方法一次
  • finalize() 是 Object 的 protected 方法,子类可以覆盖该方法以实现资源清理工作,GC 在回收对象之前调用该方法。
  • finalize() 与 C++ 中的析构函数不是对应的。C++ 中的析构函数调用的时机是确定的(对象离开作用域或delete 掉),但 Java 中的 finalize 的调用时机是不确定的
如何理解 finalize() 的调用时机?

当垃圾回收器要宣告一个对象死亡时,至少要经过两次标记过程:

  • 如果对象在进行可达性分析后发现没有和 GC Roots 相连接的引用链,就会被第一次标记,并且判断是否执行 finalize() 方法。
  • 如果对象覆盖 finalize( ) 方法且未被虚拟机调用过,那么这个对象会被放置在 F-Queue 队列中,并在稍后由一个虚拟机自动建立的低优先级的 Finalize 线程区执行触发 finalize() 方法,但不承诺等待其运行结束。

finalization 的目的:对象逃脱死亡的最后一次机会。(只要重新与引用链上的任何一个对象建立关联即可。)但是不建议使用,运行代价高昂,不确定性大,且无法保证各个对象的调用顺序。

可用try-finally或其他替代。

this 和 super 关键字可以同时调用吗

  • super 从子类中调用父类的构造方法,this() 在同一类内调用其它方法。
  • 尽管可以用 this 调用一个构造器,但却不能调用两个
  • this 和 super 不能同时出现在一个构造函数里面,因为 this 必然会调用其它的构造函数其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义
  • 从本质上讲,this 是一个指向本对象的指针, 然而 super 是一个Java关键字

你能在执行 main() 方法之前运行一个代码吗

在 Java 中,如果想在 main() 方法之前运行一段代码,只需要定义一个 static {} 静态代码块,就可以让 static 里的代码执行在 main() 方法之前。

public class staticFactory {
    static {
        System.out.println("static");
    }

    public static void main(String[] args) {
        System.out.println("main");
    }
}
// 输出
// static  main

为什么 Java 本质上是动态的

Java 本质为静态语言,而不是动态语言。动态语言显著的特点是在程序运行时,可以改变程序结构或变量类型,典型的动态语言有 Python、ruby、javascript 等。Java 不是动态语言,但 Java 具有一定的动态性,表现在以下几个方面:

  • 反射机制
  • 动态字节码操作
  • 动态编译
  • 执行其他脚本代码

解释下守护进程线程

在Java中有两类线程:User Thread(用户线程)Daemon Thread(守护线程)

  • 守护线程 是高优先级线程。JVM 会在终止之前等待任何用户线程完成其任务
  • 用户线程 是低优先级线程。其唯一作用是为用户线程提供服务。

用个比较通俗的比如,任何一个守护线程都是整个 JVM 中所有非守护线程的保姆:

只要当前 JVM 实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

User 和 Daemon 两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread 已经全部退出运行了,只剩下 Daemon Thread 存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon 也就没有工作可做了,也就没有继续运行程序的必要了。

如何实现一个守护线程?
NewThread daemonThread = new NewThread();
daemonThread.setDaemon(true);
daemonThread.start();

使用守护线程有几点需要注意:

  • thread.setDaemon(true) 必须在 thread.start() 之前设置,否则会跑出一个 IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
  • 在 Daemon 线程中产生的新线程也是 Daemon 的。
  • 不要认为所有的应用都可以分配给 Daemon 来进行服务,比如读写操作或者计算逻辑。

解释 >> 和 >>> 运算符之间的区别

虽然它们看上去比较像,但两者之间区别还是很大的。

>> 按位右移运算符。左操作数按位右移右操作数指定的位数。 即:A >> 2得到15即 1111
>>> 按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。即:A>>>2得到15即0000 1111

我们能重载一个静态方法吗

java 的静态方法不能被重写。 静态成员(方法或属性)是类的成员存放在栈中,类可以直接调用(是属于类的静态成员,当然对象也可以调用,只是说你可以使用而已);实例成员是对象的成员,存放在堆中,只能被对象调用。 重写的目的在于根据创造对象的所属类型不同而表现出多态

总结:

以上就是总结的剩余的面试题,其实和我们平时的面试题还是有一些差距。不过在整理面试题的时候,有一个题的答案非常有争议,网上也是怎么讨论的都有,所以准备单独拿出有争议的去分析。