最近,我在ImportNew网站上,看到了这篇文章,以为总结的很是好,就默默的收藏起来了,以为往后必定要好好整理学习一下,昨天忽然发如今脉脉的行业头条中,竟然也推送了这篇文章,更加坚决了我整理的信心。
文中答案和详解部分过于详细的我会附上,本人认为写的 比较好的连接,供你们参考,若是有什么不正确的地方,还但愿各位大神,在评论中给予指导,谢谢!
J2SE基础html
1. 九种基本数据类型的大小,以及他们的封装类?
Java提供了一组基本数据类型,包括 boolean, byte, char, short, int, long, float, double, void.
同时,java也提供了这些类型的封装类,分别为 Boolean, Byte, Character, Short, Integer, Long, Float, Double, Void
为何Java会这么作?在java中使用基本类型来存储语言支持的基本数据类型,这里没有采用对象,而是使用了传统的面向过程语言所采用的基本类在型,
主要是从性能方面来考虑的:由于即便最简单的数学计算,使用对象来处理也会引发一些开销,而这些开销对于数学计算原本是毫无必要的。
可是在java中,泛型类包括预约义的集合,使用的参数都是对象类型,没法直接使用这些基本数据类型,因此java又提供了这些基本类型的包装器。
区别:一、基本数据类型只能按值传递,而封装类按引用传递,
二、基本类型在堆栈中建立;而对于对象类型,对象在堆中建立,对象的引用在堆栈中建立。基本类型因为在堆栈中,效率会比较高,可是可能会存在内存泄漏的问题。java
2.Switch可否用string作参数?android
在 Java 7以前,switch 只能支持 byte、short、char、int或者其对应的封装类以及 Enum 类型。在 Java 7中,String支持被加上了。程序员
3.equals与==的区别?算法
“==”比较的是值【变量(栈)内存中存放的对象的(堆)内存地址】数据库
equal用于比较两个对象的值是否相同【不是比地址】编程
【特别注意】Object类中的equals方法和“==”是同样的,没有区别,而String类,Integer类等等一些类,是重写了equals方法,才使得equals和“==不一样”,
因此,当本身建立类时,自动继承了Object的equals方法,要想实现不一样的等于比较,必须重写equals方法。"=="比"equal"运行速度快,由于"=="只是比较引用.设计模式
参考连接:.equals与==数组
4. Object有哪些公用方法?缓存
直接上代码:
Object o = new Object();
/**
* 比较当前对象和是否等于另外一个对象,指向的对象是否相同
*/
System.out.println(o.equals(new Object()));
/**
* 返回hashCode
*/
System.out.println(o.hashCode());
/**
* 返回包名+类名+Integer.toHexString(hashCode())
*/
System.out.println(o.toString());
/**
* 返回class对象
*/
System.out.println(o.getClass());
try {
/**
* 线程等待,Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
*/
o.wait();
o.wait(1000);
o.wait(1000,1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。二者的最大区别在于:
* notifyAll使全部原来在该对象上等待被notify的线程通通退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
* notify则文明得多他只是选择一个wait状态线程进行通知,并使它得到该对象上的锁,但不惊动其余一样在等待被该对象notify的线程们,当第一个线程运行完毕之后释放对象上的锁此时若是该对象没有再次使用notify语句,则即使该对象已经空闲,其余wait状态等待的线程因为没有获得该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
*/
o.notify();
o.notifyAll();
5. Java的四种引用,强弱软虚,用到的场景。
强引用
最广泛的一种引用方式,如String s = "abc",变量s就是字符串“abc”的强引用,只要强引用存在,则垃圾回收器就不会回收这个对象。
软引用(SoftReference)
用于描述还有用但非必须的对象,若是内存足够,不回收,若是内存不足,则回收。通常用于实现内存敏感的高速缓存,
软引用能够和引用队列ReferenceQueue联合使用,若是软引用的对象被垃圾回收,JVM就会把这个软引用加入到与之关联的引用队列中。
弱引用(WeakReference)
弱引用和软引用大体相同,弱引用与软引用的区别在于:只具备弱引用的对象拥有更短暂的生命周期。
在垃圾回收器线程扫描它所管辖的内存区域的过程当中,一旦发现了只具备弱引用的对象,无论当前内存空间足够与否,都会回收它的内存。
虚引用(PhantomReference)
就是形同虚设,与其余几种引用都不一样,虚引用并不会决定对象的生命周期。若是一个对象仅持有虚引用,那么它就和没有任何引用同样,
在任什么时候候均可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,
若是发现它还有虚引用,就会在回收对象的内存以前,把这个虚引用加入到与之关联的引用队列中
参考连接:http://droidyue.com/blog/2014/10/12/understanding-weakreference-in-java/
6. Hashcode的做用
hashCode方法的主要做用是为了配合基于散列的集合一块儿正常运行,这样的散列集合包括HashSet、HashMap以及HashTable
参考连接:浅谈Java中的hashcode方法
7.ArrayList、LinkedList、Vector的区别
这三者都是实现了List接口,都拥有List接口里面定义的方法,而且同时拥有Collection接口的方法;
ArrayList:采用的是数组的方式进行存储数据的,查询和修改速度快,可是增长和删除速度慢;线程是不一样步的
LinkedList:采用的是链表的方式进行存储数据的,查询和修改速度慢,可是增长和删除速度快;线程是不一样步的
Vector:也采用的是数组的方式进行存储的,Vector在java1.0之前用,可是ArrayList是在java1.2版本后使用的,线程是同步的,效率相比ArrayList来讲慢一点;同时Vector查询数据有迭代器,有枚举,有get(int index),有indexOf(int index)四种方式,而ArrayList却没有枚举
8. String、StringBuffer与StringBuilder的区别
一、可变与不可变
String类中使用字符数组保存字符串,以下就是,由于有“final”修饰符,因此能够知道string对象是不可变的。
private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,以下就是,可知这两种对象都是可变的。
char[] value;
二、是否多线程安全
String中的对象是不可变的,也就能够理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操做,如expandCapacity、append、insert、indexOf等公共方法
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,因此是线程安全的
StringBuilder并无对方法进行加同步锁,因此是非线程安全的
9. Map、Set、List、Queue、Stack的特色与用法
Map
Map是键值对,键Key是惟一不能重复的,一个键对应一个值,值能够重复。
TreeMap能够保证顺序,HashMap不保证顺序,即为无序的。
Map中能够将Key和Value单独抽取出来,其中KeySet()方法能够将全部的keys抽取正一个Set。而Values()方法能够将map中全部的values抽取成一个集合。
Set
不包含重复元素的集合,set中最多包含一个null元素
只能用Lterator实现单项遍历,Set中没有同步方法
List
有序的可重复集合。
能够在任意位置增长删除元素。
用Iterator实现单向遍历,也可用ListIterator实现双向遍历
Queue
Queue听从先进先出原则。
使用时尽可能避免add()和remove()方法,而是使用offer()来添加元素,使用poll()来移除元素,它的优势是能够经过返回值来判断是否成功。
LinkedList实现了Queue接口。
Queue一般不容许插入null元素
Stack
Stack听从后进先出原则。
Stack继承自Vector。
它经过五个操做对类Vector进行扩展,容许将向量视为堆栈,它提供了一般的push和pop操做,以及取堆栈顶点的peek()方法、测试堆栈是否为空的empty方法等
若是涉及堆栈,队列等操做,建议使用List
对于快速插入和删除元素的,建议使用LinkedList
若是须要快速随机访问元素的,建议使用ArrayList
HashMap和Hashtable都实现了Map接口,但决定用哪个以前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步(synchronization),以及速度。
10. HashMap和HashTable的区别。
HashMap几乎能够等价于Hashtable,除了HashMap是非synchronized的,并能够接受null(HashMap能够接受为null的键值(key)和值(value),而Hashtable则不行)。
HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程能够共享一个Hashtable;而若是没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
另外一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。因此当有其它线程改变了HashMap的结构(增长或者移除元素),将会抛出ConcurrentModificationException,但迭代器自己的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并非一个必定发生的行为,要看JVM。这条一样也是Enumeration和Iterator的区别。
因为Hashtable是线程安全的也是synchronized,因此在单线程环境下它比HashMap要慢。若是你不须要同步,只须要单一线程,那么使用HashMap性能要好过Hashtable。
HashMap不能保证随着时间的推移Map中的元素次序是不变的
参考文章:HashMap和HashTable的区别
十一、HashMap和ConcurrentHashMap的区别,HashMap的底层源码。
Hashmap本质是数组加链表。根据key取得hash值,而后计算出数组下标,若是多个key对应到同一个下标,就用链表串起来,新插入的在前面。
ConcurrentHashMap:在hashMap的基础上,ConcurrentHashMap将数据分为多个segment(相似hashtable),默认16个(concurrency level),而后在每个分段上都用锁进行保护,从而让锁的粒度更精细一些,并发性能更好
参考文章:HashMap VS ConcurrentHashMap
HashMap的源码:【Java集合源码剖析】HashMap源码剖析
十二、TreeMap、HashMap、LindedHashMap的区别
1.HashMap里面存入的键值对在取出的时候是随机的,也是咱们最经常使用的一个Map.它根据键的HashCode值存储数据,根据键能够直接获取它的值,具备很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
2.TreeMap取出来的是排序后的键值对。但若是您要按天然顺序或自定义顺序遍历键,那么TreeMap会更好。
3. LinkedHashMap 是HashMap的一个子类,若是须要输出的顺序和输入的相同,那么用LinkedHashMap能够实现. (应用场景:购物车等须要顺序的)
13. Collection包结构,与Collections的区别。
Collection是集合类的上级接口,子接口主要有Set 和List、Map。
Collections是针对集合类的一个帮助类,提供了操做集合的工具方法:一系列静态方法实现对各类集合的搜索、排序、线程安全化等操做。
给你们补充一个连接:collections在java中的常见用法
参考连接:http://blog.sina.com.cn/s/blog_105817120102vzh6.html
1四、try catch finally,try里有return,finally还执行么?
一、无论有木有出现异常,finally块中代码都会执行;
二、当try和catch中有return时,finally仍然会执行;
三、finally是在return后面的表达式运算后执行的(此时并无返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,任然是以前保存的值),因此函数返回值是在finally执行前肯定的;
四、finally中最好不要包含return,不然程序会提早退出,返回值不是try或catch中保存的返回值。
状况1:try{} catch(){}finally{} return;
显然程序按顺序执行。
状况2:try{ return; }catch(){} finally{} return;
程序执行try块中return以前(包括return语句中的表达式运算)代码;
再执行finally块,最后执行try中return;
finally块以后的语句return,由于程序在try中已经return因此再也不执行。
状况3:try{ } catch(){return;} finally{} return;
程序先执行try,若是遇到异常执行catch块,
有异常:则执行catch中return以前(包括return语句中的表达式运算)代码,再执行finally语句中所有代码,
最后执行catch块中return. finally以后也就是4处的代码再也不执行。
无异常:执行完try再finally再return.
状况4:try{ return; }catch(){} finally{return;}
程序执行try块中return以前(包括return语句中的表达式运算)代码;
再执行finally块,由于finally块中有return因此提早退出。
状况5:try{} catch(){return;}finally{return;}
程序执行catch块中return以前(包括return语句中的表达式运算)代码;
再执行finally块,由于finally块中有return因此提早退出。
状况6:try{ return;}catch(){return;} finally{return;}
程序执行try块中return以前(包括return语句中的表达式运算)代码;
有异常:执行catch块中return以前(包括return语句中的表达式运算)代码;
则再执行finally块,由于finally块中有return因此提早退出。
无异常:则再执行finally块,由于finally块中有return因此提早退出。
最终结论:任何执行try 或者catch中的return语句以前,都会先执行finally语句,若是finally存在的话。
若是finally中有return语句,那么程序就return了,因此finally中的return是必定会被return的,
编译器把finally中的return实现为一个warning。
15. Excption与Error包结构。OOM你遇到过哪些状况,SOF你遇到过哪些状况
这里写图片描述
(一)Throwable
Throwable 类是 Java 语言中全部错误或异常的超类。只有当对象是此类或其子类之一的实例时,才能经过 Java 虚拟机或者 Java throw 语句抛出,才能够是 catch 子句中的参数类型。
Throwable 类及其子类有两个构造方法,一个不带参数,另外一个带有 String 参数,此参数可用于生成详细消息。
Throwable 包含了其线程建立时线程执行堆栈的快照。它还包含了给出有关错误更多信息的消息字符串。
Java将可抛出(Throwable)的结构分为三种类型:
错误(Error)
运行时异常(RuntimeException)
被检查的异常(Checked Exception)
1.Error
Error 是 Throwable 的子类,用于指示合理的应用程序不该该试图捕获的严重问题。大多数这样的错误都是异常条件。
和RuntimeException同样, 编译器也不会检查Error。
当资源不足、约束失败、或是其它程序没法继续运行的条件发生时,就产生错误,程序自己没法修复这些错误的。
2.Exception
Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。
对于能够恢复的条件使用被检查异常(Exception的子类中除了RuntimeException以外的其它子类),对于程序错误使用运行时异常。
① ClassNotFoundException
当应用程序试图使用如下方法经过字符串名加载类时:
Class 类中的 forName 方法。
ClassLoader 类中的 findSystemClass 方法。
ClassLoader 类中的 loadClass 方法。
可是没有找到具备指定名称的类的定义,抛出该异常。
CloneNotSupportedException
当调用
Object 类中的 clone 方法复制对象,但该对象的类没法实现 Cloneable 接口时,抛出该异常。重写 clone 方法的应用程序也可能抛出此异常,指示不能或不该复制一个对象。
③ IOException
当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操做生成的异常的通用类。
-EOFException
当输入过程当中意外到达文件或流的末尾时,抛出此异常。
此异常主要被数据输入流用来代表到达流的末尾。
注意:其余许多输入操做返回一个特殊值表示到达流的末尾,而不是抛出异常。
-FileNotFoundException
当试图打开指定路径名表示的文件失败时,抛出此异常。
在不存在具备指定路径名的文件时,此异常将由 FileInputStream、FileOutputStream 和 RandomAccessFile 构造方法抛出。若是该文件存在,可是因为某些缘由不可访问,好比试图打开一个只读文件进行写入,则此时这些构造方法仍然会抛出该异常。
-MalformedURLException
抛出这一异常指示出现了错误的 URL。或者在规范字符串中找不到任何合法协议,或者没法解析字符串。
-UnknownHostException
指示主机 IP 地址没法肯定而抛出的异常。
④ RuntimeException
是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明。
Java编译器不会检查它。当程序中可能出现这类异常时,仍是会编译经过。
虽然Java编译器不会检查运行时异常,可是咱们也能够经过throws进行声明抛出,也能够经过try-catch对它进行捕获处理。
-ArithmeticException
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
-ClassCastException
当试图将对象强制转换为不是实例的子类时,抛出该异常。
例如:Object x = new Integer(0);
-LllegalArgumentException
抛出的异常代表向方法传递了一个不合法或不正确的参数。
-IllegalStateException
在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操做所要求的适当状态下。
-IndexOutOfBoundsException
指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
应用程序能够为这个类建立子类,以指示相似的异常。
-NoSuchElementException
由 Enumeration 的 nextElement 方法抛出,代表枚举中没有更多的元素。
-NullPointerException
当应用程序试图在须要对象的地方使用 null 时,抛出该异常。这种状况包括:
调用 null 对象的实例方法。
访问或修改 null 对象的字段。
将 null 做为一个数组,得到其长度。
将 null 做为一个数组,访问或修改其时间片。
将 null 做为 Throwable 值抛出。
应用程序应该抛出该类的实例,指示其余对 null 对象的非法使用。
(二) SOF (堆栈溢出 StackOverflow)
StackOverflowError 的定义:
当应用程序递归太深而发生堆栈溢出时,抛出该错误。
由于栈通常默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程当中,形成栈容量超过1m而致使溢出。
栈溢出的缘由:
递归调用
大量循环或死循环
全局变量是否过多
数组、List、map数据过大
16. Java面向对象的三个特征与含义
1 . 封装性
将对象的状态信息尽量的隐藏在对象内部,只保留有限的接口和方法与外界进行交互,从而避免了外界对对象内部属性的破坏。
Java中使用访问控制符来保护对类、变量、方法和构造方法的访问
2. 继承
java经过继承建立分等级层次的类,能够理解为一个对象从另外一个对象获取属性的过程。
3.多态
多态是同一个行为具备多个不一样表现形式或形态的能力。 多态性是对象多种表现形式的体现
参考连接:https://yq.aliyun.com/articles/52843
17. Override和Overload的含义和区别
方法的重写(Overriding)和重载(Overloading)是Java多态性的不一样表现。
重写(Overriding)是父类与子类之间多态性的一种表现,而重载(Overloading)是一个类中多态性的一种表现。若是在子类中定义某方法与其父类有相同的名称和参数,咱们说该方法被重写 (Overriding) 。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被"屏蔽"了。若是在一个类中定义了多个同名的方法,它们或有不一样的参数个数或有不一样的参数类型或有不一样的参数次序,则称为方法的重载(Overloading)。不能经过访问权限、返回类型、抛出的异常进行重载。
1. Override 特色
一、覆盖的方法的标志必需要和被覆盖的方法的标志彻底匹配,才能达到覆盖的效果;
二、覆盖的方法的返回值必须和被覆盖的方法的返回一致;
三、覆盖的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
四、方法被定义为final不能被重写。
五、对于继承来讲,若是某一方法在父类中是访问权限是private,那么就不能在子类对其进行重写覆盖,若是定义的话,也只是定义了一个新方法,而不会达到重写覆盖的效果。(一般存在于父类和子类之间。)
2.Overload 特色
一、在使用重载时只能经过不一样的参数样式。例如,不一样的参数类型,不一样的参数个数,不一样的参数顺序(固然,同一方法内的几个参数类型必须不同,例如能够是fun(int, float), 可是不能为fun(int, int));
二、不能经过访问权限、返回类型、抛出的异常进行重载;
三、方法的异常类型和数目不会对重载形成影响;
四、重载事件一般发生在同一个类中,不一样方法之间的现象。
五、存在于同一类中,可是只有虚方法和抽象方法才能被覆写。
其具体实现机制:
overload是重载,重载是一种参数多态机制,即代码经过参数的类型或个数不一样而实现的多态机制。 是一种静态的绑定机制(在编译时已经知道具体执行的是哪一个代码段)。
override是覆盖。覆盖是一种动态绑定的多态机制。即在父类和子类中同名元素(如成员函数)有不一样 的实现代码。执行的是哪一个代码是根据运行时实际状况而定的。
18. Interface与abstract类的区别
抽象类和接口都不可以实例化,但能够定义抽象类和接口类型的引用。
一个类若是继承了某个抽象类或者实现了某个接口都须要对其中的抽象方法所有进行实现,不然该类仍然须要被声明为抽象类。
接口比抽象类更加抽象,由于抽象类中能够定义构造器,能够有抽象方法和具体方法,而接口中不能定义构造器并且其中的方法所有都是抽象方法。
抽象类中的成员能够是private、默认、protected、public的,而接口中的成员全都是public的。
抽象类中能够定义成员变量,而接口中定义的成员变量实际上都是常量。
有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
19. Static class 与non static class的区别
内部静态类不须要有指向外部类的引用。但非静态内部类须要持有对外部类的引用。
非静态内部类可以访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
一个非静态内部类不能脱离外部类实体被建立,一个非静态内部类能够访问外部类的数据和方法,由于他就在外部类里面。
1.首先是类中的数据,static的
class A {
static int a;
}
class B {
int b;
}
不管新建几个A对象,这几个对象公用一个int a,一个对象的a改变,另外一个也会改变。
而B对象,不一样对象之间的int b独立存在,互不影响,能够有多个值。
2.类中的方法
静态的方法,不须要创建对象就能够访问
如Math.abs()这个方法,咱们没有创建Math的对象,就能够经过类名直接使用abs这个方法。
而非静态的方法,必须先创建对象,而后经过对象名,调用这个方法。
如JButton jb = new JButton();
jb.addActionListener(l);
ps:在静态方法的定义中,不能直接引用本类的其余非静态方法。例如。咱们不能在main中直接引用,本类的其余方法。因此咱们常常能够看见,在main方法中,先创建本类的一个对象,而后才经过对象调用本类的其余方法。
3.在初始化过程当中,静态的老是先初始化
20. java多态的实现原理。
参考连接:http://www.cnblogs.com/startRuning/p/5673485.html
21. 实现多线程的两种方法:Thread与Runable
Thread 和 Runnable 的相同点:都是“多线程的实现方式”。
Thread 和 Runnable 的不一样点:
Thread 是类,而Runnable是接口;Thread自己是实现了Runnable接口的类。咱们知道“一个类只能有一个父类,可是却能实现多个接口”,所以Runnable具备更好的扩展性。
此外,Runnable还能够用于“资源的共享”。即,多个线程都是基于某一个Runnable对象创建的,它们会共享Runnable对象上的资源。
一般,建议经过“Runnable”实现多线程
参考连接:http://hjsj186.blog.163.com/blog/static/2465820332015218115231968/
22. 线程同步的方法:sychronized、lock、reentrantLock等
一.什么是sychronized
sychronized是Java中最基本同步互斥的手段,能够修饰代码块,方法,类.
在修饰代码块的时候须要一个reference对象做为锁的对象.
在修饰方法的时候默认是当前对象做为锁的对象.
在修饰类时候默认是当前类的Class对象做为锁的对象.
synchronized会在进入同步块的先后分别造成monitorenter和monitorexit字节码指令.在执行monitorenter指令时会尝试获取对象的锁,若是此没对象没有被锁,或者此对象已经被当前线程锁住,那么锁的计数器加一,每当monitorexit被锁的对象的计数器减一.直到为0就释放该对象的锁.由此synchronized是可重入的,不会出现本身把本身锁死.
二.什么ReentrantLock
以对象的方式来操做对象锁.相对于sychronized须要在finally中去释放锁
三.synchronized和ReentrantLock的区别
除了synchronized的功能,多了三个高级功能.
等待可中断,公平锁,绑定多个Condition.
1.等待可中断
在持有锁的线程长时间不释放锁的时候,等待的线程能够选择放弃等待. tryLock(long timeout, TimeUnit unit)
2.公平锁
按照申请锁的顺序来一次得到锁称为公平锁.synchronized的是非公平锁,ReentrantLock能够经过构造函数实现公平锁. new RenentrantLock(boolean fair)
3.绑定多个Condition
经过屡次newCondition能够得到多个Condition对象,能够简单的实现比较复杂的线程同步的功能.经过await(),signal();
分析理解:
在并发量比较小的状况下,使用synchronized是个不错的选择,可是在并发量比较高的状况下,其性能降低很严重,此时ReentrantLock是个不错的方案。
一、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
若是使用 synchronized ,若是A不释放,B将一直等下去,不能被中断
若是 使用ReentrantLock,若是A不释放,能够使B在等待了足够长的时间之后,中断等待,而干别的事情
ReentrantLock获取锁定与三种方式:
a) lock(), 若是获取了锁当即返回,若是别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 若是获取了锁当即返回true,若是别的线程正持有锁,当即返回false;
c)tryLock(long timeout,TimeUnit unit), 若是获取了锁定当即返回true,若是别的线程正持有锁,会等待参数给定的时间,在等待的过程当中,若是获取了锁定,就返回true,若是等待超时,返回false;
d) lockInterruptibly:若是获取了锁定当即返回,若是没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
二、synchronized是在JVM层面上实现的,不但能够经过一些监控工具监控synchronized的锁定,并且在代码执行时出现异常,JVM会自动释放锁定,可是使用Lock则不行,lock是经过代码实现的,要保证锁定必定会被释放,就必须将unLock()放到finally{}中
三、在资源竞争不是很激烈的状况下,Synchronized的性能要优于ReetrantLock,可是在资源竞争很激烈的状况下,Synchronized的性能会降低几十倍,可是ReetrantLock的性能能维持常态;
JDK5.0的多线程任务包对于同步的性能方面有了很大的改进,在原有synchronized关键字的基础上,又增长了ReentrantLock,以及各类Atomic类。了解其性能的优劣程度,有助与咱们在特定的情形下作出正确的选择。
整体的结论先摆出来:
synchronized:
在资源竞争不是很激烈的状况下,偶尔会有同步的情形下,synchronized是很合适的。缘由在于,编译程序一般会尽量的进行优化synchronize,另外可读性很是好,无论用没用过5.0多线程包的程序员都能理解。
ReentrantLock:
ReentrantLock提供了多样化的同步,好比有时间限制的同步,能够被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。可是当同步很是激烈的时候,synchronized的性能一会儿能降低好几十倍。而ReentrantLock确还能维持常态。
Atomic:
和上面的相似,不激烈状况下,性能比synchronized略逊,而激烈的时候,也能维持常态。激烈的时候,Atomic的性能会优于ReentrantLock一倍左右。可是其有一个缺点,就是只能同步一个值,一段代码中只能出现一个Atomic的变量,多于一个同步无效。由于他不能在多个Atomic之间同步。
因此,咱们写同步的时候,优先考虑synchronized,若是有特殊须要,再进一步优化。ReentrantLock和Atomic若是用的很差,不只不能提升性能,还可能带来灾难。
23. 锁的等级:方法锁、对象锁、类锁
方法锁(synchronized修饰方法时)
经过在方法声明中加入 synchronized关键字来声明 synchronized 方法。
synchronized 方法控制对类成员变量的访问:
每一个类实例对应一把锁,每一个 synchronized 方法都必须得到调用该方法的类实例的锁方能执行,不然所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能得到该锁,从新进入可执行状态。这种机制确保了同一时刻对于每个类实例,其全部声明为 synchronized 的成员函数中至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。
对象锁(synchronized修饰方法或代码块)
当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先得到对象锁。若是此对象的对象锁已被其余调用者占用,则须要等待此锁被释放。(方法锁也是对象锁)
java的全部对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,固然若是已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然能够由JVM来自动释放。
类锁(synchronized 修饰静态的方法或代码块)
因为一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。因此,一旦一个静态的方法被申明为synchronized。此类全部的实例化对象在调用此方法,共用同一把锁,咱们称之为类锁。
对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。
类锁只是一个概念上的东西,并非真实存在的,它只是用来帮助咱们理解锁定实例方法和静态方法的区别的。
24. 写出生产者消费者模式。
生产者-消费者(producer-consumer)问题,也称做有界缓冲区(bounded-buffer)问题,两个进程共享一个公共的固定大小的缓冲区。其中一个是生产者,用于将消息放入缓冲区;另一个是消费者,用于从缓冲区中取出消息。问题出如今当缓冲区已经满了,而此时生产者还想向其中放入一个新的数据项的情形,其解决方法是让生产者此时进行休眠,等待消费者从缓冲区中取走了一个或者多个数据后再去唤醒它。一样地,当缓冲区已经空了,而消费者还想去取消息,此时也可让消费者进行休眠,等待生产者放入一个或者多个数据时再唤醒它。
一,首先定义公共资源类,其中的变量number是保存的公共数据。而且定义两个方法,增长number的值和减小number的值。因为多线程的缘由,必须加上synchronized关键字,注意while判断的条件。
/**
* 公共资源类
*/
public class PublicResource {
private int number = 0;
/**
* 增长公共资源
*/
public synchronized void increace() {
while (number != 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
/**
* 减小公共资源
*/
public synchronized void decreace() {
while (number == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
} </span>
二,分别定义生产者线程和消费者线程,并模拟屡次生产和消费,即增长和减小公共资源的number值
[java] view plain copy
<span style="font-size:18px;"> /**
* 生产者线程,负责生产公共资源
*/
public class ProducerThread implements Runnable {
private PublicResource resource;
public ProducerThread(PublicResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.increace();
}
}
}
/**
* 消费者线程,负责消费公共资源
*/
public class ConsumerThread implements Runnable {
private PublicResource resource;
public ConsumerThread(PublicResource resource) {
this.resource = resource;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
resource.decreace();
}
}
} </span>
三,模拟多个生产者和消费者操做公共资源的情形,结果须保证是在容许的范围内。
public class ProducerConsumerTest {
public static void main(String[] args) {
PublicResource resource = new PublicResource();
new Thread(new ProducerThread(resource)).start();
new Thread(new ConsumerThread(resource)).start();
new Thread(new ProducerThread(resource)).start();
new Thread(new ConsumerThread(resource)).start();
new Thread(new ProducerThread(resource)).start();
new Thread(new ConsumerThread(resource)).start();
}
}
参考连接:http://blog.csdn.net/u010339647/article/details/52013123
25. ThreadLocal的设计理念与做用
ThreadLocal类的大体结构和进行ThreadLocalMap的操做.咱们能够从中得出如下的结论:
1. ThreadLocalMap变量属于线程(Thread)的内部属性,不一样的线程(Thread)拥有彻底不一样的ThreadLocalMap变量.
2. 线程(Thread)中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或者get操做时建立的.
3. 在建立ThreadLocalMap以前,会首先检查当前线程(Thread)中的ThreadLocalMap变量是否已经存在,若是不存在则建立一个;
若是已经存在,则使用当前线程(Thread)已建立的ThreadLocalMap.
4. 使用当前线程(Thread)的ThreadLocalMap的关键在于使用当前的ThreadLocal的实例做为key进行存储ThreadLocal模式
,至少从两个方面完成了数据访问隔离,有了横向和纵向的两种不一样的隔离方式,ThreadLocal模式就能真正地作到线程安全:
纵向隔离 —— 线程(Thread)与线程(Thread)之间的数据访问隔离.这一点由线程(Thread)的数据结构保证.由于每一个线程(Thread)在进行对象访问时,
访问的都是各自线程本身的ThreadLocalMap.横向隔离 —— 同一个线程中,不一样的ThreadLocal实例操做的对象之间的相互隔离.
这一点由ThreadLocalMap在存储时,采用当前ThreadLocal的实例做为key来保证.
话很少说,给你们推荐一篇原创博文,我就是在这篇博客http://blog.csdn.net/hua286306956/article/details/8660268里 学习的
26. ThreadPool用法与优点。
先定义一个线程池ThreadPoolExecutor,使用的时候用executor来调用runnable
优点:合理利用线程池可以带来三个好处。
第一:下降资源消耗。经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
第二:提升响应速度。当任务到达时,任务能够不须要等到线程建立就能当即执行。
第三:提升线程的可管理性。线程是稀缺资源,若是无限制的建立,不只会消耗系统资源,还会下降系统的稳定性,使用线程池能够进行统一的分配,调优和监控。
推荐连接:http://blog.csdn.net/scboyhj__/article/details/48805881
27. Concurrent包里的其余东西:ArrayBlockingQueue、CountDownLatch等等。
ArrayBlockingQueue
一个创建在数组之上被BlockingQueue绑定的阻塞队列。这个队列元素顺序是先进先出。队列的头部是在队列中待的时间最长的元素。队列的尾部是再队列中待的时间最短的元素。新的元素会被插入到队列尾部,而且队列从队列头部获取元素。
这是一个典型的绑定缓冲,在这个缓冲区中,有一个固定大小的数组持有生产者插入的数据,而且消费者会提取这些数据。一旦这个类被建立,那么这个数组的容量将不能再被改变。尝试使用put操做给一个满队列插入元素将致使这个操做被阻塞;尝试从空队列中取元素也会被阻塞。
这个类推荐了一个可选的公平策略来排序等待的生产者和消费者线程。默认的,这个顺序是不肯定的。可是队列会使用公平的设置true来使线程按照先进先出顺序访问。一般公平性会减小吞吐量可是却减小了可变性以及避免了线程饥饿。
参考博文:http://blog.csdn.net/startupmount/article/details/37413275?utm_source=tuicool&utm_medium=referral
concurrent包是jdk1.5引入的重要的包,主要代码由大牛Doug Lea完成,实际上是在jdk1.4时代,因为java语言内置对多线程编程的支持比较基础和有限,因此他写了这个,由于实在太过于优秀,因此被加入到jdk之中;
一般所说的concurrent包基本有3个package组成
java.util.concurrent:提供大部分关于并发的接口和类,如BlockingQueue,Callable,ConcurrentHashMap,ExecutorService, Semaphore等
java.util.concurrent.atomic:提供全部原子操做的类, 如AtomicInteger, AtomicLong等;
java.util.concurrent.locks:提供锁相关的类, 如Lock, ReentrantLock, ReadWriteLock, Condition等;
ountDownLatch, 能够用来在一个线程中等待多个线程完成任务的类;
一般的使用场景是,某个主线程接到一个任务,起了n个子线程去完成,可是主线程须要等待这n个子线程都完成任务了之后才开始执行某个操做;
掩饰代码:
@Test
public void demoCountDown()
{
int count = 10;
final CountDownLatch l = new CountDownLatch(count);
for(int i = 0; i < count; ++i)
{
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.currentThread().sleep(20 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread " + index + " has finished...");
l.countDown();
}
}).start();
}
try {
l.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("now all threads have finished");
}
28. wait()和sleep()的区别
① 这两个方法来自不一样的类分别是,sleep来自Thread类,和wait来自Object类。
sleep是Thread的静态类方法,谁调用的谁去睡觉,即便在a线程里调用b的sleep方法,实际上仍是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。
② 锁: 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其余线程能够使用同步控制块或者方法。
sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其余线程能够占用CPU。通常wait不会加时间限制,由于若是wait线程的运行资源不够,再出来也没用,要等待其余线程调用notify/notifyAll唤醒等待池中的全部线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)能够用时间指定使它自动唤醒过来,若是时间不到只能调用interrupt()强行打断。
Thread.sleep(0)的做用是“触发操做系统马上从新进行一次CPU竞争”。
③ 使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用。
synchronized(x){
x.notify()
//或者wait()
}
29. foreach与正常for循环效率对比
不是绝对的,在选择for, foreach的时候,应该考虑如下几点:
1. 若是只是读数据,优先选择foreach,由于效率高,并且代码简单,方便;
2. 若是要写数据,就只能选择for了
30. Java IO与NIO
区别对比
IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
无 选择器</span>
一、面向流与面向缓冲
Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 java IO面向流意味着每次从流中读一个或多个字节,直至读取全部字节,它们没有被缓存在任何地方。此外,它不能先后移动流中的数据。若是须要先后移动从流中读取的数据,须要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不一样。数据读取到一个它稍后处理的缓冲区,须要时可在缓冲区中先后移动。这就增长了处理过程当中的灵活性。可是,还须要检查是否该缓冲区中包含全部您须要处理的数据。并且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里还没有处理的数据。
二、阻塞与非阻塞IO
Java IO的各类流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据彻底写入。该线程在此期间不能再干任何事情了。 Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,可是它仅能获得目前可用的数据,若是目前没有数据可用时,就什么都不会获取。而不是保持线程阻塞,因此直至数据变的能够读取以前,该线程能够继续作其余的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不须要等待它彻底写入,这个线程同时能够去作别的事情。 线程一般将非阻塞IO的空闲时间用于在其它通道上执行IO操做,因此一个单独的线程如今能够管理多个输入和输出通道(channel)。
三、选择器(Selector)
ava NIO的选择器容许一个单独的线程来监视多个输入通道,你能够注册多个通道使用一个选择器,而后使用一个单独的线程来“选择”通道:这些通道里已经有能够处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。
31. 反射的做用与原理。
JAVA反射机制是在运行状态中,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
推荐连接:Class对象和java反射机制
java反射
32. 泛型经常使用特色,List<String>可否转为List<Object>。
一、类型安全。类型错误如今在编译期间就被捕获到了,而不是在运行时看成java.lang.ClassCastException展现出来,将类型检查从运行时挪到编译时有助于开发者更容易找到错误,并提升程序的可靠性
二、消除了代码中许多的强制类型转换,加强了代码的可读性
三、为较大的优化带来了可能
List<String>向上转换至List<Object>会丢失String类的身份(String类型的特有接口),这种转换是不完美的。
当须要由List向下转型时,你的程序必须明确的知道将对象转换成何种具体类型,否则这将是不不‘安全的操做!
33. 解析XML的几种方式的原理与特色:DOM、SAX、PULL
SAX是基于事件流的解析
当解析器发现元素开始、元素结束、文本、文档的开始或结束等时,发送事件,程序员编写响应这些事件的代码,保存数据
DOM是基于XML文档树结构的解析
解析器读入整个文档,而后构建一个驻留内存的树结构,而后代码就能够使用 DOM 接口来操做这个树结构
Sax定义
SAX是一个解析速度快而且占用内存少的xml解析器,很是适合用于Android等移动设备
SAX全称是Simple API for Xml,既是指一种接口,也是一个软件包
做为接口,sax是事件驱动型xml解析的一个标准接口
Sax工做原理
Sax的工做原理简单的说,就是对文档进行顺序扫描,扫描到文档(document)开始与结束,扫描到元素(element)开始、结束等地方时调用事件处理
处理函数作相应动做,而后继续扫描,直到文档结束。
Sax特色
1. 解析效率高,占用内存少
2.能够随时中止解析
3.不能载入整个文档到内存
4.不能写入xml
5.SAX解析xml文件采用的是事件驱动
---sax并不须要解析完 整个文档,在按内容顺序解析文档的过程当中,sax会判断当前读到的字符是否合法xml语法中的某部分,若是符合就会触发事件
DOM工做原理
dom全称Document Object Model ,为xml文档的已解析版本定义了一组接口。解析器读入整个文档,而后构建一个主流内存的树结构,
而后代码就能够使用dom接口来操做这个树结构
DOM的特色
>优势
1.整个文档树在内存中,便于操做;支持删除、修改、从新排列等多种功能
2.经过树形结构存取xml文档
3.能够在树的某个节点上向前或向后移动
>缺点
1.将整个文档调入内存(包括无用的节点),浪费时间和空间
>适用场合
一旦解析了文档还需屡次访问这些数据;硬件资源充足(内存,cpu)
pull解析器简介
1.pull解析器是android内置的解析器,解析原理与sax相似
2.pull它提供了相似的事件。
如:开始元素和结束元素事件,使用parse.next()能够进入下一个元素并触发相应的事件,事件将做为数值代码被发送
所以能够使用一个switch对感兴趣的事件进行处理。当元素开始解析时,调用parser.nextText()方法获取下一个Text类型节点的值
pull与sax的不一样之处
1.pull读取xml文件后触发相应的事件调用方法返回的是数字。
2.pull能够在程序中控制,想解析到哪里就能够中止到哪里
3.Android中更推荐使用pull解析
参考连接:http://blog.csdn.net/kukulongzai_123/article/details/7058008
34. Java与C++对比
Java没有显式指针,而在C++中却能够用。
Java是主动多态的,不用关心具备继承关系的多个类之间的同名成员函数会调用哪一个,Java会主动地从祖父类、祖祖父类……,追溯至最高一级父类,而后从上至下开始寻找并调用;C++却不会主动使用多态,要使用多态,就要用虚函数。
Java是隐式继承的;C++是被动多态的,C++把话说明白了,你继承谁就继承谁,继承多个均可以,你什么都不说那么就不继承。
Java有接口,C++中却没有。C++中是定义了一个抽象类,把成员函数设为常量,并改为纯虚函数,在C++中这样的抽象类就是接口。
Java是单根继承的,可是容许一个类实现多个接口;C++虽然支持多继承,尽管不多有人去用它。
Java中全部的函数都与类相关,没有全局变量和非成员函数,而C++却支持这些。
C++中使用的动态内存怎么用就怎么还,Java中因为包含一个垃圾收集系统。
Java有很紧凑的异常处理机制,而C++稍微显得草率了一些。可是,这不表明C++异常处理机制不强大,由于Java只能抛出Throwable之类的异常,而C++却什么均可以。
Java标准库是Java庞大的体现,涵盖了国际化、网络化、数学、声音、Web应用和服务以及数据库等。
35. Java1.7与1.8新特性。
JDK 1.7 新特性
1,switch中能够使用字串了
2,"<>"这个玩意儿的运用List<String> tempList = new ArrayList<>(); 即泛型实例化类型自动推断
3. 自定义自动关闭类
4. 新增一些取环境信息的工具方法
5. Boolean类型反转,空指针安全,参与位运算
6. 两个char间的equals
7,安全的加减乘除
八、对Java集合(Collections)的加强支持
九、数值可加下划线
十、支持二进制文字
十一、简化了可变参数方法的调用
十二、在try catch异常扑捉中,一个catch能够写多个异常类型,用"|"隔开,
1三、jdk7以前,你必须用try{}finally{}在try内使用资源,在finally中关闭资源,无论try中的代码是否正常退出或者异常退出。jdk7以后,你能够没必要要写finally语句来关闭资源,只要你在try()的括号内部定义要使用的资源
JDK 1.8 新特性
1、接口的默认方法
2、Lambda 表达式
3、函数式接口
4、方法与构造函数引用
5、Lambda 做用域
6、访问局部变量
8、访问接口的默认方法
9、Date API
10、Annotation 注解
强烈推荐参考连接:JDK各个版本的新特性jdk1.5-jdk8
36. 设计模式:单例、工厂、适配器、责任链、观察者等等。
Java开发中的23种设计模式详解(转)
37. JNI的使用
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其余语言的通讯(主要是C&C++)。
从Java1.1开始,JNI标准成为java平台的一部分,它容许Java代码和其余语言写的代码进行交互。
JNI一开始是为了本地已编译语言,尤为是C和C++而设计的,可是它并不妨碍你使用其余编程语言,只要调用约定受支持就能够了。
使用java与本地已编译的代码交互,一般会丧失平台可移植性。可是,有些状况下这样作是能够接受的,甚至是必须的。
例如,使用一些旧的库,与硬件、操做系统进行交互,或者为了提升程序的性能。JNI标准至少要保证本地代码能工做在任何Java 虚拟机环境。
推荐连接:JNI的简单使用
j2se的部分 可算整理完了,但愿各位读者可以喜欢,不足的地方但愿各位大神多多批评指正,过几天给你们整理JVM 的部分
JVM
1. 内存模型以及分区,须要详细到每一个区放什么
JVM内存区域模型
JVM内存模型
1.方法区
也称”永久代” 、“非堆”, 它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB,能够经过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。
运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各类符号引用,这部份内容将在类加载后放到方法区的运行时常量池中。
2.虚拟机栈
描述的是Java 方法执行的内存模型:每一个方法被执行的时候 都会建立一个“栈帧”用于存储局部变量表(包括参数)、操做栈、方法出口等信息。每一个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。声明周期与线程相同,是线程私有的。
局部变量表存放了编译器可知的各类基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(引用指针,并不是对象自己),其中64位长度的long和double类型的数据会占用2个局部变量的空间,其他数据类型只占1个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法须要在栈帧中分配多大的局部变量是彻底肯定的,在运行期间栈帧不会改变局部变量表的大小空间。
3.本地方法栈
与虚拟机栈基本相似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。
4.堆
也叫作java 堆、GC堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,在JVM启动时建立。该内存区域存放了对象实例及数组(全部new的对象)。其大小经过-Xms(最小值)和-Xmx(最大值)参数设置,-Xms为JVM启动时申请的最小内存,默认为操做系统物理内存的1/64但小于1G,-Xmx为JVM可申请的最大内存,默认为物理内存的1/4但小于1G,默认当空余堆内存小于40%时,JVM会增大Heap到-Xmx指定的大小,可经过-XX:MinHeapFreeRation=来指定这个比列;当空余堆内存大于70%时,JVM会减少heap的大小到-Xms指定的大小,可经过XX:MaxHeapFreeRation=来指定这个比列,对于运行系统,为避免在运行时频繁调整Heap的大小,一般-Xms与-Xmx的值设成同样。
因为如今收集器都是采用分代收集算法,堆被划分为新生代和老年代。新生代主要存储新建立的对象和还没有进入老年代的对象。老年代存储通过屡次新生代GC(Minor GC)任然存活的对象。
新生代:
程序新建立的对象都是重新生代分配内存,新生代由Eden Space和两块相同大小的Survivor Space(一般又称S0和S1或From和To)构成,可经过-Xmn参数来指定新生代的大小,也能够经过-XX:SurvivorRation来调整Eden Space及Survivor Space的大小。
老年代:
用于存放通过屡次新生代GC任然存活的对象,例如缓存对象,新建的对象也有可能直接进入老年代,主要有两种状况:①.大对象,可经过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来表明超过多大时就不在新生代分配,而是直接在老年代分配。②.大的数组对象,切数组中无引用外部对象。
老年代所占的内存大小为-Xmx对应的值减去-Xmn对应的值。
5.程序计数器
是最小的一块内存区域,它的做用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工做时就是经过改变这个计数器的值来选取下一条须要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都须要依赖计数器完成。
2. 堆里面的分区:Eden,survival from to,老年代,各自的特色。
HotSpot虚拟机的分代收集,分为一个Eden区、两个Survivor去以及Old Generation/Tenured区,其中Eden以及Survivor共同组成New Generatiton/Young space。
一般将对New Generation进行的回收称为Minor GC;对Old Generation进行的回收称为Major GC,但因为Major GC除并发GC外均需对整个堆以及Permanent Generation进行扫描和回收,所以又称为Full GC。
Eden区是分配对象的区域。
Survivor是minor/younger gc后存储存活对象的区域。
Tenured区域存储长时间存活的对象
1.Eden区
Eden区位于Java堆的年轻代,是新对象分配内存的地方,因为堆是全部线程共享的,所以在堆上分配内存须要加锁。而Sun JDK为提高效率,会为每一个新建的线程在Eden上分配一块独立的空间由该线程独享,这块空间称为TLAB(Thread Local Allocation Buffer)。在TLAB上分配内存不须要加锁,所以JVM在给线程中的对象分配内存时会尽可能在TLAB上分配。若是对象过大或TLAB用完,则仍然在堆上进行分配。若是Eden区内存也用完了,则会进行一次Minor GC(young GC)。
2.Survival from to
Survival区与Eden区相同都在Java堆的年轻代。Survival区有两块,一块称为from区,另外一块为to区,这两个区是相对的,在发生一次Minor GC后,from区就会和to区互换。在发生Minor GC时,Eden区和Survival from区会把一些仍然存活的对象复制进Survival to区,并清除内存。Survival to区会把一些存活得足够旧的对象移至年老代。
3.年老代
年老代里存放的都是存活时间较久的,大小较大的对象,所以年老代使用标记整理算法。当年老代容量满的时候,会触发一次Major GC(full GC),回收年老代和年轻代中再也不被使用的对象资源。
3. 对象建立方法,对象的内存分配,对象的访问定位。
Java对象的建立大体上有如下几个步骤:
类加载检查:检查这个指令的参数是否能在常量池中定位到一个类的符号引用,而且检查这个符号引用表明的类是否已被加载、解析和初始化过。若是没有,那必须先执行相应的类的加载过程
为对象分配内存:对象所需内存的大小在类加载完成后便彻底肯定,为对象分配空间的任务等同于把一块肯定大小的内存从Java堆中划分出来。因为堆被线程共享,所以此过程须要进行同步处理(分配在TLAB上不须要同步)
内存空间初始化:虚拟机将分配到的内存空间都初始化为零值(不包括对象头),内存空间初始化保证了对象的实例字段在Java代码中能够不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
对象设置:JVM对对象头进行必要的设置,保存一些对象的信息(指明是哪一个类的实例,哈希码,GC年龄等)
init:执行完上面的4个步骤后,对JVM来讲对象已经建立完毕了,但对于Java程序来讲,咱们还须要对对象进行一些必要的初始化。
对象的内存分配
Java对象的内存分配有两种状况,由Java堆是否规整来决定(Java堆是否规整由所采用的垃圾收集器是否带有压缩整理功能决定):
指针碰撞(Bump the pointer):若是Java堆中的内存是规整的,全部用过的内存都放在一边,空闲的内存放在另外一边,中间放着一个指针做为分界点的指示器,分配内存也就是把指针向空闲空间那边移动一段与内存大小相等的距离
空闲列表(Free List):若是Java堆中的内存不是规整的,已使用的内存和空闲的内存相互交错,就没有办法简单的进行指针碰撞了。虚拟机必须维护一张列表,记录哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录
对象的访问定位
对象的访问形式取决于虚拟机的实现,目前主流的访问方式有使用句柄和直接指针两种:
使用句柄:
若是使用句柄访问,Java堆中将会划分出一块内存来做为句柄池,引用中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息:
对象访问句柄
优点:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是很是广泛的行为)时只会改变句柄中的实例数据指针,而引用自己不须要修改。
直接指针:
若是使用直接指针访问对象,那么对象的实例数据中就包含一个指向对象类型数据的指针,引用中存的直接就是对象的地址:
对象访问直接指针
优点:速度更快,节省了一次指针定位的时间开销,聚沙成塔的效应很是可观。
4. GC的两种断定方法:引用计数与引用链。
基于引用计数与基于引用链这两大类别的自动内存管理方式最大的不一样之处在于:前者只须要局部信息,然后者须要全局信息
引用计数
引用计数顾名思义,就是记录下一个对象被引用指向的次数。引用计数方式最基本的形态就是让每一个被管理的对象与一个引用计数器关联在一块儿,该计数器记录着该对象当前被引用的次数,每当建立一个新的引用指向该对象时其计数器就加1,每当指向该对象的引用失效时计数器就减1。当该计数器的值降到0就认为对象死亡。每一个计数器只记录了其对应对象的局部信息——被引用的次数,而没有(也不须要)一份全局的对象图的生死信息。因为只维护局部信息,因此不须要扫描全局对象图就能够识别并释放死对象;但也由于缺少全局对象图信息,因此没法处理循环引用的情况。
引用链
引用链须要内存的全局信息,当使用引用链进行GC时,从对象图的“根”(GC Root,必然是活的引用,包括栈中的引用,类静态属性的引用,常量的引用,JNI的引用等)出发扫描出去,基于引用的可到达性算法来判断对象的生死。这使得对象的生死状态能批量的被识别出来,而后批量释放死对象。引用链不须要显式维护对象的引用计数,只在GC使用可达性算法遍历全局信息的时候判断对象是否被引用,是否存活。
5. GC的三种收集方法:标记清除、标记整理、复制算法的原理与特色,分别用在什么地方,若是让你优化收集方法,有什么思路?
标记清除
标记清除算法分两步执行:
暂停用户线程,经过GC Root使用可达性算法标记存活对象
清除未被标记的垃圾对象
标记清除算法缺点以下:
效率较低,须要暂停用户线程
清除垃圾对象后内存空间不连续,存在较多内存碎片
标记算法现在使用的较少了
复制算法
复制算法也分两步执行,在复制算法中通常会有至少两片的内存空间(一片是活动空间,里面含有各类对象,另外一片是空闲空间,里面是空的):
暂停用户线程,标记活动空间的存活对象
把活动空间的存活对象复制到空闲空间去,清除活动空间
复制算法相比标记清除算法,优点在于其垃圾回收后的内存是连续的。
可是复制算法的缺点也很明显:
须要浪费必定的内存做为空闲空间
若是对象的存活率很高,则须要复制大量存活对象,致使效率低下
复制算法通常用于年轻代的Minor GC,主要是由于年轻代的大部分对象存活率都较低
标记整理
标记整理算法是标记清除算法的改进,分为标记、整理两步:
暂停用户线程,标记全部存活对象
移动全部存活对象,按内存地址次序一次排列,回收末端对象之后的内存空间
标记整理算法与标记清除算法相比,整理出的内存是连续的;而与复制算法相比,不须要多片内存空间。
然而标记整理算法的第二步整理过程较为麻烦,须要整理存活对象的引用地址,理论上来讲效率要低于复制算法。
所以标记整理算法通常引用于老年代的Major GC
参考博文:Java虚拟机学习(2):垃圾收集算法
6. GC收集器有哪些?CMS收集器与G1收集器的特色?
常见的GC收集器以下图所示,连线表明可搭配使用:
GC收集器
1.Serial收集器(串行收集器)
用于新生代的单线程收集器,收集时须要暂停全部工做线程(Stop the world)。优势在于:简单高效,单个CPU时没有线程交互的开销,堆较小时停顿时间不长。常与Serial Old 收集器一块儿使用,示意图以下所示:
串行收集器
2.ParNew收集器(parallel new 收集器,新生代并行收集器)
Serial收集器多线程版本,除了使用多线程外和Serial收集器如出一辙。常与Serial Old 收集器一块儿使用,示意图以下:
ParNew收集器
3.Parallel Scavenge收集器
与ParNew收集器同样是一款多线程收集器,其特色在于关注点与别的GC收集器不一样:通常的GC收集器关注于缩短工做线程暂停的时间,而该收集器关注于吞吐量,所以也被称为吞吐量优先收集器。(吞吐量 = 用户运行代码时间 / (用户运行代码时间 + 垃圾回收时间))高吞吐量与停顿时间短相比主要强调任务快完成,所以常和Parallel Old 收集器一块儿使用(没有Parallel Old以前与Serial Old一块儿使用),示意图以下:
Parallel Old 收集器
4.Serial Old收集器
Serial收集器的年老代版本,不在赘述。
5.Parallel Old收集器
年老代的并行收集器,在JDK1.6开始使用。
6.CMS收集器(Concurrent Mark Sweep,并发标记清除收集器)
CMS收集器是一个年老代的收集器,是以最短回收停顿时间为目标的收集器,其示意图以下所示:
CMS收集器
CMS收集器基于标记清除算法实现,主要分为4个步骤:
初始标记,须要stop the world,标记GC Root能关联到的对象,速度快
并发标记,对GC Root执行可达性算法
从新标记,须要stop the world,修复并发标记时因用户线程运行而产生的标记变化,所需时间比初始标记长,但远比并发标记短
并发清理
CMS收集器的缺点在于:
其对于CPU资源很敏感。在并发阶段,虽然CMS收集器不会暂停用户线程,可是会由于占用了一部分CPU资源而致使应用程序变慢,总吞吐量下降。其默认启动的回收线程数是(cpu数量+3)/4,当cpu数较少的时候,会分掉大部分的cpu去执行收集器线程
没法处理浮动垃圾,浮动垃圾即在并发清除阶段由于是并发执行,还会产生垃圾,这一部分垃圾即为浮动垃圾,要等下次收集
CMS收集器使用的是标记清除算法,GC后会产生碎片
7.G1收集器(Garbage First收集器)
相比CMS收集器,G1收集器主要有两处改进:
使用标记整理算法,确保GC后不会产生内存碎片
能够精确控制停顿,容许指定消耗在垃圾回收上的时间
G1收集器能够实如今基本不牺牲吞吐量的前提下完成低停顿的内存回收,这是因为它可以极力地避免全区域的垃圾收集,以前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代、老年代)划分为多个大小固定的独立区域(Region),而且跟踪这些区域里面的垃圾堆积程度,在后台维护一个优先列表,每次根据容许的收集时间,优先回收垃圾最多的区域(这就是Garbage First名称的来由)。区域划分及有优先级的区域回收,保证了G1收集器在有限的时间内能够得到最高的收集效率。
7. Minor GC与Full GC分别在何时发生?
Minor GC也叫Young GC,当年轻代内存满的时候会触发,会对年轻代进行GC
Full GC也叫Major GC,当年老代满的时候会触发,当咱们调用System.gc时也可能会触发,会对年轻代和年老代进行GC
9. 类加载的五个过程:加载、验证、准备、解析、初始化。
JVM把class文件加载的内存,并对数据进行校验、转换解析和初始化,最终造成JVM能够直接使用的Java类型的过程就是加载机制。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称连接。
1.加载
在加载阶段,虚拟机须要完成如下事情:
经过一个类的权限定名来获取定义此类的二进制字节流
将这个字节流所表明的静态存储结构转化为方法区的运行时数据结构
在java堆中生成一个表明这个类的java.lang.Class对象,做为方法去这些数据的访问入口
2.验证
在验证阶段,虚拟机主要完成:
文件格式验证:验证class文件格式规范
元数据验证:这个阶段是对字节码描述的信息进行语义分析,以保证起描述的信息符合java语言规范要求
字节码验证:进行数据流和控制流分析,这个阶段对类的方法体进行校验分析,这个阶段的任务是保证被校验类的方法在运行时不会作出危害虚拟机安全的行为
符号引用验证:符号引用中经过字符串描述的全限定名是否能找到对应的类、符号引用类中的类,字段和方法的访问性(private、protected、public、default)是否可被当前类访问
3.准备
准备阶段是正式为类变量(被static修饰的变量)分配内存并设置变量初始值(0值)的阶段,这些内存都将在方法区中进行分配
4.解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程
常见的解析有四种:
类或接口的解析
字段解析
类方法解析
接口方法解析
5.初始化
初始化阶段才真正开始执行类中定义的java程序代码,初始化阶段是执行类构造器<clinit>()方法的过程
参考博文:Java虚拟机学习(3): 类加载机制
10. 双亲委派模型:Bootstrap ClassLoader、Extension ClassLoader、ApplicationClassLoader
上图中所展现的类加载器之间的这种层次关系,就称为类加载器的双亲委托模型。双亲委托模型要求除了顶层的启动类加载器外,其他的类加载器都应当有本身的父类加载器。这里类加载器之间的父子关系通常不会以继承的关系来实现,而是使用组合关系来复用父加载器的代码。
public abstract class ClassLoader {
privatestatic nativevoid registerNatives();
static{
registerNatives();
}
// The parent class loader for delegation
privateClassLoader parent;
// Hashtable that maps packages to certs
privateHashtable package2certs = newHashtable(11);
双亲委托的工做过程:若是一个类加载器收到了一个类加载请求,它首先不会本身去加载这个类,而是把这个请求委托给父类加载器去完成,每个层次的类加载器都是如此,所以全部的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈本身没法完成加载请求(它管理的范围之中没有这个类)时,子加载器才会尝试着本身去加载。
使用双亲委托模型来组织类加载器之间的关系,有一个显而易见的好处就是Java类随着它的类加载器一块儿具有了一种带有优先级的层次关系,例如java.lang.Object存放在rt.jar之中,不管那个类加载器要加载这个类,最终都是委托给启动类加载器进行加载,所以Object类在程序的各类类加载器环境中都是同一个类,相反,若是没有双亲委托模型,由各个类加载器去完成的话,若是用户本身写一个名为java.lang.Object的类,并放在classpath中,应用程序中可能会出现多个不一样的Object类,java类型体系中最基本安全行为也就没法保证。
分派:静态分派与动态分派
静态分派
全部依赖静态类型来定位方法执行版本的分派动做称为静态分派,其典型应用是方法重载(重载是经过参数的静态类型而不是实际类型来选择重载的版本的)
[java] view plain copy
class Car {}
class Bus extends Car {}
class Jeep extends Car {}
public class Main {
public static void main(String[] args) throws Exception {
// Car 为静态类型,Car 为实际类型
Car car1 = new Car();
// Car 为静态类型,Bus 为实际类型
Car car2 = new Bus();
// Car 为静态类型,Jeep 为实际类型
Car car3 = new Jeep();
showCar(car1);
showCar(car2);
showCar(car3);
}
private static void showCar(Car car) {
System.out.println("I have a Car !");
}
private static void showCar(Bus bus) {
System.out.println("I have a Bus !");
}
private static void showCar(Jeep jeep) {
System.out.println("I have a Jeep !");
}
}
结果
静态分派重载
动态分派
与静态分派相似,动态分派指在在运行期根据实际类型肯定方法执行版本,其典型应用是方法重写(即多态)。
举例Java代码以下:
[java] view plain copy
class Car {
public void showCar() {
System.out.println("I have a Car !");
}
}
class Bus extends Car {
public void showCar() {
System.out.println("I have a Bus !");
}
}
class Jeep extends Car {
public void showCar() {
System.out.println("I have a Jeep !");
}
}
public class Main {
public static void main(String[] args) throws Exception {
// Car 为静态类型,Car 为实际类型
Car car1 = new Car();
// Car 为静态类型,Bus 为实际类型
Car car2 = new Bus();
// Car 为静态类型,Jeep 为实际类型
Car car3 = new Jeep();
car1.showCar();
car2.showCar();
car3.showCar();
}
}
动态分派重写
能够看出来重写是一个根据实际类型决定方法版本的动态分派过程。
参考连接:http://www.importnew.com/20438.html