Java面试题之IO流、反射、序列化、注解、多线程
JAVA面试题汇总
1. JAVA中的IO面试题
面试题一:常用的io包下的类有哪些?
1.InputStream
(1)缓冲区流:BufferedInputStream
(2)对象处理流:ObjectInputStream
(3)管道流:PipedInputStream
(4)数据流:DataInputStream
(5)独立缓冲区流:ByteArrayInputStream
(6)文件流:FileInputStream
2.OutputStream
(1)缓冲区流:BufferedOutputStream
(2)对象处理流:ObjectOutputStream
(3)管道流:PipedOutputStream
(4)数据流:DataOutputStream
(5)独立缓冲区流:ByteArrayOutputStream
(6)文件流:FileOutputStream
3.Read
(1)BufferedReader
(2)FilerReader
4.Writer
(1)BufferedWriter
(2)FileWriter
5.桥梁流
(1)字节流和字符流之间的转换
InputStreamReader: 字节流转换为字符流的桥梁
OutputStreamWriter: 字符流转换为字节流的桥梁
6.File文件辅助类,得到文件信息
(1)本身并不对文件内容进行操作
①比如:得到作者,什么时候创建的这个文件,这个文件的大小等等
②也可以判断这个文件存在不存在,也可以删除文件
2. JVM反射面试题
面试题一:除了使用new创建对象之外,还可以用什么方法创建对象?
(1)利用反射创建对象
(2)利用对象处理流可以创建
面试题二:Java反射创建对象效率高还是通过new创建对象的效率高?
new的效率高
面试题二:哪里会用到反射机制
(1)Class.forName(‘com.mysql.jdbc.Driver.class’);//加载MySQL的驱动类
(2)很多框架都用到反射了
3. JAVA序列化面试题
面试题一:什么是java序列化,如何实现java序列化?
(1)序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。
(2)序列化是为了解决在对对象流进行读写操作时所引发的问题。
(3)序列化的实现:将需要被序列化的类实现Serializable接
口即可,不需要重写任何方法,JVM帮我们处理
面试题二:Java实现序列化如何处理
实现Serializable接口
4. JAVA注解面试题
面试题一:4种标准元注解是哪四种?
元注解:元注解的作用是负责注解其他注解。
(1)@Target修饰的对象范围
比如:是包注解,类注解,成员注解(方法注解,属性注解等等)
(2)@Retention定义被保留的时间长短
Retention 定义了该 Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描述注解的生命周期(即:被描述的注解在
什么范围内有效),取值(RetentionPoicy)由:
- SOURCE:在源文件中有效(即源文件保留)
- CLASS:在 class 文件中有效(即 class 保留)
- RUNTIME:在运行时有效(即运行时保留)
(3)@Documented描述-javadoc
(4)@Inherited阐述了某个被标注的类型是被继承的
5. JAVA多线程&并发面试题
面试题一:Java中实现多线程有几种方法?
(1)继承Thread类;
(2)实现Runnable接口;并用new Thread(该接口实现类的对象)来创建对象
面试题二:继承Thread类,使用线程的方式(用代码实现)
public class MyThread extends Thread
{
public void run()
{
System.out.println(“MyThread.run()”);
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
面试题三:实现Runnable接口,使用线程的方式(用代码实现)
public class MyThread extends OtherClass implements Runnable
{ public void run()
{
System.out.println(“MyThread.run()”);
}
}
//启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start(); //事实上,当传入一个 Runnable target 参数给 Thread 后, Thread 的 run()方法就会调用
target.run()
public void run()
{
if (target != null)
{
target.run();
}
}
面试题四:如何停止一个正在运行的线程?
(1)使用stop方法强行终止,开发中不推荐用。
(2)使用标识退出线程(内部寻找标识,外部传入终止标识)
(3)使用interrupt方法中断线程
面试题五:notify()和notifyAll()有什么区别?
(1)所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个
而notifyAll唤醒所有
面试题六:sleep()和wait() 有什么区别?
(1)对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
(2)在调用sleep()方法的过程中,线程不会释放对象锁。占用资源
(3)而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
面试题七:Thread类中的start()和run()方法有什么区别?
(1)start()方法被用来启动线程的方法
(2)run方式是子线程的入口函数
面试题六:为什么wait,notify和notifyAll这些方法不在thread类里面?
明显的原因是JAVA提供的锁是对象级的而不是线程级的。
没有办法唤醒某一个等待队列里面对象了
面试题七:为什么wait和notify方法要在同步块中调用?
1.只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方法。
2.如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。
3.还有一个原因是为了避免wait和notify之间产生竞态条件。
wait()方法强制当前线程释放对象锁。这意味着在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait()方法。
在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁。因此,必须在某个对象的同步方法或同步代码块中才能调用该对象的notify()或notifyAll()方法。
调用wait()方法的原因通常是,调用线程希望某个特殊的状态(或变量)被设置之后再继续执行。调用notify()或notifyAll()方法的原因通常是,调用线程希望告诉其他等待中的线程:“特殊状态已经被设置”。
这个状态作为线程间通信的通道,它必须是一个可变的共享状态(或变量)。
面试题八:有三个线程T1,T2,T3,如何保证顺序执行?
利用join关键
合并线程:如果有两个线程,分别为t1和t2,在t2线程里面有一句话,t1.jion,那么先执行t1,再执行t2
面试题九:SynchronizedMap和ConcurrentHashMap有什么区别?
SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问
map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,
仍然可以对map执行某些操作。
所以,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优
势。同时,同步操作精确控制到桶,这样,即使在遍历map时,如果其他线程试图对map进行数据修
改,也不会抛出ConcurrentModificationException。
将上面的面试题里面的桶改成片段
面试题十:什么是线程安全?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
面试题十一:Thread类中的yield方法有什么作用?
Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行。
它是一个静态方法而且只保证当前线程放弃CPU占用而不能保证使其它线程一定能占用CPU,执行yield()的线程有可能在进入
到暂停状态后马上又被执行。
面试题十二:说一说自己对于synchronized关键字的理解?
synchronized关键字解决的是多个线程之间访问资源的同步性
synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能
有一个线程执行。
面试题十三:什么是线程安全?Vector是一个线程安全类吗?
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
是的,ArrayList是线程不安全的
面试题十四:线程生命周期(状态)?
在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5 种状态。
1.新建(New):当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值。
2.就绪(Runnable):当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行。
3.运行(Running):如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。
4.阻塞(Blocked):阻塞状态是指线程因为某种原因放弃了cpu使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才
有机会再次获得 cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
A. run()或 call()方法执行完成,线程正常结束。
B.线程抛出一个未捕获的 Exception 或 Error。调用 stop
C.直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。
5.死亡(Dead):线程会以下面三种方式结束,结束后就是死亡状态。
面试题十五:JAVA后台线程?
1.定义:守护线程–也称“服务线程”, 他是后台线程, 它有一个特性,即为用户线程 提供 公共服务, 在没有用户线程可服务时会自动离开。
2.优先级:守护线程的优先级比较低
3.设置:通过 setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在线程对象创建之前用线程对象的setDaemon方法。
4.在Daemon线程中产生的新线程也是Daemon的
面试题十六:什么是乐观锁?
乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
面试题十七:什么是悲观锁?
悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上
锁,这样别人想读写这个数据就会 block 直到拿到锁。
面试题十八:Synchronized 同步锁?
synchronized 它可以把任意一个非 NULL 的对象当作锁。 他属于独占式的悲观锁。
1.作用于方法时,锁住的是对象的实例(this);
2.当作用于静态方法时,锁住的是Class实例。因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程;
3.synchronized 作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。
面试题十九:Condition类和Object类锁方法区别?
- Condition类的awiat方法和Object类的wait方法等效
- Condition类的signal方法和Object类的notify方法等效
- Condition类的signalAll方法和Object类的notifyAll方法等效
- ReentrantLock类可以唤醒指定条件的线程,而object的唤醒是随机的
面试题二十:tryLock和lock和lockInterruptibly的区别?
- tryLock能获得锁就返回true,不能就立即返回false,tryLock(long timeout,TimeUnitunit),可以增加时间限制,如果超过该时间段还没获得锁,返回false
- lock能获得锁就返回true,不能的话一直等待获得锁
- lock和lockInterruptibly,如果两个线程分别执行这两个方法,但此时中断这两个线程,lock不会抛出异常,而lockInterruptibly会抛出异常。
面试题二十:公平锁与非公平锁?
公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得。
非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
1.非公平锁性能比公平锁高5~10倍,因为公平锁需要在多核的情况下维护一个队列
2.Java中的synchronized是非公平锁,ReentrantLock默认的lock()方法采用的是非公平锁。
面试题二十一:ReadWriteLock读写锁?
为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。
读锁如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁
写锁如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。
面试题二十二:共享锁和独占锁?
独占锁:每次只能有一个线程能持有锁
共享锁:则允许多个线程同时获取锁,并发访问共享资源
共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。
面试题二十三:线程基本方法?
对象里面线程相关方法:wait, notify, notifyAll
线程类里面提供的方法有:sleep, join, yield
面试题二十四:线程等待(wait)和线程休眠(sleep)以及线程让步(yield)概念?
线程等待(wait):调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用wait()方法后,会释放对象的锁。因此,wait方法一般用在同步方法或同步代码块中。
线程睡眠(sleep):sleep导致当前线程休眠,与wait方法不同的是sleep不会释放当前占有的锁,sleep(long)会导致线程进入TIMED-WATING状态,而wait()方法会导致当前线程进入WATING状态。
yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。
面试题二十五:为什么要用join()方法?
主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到join()方法。
面试题二十六:Java线程池工作过程?
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面
有任务,线程池也不会马上执行它们。 - 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要
创建非核心线程立刻运行这个任务;
d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池
会抛出异常 RejectExecutionException。 3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。 - 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运
行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它
最终会收缩到 corePoolSize 的大小。
面试题二十七:死锁与活锁的区别,死锁与饥饿的区别?
死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生死锁的必要条件:
1、互斥条件:所谓互斥就是进程在某一时间内独占资源。
2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3、不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”,而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。
饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。
Java中导致饥饿的原因:
1、高优先级线程吞噬所有的低优先级线程的CPU时间。
2、线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总 是能在它之前持续地对该同步块进行访问。
3、线程在等待一个本身也处于永久等待完成的对象(比如调用这个 对象的wait方法),因为其他线程总是被持续地获得唤醒
面试题二十八:什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)?
- 原子操作(atomic operation)意为”不可被中断的一个或一系列操作”。
2.原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。
3.int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。
4.为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。
面试题二十九:Java Concurrency API中的Lock接口(Lock interface)是什么?对比同步它有什么优势?
1.Lock接口比同步方法和同步块提供了更具扩展性的锁操作。换句话说,就是提供了更多的方法。
2.同步方法和同步块他们只是提供了申请资源,但是,没有提供更灵活的状态处理,比如:tryLock:申请到资源那就申请到了并且返回true,没有申请到资源返回false。lock:申请到了那就申请到了,没有申请到资源就等着。
面试题三十:什么是Callable和Future?
1.首先,要知道什么是Runnable接口,这个接口里面就一个方法的声明 public void run,这个是通用执行协议
2.所以,Java给我们提供了Callable接口,Callable接口类似于Runnable,从名字就可以看出来。一个调用,一个运行。
Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。可以认为
是带有回调的Runnable。
本篇文章主要讲解的是Java面试题的基础题的一部分,虽然本人是新手也在找工作,但是还需要对Java基础面试题做一部分总结。以上文章如有侵权请联系本人删除