package test; import java.util.ArrayList; public class A implements Cloneable { public ArrayList b = new ArrayList(); // 浅克隆 // @Override // protected Object clone() throws CloneNotSupportedException { // A a = (A) super.clone(); // return a; // } // // 深克隆 // public Object clone() throws CloneNotSupportedException { // A a = (A) super.clone(); // a.b = (ArrayList) b.clone(); // return a; // } public static void main(String[] args) throws CloneNotSupportedException { A a = new A(); a.b.add("s"); A b = (A) a.clone(); a.b.add("b"); System.out.println(b.b.size()); } }
Object obj=new Object(); ReferenceQueue refQueue=new ReferenceQueue(); SoftReference softRef=new SoftReference(obj,refQueue);
String str=new String("abc"); // 强引用 SoftReference<String> softRef=new SoftReference<String>(str); // 软引用
软引用通常是当内存不足的时候才会清除,内存充足的时候调用system.gc()不会清除掉软引用。(生活无关紧要品,家满了的时候清除,平时不动)html
弱引用:也是配合一个引用队列使用,当被清理的时候,将其放入引用队列中。当gc扫描到该对象的时候,不管内存是否已满,都会回收。是一种比软引用生命力更弱的引用。可是因为gc的优先级较低,因此被清理的String str=new String("abc"); WeakReference<String> abcWeakRef = new WeakReference<String>(str);
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
一、重写equals方法时须要重写hashCode方法,主要是针对Map、Set等集合类型的使用;java
a: Map、Set等集合类型存放的对象必须是惟一的;node
b: 集合类判断两个对象是否相等,是先判断equals是否相等,若是equals返回TRUE,还要再判断HashCode返回值是否ture,只有二者都返回ture,才认为该两个对象是相等的。算法
二、因为Object的hashCode返回的是对象的hash值,因此即便equals返回TRUE,集合也可能断定两个对象不等,因此必须重写hashCode方法,以保证当equals返回TRUE时,hashCode也返回Ture,这样才能使得集合中存放的对象惟一。spring
1. Interface Iterable 迭代器接口,这是Collection类的父接口。实现这个Iterable接口的对象容许使用foreach进行遍历,也就是说,全部的Collection集合对象都具备"foreach可遍历性"。这个Iterable接口只
有一个方法: iterator()。它返回一个表明当前集合对象的泛型<T>迭代器,用于以后的遍历操做 1.1 Collection Collection是最基本的集合接口,一个Collection表明一组Object的集合,这些Object被称做Collection的元素。Collection是一个接口,用以提供规范定义,不能被实例化使用 1) Set Set集合相似于一个罐子,"丢进"Set集合里的多个对象之间没有明显的顺序。Set继承自Collection接口,不能包含有重复元素(记住,这是整个Set类层次的共有属性)。 Set判断两个对象相同不是使用"=="运算符,而是根据equals方法。也就是说,咱们在加入一个新元素的时候,若是这个新元素对象和Set中已有对象进行注意equals比较都返回false,
则Set就会接受这个新元素对象,不然拒绝。 由于Set的这个制约,在使用Set集合的时候,应该注意两点:1) 为Set集合里的元素的实现类实现一个有效的equals(Object)方法、2) 对Set的构造函数,传入的Collection参数不能包
含重复的元素 1.1) HashSet HashSet是Set接口的典型实现,HashSet使用HASH算法来存储集合中的元素,所以具备良好的存取和查找性能。当向HashSet集合中存入一个元素时,HashSet会调用该对象的
hashCode()方法来获得该对象的hashCode值,而后根据该HashCode值决定该对象在HashSet中的存储位置。 值得主要的是,HashSet集合判断两个元素相等的标准是两个对象经过equals()方法比较相等,而且两个对象的hashCode()方法的返回值相等 1.1.1) LinkedHashSet LinkedHashSet集合也是根据元素的hashCode值来决定元素的存储位置,但和HashSet不一样的是,它同时使用链表维护元素的次序,这样使得元素看起来是以插入的顺序保存的。
当遍历LinkedHashSet集合里的元素时,LinkedHashSet将会按元素的添加顺序来访问集合里的元素。 LinkedHashSet须要维护元素的插入顺序,所以性能略低于HashSet的性能,但在迭代访问Set里的所有元素时(遍历)将有很好的性能(链表很适合进行遍历) 1.2) SortedSet 此接口主要用于排序操做,即实现此接口的子类都属于排序的子类 1.2.1) TreeSet TreeSet是SortedSet接口的实现类,TreeSet能够确保集合元素处于排序状态 1.3) EnumSet EnumSet是一个专门为枚举类设计的集合类,EnumSet中全部元素都必须是指定枚举类型的枚举值,该枚举类型在建立EnumSet时显式、或隐式地指定。EnumSet的集合元素也是有序的,
它们以枚举值在Enum类内的定义顺序来决定集合元素的顺序 2) List List集合表明一个元素有序、可重复的集合,集合中每一个元素都有其对应的顺序索引。List集合容许加入重复元素,由于它能够经过索引来访问指定位置的集合元素。List集合默认按元素
的添加顺序设置元素的索引 2.1) ArrayList ArrayList是基于数组实现的List类,它封装了一个动态的增加的、容许再分配的Object[]数组。 2.2) Vector Vector和ArrayList在用法上几乎彻底相同,但因为Vector是一个古老的集合,因此Vector提供了一些方法名很长的方法,但随着JDK1.2之后,java提供了系统的集合框架,就将
Vector改成实现List接口,统一纳入集合框架体系中 2.2.1) Stack Stack是Vector提供的一个子类,用于模拟"栈"这种数据结构(LIFO后进先出) 2.3) LinkedList implements List<E>, Deque<E>。实现List接口,能对它进行队列操做,便可以根据索引来随机访问集合中的元素。同时它还实现Deque接口,即能将LinkedList看成双端队列
使用。天然也能够被看成"栈来使用" 3) Queue Queue用于模拟"队列"这种数据结构(先进先出 FIFO)。队列的头部保存着队列中存放时间最长的元素,队列的尾部保存着队列中存放时间最短的元素。新元素插入(offer)到队列的尾部,
访问元素(poll)操做会返回队列头部的元素,队列不容许随机访问队列中的元素。结合生活中常见的排队就会很好理解这个概念 3.1) PriorityQueue PriorityQueue并非一个比较标准的队列实现,PriorityQueue保存队列元素的顺序并非按照加入队列的顺序,而是按照队列元素的大小进行从新排序,这点从它的类名也能够
看出来 3.2) Deque Deque接口表明一个"双端队列",双端队列能够同时从两端来添加、删除元素,所以Deque的实现类既能够当成队列使用、也能够当成栈使用 3.2.1) ArrayDeque 是一个基于数组的双端队列,和ArrayList相似,它们的底层都采用一个动态的、可重分配的Object[]数组来存储集合元素,当集合元素超出该数组的容量时,系统会在底层重
新分配一个Object[]数组来存储集合元素 3.2.2) LinkedList 1.2 Map Map用于保存具备"映射关系"的数据,所以Map集合里保存着两组值,一组值用于保存Map里的key,另一组值用于保存Map里的value。key和value均可以是任何引用类型的数据。Map的key不允
许重复,即同一个Map对象的任何两个key经过equals方法比较结果老是返回false。 关于Map,咱们要从代码复用的角度去理解,java是先实现了Map,而后经过包装了一个全部value都为null的Map就实现了Set集合 Map的这些实现类和子接口中key集的存储形式和Set集合彻底相同(即key不能重复) Map的这些实现类和子接口中value集的存储形式和List很是相似(即value能够重复、根据索引来查找) 1) HashMap 和HashSet集合不能保证元素的顺序同样,HashMap也不能保证key-value对的顺序。而且相似于HashSet判断两个key是否相等的标准也是: 两个key经过equals()方法比较返回true、
同时两个key的hashCode值也必须相等 1.1) LinkedHashMap LinkedHashMap也使用双向链表来维护key-value对的次序,该链表负责维护Map的迭代顺序,与key-value对的插入顺序一致(注意和TreeMap对全部的key-value进行排序进行区
分) 2) Hashtable 是一个古老的Map实现类 2.1) Properties Properties对象在处理属性文件时特别方便(windows平台上的.ini文件),Properties类能够把Map对象和属性文件关联起来,从而能够把Map对象中的key-value对写入到属性文
件中,也能够把属性文件中的"属性名-属性值"加载到Map对象中 3) SortedMap 正如Set接口派生出SortedSet子接口,SortedSet接口有一个TreeSet实现类同样,Map接口也派生出一个SortedMap子接口,SortedMap接口也有一个TreeMap实现类 3.1) TreeMap TreeMap就是一个红黑树数据结构,每一个key-value对即做为红黑树的一个节点。TreeMap存储key-value对(节点)时,须要根据key对节点进行排序。TreeMap能够保证全部的
key-value对处于有序状态。一样,TreeMap也有两种排序方式: 天然排序、定制排序 4) WeakHashMap WeakHashMap与HashMap的用法基本类似。区别在于,HashMap的key保留了对实际对象的"强引用",这意味着只要该HashMap对象不被销毁,该HashMap所引用的对象就不会被垃圾回收。
但WeakHashMap的key只保留了对实际对象的弱引用,这意味着若是WeakHashMap对象的key所引用的对象没有被其余强引用变量所引用,则这些key所引用的对象可能被垃圾回收,当垃
圾回收了该key所对应的实际对象以后,WeakHashMap也可能自动删除这些key所对应的key-value对 5) IdentityHashMap IdentityHashMap的实现机制与HashMap基本类似,在IdentityHashMap中,当且仅当两个key严格相等(key1 == key2)时,IdentityHashMap才认为两个key相等 6) EnumMap EnumMap是一个与枚举类一块儿使用的Map实现,EnumMap中的全部key都必须是单个枚举类的枚举值。建立EnumMap时必须显式或隐式指定它对应的枚举类。EnumMap根据key的天然顺序
(即枚举值在枚举类中的定义顺序)
Entry对象惟一表示一个键值对,有四个属性:数据库
-K key 键对象
-V value 值对象
-int hash 键对象的hash值
-Entry entry 指向链表中下一个Entry对象,可为null,表示当前Entry对象在链表尾部编程
内部有一个指向entry的数组,数组的每一个元素都是一个entry,每一个entry结构又是个链表结构,指向下一个entry。windows
1.8以前当发生hash冲突的时候可使用entry的链表来解决冲突;可是很难保证hashmap的均匀性,很容易出现很长的链表,影响查询效率。设计模式
1.8以后使用了红黑树解决hash冲突,具体的能够看:http://www.javashuo.com/article/p-majqhclb-n.htmlapi
以上两者是相同的了解了HashMap为何线程不安全,那如今看看如何线程安全的使用HashMap。这个无非就是如下三种方式:
例子:
//Hashtable Map<String, String> hashtable = new Hashtable<>(); //synchronizedMap Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>()); //ConcurrentHashMap Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();
依次来看看。
先稍微吐槽一下,为啥命名不是HashTable啊,看着好难受,无论了就装做它叫HashTable吧。这货已经不经常使用了,就简单说说吧。HashTable源码中是使用synchronized
来保证线程安全的,好比下面的get方法和put方法:
public synchronized V get(Object key) { // 省略实现 } public synchronized V put(K key, V value) { // 省略实现 }
因此当一个线程访问HashTable的同步方法时,其余线程若是也要访问同步方法,会被阻塞住。举个例子,当一个线程使用put方法时,另外一个线程不但不可使用put方法,连get方法都不能够,好霸道啊!!!so~~,效率很低,如今基本不会选择它了。
ConcurrentHashMap(如下简称CHM)是JUC包中的一个类,Spring的源码中有不少使用CHM的地方。以前已经翻译过一篇关于ConcurrentHashMap的博客,如何在java中使用ConcurrentHashMap,里面介绍了CHM在Java中的实现,CHM的一些重要特性和什么状况下应该使用CHM。须要注意的是,上面博客是基于Java 7的,和8有区别,在8中CHM摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法,有时间会从新总结一下。
看了一下源码,SynchronizedMap的实现仍是很简单的。
// synchronizedMap方法 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } // SynchronizedMap类 private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { this.m = Objects.requireNonNull(m); mutex = this; } SynchronizedMap(Map<K,V> m, Object mutex) { this.m = m; this.mutex = mutex; } public int size() { synchronized (mutex) {return m.size();} } public boolean isEmpty() { synchronized (mutex) {return m.isEmpty();} } public boolean containsKey(Object key) { synchronized (mutex) {return m.containsKey(key);} } public boolean containsValue(Object value) { synchronized (mutex) {return m.containsValue(value);} } public V get(Object key) { synchronized (mutex) {return m.get(key);} } public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V remove(Object key) { synchronized (mutex) {return m.remove(key);} } // 省略其余方法 }
从源码中能够看出调用synchronizedMap()方法后会返回一个SynchronizedMap类的对象,而在SynchronizedMap类中使用了synchronized同步关键字来保证对Map的操做是线程安全的。
这是要靠数听说话的时代,因此不能只靠嘴说CHM快,它就快了。写个测试用例,实际的比较一下这三种方式的效率(源码来源),下面的代码分别经过三种方式建立Map对象,使用ExecutorService
来并发运行5个线程,每一个线程添加/获取500K个元素。
public class CrunchifyConcurrentHashMapVsSynchronizedMap { public final static int THREAD_POOL_SIZE = 5; public static Map<String, Integer> crunchifyHashTableObject = null; public static Map<String, Integer> crunchifySynchronizedMapObject = null; public static Map<String, Integer> crunchifyConcurrentHashMapObject = null; public static void main(String[] args) throws InterruptedException { // Test with Hashtable Object crunchifyHashTableObject = new Hashtable<>(); crunchifyPerformTest(crunchifyHashTableObject); // Test with synchronizedMap Object crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<String, Integer>()); crunchifyPerformTest(crunchifySynchronizedMapObject); // Test with ConcurrentHashMap Object crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>(); crunchifyPerformTest(crunchifyConcurrentHashMapObject); } public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException { System.out.println("Test started for: " + crunchifyThreads.getClass()); long averageTime = 0; for (int i = 0; i < 5; i++) { long startTime = System.nanoTime(); ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE); for (int j = 0; j < THREAD_POOL_SIZE; j++) { crunchifyExServer.execute(new Runnable() { @SuppressWarnings("unused") @Override public void run() { for (int i = 0; i < 500000; i++) { Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000); // Retrieve value. We are not using it anywhere Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber)); // Put value crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber); } } }); } // Make sure executor stops crunchifyExServer.shutdown(); // Blocks until all tasks have completed execution after a shutdown request crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); long entTime = System.nanoTime(); long totalTime = (entTime - startTime) / 1000000L; averageTime += totalTime; System.out.println("2500K entried added/retrieved in " + totalTime + " ms"); } System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n"); } }
测试结果:
这个就不用废话了,CHM性能是明显优于Hashtable和SynchronizedMap的,CHM花费的时间比前两个的一半还少,哈哈,之后再有人问就能够甩数据了。
Test started for: class java.util.Hashtable 2500K entried added/retrieved in 2018 ms 2500K entried added/retrieved in 1746 ms 2500K entried added/retrieved in 1806 ms 2500K entried added/retrieved in 1801 ms 2500K entried added/retrieved in 1804 ms For class java.util.Hashtable the average time is 1835 ms Test started for: class java.util.Collections$SynchronizedMap 2500K entried added/retrieved in 3041 ms 2500K entried added/retrieved in 1690 ms 2500K entried added/retrieved in 1740 ms 2500K entried added/retrieved in 1649 ms 2500K entried added/retrieved in 1696 ms For class java.util.Collections$SynchronizedMap the average time is 1963 ms Test started for: class java.util.concurrent.ConcurrentHashMap 2500K entried added/retrieved in 738 ms 2500K entried added/retrieved in 696 ms 2500K entried added/retrieved in 548 ms 2500K entried added/retrieved in 1447 ms 2500K entried added/retrieved in 531 ms For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms
通常状况下,咱们用的最多的是HashMap,HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键能够直接获取它的值,具备很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。
TreeMap取出来的是排序后的键值对。但若是您要按天然顺序或自定义顺序遍历键,那么TreeMap会更好。
LinkedHashMap 是HashMap的一个子类,若是须要输出的顺序和输入的相同,那么用LinkedHashMap能够实现,它还能够按读取顺序来排列,像链接池中能够应用。
什么是反射:
一、在运行状态中,对于任意一个类,都可以知道这个类的属性和方法。
二、对于任意一个对象,都可以调用它的任何方法和属性。
这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。
反射的做用:
在JAVA中,只有给定类的名字,就能够经过反射机制来获取类的全部信息,能够动态的建立对象和编译。
实现原理:
JAVA语言编译以后会生成一个.class文件,反射就是经过字节码文件找到某一个类、类中的方法以及属性等。
反射的实现主要借助如下四个类:
Class:类的对象
Constructor:类的构造方法
Field:类中的属性对象
Method:类中的方法对象
根据类的名字获取类:Class<T> c = Class.forName("类名");
根据类c获取对象:Object o = c.newInstance();
根据类c获取实现的接口:Class<?> interfaces = c.getInterfaces();
获取有参构造器:Constructor<?> con = c.getConstructor(String.class,int.class);
获取属性:Field f2 = c.getField("age");
方法调用:
public class ReflectCase { public static void main(String[] args) throws Exception { Proxy target = new Proxy(); Method method = Proxy.class.getDeclaredMethod("run"); method.invoke(target); } static class Proxy { public void run() { System.out.println("run"); } } }
AOP是面向切面编程,是spring中的一个特性,能够增长业务的重用性,一旦某一功能出现问题直接修改该功能所在模块就好。隔离业务,下降耦合度,提升开发效率。经常使用来处理日志记录,性能统计,安全分析等。
OOP是java的基本编程思想,面向对象编程。针对业务处理过程当中的实体以及属性和行为进行封装,以得到更加清晰的逻辑划分。
区别:对于某一类物体,其所具备的属性和动做方法可使用oop来进行封装;而对于一个行为片断咱们只能使用aop进行隔离,最经常使用的AOP应用在数据库链接以及事务处理上,实现模式为代理模式和工厂模式。