2018面试准备
1.static是java中非常重要的一个关键字,而且它的用法也很丰富,主要有四种用法:
1).用来修饰成员变量,将其变为类的成员,从而实现所有对象对于该成员的共享;
2).用来修饰成员方法,将其变为类方法,可以直接使用“类名.方法名”的方式调用,常用于工具类;
3).静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键;
4).静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便。
2.final关键字的基本用法:
1).在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
2).对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
3.equals 与 ==
object 的 equals 与 == 方法相同
基本类型类中进行了重写equals方法进行比较内容而不是地址
注意:StringBuffer中没有重写该方法
4.HashTable和HashMap采用相同的存储机制,二者的实现基本一致,不同的是:
1)HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都经过synchronized修饰。
2)因为同步、哈希性能等原因,性能肯定是HashMap更佳,因此HashTable已被淘汰。
3) HashMap允许有null值的存在,而在HashTable中put进的键值只要有一个null,直接抛出NullPointerException。
4)HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时乘2,使用位运算取得哈希,效率高于取模。而后者为乘2加1,都是素数和奇数,这样取模哈希结果更均匀。
2.HashMap和ConCurrentHashMap的对比
1)ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。
2)HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
5.ArrayList、LinkedList、Vector的区别
ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。
LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。
如果只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector或ArrayList都可以。如果是对其它指定位置的插入、删除操作,最好选择LinkedList。
ArrayList的扩容机制
在JDK1.7中,如果通过无参构造的话,初始数组容量为0,当真正对数组进行添加时,才真正分配容量。
每次按照1.5倍(位运算)的比率通过copeOf的方式扩容。
在JKD1.6中,如果通过无参构造的话,初始数组容量为10.每次通过copeOf的方式扩容后容量为原来的1.5倍加1.以上就是动态扩容的原理。
6.线程相关问题
1)线程创建的三种方式:
1.1继承Thread类
使用该方式创建及使用线程需按以下三个步骤:
(1)定义Thread类的子类,并重写父类的run()方法,方法里的内容就是线程所要执行的任务;
(2)创建子类的实例,即生成线程的实例对象;
(3)调用现成的start()方法来启动线程。
1.2实现Runnable接口
(1)定义实现Runnable接口的实现类,并重写run()方法;
(2)创建Runnable接口实现类的实例,并将该实例作为参数传到Thread类的构造方法中来创建Thread对象,该Thread对象才是真正的线程对象;
(3)调用现成的start()方法来启动线程。
我们希望各线程能共享资源,这时候就只能扩展Runnable接口了。
1.3通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
2)synchronized的用法
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修饰一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。