Skip to content

Instantly share code, notes, and snippets.

@steven-zou
Last active September 25, 2015 01:29
Show Gist options
  • Select an option

  • Save steven-zou/7ce14e01d2ad7cd07091 to your computer and use it in GitHub Desktop.

Select an option

Save steven-zou/7ce14e01d2ad7cd07091 to your computer and use it in GitHub Desktop.
Record all the interview questions I met before
引用计数法(Reference Counting):
1. 有引用,计数器+1, 引用失效,则计数器-1
2. 实现简单,判定效率高
3. 未在主流虚拟机中使用,因为解决不了循环引用的问题
可达性分析(Reachability Analysis):
1. 以GC root对象作为起点,向下搜索
2. 所走过的路径为引用链,
3. 当一个对象到GC root 没有任何的引用链的时候,则不可达,可以回收
4. 可作为GC root的对象
4.1 虚拟机栈中引用对象
4.2 方法区中的静态属性引用的对象
4.3 方法区中的常量引用对象
4.4 本地方法栈中的JNI引用对象
垃圾收集算法:
1. 标记-清除 (Mark-Sweep)
问题:效率不高,会产生大量不连续的内存碎片
2. 复制算法
等大的内存块,讲存活的对象复制到另外一块,再把已使用过的内存空间一次清理掉
现代商业虚拟机都依靠此技术来回收新生代
问题:如果对象存活率较高时,进行太多的复制,效率变低
3. 标记-整理算法
先标记,之后所有存活对象向一边移动,然后直接清理掉端边界以外的内存
现代商业虚拟机都采用的是分代收集算法
Java Thread <-> Work memory | |
| |
Java Thread <-> Work memory <-> | Save &Load | Major Memory
| |
Java Thread <-> Work memory | |
Memory operations:
lock -> major memory
unlock-> major memory
read -> major
load -> work memory
use -> work
assign -> work
store ->work
write -> major
http://www.360doc.com/content/12/0113/08/1073512_179088229.shtml
用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
Read Uncommitted(读取未提交内容)
在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未
提交的数据,也被称之为脏读(Dirty Read)。
Read Committed(读取提交内容)
这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔
离级别 也支持所谓的不可重复读(Nonrepeatable Read),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能
返回不同结果。
Repeatable Read(可重读)
这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:
幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时
,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。
Serializable(可串行化)
这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个
级别,可能导致大量的超时现象和锁竞争。
References: http://xm-king.iteye.com/blog/770721
OAuth 2.0 的基本流程
OAuth 2.0 相对于1.0的主要改进
TCP连接是面向连接的,所谓的面向连接就是,当计算机双向通信时必需先建立连接,然后才能进行数据的传输,最后还要拆除连接。而同在一个
网络层的UDP传输,是面向非连接的传输,也不是可靠的。
位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Client <------------> Server
->SYN=j
SYN=J+1&ACK=K<-
->ACK=K+1
实例:
IP 192.168.1.116.3337 > 192.168.1.123.7788: S 3626544836:3626544836
IP 192.168.1.123.7788 > 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837
IP 192.168.1.116.3337 > 192.168.1.123.7788: ack 1739326487,ack 1
第一次握手:192.168.1.116发送位码syn=1,随机产生seq number=3626544836的数据包到192.168.1.123,192.168.1.123由SYN=1知道
192.168.1.116要求建立联机;
第二次握手:192.168.1.123收到请求后要确认联机信息,向192.168.1.116发送ack number=3626544837,syn=1,ack=1,随机产生seq=1739326486
的包;
第三次握手:192.168.1.116收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,
192.168.1.116会再发送ack number=1739326487,ack=1,192.168.1.123收到后确认seq=seq+1,ack=1则连接建立成功。
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来
终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭
的一方将执行主动关闭,而另一方执行被动关闭。
Client <------------> Server
-->FIN
ACK<--
CLOSE<
FIN<--
-->ACK
>CLOSE
=======================
CLOSED: 这个没什么好说的了,表示初始状态。
  LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
  SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,
  很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。
  因此这种状态 时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
  SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,
  并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
  ESTABLISHED:这个容易理解了,表示连接已经建立了。
  FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别 是:
  FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。
  而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以
  FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
  FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,
  我暂时还有点数据需要传送给你,稍后再关闭连接。
  TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时
  带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
  CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先
  收到(或同时收到)对方的 ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收
  到了对方的FIN报文。什 么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么
  就出现了双方同时发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
  CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个
  ACK报文给对 方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么
  你也就可以 close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
  LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到
  CLOSED可用状态了。
======================
1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。
但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上
会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文
多数情况下都是分开发送的.
2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);
但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未
收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。
Weak references
A weak reference, simply put, is a reference that isn't strong enough to force an object to remain in memory. Weak references
allow you to leverage the garbage collector's ability to determine reachability for you, so you don't have to do it yourself.
You create a weak reference like this:
=====================================================
WeakReference weakWidget = new WeakReference(widget);
=====================================================
and then elsewhere in the code you can use weakWidget.get() to get the actual Widget object. Of course the weak reference
isn't strong enough to prevent garbage collection, so you may find (if there are no strong references to the widget)
that weakWidget.get() suddenly starts returning null.
Soft references
A soft reference is exactly like a weak reference, except that it is less eager to throw away the object to which it refers.
An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next
garbage collection cycle, but an object which is softly reachable will generally stick around for a while.
SoftReferences aren't required to behave any differently than WeakReferences, but in practice softly reachable objects are
generally retained as long as memory is in plentiful supply. This makes them an excellent foundation for a cache, such as the
image cache described above, since you can let the garbage collector worry about both how reachable the objects are
(a strongly reachable object will never be removed from the cache) and how badly it needs the memory they are consuming.
Soft Reference 虽然和 Weak Reference 很类似,但是用途却不同。 被 Soft Reference 指到的对象,即使没有任何 Direct Reference,
也不会被清除。一直要到 JVM 内存不足时且 没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。
如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。我觉得 Soft Reference
也适合拿来实作 pooling 的技巧。
Comments:
-----------
The Sun JRE does treat SoftReferences differently from WeakReferences. We attempt to hold on to object referenced by a
SoftReference if there isn't pressure on the available memory. One detail: the policy for the
"-client" and "-server" JRE's are different: the -client JRE tries to keep your footprint small by
preferring to clear SoftReferences rather than expand the heap, whereas the -server JRE tries to keep your performance
high by preferring to expand the heap (if possible) rather than clear SoftReferences. One size does not fit all.
Strong referebce(强引用): 默认的引用形式,只要引用存在,则永远不会被回收
Phantom Reference: 虚引用,完全不会对其生存时间构成影响,唯一的目的就是能在这个对象被收集器回收时收到一个系统通知
@steven-zou
Copy link
Copy Markdown
Author

volatile关键字:
volatile只保证变量的可见性
使用volatile关键字标示的变量也不一定是线程安全的,比如和非volatile的变量共同参与不变约束,比如运算结果依赖当前变量的值
即使编译出来的字节码只有一行,也不能代表这条字节码指令是原子的
volatile禁止指令优化重排序
多核CPU需要通过内存屏障指令来保证指令执行顺序

@steven-zou
Copy link
Copy Markdown
Author

Java内存模型允许虚拟机对64位数据类型(long&double)的读写操作划分为两次32位操作进行,即不保证load、store、read和write的原子性,这就是所谓的非原子性协定。

实际上虚拟机的实现中,一般保证了long和double的原子性操作

@steven-zou
Copy link
Copy Markdown
Author

符合Java happens-before 先行发生原则的操作序列,则不需要任何同步手段即可保证线程安全

@steven-zou
Copy link
Copy Markdown
Author

线程实现:

  1. 基于内核线程(使用内核线程高级接口 LWP, 轻量级进程),LWP与内核线程是1:1. 这种模式下,所有的线程操作都需要系统调用(需要再user mode和kernel mode之间切换)来完成,代价颇高,因而支持的数量有限。
  2. 基于用户线程实现。 直接在用户态中完成,不需要切换到内核态。和进程之间可以实现1:N的对应。缺点就是无法利用系统内核,实现复杂,代价大。
  3. 用户进程+LWP 混合实现N:M的对应关系. (P380)

@steven-zou
Copy link
Copy Markdown
Author

线程安全:
当多个线程访问一个对象的时候,如果不考虑这些线程在运行时环境下的调度和交替执行,也不需要额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象的线程就是安全的。
-- Brian Goetz

@steven-zou
Copy link
Copy Markdown
Author

synchronized明确指定了对象参数,那就是这个对象的reference;如果没有明确指定,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或者class对象来作为锁对象。

synchronized的锁为非公平锁,ReentrantLock默认也是非公平锁,可以通过带bool值的构造函数来要求使用公平锁。(公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁)

上述都是互斥同步,也即阻塞同步

避免阻塞,可以使用自旋(锁)

在编译阶段,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

@steven-zou
Copy link
Copy Markdown
Author

常见配置汇总

堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小
收集器设置
-XX:+UseSerialGC:设置串行收集器
-XX:+UseParallelGC:设置并行收集器
-XX:+UseParalledlOldGC:设置并行年老代收集器
-XX:+UseConcMarkSweepGC:设置并发收集器
垃圾回收统计信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
并行收集器设置
-XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
并发收集器设置
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
-XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

@steven-zou
Copy link
Copy Markdown
Author

新生代的划分:

SUN/Oracle 的HotSpot JVM 又把新生代进一步划分为3个区域:一个相对大点的区域,称为”伊甸园区(Eden)”;两个相对小点的区域称为”From 幸存区(survivor)”和”To 幸存区(survivor)”。按照规定,新对象会首先分配在 Eden 中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden 中的对象会被移动到survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。

基于大多数新生对象都会在GC中被收回的假设。新生代的GC 使用复制算法。在GC前To 幸存区(survivor)保持清空,对象保存在 Eden 和 From 幸存区(survivor)中,GC运行时,Eden中的幸存对象被复制到 To 幸存区(survivor)。针对 From 幸存区(survivor)中的幸存对象,会考虑对象年龄,如果年龄没达到阀值(tenuring threshold),对象会被复制到To 幸存区(survivor)。如果达到阀值对象被复制到老年代。复制阶段完成后,Eden 和From 幸存区中只保存死对象,可以视为清空。如果在复制过程中To 幸存区被填满了,剩余的对象会被复制到老年代中。最后 From 幸存区和 To幸存区会调换下名字,在下次GC时,To 幸存区会成为From 幸存区。

@steven-zou
Copy link
Copy Markdown
Author

PRODUCER-CONSUMER Problem:

public interface IStorage {
void produce();
void consume();
}


import java.util.ArrayList;
import java.util.List;

public class Storage implements IStorage{

private final int MAX_COUNT;
private final List<Good> l;

public Storage(int maxCount){
    MAX_COUNT = maxCount <=0?10:maxCount;
    l = new ArrayList<Good>(MAX_COUNT);
}

public void produce(){
    synchronized(l){
        while(l.size() == MAX_COUNT){
            System.out.println("Storage is full! Waiting for consuming...");
            try {
                l.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        l.add(new Good(l.size()));
        System.out.println("Current size[P]:"+l.size());
        l.notifyAll();
    }
}

public void consume(){
    synchronized(l){
        while(l.size() == 0){
            System.out.println("Storage is empty! Waiting for producing");
            try {
                l.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        l.remove(0);
        System.out.println("Current size[C]:"+l.size());
        l.notifyAll();
    }
}

}


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CStorage implements IStorage {

private final int MAX_COUNT;
private final List<Good> l;
private final Lock lock;
private final Condition signal;

public CStorage(int maxCount){
    MAX_COUNT = maxCount<=0?10:maxCount;
    l = new ArrayList<Good>(MAX_COUNT);
    lock = new ReentrantLock(true);
    signal = lock.newCondition();
}

@Override
public void produce() {
    lock.lock();
    while(l.size() == MAX_COUNT){
        System.out.println("Storage is full! Waiting for consuming...");
        try {
            signal.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    l.add(new Good(l.size()));
    System.out.println("Current size[P]:"+l.size());
    signal.signalAll();

    lock.unlock();
}

@Override
public void consume() {
    lock.lock();

    while(l.size() == 0 ){
        System.out.println("Storage is empty! Waiting for producing");
        try {
            signal.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    l.remove(0);
    System.out.println("Current size[C]:"+l.size());
    signal.signalAll();

    lock.unlock();
}

}


public class Producer implements Runnable {

private final IStorage store;

public Producer(IStorage store){
    this.store = store;
}

@Override
public void run() {
    while(true){
        store.produce();
    }
}

}


public class Consumer implements Runnable {

private final IStorage store;

public Consumer(IStorage store){
    this.store = store;
}
@Override
public void run() {
    while(true){
        store.consume();
    }
}

}


public class Main {

/**
 * @param args
 */
public static void main(String[] args) {
    //Storage store = new Storage(10);
    CStorage store = new CStorage(10);

    for(int i=0;i<5;i++){
        new Thread(new Producer(store),"P"+i).start();
    }

    for(int i=0;i<3;i++){
        new Thread(new Consumer(store),"C"+i).start();
    }
}

}

@steven-zou
Copy link
Copy Markdown
Author

PRODUCER-CONSUMER Problem part2:

private LinkedBlockingQueue list = new LinkedBlockingQueue(MAX_COUNT);

put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。

@steven-zou
Copy link
Copy Markdown
Author

排序算法分析:

Sort Methods | Average Time | Worst Time | Space
简单排序 O(n^2) O(n^2) O(1)
快速排序 O(nlogN) O(n^2) O(logN)
堆排序 O(nlogN) O(nlogN) O(1)
归并排序 O(nlogN) O(nlogN) O(n)
基数排序 O(d(n+rd)) O(d(n+rd)) O(rd)

@steven-zou
Copy link
Copy Markdown
Author

几个排序算法:

 public void StraightInsertionSort(int[] arr){
    for(int i=1;i<arr.length;i++){
        if(arr[i-1]>arr[i]){
            int temp = arr[i];

            int j=i-1;
            for(;j>=0&&temp<arr[j];j--){
                arr[j+1] = arr[j];
            }

            arr[j+1] = temp;
        }
    }
}

public void shellSort(int[] arr){
    double dk = arr.length;
    int temp = 0;

    while(true){
        dk = Math.ceil(dk/2);
        int d = (int)dk;

        for(int x=0;x<d;x++){
            for(int i=x+d;i<arr.length;i+=d){
                if(arr[i-d]>arr[i]){
                    temp = arr[i];
                    int j=i-d;
                    for(;j>=0&&temp<arr[j];j-=d){
                        arr[j+d] =arr[j];
                    }

                    arr[j+d] = temp;

                }
            }
        }

        if(d==1){
            break;
        }
    }
}

public void selectionSort(int[] arr){
    for(int i=0,len=arr.length;i<len;i++){
        int temp=arr[i], position=i;
        for(int j=i+1;j<len;j++){
            if(arr[j]<temp){
                temp=arr[j];
                position=j;
            }
        }

        arr[position] =arr[i];
        arr[i] = temp;
    }
}

public void quickSort(int[] arr){
    if(arr!=null&&arr.length>0){
        quickSort(arr,0,arr.length-1);
    }
}

private void quickSort(int[] arr, int low, int high){
    if(low<high){
        int pv = partition(arr,low,high);
        quickSort(arr,low,pv-1);
        quickSort(arr,pv+1,high);
    }
}

private int partition(int[] arr, int low, int high){
    int pv = arr[low];
    while(low<high){
        while(low<high&&arr[high]>=pv){
            high--;
        }
        arr[low]=arr[high];
        while(low<high&&arr[low]<=pv){
            low++;
        }
        arr[high]=arr[low];
    }
    arr[low]=pv;

    return low;
}

@steven-zou
Copy link
Copy Markdown
Author

ORM:

ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射.

Drawbacks:

  1. 自动化意味着映射和关联管理,代价是牺牲性能
  2. 面向对象的查询语言(X-QL)不能完全的屏蔽掉数据库层的设计,增加学习成本
  3. 对于复杂查询,ORM仍然力不从心

@steven-zou
Copy link
Copy Markdown
Author

OOP 四大特征 "抽象"、"封装"、"继承"、"多态"
OOP 四大原则:

  1. SRP 单一职责原则: 一个类,应该仅有一个引起它变化的原因
  2. 开放封闭原则: 软件实体(类、模块、函数等)应该可以扩展,但不可修改
  3. 依赖倒转原则: 抽象不应该依赖细节,细节应该依赖抽象(面向接口编程)
  4. 里氏代换原则: 子类型必须能够替换掉他们的父类型

@steven-zou
Copy link
Copy Markdown
Author

AOP技术的具体实现,无非也就是通过动态代理技术或者是在程序编译期间进行静态的"织入"方式。下面是这方面技术的几个基本术语: 1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point。 2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。 3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑。 4、aspect(方面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。 5、introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。有的 OP工具又将其称为mixin。

@steven-zou
Copy link
Copy Markdown
Author

JDK动态代理:

包含一个类和一个接口:
InvocationHandler接口:
public interface InvocationHandler {
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
}
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数

可以将InvocationHandler接口的子类想象成一个代理的最终操作类,替换掉ProxySubject。

Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException 参数说明: ClassLoader loader:类加载器 Class[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例

Ps:类加载器
在Proxy类中的newProxyInstance()方法中需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有一下三种类加载器;
Booststrap ClassLoader:此加载器采用C++编写,一般开发中是看不到的;
Extendsion ClassLoader:用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类;
AppClassLoader:(默认)加载classpath指定的类,是最常使用的是一种加载器。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment