最近看到一份面试题,觉得写的还不错,虽然作者只给出了题目没有答案,但是它既是一份面试题同样作者其实很好的总结了java程序员应该具备的技术体系,希望能够对初入java和迷茫不知学什么的同学以指引
由于某些原因正打算换工作,所以试着整理解答,答案只是我的想法,如有不对之处欢迎大神批评指正,
由于题目太多,我会持续添加
1. Java线程的状态
1 public enum State { 2 /** 3 * Thread state for a thread which has not yet started. 4 */ 5 NEW, 6 7 /** 8 * Thread state for a runnable thread. A thread in the runnable 9 * state is executing in the Java virtual machine but it may 10 * be waiting for other resources from the operating system 11 * such as processor. 12 */ 13 RUNNABLE, 14 15 /** 16 * Thread state for a thread blocked waiting for a monitor lock. 17 * A thread in the blocked state is waiting for a monitor lock 18 * to enter a synchronized block/method or 19 * reenter a synchronized block/method after calling 20 * {@link Object#wait() Object.wait}. 21 */ 22 BLOCKED, 23 24 /** 25 * Thread state for a waiting thread. 26 * A thread is in the waiting state due to calling one of the 27 * following methods: 28 * <ul> 29 * <li>{@link Object#wait() Object.wait} with no timeout</li> 30 * <li>{@link #join() Thread.join} with no timeout</li> 31 * <li>{@link LockSupport#park() LockSupport.park}</li> 32 * </ul> 33 * 34 * <p>A thread in the waiting state is waiting for another thread to 35 * perform a particular action. 36 * 37 * For example, a thread that has called <tt>Object.wait()</tt> 38 * on an object is waiting for another thread to call 39 * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on 40 * that object. A thread that has called <tt>Thread.join()</tt> 41 * is waiting for a specified thread to terminate. 42 */ 43 WAITING, 44 45 /** 46 * Thread state for a waiting thread with a specified waiting time. 47 * A thread is in the timed waiting state due to calling one of 48 * the following methods with a specified positive waiting time: 49 * <ul> 50 * <li>{@link #sleep Thread.sleep}</li> 51 * <li>{@link Object#wait(long) Object.wait} with timeout</li> 52 * <li>{@link #join(long) Thread.join} with timeout</li> 53 * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> 54 * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> 55 * </ul> 56 */ 57 TIMED_WAITING, 58 59 /** 60 * Thread state for a terminated thread. 61 * The thread has completed execution. 62 */ 63 TERMINATED; 64 }
1)New (新建)
当用new操作符创建一个新线程时,如 new Thread (runnable);此时该线程还没有开始
2)Runnable (可运行状态)
当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3) Blocked (被阻塞)
当线程调用sleep()或者join()方法就会进入Blocked状态,但是要注意的是阻塞的线程是不释放当前所占有的系统资源,当sleep()结束或者join()等待其他线程来到,当前线程则进入Runnable状态等待JVM分配资源。
4)Waiting(等待)
如果当前线程调用wait()方法,则当前线程进入Time waiting但是这个时候当前线程会释放所占有的JVM资源,进入这个状态过后是不能自动唤醒的,必须调用notify()或者notifyAll()方法,线程进入Waiting。
5)Timed Waiting (计时等待)
当线程进入Runnable状态,但是还没有开始运行的时候,此时发现需要的资源处于同步状态synchronized,这个时候线程将会进入Time waiting,JVM会使用队列对这些线程进行控制,既先进行Time waiting的线程会先得到JVM资源进行执行进入Waiting
6)Terminated (被终止)
线程如果执行run() main()方法结束后,完成逻辑,线程就进入Terminated
2.进程和线程的区别,进程间如何通讯,线程间如何通讯
进程是程序在计算机上的一次执行活动,一般当启动一个程序的时候操作系统就启动了一个进程。
线程是进程内的一个执行单元,也是进程内的可调度实体。
主要区别在于:
1)地址空间:线程是进程内的一个执行单元,一个进程至少有一个线程,他们共享进程的地址空间而进程有自己独立的地址空间;
2)资源共有:进程是资源分配和拥有的单位。同一个进程内的线程共享资源;
进程间通讯方式:
1)无名管道(pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系;
2)高级管道(popen):将另一个程序当做新的进程在当前程序中启动,则他算是当前进程的子进程,这种方式我们称为高级管道方式;
3)有名管道(named pipe):有名管道也是半双工的通信方式,但是他允许无亲缘关系的进程间通信;
4)消息队列(message queue):消息队列是由消息链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点;
5)信号量(semophore):信号量是一个计数器,可以用来控制多个进程对共享资源的访问,它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6)信号(sinal):信号是一种比较复杂的通信方式,用于通知接受进程某个时间已发生;
7)共享内存(shared memory):共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
8)套接字(socket):套接字也是一种进程间通信机制,与其他通信机制不同的是,它可以用于不同机器间的进程通信。
线程间通讯方式:
1)锁机制:包括互斥锁,条件变量,读写锁。
互斥锁提供了以排他的方式防止数据结构被并发修改的方法;
读写锁允许多个线程同时读共享数据,而对写操作是互斥的;
条件变量可以以原子的方式阻塞进程,直到某个条件为真为止,对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
2)信号量机制(Semaphore):包括无名线程信号量和命名线程信号量;
3)信号机制(Signal):类似进程间的信号处理;
线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制
3.HashMap的数据结构是什么?如何实现的。和HashTable,ConcurrentHashMap的区别
HashMap的数据结构是哈希表(散列表)。
它是基于哈希表的 Map 接口的实现,以key-value的形式存在。在HashMap中,key-value总是会当做一个整体来处理,系统会根据hash算法来来计算key-value的存储位置,我们总是可以通过key快速地存、取value。下面就来分析HashMap的存取。
HashTable与HashTable区别
1)继承和实现方式不同
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
Hashtable 继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口。
2)线程安全不同
Hashtable的几乎所有函数都是同步的,即它是线程安全的,支持多线程。
而HashMap的函数则是非同步的,它不是线程安全的。若要在多线程中使用HashMap,需要我们额外的进行同步处理。 对HashMap的同步处理可以使用 Collections类提供的synchronizedMap静态方法,或者直接使用JDK 5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。
3)对null值的处理不同
HashMap的key、value都可以为null。
Hashtable的key、value都不可以为null。
4)支持的遍历种类不同
HashMap只支持Iterator(迭代器)遍历。
而Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历。
5)通过Iterator迭代器遍历时,遍历的顺序不同
HashMap是“从前向后”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
Hashtabl是“从后往前”的遍历数组;再对数组具体某一项对应的链表,从表头开始进行遍历。
6)容量的初始值和增加方式不一样
HashMap默认的容量大小是16;增加容量时,每次将容量变为“原始容量x2”。
Hashtable默认的容量大小是11;增加容量时,每次将容量变为“原始容量x2 + 1”。
7)添加key-value时的hash值算法不同
HashMap添加元素时,是使用自定义的哈希算法。
Hashtable没有自定义哈希算法,而直接采用的key的hashCode()
ConcurrentHashMap 同样是线程按全的,他与HashMap之间的差别主要在于锁的粒度与如何锁,
HashTable的实现方式是锁整个表;
ConcurrentHashMap的实现方式是锁桶(或段);这样可以提供相同的线程安全,但是效率提升N倍,因为默认容量大小是16所以,默认是16倍
4.Cookie和Session的区别
1)cookie数据存放在客户的浏览器上,session数据存放在服务器上
2)cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应该使用session
3)session会在一定时间保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器压力方法,应当使用cookie
4)单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie
所以一般登录信息等重要信息应该放在session里,而一些不重要的提升用户体验的信息放在cookie中
5.索引有什么用?如何建索引?
索引作用
1)通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
2)可以大大加快数据的检索速度,这也是创建索引的最主要原因
3)可以加速表与表之间的链接,特别是在实现数据的参考完整性方面特别有意义
4)在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
5)通过使用检索,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
如何建索引
直接创建索引:例如使用CREATE INDEX语句或者使用创建索引向导;
间接创建索引:例如在表中创建主键约束和唯一性约束时,同时也创建了索引。
6.ArrayList是如何实现的,ArrayList和LinkedList的区别?ArrayList如何实现扩容。
ArrayList实现了List接口,底层使用数组保存所有元素。其操作基本上是对数组的操作。
ArrayList与LinkedList区别:
1)ArrayList是基于动态数组的数据结构,LinkedList基于链表的数据结构。
2)对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针
3)对于新增和删除操作add 和 remove ,LinkedList比较好,因为ArrayList要移动数据。
也就是当用于需要频繁查询和设置表中随机位置数据的时候用ArrayList比较好,如果用于经常需要在两端存取数据时用LinkedList比较好
ArrayList的扩容
先看一下jdk中ArrayList类中的扩容方法
1 /** 2 * Default initial capacity. 3 */ 4 private static final int DEFAULT_CAPACITY = 10; 5 6 /** 7 * The maximum size of array to allocate. 8 * Some VMs reserve some header words in an array. 9 * Attempts to allocate larger arrays may result in 10 * OutOfMemoryError: Requested array size exceeds VM limit 11 */ 12 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 13 14 /** 15 * Increases the capacity to ensure that it can hold at least the 16 * number of elements specified by the minimum capacity argument. 17 * 18 * @param minCapacity the desired minimum capacity 19 */ 20 private void grow(int minCapacity) { 21 // overflow-conscious code 22 int oldCapacity = elementData.length; 23 int newCapacity = oldCapacity + (oldCapacity >> 1); 24 if (newCapacity - minCapacity < 0) 25 newCapacity = minCapacity; 26 if (newCapacity - MAX_ARRAY_SIZE > 0) 27 newCapacity = hugeCapacity(minCapacity); 28 // minCapacity is usually close to size, so this is a win: 29 elementData = Arrays.copyOf(elementData, newCapacity); 30 } 31 32 private static int hugeCapacity(int minCapacity) { 33 if (minCapacity < 0) // overflow 34 throw new OutOfMemoryError(); 35 return (minCapacity > MAX_ARRAY_SIZE) ? 36 Integer.MAX_VALUE : 37 MAX_ARRAY_SIZE; 38 }
从中可以看出在new ArrayList();时 ArrayList 初始的默认容量是10;
而后在容量不足时会掉用自动扩容方法 通过 int newCapacity = oldCapacity + (oldCapacity >> 1);获取新容量的大小也就是在原有容量的基础上加上原容量的一半也就是
如果初始容量为10时,经过一次扩容后容量变为15,2次为22以此类推。在不超过整形Integer最大值-8的情况下回返回此值
7.equals方法实现
Object类中定义的equals方法是返回两个对象的引用是否相同,所以在自己定义类时应该根据自己的情况重写equals方法
public boolean equals(Object obj) {
return (this == obj);
}
java规范建议equals方法遵循以下几个特性:
1) 自反性:对于任何非空引用 x, x.equals(Object) 将返回 true;
2) 对称性:对于任何引用 x 和 y,当且仅当 y.equals(x)返回 true 时,x.equals(y)返回 true;
3) 传递性:对于任何引用 x、y 和 z,如果x.equals(y) 返回true 并且 y.equals(z)也返回true,那么 x.equals(z) 也应该返回 true;
4) 一致性:如果 x 和 y 引用的对象没有改变,那么 x.equals(y)的重复调用应该返回同一结果;
5) 对任何非空引用 x, x.equals(null)应该返回false。
在实现equals方法时,应该根据实例域的类型进行不同的比较:
1) 对象域,使用equals方法
2) 类型安全的枚举,使用equals或==
3) 可能为null的对象域 : 使用 == 和 equals
4) 数组域 : 使用 Arrays.equals
5) 除float和double外的原始数据类型 : 使用 ==
6) float类型: 使用Float.foatToIntBits转换成int类型,然后使用==
7) double类型: 使用Double.doubleToLongBit转换成long类型,然后使用==