说明:本面试题大多都是根据《Java面试问题集》、《Java程序员面试宝典》、《程序员面试宝典》等书籍概括精选而来,其中也补充了一些自己在找工作的过程中遇到的面试题,虽然不能遍及Java的所有知识点,但绝对都是面试会问到的高频知识点。
31.面向对象有哪些特点?
四个方面:抽象,封装,继承,多态。
http://seapigxie.iteye.com/blog/1109925
抽象是忽略一个主题中与当前目标无关的方面,把现实世界中的概念转换为对象,从众多变化的状态中抽离出不变的东西。比如,我们把汽车抽象为一个类或者接口,这个汽车类只有加油,发动方法,忽略了其它不相关的因素。抽象的好处在于将复杂的东西简单化,有利于使用继承,实现多态。
继承是一种连结类的层次模型,有利于实现类的重用,提供了一种明确表示共性的方法。比如,我们先定义一个汽车类,然后让卡车类,轿车类,公交车类都继承这个汽车类,从而可以使用汽车类中已经定义好的加油和发动等方法。继承的好处是使得层次关系更加清晰,重用父类中的数据,同时还可以声明自己的新的方法,还可以代替或者扩展父类的方法。
多态是指一个对象变量可以指向多种实际类型的现象。多态需要用到动态绑定,即虚拟机必须调用对象变量所指向的对象的实际类型和匹配的方法版本。方法的重载和重写就是多态的体现。
封装是把数据和行为结合在一个包中,并对对象的使用者隐藏数据有关的实现过程,封装赋予对象“黑盒”特性。比如,我们调用汽车的发动方法就可以使得汽车发动起来,却不需要了解发动的其中细节。
32.String是最基本的数据类型吗?
Java中的基本数据类型有:boolean,char,byte,short,int,long,float,double,void
String不是最基本的数据类型,而是public final class String
补充:Java中的所有数值类型都是有正负号的,没有无符号数这一说法。
33.int和Integer有什么区别?
int是Java的基本数据类型,而Integer是针对int设计的包装类。
在Java中,每种基本数据类型都有对应的包装类。
int的默认值为0,Integer的默认类型为null。在设计JavaBean的时候,属性的类型最好使用包装类,这样,即使赋值为null也不会抛异常,而是用基本数据类型的话,当赋值为null是会抛异常的。
此外,Integer还提供了多个与整数相关的操作方法,例如: Integer.parseInt(“123”);
可以将字符串转换成整数;
Integer.MAX_VALUE表示最大整数值,Integer.MIN_VALUE表示最小整数值。
34.运行时异常与一般异常有什么不同?
一般异常在程序中必须被捕获或者声明抛出该异常,然后交给相应的处理程序处理;而运行时异常(ClassCastException…)不需要被捕获,出现这种异常一般由虚拟机来处理。
35.说出ArrayList,Vector和LinkedList的存储性能和特性
ArrayList和Vector是采用数组(顺序表)的方式来存储数据的,它们都允许动态增加存储空间,可以直接按序号索引元素,然而插入和删除操作效率较低。Vector是线程安全的,所以性能上较ArrayList较差。
LinkedList采用双向链表的方式来存储数据的,索引数据需要遍历整个表,但是插入和删除操作的效率很高。
36.HashMap和HashTable的区别
HashTable是线程安全的,而HashMap则不是,所以效率上,HashMap较高。
HashMap允许有null的键和值,但是HashTable不行。
37.Heap(堆)和Stack(栈)有什么区别?
堆用来存放由new关键字创建的对象和数组,在堆中分配的内存,由Java虚拟机的垃圾回收器进行管理。
栈是进程在运行过程中的存储区域,主要存放局部变量(一些基本类型数据和对象的引用)和操作数,具有先进后出的特性。比如说递归调用就是用到栈。
38.Java中的异常处理的简单原理和应用
当程序违反了Java的语义规则时,JVM就会将发生的错误表示为一个异常。违反语义规则有两种情况,一是违反Java的内置类库语法检查,比如空指针异常,数组越界异常等;二是用户自己定义的异常。当异常发生时,需要抛出异常,然后需要有处理异常的程序对异常进行捕获,然后进行处理。所有的异常都是java.lang.Throwable的子类。
39.你所知道的集合类都有哪些?主要方法?
集合类大致分为两种Collection和Map。
Collection下面有List,Queue,Set等,其中List的实现类主要有ArrayList,LinkedList和Vector等;Set的实现类主要有HashSet,TreeSet等。
Map的实现类主要有HashMap,HashTable和TreeMap等。
主要方法:size()等等。
40.简要描述一下JVM加载class文件的原理机制。
JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
41.排序都有哪几种方法?请列举。
排序算法分为内部排序(只使用内存)和外部排序(内存+硬盘)。
内排序主要有:
插入排序:直接插入排序、希尔排序
选择排序:简单选择排序、堆排序
交换排序:冒泡排序、快速排序
归并排序
基数排序
详情可见:
http://blog.csdn.net/u012050416/article/details/50681591
42.Java中如何进行异常处理,关键字throws,throw,try,catch,finally分别代表什么意思,在try块中可以抛出异常吗?
Java将各种不同的异常进行了分类,并且提供了良好的接口。每个异常都是一个对象,是Throwable类或者其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。
try是用来包含一块可能出现异常的代码,紧接在try后面的catch来捕捉将要抛出的异常。,throw是在程序中用来抛出异常的语句,throws是放在方法名后面用来声明可能会抛出的异常的类型。finnally则是用来放置不管发生异常与否都会执行的一段代码。try块中当然可以抛出异常,只需要在try后面捕捉到该异常进行处理即可,或者是在方法名后面将该异常抛出给上级代码来处理。
43.一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?
可以,但是只能有一个类的类名与文件名相同,并且只能是与文件名相同的类为public。
public class B{}
class c{}
44.Java中有几种类型的流?JDK为每种类型的流提供了哪些抽象类以供继承?
主要分为字节流和字符流。
字节流的抽象类有InputStream和OutputStream。
字符流的抽象类有Reader和Writer。(OutputStreamWriter不是抽象类)
在java.io包中还有许多其它的流,主要是为了提高性能和使用的方便。
45.解释一下内存泄露(Memory Leak)和内存溢出(Out Of Memory)的区别
内存溢出是指程序在申请内存的时候,没有足够的内存空间供其使用,此时就会抛出Out Of Memory异常。
内存泄露是指程序在申请内存后无法释放已经申请的内存。一次内存泄露危害可以忽略,但多次内存泄露堆积后果会很严重,最终会造成内存溢出。
46.Java中会出现内存泄露吗?
Java中虽然有垃圾回收机制自动管理内存回收,但也会出现内存泄露的情况。
1)静态集合类像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,所持有的对象Object也不能被释放,因为它们将一直被Vector等引用着。
看如下代码:
Vector v = new Vector(10);
for (int i = 1; i<100; i++){
Object o = new Object();
v.add(o);
o = null;
}
虽然对象的引用o被赋值为null,但是这个对象被Vector引用了,所以该对象不会被GC回收,这样就很可能会导致内存泄露。
2)内部类和外部类的引用容易出现内存泄露的问题。
3)监听器的使用,java中往往会使用到监听器,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。
4)大量临时变量的使用,没有及时将对象设置为null也可能导致内存的泄露。
5)数据库的连接没有关闭情况,包括连接池方法连接数据库,如果没有关闭ResultSet等也都可能出现内存泄露的问题。
47.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知JVM进行垃圾回收?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。通常,GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当GC确定一些对象为”不可达”时,GC就有责任回收这些内存空间。
一般,垃圾回收器会自动地进行垃圾回收,程序员也可以手动调用System.gc()来通知垃圾回收器(GC)运行。但是不保证GC会立马执行回收操作。
48.静态变量和实例变量的区别
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。
静态变量在内存中只有一个,被类的所有实例共享,可以通过类名进行访问,其生命周期取决于类的生命周期。
每创建一个对象实例,JVM就会创建一个新的实例变量,实例变量必须通过实例对象来进行访问,其生命周期取决于实例的生命周期。
49.什么是Java的序列化,如何实现Java的序列化?
Java序列化能够将对象的状态写入字节流存储起来,也能从其他地方将字节流读取出来,重新构造一个新的对象。这种机制允许你将对象通过网络进行传播,并且可以随时把对象持久化到数据库,文件系统中。简而言之,序列化就是将一个对象的状态保存起来,而反序列化就是将已经保存的数据恢复成原来的对象。
实现序列化有一个条件,即实现序列化的类必须实现java.io.Serializable接口。之后,利用ObjectInputStream的readObject()方法和ObjectOutputStream的writeObject()方法进行对象的读写,即反序列化和序列化。
http://blog.csdn.net/csywwx2008/article/details/12359411
50.是否可以从一个static方法内部发出对非static方法的调用
不能。在static方法内部调用该非static方法的时候,不一定能保证该方法的对象已经实例化。
51.在Java中如何跳出当前的多重循环?
break;return;
continue是跳出当前循环。
52.List,Map和Set三个接口在存取元素时,各自拥有什么特点?
List通过add()方法来将指定的元素添加到尾部或者指定的位置,通过get()获取指定位置的元素,元素可以重复,可以为null;
Set通过add()方法来将元素放入集合中,但是顺序是由内部排序规则决定的,通过contains()方法判断集合中是否存在某个元素,元素不可以重复,最多只能有一个null。
Map通过put()方法来存放键值对,存放顺序是由内部排序规则决定,通过get()方法来获取某个键的值,在Map中,key不可以重复,但允许key和value为null。
53.说出一些常用的类,包和接口
类:InputStream, OutputStream, Reader, Writer, String
包:java.lang; java.io; java.util; java.sql; java.net; java.nio
接口:List, Set, Map, Serizable, Runnable, Cloneable
54.写clone()方法时候,通常都有一行代码是什么?
super.clone();
首先要把父类中的成员复制好,才能复制自己的成员。
影子克隆和深度克隆,在需要克隆的对象中的属性为复合数据类型时,需要使用深度克隆,此时需要在这个对象的类中重写clone方法。
class Empolyee implements Cloneable {
public Date date;
public Object clone() throws CloneNotSupportException{
//调用Object中的clone方法
Employee e = (Employee)super.clone();
e.date= (Date)date.clone;
return e;
}
}
参考:
http://www.cnblogs.com/o-andy-o/archive/2012/04/06/2434904.html
http://blog.csdn.net/shootyou/article/details/3945221
55.Java中实现多态的机制是什么?
多态表现为两种类型:静态多态和动态动态。
静态多态是指变量的隐藏,方法的重载等,也称为编译时多态。
动态多态是指子类重写父类(接口)的方法,程序中用父类的引用去指向子类的具体实例,从形式上看是父类引用去调用父类的方法,在实际运行过程中,JVM能够根据具体的子类去调用该子类的方法,从而表现为不同子类对象有多种不同的形态。程序在编译时还不能确定调用哪一个类的方法,只有运行时才能确定,所以动态多态也称为运行时多态。
方法的重写(运行时)和重载(编译时)就是多态的两种表现。见下例:
public class Father {
public String str1 = "Father";
public void print() {
System.out.println("hello,father");
}
}
public class Son extends Father{
public String str1 = "son";
public void print() {
System.out.println("hello,son");
}
}
public class Test3 {
public static void main(String[] args) {
Father son = new Son();
son.print();
System.out.println((son.str1);
}
}
运行结果为:
运行结果:
hello,son
Father
如果想使用子类的属性,应该这样写: System.out.println(((Son)son).str1);
上面的代码创建了两个对象,通过引用变量(son)来访问其实例属性(str1)时,系统总是试图访问编译时类所定义的属性(“Father”),而不是运行时类所定义的属性(“son”)。
56.使用final关键字修饰一个变量时,是指引用不能变还是说引用的对象不能变?
final修饰的变量是指引用不可变,引用所指向的对象的内容是可以改变的。比如:
final StringBuffer a = new StringBuffer(“hello”);
a.append(“,world!”);
上面这样写是完全可以的,a所指向的StringBuffer从”hello”变成了“hello,world”。
但是以下这样写是错误的:
final StringBuffer a = new StringBuffer(“hello”);
a = new StringBuffer(“aaa”);
因为我们想让原本指向hello的引用变成指向aaa的引用。
同样的,平时我们这样写也是错误的:
final int a = 5;
a = 6;
final String b = "123";
b = "234";
以上错在尝试改变引用。
最后,值得一提的是,有时候我们在定义方法的参数时,指定其为fianl类型,目标是希望这个参数在方法的内部不可变:
public void method(final StringBuffer param) {
}
这样是行不通的,我们在方法内可以通过param.append(“asd”);
来改变这个参数的内容。但是这样:
public void method(final int param) {
param = 5;
}
上面的param=5会报错,同样道理,这里改变的不是参数的对象,而是新建了个int的对象,改变了参数的引用。只是int类型没提供相应的改变自身内容的方法而已。
57.下面代码有什么不妥之处?
if(userName.equals(“mason”)) {
...
}
不妥:如果userName是null的话,会报出空指针异常,但是只要将上面的倒过来写就可以确保正常运行。
int x = 1;
return x == 1 ? true:false;
不妥:这句话本身没有错,可以正确编译,但是多此一举。x==1本身就可以返回true或者false。
58.子类是否一定要实现抽象父类或者接口中的抽象方法?
是的。当一个子类继承抽象父类或者实现接口的时候,必须实现其中所有的抽象方法,如果不实现所有的,那么该类必须声明为抽象的。
59.给出以下代码的运行结果
public class Test1 extends Date{
public static void main(String[] args) {
Test1 t = new Test1();
t.print();
}
public void print() {
String name = super.getClass().getName();
System.out.println(name);
}
}
以上代码的运行结果是什么?是Test1还是Date?
解析:首先需要知道的知识是getClass方法是定义在Object中的final方法。也就是说,子类只能继承却不能重写该方法。所以,this.getClass()和super.getClass()调用的都是Object中的getClass()方法。那么返回的是不是Object类?当然不是,返回的是Object的运行时类,此时Object的运行时类是其子类Test1,所以返回的是Test1。如果想要获取Date,那么应该这样写:this.getClass().getSuperClass().getClassName();
60.下面的语句一共创建了多少个对象?
String s = “a”+”b”+”c”+”d”;
Java编译器会对字符串常量相加的表达式进行优化,直接将其编译成一个这些常量相连的结果。于是,上面这句话总共就创建了两个对象,一个“abcd”字符串对象,另外一个String的引用对象。
String s1 = “a”+“b”;
System.out.println(s1 == “ab”);
结果为true。
String s2 = “a”;
String s3 = s2 + “b”;
System.out.println(s3 == “ab”);
结果为false。
所以,只对字符串常量相加的表达式有效。