Java面试题总结
1、java八种基本数据类型的大小、以及它们的封装类.
答:byte(Byte) : 8位,short(Short) : 16位, int(Integer):32位, long(Long):64位 , float(Float) 32位, double(Double):64位, char(Char) 16位, boolean(Boolean) 1位;
2、Switch能否用String做参数?
答:可以,Switch支持byte、short、char、int、enum、String(1.7支持)作为参数。
3、equals与==的区别?
答:首先“==”是运算符,而equals是方法。“==”比较的是两个对象的地址是否相同,equals默认比较的也是地址。但String、Integer的equals的方法被重写,用来比较两个对象的值是否相同。
4、Object有哪些公有方法?
答:equals(比较两个对象地址是否相同)、hashcode(用来查找对象的地址)、getClass(获得对象的类)、wait、notify、notifyAll(线程间通信)、finallize(对象被销毁之前会调用该方法,使用该方法不代表一定会被JVM回收)、toString、clone
5、Java的四种引用、各自的应用场景
答:首先明确java提供这四种引用的目的:
(1)可以让程序员通过代码的方式决定某些对象的生命周期
(2)有利于JVM进行回收
强引用:指创建一个对象,并把该对象赋给一个应用变量
例如:Object o = new Object();
String str = "hello world"
强引用的对象不会被JVM回收
软引用:如果一个对象被软引用,只要有足够的内存空间,就不会被回收
软引用可用来实现图片缓存,网页缓存,还能防止内存泄漏
弱引用:弱引用用来描述非必须对象,无论内存是否充足都有可能被回收
虚引用:不影响对象的生命周期、对象就跟没有引用一样
http://www.cnblogs.com/huajiezh/p/5835618.html
6、HashCode的作用?
答:HashCode使用来方便查找对象的,两个对象如果相同(即用equals比较返回true),则hashcode一定相同。如果equals方法被重写,hashcode也要重写。如果两个hashcode相同,则两个对象不一定相同。类比HashMap查找key的过程。
7、ArrayList、LinkedList、Vector的区别。
答:ArrayList是基于动态数组实现的(线程不安全),LinkedList是双向链表(线程不安全),vector是数组容器(线程安全)
对于ArrayList,在多线程情况下可以使用Collections.synchronizedList返回一个线程安全的ArrayList类,或者使用concurrent包下的CopyOnWriteArrayList类。ArrayList的初始容量为10,超出的话会扩容(变为原始容量的1.5倍+1),扩容时会将老的元素拷贝到新的数组中,这样比较耗费资源,因此在使用ArrayList时最好指定初始容量大小,这样能避免多次扩容。
8、String、StringBuffer与StringBuilder的区别。
答:String是字符串常量、StringBuffer是字符串变量(线程安全)、StringBuilder是字符串变量(线程不安全)
执行速度上,StringBuilder > StringBuffer > String
为什么String执行速度慢呢?因为String是定义为final的,即不可变的,String添加子串的时候,会在常量池重新生成一个String。
例如:
String s= "ABC";
s += "abc";
如果要操作少量的数据用 String。
单线程操作字符串缓冲区下操作大量数据 StringBuilder。
多线程操作字符串缓冲区下操作大量数据 StringBuffer。
9、Collection包结构,与Collections的区别?
答:Collection包的结构如下图:
Collections是一个工具类,服务于Java集合框架,不能实例化
Collections的常用方法有:sort() binarySearch() shuffle() min() max() copy() fill()
10、HashMap与HashTable的区别?
答:HashMap根据键的HashCode存储数据,线程不安全、允许null键和值、初始容量为16,负载因子为0.75,扩容之后为之前的两倍。JAVA1.8的HashMap是基于数组+链表+红黑树实现的,当链表长度大于8时,转变为红黑树。
HashTable功能与HashMap相同,但是任一时间只能有一个线程访问HashTable,并发性能不如ConcurrentHashMap,在单线程情况下使用HashTable、多线程情况下使用ConcurrentHashMap。
详见:Java8系列之重新认识HashMap
11、TreeMap、HashMap、LinkedHashMap的区别。
答:TreeMap的key值是默认升序排列的,可以指定排序规则。HashMap默认无序排列,LinkedHashMap按照插入的顺序排列
12、try catch finally,try里有return,finally还执行么?
答:1、不管有木有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的;
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。
13、Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。
答:
情况一:
Java.lang.OutOfMemoryError: Java heap space 堆内存不够、或者程序中有死循环。
如果是java堆内存不够的话,可以通过调整JVM下面的配置来解决:
< jvm-arg>-Xms3062m < / jvm-arg>
< jvm-arg>-Xmx3062m < / jvm-arg>
(-Xms调整初始堆大小 ,-Xmx调整最大堆大小)
详见JVM调优总结
情况二:
java.lang.OutOfMemoryError: GC overhead limit exceeded
【解释】:JDK6新增错误类型,当GC为释放很小空间占用大量时间时抛出;一般是因为堆太小,导致异常的原因,没有足够的内存。
【解决方案】:
1、查看系统是否有使用大内存的代码或死循环;
2、通过添加JVM配置,来限制使用内存:
< jvm-arg>-XX:-UseGCOverheadLimit< /jvm-arg>
情况三:
java.lang.OutOfMemoryError: PermGen space:这种是P区内存不够,可通过调整JVM的配置:
< jvm-arg>-XX:MaxPermSize=128m< /jvm-arg>
< jvm-arg>-XXermSize=128m< /jvm-arg>
【注】:
JVM的Perm区主要用于存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space,这个区域成为年老代,GC在主程序运行期间不会对年老区进行清理,默认是64M大小,当程序需要加载的对象比较多时,超过64M就会报这部分内存溢出了,需要加大内存分配,一般128m足够。
情况四:
java.lang.OutOfMemoryError: Direct buffer memory
调整-XX:MaxDirectMemorySize= 参数,如添加JVM配置:
< jvm-arg>-XX:MaxDirectMemorySize=128m< /jvm-arg>
情况五:
java.lang.OutOfMemoryError: unable to create new native thread
【原因】:Stack空间不足以创建额外的线程,要么是创建的线程过多,要么是Stack空间确实小了。
【解决】:由于JVM没有提供参数设置总的stack空间大小,但可以设置单个线程栈的大小;而系统的用户空间一共是3G,除了Text/Data/BSS /MemoryMapping几个段之外,Heap和Stack空间的总量有限,是此消彼长的。因此遇到这个错误,可以通过两个途径解决:
1.通过 -Xss启动参数减少单个线程栈大小,这样便能开更多线程(当然不能太小,太小会出现StackOverflowError);
2.通过-Xms -Xmx 两参数减少Heap大小,将内存让给Stack(前提是保证Heap空间够用)。
【情况六】:
java.lang.StackOverflowError
【原因】:这也内存溢出错误的一种,即线程栈的溢出,要么是方法调用层次过多(比如存在无限递归调用),要么是线程栈太小。
【解决】:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。
14、interface与abstract类的区别。
答:一个类可以实现多个interface,但却只能继承一个abstract类。interface定义了一种行为规范,强调特定功能的实现,abstract类定义了事物的本质,强调的是事物的关系,两者都不可实例化。
详见:abstract class和interface的区别
15、锁的等级
方法锁、对象锁、类锁
对象锁使用synchronized修饰方法,或者修饰this,例如:
public class DemoClass
{
public synchronized void demoMethod(){}
}
或者
public class DemoClass
{
public void demoMethod(){
synchronized (this)
{
//other thread safe code
}
}
}
或者
public class DemoClass
{
private final Object lock = new Object();
public void demoMethod(){
synchronized (lock)
{
//other thread safe code
}
}
}
类锁修饰静态方法,或者直接修饰类名.class
例如:
public class DemoClass
{
public synchronized static void demoMethod(){}
}
或者
public class DemoClass
{
public void demoMethod(){
synchronized (DemoClass.class)
{
//other thread safe code
}
}
}
或者
public class DemoClass
{
private final static Object lock = new Object();
public void demoMethod(){
synchronized (lock)
{
//other thread safe code
}
}
}
16、写出生产者消费者模式
答:使用notify、wait实现
要注意,wait要在while里面使用
public class Main {
public static void main(String args[]) throws InterruptedException {
int maxSize = 10;
Queue<Integer> queue = new LinkedList<>();
Producer1 producer1 = new Producer1(queue,maxSize,"Producer1");
Consumer1 consumer1 = new Consumer1(queue,maxSize,"Consumer1");
producer1.start();
consumer1.start();
}
}
class Producer1 extends Thread {
private Queue<Integer> queue;
private int maxSize;
public Producer1(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.maxSize = maxSize;
this.queue = queue;
}
public void run() {
while (true) {
synchronized (queue) {
while (queue.size() == maxSize) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int i = new Random().nextInt();
queue.add(i);
queue.notifyAll();
}
}
}
}
class Consumer1 extends Thread {
private Queue<Integer> queue;
private int maxSize;
public Consumer1(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.maxSize = maxSize;
this.queue = queue;
}
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.remove();
queue.notifyAll();
}
}
}
}
17、ThreadLocal的设计理念与作用
答:
它是一种线程局部变量,即给在每个线程上分配一个局部变量的副本,这样每个线程都可独立更改各自的变量而互不影响。ThreadLocal类是使用Map来存放值的。
ThreadLocal的应用场景:用来解决数据库连接和Session管理等。
详见:Java并发编程:深入剖析ThreadLocal
18、ThreadPool用法与优势。
答:线程池减少了创建和销毁线程的次数,每个工作线程都可以重复利用,可执行多个任务,有四种线程池:
1. newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2.newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
3. newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。这个线程池看起来比较完美,能够只能调整线程池大小,并且回收空闲线程
4.newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
19、Concurrent包的内容
答:
主要包括
参见: Java 并发工具包 java.util.concurrent 用户指南
20、wait和sleep的区别
答:执行wait之后,会放弃同步锁,进入等待队列,可由notify唤醒,唤醒之后状态变为可运行状态,等待cpu时间片(注意:wait和notify要在synchronized中使用)。sleep执行之后不会放弃同步锁。
21、foreach与for效率比较
答:需要循环数组结构的数据时,建议使用普通for循环,因为for循环采用下标访问,对于数组结构的数据来说,采用下标访问比较好。
需要循环链表结构的数据时,一定不要使用普通for循环,这种做法很糟糕,数据量大的时候有可能会导致系统崩溃。】
详情见链接:for遍历和foreach遍历的性能比较
为什么使用迭代器iterator遍历Linkedlist要比普通for快
22、反射的作用与原理
答:反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法;对于任意一个对象都能调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能成为java语言的反射机制。
获取Class的三种方法
Car car = new Car();
Class cls1 = car.getClass();
Class cls2 = Car.class;
Class cls3 = Class.forName("类的全名称")
通过反射可以获取类中所有的信息,包括方法名、返回值和方法参数
例如:
Class c = obj.getClass();
System.out.println("类的名称是:" + c.getName());
Method[] methods = c.getMethods();
for(int i = 0; i < methods.length; i++) {
Class returnType = methods[i].getReturnType();
System.out.print(returnType.getName() + " ");
System.out.print(methods[i].getName() + "(");
Class[] parameterizedTypes = methods[i].getParameterTypes();
for(Class c1 : parameterizedTypes) {
System.out.print(c1.getName() + ",");
}
System.out.println(")");
}
方法的反射:method.invoke(对象,参数列表)
23、java1.8新特性
答:PermGen空间被移除,用Metaspace取代。
函数式接口、Lambda表达式等