首页 文章 精选 留言 我的

精选列表

搜索[源码学习],共10000篇文章
优秀的个人博客,低调大师

Java LinkedHashMap类源码解析

LinkedHashMap继承了HashMap,他在HashMap的基础上增加了一个双向链表的结构,链表默认维持key插入的顺序,重复的key值插入不会改变顺序,适用于使用者需要返回一个顺序相同的map对象的情况。还可以生成access-order顺序的版本,按照最近访问顺序来存储,刚被访问的结点处于链表的末尾,适合LRU,put get compute merge都算作一次访问,其中put key值相同的结点也算作一次访问,replace只有在换掉一个键值对的时候才算一次访问,putAll产生的访问顺序取决于原本map的迭代器实现。 在插入键值对时,可以通过对removeEldestEntry重写来实现新键值对插入时自动删除最旧的键值对 拥有HashMap提供的方法,迭代器因为是通过遍历双向链表,所以额外开销与size成正比与capacity无关,因此选择过大的初始大小对于遍历时间的增加没有HashMap严重,后者的遍历时间依赖与capacity。 同样是非线程安全方法,对于LinkedHashMap来说,修改结构的操作除了增加和删除键值对外,还有对于access-order时进行了access导致迭代器顺序改变,主要是get操作,对于插入顺序的来说,仅仅修改一个已有key值的value值不是一个修改结构的操作,但对于访问顺序,put和get已有的key值会改变顺序。迭代器也是fail-fast设计,但是fail-fast只是一个调试功能,一个设计良好的程序不应该出现这个错误 因为HashMap加入了TreeNode,所以现在LinkedHashMap也有这个功能 以下描述中的链表,若无特别说明都是指LinkedHashMap的双向链表 先来看一下基本结构,每个键值对加入了前后指针,集合加入了头尾指针来形成双向链表,accessOrder代表链表是以访问顺序还是插入顺序存储 static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after;//增加了先后指针来形成双向链表 Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } } /** * The head (eldest) of the doubly linked list.头部 */ transient LinkedHashMap.Entry<K,V> head; /** * The tail (youngest) of the doubly linked list.尾部 */ transient LinkedHashMap.Entry<K,V> tail; //true访问顺序 false插入顺序 final boolean accessOrder; 然后是几个内部方法。linkNodeLast将p连接到链表尾部 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) { LinkedHashMap.Entry<K,V> last = tail; tail = p; if (last == null) head = p;//原本链表为空则p同时为头部 else { p.before = last; last.after = p; } } transferLinks用dst替换src private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) { LinkedHashMap.Entry<K,V> b = dst.before = src.before; LinkedHashMap.Entry<K,V> a = dst.after = src.after; if (b == null) head = dst; else b.after = dst; if (a == null) tail = dst; else a.before = dst; } reinitialize在调用HashMap方法的基础上,将head和tail设为null void reinitialize() { super.reinitialize(); head = tail = null; } newNode生成一个LinkedHashMap结点,next指向e,插入到LinkedHashMap链表末端 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) { LinkedHashMap.Entry<K,V> p = new LinkedHashMap.Entry<K,V>(hash, key, value, e);//新建一个键值对,next指向e linkNodeLast(p);//p插入到LinkedHashMap链表末端 return p; } replacementNode根据原结点生成一个LinkedHashMap结点替换原结点 Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);//生成一个新的键值对next是给出的next参数 transferLinks(q, t);//用t替换q return t; } newTreeNode生成一个TreeNode结点,next指向next,插入到LinkedHashMap链表末端 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) { TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);//生成一个TreeNode,next指向参数next linkNodeLast(p);//p插入到LinkedHashMap链表末端 return p; } replacementTreeNode根据结点p生成一个新的TreeNode,next设为给定的next,替换原本的p TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) { LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p; TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next); transferLinks(q, t);//根据结点p生成一个新的TreeNode,next设为给定的next,替换原本的p return t; } afterNodeRemoval从LinkedHashMap的链上移除结点e void afterNodeRemoval(Node<K,V> e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.before = p.after = null; if (b == null) head = a; else b.after = a; if (a == null) tail = b; else a.before = b; } afterNodeInsertion可能移除最旧的结点,需要evict为true同时链表不为空同时removeEldestEntry需要重写 void afterNodeInsertion(boolean evict) { LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) {//removeEldestEntry需要重写才从发挥作用,否则一定返回false K key = first.key;//移除链表头部的结点 removeNode(hash(key), key, null, false, true); } } afterNodeAccess在访问过后将结点e移动到链表尾部,需要Map是access-order,若移动成功则增加modCount void afterNodeAccess(Node<K,V> e) { LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) {//Map是access-order同时e不是链表的尾部 LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after = null; if (b == null)//将结点e从链表中剪下 head = a; else b.after = a; if (a != null) a.before = b; else last = b; if (last == null) head = p; else { p.before = last; last.after = p; } tail = p;//结点e移动到链表尾部 ++modCount;//因为有access-order下结点被移动,所以增加modCount } } 构造函数方面,accessOrder默认是false插入顺序,初始大小为16,负载因子为0.75,这里是同HashMap。复制构造也是调用了HashMap.putMapEntries方法 containsValue遍历链表寻找相等的value值,这个操作一定不会造成结构改变 public boolean containsValue(Object value) { for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {//检查同样是根据LinkedHashMap提供的链表顺序进行遍历 V v = e.value; if (v == value || (value != null && value.equals(v))) return true; } return false; } get方法复用HashMap的getNode方法,若找到结点且Map是访问顺序时,要将访问的结点放到链表最后,若没找到则返回null。而getOrDefault仅有的区别是没找到时返回defaultValue public V get(Object key) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null)//复用HashMap的getNode方法 return null; if (accessOrder) afterNodeAccess(e);//access-order时将e放到队尾 return e.value; } public V getOrDefault(Object key, V defaultValue) { Node<K,V> e; if ((e = getNode(hash(key), key)) == null) return defaultValue;//复用HashMap的getNode方法,若没有找到对应的结点则返回defaultValue if (accessOrder) afterNodeAccess(e);//access-order时将e放到队尾 return e.value; } clear方法在HashMap的基础上要把head和tail设为null public void clear() { super.clear(); head = tail = null; } removeEldestEntry在put和putAll插入键值对时调用,原本是一定返回false的,如果要自动删除最旧的键值对要返回true,需要进行重写。比如下面这个例子,控制size不能超过100 private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } 下面两个方法和HashMap相似,返回key的Set和value的Collection还有返回键值对的Set,这个是直接引用,所以对它们的remove之类的修改会直接反馈到LinkedHashMap上 public Set<K> keySet() { Set<K> ks = keySet; if (ks == null) { ks = new LinkedKeySet(); keySet = ks; } return ks;//返回key值的set } public Collection<V> values() { Collection<V> vs = values; if (vs == null) { vs = new LinkedValues(); values = vs; } return vs;//返回一个包含所有value值的Collection } public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es; return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;//返回一个含有所有键值对的Set } 检查HashMap的putVal方法,我们可以看到在找到了相同key值并修改value值时会调用afterNodeAccess,对于access-order会改变结点顺序 if (e != null) { // 找到了相同的key则修改value值并返回旧的value V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; }

优秀的个人博客,低调大师

Java LinkedList类源码解析

LinkedList底层为双向链表同样继承了AbstractSequentialList<E>,跟ArrayList的数组相比读取效率低,不支持随机读取,碎片化空间利用率高,平均随机插入效率相对高。同时可以用来实现queue。属性有: transient int size = 0;list大小 transient Node<E> first;头指针 transient Node<E> last;尾指针 private void linkFirst(E e) void linkLast(E e) 将e添加到链表的头部和尾部,size与modCount加一 void linkBefore(E e, Node<E> succ)将e插入到succ结点之前 private E unlinkFirst(Node<E> f) private E unlinkLast(Node<E> l) 移除头部或尾部结点,size减1,modCount加1,将移除结点的next prev item值都设为null以触发gc E unlink(Node<E> x)移除结点x,需要再判断有无前驱和后驱结点,若没有则要改变头尾指针,同样将移除结点的next prev item值都设为null以触发gc public E getFirst() public E getLast() 返回first或last指向的结点,链表为空时抛错 public E removeFirst() public E removeLast() 调用unlinkLast移除并返回头部或尾部的结点,链表为空时抛错 public void addFirst(E e)调用linkFirst(e),将元素插入到头部 public void addLast(E e) public boolean add(E e) 两个方法都是调用linkLast(e),除了返回值外是等价的 public boolean remove(Object o)o==null时,通过unlink方法移除所有x==null的元素,否则移除o.equals(x)的元素,每有一个符合的元素就调用一次unlink所以modCount的增加值为移除元素的个数 public boolean addAll(int index, Collection<? extends E> c)将c中的集合插入到index位置。首先检查index是否符合链表长度范围,若c中没有元素则直接返回false,否则遍历c中的元素产生新的结点并链接到index指向位置,检查是否需要修改first和last的位置,最后修改size和modCount++ public void clear()遍历所有结点,将last prev item全部设为null,size为0,modCount++ public E set(int index, E element)检查index范围后设置为item=element,不会改变modCount public void add(int index, E element)检查index范围,若index==size即插入再末尾,调用linkLast(element),否则调用linkBefore(element, node(index))因此会造成modCount++ public E remove(int index) 检查index范围,index >= 0 && index < size则调用unlink(node(index))移除元素,modCount++ Node<E> node(int index)返回index下标的结点,若index超过size的一半则从last开始向头寻找,否则从first开始向后寻找 public int indexOf(Object o)寻找与o相等的下标最小的链表元素,若没有则返回-1,比较逻辑依然根据o是否是null来区分 public int lastIndexOf(Object o)从尾部开始搜索第一个符合条件的元素下标,和上面一个方法类似 public E peek()返回first指向结点的item,若为空则返回null public E element()也是返回first.item,区别是为空会抛错 public E poll()在peek()的基础上,若不为null会删除第一个元素 public boolean offer(E e)同add(e) public boolean offerFirst(E e)同addFirst public boolean offerLast(E e)同addLast public E peekFirst()同peek() public E peekLast()返回尾部元素,为空则返回null public E pollFirst()同poll() public E pollLast()返回尾部元素,为空则返回null,不为空移除尾部元素 public void push(E e)将e添加到头部 public E pop()移除头部元素 public boolean removeFirstOccurrence(Object o)同remove(o) public boolean removeLastOccurrence(Object o)移除最后一个与o相等的元素 public Object[] toArray()新建一个数组,遍历链表将元素复制到数组中 private void writeObject(java.io.ObjectOutputStream s) private void readObject(java.io.ObjectInputStream s) 序列化的方式和ArrayList相同,是通过对象输入输出流来完成,输入时调用linkLast将读取到的元素加入链表末尾个人GitHub地址: https://github.com/GrayWind33

优秀的个人博客,低调大师

Java ArrayList类源码解析

ArrayList是最常用的集合类,底层是由数组实现的 首先可以看到,有两个static final对象数组,也就是被线程间共享的,EMPTY_ELEMENTDATA是非default大小的空集合,原因是要辨别第一次添加元素时应该扩展的大小。 private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; transient Object[] elementData;这里是存储实际元素的对象数组,transient关键字表示该部分内容不能被序列化,ArrayList实际上序列化调用的是writeObject和readObject方法,而不是直接由elementData进行序列化,原因是elementData里面有空元素,这部分不需要进行序列化,可以节约内存和时间。 private int size;是实际元素的个数 protected transient int modCount = 0;modCount的作用的保证线程安全,ArrayList本身是非同步的集合类,出现遍历操作都要检查modCount值是否改变。除了自身的set方法外,其他所有存在对elementData写入的操作都会增加modCount值。 public ArrayList(int initialCapacity)给定参数为0时elementData直接赋值为EMPTY_ELEMENTDATA,否则新建一个指定大小的数组 public ArrayList()直接赋值DEFAULTCAPACITY_EMPTY_ELEMENTDATA public ArrayList(Collection<? extends E> c)若集合c不为空,elementData引用c.toArray产生的数组,若产生的不是Object[]需要再调用Arrays.copyOf进行复制。集合为空则赋值为EMPTY_ELEMENTDATA public void trimToSize()通过Arrays.copyOf将elementData中的空元素去掉,若size==0则赋值为EMPTY_ELEMENTDATA public void ensureCapacity(int minCapacity)参数给定所需的最小大小,检查该大小是否大于最小扩展大小(DEFAULTCAPACITY_EMPTY_ELEMENTDATA时该值为10否则为0),大于时调用ensureExplicitCapacity(minCapacity)对集合进行扩展,可以手动调用 private void ensureCapacityInternal(int minCapacity)和上一个相似,add元素时检查大小是否足够,仍未DEFAULTCAPACITY_EMPTY_ELEMENTDATA时扩展10和minCapacity的较大值,否则直接扩展minCapacity private void ensureExplicitCapacity(int minCapacity) minCapacity大于elementData的元素个数时,调用grow(minCapacity),无论是否增长了大小,该方法都会增加modCount private void grow(int minCapacity)增长大小为minCapacity和elementData.length*1.5中的较大值,最大不能超过Integer.max(2的31次)-8,通过Arrays.copyOf复制元素 public int size()返回size值 public boolean isEmpty()返回size是否为0 public int indexOf(Object o)o为null时检查elementData中是否有元素==o,否则检查是否有o.equals(x),存在则返回第一个符合的下标,不存在返回-1 public int lastIndexOf(Object o)与上一个方法相比区别在于从尾部开始遍历,返回的是下标最大的一个 public boolean contains(Object o)调用indexOf,返回不为-1时为true public Object clone()新建一个ArrayList,elementData复制过去,其他内部变量也相同 public Object[] toArray()返回Arrays.copyOf(elementData, size),所以不含后面的空数组 public <T> T[] toArray(T[] a)若a.length<size,则新建一个包含elementData中实际对象的数组返回。否则将elementData中的元素复制给a并设a[size] = null,返回a public E get(int index)获取指定下标元素内容 public E set(int index, E element)将指定下标元素改为element并返回旧值 public boolean add(E e)检查数组大小是否需要扩展后,添加元素,增加size,一定会增加modCount public void add(int index, E element)先检查index是否符合范围,然后检查数组大小是否需要扩展,将index后的元素向后复制以为,然后elementData[index]=element,一定会增加modCount public E remove(int index) 先检查index是否符合范围,然后modCount增加,index后的元素往前复制一位,elementData[size]的值为null便于触发gc,size减一,返回旧元素的值 public boolean remove(Object o)首先按照indexOf的逻辑寻找第一个相等的元素,如果存在则将后面的元素往复制一位最后一个元素设为null并增加modCount返回true,否则返回false。因为index肯定是符合返回的,所以调用不检查index是否符合范围private void fastRemove(int index) public void clear()modCount增加,将size个元素都设为null,size改为0 public boolean addAll(Collection<? extends E> c)通过System.arraycopy将c的元素复制到集合的后面,modCount增加,size增加c的元素个数,若新size不为0则返回true public boolean addAll(int index, Collection<? extends E> c)和上面相比区别在于要将c复制到从index开始的位置而不是末尾,同样会增加modCount protected void removeRange(int fromIndex, int toIndex)增加modCount,将toIndex后面的元素复制到fromIndex开始的位置,多余的设为null便于触发GC,修改size值 public boolean removeAll(Collection<?> c)移除和c中相同的元素,若原本集合中元素个数变动增加modCount public boolean retainAll(Collection<?> c)保留和c中相同的元素,若原本集合中元素个数变动增加modCount private void writeObject(java.io.ObjectOutputStream s) 序列化函数,通过s.writeObject将对象写入到输出流中,通过检查modCount的值是否改变来确保线程安全 private void readObject(java.io.ObjectInputStream s)序列化函数,通过s.readObject()将输入流中的对象读取到elementData中 public Iterator<E> iterator()返回一个实现的迭代器,只能从头向尾移动,移动时元素个数不能被其他线程改变。可以移除和forEachRemaining (Consumer<? super E> consumer)进行批量操作,批量操作时modCount不能被其他线程改变 public ListIterator<E> listIterator(int index)返回一个从指定下标开始的列表迭代器是Iterator<E>的继承类,若不输入参数默认从0开始。可以前后移动,移动时检查元素个数不能被其他线程修改。额外提供了set和add方法。 public List<E> subList(int fromIndex, int toIndex)返回一个子集合,对子集合的add remove set操作会改变parent对应的元素,并改变两者的modCount值。subList为抽象类,本身并没有elementData区域,对他元素的操作会直接操作在parent的elementData上 public boolean removeIf(Predicate<? super E> filter)根据过滤器filter移除符合的元素,整个过程要求modCount不能被其他线程改变,先用一个BitSet存储要删除的序号,然后通过removeSet.nextClearBit(i)进行复制移动,最后modCount++ public void replaceAll(UnaryOperator<E> operator)根据规则替换所有符合的元素,会改变modCount public void sort(Comparator<? super E> c)排序,会改变modCount个人GitHub地址: https://github.com/GrayWind33

优秀的个人博客,低调大师

JDK动态代理源码解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/80557290 分析版本jdk1.8 在分析jdk动态代理之前,先来了解java WeakReference弱引用的使用。运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要。jdk动态代理使用弱引用指向cache中的代理类,以便代理类对象能够被GC回收。 在java中,当一个对象O被创建时,它被放在Heap里,当GC运行的时候,如果发现没有任何引用指向O,O就会被回收以腾出内存空间,或者说,一个对象被回收,必须满足两个条件:1)没有任何引用指向它;2)GC被运行。在现实情况写代码的时候, 我们往往通过把所有指向某个对象的referece置空来保证这个对象在下次GC运行的时候被回收 (可以用java -verbose:gc来观察gc的行为)。 [java] view plain copy Objectc=newCar(); c=null; 但是, 手动置空对象对于程序员来说, 是一件繁琐且违背自动回收的理念的. 对于简单的情况, 手动置空是不需要程序员来做的, 因为在java中, 对于简单对象, 当调用它的方法执行完毕后, 指向它的引用会被从stack中popup, 所以他就能在下一次GC执行时被回收了. 但是, 也有特殊例外. 当使用cache的时候, 由于cache的对象正是程序运行需要的, 那么只要程序正在运行, cache中的引用就不会被GC给(或者说, cache中的reference拥有了和主程序一样的life cycle). 那么随着cache中的reference越来越多, GC无法回收的object也越来越多, 无法被自动回收. 当这些object需要被回收时, 回收这些object的任务只有交给程序编写者了. 然而这却违背了GC的本质(自动回收可以回收的objects).所以, java中引入了weak reference. 相对于前面举例中的strong reference: [java] view plain copy Objectc=newCar();//只要c还指向carobject,carobject就不会被回收 当一个对象仅仅被weak reference指向, 而没有任何其他strong reference指向的时候, 如果GC运行, 那么这个对象就会被回收. weak reference的语法是: [java] view plain copy WeakReference<Car>weakCar=newWeakReference(Car)(car); 当要获得weak reference引用的object时, 首先需要判断它是否已经被回收: [java] view plain copy weakCar.get(); 如果此方法为空, 那么说明weakCar指向的对象已经被回收了,下面来看一个例子: [java] view plain copy packageweakreference; /** *@authorywchen */ publicclassCar{ privatedoubleprice; privateStringcolour; publicCar(doubleprice,Stringcolour){ this.price=price; this.colour=colour; } publicdoublegetPrice(){ returnprice; } publicvoidsetPrice(doubleprice){ this.price=price; } publicStringgetColour(){ returncolour; } publicvoidsetColour(Stringcolour){ this.colour=colour; } publicStringtoString(){ returncolour+"carcosts$"+price; } } packageweakreference; importjava.lang.ref.WeakReference; /** *@authorywchen */ publicclassTestWeakReference{ publicstaticvoidmain(String[]args){ Carcar=newCar(22000,"silver"); WeakReference<Car>weakCar=newWeakReference<Car>(car); inti=0; while(true){ if(weakCar.get()!=null){ i++; System.out.println("Objectisalivefor"+i+"loops-"+weakCar); }else{ System.out.println("Objecthasbeencollected."); break; } } } } 在上例中, 程序运行一段时间后, 程序打印出"Object has been collected." 说明, weak reference指向的对象的被回收了. ReferenceQueue在weak reference指向的对象被回收后, weak reference本身其实也就没有用了. java提供了一个ReferenceQueue来保存这些所指向的对象已经被回收的reference. 用法是在定义WeakReference的时候将一个ReferenceQueue的对象作为参数传入构造函数. jdk动态代理 面向对象编程(OOP)有一些弊端,为多个不具有继承关系的对象引入同一个公共行为,例如日志、安全监测等,需要在每个对象里引入公共行为,导致程序中产生大量重复代码。不便于维护。所以就有了一个面向对象编程的补充,即面向切面编程(AOP),AOP所关注的是横向,OOP关注的是纵向。AOP的实现,可采用JDK动态代理、CGLIB代理。 JDK动态代理:其代理对象必须是某个接口的实现,它是通过在运行期间创建一个接口的实现来完成目标对象的代理。 CBLIB代理:实现原理类似于JDK动态代理,它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层依靠ASM(开源的字节码编辑类库)操作字节码实现,性能比JDK强。 jdk动态代理实现示例 1.新建委托类 [java] view plain copy /** *目标对象实现的接口,用JDK来生成代理对象一定要实现一个接口 *@authoryawenchen *@since2016-11-15 * */ publicinterfaceUserService{ /** *目标方法 */ publicabstractvoidadd(); } [java] view plain copy /** *目标对象 *@authoryawenchen *@since2016-11-15 * */ publicclassUserServiceImplimplementsUserService{ /*(non-Javadoc) *@seedynamic.proxy.UserService#add() */ publicvoidadd(){ System.out.println("--------------------add---------------"); } } UserService 是一个接口,UserServiceImpl 接口的实现类,也称委托类。动态代理要求代理对象必须是接口的实现类。因此UserServiceImpl 实现了UserService 。 2.实现InvocationHandlerj接口 [java] view plain copy importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; /** *实现自己的InvocationHandler *@authorywchen *@since2016-11-15 * */ publicclassMyInvocationHandlerimplementsInvocationHandler{ //目标对象 privateObjecttarget; /** *构造方法 *@paramtarget目标对象 */ publicMyInvocationHandler(Objecttarget){ super(); this.target=target; } /** *执行目标对象的方法 */ publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{ //在目标对象的方法执行之前简单的打印一下 System.out.println("------------------before------------------"); //执行目标对象的方法 Objectresult=method.invoke(target,args); //在目标对象的方法执行之后简单的打印一下 System.out.println("-------------------after------------------"); returnresult; } /** *获取目标对象的代理对象 *@return代理对象 */ publicObjectgetProxy(){ returnProxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this); } } target属性,表示代理的目标对象。InvocationHandler是负责连接代理对象与目标对象的自定义中间类MyInvocationHandler必须实现的接口,只有一个方法。 public Object invoke(Object proxy, Method method, Object[] args) 参数说明:Proxy表示通过Proxy.newProxyInstance()生成的代理类对象。Method表示目标对象被调用的方法。Args表示目标对象被调用方法的输入参数。 3.通过Proxy类静态函数生成代理对象 [java] view plain copy publicclassProxyTest{ publicstaticvoidmain(String[]args){ //实例化目标对象 UserServiceuserService=newUserSericeImpl(); //实例化InvocationHander MyInvocationHandlerinvocationHandler=newMyInvocationHandler(userService); //根据目标对象生成代理对象 UserServiceproxy=(UserService)invocationHandler.getProxy(); //调用代理对象的方法 proxy.add(); } } 执行结果 ------------------before------------------ --------------------add--------------- -------------------after------------------ 动态代理原理 [java] view plain copy /** *loader:类加载器 *interfaces:目标对象实现的接口 *h:InvocationHandler的实现类 */ publicstaticObjectnewProxyInstance(ClassLoaderloader, Class<?>[]interfaces, InvocationHandlerh) throwsIllegalArgumentException { Objects.requireNonNull(h); finalClass<?>[]intfs=interfaces.clone(); finalSecurityManagersm=System.getSecurityManager(); if(sm!=null){ checkProxyAccess(Reflection.getCallerClass(),loader,intfs); } /* *Lookuporgeneratethedesignatedproxyclass.从缓存中查找或生成目标对象的代理类 */ Class<?>cl=getProxyClass0(loader,intfs); /* *Invokeitsconstructorwiththedesignatedinvocationhandler. */ try{ if(sm!=null){ checkNewProxyPermission(Reflection.getCallerClass(),cl); } //调用代理对象的构造函数(代理对象的构造函数$Proxy0(InvocationHandlerh),通过字节码反编译可以查看生成的代理类) finalConstructor<?>cons=cl.getConstructor(constructorParams); finalInvocationHandlerih=h; if(!Modifier.isPublic(cl.getModifiers())){ AccessController.doPrivileged(newPrivilegedAction<Void>(){ publicVoidrun(){ cons.setAccessible(true); returnnull; } }); } //生成代理类的实例,并把MyInvocationHander的实例作为构造函数参数传入 returncons.newInstance(newObject[]{h}); }catch(IllegalAccessException|InstantiationExceptione){ thrownewInternalError(e.toString(),e); }catch(InvocationTargetExceptione){ Throwablet=e.getCause(); if(tinstanceofRuntimeException){ throw(RuntimeException)t; }else{ thrownewInternalError(t.toString(),t); } }catch(NoSuchMethodExceptione){ thrownewInternalError(e.toString(),e); } } 进到getProxyClass0方法 [java] view plain copy privatestaticClass<?>getProxyClass0(ClassLoaderloader, Class<?>...interfaces){ if(interfaces.length>65535){ thrownewIllegalArgumentException("interfacelimitexceeded"); } //Iftheproxyclassdefinedbythegivenloaderimplementing //thegiveninterfacesexists,thiswillsimplyreturnthecachedcopy; //otherwise,itwillcreatetheproxyclassviatheProxyClassFactory returnproxyClassCache.get(loader,interfaces); } proxyClassCache为WeakCache的实例化对象,在Proxy类中定义,表示代理类的缓存。定义如下: [java] view plain copy privatestaticfinalWeakCache<ClassLoader,Class<?>[],Class<?>>proxyClassCache=newWeakCache<>(newKeyFactory(),newProxyClassFactory()); KeyFactory、ProxyClassFactory是WeakCache的内部静态类。实现了BiFunction接口。WeakCache实例化时作为构造函数参数传入,继承关系如下: 主要的代码如下: [java] view plain copy publicinterfaceBiFunction<T,U,R>{ Rapply(Tt,Uu); } [java] view plain copy privatestaticfinalclassProxyClassFactory implementsBiFunction<ClassLoader,Class<?>[],Class<?>> { //prefixforallproxyclassnames代理类的前缀 privatestaticfinalStringproxyClassNamePrefix="$Proxy"; //nextnumbertouseforgenerationofuniqueproxyclassnames //生成唯一的代理类名称 privatestaticfinalAtomicLongnextUniqueNumber=newAtomicLong(); @Override publicClass<?>apply(ClassLoaderloader,Class<?>[]interfaces){ Map<Class<?>,Boolean>interfaceSet=newIdentityHashMap<>(interfaces.length); for(Class<?>intf:interfaces){ /* *Verifythattheclassloaderresolvesthenameofthis *interfacetothesameClassobject. */ //确保接口的类对象与类加载器加载的类对象相同,且由同一个加载器加载。《深入理解java虚拟机》提到,类加载器虽然只用于实现类的加载动作,但在java程序起的作用远不止于类的加载。对于任何一个类,都需要由加载它的的类加载器和这个类本身一同确立其在java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间,通俗点,比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。interfaceClass=Class.forName(intf.getName(),false,loader);验证类是否相等,实现原理如上所述。 Class<?>interfaceClass=null; try{ interfaceClass=Class.forName(intf.getName(),false,loader); }catch(ClassNotFoundExceptione){ } if(interfaceClass!=intf){ thrownewIllegalArgumentException( intf+"isnotvisiblefromclassloader"); } /* *VerifythattheClassobjectactuallyrepresentsan *interface.验证类对象表示的是接口 */ if(!interfaceClass.isInterface()){ thrownewIllegalArgumentException( interfaceClass.getName()+"isnotaninterface"); } /* *Verifythatthisinterfaceisnotaduplicate.验证接口未重复 */ if(interfaceSet.put(interfaceClass,Boolean.TRUE)!=null){ thrownewIllegalArgumentException( "repeatedinterface:"+interfaceClass.getName()); } } StringproxyPkg=null;//packagetodefineproxyclassin定义待生成的代理类所在包名 intaccessFlags=Modifier.PUBLIC|Modifier.FINAL; /* *Recordthepackageofanon-publicproxyinterfacesothatthe *proxyclasswillbedefinedinthesamepackage.Verifythat *allnon-publicproxyinterfacesareinthesamepackage.非public修饰的代理接口,需要定义在相同的包中,如果非public修饰的接口不在相同包,会因访问权限的限制而无法访问。intf.getModifiers()返回的是一个整数,用不同的位开关表示接口中public/final修饰符的使用情况。 */ for(Class<?>intf:interfaces){ intflags=intf.getModifiers(); if(!Modifier.isPublic(flags)){ accessFlags=Modifier.FINAL; Stringname=intf.getName(); intn=name.lastIndexOf('.'); Stringpkg=((n==-1)?"":name.substring(0,n+1)); if(proxyPkg==null){ proxyPkg=pkg;//代理对象的包名,如com.aop }elseif(!pkg.equals(proxyPkg)){ thrownewIllegalArgumentException( "non-publicinterfacesfromdifferentpackages"); } } } if(proxyPkg==null){ //ifnonon-publicproxyinterfaces,usecom.sun.proxypackage proxyPkg=ReflectUtil.PROXY_PACKAGE+"."; } /* *Chooseanamefortheproxyclasstogenerate. */ longnum=nextUniqueNumber.getAndIncrement(); StringproxyName=proxyPkg+proxyClassNamePrefix+num; /* *Generatethespecifiedproxyclass. *生成目标对象的代理类的字节码,并保存到硬盘中。 */ byte[]proxyClassFile=ProxyGenerator.generateProxyClass( proxyName,interfaces,accessFlags); try{ //返回代理类对象,将字节码加载到内存中 returndefineClass0(loader,proxyName, proxyClassFile,0,proxyClassFile.length); }catch(ClassFormatErrore){ /* *AClassFormatErrorheremeansthat(barringbugsinthe *proxyclassgenerationcode)therewassomeother *invalidaspectoftheargumentssuppliedtotheproxy *classcreation(suchasvirtualmachinelimitations *exceeded). */ thrownewIllegalArgumentException(e.toString()); } } } [java] view plain copy /** *映射接口数组到最后键的函数 *接口数组映射到最优键对象上(如实例化Key1时,接口作为Key1的构造函数参数,保存到Key1中),同时,Key1/Key2/KeyX继承了WeakReference弱引用类类,因此接口所在的类对象被弱引用 */ privatestaticfinalclassKeyFactory implementsBiFunction<ClassLoader,Class<?>[],Object> { @Override publicObjectapply(ClassLoaderclassLoader,Class<?>[]interfaces){ switch(interfaces.length){ case1:returnnewKey1(interfaces[0]);//themostfrequent case2:returnnewKey2(interfaces[0],interfaces[1]); case0:returnkey0; default:returnnewKeyX(interfaces); } } } WeakCache类中定义的map变量如下: [java] view plain copy privatefinalConcurrentMap<Object,ConcurrentMap<Object,Supplier<V>>>map=newConcurrentHashMap<>(); 双层map映射,第一层key为ClassLoader,第二层key为接口的弱引用对象,value为代理类的Class对象。怎么看出是弱引用的呢,value中存放的值是一个类,该类继承了弱引用类WeakReference,我们的代理类在对象实例化时,通过构造函数传入。为对象添加引用,可看WeakReference用法。第一层是类加载器,第二层才是类对象,为什么不用一层,代理类对象作为键呢,这里就涉及到类加载器的使用。记住一点,类的唯一性是由类加载器和类本身共同决定的。 对Supplier接口,Factory类进行说明,接下来的代码中会用到。 调用proxyClassCache.get(loader, interfaces),进入WeakCache的get方法。代码如下: [java] view plain copy publicVget(Kkey,Pparameter){ Objects.requireNonNull(parameter); expungeStaleEntries(); //refQueue的类型为ReferenceQueue,存放对象的弱引用,CacheKey继承了WeakReference,因此key(ClassLoader)所在对象被弱引用,对象的应用存放在队列refQueue中。 ObjectcacheKey=CacheKey.valueOf(key,refQueue); //lazilyinstallthe2ndlevelvaluesMapfortheparticularcacheKey //生成第一层key,从缓存中获取valuesMap,如果为空,则新建ConcurrentHashMap实例,把当前加载器和ConcurrentHashMap实例放到缓存中。 ConcurrentMap<Object,Supplier<V>>valuesMap=map.get(cacheKey); if(valuesMap==null){ ConcurrentMap<Object,Supplier<V>>oldValuesMap =map.putIfAbsent(cacheKey, valuesMap=newConcurrentHashMap<>()); if(oldValuesMap!=null){ valuesMap=oldValuesMap; } } //createsubKeyandretrievethepossibleSupplier<V>storedbythat //subKeyfromvaluesMap //subKeyFactory是Proxy类的内部静态类KeyFactory的实例对象 //valueFactory是Proxy类的内容静态类ProxyClassFactory的实例对象 //subKeyFactory.apply(key,parameter)返回接口的弱引用对象。subKey,即接口的弱引用对象作为第二层映射的键 ObjectsubKey=Objects.requireNonNull(subKeyFactory.apply(key,parameter)); Supplier<V>supplier=valuesMap.get(subKey); Factoryfactory=null; while(true){ if(supplier!=null){ //suppliermightbeaFactoryoraCacheValue<V>instance,supplier不为空,缓存中存在代理类 Vvalue=supplier.get(); if(value!=null){//value不为空,即代理类存在,将代理类返回。 returnvalue; } } //elsenosupplierincache //orasupplierthatreturnednull(couldbeaclearedCacheValue //oraFactorythatwasn'tsuccessfulininstallingtheCacheValue) //lazilyconstructaFactory if(factory==null){ factory=newFactory(key,parameter,subKey,valuesMap); } if(supplier==null){ supplier=valuesMap.putIfAbsent(subKey,factory); if(supplier==null){ //successfullyinstalledFactory supplier=factory; } //elseretrywithwinningsupplier }else{ if(valuesMap.replace(subKey,supplier,factory)){ //successfullyreplaced //clearedCacheEntry/unsuccessfulFactory //withourFactory supplier=factory; }else{ //retrywithcurrentsupplier supplier=valuesMap.get(subKey); } } } } 上面执行流程中,主要的两个步骤为: Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); subKey为接口对象的弱引用。 进入Proxy类的内部静态类KeyFactory,查看apply方法(本文前面提到的KeyFactory类)。 V value = supplier.get(); value为缓存中的代理类对象,Supplier是个接口,实现代码是在接口的实现类Factory中, 进入WeakCache类的内部类Factory,查看get方法。如下: Supplier是接口 [java] view plain copy publicinterfaceSupplier<T>{ /** *Getsaresult. * *@returnaresult */ Tget(); } } Factory是WeakCache的内部类, Factory实现了接口Supplier [java] view plain copy privatefinalclassFactoryimplementsSupplier<V>{ privatefinalKkey; privatefinalPparameter; privatefinalObjectsubKey; privatefinalConcurrentMap<Object,Supplier<V>>valuesMap; Factory(Kkey,Pparameter,ObjectsubKey, ConcurrentMap<Object,Supplier<V>>valuesMap){ this.key=key; this.parameter=parameter; this.subKey=subKey; this.valuesMap=valuesMap; } @Override publicsynchronizedVget(){//serializeaccess //re-check Supplier<V>supplier=valuesMap.get(subKey); if(supplier!=this){ //somethingchangedwhilewewerewaiting: //mightbethatwewerereplacedbyaCacheValue //orwereremovedbecauseoffailure-> //returnnulltosignalWeakCache.get()toretry //theloop returnnull; } //elsestillus(supplier==this) //createnewvalue Vvalue=null; try{ //valueFactory.apply(key,parameter)返回代理类对象,即Class对象。 //如果value不为空,说明缓存中已经存在代理类,可以直接返回。 //通过前面的判断,已经能确定缓存中存在代理类,否则,程序是走不到这里来的。 value=Objects.requireNonNull(valueFactory.apply(key,parameter)); }finally{ if(value==null){//removeusonfailure valuesMap.remove(subKey,this); } } //theonlypathtoreachhereiswithnon-nullvalue assertvalue!=null; //wrapvaluewithCacheValue(WeakReference) CacheValue<V>cacheValue=newCacheValue<>(value); //tryreplacinguswithCacheValue(thisshouldalwayssucceed) if(valuesMap.replace(subKey,this,cacheValue)){ //putalsoinreverseMap reverseMap.put(cacheValue,Boolean.TRUE); }else{ thrownewAssertionError("Shouldnotreachhere"); } //successfullyreplaceduswithnewCacheValue->returnthevalue //wrappedbyit returnvalue; } } 在Facotry类的get方法中,value是由valueFactory.apply(key, parameter)生成,返回代理类Class对象。具体的实现可在本文前面提到的ValueFactory中查看。 JDK动态代理实现原理 Java WeakReference的理解与使用

优秀的个人博客,低调大师

源码安装PHP7

1. 下载 下载地址http://www.php.net/downloads.php 点击下面红色框,下载“php-7.2.4.tar.bz2” 2. 解压 tar -xZf php-7.2.4.tar.bz2 3. 安装 cd php-7.2.4 ./configure --prefix=/data/server/php7 #配置安装路径 make # 编译 make install # 安装 4. 配置全局环境 如果运行php -v命令无效则进行这项配置 打开配置文件 vim ~/.bash_profile 编辑内容 # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=/server/php7/bin:$PATH export PATH export LANG="zh_CN.UTF-8" export LC_ALL="zh_CN.UTF-8" 更新环境变量 ··· source .bash_profile ···· 排除一些坑 提示没有权限,记着前面增加sudo 执行./configure命令无法通过请检查是否又gcc和autoconf环境 1)mac环境安装brew(软件安装工具),时间比较长耐心等待 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 2)安装gcc brew install gcc php环境找不到php.ini 1)在安装包目录下找到php.ini-devopment,将此文件拷贝到自己对应的安装目录,我自己这块是/data/server/php7/etc,然后更改名称mv php.ini-devopment php.ini 2)但发现php.ini配置无效,使用php -i | grep php.ini命令检查 发现图示是lib目录下,那则将php.ini文件移动到此目录下,如果想更改到/etc/环境下,就需要在./configure配置时设置 配置出现libxml2 not found.错误 解决办法: ubuntu/debian: apt-get install libxml2-dev centos/redhat: yum install libxml2-devel

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册