java

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系列

  1. ArrayList
    ArrayList以数组实现,允许重复。超出限制时会增加50%的容量(grow()方法中实现,如下所示),每次扩容都底层采用System.arrayCopy()复制到新的数组,因此最好能给出数组大小的预估值。默认第一次插入元素时创建数组的大小为10.
  2. LinkedList
    LinkedList以双向链表实现,允许重复。(如下Node的实现)并保留头指针和尾指针。链表无容量限制,但双向链表本身使用了更多空间,也需要额外的链表指针操作。
    按下标访问元素—get(i)/set(i,e) 要悲剧的遍历链表将指针移动到位(如果i>数组大小的一半,会从末尾移起)。
  3. Vector
    VectorArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.
  4. Stack
    Stack继承自Vector,实现了一个后进先出的堆栈。

ArrayList和LinkedList的区别

  • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  • 对于随机访问getsetArrayList觉得优于LinkedList,因为LinkedList要移动指针。
  • 对于新增和删除操作addremoveLinkedList比较占优势,因为ArrayList要移动数据。

ArrayList和Vector的区别

  • VectorArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
  • Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
  • Vector还有一个子类Stack.

Map

  1. HashMap
    通过hash的方法,通过putget存储和获取对象。存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Factorresize为原来的2倍)。获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来,在Java 8中,如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。
    HashMap的容量(Capacity)是 16,负载因子(load factor0.75
  2. LinkedhashMap
    LinkedHashMapHash表和链表的实现,并且依靠着双向链表保证了迭代顺序是插入的顺序。非线程安全,LinkedHashMap会记录插入的顺序,允许null的键值,当key值重复时,后面的会替换前面的。
  3. TreeHash
    红黑树实现
  4. HashTable
    HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null
  5. WeakHashMap
    WeakHashMap实现了Map接口,是HashMap的一种实现,它比HashMap多了一个引用队列.
    博主认真比对过WeakHashMapHashMap的源码,发现WeakHashMap中方法的实现方式基本和HashMap的一样,注意“基本”两个字,除了没有实现Cloneable和Serializable这两个标记接口,最大的区别在于在于expungeStaleEntries()这个方法,这个是整个WeakHashMap的精髓:每调用一次expungeStaleEntries()方法,就会在引用队列中寻找是否有被清楚的key对象,如果有则在table中找到其值,并将value设置为nullnext指针也设置为null,让GC去回收这些资源。
  6. EnumMap
    EnumMap是专门为枚举类型量身定做的Map实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。EnumMap在内部使用枚举类型的ordinal()得到当前实例的声明次序,并使用这个次序维护枚举类型实例对应值在数组的位置。

HashMapHashTable的区别

  • HashTable 是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。
  • 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtableenumerator迭代器不是fail-fast的。
  • HashtableHashMap它们两个内部实现方式的数组的初始大小和扩容的方式。HashTablehash数组默认大小是11,增加的方式是 old*2+1。HashMaphash数组的默认大小是16,而且一定是2的指数。

Set:
Set几乎都是内部用一个Map来实现, 因为Map里的KeySet就是一个Set,而value是假值,全部使用同一个ObjectSet的特征也继承了那些内部Map实现的特征。
HashSet
HashSet是基于HashMap来实现的,操作很简单,更像是对HashMap做了一次“封装”,而且只使用了HashMap的key来实现各种特性,而HashMapvalue始终都是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先找元素,再执行一个普通的链表节点删除操作。
Segmentrehash 是不加锁的,它先创建一个新的空数组,接着将元素 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的countmodcount两次,如果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 数据结构。它提供了可阻塞的puttake方法。如果Queue已经满了,put方法会被阻塞直到有空间可用;如果Queue是空的,那么take方法会被阻塞,直到有元素可用。Queue的长度可用有限,也可以无限;无限的Queue永远不会充满,所以它的put方法永远不会被阻塞。阻塞队列支持生产者-消费者设计模式。

BlockingQueue接口的几个主要的实现类:
  • ArrayBlockingQueue :一个由数组支持的有界队列。
  • LinkedBlockingQueue:一个由链接节点支持的可选有界队列。
  • PriorityBlockingQueue :一个由优先级堆支持的无界优先级队列。
  • DelayQueue :一个由优先级堆支持的、基于时间的调度队列。
  • SynchronousQueue :一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。

3. 部分常用集合类的内部实现方式

java线程

1. 线程sleep和wait的区别

Java程序中waitsleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。需要注意的是,sleep()并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。

3. Thread、Runnable、Callable、Futrue类关系与区别

Thread:
java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口。但是最终被线程执行的是Runnable,而非ThreadThread仅仅是对于Runnable的包装。

public class Thread extends Object implements Runnable```
`Runnable`:
它只有一个`run()`函数,用于将耗时操作写在其中,**该函数没有返回值**。然后使用某个线程去执行该`runnable`即可实现多线程,`Thread`类在调用`start()`函数后就是执行的是`Runnable`的`run()`函数

public interface Runnable {
public abstract void run();
}```
Callable:
CallableRunnable的功能大致相似,Callable中有一个call()函数,但是call()函数有返回值,而Runnablerun()函数不能将结果返回给客户程序

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;
      }
      }
      } ```

  1. 九种基本数据类型的大小,以及他们的封装类。
    byte 8 Byte short 16 Short int 32 Integer long 64 Long float 32 Float double 64 Double char 16 Char boolean Boolean
  2. Switch能否用string做参数?
    可以自动转换为整型的(byte,short,int),String类型,枚举类型。
    Java中不能做为Switch参数的有boolean,float,double,long(不能直接转换为int啊)。
  3. equals与==的区别。
    1.==是判断两个变量或实例是不是指向同一个内存空间
    equals是判断两个变量或实例所指向的内存空间的值是不是相同
    2.==是指对内存地址进行比较
    equals()是对字符串的内容进行比较
    3.==指引用是否相同
    equals()指的是值是否相同
  4. Object有哪些公用方法?
    clone getClass toString finalize equals hashCode
    wait notify notifyAll
  5. Java的四种引用,强弱软虚,用到的场景。
    见上面
  6. Hashcode的作用。
    set和map中的元素无序不重复
    哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以 直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了;不相同,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同hashcode的对象放到这个单链表上去,串在一起。
  7. ArrayList、LinkedList、Vector的区别。
    见上面
  8. String、StringBuffer与StringBuilder的区别。
    String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String类型进行改变的时候其实都等同于生成了一个新的 String对象,然后将指针指向新的 String 对象
    StringBuild(线程不安全) > StringBuffer(线程安全) > String
  9. Map、Set、List、Queue、Stack的特点与用法。
  10. HashMap和HashTable的区别。
  11. HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
  12. TreeMap、HashMap、LindedHashMap的区别。
    见上面
  13. Collection包结构,与Collections的区别。
  • Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法
    ├List
    │├LinkedList
    │├ArrayList
    │└Vector
    │ └Stack
    └Set
  • java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化
  1. try catch finally,try里有return,finally还执行么?
  • try语句没有被执行到,如在try语句之前就返回了,这样finally语句就不会执行,这也说明了finally语句被执行的必要而非充分条件是:相应的try语句一定被执行到。
  • 在try块中有System.exit(0);这样的语句,System.exit(0);是终止Java虚拟机JVM的,连JVM都停止了,所有都结束了,当然finally语句也不会被执行到。
  1. 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的应用中,要特别注意这点。
  2. Java面向对象的三个特征与含义。
    封装、继承、多态
  3. Override和Overload的含义去区别。
  4. Interface与abstract类的区别。
  • 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
  • 类可以实现很多个接口,但是只能继承一个抽象类
  • 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
  • 抽象类可以在不提供接口方法实现的情况下实现接口。
  • Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
  • Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
  1. Static class 与non static class的区别。
    内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用
  2. java多态的实现原理。
  3. 实现多线程的两种方法:Thread与Runable。
    见上面
  4. 线程同步的方法:sychronized、lock、reentrantLock等。
    见上面
  5. 锁的等级:方法锁、对象锁、类锁。
  6. 写出生产者消费者模式。
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);
            }
        }
    }
}
  1. ThreadLocal的设计理念与作用。
  2. ThreadPool用法与优势。
  3. Concurrent包里的其他东西:ArrayBlockingQueue、CountDownLatch等等。
  4. wait()和sleep()的区别。
  5. foreach与正常for循环效率对比。
    for循环的效率最高
  6. Java IO与NIO。
  7. 反射的作用于原理。
  8. 泛型常用特点,List<String>能否转为List<Object>。
  9. 解析XML的几种方式的原理与特点:DOM、SAX、PULL。
  10. Java与C++对比。
  11. Java1.7与1.8新特性。
  12. 设计模式:单例、工厂、适配器、责任链、观察者等等。
  13. JNI的使用。

//

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,567评论 18 399
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,174评论 11 349
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,066评论 0 62
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,095评论 0 8
  • 有了晨间日记,你也可以建立自己的大数据库 提到日记,大家不陌生,小时候都我们都被要求记过。 记得我的第一篇日记是在...
    天行践阅读 279评论 0 0