`
musou
  • 浏览: 788 次
  • 性别: Icon_minigender_1
  • 来自: 成都
文章分类
社区版块
存档分类
最新评论

Java面试题解惑系列笔记

阅读更多
这个是看了Java面试题解惑系列之后自己做的读书笔记

(一)类的初始化顺序

1.
静态变量是在类第一次被加载(classloader)的时候初始化的,而非创建一个新对象(new对象)
的时候。静态变量是属于类的,而不是类的实例(对象),因此调用对象的finalize方法时,它
并不会被销毁。
2.
静态变量变量被初始化后值的存放位置则取决于它的类型。栈中主要存放一些基本类型
的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
而对象则保存在堆中。
3.
静态代码为什么先于非静态代码这是因为静态代码是在类加载完毕后执行的,而加载类的顺序是先父类后子类,所以静态代码的执行是
先执行父类的,然后执行子类的。对于非静态变量以及实例初始化块都是在构造函数里的代码执行前执行。所以静态代码是在类加载后执行,
而实例代码是在构造函数执行前执行。但是当我们显示控制类加载的时候情况有点变化,显示加载可以有关两种方法:
第一种:利用forName方法
当我们查API文档就会发现forName方法有两种形式。分别如下:
public static Class<?> forName(String className)
throws ClassNotFoundException


public static Class<?> forName(String name,
boolean initialize,
ClassLoader loader)
throws ClassNotFoundException

第二个方法值得注意的就是第二个参数boolean initialize,如果我们把这个参数设置为false,那么当我们加载完类后就不会执行静态代码
和静态的初始化动作。只有当我们new一个对象的时候才会初始化。而第三个参数是用来指明类的加载器的。
如果查看java.lang.Class类的源代码,上述两种方法最终都会调用Class类中的私有的native方法forName0(),此方法的声明如下:
private static native Class forName0(String name, boolean init , ClassLoader loader)
throws ClassNotFoundException;
所以当我们调用Class.forName(name )时,其实是在方法内部调用了:
forName0(name, true, ClassLoader.getCallerClassLoader());
当我们调用Class.forName(name, initialize, loader )的时候,实际上此方法内部调用了:
forName0(name, initialize, loader);

第二种:利用Class对象获取的ClassLoader装载。
此方法也是在实例化时才执行静态代码的执行。

综上所述可以总结如下:
1 对于隐式的加载(new一个对象和调用类的静态方法),静态代码是在类加载后立刻执行,而对于显示加载(第一种是用java.lang.Class的
forName(String str)方法,第二种是用java.lang.ClassLoader的loadClass())就如同我上面所说,加载过程是可以由我们来控制的。
2 实例化代码执行是载构造函数执行之前,涉及到继承时,父类的构造函数执行之前执行父类里的实例化代码,子类的构造函数执行之前执行
子类的实例化代码。所以这样可以保证子类中用到的变量都是已经经过父类初始化的,从而保证了初始化的正确性。
===========================================================================================================================
(二)到底创建了几个String对象?

===========================================================================================================================
(三)变量(属性)的覆盖

1.由于private变量受访问权限的限制,它不能被覆盖。
2.属性的值取父类还是子类并不取决于我们创建对象的类型,而是取决于我们定义的变量的类型。
3.friendly、protected和public修饰符并不影响属性的覆盖。
4.静态变量和静态常量属于类,不属于对象,因此它们不能被覆盖。
5.常量可以被覆盖。
6.对于基本类型和对象,它们适用同样的覆盖规律。
7.转型成父类类型,那么访问的本应该都是父类的属性和方法,
但方法调用涉及到多态,故理解多态的特性应该就ok了吧。
8.你写这么多总结起来就是一句话:属性是不能被覆盖的。
9.JAVA语言中子类定义与父类相同类型和名称的变量、嵌套类型和静态方法,都将隐藏掉父类中相应的方法,而不是覆写,
所以,请避免隐藏!此过程中当然也不存在所谓多态。另外,我们不应该违反这样一条规则:对基类所做的任何行为,都应当
可以同样作用于子类。此例中子类className为private,违反了基类中className是public的定义,这样的写法应该避免。
10.子类对父类的继承可能导致各种陷阱,比如隐藏(hidden),遮蔽(shadow),遮掩(obscure),覆写(override),
重载(overload)等行为。
===========================================================================================================================
(四)final、finally和finalize的区别

final关键字

说说final,它可以用于以下四个地方:
1. 定义变量,包括静态的和非静态的。
2. 定义方法的参数。
3. 定义方法。
4. 定义类。

被final修饰的变量必须被初始化。初始化的方式有以下几种:
1. 在定义的时候初始化。
2. final变量可以在初始化块中初始化,不可以在静态初始化块中初始化。
3. 静态final变量可以在静态初始化块中初始化,不可以在初始化块中初始化。
4. final变量还可以在类的构造器中初始化,但是静态final变量不可以。

final用来定义一个方法时,它表示这个方法不可以被
子类重写,但是它这不影响它被子类继承。

final的类的所有方法都不能被重写,
但这并不表示final的类的属性(变量)值也是不可改变的,
要想做到final类的属性值不可改变,
必须给它增加final修饰

finally关键字
return、continue和break都没能阻止finally语句块的执行
finally语句块是在程序退出方法之前被执行的

finalize关键字
由于finalize()属于Object类,因此所有类都有这个方法,Object的任意子类都可以重写
(override)该方法,在其中释放系统资源或者做其它的清理工作,如关闭输入输出流。
===========================================================================================================================
(五)传了值还是传了引用?

不管是大家认为是值传递,还是引用传递,有一些却是共识:
对于基本类型,在方法体内对方法参数进行重新赋值,并不会改变原有变量的值。
对于引用类型,在方法体内对方法参数进行重新赋予引用,并不会改变原有变量所持有的引用。
方法体内对参数进行运算,不影响原有变量的值。
方法体内对参数所指向对象的属性进行运算,将改变原有变量所指向对象的属性值。

在Java虚拟机中,false是由整数零来表示的,所有非零整数都表示true
===========================================================================================================================
(六)字符串(String)杂谈


===========================================================================================================================
(七)日期和时间的处理

UTC
即Coordinated Universal Time(中文名译作世界协调时间、世界
统一时间或世界标准时间)
GMT,即Greenwich Mean Time(中文名译作格林威治标
准时间或格林威治平均时间)两种。严格来讲,UTC比GMT更加精确一些,不过它们的差
值不会超过0.9秒,如果超过了,将会为UTC增加闰秒以与GMT,也就是地球自转周期保
持一致。
所以在日常使用中,我们可以把UTC和GMT一样看待。
===========================================================================================================================
(八)聊聊基本类型(内置类型)

1. 未带有字符后缀标识的整数默认为int类型;未带有字符后缀标识的浮点数默认为
double类型。
2. 如果一个整数的值超出了int类型能够表示的范围,则必须增加后缀“L”(不区分大小
写,建议用大写,因为小写的L与阿拉伯数字1很容易混淆),表示为long型。
3. 带有“F”(不区分大小写)后缀的整数和浮点数都是float类型的;带有“D”(不区分
大小写)后缀的整数和浮点数都是double类型的。
4. 编译器会在编译期对byte、short、int、long、float、double、char型变量的值进行检查,
如果超出了它们的取值范围就会报错。
5. int型值可以赋给所有数值类型的变量;long型值可以赋给long、float、double类型的
变量;float型值可以赋给float、double类型的变量;double型值只能赋给double类型变量。

运算符对基本类型的影响
当使用+、-、*、/、%运算符对基本类型进行运算时,遵循如下规则:
1.只要两个操作数中有一个是double类型的,另一个将会被转换成double类型,并且结果也是double类型;
2.否则,只要两个操作数中有一个是float类型的,另一个将会被转换成float类型,并且结果也是float类型;
3.否则,只要两个操作数中有一个是long类型的,另一个将会被转换成long类型,并且结果也是long类型;
4.否则,两个操作数(包括byte、short、int、char)都将会被转换成int类型,并且结果也是int类型。

当使用+=、-=、*=、/=、%=、运算符对基本类型进行运算时,遵循如下规则:
运算符右边的数值将首先被强制转换成与运算符左边数值相同的类型,然后再执行运算,
且运算结果与运算符左边数值类型相同。

当使用“==”运算符在基本类型和其包装类对象之间比较时,遵循如下规则:
1. 只要两个操作数中有一个是基本类型,就是比较它们的数值是否相等。
2. 否则,就是判断这两个对象的内存地址是否相等,即是否是同一个对象。

Math类的round()方法的运算结果是一个<=(参数值+0.5d)的最大整数。

哪些类型可以用于switch语句的判断呢?
1. byte、char、short、int四种基本类型以及它们的包装类(需要Java5.0/1.5以上版本支持)
都可以用于switch语句。
2. long、float、double、boolean四种基本类型以及它们的包装类(在Java所有版本中)
都不能用于switch语句。
3. enum类型,即枚举类型可以用于switch语句,但是要在Java5.0(1.5)版本以上才支
持。
4. 所有类型的对象(包括String类,但在Java5.0/1.5以上版本中,该项要排除
byte、char、short、int四种基本类型对应的包装类)都不能用于switch语句。

int型数值可以赋给所有数值类型的变量
===========================================================================================================================
(九)继承、多态、重载和重写

类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。
抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接
口。
接口只能继承(extends)接口。

1. 类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互
斥的,也就是说它们要么继承一个类,要么继承一个抽象类。
2. 类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接口。
当然,对于类来说,它必须实现它所继承的所有接口中定义的全部方法。
3. 抽象类继承抽象类,或者实现接口时,可以部分、全部或者完全不实现父类抽象类的
抽象(abstract)方法,或者父类接口中定义的接口。
4. 类继承抽象类,或者实现接口时,必须全部实现父类抽象类的全部抽象(abstract)方
法,或者父类接口中定义的全部接口。

多态就是一种类型表现出多种状态
将一个方法调用同这个方法所属的主体(也就是对象或类)关联起来叫做绑定
1. 前期绑定:在程序运行之前进行绑定,由编译器和连接程序实现,又叫做静态绑定。
比如static方法和final方法,注意,这里也包括private方法,因为它是隐式final的。
2. 后期绑定:在运行时根据对象的类型进行绑定,由方法调用机制实现,因此又叫做动
态绑定,或者运行时绑定。除了前期绑定外的所有方法都属于后期绑定。

型构(英文名是signature,有的译作“签名”,虽然它被使用的较为广泛,但是这个翻译
不准确的)。型构就是指方法的组成结构,具体包括方法的名称和参数,涵盖参数的数量、
类型以及出现的顺序,但是不包括方法的返回值类型,访问权限修饰符,以及
abstract、static、final等修饰符。

重写,英文名是overriding,是指在继承情况下,子类中定义了与其基类中方法具有相同
型构的新方法,就叫做子类把基类的方法重写了。这是实现多态必须的步骤。
重载,英文名是overloading,是指在同一个类中定义了一个以上具有相同名称,但是型构
不同的方法。在同一个类中,是不允许定义多于一个的具有相同型构的方法的。

指导性原则:
第一,继承的层级不宜过多,5层基本上是极限,否则系统难以维护,如果你不得不用超过5层的继承,考虑用组合代替封装;
第二,每个类的功能,应该能够用一句话来进行描述,如果你发现不得不用很多句话来描述一个类的功能,考虑拆分。

===========================================================================================================================
(十)话说多线程

实现线程的方式有两种:
1. 继承java.lang.Thread,并重写它的run()方法,将线程的执行主体放入其中。
2. 实现java.lang.Runnable接口,实现它的run()方法,并将线程的执行主体放入其中。

总结起来就一句话,线程类有一个Runnable类型的target属性,它是线程
启动后要执行的run()方法所属的主体,如果我们采用的是继承Thread类的方式,那么这个
target就是线程对象自身,如果我们采用的是实现Runnable接口的方式,那么这个target就
是实现了Runnable接口的类的实例。

1. NEW(新建状态、初始化状态)
线程对象已经被创建,但是还没有被启动时的状态。
这段时间就是在我们调用new命令之后,调用start()方法之前。
2. RUNNABLE(可运行状态、就绪状态)
在我们调用了线程的start()方法之后线程所
处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可
能还正在等待操作系统分配给它相应的运行资源以得以运行。
3. BLOCKED(阻塞状态、被中断运行)
A)线程正在等待其它的线程释放同步锁,以进入一个同步块或者同步方法继续运行;
B)或者它已经进入了某个同步块或同步方法,在运行的
过程中它调用了某个对象继承自java.lang.Object的wait()方法,正在等待重新返回这个同步
块或同步方法。
4. WAITING(等待状态)
当前线程调用了
A)java.lang.Object.wait()、
B)java.lang.Thread.join()
C)或者java.util.concurrent.locks.LockSupport.park()三个中的任意一个方法,正在等待另外一个线程执行某个操作。
比如一个线程调用了某个对象的wait()方法,正在等待其它线程调用这个对象的notify() 或者notifyAll()
(这两个方法同样是继承自Object类)方法来唤醒它;
或者一个线程调用了另一个线程的join()(这个方法属于 Thread类)方法,正在等待这个方法运行结束。
5. TIMED_WAITING(定时等待状态)
当前线程调用了
A)java.lang.Object.wait(longtimeout)、
B)java.lang.Thread.join(longmillis)、
C)java.util.concurrent.locks.LockSupport.packNanos(longnanos)、
D)java.util.concurrent.locks.LockSupport.packUntil(long deadline)四个方法中的任意一个,
进入等待状态,但是与WAITING状态不同的是,
它有一个最大等待时间,即使等待的条件仍然没有满足,只要到了这个时间它就会自动醒来。
6. TERMINATED(死亡状态、终止状态)
线程完成执行后的状态。线程执行完run()方
法中的全部代码,从该方法中退出,进入TERMINATED状态。
还有一种情况是run()在运行过程中抛出了一个异常,而这个异常没有被程序捕获,导致这个线程异常终止进入TERMINATED状态。

调用sleep()方法并不会让线程释放它所持有的同步锁;而且在这期间它也不会阻碍其它线程的运行。
在调用了wait()后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait()方法的对象。

给一个方法增加synchronized修饰符之后就可以使它成为同步方法,这个方法可以是静态方法和非静态方法,
但是不能是抽象类的抽象方法,也不能是接口中的接口方法。

所以静态同步方法只受它所属类的其它静态同步方法的制约,而跟这个类的实例(对象)没有关系。

程序中必须同时满足以下四个条件才会引发死锁:
1. 互斥(Mutual exclusion):线程所使用的资源中至少有一个是不能共享的,它在同一
时刻只能由一个线程使用。
2. 持有与等待(Hold and wait):至少有一个线程已经持有了资源,并且正在等待获取
其他的线程所持有的资源。
3. 非抢占式(No pre-emption):如果一个线程已经持有了某个资源,那么在这个线程释
放这个资源之前,别的线程不能把它抢夺过去使用。
4. 循环等待(Circular wait):假设有N个线程在运行,第一个线程持有了一个资源,并
且正在等待获取第二个线程持有的资源,而第二个线程正在等待获取第三个线程持有的资源,
依此类推……第N个线程正在等待获取第一个线程持有的资源,由此形成一个循环等待。

newCachedThreadPool()方法创建一个动态的线程池,其中线程的数量会根据实际需要来创建
和回收,适合于执行大量短期任务的情况
newFixedThreadPool(int nThreads)方法创建一个
包含固定数量线程对象的线程池
nThreads代表要创建的线程数,如果某个线程在运行的过
程中因为异常而终止了,那么一个新的线程会被创建和启动来代替它
newSingleThreadExecutor()方法则只在线程池中创建一个线程,来执行所有的任务。

1. Runnable接口:继承Runnable接口的类要实现它的run()方法,并将执行任务的代码放
入其中,run()方法没有返回值。适合于只做某种操作,不关心运行结果的情况。
2. Callable接口:继承Callable接口的类要实现它的call()方法,并将执行任务的代码放入
其中,call()将任务的执行结果作为返回值。适合于执行某种操作后,需要知道执行结果的情况。
===========================================================================================================================
(十一)这些运算符你是否还记得

自增与自减运算符还遵循以下规律:
1. 可以用于整数类型byte、short、int、long,浮点类型float、double,以及字符串类型
char。
2. 在Java5.0及以上版本中,它们可以用于基本类型对应的包装器类
Byte、Short、Integer、Long、Float、Double、Character。
3. 它们的运算结果的类型与被运算的变量的类型相同。

1. 按位与运算(&):二元运算符。当被运算的两个值都为1时,运算结果为1;否则为0
2. 按位或运算(|):二元运算符。当被运算的两个值都为0时,运算结果为0;否则为1
3. 按位异或运算(^):二元运算符。当被运算的两个值中任意一个为1,另一个为0时,运算结果为1;否则为0
4. 按位非运算(~):一元运算符。当被运算的值为1时,运算结果为0;当被运算的值为0时,运算结果为1

1. 按位运算符能操作整型值,包括byte、short、int、long,但是不能操作浮点型值(即float和double),它还可以操
作字符型(char)值。按位运算符不能够操作对象,但是在Java5.0及以上版本中,byte、
short、int、long、char所对应的包装器类是个例外,因为JAVA虚拟机会自动将它们转换为
对应的基本类型的数据。
2. 逻辑运算符的运算遵循短路形式,而按位运算符则不是。所谓短路就是一旦能够确定运算的结果,就不再进行余下的运算。

移位运算符
1. 左移位(<<):将操作符左侧的操作数向左移动操作符右侧指定的位数。移动的规则是在二进制的低位补0
2. 有符号右移位(>>):将操作符左侧的操作数向右移动操作符右侧指定的位数。移动
的规则是,如果被操作数的符号为正,则在二进制的高位补0;如果被操作数的符号为负,
则在二进制的高位补1
3. 无符号右移位(>>>):将操作符左侧的操作数向右移动操作符右侧指定的位数。移
动的规则是,无论被操作数的符号是正是负,都在二进制位的高位补0

移位运算符可以用于byte、short、 int、long等整数类型,和字符串类型char,但是不能用于浮点数类
型float、double;当然,在Java5.0及以上版本中,移位运算符还可用于byte、short、int、long、char对应的包装器类

1. byte、short、char在做移位运算之前,会被自动转换为int类型,然后再进行运算。
2. byte、short、int、char类型的数据经过移位运算后结果都为int型。
3. long经过移位运算后结果为long型。
4. 在左移位(<<)运算时,如果要移位的位数大于被操作数对应数据类型所能表示的最
大位数,那么先将要求移位数对该类型所能表示的最大位数求余后,再将被操作数移位所得
余数对应的数值,效果不变。比如1<<35=1<<(35%32)=1<<3=8。
5. 对于有符号右移位(>>)运算和无符号右移位(>>>)运算,当要移位的位数大于被
操作数对应数据类型所能表示的最大位数时,那么先将要求移位数对该类型所能表示的最大
位数求余后,再将被操作数移位所得余数对应的数值,效果不变。
比如100>>35=100>>(35%32)=100>>3=12。
===========================================================================================================================
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics