Java高级面试题Word格式文档下载.docx
《Java高级面试题Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Java高级面试题Word格式文档下载.docx(90页珍藏版)》请在冰点文库上搜索。
正确答案:
C
数据默认值(必须了解的)
数字类型成员的缺省值是0,布尔型为false,对象引用唯一的缺省值类型是null
各种类型的缺省值:
boolean:
false
byte:
0
char:
'
\u0000'
short:
int:
float:
0.0
long:
double:
Reference:
null
privatedoubled;
privateStringstr;
privatebooleanbool;
TTtt=newTT();
System.out.println(tt.d+tt.str+tt.bool);
关于以上代码,说法正确的是:
A.运行时抛异常
B.输出"
0.0nullfalse"
C.输出"
\u00000.0null"
D.输出"
00.0null"
B
局部变量没有默认值
局部变量在使用之前必须显式赋值才能使用,否则将会有编译错误。
由于局部变量使用的是栈内存,由于某些原因(主要是效率)虚拟机不能为栈上的变量设置默认值,所以局部变量需要显式赋初始值。
注:
除局部变量以外的所有变量都是使用堆内存,由于在各种现代的机器上都可以很方便、快速地使大块内存初始化为相同的数值,比如可以将它初始化为0,这个特性使得堆内存都可以由虚拟机赋初始值。
由于在调用函数时,栈需要频繁地做出栈、入栈操作,若虚拟机要为各函数中的局部变量赋初始值的话会极大地影响效率,所以局部变量不可以从虚拟机得到这种好处。
Stringstr="
String"
inti;
System.out.println(str+i);
以上程序,下列说法正确的是:
A.编译错误
B.运行时抛异常
C.输出”String0”
D.输出”String”
A
数组(必须了解的)
刚构造的数组中的元素总是设为默认值
Java数组总是存在被设定的默认值。
各种数据类型的数组的默认值:
publicclassTest
finalintlen=5;
intarray[]=newint[len];
for(inti=0;
i<
len-2;
i++)
array[i]=i;
array.length;
System.out.print(array[i]+"
"
);
以上代码下列说法正确的是:
A.输出"
0,1,2,0,0,"
0,1,2,"
C.运行时抛异常
声明一个数组变量仅产生了一个数组引用(也可叫指针),但未分配数组的存储空间,一个数组的大小将在数组使用new关键字真正创建时被给定
数组变量本身仅仅是一个引用,数组的内存要用new运算符来分配。
定义数组变量的时候不能(也不必)指定数组大小,数组的大小是在分配数组内存时才确定的。
以下是定义数组的几种方法:
intarray1[]=newint[3];
//声明一个大小为3的数组
intarray2[]=newint[]{1,2,3};
//声明数组同时分配内存
用以下方法定义数组是错误的:
intarray3[3]=newint[3];
//数组变量不必指定大小,编译错误
intarray4[]=newint[3]{1,2,3};
//数组大小由初始化列表中元素个数决定,编译错误
下面几种声明数组的函数,错误的有()
A.charchr[]=newchar[3]{'
A'
'
B'
C'
};
B.charchr[]=newchar[3];
C.charchr[]={'
拷贝数组应当使用System.arrayCopy()函数以提高效率
相对于用循环来拷贝数组,System.arrayCopy()执行效率更高,这是因为它是一个原生(native)函数,可以直接高效地将”源array”的数据复制到”目标array”。
下面两种数组的拷贝,哪个更有效率?
String[]from=newString[10];
Stringto=newString[10];
A.for(inti=0,n=from.length;
n;
to[i]=from[i];
B.System.arrayCopy(from,0,to,0,from.length);
数据初始化(必须了解的)
一个final变量的值不能被改变,并且必须在一定的时刻赋值
声明为final的变量被标识为不可改变的,但该变量引用的对象属性却可以通过访问变量、或调用成员函数改变,当然这要求那些对象提供了“写”数据的接口(String类就没有提供“写”数据的接口,所以字符串的内容不可变)。
声明为final的成员变量必须在构造对象的过程中完成初始化:
1)其定义处,也就是说在final变量定义时直接给其赋值;
2)或者是在初始化块中;
3)或者是在构造函数中;
4)这三个地方必须(且只能)选其一
5)在以后的引用中只能读取,不能修改。
声明为final的静态变量不必在构造函数中初始化,但必须(且只能)在静态上下文中初始化一次(定义时、静态区段均可)。
声明为final的局部变量必须(且只能)在使用前初始化一次,不使用的话可不必初始化。
。
比如可以定义以下变量(编译通过):
finalinta=0;
finalintb;
b=0;
finalintc;
下面关于final变量说法正确的是:
A.声明为final的数组其元素只能赋值一次;
B.声明为final的整型变量其内容不能被改变;
C.声明为final的String对象可以通过replace函数改变其内容
final的基本用法及特点(final类、final函数、final参数)
1、final类:
如果一个类被声明为final,意味着它不能再派生新的子类,因此一个类不能同时既被声明为abstract,又被声明为final。
2、final函数:
如果一个函数被声明为final,意味着它不能再被子类覆写,但可以在覆写一个父类函数时将这个函数声明为final。
若一个final函数不是覆写父类的函数,则意味着编译时就可确定被调用的函数是哪一个函数,这样可以提高运行效率。
设计final函数时,可考虑两点:
第一,子类需要访问这个函数、但不能让子类覆写这个函数;
第二,在第一点的基础上,函数的性能非常重要,在多数情况下,这点比较次要一些。
3、final参数:
类似于final变量,只能引用,不能修改。
final参数仅需声明,不必显式赋值(赋值是通过函数调用传参数完成的),这点和final变量不太一样。
classabstractfinalA
publicabstractvoiddoSomething();
classBextendsA
B()
System.out.println("
initB…"
publicvoiddoSomething()
doSomething…"
publicstaticvoidmain(Stringargv[])
Bb=newB();
b.doSomething();
关于以上代码,下面说明正确的是:
常量
尽管各种类型数据都可声明为final,但仅有少数数据在编译期间就可获知确定不变的值,这种数据可称为常量。
在java语法中有一个保留字const,但这个关键字从来就没有被使用过。
除直接用数值表示的常量以外,其它常量都是用final来声明的,但不是所有的final数据都是常量,只有在编译期间能确定数值的才能算是常量,从以下代码可以识别出一些常量(以及一些定义常量的方法,这段代码可以编译通过):
finalstaticintSTATIC_CONST=1;
//静态常量
finalintMEMBER_CONST=2;
//成员常量
//下面这个数据编译期不能确定值,它的值需要运行期才确定,不是常量
finalstaticintstaticNoneConst=(int)(Math.random()*20);
voidtest(finalintnoneConst)
//final参数,不是常量
finalintTEMP_CONST=3;
//局部常量
finalintTEMP_CONST2=TEMP_CONST+1;
//常量和常量运算结果也是常量
finalintnoneConst2=noneConst+1;
//不是常量
switch(noneConst)
caseSTATIC_CONST:
System.out.println("
STATIC_CONST"
break;
caseMEMBER_CONST:
MEMBER_CONST"
caseTEMP_CONST:
TEMP_CONST"
caseTEMP_CONST2:
需要注意的是,一个数据是不是常量跟这个数据是不是static没有关系,比如上例中,成员数据、局部数据都可能是常量,关键只在于数据是在编译期间就可以确定它的值。
常量不仅在虚拟机的一次运行期间数值不会变,并且多次重新启动虚拟机反复运行后,它的值还是恒定不变的。
从这个意义上说,所有的引用都不是常量,因为引用都是在运行期间才能确定它的值。
以下说法正确的是:
A.只要是用finalstaticint修饰的数据都恒定不变的,多次重新启动虚拟机反复运行后,它的值还是恒定不变的
B.只要是用finalstaticint修饰的数据都可以用在switch–case语句的case子句中
C.只能用finalstatic定义常量
D.保留字const不能用来定义常量
D
构造函数(必须了解的)
在构造过程中避免调用子类可以覆写的函数
有继承关系的类之间的初始化过程,是父类的构造函数先执行,子类的构造函数后执行。
如果在父类构造函数中调用了子类可以覆写的函数,一旦子类覆写了父类的函数,由于父类构造函数是先于所有子类构造函数执行,在子类覆写的函数中看到的所有子类数据都是未初始化的,这样做非常危险。
以下程序输出什么?
classParent
protectedStringpStr;
publicParent()
invokePrint();
pStr="
parent"
protectedvoidinvokePrint()
Parentprint,pStr="
+pStr);
publicclassChildextendsParent
privateStringcStr;
publicChild()
super();
cStr="
child"
Childprint,pStr="
+pStr+"
+cStr);
publicstaticvoidmain(String[]args)
Childc=newChild();
c.invokePrint();
Parentp=newParent();
p.invokePrint();
A.
Childprint,pStr=nullcStr=null
Childprint,pStr=parentcStr=child
Parentprint,pStr=null
Parentprint,pStr=parent
B.
C.
Childprint,pStr=parentcStr=null
D.
Childprint,pStr=nullcStr=child
有继承关系的类之间的初始化顺序(必须了解的)
有继承关系的类之间的初始化顺序是先静态后非静态,先父后子
初始化分为类的初始化和对象的初始化。
对于类的初始化,其顺序是:
1、若父类未加载,则先执行父类的类初始化过程,然后加载本类
2、给类变量分配存储空间
3、给类变量显式赋值
4、执行static块
在虚拟机一次运行的整个生命周期中,类的初始化一般只做一次(仅在特殊情况下才有可能做多次,比如一个类初始化过程抛出了异常导致类加载失败后,以后再用这个类时,虚拟机会再次尝试加载这个类)。
另外如果在类的初始化过程中需要使用其他未加载的类,则对未加载的类也需执行上述过程。
说明:
第2步“给类变量分配存储空间”时,由于新分配的存储空间都被虚拟机初始化过了,所以自动取得了默认值。
虚拟机会通过某些方法(比如调用memset()函数)将一大块内存全部置为默认值,这在java代码中不可见,编译出来的字节码也没有相关的内容,如果没有显式赋初始值则声明变量的地方不能设置断点就是这个原因。
对象的初始化,其顺序是:
1、给对象分配存储空间(自动取得了默认值)
2、若有父类,则执行父类的对象初始化过程
3、给对象变量显式赋值
4、执行构造函数。
只有java.lang.Object类无需执行步骤2,在执行父类的对象初始化过程中,具体要调用的构造函数由子类构造函数中的super()调用确定,若没有明确的super()调用,则调用默认构造函数。
对象的初始化过程在每次调用new运算符时都会被执行,但在对象克隆时不会执行(除非故意在克隆函数中用new构造了新对象)。
考题样例:
运行下面程序输出的是:
protectedstaticStringstr=getParentMessage();
str="
staticblockinit"
privatestaticStringgetParentMessage()
getParentMessage"
return"
staticclassInnerClass
innerclassstaticblockinit"
Child()
finalstr="
+str);
newChild();
A.getParentMessage
finalstr=staticblockinit
B.getParentMessage
finalstr=innerclassstaticblockinit
C.抛出运行时异常
D.finalstr=staticblockinit
运算
赋值运算
将一个对象引用赋值给另一个的一般规则是,你可以对继承树向上赋值但不能向下赋值
将一个对象引用赋值给另一个的一般规则是,可以对继承树向上赋值但不能向下赋值(子类的对象一定是父类的对象,但是父类的对象不一定是子类的对象)。
可以这样想,如果将一个子类的实例赋值给基类,Java知道哪个函数会在子类中。
但是一个子类可能有基类没有的额外函数,这时候想调用子类的函数,就必须将该对象强制类型转换成子类的对象。
子类对象可以当成父类对象使用,在设计领域就延伸为里氏代换原则。
里氏代换原则:
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有对象o1都换成o2时,程序P的行为没有变化,那么类型T2是T1的子类型,即如果一个软件实体使用的是基类的话那么也一定适用于子类,但反过来的代换不成立。
在下面给定的代码中,哪一个会编译失败:
interfaceIFace{}
classCFaceimplementsIFace{}
classBase{}
publicclassObRefextendsBase
ObRefob=newObRef();
Baseb=newBase();
Objecto1=newObject();
IFac