面试题第二弹,如有问题,欢迎指正
面试题原文链接:https://github.com/tangyouhua/program-resource/blob/master/program-interview/java-baidu-200.md
1.finalize
1.1什么是finalize()方法
finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。
finalize()与C++中的析构函数不是对应的。C++中的析构函数调用的时机是确定的(对象离开作用域或delete掉),但Java中的finalize的调用具有不确定性,不建议用finalize方法完成“非内存资源”的清理工作,但建议用于:① 清理本地对象(通过JNI创建的对象);② 作为确保某些非内存资源(如Socket、文件等)释放的一个补充:在finalize方法中显式调用其他资源释放方法。
1.2finalize()方法什么时候被调用
有三种情况
1.所有对象被Garbage Collection时自动调用,比如运行System.gc()的时候.
2.程序退出时为每个对象调用一次finalize方法。
3.显式的调用finalize方法
1.3finalize的执行过程(生命周期)
(1) 首先,大致描述一下finalize流程:当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活”。
(2) 具体的finalize流程:
对象可由两种状态,涉及到两类状态空间,一是终结状态空间 F = {unfinalized, finalizable, finalized};二是可达状态空间 R = {reachable, finalizer-reachable, unreachable}。各状态含义如下:
unfinalized: 新建对象会先进入此状态,GC并未准备执行其finalize方法,因为该对象是可达的
finalizable: 表示GC可对该对象执行finalize方法,GC已检测到该对象不可达。正如前面所述,GC通过F-Queue队列和一专用线程完成finalize的执行
finalized: 表示GC已经对该对象执行过finalize方法
reachable: 表示GC Roots引用可达
finalizer-reachable(f-reachable):表示不是reachable,但可通过某个finalizable对象可达
unreachable:对象不可通过上面两种途径可达
状态变迁图:

变迁说明:
新建对象首先处于[reachable, unfinalized]状态(A)
随着程序的运行,一些引用关系会消失,导致状态变迁,从reachable状态变迁到f-reachable(B, C, D)或unreachable(E, F)状态
若JVM检测到处于unfinalized状态的对象变成f-reachable或unreachable,JVM会将其标记为finalizable状态(G,H)。若对象原处于[unreachable, unfinalized]状态,则同时将其标记为f-reachable(H)。
在某个时刻,JVM取出某个finalizable对象,将其标记为finalized并在某个线程中执行其finalize方法。由于是在活动线程中引用了该对象,该对象将变迁到(reachable, finalized)状态(K或J)。该动作将影响某些其他对象从f-reachable状态重新回到reachable状态(L, M, N)
处于finalizable状态的对象不能同时是unreahable的,由第4点可知,将对象finalizable对象标记为finalized时会由某个线程执行该对象的finalize方法,致使其变成reachable。这也是图中只有八个状态点的原因
程序员手动调用finalize方法并不会影响到上述内部标记的变化,因此JVM只会至多调用finalize一次,即使该对象“复活”也是如此。程序员手动调用多少次不影响JVM的行为
若JVM检测到finalized状态的对象变成unreachable,回收其内存(I)
若对象并未覆盖finalize方法,JVM会进行优化,直接回收对象(O)
注:System.runFinalizersOnExit()等方法可以使对象即使处于reachable状态,JVM仍对其执行finalize方法
1.4析构函数(finalization)的目的是什么
在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。一般建议在该方法中释放对象持有的资源。
2.final
2.1final关键字有哪些用法
final修饰类:
被final修饰的类,是不可以被继承的,这样做的目的可以保证该类不被修改,Java的一些核心的API都是final类,例如String、Integer、Math等
final修饰方法:
子类不可以重写父类中被final修饰的方法
final修饰实例变量(类的属性,定义在类内,但是在类内的方法之外):
final修饰实例变量时必须初始化,且不可再修改
final修饰局部变量(方法体内的变量):
final修饰局部变量时只能初始化(赋值)一次,但也可以不初始化
final修饰方法参数:
final修饰方法参数时,是在调用方法传递参数时候初始化
2.2final 与 static 关键字可以用于哪里?它们的作用是什么
static关键字:
static方法:
1、只能调用其他的static方法
2、只能使用static变量
3、不能以任何方式引用this或者super关键字
(因为静态方法在编译时,最先被加载,所以有他的优势 以及 一定的局限性)因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
static常常在应用开发中提供一些实用工具类
static变量
按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。两者的区别是:对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
当final和static同时使用时,相当于全局常量;
3.final、finalize 和 finally 的不同之处?
一、性质不同
(1)final为关键字;
(2)finalize()为方法;
(3)finally为为区块标志,用于try语句中;
二、作用
(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中;
(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
(3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行
4.能否在运行时向 static final 类型的赋值
不能
5.使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变
使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。
6.一个类被声明为final类型,表示了什么意思
表示该类不能被继承,不能被修改
7.throws, throw, try, catch, finally分别代表什么意义
try块表示程序正常的业务执行代码。如果程序在执行try块的代码时出现了“非预期”情况,JVM将会生成一个异常对象,这个异常对象将会被后面相应的catch块捕获。
catch块表示一个异常捕获块。当程序执行try块引发异常时,这个异常对象将会被后面相应的catch块捕获。
throw用于手动地抛出异常对象。throw后面需要一个异常对象。
throws用于在方法签名中声明抛出一个或多个异常类,throws关键字后可以紧跟一个或多个异常类。
finally块代表异常处理流程中总会执行的代码块。
对于一个完整的异常处理流程而言,try块是必须的,try块后可以紧跟一个或多个catch块,最后还可以带一个finally块。
try块中可以抛出异常。
8.java 有几种修饰符?分别用来修饰什么

9.volatile
volatile让变量每次在使用的时候,都从主存中取。而不是从各个线程的“工作内存”。
volatile具有synchronized关键字的“可见性”,但是没有synchronized关键字的“并发正确性”,也就是说不保证线程执行的有序性。也就是说,volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。
9.1volatile 变量是什么?
volatile变量的意思是易变的,它的主要作用是避免编译器优化.volatile简单来讲,就是告诉编译器,在读取该变量数值的时候,应该直接从内存读取,而不是从寄存器读取。
9.2synchronized、volatile、Atomic区别和用法
可见性:指的是在一个线程中修改变量的值以后,在其他线程中能够看到这个值。
synchronized关键字除了有互斥的作用外,还有可见性的作用。synchronized保证了synchronized块中变量的可见性,而volatile则是保证了所修饰的变量的可见。因为volatile只是在保证了同一个变量在多线程中的可见性,所以它更多是用于修饰作为开关状态的变量,即Boolean类型的变量。
在jdk5中增加了java.util.concurrent.atomic包,这个包中是一些以Atomic开头的类,这些类主要提供一些相关的原子操作
Atomic修饰的类进行的操作相对于synchronized修饰的代码块会有很大性能上的提升,建议在计数器相关的多线程并发中用Atomic开头的相关类进行操作。
总结:volatile多用于修饰类似开关类型的变量、Atomic多用于类似计数器相关的变量、其它多线程并发操作用synchronized关键字修饰。
9.3volatile 类型变量提供什么保证?能使得一个非原子操作变成原子操作吗
volatile变量对于每次使用,线程都能得到当前volatile变量的最新值。但是volatile变量并不保证并发的正确性。
能使对long类型和double类型的读取变成原子操作,因为 Java 中读取 long 类型变量不是原子的,需要分成两步,如果一个线程正在修改该 long 变量的值,另一个线程可能只能看到该值的一半(前 32 位)。但是对一个 volatile 型的 long 或 double 变量的读写是原子。
9.4能创建 volatile 数组吗?
能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。我的意思是,如果改变引用指向的数组,将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。
10.transient变量有什么特点
java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
我们知道在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关
11.super什么时候使用
子类的构造函数中不是必须使用super,在构造函数中,如果第一行没有写super(),编译器会自动插入.但是如果父类没有不带参数的构造函数,或这个函数被私有化了(用private修饰).此时你必须加入对父类的实例化构造.而this就没有这个要求,因为它本身就进行实例化的构造.
如果父类的构造函数是无参的,那子类构造函数会在第一行默认调用super().
子类调用父类成员变量或方法时。
12.public static void 写成 static public void会怎样
个人测试貌似没有影响
13.说明一下public static void main(String args[])这段声明里每个关键字的作用
这是java程序的入口地址,java虚拟机运行程序的时候首先找的就是main方法.跟C语言里面的main()函数的作用是一样的.只有有main()方法的java程序才能够被java虚拟机欲行,可理解为规定的格式
对于里面的参数及修饰符
public:表示的这个程序的访问权限,表示的是任何的场合可以被引用,这样java虚拟机就可以找到main()方法,从而来运行javac程序
static:表明方法是静态的,不依赖类的对象的,是属于类的,在类加载的时候main()方法也随着加载到内存中去
void:main()方法是不需要返回值的
main:约定俗成,规定的
String[] args:从控制台接收参数
14.sizeof 是Java 的关键字吗
不是
15.static
15.1static class 与 non static class的区别
15.2Java中是否可以覆盖(override)一个private或者是static的方法
不可以
15.3静态类型有什么特点
static特点 1、随着类的加载而加载。也就是说:静态会随着类的消失而消失,说明他的生命周期最长 2、优先于对象存在 3、被所有对象所共享 4、可以直接被类名调用
15.4main() 方法为什么必须是静态的?能不能声明 main() 方法为非静态
不能,main()方法必须声明为静态的,这样JVM才可以调用main()方法而无需实例化它的类。
如果从main()方法去掉“static”这个声明,虽然编译依然可以成功,但在运行时会导致程序失败。
15.5是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用
不可以。因为非static方法是要与对象关联在一起的,必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。也就是说,当一个static方法被调用时,可能还没有创建任何实例对象,如果从一个static方法中发出对非static方法的调用,那个非static方法是关联到哪个对象上的呢?这个逻辑无法成立,所以,一个static方法内部发出对非static方法的调用。
15.6静态变量在什么时候加载?编译期还是运行期?静态代码块加载的时机呢
当类加载器将类加载到JVM中的时候就会创建静态变量,这跟对象是否创建无关。静态变量加载的时候就会分配内存空间。静态代码块的代码只会在类第一次初始化的时候执行一次。一个类可以有多个静态代码块,它并不是类的成员,也没有返回值,并且不能直接调用。静态代码块不能包含this或者super,它们通常被用初始化静态变量。
16.switch 语句中的表达式可以是什么类型数据
在switch(expression)中,expression只能是一个整数表达式或者枚举常量,整数表达式可以是int类型或Integer包装类型。由于,byte,short,char都可以隐式转换为int类型,所以,这些类型也可以用作表达式。
另外jdk7以后,switch表达式也可以为String类型,具体原理参考这位大神的博客。
17.while循环和do循环有什么不同
while是先判断在执行如果判断不成立,就不会执行;do/while是先执行在判断,不管判断是否成立都会执行一次






