java基础
1. 抽象、继承 和 多态
抽象:就是对一些具有共同特征的对象,抽象出一个具有共同特征的类;这个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、 设计中得出的抽象概念
继承:当两个类具有相同的特征(属性)和行为(方法)时,可以将相同的部分抽取出来放到一个类中作为父类,其它两个类继承这个父类。继承后子类自动拥有了父类的属性和方法,但特别注意的是,父类的私有属性和构造方法并不能被继承。
多态:相同的事务,调用其相同的方法,属性值也相同,但表现的行为却不同
三个必要条件: 继承,重写,向上转型
2. 泛型的作用及其使用场景
泛型定义类和接口的时候使用类型参数(type parameter)。声明的类型参数在使用时用具体的类型来替换。
作用:能够更好的保护数据类型,避免编译时出错。减少类型转换的代码。
使用场景:配合抽象类说
3. 枚举的特点及使用场景
它被用来将一组类似的值包含到一种类型当中。而这种枚举类型的名称则会被定义成独一无二的类型描述符,在这一点上和常量的定义相似。不过相比较常量类型,枚举类型可以为申明的变量提供更大的取值范围。
使用:
1,枚举常量没有任何修饰符
2,每个常量以“,”分隔,以“;”结束枚举常量的描述。
3,枚举常量必须定义在所有方法或者构造器之前
4. JAVA反射机制
只要有了java.lang.Class类 的对象,就可以通过其中的方法来获取到该类中的构造方法、域和方法。对应的方法分别是getConstructor、getField和getMethod。类继承自哪个类 实现了哪些接口 有哪些属性 有哪些方法 有哪些构造方法
5. weak/soft/strong引用的区别
1、强引用(StrongReference)
如果一个对象具有强引用,那垃圾回收器绝不会回收它。显式地设置o为null,或超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象。具体什么时候收集这要取决于gc的算法。
2、 软引用(SoftReference)
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
3、弱引用(WeakReference)
只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
4、虚引用(PhantomReference)
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
java集合类
1. JAVA常用集合类功能、区别和性能
List系列
-
ArrayList
ArrayList
以数组实现,允许重复。超出限制时会增加50%的容量(grow()方法中实现,如下所示),每次扩容都底层采用System.arrayCopy()
复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建数组的大小为10. -
LinkedList
LinkedList
以双向链表实现,允许重复。(如下Node
的实现)并保留头指针和尾指针。链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。
按下标访问元素—get(i)/set(i,e)
要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。 -
Vector
Vector
和ArrayList
几乎是完全相同的,唯一的区别在于Vector
是同步类(synchronized
),属于强同步类。Vector
每次请求其大小的双倍空间,而ArrayList每次对size增长50%. -
Stack
Stack
继承自Vector
,实现了一个后进先出的堆栈。
ArrayList和LinkedList的区别
-
ArrayList
是实现了基于动态数组的数据结构,LinkedList
基于链表的数据结构。 - 对于随机访问
get
和set
,ArrayList
觉得优于LinkedList
,因为LinkedList
要移动指针。 - 对于新增和删除操作
add
和remove
,LinkedList
比较占优势,因为ArrayList
要移动数据。
ArrayList和Vector的区别
-
Vector
和ArrayList
几乎是完全相同的,唯一的区别在于Vector
是同步类(synchronized),属于强同步类。因此开销就比ArrayList
要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList
而不是Vector
,因为同步完全可以由程序员自己来控制。 -
Vector
每次扩容请求其大小的2倍空间,而ArrayList
是1.5倍。 -
Vector
还有一个子类Stack
.
Map
-
HashMap
通过hash
的方法,通过put
和get
存储和获取对象。存储对象时,我们将K/V
传给put
方法时,它调用hashCode
计算hash
从而得到bucket
位置,进一步存储,HashMap
会根据当前bucket
的占用情况自动调整容量(超过Load Factor
则resize
为原来的2倍)。获取对象时,我们将K
传给get
,它调用hashCode
计算hash
从而得到bucket
位置,并进一步调用equals()
方法确定键值对。如果发生碰撞的时候,Hashmap
通过链表将产生碰撞冲突的元素组织起来,在Java 8
中,如果一个bucket
中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
HashMap
的容量(Capacity
)是 16,负载因子(load factor
是0.75 -
LinkedhashMap
LinkedHashMap
是Hash
表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序。非线程安全,LinkedHashMap
会记录插入的顺序,允许null
的键值,当key
值重复时,后面的会替换前面的。 -
TreeHash
红黑树实现 -
HashTable
HashMap
几乎可以等价于Hashtable
,除了HashMap
是非synchronized
的,并可以接受null
-
WeakHashMap
WeakHashMap
实现了Map
接口,是HashMap
的一种实现,它比HashMap
多了一个引用队列.
博主认真比对过WeakHashMap
和HashMap
的源码,发现WeakHashMap
中方法的实现方式基本和HashMap
的一样,注意“基本”两个字,除了没有实现Cloneable和Serializable
这两个标记接口,最大的区别在于在于expungeStaleEntries()
这个方法,这个是整个WeakHashMap
的精髓:每调用一次expungeStaleEntries()
方法,就会在引用队列中寻找是否有被清楚的key
对象,如果有则在table
中找到其值,并将value
设置为null
,next
指针也设置为null
,让GC去回收这些资源。 -
EnumMap
EnumMap
是专门为枚举类型量身定做的Map
实现。虽然使用其它的Map
实现(如HashMap
)也能完成枚举类型实例到值得映射,但是使用EnumMap
会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap
使用数组来存放与枚举类型对应的值。这使得EnumMap
的效率非常高。EnumMap
在内部使用枚举类型的ordinal()
得到当前实例的声明次序,并使用这个次序维护枚举类型实例对应值在数组的位置。
HashMap
与HashTable
的区别
-
HashTable
是线程安全的,多个线程可以共享一个Hashtable
;而如果没有正确的同步的话,多个线程是不能共享HashMap
的。 - 另一个区别是
HashMap
的迭代器(Iterator)是fail-fast
迭代器,而Hashtable
的enumerator
迭代器不是fail-fast
的。 -
Hashtable
和HashMap
它们两个内部实现方式的数组的初始大小和扩容的方式。HashTable
中hash
数组默认大小是11,增加的方式是 old*2+1。HashMap
中hash
数组的默认大小是16,而且一定是2的指数。
Set:
Set
几乎都是内部用一个Map
来实现, 因为Map
里的KeySet
就是一个Set
,而value
是假值,全部使用同一个Object
。Set
的特征也继承了那些内部Map
实现的特征。
HashSet
HashSet
是基于HashMap
来实现的,操作很简单,更像是对HashMap
做了一次“封装”,而且只使用了HashMap的key
来实现各种特性,而HashMap
的value
始终都是PRESENT
。
HashSet
不允许重复(HashMap
的key不允许重复,如果出现重复就覆盖),允许null
值,非线程安全。
LinkHashSet
LinkedHashSet
就是基于LinkedHashMap
实现的,允许null
值,保留插入顺序,非线程安全。
TreeSet
TreeSet
的内部基于TreeMap
实现,同样value
永远为PRESENT
.
不允许重复,不允许null
值(如果有基于null的比较器,就可以允许为null),默认按升序排列。
2.并发相关的集合类
-
Collections 类提供的并发集合
java.util.Collections提供了一系列方法将一个普通的集合包装成线程安全的集合,如Collections.synchronizedCollection() Collections.synchronizedSet()
等。
它们的实现很简单,Collections
内部定义了一系列集合类,它们的作用就是包装用户传进来的集合并把操作都代理给后者,唯一不同的是,这些内部集合类的每个方法都是synchronized
的,保证每个方法的互斥,虽然正确,但是效率不高,不推荐使用。
- JUC 提供的并发集合
ConcurrentHashMap
拆分锁
一个动作只会影响结构的一部分,则把整体拆分成若干部分,每个部分一个锁,部分A被锁不会影响部分B,从而提高并发程度。
hashcode 决定桶的位置,equals决定两个对象是否相同。
内部用若干 segment
保存 entry
;每个segment
是一个小hashmap
,它继承 ReentrantLock
,内部的 update
动作均须先加锁。segment
个数由参数concurrencyLevel
决定。
put/remove
首先找segmengt
,后者先加锁,再操作。put
插入时是插在链表头;remove
先找元素,再执行一个普通的链表节点删除操作。
Segment
的 rehash
是不加锁的,它先创建一个新的空数组,接着将元素 rehash
到该数组,最后将新数组和旧数组切换。
get/contains/iterator
读取操作不加锁,这是因为put/remove
动作对数据结构的改变最终是个原子动作(put
是一个对数组元素Entry
指针的赋值操作;remove
是一个对 entry.next
的赋值操作,rehash是一个对数组引用的赋值操作),因此read
不会看到一个更新动作的中间状态;但它可能和并发的put/remove
方法调用重叠,它所看到的状态是其所在Segment
在最后一个完成的update
动作后的状态,正在进行但未完成的put/remove
对read是不可见的,如果前者先于read
完成,read
是有可能看到脏数据的。
没有提供锁全部segment
的方法,size
的实现是先走几次fast-path
,即不加锁统计所有segment的count
和modcount
两次,如果modcount
发生改变,说明有并发操作,需要重新统计。如果重复该动作3次依然有问题,则依次对所有segment
加锁,统计count
。
CopyOnWriteArrayList
** copyonwrite **
所有的update动作都加锁,且对当前结构创建一个snapshot,在snapshot上完成update动作后,再将其转正,丢弃原结构 **
add/remove/set
均使用同一把 reentrantlock
实现互斥,并复制一份当前的数组,在该数组上完成write动作,最后用一个原子的引用赋值动作将snapshot
切换为当前数组;即内部数组永远不会改变结构(readonly),只会发生整个数组的切换。
get不加锁,和ConcurrentHashMap
类似,由于write
动作最终实质上是个原子的引用切换动作,因此get
看到的要么是修改完成前的数组,要么是完成后的数组,它不会看到一个不稳定的中间状态,它也是不用加锁的。read
看到的也是最后一个完成的write
后的数组,但很可能read
时依然有进行中的write
动作,这对read
而言是不可见的,但如果它先于read
完成,read
是有可能读到脏数据的。
CopyOnWriteArraySet:
基于 CopyOnWriteArrayList
实现,add
时创建数组副本,并用equals
判重。
不是hashset
那种实现,和hashcode
没关系。
BlockQueue接口
阻塞队列,它实质上就是一种带有一点扭曲的 FIFO
数据结构。它提供了可阻塞的put
和take
方法。如果Queue
已经满了,put
方法会被阻塞直到有空间可用;如果Queue
是空的,那么take
方法会被阻塞,直到有元素可用。Queue
的长度可用有限,也可以无限;无限的Queue
永远不会充满,所以它的put
方法永远不会被阻塞。阻塞队列支持生产者-消费者设计模式。
BlockingQueue
接口的几个主要的实现类:
-
ArrayBlockingQueue
:一个由数组支持的有界队列。 -
LinkedBlockingQueue
:一个由链接节点支持的可选有界队列。 -
PriorityBlockingQueue
:一个由优先级堆支持的无界优先级队列。 -
DelayQueue
:一个由优先级堆支持的、基于时间的调度队列。 -
SynchronousQueue
:一个利用BlockingQueue
接口的简单聚集(rendezvous)机制。
3. 部分常用集合类的内部实现方式
java线程
1. 线程sleep和wait的区别
Java程序中wait
和sleep
都会造成某种形式的暂停,它们可以满足不同的需要。wait()
方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()
方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。需要注意的是,sleep()
并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable
,并且根据线程调度,它将得到执行。
3. Thread、Runnable、Callable、Futrue类关系与区别
Thread
:
在java中可有两种方式实现多线程,一种是继承Thread
类,一种是实现Runnable
接口。但是最终被线程执行的是Runnable
,而非Thread
,Thread
仅仅是对于Runnable
的包装。
public class Thread extends Object implements Runnable```
`Runnable`:
它只有一个`run()`函数,用于将耗时操作写在其中,**该函数没有返回值**。然后使用某个线程去执行该`runnable`即可实现多线程,`Thread`类在调用`start()`函数后就是执行的是`Runnable`的`run()`函数
public interface Runnable {
public abstract void run();
}```
Callable
:
Callable
与Runnable
的功能大致相似,Callable
中有一个call()
函数,但是call()
函数有返回值,而Runnable
的run()
函数不能将结果返回给客户程序
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}```
`Future`:
`Future`就是对于具体的`Runnable`或者`Callable`任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过`get`方法获取执行结果,该方法会阻塞直到任务返回结果。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning); //取消任务
boolean isCancelled(); //判断任务是否被取消
boolean isDone(); //任务是否已经完成
V get() throws InterruptedException, ExecutionException;//用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException; //用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
}```
FutureTask
:
我们先来看一下FutureTask的实现:
public class FutureTask<V> implements RunnableFuture<V>```
`FutureTask`类实现了`RunnableFuture接口`,我们看一下`RunnableFuture`接口的实现:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}```
可以看出RunnableFuture
继承了Runnable
接口和Future
接口,而FutureTask
实现了RunnableFuture
接口。所以它既可以作为Runnable
被线程执行,又可以作为Future
得到Callable
的返回值。
FutureTask提供了2个构造器:
public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}```
事实上,FutureTask是Future接口的一个唯一实现类。
#### 4. wait、 sleep、 join 、yield、notify、 notifyAll
`wait`:
wait()方法使当前线程暂停执行并释放对象锁标志,让其他线程可以进入Synchronized数据块,当前线程被放入对象等待池中。
`sleep`:
使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。
`join`:
调用该方法的线程在此之前执行完毕,也就是等待调用该方法的线程执行完毕后再往下继续执行。注意该方法也要捕获异常。
`yield`:
停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么`Yield()`方法将不会起作用。
`notify`:
唤醒在此对象监视器上等待的单个线程。当它被一个`notify()`方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。
`notifyAll`:
notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中。
#### 5.JDK中默认提供了哪些线程池,有何区别
`newSingleThreadExecutor`
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
`newFixedThreadPool`
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
`newCachedThreadPool`
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
`newScheduledThreadPool`
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
#### 6. 线程同步有几种方式,分别阐述在项目中的用法
同步方法、同步代码块、Volatile、可重入锁ReentreentLock、局部变量ThreadLocal
#### 7. 在理解默认线程池的前提下,自己实现线程池
package mine.util.thread;
import java.util.LinkedList;
import java.util.List;
/**
-
线程池类,线程管理器:创建线程,执行任务,销毁线程,获取线程基本信息
*/
public final class ThreadPool {
// 线程池中默认线程的个数为5
private static int worker_num = 5;
// 工作线程
private WorkThread[] workThrads;
// 未处理的任务
private static volatile int finished_task = 0;
// 任务队列,作为一个缓冲,List线程不安全
private List<Runnable> taskQueue = new LinkedList<Runnable>();
private static ThreadPool threadPool;// 创建具有默认线程个数的线程池
private ThreadPool() {
this(5);
}// 创建线程池,worker_num为线程池中工作线程的个数
private ThreadPool(int worker_num) {
ThreadPool.worker_num = worker_num;
workThrads = new WorkThread[worker_num];
for (int i = 0; i < worker_num; i++) {
workThrads[i] = new WorkThread();
workThrads[i].start();// 开启线程池中的线程
}
}// 单态模式,获得一个默认线程个数的线程池
public static ThreadPool getThreadPool() {
return getThreadPool(ThreadPool.worker_num);
}// 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
// worker_num<=0创建默认的工作线程个数
public static ThreadPool getThreadPool(int worker_num1) {
if (worker_num1 <= 0)
worker_num1 = ThreadPool.worker_num;
if (threadPool == null)
threadPool = new ThreadPool(worker_num1);
return threadPool;
}// 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(Runnable task) {
synchronized (taskQueue) {
taskQueue.add(task);
taskQueue.notify();
}
}// 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(Runnable[] task) {
synchronized (taskQueue) {
for (Runnable t : task)
taskQueue.add(t);
taskQueue.notify();
}
}// 批量执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void execute(List<Runnable> task) {
synchronized (taskQueue) {
for (Runnable t : task)
taskQueue.add(t);
taskQueue.notify();
}
}// 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
public void destroy() {
while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 工作线程停止工作,且置为null
for (int i = 0; i < worker_num; i++) {
workThrads[i].stopWorker();
workThrads[i] = null;
}
threadPool=null;
taskQueue.clear();// 清空任务队列
}// 返回工作线程的个数
public int getWorkThreadNumber() {
return worker_num;
}// 返回已完成任务的个数,这里的已完成是只出了任务队列的任务个数,可能该任务并没有实际执行完成
public int getFinishedTasknumber() {
return finished_task;
}// 返回任务队列的长度,即还没处理的任务个数
public int getWaitTasknumber() {
return taskQueue.size();
}// 覆盖toString方法,返回线程池信息:工作线程个数和已完成任务个数
@Override
public String toString() {
return "WorkThread number:" + worker_num + " finished task number:"
+ finished_task + " wait task number:" + getWaitTasknumber();
}/**
-
内部类,工作线程
*/
private class WorkThread extends Thread {
// 该工作线程是否有效,用于结束该工作线程
private boolean isRunning = true;/*
- 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
*/
@Override
public void run() {
Runnable r = null;
while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
synchronized (taskQueue) {
while (isRunning && taskQueue.isEmpty()) {// 队列为空
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!taskQueue.isEmpty())
r = taskQueue.remove(0);// 取出任务
}
if (r != null) {
r.run();// 执行任务
}
finished_task++;
r = null;
}
}
// 停止工作,让该线程自然执行完run方法,自然结束
public void stopWorker() {
isRunning = false;
}
}
} ``` - 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
-
- 九种基本数据类型的大小,以及他们的封装类。
byte 8 Byte
short 16 Short
int 32 Integer
long 64 Long
float 32 Float
double 64 Double
char 16 Char
boolean Boolean
- Switch能否用string做参数?
可以自动转换为整型的(byte,short,int),String
类型,枚举类型。
Java中不能做为Switch参数的有boolean,float,double,long
(不能直接转换为int
啊)。 - equals与==的区别。
1.==是判断两个变量或实例是不是指向同一个内存空间
equals是判断两个变量或实例所指向的内存空间的值是不是相同
2.==是指对内存地址进行比较
equals()是对字符串的内容进行比较
3.==指引用是否相同
equals()指的是值是否相同 - Object有哪些公用方法?
clone
getClass
toString
finalize
equals
hashCode
wait
notify
notifyAll
- Java的四种引用,强弱软虚,用到的场景。
见上面
-
Hashcode
的作用。
set和map中的元素无序不重复
哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode
方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以 直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals
方法与新元素进行比较,相同的话就不存了;不相同,也就是发生了Hash key
相同导致冲突的情况,那么就在这个Hash key
的地方产生一个链表,将所有产生相同hashcode
的对象放到这个单链表上去,串在一起。 - ArrayList、LinkedList、Vector的区别。
见上面
- String、StringBuffer与StringBuilder的区别。
String
类型和StringBuffer
类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对String
类型进行改变的时候其实都等同于生成了一个新的String
对象,然后将指针指向新的String
对象
StringBuild(线程不安全) > StringBuffer(线程安全) > String
- Map、Set、List、Queue、Stack的特点与用法。
- HashMap和HashTable的区别。
- HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
- TreeMap、HashMap、LindedHashMap的区别。
见上面
- Collection包结构,与Collections的区别。
- Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set - java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化
- try catch finally,try里有return,finally还执行么?
-
try
语句没有被执行到,如在try
语句之前就返回了,这样finally
语句就不会执行,这也说明了finally
语句被执行的必要而非充分条件是:相应的try语句一定被执行到。 - 在try块中有
System.exit(0)
;这样的语句,System.exit(0)
;是终止Java虚拟机JVM
的,连JVM
都停止了,所有都结束了,当然finally
语句也不会被执行到。
- Excption与Error包结构。OOM你遇到过哪些情况,SOF你遇到过哪些情况。
Throwable------Error
|
Exception------RuntimeException
Throwable
是 Java 语言中所有错误或异常的超类。
Exception
及其子类是Throwable
的一种形式,它指出了合理的应用程序想要捕获的条件
RuntimeException
是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
Error
也是Throwable
的子类。 它用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件。
Java将可抛出(Throwable)的结构分为三种类型: 被检查的异常(Checked Exception),运行时异常(RuntimeException)和错误(Error)。
OOM
1,OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)
异常的可能,
Java Heap
溢出
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。
2,虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError
异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError
异常
这里需要注意当栈的大小越大可分配的线程数就越少。
3,运行时常量池溢出
异常信息:java.lang.OutOfMemoryError:PermGen space
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()
这个Native
方法。该方法的作用是:如果池中已经包含一个等于此String
的字符串,则返回代表池中这个字符串的String
对象;否则,将此String
对象包含的字符串添加到常量池中,并且返回此String对象的引用。由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize
限制方法区的大小,从而间接限制其中常量池的容量。
4, 方法区溢出
方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。
异常信息:java.lang.OutOfMemoryError:PermGen space
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收,判定条件是很苛刻的。在经常动态生成大量Class
的应用中,要特别注意这点。 - Java面向对象的三个特征与含义。
封装、继承、多态 - Override和Overload的含义去区别。
- Interface与abstract类的区别。
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类
- 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
- 抽象类可以在不提供接口方法实现的情况下实现接口。
- Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
- Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
- Static class 与non static class的区别。
内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用 - java多态的实现原理。
- 实现多线程的两种方法:Thread与Runable。
见上面
- 线程同步的方法:sychronized、lock、reentrantLock等。
见上面
- 锁的等级:方法锁、对象锁、类锁。
- 写出生产者消费者模式。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ProducerConsumerPattern {
public static void main(String args[]){
BlockingQueue sharedQueue = new LinkedBlockingQueue();
Thread prodThread = new Thread(new Producer(sharedQueue));
Thread consThread = new Thread(new Consumer(sharedQueue));
prodThread.start();
consThread.start();
}
}
class Producer implements Runnable {
private final BlockingQueue sharedQueue;
public Producer(BlockingQueue sharedQueue) {
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
for(int i=0; i<10; i++){
try {
System.out.println("Produced: " + i);
sharedQueue.put(i);
} catch (InterruptedException ex) {
Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
class Consumer implements Runnable{
private final BlockingQueue sharedQueue;
public Consumer (BlockingQueue sharedQueue) {
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
while(true){
try {
System.out.println("Consumed: "+ sharedQueue.take());
} catch (InterruptedException ex) {
Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
- ThreadLocal的设计理念与作用。
- ThreadPool用法与优势。
- Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
- wait()和sleep()的区别。
- foreach与正常for循环效率对比。
for循环的效率最高 - Java IO与NIO。
- 反射的作用于原理。
- 泛型常用特点,List<String>能否转为List<Object>。
- 解析XML的几种方式的原理与特点:DOM、SAX、PULL。
- Java与C++对比。
- Java1.7与1.8新特性。
- 设计模式:单例、工厂、适配器、责任链、观察者等等。
- JNI的使用。
//