0%

如果本文有错,希望在下面的留言区指正。

TCP/IP 五层模型如图所示:

面向连接的 TCP

TCP(Transmission Control Protocol,传输控制协议)是基于连接的协议,也就是说,在正式收发数据前,必须和对方建立可靠的连接。一个 TCP 连接必须要经过三次”对话”才能建立起来,其中的过程非常复杂,我们这里只做简单、形象的介绍,你只要做到能够理解这个过程即可。

三次握手

TCP 建立连接如下图所示:

四次挥手

先由客户端向服务器端发送一个FIN,请求关闭数据传输。

当服务器接收到客户端的FIN时,向客户端发送一个ACK,其中ack的值等于FIN+SEQ

然后服务器向客户端发送一个FIN,告诉客户端应用程序关闭。

当客户端收到服务器端的FIN是,回复一个ACK给服务器端。其中ack的值等于FIN+SEQ

面向非连接 UDP

UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是面向非连接的协议,它不与对方建立连接,而是直接就把数据包发送过去。

总结

  • TCP 面向连接;UDP 是无连接,即发送数据前不需要建立连接
  • TCP 提供可靠的服务,即通过 TCP 连接传送的数据,无差错,不丢失,不重复,且有序到达;UDP 尽最大努力交付,但不保证可靠交付。
  • UDP 具有较好的实时性,工作效率比 TCP 高,适用于高速传输和实时性较高的通信或者广播通信。
  • 每一条 TCP 连接只能点到点的;UDP 支持一对一、一对多、多对一和多对多通信。
  • TCO 对系统资源需求比较多;UDP 对系统资源需求比较少。

参考

声明:如果本文有错误,希望指出。

虽然学习和使用 Java 好多年了,但是对于多态的整体概念还是有点模糊。今天在看之前写的关于反射博文时,突然想到了 Java 的三大特性——多态。其他两个分别是封装、继承。只是想起多态的继承、重写,对于别的有点模糊了,于是就像自己整理下。

多态

多态指的是对象的多种形态。继承是多态的实现基础。当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。

多态的分类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class PolymorphicCase {

public static void main(String[] args) {
show(new Cat());
show(new Dog());

//向上转型
Animal animal = new Cat();
animal.eat();//调用的是Cat的 eat
//当使用多态方式调用方法时,首先检查父类中是否有该方法,
// 如果没有,则编译错误;如果有,再去调用子类的同名方法
animal.work();//编译报错
//向下转型
Cat cat = (Cat) animal;
cat.work();//调用的是Cat的work
}

public static void show(Animal animal) {
animal.eat();
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.work();
}
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.work();
}
}
}

abstract class Animal {
abstract void eat();
}

class Cat extends Animal {

@Override
void eat() {
System.out.println("猫吃鱼....");
}

public void work() {
System.out.println("猫抓老鼠....");
}
}

class Dog extends Animal {

@Override
void eat() {
System.out.println("狗吃骨头....");
}

public void work() {
System.out.println("look door....");
}
}

向上转型

Animal animal = new Cat(); 对于这个对象的创建,就是体现了向上转型。

子类引用的对象转换为父类类型成为向上转型。通俗的说就是将子类对象转化为父类对象,此处对象可以是接口。

向下转型

Cat cat = (Cat) animal; 这里就是向下转型。与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。

多态的实现

  • 重写
  • 接口
  • 抽象类和抽象方法

总结

多态的优点

  • 消除类型的耦合关系
  • 可替换性
  • 可扩充性
  • 接口性
  • 灵活性
  • 简化性

多态存在的三个必要条件

  • 继承
  • 重写
  • 父类引用子类对象

参考

最近在看阿里出的一本书 —— 《码出高效 Java 开发手册》,这本书不是之前那本关于 Java 开发注意规范的手册,这本书内容还是不错的,干货满满。

在看书的时候,就看到了关于上面讲的知识点,于是想到之前在看 Java 一些源码时候,也看到一些关于这个泛型的写法,当时还没注意,比如下面关于 ThreadLocal 源码是看到的。

这样以后看 Java 的源码就能看懂这些泛型的写法了。

是 Java 泛型中的 “通配符“和”边界”的概念。 - : 是指“上界通配符”。 A super B 表示 A 是 B 的父类或者祖先,在 B 上面。 - :是指“下界通配符”。A extends B 表示 A 是 B 的子类或者孙类,在 B 的下面。 两种语法,但是两者的区别非常微妙。简单来说,是 Get first,适用于消费集合元素为主的场景;是 Put First,适用于生产集合元素为主的场景。 # 可以赋值给任何 T 及 T 子类的集合,上界为T,取出来的类型带有泛型限制,向上强制转型为T。null可以表示任何类型,所以除nul外,任何元素都不得添加进集合内。 # 可以赋值给任何 T 及 T 的父类集合,下界为T。在生活中,投票选举类似于 的操作。选举代表时,你只能往里投选票,取数据时,根本不知道是谁的票,相当于泛型丢失。有人说,这只是一种生活场景,在系统设计中。很难有这样的情形。再举例说明一下,我们在填写对主管的年度评价时,提交后若想再次访问之前的链接修改评价,就会被告之:“您已经完成对主管的年度反馈,谢谢参与。" extends的场景是put功能受限,而 super的场景是get功能受限。 # 集合和泛型 下面的代码和说明出自《码出高效,Java开发手册》
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) {

//第一阶段:泛型出现之前的集合定义方式
List a1 = new ArrayList();
a1.add(new Object());
a1.add(new Integer(111));
a1.add(new String("Hello World"));

for (Object o : a1){
System.out.println(o);
}

//第二段:把a1 引用赋值给a2,注意a2与a1的区别是增加了泛型限制<Object>
List<Object> a2 = a1;
a2.add(new Object());
a2.add(new Integer(222));
a2.add(new String("hello a2a2"));

//第三段:把a1引用赋值给a3,注意a3与a1的区别是增加了泛型<Integer>
List<Integer> a3 = a1;
a3.add(new Integer(333));
//下方两行编译出错,不允许增加非 Integer类型进入集合
a3.add(new Object());
a3.add(new String("hello a3a3"));

//第四段:把a1引用赋值给a4,a1与a4的区别是增加了通配符
List<?> a4 = a1;
a1.remove(0);
a4.clear();
//编译出错。不允许增加任何元素
a4.add(new Object());

}
第一段说明:在定义List之后,毫不犹豫地往集合里装入三种不同的对象:Object、 Integer 和 String,遍历没有问题,但是贸然以为里边的元素都是 Integer,使用强制转化,则抛出 ClassCastException异常。 第二段说明:把a赋值给a2,a2是 List类型的,也可以再往里装入三种不同的对象。很多程序员认为List和List是完全相同的,至少从目前这两段来看是这样的。 第三段说明:由于泛型在JDK5之后才出现,考虑到向前兼容,因此历史代码有时需要赋值给新泛型代码,从编译器角度是允许的。这种代码似乎有点反人类,在实际故障案例中经常出现。 第四段说明:问号在正则表达式中可以匹配任何字符,List

称为通配符集合。它可以接受任何类型的集合引用赋值,不能添加任何元素,但可以 remove 和 clear,并非 immutable集合。List<?>一般作为参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。

参考

  • 《码出高效:Java开发手册》

欢迎指正。

队列同步器(AbstractQueuedSynchronizer),是用来构建锁或者其他同步组件的基础框架,它使用了一个 int 成员变量表示同步状态, 通用过内置的 FIFO 队列来,可以用于构建锁或者其他同步装置,完成资源获取线程的排队工作。

同步器的主要使用方法是继承、子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态镜像更改,这时就需要使用同步器提供的方法(getState()、setState(in newSate) 和 compareAndSetState(int expect, int update))来进行操作。

AQS 是一个抽象类,内置自旋锁实现的同步队列,封装入队和出队的操作,提供独占、共享、中断等特性。

基于AQS构建的同步器:

  • ReentrantLock
  • Semaphore
  • CountDownLatch
  • ReentrantReadWriteLock
  • SynchronusQueue
  • FutureTask

AQS 核心知识

队列同步器的接口与实例

重写同步器指定的方法是,需要使用同步器提供的3个方法来访问或者修稿同步状态:

  • getState():获取当前同步状态
  • setState(in newSate):设置当前同步状态
  • ompareAndSetState(int expect, int update):使用 CAS 设置当前状态,该方法能够保证状态设置的原子性。

CLH 同步队列

CLH 同步队列(Craig、Landin、Hagersten)是一个FIFO双向对象,AQS 依赖它来完成同步状态的管理,当线程如果获取同步状态失败时,AQS 则会将当前线程等待状态等信息构造成一个节点(Node)并将其加入到 CLH 同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试同步状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
static final class Node {
/** 共享*/
static final Node SHARED = new Node();
/** 独占*/
static final Node EXCLUSIVE = null;
/**因为超时或者中断,节点会被设置为取消状态,被取消的节点时不会参与到竞争中的,他会一直保持取消状态不会转变为其他状态 */
static final int CANCELLED = 1;
/** 后继节点的线程处于等待状态,而当前节点的线程如果释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行 */
static final int SIGNAL = -1;
/** 节点在等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()后,改节点将会从等待队列中转移到同步队列中,加入到同步状态的获取中*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
* 表示下一次共享式同步状态获取将会无条件地传播下去
*/
static final int PROPAGATE = -3;
/** 等待状态 */
volatile int waitStatus;
/** 前驱节点*/
volatile Node prev;
/**后续节点*/
volatile Node next;
/** 获取同步状态的线程*/
volatile Thread thread;

Node nextWaiter;

/**Returns true if node is waiting in shared mode.*/
final boolean isShared() {
return nextWaiter == SHARED;
}

final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}

Node() { // Used to establish initial head or SHARED marker
}

Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}

Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}

AQS 实现细节

线程首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到FIFO队列中。 接着会不断的循环尝试获取锁,条件是当前节点为head的直接后继才会尝试。如果失败就会阻塞自己直到自己被唤醒。而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。

参考

如果有不正确的,欢迎指正。

String,StringBuilder,StringBuffer源码分析
上面这篇是之前写的关于 String 的一些介绍,有兴趣可以看下。

今天重新翻看 《Effective Java》这本书的时候,看到第五条:避免创建不必要的对象。文中是以 String s = new String(“stringette”); 拿来举例的。看到这里,就想起以前看一些面经中看到的一个问题,对于上面的的语句,在运行时涉及几个String实例?

对于上面的答案: 两个,一个是字符串 “abc” 锁在常量池中的实例,另一个是通过 new String(String) 创建并初始化、内容与 “abc” 相同的实例。

但是对下面的代码:

1
2
3
4
5
6
7
8
public class StringDemo {

public static void main(String[] args) {
String s1 = "abc";
String s = new String("abc");
System.out.println(s == s1);
}
}

字节码 ldc:将常量值从常量池中取出压入栈中。

通过查看上面代码的字节码的第0行和第7行可以看出,该字符串变量的值已经确定了,并没有重新创建一个变量,而是从缓冲区中取出,同时让该变量指向该字符串值。

参考

Java 提供了很多同步工具,最近看Java锁的时候,看到了 CountDownLatch 和 Semaphore,同时了解到还有 CyclicBarrier,就想对它们做一个一定的了解。

CountDownLatch

CountDownLatch 是在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。使用一个计数器进行实践,计数器初始值为线程的数量。每当一个线程完成后,计数器的值就会减一。当计数器的值为0时,便是所有线程都完成了任务,然后等待的线程就可以恢复执行任务了。

用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

初始化

1
CountDownLatch countDownLatch = new CountDownLatch(1);

CountDownLatch 是一次性的,计数值在构建的时候就已经初始化完成了。

await

提供两种方法是当前线程处于等待状态

1
2
3
4
5
6
7
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

countDown

当调用 countDown() 方法时,CountDownLatch 计数器就会减去一,而当计数器为0的时候,等待的线程就会被唤醒并执行线程。

CyclicBarrier

CyclicBarrier 是一个同步工具,允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障(Barrier)。

初始化

1
2
3
4
5
6
7
8
9
10
//parties表示屏障拦截的线程数量,当屏障撤销时,先执行barrierAction,然后在释放所有线程
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}

await()

1
2
3
4
5
6
7
8
9
10
11
12
13
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}

reset()

将屏障重置为其初始状态。如果所有参与者目前都在屏障处等待,则它们将返回,同时抛出一个BrokenBarrierException。

Semaphore

Semaphore 是Java中提供的信号量,它是一个计数信号量,必须由获取线程释放。

初始化

1
2
3
4
5
6
7
8
//默认构建非公平锁  
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

获取许可:acquire()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}

释放许可:release()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}

总结

  • CountDownLatch:允许一个或多个线程等待,直到在某些线程中执行的一组操作完成
  • CyclicBarrier:一个辅助性的同步结构,允许多个线程等待到达某个屏障
  • Semaphore:Java 版本的信号量实现

声明:如果本文有错误,希望指出。

在之前的一篇博文中简单介绍了 Java 中的一些锁:Java 中各种锁
最近在极客时间上买了杨晓峰的《Java核心技术36讲》,今天看到关于标题的东西,于是想记录下自己的学习。
Synchronized 和 ReentrantLock 这两个都是可重入锁,指的是同一线程在外层函数获取锁之后,内层函数仍然可以获取该锁,且不受影响。

Synchronized 可重入测试源码

Synchronized

1
2
3
4
5
6
7
8
9
10
11
12
public class SynchronizedTest {

public synchronized void doSth() {
System.out.println("doSth");
}

public void method() {
synchronized (SynchronizedTest.class) {
System.out.println("method");
}
}
}

使用javac SynchronizedTest.javajavap -c -s -v -l SynchronizedTest 命令查看编译后的Test.class的字节码,可以很容易看出在代码块上和方法上是有区别的。代码块上使用 monitorenter/monitorexit 指令来实现的;方法上是使用 ACC_SYNCHRONIZED。详情看下面的图片,是编译后的字节码:

在 Java6 之前,Monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要用户状态到内核状态的切换,所有同步操作是一个重量级操作。

现在的JVM提供三种不同的 Monitor 实现方式:

  • 偏向锁
  • 轻量级锁
  • 重量级锁

所谓锁的升级降级,就是JVM优化 synchronized 运行的机制,当JVM 检测到不同的竞争状态是,会自动切换到适合的锁的实现。

当没有竞争出现时,默认会使用偏斜锁。JVM 会利用 CAS 操作,在对象头上的 Mark Word 部分设置线程ID,以表示这个对象偏向于当前线程,所以并不涉及到真正的互斥锁。这样做的假设是基于在很多应用场景中,大部分对象声明周期最大会被一个线程锁定,使用偏向锁可以降低无竞争开销。

以Hotspot虚拟机为例,Hotspot的对象头主要包括两部分数据:Mark Word(标记字段)、Klass Pointer(类型指针)。

Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。这些信息都是与对象自身定义无关的数据,所以Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。

Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

无锁

没有对资源进行锁定,所有线程都能访问和修改,但同时只有一个线程修改成功。

偏向锁

在锁竞争不强烈的情况下,通常一个线程会多次获取同一个锁,为了减少获取锁的代价引入了偏向锁,会在 Java 对象头中记录锁的 ThreadID

  • 当线程发现对相同的ThreadID存在是,判断于当前线程是否同一个线程;
  • 如果相同则不需要再次加锁、解锁;
  • 如果不是,则判断ThreadID是否存活。如果不存活,设置为无锁状态,其他线程竞争设置偏向锁;如果存活,查找ThreadID堆栈信息判断是否需要继续持有锁。如果需要持有则升级ThreadID线程的锁为轻量级锁,不需要持有则撤销锁,设置为无锁状态等待其他线程竞争。

因为偏向锁的撤销操作还是比较重的,导致进入安全点,因此在竞争比较激烈时,会影响性能,可以使用-XX:-UseBiasedLocking=false 禁用偏向锁。

轻量级锁

当偏向锁升级为轻量级锁时,其他线程尝试通过 CAS 方法设置对象头来获取锁。

  • 先在当前线程中的栈帧中设置 Lock Record,用来存储当前对象头中的 Mark word 的拷贝;
    复制 Mark word 的内容到 Lock Record,并尝试使用 CAS 将 Mark word的指针指向Lock Record
  • 如果替换成功,则获取轻量级锁;替换不成功,则自旋重试一定次数
  • 自旋一定次数或者有新的线程来竞争锁时,轻量级锁升级为重量级锁。

重量级锁

自旋是消耗CPU的,因此在自旋一段时间,或者一个线程在自旋的时候,又有新的线程来竞争,则升级为重量级锁。

重量级锁,通过monitor实现,底层实现是依赖操作系统的互斥锁实现。

需要从用户态切换到内核态,成本比较高。

上面就是所谓的锁的升级,而对于锁的降级,当JVM 进入安全点的时候,会检查是否有闲置的 Monitor,然后试图进行降级。

锁消除和锁粗化

锁消除

我们知道Vector的方法是由synchronized修饰的,它是线程安全的,但是我们可能仅在线程内使用,作为局部变量使用。不同线程调用时,都会创建不同的 Vector,因此,执行 add()方法的时候,使用同步操作,就是白白浪费系统资源。这时,我们可以通过编译器将其优化,将锁消除。-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks,前提是Java必须运行在server模式下,同事必须开启逃逸分析。

1
2
3
4
5
6
7
public static void main(String [] args) {
Vector<String> vector = new Vector<>();
for (int i=0; i<10; i++) {
vector.add(i+"");
}
System.out.println(vector);
}

锁粗化

在一些情况下,我们希望多次锁的请求能够合并成一个请求,以降低最短时间内大量锁的请求、同步、释放带来的性能消耗。

比如以下情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void test() {
List<String> list = new ArrayList<>();
for (int i=0; i<10; i++) {
synchronized (Demo.class) {
list.add(i + "");
}
}
System.out.println(list);
}
// 代码经过锁初粗化后,将变成下面这种情况
public static void test() {
List<String> list = new ArrayList<>();
synchronized (Demo.class) {
for (int i=0; i<10; i++) {
list.add(i + "");
}
}
System.out.println(list);
}

ReentrantLock

ReentrantLock 默认非公平但可实现公平的(构造器传true),悲观,独享,互斥,可重入,重量级锁。ReentrantLock 就是一个普通的类,它是基于 AQS(AbstractQueuedSynchronizer)来实现的。

下面几个概念:

  • 可中断锁:可中断锁是指线程尝试获取锁的过程中,是否可以响应中断。Synchronized 是不可中断锁,而 ReentrantLock 提供中断功能。

构造方法

1
2
3
4
5
6
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}

ReentrantLock 提供公平锁和非公平锁的构造方法,默认构造方法是非公平锁。

几种获取锁的方法

获取 ReentrantLock 的几种方式:

  • lock(): 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
  • tryLock():如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false
  • tryLock(long timeout,TimeUnit unit):如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
  • lockInterruptibly():如果获取了锁定立即返回,如果没有获取锁,当前线程处于休眠状态,直到获取锁定,或者当前线程被别的线程中断

如果有误,欢迎批评指正。友情提示,本文有点长。
SpringBoot 版本 2.0.5.RELEASE。

在学习 SpringBoot 时,会在启动项里面看到在类名上面有一个注解 @@SpringBootApplication。前几天在一个公众号中看到关于一道面试题,题目类似于:知道 @SpringBootApplication 的原理吗?

突然发现自己对于 SpringBoot 还是处在‘知其然而不知所以然’的状态。于是就去官网和网上查看一些资料,于是有了这篇文章。

SpringBoot 启动项

1
2
3
4
5
6
7
@SpringBootApplication
public class ApplicationStartup {

public static void main(String[] args) {
SpringApplication.run(ApplicationStartup.class, args);
}
}

@SpringBootApplication

看下 @SpringBootApplication 注解的详情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
//...
}

从注解的源码中可以看到它被 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 等注解修饰。

@SpringBootConfiguration

@SpringBootConfiguration 是 SpringBoot 项目的配置注解,这也是一个组合注解。

通过源码上的注解,可以看出 @SpringBootConfiguration 主要是 @Configuration。
@Configuration 是一个类级注释,指示对象是一个bean定义的源。@Configuration 类通过 @bean 注解的公共方法声明 bean。
@Bean 注释是用来表示一个方法实例化,配置和初始化是由 Spring IoC 容器管理的一个新的对象。

@EnableAutoConfiguration

启用 Spring 应用程序上下文的自动配置,试图猜测和配置您可能需要的bean。自动配置类通常采用基于你的 classpath 和已经定义的 beans 对象进行应用。
启用自动配置,该注解会使 SpringBoot 根据项目中依赖的jar包自动配置项目的配置项。

@ComponentScan

组件扫描,为 @SpringBootConfiguration 自动扫描类配置组件

SpringBoot 启动流程

上面图片来源,不过其中的流程是以前版本的,但是原理差不多。

Spring Boot 流程分析:

下面这个是 SpringBoot 的启动项:

通过 run() 方法,我们可以看出:

通过上面的代码,分别分析下SpringApplication()run()

SpringApplication()

通过一些类的调用,最后到下面这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
   /**
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//设置mainApplicationClass为AppBootstrap
this.mainApplicationClass = deduceMainApplicationClass();
}

从上面的这个 SpringApplication() 方法中可以看出 SpringBoot 在初始化的时候,需要加载的资源。this.webApplicationType = deduceWebApplicationType(); 通过下面的 deduceWebApplicationType() 方法判断类型:

  • NONE:应用程序不应作为Web应用程序运行,也不应启动嵌入式Web服务器。
  • SERVLET:应用程序应作为基于servlet的Web应用程序运行,并应启动嵌入式servlet Web服务器。
  • REACTIVE:应用程序应作为响应式Web应用程序运行,并应启动嵌入式响应式Web服务器。

ApplicationContextInitializer

通过打断点,可以看到setInitializer()被加载的内容:

ApplicationListener

通过断点看到setListeners()方法中被加载的监听事件:

setInitializer()setListeners() 这两个方法都使用了 getSpringFactoriesInstances 方法。代码中使用了 SpringFactoriesLoader.loadFactoryNames(type, classLoader));,这个根据 type 和 classLoader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();

while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();

while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue()));
result.addAll((String)entry.getKey(), factoryClassNames);
}
}

cache.put(classLoader, result);
return result;
} catch (IOException var9) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9);
}
}
}

spring.factories文件的作用也就间接的实现了自动化配置。我们可以在项目下创建自己的spring.factories文件。从 spring boot启动流程中加入我们自己需要的东西。

run()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//context 上下文
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//统一管理监听listeners,在事件发生是被调用
SpringApplicationRunListeners listeners = getRunListeners(args);
//如果有监听器监听启动事件,则执行对应的动作
listeners.starting();
try {
//从命令行中读取参数作为propertySource,放入到这里,会加入到env中
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//创建env,加载配置文件,系统的属性,profile文件,application.yml, 初始化日志系统
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建 context
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}

try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}

获取监听事件

SpringApplicationRunListeners listeners = getRunListeners(args);首先看下图的SpringApplicationRunListeners类。其中包含一个 SpringApplicationRunListener 集合,可以看那个的那个 SpringApplicationRunListener 接口代码

SpringApplicationRunListeners

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface SpringApplicationRunListener {

void starting();

void environmentPrepared(ConfigurableEnvironment environment);

void contextPrepared(ConfigurableApplicationContext context);

void contextLoaded(ConfigurableApplicationContext context);

void started(ConfigurableApplicationContext context);

void running(ConfigurableApplicationContext context);

void failed(ConfigurableApplicationContext context, Throwable exception);
}

从上面可以看出在run()方法中构建了一个SpringApplicationRunListeners,其中包含多个 SpringApplicationRunListener,用来监听应用启动过程中的start、environmentPrepared、contextPrepared、contextLoaded 等事件。

创建和准备环境:environment

1
2
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, pplicationArguments);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//创建env,如果是web环境,则创建StandardServletEnvironment,如果不是StandardEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置env
configureEnvironment(environment, applicationArguments.getSourceArgs());
//广播environment事件
listeners.environmentPrepared(environment);
//绑定到SpringApplication
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}

创建应用上下文

1
context = createApplicationContext();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

context 初始化过程:

  • AbstractApplicationContext
    • 设置Log
    • 设置id和displayName
    • 初始化BeanFactoryPostProcessor的集合
    • active,closed初始化
    • 初始化ApplicationListener的集合
    • 设置resourcePatternResolver
  • GenericApplicationContext
    • customClassLoader = false
    • refreshed = false
    • beanFactory = new DefaultListableBeanFactory
  • AnnotationConfigEmbeddedWebApplicationContext
    • this.reader = new AnnotatedBeanDefinitionReader(this);注册了几个BeanDefinition,最重要的是CongiurationClassPostProcessor的bean definition
    • this.scanner = new ClassPathBeanDefinitionScanner(this);设置了需要识别的注解的类型

prepareContext()

  • 调用用户配置的initializer的方法
  • 设置context的Id
  • context注册 ConfigurationWarningsPostProcessor用于检查错误的配置
  • 向env中设置实际的端口
  • SharedMetadataReaderFactoryContextInitializer.CachingMetadataReaderFactoryPost- Processor
  • 设置ContextRefreshedEvent监听器,打印ConditionEvaluationReport日志
  • 打印启动日志
  • 注册固定的单例bean
  • 加载主类的bean definition
  • 向context中添加-
  • ConfigFileApplicationListener.PropertySourceOrderingPostProcessor

总结

SpringApplication()

调用里面的构造器,初始化一个 SpringApplication()。

  • 加载资源文件
  • 加载主要的bean
  • 判断是否web环境以及判断是那种web环境
  • 设置setInitializers
    • 调用 getSpringFactoriesInstances 加载 META-INF/spring.factories 里面的信息
  • 设置setListeners
    • 调用 getSpringFactoriesInstances 加载 META-INF/spring.factories 中配置的org.springframework.context.ApplicationListener
  • 设置mainApplicationClass:deduceMainApplicationClass()该方法通过获取当前方法调用栈,找到main函数的类

run()

  • 初始化StopWatch,调用其start方法开始计时.
  • 调用configureHeadlessProperty设置系统属性java.awt.headless,这里设置为true,表示运行在服务器端,在没有显示器和鼠标键盘的模式下工作,模拟输入输出设备功能
  • 调用SpringApplicationRunListeners#starting
  • 创建一个DefaultApplicationArguments对象,它持有着args参数,就是main函数传进来的参数.调用prepareEnvironment方法.
  • 打印banner
  • 创建SpringBoot上下文
  • 初始化FailureAnalyzers
  • 调用prepareContext
  • 调用AbstractApplicationContext#refresh方法,并注册钩子
  • 在容器完成刷新后,依次调用注册的Runners
  • 调用SpringApplicationRunListeners#finished
  • 停止计时
  • 初始化过程中出现异常时调用handleRunFailure进行处理,然后抛出IllegalStateException异常.

参考

如果有错,希望指出

动态代理在Java中有着广泛的应用,比如 Spring Aop、Hibernate 数据查询、RPC、Java 注解对象的获取。静态代理的代理关系在编译时确定,而动态代理的代理关系是在编译期确定的。主要了解:JDK Proxy 和 cglib 动态代理。

动态代理可以提供另一个对象的访问,同时可以隐藏实际对象的具体实例。

静态代理

再说动态代理之前,先了解下静态代理。

静态代理是代理模式实现方式之一,在程序运行前,由程序员创建或者特定工具自动生成源代码并对其编译生成 .class 文件。静态代理的实现需要三步:

  • 定义业务接口
  • 实现业务接口
  • 定义代理类并实现业务接口

最后就可以直接通过客户顿进行调用静态代理了。

静态代理总结:

  • 优点:可以做到不对目标对象进行修改的前提下,对目标对象进行功能的扩展和拦截。
  • 缺点:因为代理对象,需要实现和目标对象一样的接口,会导致代理对象十分繁多,不易维护,同时一旦接口增加方法,则目标对象和代理类都需要维护。

JDK Proxy

先实现一个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Hello {
void sayHello();
void sayJava();
}
public class HelloImpl implements Hello {

@Override
public void sayHello() {
System.out.println("Hello Java!");
}
@Override
public void sayJava() {
System.out.println("Hello Java");
}
}

下面使用java.lang.reflect.Proxy 实现动态代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class MyInvocationHandler implements InvocationHandler {

private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}

/**
* @param proxy 动态代理类的引用,通常情况下不需要它
* @param method 方法对象的引用,代表被动态代理类调用的方法
* @param args args对象数组,代表被调用方法的参数
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正在执行" + method.getName() + "方法");
Object result = method.invoke(target, args);
return result;
}
}

public class MyDynamicProxy {

public static void main(String[] args) {

HelloImpl hello = new HelloImpl();
MyInvocationHandler handler = new MyInvocationHandler(hello);
//构造代码实例 Proxy.newProxyInstance(类加载器, 需要实现的接口数组,InvocationHandler接口)
Hello proxyHello = (Hello) Proxy.newProxyInstance(HelloImpl.class.getClassLoader(),
HelloImpl.class.getInterfaces(),
handler);
//调用代理
proxyHello.sayHello();
proxyHello.sayJava();
}
}

newProxyInstance()会返回一个实现了指定接口的代理对象,对该对象的所有方法调用都会转发给InvocationHandler.invoke()方法。理解上述代码需要对Java反射机制有一定了解。动态代理神奇的地方就是:

  • 代理对象是在程序运行时产生的,而不是编译期;
  • 对代理对象的所有接口方法调用都会转发到InvocationHandler.invoke()方法,在invoke()方法里我们可以加入任何逻辑,比如修改方法参数,加入日志功能、安全检查功能等;之后我们通过某种方式执行真正的方法体,示例中通过反射调用了Hello对象的相应方法,还可以通过RPC调用远程方法。

cglib

GCLIB(Code Generation Library)是一个基于 ASM 的字节码生成库,它允许在运行时对字节码进行修改和动态生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//实现MethodInterceptor方法
public class MyMethodInterceptor implements MethodInterceptor {

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行GCLIB动态代理");
return methodProxy.invokeSuper(o, objects);
}

}

public class CGlibTest {
public static void main(String[] args) {
//通过 CGlib 动态获取代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloImpl.class);
enhancer.setCallback(new MyMethodInterceptor());
Hello helloProxy = (Hello) enhancer.create();
helloProxy.sayHello();
helloProxy.sayJava();
}
}

总结

JDK Proxy

  • JDK动态代理只能代理有接口的类,并且是只能代理接口方法,不能代理一般的类中的方法
  • 提供了一个使用 InvocationHandler 作为参数的构造方法,在代理类中做一层包装,业务逻辑在 invoke 方法中实现;
  • 重写了 Object 类的 equals、hashCode、toString,它们都只是简单的调用了InvocationHandler 的 invoke 方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法;
  • 在 invoke 方法中我们甚至可以不用 Method.invoke 方法调用实现类就返回。这种方式常常用在 RPC 框架中,在 invoke 方法中发起通信调用远端的接口等。

CGlib

  • CGlib 可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类;
  • 由于是继承方式,如果是 static方法、private方法、final方法等描述的方法是不能被代理的;
  • 做了方法访问优化,使用建立方法索引的方式避免了传统 Method 的方法反射调用;
  • 提供 callback 和 filter 设计,可以灵活地给不同的方法绑定不同的 callback。编码更方便灵活。
  • CGLIB 会默认代理 Object 中 finalize、equals、toString、hashCode、clone等方法。比JDK代理多了finalize和clone。

参考

如果有问题,希望指出

Reflection(反射)是 Java 被视为动态语言的关键,反射机制允许程序在执行期借用 Reflection API 获取任何内部信息,并能操作任何对象的内部属性级方法。

Java 反射机制主要提供一下功能:

  • 在运行时构造任意一个类的对象
  • 在运行时获取任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法
  • 生成动态代理

Java 反射提供三种获取 Class 对象的方法:

1
2
3
4
5
6
7
Person person = new Person(1L, "反射");
//1、通过对象名
Class personClass = person.getClass();
//2、通过类名
Class baseClass = Person.class;
//3、通过全类名
Class baseClass2 = Class.forName("com.example.basejava.basics.reflect.Person");

Class 是一个类,一个描述类的类。它封装了描述方法的 Method,描述字段的 Filed,描述构造器的 Constructor 等属性。

代码

Person 类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Person implements Serializable {

private Long id;
private String name;

static {
System.out.println("测试反射静态代码块");
}

Person(Person person) {
System.out.println("(默认)反射测试类构造了" + person);
}

public Person() {
System.out.println("调用了公有、无参构造方法执行了。。。");
}

Person(Long id, String name) {
this.id = id;
this.name = name;
}

Person(String name) {
this.name = name;
System.out.println("name+" + name);
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}

反射具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class ReflectTest {

public static void main(String[] args) throws ClassNotFoundException,
IntrospectionException,
IllegalAccessException,
InvocationTargetException,
InstantiationException,
NoSuchMethodException {
Person person = new Person(1L, "反射");
//1、通过对象名
Class clazz1 = person.getClass();
//2、通过类名
Class clazz = Person.class;
//3、通过全类名
Class clazz2 = Class.forName("com.example.basejava.basics.reflect.Person");

System.out.println("----获取文件名----");
System.out.println(clazz.getName());

System.out.println("---获取类内部信息-----");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//获取内部成员变量,比如 id、name
String key = field.getName();
PropertyDescriptor descriptor = new PropertyDescriptor(key, clazz);
Method method = descriptor.getReadMethod();
Object value = method.invoke(person);
System.out.println(key + ":" + value);
}

System.out.println("----获取实例对象----");
//利用Class对象的newInstance方法创建一个类的实例
Object obj = clazz.newInstance();
System.out.println(obj);

System.out.println("------获取方法---------");
//获取方法,不能获取private方法,且获取从父类继承来的所有方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.print(" " + method.getName());
}
System.out.println();

//获取所有方法,包括私有方法
methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(" " + method.getName());
}
System.out.println();

//获取指定方法
Method method = clazz.getDeclaredMethod("setName", String.class);
System.out.println(method);

//修改类内部信息,setName("反射)
method.invoke(obj, "反射");
System.out.println(obj);

System.out.println("----所有公有构造方法-----");
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}

System.out.println("-----所有的构造方法(包括:私有、受保护、默认、公有)-------");
constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}

System.out.println("--------获取公有、无参的构造方法--------");
Constructor constructor = clazz.getConstructor(null);
System.out.println("constructor : " + constructor);

System.out.println("------获取私有构造方法,并调用-------");
Object object = constructor.newInstance();
constructor = clazz.getDeclaredConstructor(String.class);
System.out.println(constructor);
//调用构造方法
constructor.setAccessible(true);
object = constructor.newInstance("男");
System.out.println(object);
}
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
测试反射静态代码块
----获取文件名----
com.example.basejava.basics.reflect.Person
---获取类内部信息-----
id:1
name:反射
----获取实例对象----
调用了公有、无参构造方法执行了。。。
Person{id=null, name='null'}
------获取方法---------
toString getName setName getId setId wait wait wait equals hashCode getClass notify notifyAll
toString
getName
setName
getId
setId

public void com.example.basejava.basics.reflect.Person.setName(java.lang.String)
Person{id=null, name='反射'}
----所有公有构造方法-----
public com.example.basejava.basics.reflect.Person()
-----所有的构造方法(包括:私有、受保护、默认、公有)-------
com.example.basejava.basics.reflect.Person(java.lang.String)
com.example.basejava.basics.reflect.Person(java.lang.Long,java.lang.String)
public com.example.basejava.basics.reflect.Person()
com.example.basejava.basics.reflect.Person(com.example.basejava.basics.reflect.Person)
--------获取公有、无参的构造方法--------
constructor : public com.example.basejava.basics.reflect.Person()
------获取私有构造方法,并调用-------
调用了公有、无参构造方法执行了。。。
com.example.basejava.basics.reflect.Person(java.lang.String)
name+男
Person{id=null, name='男'}