Java面试宝典.docx
- 文档编号:9104201
- 上传时间:2023-02-03
- 格式:DOCX
- 页数:24
- 大小:23.76KB
Java面试宝典.docx
《Java面试宝典.docx》由会员分享,可在线阅读,更多相关《Java面试宝典.docx(24页珍藏版)》请在冰豆网上搜索。
Java面试宝典
Java面试宝典:
文中的部分例子和定义来自作者十星推荐,每个学java人都看过的《ThinkinginJava》,推荐大家看英文原版,会很大程度加深对Java的理解。
另外,问题提纲的总结来自作者最近研读的机械工业出版社的《Java程序员面试笔试宝典》。
文章主干,分析解释部分全部由作者手打,标明网上资料忘记出处的,希望细心人指点。
1.static的作用
static,顾名思义,静态。
当类中的成员变量或方法声明为static时,无需为此类创建参照,便可对其进行操作。
即,不依赖该类的实例,同时也被该类的实例所共享。
下面通过两种static的使用情况进行分析:
static变量
同为成员变量,与实例变量每创建一次便为其分配一次内存不同,JVM只为static变量分配一次内存,并在加载类的过程中完成该操作。
可用类名直接访问此变量或通过实例调用,前者是被推荐的,因为这样不仅强调了该变量的static属性,而且也在某种程度上使编译器更容易去进行优化。
所以在对象之间有共享值或为了方便访问某种变量时一般需要使用static变量。
static方法
对于static方法,也同时可以通过类名调用或实例调用。
因此需要注意的是,static方法中不能用this或super关键字,不能直接访问此方法所在类的实例变量或实例方法,只能访问该类的静态成员变量和方法。
因为实例变量和方法有特定的对象,而静态方法占据一个特定的数据区域。
举例:
ClassStaticTest{
staticinti=47;
intj=10;
}
ClassIncrementable{
staticvoidincrement(){
//通过类名直接对i进行操作
StaticTest.i++;
//此处无法对j进行访问,因为其为实例变量
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2.final的作用
final,在Java中通常解释为不可变的,也就是说,final关键字是用来防止变化。
一般我们在两种情况下使用:
设计(design)或效率(efficiency)。
下面分情况来分析final关键字的作用:
final数据——声明类中属性或变量
每种编程语言都有一种声明常量的方法,java中便是final。
基本类型(PrimitiveType)和引用类型(ObjectReferenceType)的属性在声明final后,里面存放的值都不可再改变。
但有所不同的是,在基本类型中,这个值是实在的,比如100,”java”;在引用类型中存放的是地址,所以final只是使其地址不可改变,这个地址所指的对象、数组皆是可以改变的。
需要注意的是,staticfinal的基本变量命名法,全大写字母,单词之间用”_”(underscore)连接。
举例:
publicstaticfinalintVALUE_ONE=9;
1
final方法
使用final声明方法主要是为了给方法加锁以防止继承的类对其内容进行修改,也就是使其不可重写(override)。
因此final方法可以被继承,但不能被重写。
final类
在前面加上final关键字的类是因为你不希望此类被继承,换句话说,在某种设计情况下你的类永远不需要去作出改变,或者为了安全原因你不希望它有子类。
简单举例:
classSmallBrain{}
finalclassDinosaur{
inti=7;
intj=1;
SmallBrainx=newSmallBrain();
voidf(){}
}
//classFurtherextendsDinosaur{}此句无法执行,因为Dinosaur类为final
publicclassJurassic{
Publicstaticvoidmain(String[]args){
Dinosaurn=newDinosaur();
n.f();
n.i=40;
n.j++
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
还有一个网上看到的例子,忘记出处了:
finalbytebt1=1;
finalbytebt2=2;
bytebt3=bt1+bt2;
1
2
3
此例中若没有final便会报错,因为如果去掉final,bt1和bt2在运算时JVM将其自动转换为了int类型变量,最后相当于将一个int类型赋值给了一个byte类型。
3.Overload与Override
为了对比,我们先来看一下两者的英文定义:
Overriding
Havingtwomethodswiththesamearguments,butdifferentimplementations.
Overloading
Afeaturethatallowsaclasstohavetwoormoremethodshavingsamename,iftheirargumentlistsaredifferent.
不难看出,Overload和Override都是Java多态性(Polymorphism)的体现。
其中Overriding(重写)是指父类与子类中,同一方法,相同名称和参数,重新写其内容,相当于“推翻”了父类中的定义。
而Overloading(重载)是指在同一类中,定义多个同名方法,但其中的参数类型或次序不同。
例子如下:
Overriding
classDog{
publicvoidbark(){
System.out.println("woof");
}
}
classHoundextendsDog{
publicvoidsniff(){
System.out.println("sniff");
}
publicvoidbark(){
System.out.println("bowl");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
Overloading
classDog{
publicvoidbark(){
System.out.println("woof");
}
//overloadingmethod
publicvoidbark(intnum){
for(inti=0;i System.out.println("woof"); } } } 1 2 3 4 5 6 7 8 9 10 11 12 4.组合与继承 组合(Composition)与继承(Inheritance)是Java最常用的两种类的复用方法。 要了解他们的区别,我们先来看一下英文定义。 组合 Achievedbyusinginstancevariablesthatreferstootherobjects. 继承 Amechanismwhereinanewclassisderivedfromanexistingclass. 从字面意思来看,组合是在一个类中使用一个类的引用,通常说明一个类具有某种属性,有”hasa”的关系。 比如下面的例子,洒水机”hasa”水源: classWaterSource{ privateStrings; WaterSource(){ System.out.println("WaterSource()"); s="Constructed"; } publicStringtoString(){ returns; } } publicclassSprinklerSystem{ privateStringvalve1,valve2,valve3,valve4; privateWaterSourcesource=newWaterSource(); privateinti; privatefloatf; publicStringtoString(){ return "valve1="+valve1+""+ "valve2="+valve2+""+ "valve3="+valve3+""+ "valve4="+valve4+"\n"+ "i="+i+""+"f="+f+""+ "source="+source; } publicstaticvoidmain(String[]args){ SprinklerSystemsprinklers=newSprinklerSystem(); System.out.println(sprinklers); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 例子中的SprinklerSystem类中创建了WaterSource类的参照,因此此段代码中最先输出的为”WaterSource”,这就是最简单的组合。 而继承是一个新类派生于旧类的关系,那么也就是说具有旧类的属性,有”isa”的关系,大家应该都对此非常熟悉,因此在此处不再举例。 不难看出,当某物具有多项属性时使用组合,比如汽车有引擎,车门,轮胎。 当某物属于一种某物时使用继承,比如哈士奇是一种狗。 5.clone的作用 clone()是Java中用来复制对象的方法,由于Java取消了指针的概念,很多人对引用和对象的区别有所忽视,全面理解clone()将对此有很大帮助。 需要注意的是,当写出如下代码时 Studentst1=newStudent("Daniel"); Studentst2=st1; System.out.println(st1); System.out.println(st2); 1 2 3 4 5 打印结果(地址)是完全相同的,因为st1与st2为一个对象的两个引用。 因此我们在使用”=”操作符赋值是,只是进行了引用的复制。 而clone()方法,才是真正实现对象复制的途径,与上面的做对比: Studentst1=newStudent("Daniel"); Studentst2=(Student)st1.clone(); System.out.println(st1); System.out.println(st2); 1 2 3 4 5 在上面的代码中,由于clone()返回的是Object对象,所以需要进行向下强制转换为Student。 此时打印的结果就是两个不同的地址,真正地完成了对象的复制。 深拷贝(DeepCopy)和浅拷贝(ShallowCopy) 这里我们又要再次提到基本数据类型(PrimitiveType)和非基础类型(Non-PrimitiveType)的区别了,看下面一段代码: PublicclassStudent{ privateintid; privateStringname; PublicStudent(intid,Stringname){ this.age=age; this.name=name; } publicStudent(){} publicintgetId(){ returnage; } publicStringgetName(){ returnname; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 其中id是基本数据类型,在拷贝后没有什么问题,然而name是String类型,因此前面提到的方法拷贝过来的只是一个引用值,便是浅拷贝。 相对而言,深复制就是再创建一个相同的String对象,将这个对象的饮用赋值给拷贝出的新对象。 要实现深拷贝,我们需要先实现Cloneable接口并重写clone()方法,将上述代码略作修改: PublicclassStudentimplementsCloneable{ privateintid; privateStringname; PublicStudent(intid,Stringname){ this.age=age; this.name=name; } publicStudent(){} publicintgetId(){ returnage; } publicStringgetName(){ returnname; } @Override protectedObjectclone()throwsCloneNotSupportedException{ return(Student)super.clone(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 需要注意的是,在套用多层对象的组合方法中,彻底的深拷贝需要在每一层对象中都实现cloneable接口并重写clone()方法。 非常复杂,在实际开发中用处不多,但如前文所题,有助于读者对内存结构有深一步的了解。 6.内部类(InnerClasses) 顾名思义,内部类是指讲一个类定义内置于另一个类定义之中。 关于它的作用,《ThinkinginJava》里是这样说明的: Theinnerclassisavaluablefeaturebecauseitallowsyoutogroupclassesthatlogicallybelongtogetherandtocontrolthevisibilityofonewithintheother. 将逻辑相通的类声明为内部类使代码更易控制或处理,在安卓开发中我们会经常见到此类用法。 因为每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。 ——以上黑体摘自《ThinkinginJava》,如翻译拗口请见谅。 下面我们来看一个包裹的例子,来简单分析内部类的使用方法。 publicclassParcel1{ classContents{ privateinti=11; publicvalue(){returni;} } classDestination{ privateStringlabel; Destination(StringwhereTo){ label=whereTo; } StringreadLabel(){returnlabel;} } publicvoidship(Stringdest){ Contentsc=newContents(); Destinationd=newDestination(dest); System.out.println(d.readLabel()); } publicstaticvoidmain(String[]args){ Parcel1p=newParcel1(); p.ship("Tasmania"); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 其中Destination和Contents便是内部类我们可以在非静态(non-static)方法中对其进行调用,当再静态方法如main()中对其进行调用时,需使用“外部类.内部类”的方式对其进行引用。 如,将上述代码进行修改。 publicclassParcel2{ classContents{ privateinti=11; publicvalue(){returni;} } classDestination{ privateStringlabel; Destination(StringwhereTo){ label=whereTo; } StringreadLabel(){returnlabel;} } publicDestinationto(Strings){ returnnewDestination(s); } publicContentscontents(){ returnnewContents(); } publicvoidship(Stringdest){ Contentsc=newContents(); Destinationd=newDestination(dest); System.out.println(d.readLabel()); } publicstaticvoidmain(String[]args){ Parcel2p=newParcel2(); p.ship("Tasmania"); Parcel2q=newParcel2(); Parcel2.Contentsc=q.contents(); Parcel2.Destinationd=q.to("Borneo") } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 在此例子中调用Contents以及Destination对象时,使用了“外部类.内部类”的调用方法。 7.接口与抽象类 在国外论坛上对于接口(Interface)和抽象类(Abstractclass)的讨论就一直不断。 我们还是先来看一下它们的英文定义。 抽象类 Classesthatcontainoneormoreabstractmethods. 接口 Itisacollectionofabstractmethods. 乍一看貌似差不多,都是有关抽象方法的集合,关于它们的区别,仅从定义上来看,有两点不同: 1.接口暗示着全部方法为抽象,且不能有任何的implementation。 需要注意的是,这里有一个常考的点就是,既然不能implement,那么接口可以继承吗? 答案是可以的。 相对于接口,抽象类中可以有执行默认行为的实例方法,也就是可以有implementation。 2.在接口中声明的变量默认为final类型,然而在抽象类中可以有非final类型的变量。 那么在应用上该如何选用这两者呢,首先我们需要知道他们的本质。 接口是对动作的抽象,而抽象类则是对根源的抽象。 比如人要吃东西,狗也要吃东西,那么我们就可以把吃东西作为一个接口。 其中人和狗都属于生物,那么我们就可以将生物定为一个抽象类。 需要注意的是,一个类只可以继承一个类(抽象类),但却可以实现多个接口。 理解这一点不难,上例中人和狗是生物,但不可能同时是非生物体。 我们来看一个经典的报警门的例子,相信大部分人都见过: interfaceAlram{ voidalarm(); } abstractclassDoor{ voidopen(); voidclose(); } classAlarmDoorextendsDoorimplementsAlarm{ voidoepn(){} voidclose(){} voidalarm(){} } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 此例中,只要是门,固有属性都会有开和关,所以将门设置为抽象类,报警门只是门的一种。 然而报警门的特殊功能是报警,因此我们将报警设置为一个接口。 在报警门的这个类中,便继承了门,实现了报警接口。 8.stack和heap 关于堆(heap)和栈(stack)的理解不只局限于java,无论是任何语言,对此概念区分理解都是非常重要的。 stackoverflow是这么说的: StackisusedforstaticmemoryallocationandHeapfordynamicmemoryallocation stack是静态内存,而heap是动态分配。 通俗来说,栈就是用来放引用的,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。 而堆是用来放对象和数组的,在Java中,这些对象和数组是由new来创建的,而在堆中分配的内存,将由Java虚拟机的自动垃圾回收器来管理。 关于实例中的关系,只要你能理解上文中提到过的clone()方法,理解起来非常轻松。 9.==和equals 作为经常出现在condition里的判断方法,区分==和equals是非常重要的,尤其是在安卓的开发中。 关于两者的比较,我们要联合前文提到的堆和栈来理解。 ”==“是比较两个变量栈中的内容是否相等,而与之相对应的”equals”则是比较堆中的内容是否相等。 让我们来举例说明: publicclassTest{ publicstaticvoidmain(String[]args){ Strings1="Daniel"; Strings2=newString("Daniel"); if(s1==s2){ System.out.println("s1==s2"); }else{ System.out.println("s1! =s2"); } if(s1.equals(s2)){ System.out.println("s1equalss2"); }else{ System.out.println("s1notequalss2"); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 本例中输出的为s1! =s2以及s1equalss2。 由此不难理解,s1与s2地址不同,但内容相同。 10.反射机制
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 面试 宝典