对Java Serializable(序列化)的理解和总结
http://blog.csdn.net/dreamtdp/article/details/15378329
如果真正只有一个类需要序列化,其他的不能序列化的类只是出现在它的属性中:就写一个它的子类,然后把父类里不能序列化的成员变量用transient覆盖。
一般不能被序列化的类,都有它不能被序列化的原因,比如还原之后可能会出现大量空引用什么的。
子类在隐藏了父类的成员变量或重载了父类的方法后,常常还要用到父类的成员变量,或在重载的方法中使用父类中被重载的方法以简化代码的编写,这时就要访问父类的成员变量或调用父类的方法,Java中通过super来实现对父类成员的访问。
Java中,this用来引用当前对象,与this 类似,super用来引用当前对象的父类。
super的使用有三种情况:
1. 用来访问父类被隐藏的成员变量,如:super.variable
2. 用来调用父类中被重载的方法,如:super.Method ( [paramlist] );
3. 用来调用父类的构造函数,如: super( [paramlist] );
被序列化的对象中的成员变量有如下的规则:
基本类型自动地被序列化,并且在解序列化的时候是有效的。
java对象用暂态关键字标注的对象不能序列化,并且在解序列化的时候是无效的。
任何没有用暂态关键字标注的对象必须实现java.lang.Serializable。
如果对象非常大,不适合在网络上传输或保存在磁盘上,那么最好标识为暂态的。
如果对象代表了一个不能在目标机器上重新构建的资源,如数据库连接,套接字等。
如果对象代表了不想用序列化流传递的敏感信息,也最好标识为暂态。
序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流) 对象,接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。
Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用 serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。 当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。
默认序列化机制
如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。使用默认机制,在序列化对象时,不仅会序列 化当前对象本身,还会对该对象引用的其它对象也进行序列化,同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变 量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。
无论是使用transient关键字,还是使用writeObject()和readObject()方法,其实都是基于Serializable接口的序 列化。JDK中提供了另一个序列化接口--Externalizable,使用该接口之后,之前基于Serializable接口的序列化机制就将失效。
Externalizable继承于Serializable,当使用该接口时,序列化的细节需要由程序员去完成
writeExternal
readExternal
java锁机制
java.util.concurrent.locks.lock
显式的加锁解锁
Lock替代了synchronized
Condition的await和signal方法替代了wait notify notifyAll
参考文章:
http://www.cnblogs.com/dolphin0520/p/3920385.html
ArrayList和LinkedList的底层实现
ArrayList底层实现的数据结构是数组
LinkedList底层实现的数据结构是链表
数组在内存中是连续的
在某个下标想插入一个值的时候
需要把这个下标上的值与他后面所有的值都往后挪一位
如果在存储的数据过多的情况下
你在前面插入一个值后面所有的值都会往后挪一位
挪位操作是通过交换来完成的,所以时间和资源都浪费在交换和挪位上了
但是他的查询是很快的,也是相对链表来说的
链表由于在内存中不一定连续,他是用指针串起来的一个一个的节点
想插入数据直接改变一下指针的指向
它的插入和删除的效率比数组高的不是一点半点
存储的数据越多愈能显示出来差距
但是链表的查询是很麻烦的
他需要从头至尾一个节点一个节点的遍历
数据越多查询效率就越低
这就是为什么ArrayList 和LinkedList一个查询效率高,一个插入删除效率高的原因
hashmap的底层数据结构
那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。
HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。
transient Entry[] table; static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; …… }
可以看出,Entry就是数组中的元素,实现了Map.Entry就是一个key-value对接口,它持有一个指向下一个元素的引用,这就构成了链表。
当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素, 就直接将该元素放到此数组中的该位置上。
散列表(Hash table,也叫哈希表)
参考文章:
http://blog.csdn.net/vking_wang/article/details/14166593
java.util.queue
队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素就是说,队列以一种先进先出的方式管理数据。
BlockingQueue\LinkedBlockingQueue 阻塞
ConcurrentLinkedQueue\PriorityQueue 非阻塞
ConcurrentLinkedQueue是无锁的并发线程安全的队列
阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程 使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列.
阻塞队列或者非阻塞队列参考文章:
http://blog.csdn.net/madun/article/details/20313269
volatile 变量可以被看作是一种 “程度较轻的 synchronized;与 synchronized
块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。
Java语言规范中指出:为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。
这样当多个线程同时与某个对象交互时,就必须要注意到要让线程及时的得到共享成员变量的变化。
而volatile关键字就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
使用建议:在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。
由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
就跟C中的一样 禁止编译器进行优化~~~~
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。
CAS操作――Compare & Set,或是 Compare & Swap 无锁队列
for (;;)
for(;;)
相当于
while(1)
即不设初值,不判断条件,循环变量不增值,无终止的循环。
for后的圆括号中,第一个分号前的内容是执行第一次循环前执行的,第二个分号前的内容是每次执行前都要判断的(如果该处表达式的值为真,那么执行循环体,如果为假,那么就跳出循环体),第二个分号后的内容是每执行完一次循环体后执行的
AbstractQueuedSynchronizer,简称AQS
http://blog.csdn.net/hsuxu/article/details/9467651
编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快;
而解释器则是只在执行程序时,才一条一条的解释成机器语言给计算机来执行,所以运行速度是不如编译后的程序运行的快的.
这是因为计算机不能直接认识并执行我们写的语句,它只能认识机器语言(是二进制的形式)