从网上找了些面试题,本身手工总结了理解了一下,若有理解错误,还请指正。javascript
java基础html
4. 描述一下ArrayList和LinkedList各自实现和区别 java
ArrayList是线性表 ,随机取数快,数据删除插入较慢,占用内存小面试
LinkedList是链表 插入删除快,查找遍历较慢占用内存稍大算法
5. Java中的队列都有哪些,有什么区别。编程
队列是实现生产者消费者的重要工具,java中的队列通常氛围阻塞的和非阻塞的,或者是根据须要有其余多重的实现方式:api
ArrayBlockingQueue——带边界的阻塞式队列数组
ConcurrentLinkedDeque / ConcurrentLinkedQueue——无边界的链表队列(CAS)缓存
DelayQueue——元素带有延迟的队列安全
LinkedBlockingDeque / LinkedBlockingQueue——链表队列(带锁),可设定是否带边界
LinkedTransferQueue——可将元素`transfer`进行w/o存储
PriorityBlockingQueue——并发PriorityQueue
SynchronousQueue——使用Queue接口进行Exchanger
Queue在设计之处就考虑了多线程的安全性,表明性的Queue的两种实现,表示实现线程安全的两套方案:•BlockingQueue,•ConcurrentLinkedQueue
前者是阻塞的同步的,后者是并发的。
这里提一下多线程安全问题的含义,以前有些搞混了。线程安全的类 ,指的是类内共享的全局变量的访问必须保证是不受多线程形式影响的。若是因为多线程的访问(好比修改、遍历、查看)而使这些变量结构被破坏或者针对这些变量操做的原子性被破坏,则这个类就不是线程安全的。实现线程安全又有两种方式,一种是同步的,将多线程的请求排个队,A线程进来了关上厕所门,其余线程在外面等待。A线程执行结束,B线程进入一样关上门。这样的操做保证了多线程下共享变量不会被修改,可是效率比较差,若是线程太多会致使严重的锁争用。还有一种方式是并发的,多个线程能够同时进入一段代码,如何保证线程安全呢?就是经过各个线程之间对共享变量的修改和操做都是可见的,你改了什么我必须第一时间知道,而后针对性地作一些操做。这样极大地提升了处理速度,只是算法的实现会很是的复杂。另外,类自身实现线程安全,只是表明类自身的api保持原子性,可是外部使用多个api的状况下,仍是要手动进行加锁。concurrent包下的集合类实现,都是采用的并发方式,而Collections工具类包装的集合都是使用同步锁方式。其中CurrentHashMap应该是使用了分段锁和volatile。java并发:同步容器&并发容器 - 烟雨暗千家 - 博客园
LinkedList实现了Queue接口,也能够做为队列使用,可是不能够访问非Queue接口的方法,以避免发生错乱。
BlocingQueue:ArrayBlockingQueue,LinkedBlockingQueue(入队和出队两把锁),DelayQueue,PriorityBlockingQueue(公平锁),SynchronousQueue。
ReentrantLock 只锁部分代码块
ConcurrentLinkedQueue:使用volatile关键字使共享变量可见,具体细节颇为复杂。
【Java多线程总结之聊一聊Queue - I'm Sure - ITeye技术网站 】【java中线程队列BlockingQueue的用法-shwenwen-ITPUB博客 】
这个坑很深,目前只能研究到这里了……
6. 反射中,Class.forName和classloader的区别。
Class.forName()方法底层调用Class.forName(className,true,classloader);表示在加载类的时候,执行初始化步骤;
ClassLoader.loadClass()方法底层调用ClassLoader.loadClass(className,false);表示不执行初始化步骤;
初始化步骤会初始化静态变量和静态代码块,若是此处没有初始化,那就须要在newInstance时,初始化了。根据不一样的须要,调用相应的方法。
可能会引伸到JVM装载类的过程:编译解释java文件为二进制class,装载class(经过类的彻底限定名和类加载器实例),连接(对二进制字节码校验,初始化静态变量,找到父类接口和类,肯定调用的方法和类都是存在的且具有权限),初始化(静态代码块其余)。
7. Java七、Java8的新特性
java7的更新多数是语法糖:
try包含资源代码块,自动释放;多个catch合并;更强的类型推测,new的时候再也不须要指定类型,直接<>便可;二进制字面量支持;经过[],{}初始化集合。
java8
Lambda表达式与函数接口,接口支持默认方法和静态方法,可以使用相似于C++的方法引用 class::method,重复注解,更强的类型推测,可将参数名称保存在字节码中,Optional 支持对null的处理,Stream 相似于mapReduce的集合处理方式,api能够直接连缀调用,Date/Time API 的更新,在兼容旧api分基础上提供了许多新的api,传闻java 9 将进一步支持货币api,base64直接支持,支持运行javascript以及js和java的相互调用(java8就有了?),jvm的permGen移除,类依赖分析器jdeps。
8. Java数组和链表两种结构的操做效率,在哪些状况下(从开头开始,从结尾开始,从中间开始),哪些操做(插入,查找,删除)的效率高。
数组 不能动态扩容,占内存小,添加删除操做稍慢,查询快。
链表 能够动态扩容,占内存大,添加删除操做快,查询慢。
9. Java内存泄露的问题调查定位:jmap,jstack的使用等等。
没用过很差说
10. string、stringbuilder、stringbuffer区别
String 是final的字符串,不可变。StringBuffer是线程安全的可变String,StringBuilder是简化的可变字符串,不是线程安全的。
11. hashtable和hashmap的区别
上面有所说起:
1. HashTable不支持用null做为key,而HashMap支持
2. HashTable是线程安全的map集合,HashMap不是线程安全的。
3. HashTable由于支持线程安全,因此没有HashMap的快速失败机制,可是HashTable不能保证多个api复合调用时保证安全。
4. Map想要支持多线程安全能够经过Collections工具类获取同步的Map,或者经过concurrent包中的concurrentHashMap返回并发的Map,并且能够与有序的TreeMap转化,不少时候,即使是多线程环境下也并不必定须要HashTable。
13 .异常的结构,运行时异常和非运行时异常,各举个例子。
单独再说
14. String 类的经常使用方法
length() , subString , indexOf , lastIndexOf , charAt , equals ,toUpperCase , equalsIgnoreCase , trim , toString , toLowerCase , replace , match(用于正则) , startsWith , compareTo. format(容许相似于C语言的输出字符串格式控制)
15. Java 的引用类型有哪几种
String , Object ,基本类型的包装类型:Integer,Long , Boolean , Short , Float,Double,Byte,Character,Void。Enum枚举类型
16. 抽象类和接口的区别
从语法上来看:
参数 | 抽象类 | 接口 |
默认的方法实现 | 它能够有默认的方法实现 | 接口彻底是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。若是子类不是抽象类的话,它须要提供抽象类中全部声明的方法的实现。 | 子类使用关键字implements来实现接口。它须要提供接口中全部声明的方法的实现 |
构造器 | 抽象类能够有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类以外,它和普通Java类没有任何区别 | 接口是彻底不一样的类型 |
访问修饰符 | 抽象方法能够有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可使用其它修饰符。 |
main方法 | 抽象方法能够有main方法而且咱们能够运行它 | 接口没有main方法,所以咱们不能运行它。 |
多继承 | 抽象方法能够继承一个类和实现多个接口 | 接口只能够继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,由于它须要时间去寻找在类中实现的方法。 |
添加新方法 | 若是你往抽象类中添加新的方法,你能够给它提供默认的实现。所以你不须要改变你如今的代码。 | 若是你往接口中添加方法,那么你必须改变实现该接口的类。 |
从本质上来看:
抽象类是对子类的一个抽象,抽象类提供了子类的某些方法约束,做为公共的你们都必须遵照的约束,有利于直接复用代码。不能肯定的子类行为能够采用抽象方法定义,交给具体子类实现。抽象类更多的是在具体代码编写时的一种设计。
接口是定义一组约束规则,做为完全的抽象层存在。它只要求实现者实现全部的方法,而不关心具体的实现方式,它只为子类指定功能,可是不存在对子类实现方式的要求,也就是说没有公共的已经实现的方法。更多的是用于软件架构设计中,对不一样模块的抽象。
java8中支持了接口的默认方法,具体后面再研究。
18. java的基础类型和字节大小
java中基本数据类型(不是引用类型)有8种
byte | Byte | 1 | -128 | 127 |
char | Character | 2 | unicode-0 | unicode 2^16-1 |
short | Short | 2 | -2^15 | 2^15-1 |
int | Integer | 4 | -2^31 | 2^31-1 |
float | Float | 4 | ||
double | Double | 8 | ||
long | Long | 8 | ||
boolean | Boolean | - | true | false |
19. Hashtable,HashMap,ConcurrentHashMap底层实现原理与线程安全问题。
HashMap没有作多线程的控制,底层实现原理以前有提到过。HashTable是使用了重用锁,在操做时锁住了一段代码块,是同步的。ConcurrentHashMap,内部使用了volatile关键字,使得共享变量在多线程之间可见和同步,编程上考虑的比较多算法比较复杂。具体的上面的文章中有提到过。
20. Hash冲突怎么办?哪些解决散列冲突的方法?
hash冲突应该是hash算法的问题,也有多是hashCode算法的问题。这个//todo
21. HashMap冲突很厉害,最差性能,你会怎么解决?从O(n)提高到log(n)。
首先哈希表是经过哈希函数计算元素的哈希值,而后将次哈希值做为要存放的数组地址的下标,以便达到用数组存放元素,同时可以实现较快的查找删除操做。
这里哈希函数就很是重要,若是哈希函数计算的不一样元素的哈希值容易重复,就会致使哈希冲突(不一样元素映射到同一下标),哈希表不得不额外地解决这些冲突。
好的哈希函数会尽可能避免冲突,能均匀的分布。以及计算简单。
解决哈希冲突的办法主要有下面几种:
开放地址法:若是发现哈希值下标已经被占用,就向下查找下一个空元素存入,问题是很是容易产生集聚现象,使得冲突加重。其中也有一些变种办法,例如不直接向下查找下一个元素,而是生成不一样的查找步数,这个能够根据随机数或者二次哈希产生。
再哈希法:冲突后再次执行另外一个哈希算法,获得新的地址。
链地址法:若是哈希冲突了,就直接将冲突的元素接在原占有的元素后面,造成一个链表。
而HashMap就是采用这种办法作的。HashMap的性能,也是与其hash函数有关,当存放的key是用户自定义的对象而不是String的时候,须要重写这个对象的hashCode方法,这个hashCode方法的效率就会影响到HashMap的哈希函数的效率,若是HashCode不能作到计算简单随机分布,hash函数就会容易冲突。
HashMap冲突很严重的话,还能够将HashMap转化为平衡树实现的TreeMap,后者的性能是logn.
23. rehash
看起来应该是HashMap的再哈希算法,主要用于当HashMap的容量超过负载因子(致使元素容易集聚成链表),HashMap就会扩容,全部的元素的哈希值就要从新计算,rehash大概就是用于此处的。rehash比较消耗性能,所以应该避免HashMap频繁扩容。
24. hashCode() 与 equals() 生成算法、方法怎么重写。
hashCode
hashCode用于生成标记一个对象的最好是惟一的散列码,它将被用在HashMap等散列表中,哈希函数会根据该散列码计算数组下标从而存放起来。
所以若是散列码是同样的话,在HashMap中认为是同一个对象,若是不同,它就会认为这个一个新的对象。
你们说在重写equals时重写hashCode方法,是为了不对象在用在哈希表的键时形成的问题。
设想重写了equals方法,两个不一样的对象被认为是相等的,咱们把相等的对象存到map的key中,而后再根据这个相等的对象取value的时候,就会出错。由于HashMap根据hashCode计算出来的两个对象的值不一样,HashMap认为是不同的。因此就找不到当初存放的对象。
因此hashCode须要重写,而且保证两个值相同的对象,它的hashCode必定是相同的。(若是固定地返回某个常量值,会致使系统会将全部的这种类型的对象都视为相同。)
重写hashCode方法是一项技术活,不过一些简单的重写方案仍是有的。下面的代码从《effective java》中摘录
/**此处的变量都是int类型*/ @Override public int hashCode(){ int result = 17; result = 31*reuslt + areaCode; result = 31*result + prefix; result = 31*result + lineNumber; return result; }
equals
/** * equals方法须要知足自反性,对称性,传递性,一致性和非空性 * equasl方法写完了要进行相关的测试 * 过程是: * 检查被比较的对象是否是本身,若是是返回true * 检查被比较的对象是否是null,若是是返回false * 检查被比较的对象是否是和本身是同一个类型的,若是不是返回false * 将被比较对象强制转换成本身的类型,而后逐一比较值。 * 对于float,double,数组等格式的域,须要使用特殊的比较方法,而不能用==。 * */ @Override public boolean equals(Object obj){ if(this == obj)return true; if(obj == null)return false; if(obj.getClass() != this.getClass())return false;//!(obj instanceof Te1) Te1 t = (Te1)obj; if(!t.name.equals(name))return false; if(!(t.id==id))return false; if(Double.compare(salary,t.salary)!=0)return false; if(!Arrays.equals(strs,t.strs))return false; return true; }