JavaSE中面试题

1.Object类自带那些方法?

clone() equals() getClass() finalize() toString() wait() 3个 notify() notifyAll() hashCode()
protected Object clone() //创建并返回此对象的一个副本。
boolean equals(Object obj) //指示某个其他对象是否与此对象“相等”。
protected void finalize() //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
Class<? extends Object> getClass() //返回一个对象的运行时类。
int hashCode() //返回该对象的哈希码值。
void notify() //唤醒在此对象监视器上等待的单个线程。
void notifyAll() //唤醒在此对象监视器上等待的所有线程。
String toString() //返回该对象的字符串表示。
void wait() //导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
void wait(long timeout) //导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或notifyAll() 方法,或者超过指定的时间量。
void wait(long timeout, int nanos) //导致当前的线程等待,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

2.对String了解多少?

1.String类是被final修饰,因此是不可以继承的,是不可变的;
2.String类的本质是字符数组char[];
3.Java运行时会维护一个String 池,JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放运行时中产生的各种字符串,并且创建的对象仅仅存在于方法的堆栈区。

3.String、StringBuffer、StringBuilde的区别?

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

4.collection和collections的区别?

Collection是集合类的上级接口,继承与他的接口主要有Set 和List。Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

5.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?

Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。

6.从底层区分下ArrayList和LinkedList,Arraylist、LinkedList、HashMap的初始大小以以及如何扩容?

ArrayList

ArrayList的底层实现为数组存储在内存中,线程不同步。可通过数组下标的形式进行查找,所以在查询方面的效率较为出色,常用在查询较多的情景下。

LinkedList

LinkedList的底层实现为链表形式,也为线程不同步。而链表的底层也决定了它在查询方面不如数组底层的ArrayList而在指定位置插入等修改操作下,性能优于ArrayList。

1.ArrayList

ArrayList 初始化大小是 10 (如果你知道你的arrayList 会达到多少容量,可以在初始化的时候就指定,能节省扩容的性能开支)
扩容点规则是,新增的时候发现容量不够用了,就去扩容
扩容大小规则是,扩容后的大小= 原始大小+原始大小/2 + 1。(例如:原始大小是 10 ,扩容后的大小就是 10 + 5+1 = 16)
(扩容的规则也在源码中有体现,扩容后的大小= 原始大小+原始大小/2 + 1。在进行插入等操作的时候,如果判断出大小不够,会依据此方法进行扩容。(以上是JDK1.6版本的源码,在JDK1.7中扩容规则进行了修改,改为了扩容后的大小= 原始大小+原始大小/2))

2.linkedList

linkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
对于双向链表的理解

3.HashMap

HashMap 初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子)
扩容机制.(当前大小 和 当前容量 的比例超过了 扩容因子,就会扩容,扩容后大小为 一倍。例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12的时候,比例已经是0.75 。触发扩容,扩容后的大小为 32.)

7.HashMap、Hashtable的区别?

hashMap去掉了HashTable 的contains方法,但是加上了containsValue()和containsKey()方法。
hashTable同步的,而HashMap是非同步的,效率上逼hashTable要高。
hashMap允许空键值,而hashTable不允许。

8.HashMap的底层?

HashMap是基于哈希表的Map接口的非同步实现,Java最基本数据结构就是两种,一种是数组,一种是引用。所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。Entry就是数组中的元素,每个 Map.Entry 其实就是一个key-value对,它持有一个指向下一个元素的引用,这就构成了链表。

9.HashMap、LinkedHashMap、ConcurrentHashMap的异同?

①LinkedHashMap:

1.LinkedHashMap是有序的
2.每次访问一个元素(get或put),被访问的元素都被提到最后面去了
3.非线程安全

② ConcurrentHashMap:

线程安全,使用了分段锁,在线程需要安全的时候,使用ConcurrentHashMap替代LinkedHashMap

③hashMap

1.遍历顺序却是不确定的。
2.2.HashMap最多只允许一条记录的键为null,允许多条记录的值为null。
3.HashMap非线程安全

10.Java中HashMap的key值要是为类对象,则该类需要满足什么条件?

必须重写equals()和hashCode()方法。

11.Comparable和Comparator接口是干什么的?列出它们的区别。

区别:Comparable 接口用于定义对象的自然顺序,而 comparator 通常用于定义用户定制的顺序。Comparable 总是只有一个,但是可以有多个 comparator 来定义对象的顺序。

12.什么是流?按照传输的单位,分成哪两种流?他们的父类叫什么?

JAVA程序中对数据的输入输出称为流
1》流分为 字节流和字符流
  2》字节流继承inputStream和OutputStream
  3》字符流继承自InputSteamReader和OutputStreamWriter

13.什么叫对象序列化,什么是反序列化,如何实现对象序列化

把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
实现Seializable这个接口

14.开启线程的三种方式

1.继承Thread类,重写run方法。

缺点:java是单继承,继承了Thread类,不可继承其他类,不方便扩展。
开发中:少用。

2.实现Runnable接口,实现run方法。

优点:该类实现了Runnable接口,同时也可以继承其他类,可扩展。
开发:常用。

3.实现Callable接口,重写call方法。

优点:可以获取返回值,可扩展。
开发:目前遇到过Oracle底层代码中使用过此方法。

15.线程之间是如何通信的

当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。

16.在Java中wait和seelp方法的不同

sleep()来自Thread类,和wait()来自Object类.调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁

sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU

sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait()需要配合notify()或者notifyAll()使用

17.谈谈ThreadLocal关键字

什么是线程局部变量ThreadLocal

线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。Java提供ThreadLocal类来支持线程局部变量,是一种实现线程安全的方式。但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。

ThreadLoal的作用是什么?

简单说ThreadLocal就是一种以空间换时间的做法在每个Thread里面维护了一个ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了.

18.run()和start()方法区别

start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

19.什么是线程池,为什么要用线程池,说出几种常见的线程池

线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程。线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求。然而,增加可用线程数量是可能的。线程池中的每个线程都有被分配一个任务,一旦任务已经完成了,线程回到池子中并等待下一次分配任务。
使用线程池的好处主要有以下几点:
(1)因为在请求时,线程已经存在,从而消除了线程创建所带来的延迟
(2)通过调整线程池中的线程数目,可以有效防止资源不足
常用的几种线程池:
1.newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2.newFixedThreadPool
创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
3.newSingleThreadExecutor
创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
4.newScheduleThreadPool
创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

20.描述一下线程的生命周期(描述5个状态)

线程是一个动态的执行过程,他也有一个从产生到死亡的过程。
线程的生命周期有五种状态:
新建(new Thread):当创建Thread类的一个实例时,此线程进入新建状态(未被启动)
就绪(Runnable):该线程的实例调用start()方法,线程被启动,或者线程从阻塞状态进入到就绪状态,正在等待被分配cpu资源(时间片);
运行(running):线程获得了cpu资源正在执行run()方法,此时除非此线程自动放弃cpu资源或者有优先级跟高的线程进入,线程将一直运行到结束。
死亡(dead):当线程执行完毕或被其他线程杀死,线城就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法,error或exception让一个线程终止运行
阻塞(blocked):由于某种原因导致正在运行的现线程让出cpu并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t)方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用notify方法回到就绪状态)。

21.、为什么会发生死锁,如何避免死锁

死锁,它是操作系统或软件运行的一种状态:在多任务下,当一个或多个进程等待系统资源而资源又被系统本身或其它进程占用时,就形成了死锁。
死锁发生的最常见形式是两个或多个线程等待被另一个线程占用的资源:
在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免发生死锁。

22.多线程中的锁有哪些种类,说下区别

一、公平锁/非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。

二、可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

三、独享锁/共享锁

独享锁是指该锁一次只能被一个线程所持有。
共享锁是指该锁可被多个线程所持有。

四、互斥锁/读写锁

上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock

五、乐观锁/悲观锁

乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。
悲观锁认为对于同一个数据的并发操作,一定是会发生修改的,哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。
乐观锁则认为对于同一个数据的并发操作,是不会发生修改的。在更新数据的时候,会采用尝试更新,不断重新的方式更新数据。乐观的认为,不加锁的并发操作是没有事情的。
从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升。

23.Synchronized有什么缺陷

如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
2)线程执行发生异常,此时JVM会让线程自动释放锁。

24.并行与并发的异同

并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生。
并行是在不同实体上的多个事件,并发是在同一实体上的多个事件。
在一台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如hadoop分布式集群。
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。

25.Java如何实现并发

1.悲观锁的实现:悲观的认为所有代码执行都会有并发问题,所以将所有代码块都用sychronized锁住
2.乐观锁的实现:乐观的认为在读的时候不会产生冲突为题,在写时添加锁。所以解决的应用场景是读远大于写时的场景。
3.实用String.intern()实现:类String维护了一个字符串池。当调用intern方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals(Object)方法确定),则返回池中的字符串。可见,当String 相同时,总返回同一个对象,因此就实现了对同一用户加锁。由于所的颗粒度局限于具体用户,使得系统获得最大程度的并发。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值