From 0af46f944bf27cbac728e1a8fd11daed8146495e Mon Sep 17 00:00:00 2001
From: heibaiying <2806718453@qq.com>
Date: Wed, 10 Jul 2019 09:24:20 +0800
Subject: [PATCH] =?UTF-8?q?docker=E5=9F=BA=E7=A1=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
notes/Docker基础.md | 303 ++++++++++++++++++++++++++
notes/Java反射.md | 369 ++++++++++++++++++++++++++++++++
pictures/dockerfile01.png | Bin 0 -> 44154 bytes
pictures/docker与虚拟机.png | Bin 0 -> 37231 bytes
pictures/docker常用命令.jpg | Bin 0 -> 55021 bytes
pictures/docker架构.png | Bin 0 -> 49705 bytes
6 files changed, 672 insertions(+)
create mode 100644 notes/Docker基础.md
create mode 100644 notes/Java反射.md
create mode 100644 pictures/dockerfile01.png
create mode 100644 pictures/docker与虚拟机.png
create mode 100644 pictures/docker常用命令.jpg
create mode 100644 pictures/docker架构.png
diff --git a/notes/Docker基础.md b/notes/Docker基础.md
new file mode 100644
index 0000000..88fac7d
--- /dev/null
+++ b/notes/Docker基础.md
@@ -0,0 +1,303 @@
+# Docker 基础
+
+
+
+## 一、Docker 简介
+
+Docker 是一个构建,发布和运行应用程序的开放平台。Docker 以容器为资源分隔和调度的基本单位,容器封装了整个项目运行时所需要的所有环境,通过 Docker 你可以将应用程序与基础架构分离,像管理应用程序一样管理基础架构,以便快速完成项目的部署与交付。
+
+Docker 使用 Go 语言进行开发,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。最初实现是基于 LXC,从 0.7 版本以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
+
+- runc :是一个 Linux 命令行工具,用于根据 [OCI容器运行时规范](https://github.com/opencontainers/runtime-spec) 创建和运行容器。
+- containerd :是一个守护程序,它管理容器生命周期,提供了在一个节点上执行容器和管理镜像的最小功能集。
+
+下图体现了 Docker 和传统虚拟化方式的不同之处:传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,再在该系统上运行所需应用进程;而 Docker 容器内的应用进程则是直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟,因此要比传统虚拟机更为轻便。
+
+
+
+
+## 二、Docker 架构与核心概念
+
+Docker 使用 client-server 架构, Docker 客户端将命令发送给 Docker 守护进程,后者负责构建,运行和分发 Docker 容器。 Docker客户端和守护程序使用 REST API,通过 UNIX 套接字或网络接口进行通信。核心概念如下:
+
+
+### 2.1 镜像
+
+Docker 镜像(Image)是一个特殊的文件系统,包含了程序运行时候所需要的资源和环境。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
+
+因为镜像包含操作系统完整的 `root` 文件系统,其体积往往是庞大的,因此在 Docker 设计时,充分利用 Union FS (联合文件系统)的技术,将其设计为分层存储的架构。所以一个镜像实际上是由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
+
+分层存储的特征使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
+
+### 2.2 容器
+
+镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
+
+容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样,这种特性使得容器封装的应用比直接在宿主运行更加安全。
+
+前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层称为 **容器存储层**。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
+
+按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
+
+### 2.3 仓库
+
+镜像构建完成后,可以很容易的在当前宿主机上运行,但如果需要在其它服务器上使用这个镜像,就需要一个集中的存储、分发镜像的服务,这就是镜像仓库(Registry)。[Docker Hub](https://hub.docker.com/) 是 Docker 官方提供的镜像公有仓库,提供了大量常用软件的镜像,当然出于安全和保密的需要,你也可以构建自己的私有仓库。
+
+### 2.4 Docker daemon
+
+Docker daemon(dockerd)负责监听 Docker API 请求并管理 Docker 对象,如镜像,容器,网络和卷,守护程序彼此之间也可以进行通讯。
+
+### 2.5 Docker Client
+
+Docker 客户端(docker)是用户与 Docker 交互的主要方式。当你使用 docker run 等命令时,客户端会将这些命令发送到 dockerd,dockerd 负责将其执行。一个 Docker客户端可以与多个 dockerd 进行通讯。
+
+## 三、Docker 常用命令
+
+Docker 提供了大量命令用于管理镜像、容器和服务,命令的统一使用格式为:` docker [OPTIONS] COMMAND` ,其中 OPTIONS 代表可选参数。需要注意的是 Docker 命令的执行一般都需要获取 root 权限,这是因为 Docker 的命令行工具 docker 与 docker daemon 是同一个二进制文件,docker daemon 负责接收并执行来自 docker 的命令,它的运行需要 root 权限。所有常用命令及其使用场景如下:
+
+
+### 2.1 基础命令
+
+- **docker version**:用于查看 docker 的版本信息
+- **docker info**:用于查看 docker 的配置信息
+- **docker help**:用于查看帮助信息
+
+### 2.2 镜像相关命令
+
+**1. docker search 镜像名**
+
+从官方镜像仓库 Docker Hub 查找指定名称的镜像。常用参数为`--no-trunc`,代表显示完整的镜像信息。
+
+**2. docker images **
+
+列出所有顶层镜像的相关信息。常用参数如下:
+
+- -a :显示所有镜像,包括中间隐藏的镜像
+- -q :只显示镜像 ID
+- --digests :显示摘要信息
+- --no-trunc :显示完整镜像信息
+
+**3. docker pull 镜像名 [:TAG]**
+
+从官方仓库下载镜像,`:TAG`为镜像版本,不加则默认下载最新版本。
+
+**4. docker rmi 镜像名或ID [:TAG] **
+
+删除指定版本的镜像,不加`:TAG`则默认删除镜像的最新版本。如果有基于该镜像的容器存在,则该镜像无法直接删除,此时可以使用参数`-f`,代表强制删除。rmi 命令支持批量删除,多个镜像名之间使用空格分隔。如果想要删除所有镜像,则可以使用命令`docker rmi -f $(docker images -qa)`。
+
+### 2.3 容器相关命令
+
+**1. docker run [OPTIONS] IMAGE [COMMAND] [ARG...]**
+
+run 是 docker 中最为核心的一个命令,用于新建并启动容器,其拥有众多可用参数,可以使用`docker run --help`查看所有可用参数。常用参数如下:
+
++ -i :表示使用交互模式,始终保持输入流开放;
++ -t :表示分配一个伪终端,通常和`-i`结合使用,表示使用伪终端与容器进行交互;
++ -d :以后台方式运行容器;
++ --name :指定容器启动容器的名字,如果不指定,则由 docker 随机分配;
++ -c :用于给运行在容器中的所有进程分配 CPU 的 shares 值,这是一个相对权重,实际的处理速度与宿主机的 CPU 相关;
++ -m :用于限制为容器中所有进程分配的内存总量,以B、K、M、G为单位;
++ -v :用于挂载数据卷 volume,可以用多个`-v`参数同时挂载多个 volume。volume 的格式为:`[host-dir]:[container-dir]:[rw:ro]`,`[rw:ro]`用于指定数据卷的模式,`rw`代表读写模式,`ro`代表只读模式。
++ -p :用于将容器的端口暴露给宿主机的端口,格式为:`hostPort:containerPort`,通过端口的暴露,可以让外部主机能够访问容器内的应用。
+
+**2. docker ps [OPTIONS]**
+
+列出当前所有正在运行的容器。常用参数如下:
+
+- -a :列出所有容器,包括运行的和已经停止的所有容器
+- -n :显示最近创建的 n 个容器
+- -q :只显示容器编号
+- --no-trunc :不要截断输出信息
+
+**3. 启动\重启\停止\强制停止容器**
+
+与容器启动、停止相关的命令为:`docker start|restart|stop|kill 容器名或ID`,start 命令用于启动已有的容器,restart 用于重启正在运行的容器,stop 用于停止正在运行的容器,kill 用于强制停止容器。
+
+**4. 进入正在运行的容器 **
+
+想要进入正在运行的容器,与容器主进程交互,有以下两种常用方法:
+
++ docker attach 容器名或ID
++ docker exec -it 容器名或ID /bin/bash
+
+**5. 退出容器**
+
+想要退出正在运行的容器,有以下两种常用方法:
+
++ exit :退出并停止容器;
++ ctrl+P+Q :退出。
+
+**6. docker rm 容器名或ID**
+
+删除已停止的容器,常用参数为`-f`,表示强制删除容器,即便容器还在运行。想要删除所有容器,可以使用`docker rm -f $(docker ps -aq)`命令。
+
+**7. 查看容器信息**
+
+可以使用`docker inspect [OPTIONS] NAME|ID [NAME|ID...]`查看容器或者镜像的详细信息,想要查看指定的信息,可以使用 `-- format`参数来指定输出的模板格式,示例如下:
+
+```shell
+docker inspect --format='{{.NetworkSettings}}' 32cb3ace3279
+```
+
+**8. 查看容器运行日志**
+
+可以使用`docker logs [OPTIONS] CONTAINER`查看容器中进程的运行日志,常用参数如下:
+
+- --details :显示日志详情
+- -f :跟随日志输出显示
+- --tail :从末尾开始显示指定行的数据
+- -t :显示时间戳
+- --since :开始时间
+- --until : 结束时间
+
+## 四、DockerFile
+
+dockerfile 是 Docker 用来构建镜像的文本文件,包含自定义的指令和格式,可以通过 build 命令从 dockerfile 中构建镜像,命令格式为:`docker build [OPTIONS] PATH | URL | -` 。
+
+dockerfile 描述了组装镜像的步骤,其中每条指令都是单独执行的。除了 FROM 指令,其他的每一条指令都会在上一条指令所生成镜像的基础上执行,执行完后会生成一个新的镜像层,新镜像层覆盖在原来的镜像之上从而形成新的镜像。为了提高镜像构建的速度, Docker Daemon 会缓存构建过程中的中间镜像。在构建镜像时,它会将 dockerfile 中下一条指令和基础镜像的所有子镜像做比较,如果有一个子镜像是由相同的指令生成的,则命中缓存,直接使用该镜像,而不用再生成一个新的镜像。常用指令如下:
+
+### 1. FROM
+
+FROM 指令用于指定基础镜像,因此所有的 dockerfile 都必须使用 FROM 指令开头。FROM 指令可以出现多次,这样会构建多个镜像,每个镜像创建完成后,Docker 命令行界面会输出该镜像的 ID。常用指令格式为:`FROM [:] [AS ]`。
+
+### 2. MAINTAINER
+
+MAINTAINER 指令可以用来设置作者名称和邮箱,目前 MAINTAINER 指令被标识为废弃,官方推荐使用 LABEL 代替。
+
+### 3. LABEL
+
+LABEL 指令可以用于指定镜像相关的元数据信息。格式为:`LABEL = = = ...` 。
+
+### 4. ENV
+
+ENV 指令用于声明环境变量,声明好的环境变量可以在后面的指令中引用,引用格式为`$variable_name`或`${variable_name}`。常用格式有以下两种:
+
++ `ENV ` :用于设置单个环境变量;
++ `ENV = ...` :用于一次设置多个环境变量。
+
+### 5. EXPOSE
+
+EXPOSE 用于指明容器对外暴露的端口号,格式为:`EXPOSE [/...]`,您可以指定端口是侦听 TCP 还是 UDP,如果未指定协议,则默认为 TCP。
+
+### 6. WORKDIR
+
+WORKDIR 用于指明工作目录,它可以多次使用。如果指明的是相对路径,则它将相对于上一个WORKDIR指令的路径。示例如下:
+
+```shell
+WORKDIR /a
+WORKDIR b
+WORKDIR c
+RUN pwd # 此时pwd为:/a/b/c
+```
+
+### 7. COPY
+
+COPY 指令的常用格式为:`COPY ... `,用于将指定路径中的文件添加到新的镜像中,拷贝的目标路径可以不存在,程序会自动创建。
+
+### 8. ADD
+
+ADD 指令的常用格式为:`COPY ... `,作用与 COPY 指令类似,但功能更为强大,例如`Src`支持文件的网络地址,且如果`Src`指向的是压缩文件,ADD 在复制完成后还会自动进行解压。
+
+### 9. RUN
+
+RUN 指令会在前一条命令创建出的镜像基础上再创建一个容器,并在容器中运行命令,在命令结束后提交该容器为新的镜像。它支持以下两种格式:
+
+- `RUN ` (shell 格式)
+- `RUN ["executable", "param1", "param2"]` (*exec* 格式)
+
+使用 shell 格式时候,命令通过`/bin/sh -c`运行,而当使用 exec 格式时,命令是直接运行的,容器不调用 shell 程序,这意味着不会发生正常的 shell 处理。例如,`RUN ["echo","$HOME"]`不会对`$HOME`执行变量替换,此时正确的格式应为:`RUN ["sh","-c","echo $HOME"]`。下面的 CMD 指令也存在同样的问题。
+
+### 10. CMD
+
+- `CMD ["executable","param1","param2"]` (*exec* 格式, 首选)
+- `CMD ["param1","param2"]` (作为 *ENTRYPOINT* 的默认参数)
+- `CMD command param1 param2` (*shell* 格式)
+
+CMD 指令提供容器运行时的默认值,这些默认值可以是一条指令,也可以是一些参数。一个 dockerfile 中可以有多条 CMD 指令,但只有最后一条 CMD 指令有效。CMD 指令与 RUN 指令的命令格式相同,但作用不同:RUN 指令是在镜像的构建阶段用于产生新的镜像;而 CMD 指令则是在容器的启动阶段默认将 CMD 指令作为第一条执行的命令,如果用户在 docker run 时指定了新的命令参数,则会覆盖 CMD 指令中的命令。
+
+### 11. ENTRYPOINT
+
+ENTRYPOINT 指令 支持以下两种格式:
+
+- `ENTRYPOINT ["executable", "param1", "param2"]` (*exec* 格式,首先)
+- `ENTRYPOINT command param1 param2` (*shell* 格式)
+
+ENTRYPOINT 指令 和 CMD 指令类似,都可以让容器在每次启动时执行相同的命令。但不同的是 CMD 后面可以是参数也可以是命令,而 ENTRYPOINT 只能是命令;另外 docker run 命令提供的运行参数可以覆盖 CMD,但不能覆盖 ENTRYPOINT ,这意味着 ENTRYPOINT 指令上的命令一定会被执行。如下 dockerfile 片段:
+
+```
+ENTRYPOINT ["/bin/echo", "Hello"]
+CMD ["world"]
+```
+
+当执行`docker run -it image`后,此时输出为`hello world`,而当你执行`docker run -it image spring`,此时 CMD 中的参数会被覆盖,此时输出为`hello spring`。
+
+## 五、案例
+
+### 4.1 基于 Centos 镜像部署 Spring Boot 项目
+
+生产环境中的大多数项目通常都部署在 Linux 服务器上,这里我们从基础的 Linux 镜像开始,并将我们的项目(这里以 Spring Boot 项目为例)一起打包构建成为一个完整的可执行的镜像。首先需要创建**Dockerfile**文件,其内容如下:
+
+```shell
+# 以官方仓库的centos镜像为基础开始创建
+FROM centos
+# 作者信息
+MAINTAINER heibaiying@heibaiying.com
+
+# 把JDK安装包拷贝到容器中并自动进行解压
+ADD jdk-8u211-linux-x64.tar.gz /usr/java/
+# 拷贝项目Jar包到容器中
+COPY spring-boot-base.jar /usr/app/
+# 配置Java环境变量
+ENV JAVA_HOME /usr/java/jdk1.8.0_211
+ENV JRE_HOME ${JAVA_HOME}/jre
+ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
+ENV PATH ${JAVA_HOME}/bin:$PATH
+# 项目启动命令
+ENTRYPOINT ["java", "-jar", "/usr/app/spring-boot-base.jar"]
+```
+
+将 JDK 安装包,Spring Boot 项目的 Jar 包以及 Dockerfile 文件放在同一个目录,然后执行下面镜像构建命令:
+
+```shell
+docker build -t spring-boot-base-java:latest .
+```
+
+镜像构建完成后,可以使用以下命令进行启动:
+
+```shell
+docker run -it -p 8080:8080 spring-boot-base-java
+```
+
+这里为了观察到启动效果,所以使用交互的方式启动,实际部署时可以使用`-d`参数来后台启动,输出如下:
+
+
+### 4.2 基于 JDK 镜像部署 Spring Boot 项目
+
+上面的项目我们是基于最基础的 Centos 镜像开始构建,但由于 Docker Hub 上已经提供了 JDK 的镜像,我们也可以选择从 JDK 镜像开始构建,此时构建过程更加简单。构建步骤和上面的完全一致,只是 Dockerfile 的内容有所不同,如下:
+
+```shell
+# 由于只需要运行环境,这里我们直接以官方仓库的jre镜像为基础开始创建
+FROM openjdk:8u212-jre
+# 作者信息
+MAINTAINER heibaiying@heibaiying.com
+
+# 拷贝项目Jar包到容器中
+COPY spring-boot-base.jar /usr/app/
+# 项目启动命令
+ENTRYPOINT ["java", "-jar", "/usr/app/spring-boot-base.jar"]
+```
+
+
+
+# 参考资料
+
+1. Docker 官方简介:https://docs.docker.com/engine/docker-overview/
+2. Docker CLI 和 Dockerfile 官方文档: https://docs.docker.com/reference/
+3. 浙江大学SEL实验室 . Docker 容器与容器云(第2版). 人民邮电出版社 . 2016-10
+4. Docker 从入门到实践:https://yeasy.gitbooks.io/docker_practice/
\ No newline at end of file
diff --git a/notes/Java反射.md b/notes/Java反射.md
new file mode 100644
index 0000000..81d0ed3
--- /dev/null
+++ b/notes/Java反射.md
@@ -0,0 +1,369 @@
+# Java 反射
+
+## 一、Class 类
+
+### 1.1 Class 对象
+
+在 Java 中,每个已加载的类在内存中都有一份类信息,类信息对应的类是 java.lang.Class,每个对象都持有指向它所属类信息的引用。所以想要获取 Class 对象,有以下三种方法:
+
++ 通过类名获取:
+
+```java
+Class dateClass = Date.class;
+Class integerClass = int.class;
+```
+
++ 通过对象的`getClass()`方法获取:
+
+```java
+Date date = new Date();
+Class extends Date> aClass = date.getClass();
+```
+
++ 通过 Class 类的静态方法 `forName` 获取:
+
+```java
+Class> aClass = Class.forName("java.util.Date");
+```
+
+### 1.2 名称信息
+
+Class 提供了如下三个方法,由于获取名称信息,其作用和输出分别如下:
+
+- **getName**:返回 Java 内部使用的名称;
+- **getSimpleName**:返回简称;
+- **getCanonicalName**:返回由 Java 语言规范定义的命名。
+
+| Class 对象 | getName | getSimpleName | getCanonicalName |
+| --------------- | ------------------- | ------------- | ------------------- |
+| int.class | int | int | int |
+| int[].class | [I | int[] | int[] |
+| int\[][].class | [[I | int\[][] | int\[][] |
+| String.class | java.lang.String | String | java.lang.String |
+| String[].class | [Ljava.lang.String; | String[] | java.lang.String[] |
+| ArrayList.class | java.util.ArrayList | ArrayList | java.util.ArrayList |
+
+对于数组类型的getName返回值,使用前缀`[`表示数组,有几个`[`表示是几维数组,数组类型使用字符表示:boolean(Z)、byte(B)、char(C)、double(D)、float(F)、int(I)、long(J)、short(S)、接口和类(L)。
+
+### 1.3 构造器
+
+Class 有以下四个获取构造器信息的方法:
+
+```java
+//឴ 获取所有的public构造器
+public Constructor>[] getConstructors()
+//឴ 获取所有构造器
+public Constructor>[] getDeclaredConstructors()
+//឴ 获取指定参数的public构造器
+public Constructor getConstructor(Class>... parameterTypes)
+// 获取指定参数的构造器឴
+public Constructor getDeclaredConstructor(Class>... parameterTypes)
+```
+
+获取指定的构造器后,可以使用`newInstance`方法来创建其实例,实例如下:
+
+```java
+class Test {
+
+ static class Student {
+ private String name;
+ private int age;
+
+ Student() {}
+
+ Student(String name, int age) {
+ this.name = name;
+ this.age = age;
+ }
+
+ @Override
+ public String toString() {return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';}
+ }
+
+ public static void main(String[] args) throws Exception {
+ Constructor extends Student> constructor = Student.class.getDeclaredConstructor(String.class, int.class);
+ Student student = constructor.newInstance("heibaiying", 10);
+ System.out.println(student);
+ }
+}
+
+输出:Student{name='heibaiying', age=10}
+```
+
+如果你只是想调用默认的无参构造器,则可以直接使用 Class 对象的`newInstance`方法:
+
+```java
+Student student = Student.class.newInstance();
+```
+
+
+### 1.4 字段信息
+
+Class 有以下四个获取字段信息的方法:
+
+```java
+// 返回本类或其父类的所有public字段
+public Field[] getFields()
+// 返回本类声明的所有字段,但不包括父类的
+public Field[] getDeclaredFields()
+// 返回本类或其父类中指定名称的public字段
+public Field getField(String name)
+// 返回本类声明的指定名称的字段
+public Field getDeclaredField(String name)
+```
+
+字段信息被封装在 Field 类中,Field 对象有以下常用方法:
+
+```java
+// 获取字段的名称
+public String getName()
+// 判断是否有该字段的访问权限
+public boolean isAccessible()
+// 设置为true表示忽略Java的访问检查机制,从而可以读写非public的字段
+public void setAccessible(boolean flag)
+// 获取指定对象obj中该字段的值
+public Object get(Object obj)
+// 将指定对象obj中该字段的值设为value
+public void set(Object obj, Object value)
+```
+
+以下给出一个修改字段的使用示例:
+
+```java
+class Test {
+
+ static class Student {
+ private String name;
+ private int age;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Student student = new Student();
+ Class extends Student> aClass = student.getClass();
+ Field field = aClass.getDeclaredField("name");
+ field.setAccessible(true);
+ field.set(student, "heibaiying");
+ System.out.println(field.get(student));
+ }
+}
+输出: heibaiying
+```
+
+### 1.5 方法信息
+
+Class 有以下四个获取方法信息的方法:
+
+```java
+// 返回本类或其父类的所有public方法
+public Method[] getMethods()
+// 返回本类声明的所有方法,但不包括父类的
+public Method[] getDeclaredMethods()
+// 返回本类或其父类中指定名称及其参数类型的public方法
+public Method getMethod(String name, Class>... parameterTypes)
+// 返回本类声明的指定名称及其参数类型的方法
+public Method getDeclaredMethod(String name, Class>... parameterTypes)
+```
+
+方法信息被封装在 Method 类中,Method 对象有以下常用方法:
+
+```java
+// 获取方法的名称
+public String getName()
+// 设置为true表示忽略Java的访问检查机制,从而可以调用非public方法
+public void setAccessible(boolean flag)
+// 在指定的obj对象上调用invoke方法,args为传递的参数列表
+public Object invoke(Object obj, Object... args)
+```
+
+基本使用示例如下:
+
+```java
+class Test {
+
+ static class Student {
+ private String name;
+ public void setName(String name) {this.name = name;}
+ public String getName() { return name;}
+ }
+
+ public static void main(String[] args) throws Exception {
+ Student student = new Student();
+ Class extends Student> aClass = student.getClass();
+ Method setMethod = aClass.getDeclaredMethod("setName",String.class);
+ setMethod.invoke(student, "heibaiying");
+ Method geMethod = aClass.getDeclaredMethod("getName");
+ System.out.println(geMethod.invoke(student));
+ }
+}
+输出: heibaiying
+```
+
+### 1.6 类型检查
+
+想要判断某个对象是否是某个类或其子类的示例,可以使用`instanceof`关键字,或者使用 Class 的 `isInstance`方法,示例如下:
+
+```java
+static class Manager {}
+
+static class Employee extends Manager {}
+
+public static void main(String[] args) {
+ Employee employee = new Employee();
+ System.out.println(employee instanceof Manager); // true
+ System.out.println(employee instanceof Employee); // true
+
+ System.out.println(Manager.class.isInstance(employee)); // true
+ System.out.println(Employee.class.isInstance(employee)); // true
+
+}
+```
+
+如果想要判断对象的具体类型,则可以使用`equals`方法进行比较,示例如下:
+
+```java
+employee.getClass().equals(Manager.class); // false
+employee.getClass().equals(Employee.class); // true
+```
+
+在知道对象的类型后,如果你需要对其进行类型转换,则可以使用类型转换语法或者 Class 的`cast`方法:
+
+```java
+Manager manager01 = Manager.class.cast(employee);
+Manager manager02 = (Manager) employee;
+```
+
+
+
+## 二、注解
+
+### 2.1 注解定义
+
+下面是`@Override`注解的定义,所有注解均使用`@interface`关键字修饰,`@Target`定义注解的运用对象,`@Retention`表示注解信息保留到什么时候。
+
+```java
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface Override {
+}
+```
+
+`@Target`有以下可选值,其含义分别如下:
+
+- **TYPE**:表示类、接口(包括注释类型)或枚举声明
+- **FIELD**:字段声明(包括枚举常量)
+- **METHOD**:方法声明
+- **PARAMETER**:参数声明
+- **CONSTRUCTOR**:构造器声明
+- **LOCAL_VARIABLE**:本地变量声明
+- **ANNOTATION_TYPE**:注解声明
+- **PACKAGE**:包声明
+- **TYPE_PARAMETER**:类型参数声明
+- **TYPE_USE**:任何使用类型的语句
+
+`@Retention`有以下可选值,其含义分别如下:
+
+- **SOURCE**:只在源代码中保留,编译器将代码编译为字节码后就会丢掉。
+
+- **CLASS**:保留在字节码文件中,但虚拟机将 class 文件加载到内存时不一定会在内存中保留,这是默认的行为。
+
+- **RUNTIME**:一直保留到运行时,可以通过反射获取其信息。
+
+### 2.2 注解信息
+
+对于`@Retention`类型为`RUNTIME`的注解,可以利用反射机制查看其信息。因为注解可以运用在不同目标上,所以 Class 、Field、Method、Constructor 都有如下方法:
+
+```
+// 获取所有注解
+public Annotation[] getAnnotations()
+public Annotation[] getDeclaredAnnotations()
+public T getAnnotation(Class annotationClass)
+public boolean isAnnotationPresent(Class extends Annotation> annotationClass)
+```
+
+这里给出一个使用示例,模仿常用的序列化框架,通过注解定义字段的序列化名称和序列化格式,注解定义如下:
+
+```java
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Json {
+ String value();
+ String format() default "";
+}
+```
+
+
+
+```java
+class Test {
+
+ static class Student {
+ /*当只有一个值并且其对应的方法名为value时候,可以省略 value = */
+ @Json("姓名")
+ private String name;
+ @Json(value = "出生日期", format = "yyyy-MM-dd HH:mm:ss")
+ private Date birthday;
+ ......
+ }
+
+ static void parse(Object object, Class> clazz) throws IllegalAccessException {
+
+ Field[] fields = clazz.getDeclaredFields();
+ for (Field field : fields) {
+ if (field.isAnnotationPresent(Json.class)) {
+ field.setAccessible(true);
+ Object value = field.get(object);
+ Json json = field.getAnnotation(Json.class);
+ String name = json.value();
+ String format = json.format();
+ if (!"".equals(format) && field.getType() == Date.class) {
+ SimpleDateFormat formatter = new SimpleDateFormat(format);
+ value = formatter.format((Date) value);
+ }
+ System.out.println(name + ":" + value);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Student student = new Student("heibai", new Date());
+ parse(student, Student.class);
+ }
+}
+
+输出如下:
+姓名:heibai
+出生日期:2019-07-06 08:45:47
+```
+
+### 2.3 注解继承
+
+一个常用的元注解是`@Inherited`,它表示某个注解是否能够被继承。示例如下:
+
+```java
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+public @interface ClassName {
+ String value();
+}
+```
+
+使用示例如下:此时如果加上`@Inherited`注解,则两者的输出都是 true;如果不加上`@Inherited`,第二行语句输出 false,代表子类 PrimaryStudent 没有继承到` @ClassName`注解。
+
+```java
+class Test {
+
+ @ClassName("学生类")
+ static class Student {}
+
+ static class PrimaryStudent extends Student {}
+
+ public static void main(String[] args) {
+ System.out.println(Student.class.isAnnotationPresent(ClassName.class));
+ System.out.println(PrimaryStudent.class.isAnnotationPresent(ClassName.class));
+ }
+}
+```
+
+
+
diff --git a/pictures/dockerfile01.png b/pictures/dockerfile01.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b20fc9a17a5daecc768d4ad9b125d84c3fad931
GIT binary patch
literal 44154
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