(5)ArrayList、LinkedList、Vector的区别。
ArrayList:内部用数组的方式存储数据,数组的初始容量为10,当需要扩容时,新容量增加源容量的0.5倍(int newCapacity = (oldCapacity * 3)/2 + 1; 1.5倍 )。添加数据的时候涉及到内存的移动,查找的时候直接根据索引查找,所以添加慢查找快。适合存储写入少读取多的数据。非线程安全。
LinkedList:内部以双向链表的方式存储数据,添加数据的时候只需记录当前节点的左右节点,查询数据的时候需要遍历链表,所以添加数据快查找慢。
Vector:存储结构和arraylist相同。初始容量为10,不同的是扩容的时候每次增加原油容量的1倍。线程安全
(6)String、StringBuffer与StringBuilder的区别
string被定义为final(不可变),即每次对string修改都会新创建一个string变量,原来的会被gc回收。
StringBuffer和StringBuilder是可变的,每次修改都是在一个对象上,所以多次修改更节省空间,stringbuffer是线程安全的。
三者在执行速度方面StringBuilder> StringBuffer>string
(7)Map、Set、List、Queue、Stack的特点与用法
map:map是以键值对的方式 存储数据,键不能重复,一个键对应一个值,值可以重复。hashmap内部结构采用数组+链表的方式对数据进行存储。存储时:先对键计算hashcode值,在对数组大小取余,确定在数组中存储的位置,如果hashcode值相同,则在数组中以链表的形式存储。空key的数据存储在数组的第一个位置。
set:可以方便地将需要的类型以集合类型保存在一个变量中.主要应用在显示列表.Set是一个不包含重复元素的 collection。更确切地讲,
set 不包含满足 e1.equals(e2) 的元素对 e1 和 e2,并且最多包含一个 null 元素。 hashset内部存储结构:transient HashMap
private static class HashtableEntry<K, V> implements Entry<K, V> {
final K key;
V value;
final int hash;
HashtableEntry<K, V> next;
HashtableEntry(K key, V value, int hash, HashtableEntry<K, V> next) {
this.key = key;
this.value = value;
this.hash = hash;
this.next = next;
}
hashtable是线程安全的。继承自Dictionary。hashmap实现Cloneable和Serializable接口。
(9)HashMap和ConcurrentHashMap的区别,HashMap的底层源码
hashmap源码:
transient HashMapEntry<K, V>[] table;//存储数组
//数组内存储类型结构
static class HashMapEntry<K, V> implements Entry<K, V> {
final K key;
V value;
final int hash;
HashMapEntry<K, V> next;
HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
this.key = key;
this.value = value;
this.hash = hash;
this.next = next;
}
}
hashtable谁先线程安全时,是锁住整个整个结构,每次只能一个线程进入取操作结构数据。
ConcurrentHashMap就是解决了hashtable的效率问题,写入数据时每次只锁住要操作的那一个桶(数组中操作的那个单元),这样就可以同时16(默认数组大小)个线程对结构数据进行操作,显著提升了效率。当读取时没有用到锁,完全并发的操作。在求size的时候会锁住整个结构。在进行数据迭代时,集合再发生改变不会抛出异常取而代之的是在改变时new新的数据从而不影响原有的数据,iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变,更重要的,这保证了多个线程并发执行的连续性和扩展性,是性能提升的关键
(10)TreeMap、HashMap、LindedHashMap的区别
LinkedHashMap设计思想:采用双向链表存储数据。
transient LinkedEntry<K, V> header;
static class LinkedEntry<K, V> extends HashMapEntry<K, V> {
LinkedEntry<K, V> nxt;
LinkedEntry<K, V> prv;
/\*\* Create the header entry \*/
LinkedEntry() {
super(null, null, 0, null);
nxt = prv = this;
}
/\*\* Create a normal entry \*/
LinkedEntry(K key, V value, int hash, HashMapEntry<K, V> next,
LinkedEntry<K, V> nxt, LinkedEntry<K, V> prv) {
super(key, value, hash, next);
this.nxt = nxt;
this.prv = prv;
}
}
treemap设计思想:
Node<K, V> root;
static class Node<K, V> implements Map.Entry<K, V> {
Node<K, V> parent;
Node<K, V> left;
Node<K, V> right;
final K key;
V value;
int height;
Node(Node<K, V> parent, K key) {
this.parent = parent;
this.key = key;
this.height = 1;
}
(11)Collection包结构,与Collections的区别
collection是集合的上级接口(存放单值),它的自接口包括set,list.
collections是集合的帮助类,提供操作集合的工具方法。
(12)try catch finally,try里有return,finally还执行么?
先执行try里面的语句,如果try代码块中有return,先计算出return后代码块的值,完成后将这个值保存在内存中。如果try代码块出现异常,则进入catch中。最后都会进入finally结构中。
先执行完finally中语句才会执行try中的return语句,计算完return的值之后,无论在什么地方修改了参数的值,retnrn需要返回的值都是确定的,在执行finally前就已经确定了,不会改变。
(13)Override和Overload的含义去区别。
Override 特点
1、覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果;
2、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
3、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
4、方法被定义为final不能被重写。
5、对于继承来说,如果某一方法在父类中是访问权限是private,那么就不能在子类对其进行重写覆盖,如果定义的话,也只是定义了一个新方法,而不会达到重写覆盖的效果。(通常存在于父类和子类之间。)
Overload 特点
1、在使用重载时只能通过不同的参数样式。例如,不同的参数类型,不同的参数个数,不同的参数顺序(当然,同一方法内的几个参数类型必须不一样,例如可以是fun(int, float), 但是不能为fun(int, int));
2、不能通过访问权限、返回类型、抛出的异常进行重载;
3、方法的异常类型和数目不会对重载造成影响;
4、重载事件通常发生在同一个类中,不同方法之间的现象。
5、存在于同一类中,但是只有虚方法和抽象方法才能被覆写。
(14)Interface与abstract类的区别
1.abstract class 在Java中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。
2.在abstract class 中可以有自己的数据成员,也可以有非abstarct的方法,而在interface中,只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在 interface中一般不定义数据成员),所有的方法都是public abstract的。
3.抽象类中的变量默认是 friendly 型,其值可以在子类中重新定义,也可以重新赋值。接口中定义的变量默认是public static final 型,且必须给其赋初值,所以实现类中不能重新定义,也不能改变其值。
4.abstract class和interface所反映出的设计理念不同。其实abstract class表示的是”is-a”关系,interface表示的是”like-a”关系。
5.实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法。接口中则不能有实现方法
(15)ThreadLocal的设计理念与作用
Java中的ThreadLocal类允许我们创建只能被同一个线程读写的变量。因此,如果一段代码含有一个ThreadLocal变量的引用,即使两个线程同时执行这段代码,它们也无法访问到对方的ThreadLocal变量。
threadlocal的set方法:
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
threadlocal的get方法:
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
上面提到的values是创建在threadlocal中的values实例,其结构如下:
static class Values {
/\*\*
\* Size must always be a power of 2.
\*/
private static final int INITIAL_SIZE = 16;
/\*\*
\* Placeholder for deleted entries.
\*/
private static final Object TOMBSTONE = new Object();
/\*\*
\* Map entries. Contains alternating keys (ThreadLocal) and values.
\* The length is always a power of 2.
\*/
private Object[] table;
/\*\* Used to turn hashes into indices. \*/
private int mask;
/\*\* Number of live entries. \*/
private int size;
/\*\* Number of tombstones. \*/
private int tombstones;
/\*\* Maximum number of live entries and tombstones. \*/
private int maximumLoad;
/\*\* Points to the next cell to clean up. \*/
private int clean;
/\*\*
\* Constructs a new, empty instance.
\*/
Values() {
initializeTable(INITIAL_SIZE);
this.size = 0;
this.tombstones = 0;
}
实际上threadlocal 存储数据就是将数据添加到一个指定好的数组(Object[] table;)上。每个线程都有一个localvalues对象实例,用于存储数据。
values存储数据的put方法核心代码:
void put(ThreadLocal<?> key, Object value)
table[index] = key.reference;//threadlocal的引用
table[index + 1] = value;
前一个是存放key的引用,后一个存储值(一定是成双出现的)。
set完整逻辑:
->实例化threadlocal对象实例(调用set方法)
->获取到当前线程的threadlocal的localValues对象实例(即Values实例)
->向values中写入数据
values.put(this, value);
void put(ThreadLocal<?> key, Object value)
->左后将数据保存到数组中
table[index] = key.reference;
table[index + 1] = value;
(16)Static class 与non static class的区别
内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。
很形象的例子:Static class 与non static class的区别的案例
(17)ThreadPool用法与优势
线程池:合理利用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
线程池的创建:
ThreadPoolExecutor executor =new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
参数的作用:
corePoolSize:线程池的基本大小,有任务来时就创建新线程,当到达这个容量时,就不再创建新线程,如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。maximumPoolSize:程池允许创建的最大线程数
keepAliveTime:线程池的工作线程空闲后,保持存活的时间。
unit:线程活动保持时间的单位
workQueue:用于保存等待执行的任务的阻塞队列,有一下几种类型:
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
(18)wait()和sleep()的区别
1.wait方法是object类中的方法,使用object方法时会释放对象资源锁。进入等待被唤醒的线程池,需要notify方法区唤醒,才会继续执行。
2.sleep方法是thread类的方法,使用时不会释放资源锁,会让出cpu给其它线程,睡眠时间到达后自动恢复运行状态。
(19)foreach与正常for循环效率对比
•for循环中的循环条件中的变量只求一次值!如for (int i = 0; i < names.length; i++),names.length只求一次,如果在遍历期间数组发 生改变,会出现问题。
•foreach语句是java5新增,在遍历数组、集合的时候,foreach拥有不错的性能。