<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>编程学习记录</title><link>https://68686.ltd/</link><description></description><item><title>Nginx配置教程</title><link>https://68686.ltd/?id=124</link><description>&lt;p&gt;Nginx是一个反向代理服务器，可以通过轮询，加权轮询，ip hash实现负载均衡。&lt;/p&gt;
&lt;p&gt;底层采用epoll模型，多路复用，可以实现很高的并发量，而且内存占用小。&lt;/p&gt;
&lt;p&gt;一般这个都是运维去管理。可是作为开发还是得知道怎么用才行。&lt;/p&gt;
&lt;h3 id=&quot;h3-nginx-&quot;&gt;&lt;a name=&quot;Nginx 配置文件结构&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;Nginx 配置文件结构&lt;/h3&gt;&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;...              #全局块

events {         #events块
   ...
}

http      #http块
{
    ...   #http全局块
    server        #server块
    { 
        ...       #server全局块
        location [PATTERN]   #location块
        {
            ...
        }
        location [PATTERN] 
        {
            ...
        }
    }
    server
    {
      ...
    }
    ...     #http全局块
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-u5168u5C40u5757&quot;&gt;&lt;a name=&quot;全局块&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;全局块&lt;/h3&gt;&lt;p&gt;写在一开始的配置，影响nginx全局的指令。一般有运行nginx服务器的用户组，nginx进程pid存放路径，日志存放路径，配置文件引入，允许生成worker process数等。&lt;/p&gt;
&lt;p&gt;如&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;#user  nobody;                          # 默认运行用户 编译时未指定的话为nobody
worker_processes  4;                    # Nginx主进程要开启多少个工作进程 一般为当前CPU核心数
worker_cpu_affinity 0001 0010 0100 1000;   # 将每个进程绑定到CPU的每个核心上 可以略提高性能

#error_log  logs/error.log;             # 错误日志的位置 默认是安装目录下的logs/error.log
#error_log  logs/error.log  notice;     # 可以在日志后面添加纪录级别 
#error_log  logs/error.log  info;       # 可选项有: [debug|info|notice|warn|error|crit]

#pid        logs/nginx.pid;             # pid文件路径 nginx -s 控制服务就是从这里读取主进程的PID
worker_rlimit_nofile 65535;             # 指定文件描述符数量 增大可以让Nginx处理更多连接
                                    # 还需要增大系统允许进程打开文件描述符的上限 ulimit -n 65536&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-events-&quot;&gt;&lt;a name=&quot;events块&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;events块&lt;/h3&gt;&lt;p&gt;配置影响nginx服务器或与用户的网络连接。有每个进程的最大连接数，选取哪种事件驱动模型处理连接请求，是否允许同时接受多个网路连接，开启多个网络连接序列化等。&lt;/p&gt;
&lt;p&gt;如&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;events {
    accept_mutex on;   #设置网路连接序列化，防止惊群现象发生，默认为on
    multi_accept on;  #设置一个进程是否同时接受多个网络连接，默认为off
    #use epoll;      #事件驱动模型，select|poll|kqueue|epoll|resig|/dev/poll|eventport
    worker_connections  1024;    #最大连接数，默认为512
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-http-&quot;&gt;&lt;a name=&quot;http块&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;http块&lt;/h3&gt;&lt;p&gt;可以嵌套多个server，配置代理，缓存，日志定义等绝大多数功能和第三方模块的配置。如文件引入，mime-type定义，日志自定义，是否使用sendfile传输文件，连接超时时间，单连接请求数等。&lt;/p&gt;
&lt;p&gt;如&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;http {
    include       mime.types;         # 其他的block配置 可以通过include导入 这样可以很方便管理配置
    default_type  application/octet-stream; # 响应类型未定义content-type时 文件默认类型为二进制流 
                                            # mime.types中包含文件扩展名与文件类型映射表

    #log_format  main  &amp;#39;$remote_addr - $remote_user [$time_local] &amp;quot;$request&amp;quot; &amp;#39;
    #                  &amp;#39;$status $body_bytes_sent &amp;quot;$http_referer&amp;quot; &amp;#39;
    #                  &amp;#39;&amp;quot;$http_user_agent&amp;quot; &amp;quot;$http_x_forwarded_for&amp;quot;&amp;#39;;
                                      # 定义access.log日志的格式 main为格式名称 

    access_log  logs/access.log  main;      # 默认access.log的位置 以及使用main中定义的格式

    client_max_body_size     20m;           # 设置允许客户上传大小
    client_header_buffer_size   32k;        # 客户端请求 header_buffer大小 默认1k
    large_client_header_buffers  4 32k;     # 指定客户请求header_buffer的缓存最大数量和大小

    sendfile        on;                     # 开启高效的文件传输模式
    tcp_nopush      on;                     # 开启tcp_nopush和tcp_nodelay用于防止网络阻塞
    tcp_nodelay     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;                  # 客户端连接后保持连接的超时时间
    client_header_timeout  10;              # 设置客户端请求头读取超时时间 
                                            # 超时将返回Request time out (408)
    client_body_timeout    10;              # 设置客户端请求主体读取超时时间  超时也将返回408
    send_timeout           10;              # 指定响应客户端的超时时间
                                            # 如果超过这个时间 客户端没有任何活的 Nginx将关闭连接

    gzip  on;                               # 开启还是关闭gzip模块 on表示开启 实时压缩输出数据流
    gzip_min_length  1k;                    # 允许压缩页面最小字节数 小于1k的文件可能越压越大
    gzip_buffers     4  16k;                # 申请4个单位16KB的内存做压缩结果流缓存 默认源文件大小
    gzip_http_version  1.1;                 # 用于识别HTTP协议版本 默认 1.1
    gzip_comp_level  2;                     # 压缩等级 1 表示最快压缩 但压缩比最小 9反之
    gzip_types  text/plain application/x-javascript text/css application/xml;   # 指定压缩类型
    gzip_vary  on;                          # 让前端的缓存服务器缓存经过gzip压缩的页面

    # http_proxy 设置
 # 故障转移策略，当后端服务器返回如下错误时，自动负载到后端其余机器
 proxy_next_upstream http_500 http_502 http_503 error timeout invalid_header;

 # 设置后端服务器获取用户真实IP、代理者真实IP等
 proxy_redirect off;
 proxy_set_header Host $host;
 proxy_set_header X-Real-IP $remote_addr;
 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

 # 用于指定客户端请求主体缓存区大小，可以理解成先保存到本地再传给用户
 client_body_buffer_size 128k;

 # 表示与后端服务器连接的超时时间，即发起握手等侯响应的超时时间
 proxy_connect_timeout 90;

 # 表示后端服务器的数据回传时间，即在规定时间之后端服务器必须传完所有的数据，否则 Nginx 将断开这个连接
 proxy_send_timeout 90;

 # 设置 Nginx 从代理的后端服务器获取信息的时间，表示连接建立成功后，Nginx 等待后端服务器的响应时间，其实是 Nginx 已经进入后端的排队中等候处理的时间
 proxy_read_timeout 90;

 # 设置缓冲区大小，默认该缓冲区大小等于指令 proxy_buffers 设置的大小
 proxy_buffer_size 4k;

 # 设置缓冲区的数量和大小。Nginx 从代理的后端服务器获取的响应信息，会放置到缓冲区
 proxy_buffers 4 32k;

 # 用于设置系统很忙时可以使用的 proxy_buffers 大小，官方推荐大小为 proxu_buffers 的两倍
 proxy_busy_buffers_size 64k;

 # 指定 proxy 缓存临时文件的大小
 proxy_temp_file_write_size 64k;
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代理部分也可以配置在server中。&lt;/p&gt;
&lt;h3 id=&quot;h3-server-&quot;&gt;&lt;a name=&quot;server 块&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;server 块&lt;/h3&gt;&lt;p&gt;server是在http中的，用于配置虚拟主机的相关参数，一个http中可以有多个server。&lt;/p&gt;
&lt;p&gt;如&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {                                # server段就是虚拟主机的配置
    listen       80;                    # 监听所有IP的80端口 可以指定单个IP
    server_name  localhost  example.com;;             # 用来指定IP地址或域名 多个域名间用空格分开

    #charset utf-8;;                    # 用于设置网页默认编码

    #access_log  logs/host.access.log  main;    # 每一个虚拟主机也可以定义单独的access.log

    location / {                        # URL地址匹配设置
        root   /var/www/html;           # 网页根目录
        index  index.html index.htm;    # 默认首页地址 会先寻找index.html 然后往后
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$  {   # 匹配以这些格式结尾的请求
        root    /var/www/static;                    # 匹配到的请求从这个目录找文件
        expires 30d;                                # 客户端缓存静态文件过期时间30天
    }


    error_page  404              /404.html;         # 自定义404页面 /404.html为root指定的位置

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;        # 自定义50x页面 到/50x.html
    location = /50x.html {                          # 匹配50x页面的请求
        root   html;                                # 匹配到的请求从这个目录找文件
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    location ~ \.php$ {                             # 匹配php页面的请求
        index index.php                             # 默认索引index.php
        proxy_pass   http://127.0.0.1               # 匹配到的请求转发到本机80端口处理
    }

    location ~ \.jsp$ {                             # 匹配jsp页面的请求
        index index.jsp                             # 默认索引index.jsp
        proxy_pass   http://127.0.0.1:8080          # 匹配到的请求转发到本机8080端口处理
    }

    location /status {                              # 匹配status的请求
        stub_status on;                             # 开启Nginx工作状态统计
        access_log    logs/status.log;              # 状态统计页面访问日志
        auth_basic    &amp;quot;Nginx Status&amp;quot;;               # 自定义提醒 认证界面会显示
        auth_basic_user_file    /var/www/htpasswd;  # 认证密码文件 htpasswd工具由httpd提供
        deny 127.0.0.1;  #拒绝的ip
        allow 172.18.5.54; #允许的ip 
        allow 192.168.10.100;
     allow 172.29.73.0/24;
     deny all;
    }                                               # 访问http://host/status 就可以看到状态统计了



    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {                    # FastCGI模式
    #    root           /var/www/html;      # php页面根目录
    #    fastcgi_pass   127.0.0.1:9000;     # FastCGI监听地址 也可以使用unix套接字监听地址
    #    fastcgi_index  index.php;          # 索引文件
    #    fastcgi_param  SCRIPT_FILENAME  /var/www/html$fastcgi_script_name;  # php脚本全路径
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache&amp;#39;s document root
    # concurs with nginx&amp;#39;s one
    #
    #location ~ /\.ht {                     # 匹配ht开头的文件
    #    deny  all;                         # 规则是全部拒绝
    #}
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-location-&quot;&gt;&lt;a name=&quot;location 块&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;location 块&lt;/h3&gt;&lt;p&gt;location是在server中的，一个server可以有多个location，用于配置请求的路由，以及各种页面的处理情况。如实现动静分离，请求转发。&lt;/p&gt;
&lt;p&gt;请求中带 /api/ 的转发到 &lt;a href=&quot;http://192.168.76.10:9000&quot;&gt;http://192.168.76.10:9000&lt;/a&gt;;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
        listen       80;
        server_name  localhost;  #服务器域名
        location / {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;

            root   html/default; #网页根目录（/usr/local/nginx/html/default/）
            index  index.html index.htm; 

            # autoindex   on; # 开启索引
        }
        location /api/ {
            proxy_pass  http://192.168.76.10:9000; 
            proxy_set_header Host $host;
        }
}
单独定义 html页面、图片、js脚本等静态资源入口，可简化访问路径

server {
        listen       80;
        server_name  localhost;  
        location / {
            proxy_set_header Host $host; 
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $remote_addr;

            root   html/default; #网页根目录（/usr/local/nginx/html/default/）
            index  index.html index.htm; 
        }
        location /api/ {
            proxy_pass  http://192.168.76.10:9000; # 动态资源，后台接口
            proxy_set_header Host $host;
        }
        location /image/ {
            root  html/default/static;  # 静态资源 - 图片
            # autoindex   on;
        }
        location /page/ {
            root  html/default/static;  # 静态资源 - html页面
            # autoindex   on;
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果请求这个 &lt;a href=&quot;http://192.168.76.100/image/a.png&quot;&gt;http://192.168.76.100/image/a.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;访问图片是 /usr/local/nginx/html/default/static/image/a.png&lt;/p&gt;
&lt;p&gt;如果请求 &lt;a href=&quot;http://192.168.76.100/static/image/a.png&quot;&gt;http://192.168.76.100/static/image/a.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;访问图片 /usr/local/nginx/html/default/static/image/a.png&lt;/p&gt;
&lt;p&gt;即  /usr/local/nginx/ + 配置的root目录 + URL中匹配的资源路径&lt;/p&gt;
&lt;p&gt;root 后面的参数，如果是 / 开始，代表的是 linux 根目录下的目录，非 / 开始代表的是从 nginx 目录下开始&lt;/p&gt;
&lt;p&gt;root 后面的参数，如果是 / 结尾，则 location 后面的参数，就不用以 / 开始，否则必须以 / 开始&lt;/p&gt;
&lt;h3 id=&quot;h3-upstream&quot;&gt;&lt;a name=&quot;upstream&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;upstream&lt;/h3&gt;&lt;p&gt;在location中有一个这个&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;location ~ \.jsp$ {                             # 匹配jsp页面的请求
    index index.jsp                             # 默认索引index.jsp
    proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而 mysvr 就是配置的 upstream。和server平级，一般用于负载均衡。&lt;/p&gt;
&lt;h4 id=&quot;h4-u70EDu5907&quot;&gt;&lt;a name=&quot;热备&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;热备&lt;/h4&gt;&lt;p&gt;如果你有2台服务器，当一台服务器发生事故时，才启用第二台服务器给提供服务。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;upstream mysvr { 
    server 127.0.0.1:7878; 
    server 192.168.10.121:3333 backup;  #热备     
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;h4-u8F6Eu8BE2&quot;&gt;&lt;a name=&quot;轮询&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;轮询&lt;/h4&gt;&lt;p&gt;nginx默认就是轮询其权重都默认为1，服务器处理请求的顺序：ABABABABAB….&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;upstream mysvr { 
    server 192.168.10.121:3333;
    server 192.168.10.122:3333;
}
server {
    ....
    location  ~*^.+$ {         
        proxy_pass  http://mysvr;  #请求转向mysvr 定义的服务器列表         
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;h4-u52A0u6743u8F6Eu8BE2&quot;&gt;&lt;a name=&quot;加权轮询&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;加权轮询&lt;/h4&gt;&lt;p&gt;跟据配置的权重的大小而分发给不同服务器不同数量的请求。如果不设置，则默认为1。下面服务器的请求顺序为：ABBABBABBABBABB….&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;upstream mysvr { 
    server 127.0.0.1:7878 weight=1;
    server 192.168.10.121:3333 weight=2;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;h4-ip_hash&quot;&gt;&lt;a name=&quot;ip_hash&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;ip_hash&lt;/h4&gt;&lt;p&gt;nginx会让相同的客户端ip请求相同的服务器。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;upstream mysvr { 
    server 127.0.0.1:7878; 
    server 192.168.10.121:3333;
    ip_hash;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关于nginx负载均衡配置的几个状态参数讲解。&lt;/p&gt;
&lt;p&gt;down，表示当前的server暂时不参与负载均衡。&lt;br&gt;backup，预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候，才会请求backup机器，因此这台机器的压力最轻。&lt;br&gt;max_fails，允许请求失败的次数，默认为1。当超过最大次数时，返回proxy_next_upstream 模块定义的错误。&lt;br&gt;fail_timeout，在经历了max_fails次失败后，暂停服务的时间。max_fails可以和fail_timeout一起使用。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;upstream mysvr { 
    server 127.0.0.1:7878 weight=2 max_fails=2 fail_timeout=2;
    server 192.168.10.121:3333 weight=1 max_fails=2 fail_timeout=1;    
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-include&quot;&gt;&lt;a name=&quot;include&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;include&lt;/h3&gt;&lt;p&gt;一个nginx可以管理很多服务器，随着需要配置的东西越来越多，配置文件也越来越长。&lt;/p&gt;
&lt;p&gt;所以为了简化文件，可以使用include。&lt;/p&gt;
&lt;p&gt;如把server模块和对应的upstream写在一个文件里，通过引入的方式，可以是配置文件更加清晰。&lt;/p&gt;
&lt;p&gt;如&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;# Load configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-u6700u540E&quot;&gt;&lt;a name=&quot;最后&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;最后&lt;/h3&gt;&lt;p&gt;最后是一个完整的Nginx配置文件。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;user root root; #使用什么用户启动NGINX 在运行时使用哪个用户哪个组
worker_processes 4; #启动进程数，一般是1或8个，根据你的电脑CPU数，一般8个
worker_cpu_affinity 00000001 00000010 00000100 00001000; #CPU逻辑数——把每个进程分别绑在CPU上面，为每个进程分配一个CPU
#pid /usr/local/nginx/logs/nginx.pid
worker_rlimit_nofile 102400; #一个进程打开的最大文件数目，与NGINX并发连接有关系

#工作模式及连接数上限
events
{
  use epoll; #多路复用IO 基于LINUX2.6以上内核，可以大大提高NGINX的性能 uname -a查看内核版本号
  worker_connections 102400; #单个worker process最大连接数,其中NGINX最大连接数＝连接数*进程数,一般1GB内存的机器上可以打开的最大数大约是10万左右
  multi_accept on;   #尽可能多的接受请求，默认是关闭状态
}

#处理http请求的一个应用配置段
http
{
  #引用mime.types,这个类型定义了很多，当web服务器收到静态的资源文件请求时，依据请求文件的后缀名在服务器的MIME配置文件中找到对应的MIME #Type，根据MIMETYPE设置并response响应类型（Content-type）
  include       mime.types;
  default_type  application/octet-stream; #定义的数据流，有的时候默认类型可以指定为text,这跟我们的网页发布还是资源下载是有关系的
  fastcgi_intercept_errors on; #表示接收fastcgi输出的http 1.0 response code
  charset utf-8;
  server_names_hash_bucket_size 128; #保存服务器名字的hash表
  #用来缓存请求头信息的，容量4K，如果header头信息请求超过了，nginx会直接返回400错误，先根据client_header_buffer_size配置的值分配一个buffer，如果##分配的buffer无法容纳request_line/request_header，那么就会##再次根据large_client_header_buffers配置的参数分配large_buffer，如果large_buffer还是无#法容纳，那么就会返回414（处理request_line）/400（处理request_header）错误。
  client_header_buffer_size 4k;
  large_client_header_buffers 4 32k;
  client_max_body_size 300m; #允许客户端请求的最大单文件字节数 上传文件时根据需求设置这个参数
  #指定NGINX是否调用这个函数来输出文件，对于普通的文件我们必须设置为ON，如果NGINX专门做为一个下载端的话可以关掉，好处是降低磁盘与网络的IO处理数及#系统的UPTIME
  sendfile on;
  #autoindex on;开启目录列表访问，适合下载服务器
  tcp_nopush on; #防止网络阻塞
  #非常重要，根据实际情况设置值，超时时间，客户端到服务端的连接持续有效时间，60秒内可避免重新建立连接，时间也不能设太长，太长的话，若请求数10000##，都占用连接会把服务托死
  keepalive_timeout 60;
  tcp_nodelay on; #提高数据的实时响应性
  client_body_buffer_size 512k; #缓冲区代理缓冲用户端请求的最大字节数（请求多）

  proxy_connect_timeout   5; #nginx跟后端服务器连接超时时间（代理连接超时）
  proxy_read_timeout      60; #连接成功后，后端服务器响应时间(代理接收超时)
  proxy_send_timeout      5; #后端服务器数据回传时间(代理发送超时)
  proxy_buffer_size       16k; #设置代理服务器（nginx）保存用户头信息的缓冲区大小
  proxy_buffers           4 64k; #proxy_buffers缓冲区，网页平均在32k以下的话，这样设置
  proxy_busy_buffers_size 128k; #高负荷下缓冲大小
  proxy_temp_file_write_size 128k; #设定缓存文件夹大小，大于这个值，将从upstream服务器传

  gzip on; #NGINX可以压缩静态资源，比如我的静态资源有10M，压缩后只有2M，那么浏览器下载的就少了
  gzip_min_length  1k;
  gzip_buffers     4 16k;
  gzip_http_version 1.1;
  gzip_comp_level 2; #压缩级别大小,最小1,最大9.值越小,压缩后比例越小,CPU处理更快,为1时,原10M压缩完后8M,但设为9时,压缩完可能只有2M了。一般设置为2
  gzip_types       text/plain application/x-javascript text/css application/xml; #压缩类型:text,js css xml 都会被压缩
  gzip_vary on; #作用是在http响应中增加一行目的是改变反向代理服务器的缓存策略

#日志格式
log_format  main &amp;#39;$remote_addr - $remote_user [$time_local] &amp;quot;$request&amp;quot; &amp;#39; #ip 远程用户 当地时间  请求URL
                 &amp;#39;$status $body_bytes_sent &amp;quot;$http_referer&amp;quot; &amp;#39; #状态  发送的大小  响应的头
         &amp;#39;&amp;quot;$http_user_agent&amp;quot; $request_time&amp;#39;; #客户端使用的浏览器  页面响应的时间

#动态转发
upstream web1 {
    #每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。配置了ip_hash就没有负载均衡的效果了，每次访问的都是同一个tomcat
    #ip_hash;
    #转发的后端的tomcat服务器,weight表示转发的权重,越大转发的次数越多,机器性能不一样配置的weight值不一样
     server   127.0.0.1:8080 weight=1 max_fails=2 fail_timeout=30s;
     server   127.0.0.1:8081 weight=1 max_fails=2 fail_timeout=30s;
}
upstream web2 {
     server   127.0.0.1:8090 weight=1 max_fails=2 fail_timeout=30s;
     server   127.0.0.1:8091 weight=1 max_fails=2 fail_timeout=30s;
}

server {
    listen       80; #监听80端口
    server_name  www.dbspread.com; #域名
    index  index.jsp index.html index.htm;
    root   /usr/local/nginx/html; #定义服务器的默认网站根目录位置

    #监听完成以后通过斜杆(/)拦截请求转发到后端的tomcat服务器
    location /
        {
            #如果后端的服务器返回502、504、执行超时等错误，自动将请求转发到upstream负载均衡池中的另一台服务器，实现故障转移。
            proxy_next_upstream http_502 http_504 error timeout invalid_header;
            proxy_set_header Host  $host; #获取客户端的主机名存到变量Host里面,从而让tomcat取到客户端机器的信息
            proxy_set_header X-Real-IP $remote_addr; #获取客户端的主机名存到变量X-Real-IP里面,从而让tomcat取到客户端机器的信息
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://web1; #跳转到对应的应用web1
        }

       # location ~ .*\.(php|jsp|cgi|shtml)?$ #动态分离 ~匹配 以.*结尾（以PHP JSP结尾走这段）
       #  {
       #     proxy_set_header Host  $host;
       #        proxy_set_header X-Real-IP $remote_addr;
       #        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       #        proxy_pass http://jvm_web2;
       # }

        #静态分离 ~匹配 以.*结尾（以PHP JSP结尾走这段），当然不是越久越好，如果有10000个用户在线，都保存几个月，系统托跨
        location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)$
        {
            root /var/local/static;
            expires    30d;
        }

        #日志级别有[debug|info|notice|warn|error|crit]  error_log 级别分为 debug, info, notice, warn, error, crit  默认为crit, 生产环境用error
        #crit 记录的日志最少,而debug记录的日志最多
        access_log  /usr/local/logs/web2/access.log main;
        error_log   /usr/local/logs/web2/error.log  crit;

    }

}&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Thu, 30 Mar 2023 18:56:16 +0800</pubDate></item><item><title>安卓远程投屏</title><link>https://68686.ltd/?id=65</link><description>&lt;p&gt;使用的工具是scrcpy和frp，在电脑上下载scrcpy客户端，然后在手机上下载frp安卓版，打开无线调试，配置frp进行穿透.&lt;br&gt;&lt;a href=&quot;https://github.com/Genymobile/scrcpy&quot; title=&quot;scrcpy链接&quot;&gt;scrcpy链接&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://github.com/AceDroidX/frp-Android&quot; title=&quot;frp安卓版&quot;&gt;frp安卓版&lt;/a&gt;&lt;br&gt;在用scrcpy之前，需要先连接到手机&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adb connect ip:port
adb devices
scrcpy&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;投屏的画质取决于服务器的带宽&lt;/p&gt;
</description><pubDate>Wed, 19 Oct 2022 10:31:50 +0800</pubDate></item><item><title>判断点是否在区域内</title><link>https://68686.ltd/?id=62</link><description>&lt;p&gt;MapPoint.java&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class MapPoint {
    /**
     * 经度
     */
    private double lng;
    /**
     * 纬度
     */
    private double lat;

    public MapPoint() {

    }

    public MapPoint(double lng, double lat) {
        this.lng = lng;
        this.lat = lat;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MapPoint) {
            MapPoint mapPoint = (MapPoint) obj;
            return (mapPoint.getLng() == lng &amp;amp;&amp;amp; mapPoint.getLat() == lat) ? true : false;
        } else {
            return false;
        }
    }

    public double getLng() {
        return lng;
    }

    public void setLng(double lng) {
        this.lng = lng;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double lat) {
        this.lat = lat;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;GraphUtils.java&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public class GraphUtils {
    /**
     * 判断点是否在多边形内(基本思路是用交点法)
     *
     * @param point
     * @param boundaryPoints
     * @return
     */
    public static boolean isPointInPolygon(MapPoint point, MapPoint[] boundaryPoints) {
        // 防止第一个点与最后一个点相同  
        if (boundaryPoints != null &amp;amp;&amp;amp; boundaryPoints.length &amp;gt; 0
                &amp;amp;&amp;amp; boundaryPoints[boundaryPoints.length - 1].equals(boundaryPoints[0])) {
            boundaryPoints = Arrays.copyOf(boundaryPoints, boundaryPoints.length - 1);
        }
        int pointCount = boundaryPoints.length;

        // 首先判断点是否在多边形的外包矩形内，如果在，则进一步判断，否则返回false  
        if (!isPointInRectangle(point, boundaryPoints)) {
            return false;
        }

        // 如果点与多边形的其中一个顶点重合，那么直接返回true  
        for (int i = 0; i &amp;lt; pointCount; i++) {
            if (point.equals(boundaryPoints[i])) {
                return true;
            }
        }

        /**
         * 基本思想是利用X轴射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则在多边形内。还会考虑一些特殊情况，如点在多边形顶点上 
         * ， 点在多边形边上等特殊情况。 
         */
        // X轴射线与多边形的交点数  
        int intersectPointCount = 0;
        // X轴射线与多边形的交点权值  
        float intersectPointWeights = 0;
        // 浮点类型计算时候与0比较时候的容差  
        double precision = 2e-10;
        // 边P1P2的两个端点  
        MapPoint point1 = boundaryPoints[0], point2;
        // 循环判断所有的边  
        for (int i = 1; i &amp;lt;= pointCount; i++) {
            point2 = boundaryPoints[i % pointCount];

            /**
             * 如果点的y坐标在边P1P2的y坐标开区间范围之外，那么不相交。 
             */
            if (point.getLat() &amp;lt; Math.min(point1.getLat(), point2.getLat())
                    || point.getLat() &amp;gt; Math.max(point1.getLat(), point2.getLat())) {
                point1 = point2;
                continue;
            }
            /**
             * 此处判断射线与边相交 
             */
            if (point.getLat() &amp;gt; Math.min(point1.getLat(), point2.getLat())
                    // 如果点的y坐标在边P1P2的y坐标开区间内
                    &amp;amp;&amp;amp; point.getLat() &amp;lt; Math.max(point1.getLat(), point2.getLat())) {
                // 若边P1P2是垂直的
                if (point1.getLng() == point2.getLng()) {
                    if (point.getLng() == point1.getLng()) {
                        // 若点在垂直的边P1P2上，则点在多边形内  
                        return true;
                    } else if (point.getLng() &amp;lt; point1.getLng()) {
                        // 若点在在垂直的边P1P2左边，则点与该边必然有交点  
                        ++intersectPointCount;
                    }
                } else {// 若边P1P2是斜线
                    // 点point的x坐标在点P1和P2的左侧
                    if (point.getLng() &amp;lt;= Math.min(point1.getLng(), point2.getLng())) {
                        ++intersectPointCount;
                    }
                    // 点point的x坐标在点P1和P2的x坐标中间
                    else if (point.getLng() &amp;gt; Math.min(point1.getLng(), point2.getLng())
                            &amp;amp;&amp;amp; point.getLng() &amp;lt; Math.max(point1.getLng(), point2.getLng())) {
                        double slopeDiff = getSlopeDiff(point, point1, point2);
                        if (slopeDiff &amp;gt; 0) {
                            // 由于double精度在计算时会有损失，故匹配一定的容差。经试验，坐标经度可以达到0.0001
                            if (slopeDiff &amp;lt; precision) {
                                // 点在斜线P1P2上  
                                return true;
                            } else {
                                // 点与斜线P1P2有交点  
                                intersectPointCount++;
                            }
                        }
                    }
                }
            } else {
                // 边P1P2水平  
                if (point1.getLat() == point2.getLat()) {
                    if (checkPointInLine(point, point1, point2)) {
                        return true;
                    }
                }
                /**
                 * 判断点通过多边形顶点 
                 */
                if (((point.getLat() == point1.getLat() &amp;amp;&amp;amp; point.getLng() &amp;lt; point1.getLng()))
                        || (point.getLat() == point2.getLat() &amp;amp;&amp;amp; point.getLng() &amp;lt; point2.getLng())) {
                    if (point2.getLat() &amp;lt; point1.getLat()) {
                        intersectPointWeights += -0.5;
                    } else if (point2.getLat() &amp;gt; point1.getLat()) {
                        intersectPointWeights += 0.5;
                    }
                }
            }
            point1 = point2;
        }
        // 偶数在多边形外
        if ((intersectPointCount + Math.abs(intersectPointWeights)) % 2 == 0) {
            return false;
        } else { // 奇数在多边形内  
            return true;
        }
    }

    private static double getSlopeDiff(MapPoint point, MapPoint point1, MapPoint point2) {
        double slopeDiff = 0.0d;
        if (point1.getLat() &amp;gt; point2.getLat()) {
            slopeDiff = (point.getLat() - point2.getLat()) / (point.getLng() - point2.getLng())
                    - (point1.getLat() - point2.getLat()) / (point1.getLng() - point2.getLng());
        } else {
            slopeDiff = (point.getLat() - point1.getLat()) / (point.getLng() - point1.getLng())
                    - (point2.getLat() - point1.getLat()) / (point2.getLng() - point1.getLng());
        }
        return slopeDiff;
    }

    private static boolean checkPointInLine(MapPoint point, MapPoint point1, MapPoint point2) {
        if (point.getLng() &amp;lt;= Math.max(point1.getLng(), point2.getLng())
                &amp;amp;&amp;amp; point.getLng() &amp;gt;= Math.min(point1.getLng(), point2.getLng())) {
            // 若点在水平的边P1P2上，则点在多边形内
            return true;
        }
        return false;
    }

    /**
     * 判断点是否在矩形内在矩形边界上，也算在矩形内(根据这些点，构造一个外包矩形)
     *
     * @param point          点对象
     * @param boundaryPoints 矩形边界点
     * @return
     */
    public static boolean isPointInRectangle(MapPoint point, MapPoint[] boundaryPoints) {
        // 西南角点
        MapPoint southWestPoint = getSouthWestPoint(boundaryPoints);
        // 东北角点
        MapPoint northEastPoint = getNorthEastPoint(boundaryPoints);
        return (point.getLng() &amp;gt;= southWestPoint.getLng() &amp;amp;&amp;amp; point.getLng() &amp;lt;= northEastPoint.getLng()
                &amp;amp;&amp;amp; point.getLat() &amp;gt;= southWestPoint.getLat() &amp;amp;&amp;amp; point.getLat() &amp;lt;= northEastPoint.getLat());

    }

    /**
     * 根据这组坐标，画一个矩形，然后得到这个矩形西南角的顶点坐标
     *
     * @param vertexs
     * @return
     */
    private static MapPoint getSouthWestPoint(MapPoint[] vertexs) {
        double minLng = vertexs[0].getLng(), minLat = vertexs[0].getLat();
        for (MapPoint bmapPoint : vertexs) {
            double lng = bmapPoint.getLng();
            double lat = bmapPoint.getLat();
            if (lng &amp;lt; minLng) {
                minLng = lng;
            }
            if (lat &amp;lt; minLat) {
                minLat = lat;
            }
        }
        return new MapPoint(minLng, minLat);
    }

    /**
     * 根据这组坐标，画一个矩形，然后得到这个矩形东北角的顶点坐标
     *
     * @param vertexs
     * @return
     */
    private static MapPoint getNorthEastPoint(MapPoint[] vertexs) {
        double maxLng = 0.0d, maxLat = 0.0d;
        for (MapPoint bmapPoint : vertexs) {
            double lng = bmapPoint.getLng();
            double lat = bmapPoint.getLat();
            if (lng &amp;gt; maxLng) {
                maxLng = lng;
            }
            if (lat &amp;gt; maxLat) {
                maxLat = lat;
            }
        }
        return new MapPoint(maxLng, maxLat);
    }

}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用方式&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;GraphUtils.isPointInPolygon(MapPoint point,List&amp;lt;MapPoint&amp;gt; boundaryPoints);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回true为在区域内，返回false为不在区域内&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;转载自&lt;a href=&quot;https://blog.csdn.net/weixin_33005117/article/details/125841620&quot;&gt;https://blog.csdn.net/weixin_33005117/article/details/125841620&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description><pubDate>Wed, 24 Aug 2022 08:30:12 +0800</pubDate></item><item><title>MybatisPlus 3.5.2</title><link>https://68686.ltd/?id=61</link><description>&lt;p&gt;依赖&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-pom&quot;&gt;&amp;lt;!--mybatis-plus--&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.baomidou&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;mybatis-plus-boot-starter&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.5.2&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;//分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
    return interceptor;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自动代码生成&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;FastAutoGenerator
                // 数据源配置
                .create(&amp;quot;jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=Asia/Shanghai&amp;quot;,
                        &amp;quot;root&amp;quot;, &amp;quot;123456&amp;quot;)

                // 全局配置
                .globalConfig(builder -&amp;gt; {
                    builder.author(&amp;quot;tao&amp;quot;) // 设置作者
//                            .enableSwagger() // 开启 swagger 模式
                            .commentDate(&amp;quot;yyyy-MM-dd HH:mm:ss&amp;quot;) // 注释日期格式
                            .outputDir(&amp;quot;.\\src\\main\\java\\&amp;quot;) // 指定输出目录
                            .disableOpenDir(); // 禁止打开输出目录
                })

                // 包配置
                .packageConfig(builder -&amp;gt; {
                    builder.parent(&amp;quot;com.tao&amp;quot;) // 设置父包名
                            .moduleName(&amp;quot;mybatisplus&amp;quot;) // 设置父包模块名
//                            .entity(&amp;quot;pojo&amp;quot;) // pojo实体类包名，其他包名同理
                            .pathInfo(Collections.singletonMap(OutputFile.xml, &amp;quot;src\\main\\resources\\mapper\\&amp;quot;)); // 设置mapperXml生成路径
                })

                // 策略配置
                .strategyConfig(builder -&amp;gt; {
                    builder.addInclude(&amp;quot;user&amp;quot;)// 设置需要生成的表名
                            .addTablePrefix() //设置过滤表前缀
                            // entity配置
                            .entityBuilder()
                            .enableFileOverride()
                            .enableLombok() // 使用Lombok
                            .logicDeleteColumnName(&amp;quot;delete_time&amp;quot;) // 逻辑删除字段名
                            .naming(NamingStrategy.underline_to_camel) // 数据库表映射到实体的命名策略：下划线转驼峰命名
                            .columnNaming(NamingStrategy.underline_to_camel) // 数据库表字段映射到实体的命名策略：下划线转驼峰命名
                            .addTableFills(
                                    new Column(&amp;quot;create_time&amp;quot;, FieldFill.INSERT),
                                    new Column(&amp;quot;update_time&amp;quot;, FieldFill.INSERT_UPDATE)
                            ) //添加表字段填充，&amp;quot;create_time&amp;quot;字段自动填充为插入时间，&amp;quot;modify_time&amp;quot;字段自动填充为插入修改时间
                            .enableTableFieldAnnotation() // 开启生成实体时生成字段注解
                            .idType(IdType.AUTO) // 标记实例类的主键生成方式，如果插入时没有指定，刚自动分配一个，默认是雪花算法

                            //mapper配置
                            .mapperBuilder()
                            .enableFileOverride()
                            .superClass(BaseMapper.class) // 设置父类
                            .formatMapperFileName(&amp;quot;%sMapper&amp;quot;) // 格式化mapper文件名
                            .mapperAnnotation(Mapper.class) // 添加@Mapper注解

                            //service配置
                            .serviceBuilder()
                            .enableFileOverride()
                            .formatServiceFileName(&amp;quot;%sService&amp;quot;) // 格式化Service文件名，如:UserService
                            .formatServiceImplFileName(&amp;quot;%sServiceImpl&amp;quot;) // 格式化ServiceImpl文件名，如:UserServiceImpl
//                            .convertServiceFileName(entityName -&amp;gt; entityName + &amp;quot;Service&amp;quot;)
//                            .convertServiceImplFileName(entityName -&amp;gt; entityName + &amp;quot;ServiceImpl&amp;quot;)
                            .superServiceClass(IService.class) // 设置父类
                            .superServiceImplClass(ServiceImpl.class) // 设置父类

                            //controller配置
                            .controllerBuilder()
                            .enableFileOverride()
                            .formatFileName(&amp;quot;%sController&amp;quot;) // 格式化Controller文件名
                            .enableRestStyle() // 开启生成 @RestController 控制器
                            .build();
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板，默认的是Velocity引擎模板
                .execute();&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Mon, 22 Aug 2022 21:06:47 +0800</pubDate></item><item><title>Mysql基础</title><link>https://68686.ltd/?id=60</link><description>&lt;h1 id=&quot;h1-sql&quot;&gt;&lt;a name=&quot;SQL&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;SQL&lt;/h1&gt;&lt;h2 id=&quot;h2-ddl&quot;&gt;&lt;a name=&quot;DDL&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;DDL&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#如果不存在则创建数据库
create database if not exists user default charset utf8mb4 collate utf8mb4_general_ci;
#查看所有数据库
show databases;
#使用数据库
use user;
#查看当前使用的是哪个数据库
select database();
#创建表
create table users (
    id int,
    name varchar(20) comment &amp;#39;年龄&amp;#39;,
    age int,
    birth datetime
);
#查看所有表
show tables;
#查看创建表的语句
show create table user;
#查看表结构
desc user;
#添加新的字段
alter table user add email varchar(30);
#修改字段的数据类型
alter table user modify age tinyint unsigned comment &amp;#39;年龄&amp;#39;;
#修改字段名和字段的数据类型
alter table user change birth birthday date comment &amp;#39;生日&amp;#39;;
#删除字段
alter table user drop email;
#重命名表名
alter table user rename to user;
#截断表
truncate table user;
#如果存在则删除表
drop table if exists user;
#删库跑路
drop database if exists user;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-dml&quot;&gt;&lt;a name=&quot;DML&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;DML&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#给指定字段添加数据
insert into 表名(字段名1, 字段名2) values(值1, 值2, ...);
#给全部字段添加数据
insert into 表名 values(值1, 值2, ...);
#给指定字段添加多条数据
insert into 表名(字段名1, 字段名2) values(值1, 值2, ...),(值1, 值2, ...);
#添加多条数据
insert into 表名 values(值1, 值2, ...),(值1, 值2, ...);
#修改数据
update 表名 set 字段名1 = 值1, 字段名2 = 值2, ... [where 条件];
#删除数据
delete from 表名 [where 条件];
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-dql&quot;&gt;&lt;a name=&quot;DQL&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;DQL&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select
    字段列表
from
    表名列表
where
    条件列表
group by
    分组字段列表
having
    分组后条件列表
order by
    排序字段列表
limit
    分页参数&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#查询多个字段
select 字段1, 字段2, ... from 表名;
select * from 表名;
#设置别名
select 字段1 [as 别名1] from 表名;
#去除重复记录
select distinct 字段列表 from 表名;

select * from user where name like &amp;quot;王%&amp;quot;;
select * from user where name like &amp;quot;__&amp;quot;;
select * from user where name is null;
select * from user where age in (20, 21, 22);
select * from user where age between 20 and 22;

select count(*) from sc;
select max(score) from sc;
select min(score) from sc;
select avg(score) from sc;
select sum(score) from sc;

select gender, count(*) from emp group by gender;
select gender, avg(age) from emp group by gender;
select workaddress, count(*) from emp where age &amp;lt; 30 group by workaddress;
select workaddress, count(*) num from emp where age &amp;lt; 30 group by workaddress having num &amp;gt;= 3;

select * from emp order by age [asc];
select * from emp order by age desc;
select * from emp order by age desc, entrydate desc;
#查询前10条记录
select * from emp limit [0,] 10;
#查询索引从3开始的10条记录
select * from emp limit 3, 10;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-dcl&quot;&gt;&lt;a name=&quot;DCL&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;DCL&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;use mysql;
#查询所有用户
select * from user;
#创建用户
create user &amp;#39;tao&amp;#39;@&amp;#39;localhost&amp;#39; identified by &amp;#39;123456&amp;#39;;
create user &amp;#39;test&amp;#39;@&amp;#39;%&amp;#39; identified by &amp;#39;test&amp;#39;;
#修改用户密码
alter user &amp;#39;test&amp;#39;@&amp;#39;%&amp;#39; identified with mysql_native_password by &amp;#39;123456&amp;#39;;
#删除用户
drop user &amp;#39;test&amp;#39;@&amp;#39;%&amp;#39;;

#查询用户的权限
show grants for &amp;#39;用户名&amp;#39;@&amp;#39;主机名&amp;#39;;
#授予权限
grant 权限列表 on 数据库名.表名 to &amp;#39;用户名&amp;#39;@&amp;#39;主机名&amp;#39;;
#撤销权限
revoke 权限列表 on 数据库名.表名 from &amp;#39;用户名&amp;#39;@&amp;#39;主机名&amp;#39;;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-u51FDu6570&quot;&gt;&lt;a name=&quot;函数&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;函数&lt;/h1&gt;&lt;h2 id=&quot;h2-u5B57u7B26u4E32u51FDu6570&quot;&gt;&lt;a name=&quot;字符串函数&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;字符串函数&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;concat(s1, s2, …, sn)&lt;/td&gt;
&lt;td&gt;字符串拼接，将所有字符串拼接成一个字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lower(s)&lt;/td&gt;
&lt;td&gt;将字符串s全部转成小写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;upper(s)&lt;/td&gt;
&lt;td&gt;将字符串s全部转成大写&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;lpad(s, n, pad)&lt;/td&gt;
&lt;td&gt;左填充，用字符串pad对s的左边进行填充，达到n个字符串长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rpad(s, n, pad)&lt;/td&gt;
&lt;td&gt;右填充，用字符串pad对s的右边进行填充，达到n个字符串长度&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trim(s)&lt;/td&gt;
&lt;td&gt;去掉字符串s头部和尾部的空格&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;substring(s, start, len)&lt;/td&gt;
&lt;td&gt;返回字符串s从start位置开始长度为len的字符串&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-u6570u503Cu51FDu6570&quot;&gt;&lt;a name=&quot;数值函数&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;数值函数&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ceil(x)&lt;/td&gt;
&lt;td&gt;向上取整&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;floor(x)&lt;/td&gt;
&lt;td&gt;向下取整&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;mod(x, y)&lt;/td&gt;
&lt;td&gt;返回x/y的模&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;rand()&lt;/td&gt;
&lt;td&gt;返回0~1的随机数&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;round(x, y)&lt;/td&gt;
&lt;td&gt;求参数x的四舍五入的值，保留y位小数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-u65E5u671Fu51FDu6570&quot;&gt;&lt;a name=&quot;日期函数&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;日期函数&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;curdate()&lt;/td&gt;
&lt;td&gt;返回当前日期&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;curtime()&lt;/td&gt;
&lt;td&gt;返回当前时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;now()&lt;/td&gt;
&lt;td&gt;返回当前日期和时间&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;year(date)&lt;/td&gt;
&lt;td&gt;获取指定date的年份&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;month(date)&lt;/td&gt;
&lt;td&gt;获取指定date的月份&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;day(date)&lt;/td&gt;
&lt;td&gt;获取指定date的日期&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;date_add(date, interval expr type)&lt;/td&gt;
&lt;td&gt;返回一个日期/时间值加上一个时间间隔expr后的时间值&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;datediff(date1, date2)&lt;/td&gt;
&lt;td&gt;返回起始时间date1和结束时间date2之间的天数&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-u6D41u7A0Bu51FDu6570&quot;&gt;&lt;a name=&quot;流程函数&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;流程函数&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;函数&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;if(value, t, f)&lt;/td&gt;
&lt;td&gt;如果value为true，返回t，否则返回f&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ifnull(value1, value2)&lt;/td&gt;
&lt;td&gt;如果value不为null，返回value1，否则返回value2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;case when [val1] then [res1] …else [default] end&lt;/td&gt;
&lt;td&gt;如果val1为true，返回res1，否则返回default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;case [expr] when [val1] then [res1] …else [default] end&lt;/td&gt;
&lt;td&gt;如果expr的值为val1，返回res1，否则返回会default&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;h1-u7EA6u675F&quot;&gt;&lt;a name=&quot;约束&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;约束&lt;/h1&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;约束&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;th&gt;关键字&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;非空约束&lt;/td&gt;
&lt;td&gt;限制该字段的数据不能为null&lt;/td&gt;
&lt;td&gt;not null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;唯一约束&lt;/td&gt;
&lt;td&gt;保证该字段的所有数据都是唯一、不重复的&lt;/td&gt;
&lt;td&gt;unique&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;主键约束&lt;/td&gt;
&lt;td&gt;主键是一行数据的唯一标识，要求非空且唯一&lt;/td&gt;
&lt;td&gt;primary key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;默认约束&lt;/td&gt;
&lt;td&gt;保存数据时，如果未指定该字段的值，则采用默认值&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;检查约束(8.0.16以后)&lt;/td&gt;
&lt;td&gt;保证字段值满足某一个条件&lt;/td&gt;
&lt;td&gt;check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;外键约束&lt;/td&gt;
&lt;td&gt;用来让两张表的数据之间建立连接，保证数据的一致性和完整性&lt;/td&gt;
&lt;td&gt;foreign key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;create table user (
    id int primary key auto_increment comment &amp;#39;主键&amp;#39;,
    name varchar(10) not null unique,
    age int check (age &amp;gt; 0 &amp;amp;&amp;amp; age &amp;lt;= 120),
    status char(1) default &amp;#39;1&amp;#39;,
    gender char(1)
);

#添加外键
create table 表名 (
    字段名 数据类型
    [constraint] [外键名称] foreign key (外键字段名) referemce 主表(主表列名)
);
alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(主表列名);
#删除外键
alter table 表名 drop foreign key 外键名称; 
#外键的更新和删除行为
alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(主表列名) on update cascade on delete cascade;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-u591Au8868u67E5u8BE2&quot;&gt;&lt;a name=&quot;多表查询&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;多表查询&lt;/h1&gt;&lt;h2 id=&quot;h2-u5185u8FDEu63A5&quot;&gt;&lt;a name=&quot;内连接&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;内连接&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#隐式内连接
select 字段列表 from 表1, 表2 where 条件 ...;
#显式内连接
select 字段列表 from 表1 [inner] join 表2 on 连接条件 ...;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-u5916u8FDEu63A5&quot;&gt;&lt;a name=&quot;外连接&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;外连接&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#左外连接
select 字段列表 from 表1 left [outer] join 表2 on 连接条件 ...;
#右外连接
select 字段列表 from 表1 right [outer] join 表2 on 连接条件 ...;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-u81EAu8FDEu63A5&quot;&gt;&lt;a name=&quot;自连接&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;自连接&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select 字段列表 from 表1 别名1 join 表2 别名2 on 连接条件 ...;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-u8054u5408u67E5u8BE2&quot;&gt;&lt;a name=&quot;联合查询&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;联合查询&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#union会对合并后的数据进行去重，而union all不会
select 字段列表 from 表1 ...
union [all]
select 字段列表 from 表2 ...;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-u5B50u67E5u8BE2&quot;&gt;&lt;a name=&quot;子查询&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;子查询&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select * from t1 where column1 = (select column1 from t2);

#标量子查询
select * from emp where dept_id = (select id from dept where name = &amp;#39;研发部&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;列子查询&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;操作符&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;in&lt;/td&gt;
&lt;td&gt;在指定集合范围内，多选一&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;not in&lt;/td&gt;
&lt;td&gt;不再指定集合范围内&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;子查询返回列表中，有任意一个条件满足即可&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;some&lt;/td&gt;
&lt;td&gt;与any相同，使用some的地方都可以用any&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;all&lt;/td&gt;
&lt;td&gt;子查询返回的列表的所有值都必须满足&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select * from emp where salary &amp;gt; all(select salary from emp where dept_id = (select id from dept where name = &amp;#39;研发部&amp;#39;));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;行子查询&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select * from emp where (salary, managerid) = (select salary, managerid from emp where name = &amp;#39;小明&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表子查询&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;select * from emp where (job, salary) in (select job, salary from emp where name = &amp;#39;小明&amp;#39; or name = &amp;#39;小红&amp;#39;);&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-u4E8Bu52A1&quot;&gt;&lt;a name=&quot;事务&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;事务&lt;/h1&gt;&lt;h2 id=&quot;h2-u4E8Bu52A1u5F00u542Fu65B9u5F0F&quot;&gt;&lt;a name=&quot;事务开启方式&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;事务开启方式&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#查看事务提交方式
select @@autocommit;
#设置事务提交方式
set @@autocommit = 0;
#提交事务
commit;
#回滚事务
rollback;

#开启事务
start transaction;
begin;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-u4E8Bu52A1u7684u56DBu5927u7279u6027&quot;&gt;&lt;a name=&quot;事务的四大特性&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;事务的四大特性&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;原子性（Atomicity):事务是不可分割的最小操作单元，要么全部成功，要么全部失败。&lt;/li&gt;&lt;li&gt;一致性（Consistency) :事务完成时，必须使所有的数据都保持一致状态。&lt;/li&gt;&lt;li&gt;隔离性（Isolation):数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环境下运行。&lt;/li&gt;&lt;li&gt;持久性(Durability)︰事务一旦提交或回滚，它对数据库中的数据的改变就是永久的。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-u5E76u53D1u4E8Bu52A1u95EEu9898&quot;&gt;&lt;a name=&quot;并发事务问题&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;并发事务问题&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;问题&lt;/th&gt;
&lt;th&gt;描述&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;脏读&lt;/td&gt;
&lt;td&gt;一个事务读到另外一个事务还没有提交的数据&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;不可重复读&lt;/td&gt;
&lt;td&gt;一个事务先后读取到一条记录，但两次读取的数据不同&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;幻读&lt;/td&gt;
&lt;td&gt;一个事务按照条件查询数据时，没有对应的行，但是在插入数据时，又发现这行数据已经存在&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-u4E8Bu52A1u9694u79BBu7EA7u522B&quot;&gt;&lt;a name=&quot;事务隔离级别&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;事务隔离级别&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;隔离级别&lt;/th&gt;
&lt;th&gt;脏读&lt;/th&gt;
&lt;th&gt;不可重复读&lt;/th&gt;
&lt;th&gt;幻读&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;读未提交&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;读已提交&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;可重复读(默认)&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;串行化&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;pre&gt;&lt;code class=&quot;language-mysql&quot;&gt;#查询事务隔离级别
select @@transaction_isolation;
#设置事务隔离级别
set [session|global] transaction isolation level {read uncommitted|read committed|repeatable read|serializable};&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Fri, 05 Aug 2022 15:05:00 +0800</pubDate></item><item><title>网络编程</title><link>https://68686.ltd/?id=58</link><description>&lt;h1 id=&quot;h1-1-&quot;&gt;&lt;a name=&quot;1. 网络编程概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1. 网络编程概述&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;Java是 Internet 上的语言，它从语言级上提供了对网络应用程 序的支持，程序员能够很容易开发常见的网络应用程序。&lt;/li&gt;&lt;li&gt;Java提供的网络类库，可以实现无痛的网络连接，联网的底层 细节被隐藏在 Java 的本机安装系统里，由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库，程序员面对的是一个统一 的网络编程环境。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-1-1-&quot;&gt;&lt;a name=&quot;1.1 网络基础&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.1 网络基础&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;计算机网络： 把分布在不同地理区域的计算机与专门的外部设备用通信线路互连成一个规 模大、功能强的网络系统，从而使众多的计算机可以方便地互相传递信息、 共享硬件、软件、数据信息等资源。&lt;/li&gt;&lt;li&gt;网络编程的目的： &lt;ul&gt;
&lt;li&gt;直接或间接地通过网络协议与其它计算机实现数据交换，进行通讯。 &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;网络编程中有两个主要的问题：&lt;ul&gt;
&lt;li&gt;如何准确地定位网络上一台或多台主机；定位主机上的特定的应用&lt;/li&gt;&lt;li&gt;找到主机后如何可靠高效地进行数据传输&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-2-&quot;&gt;&lt;a name=&quot;2. 网络通信要素概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2. 网络通信要素概述&lt;/h1&gt;&lt;h2 id=&quot;h2-2-1-&quot;&gt;&lt;a name=&quot;2.1 如何实现网络中的主机互相通信&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.1 如何实现网络中的主机互相通信&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;通信双方地址 &lt;ul&gt;
&lt;li&gt;IP&lt;/li&gt;&lt;li&gt;端口号&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;一定的规则（即：网络通信协议。有两套参考模型）&lt;ul&gt;
&lt;li&gt;OSI参考模型：模型过于理想化，未能在因特网上进行广泛推广&lt;/li&gt;&lt;li&gt;TCP/IP参考模型(或TCP/IP协议)：事实上的国际标准。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-2-2-&quot;&gt;&lt;a name=&quot;2.2 网络通信协议&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.2 网络通信协议&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OSI参考模型&lt;/th&gt;
&lt;th&gt;TCP/IP参考模型&lt;/th&gt;
&lt;th&gt;TCP/IP参考模型各层对应协议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;应用层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;表示层&lt;/td&gt;
&lt;td&gt;应用层&lt;/td&gt;
&lt;td&gt;HTTP、FTP、Telnet、DNS…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;会话层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;传输层&lt;/td&gt;
&lt;td&gt;传输层&lt;/td&gt;
&lt;td&gt;TCP、UDP…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;网络层&lt;/td&gt;
&lt;td&gt;网络层&lt;/td&gt;
&lt;td&gt;IP、ICMP、ARP…&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据链路层&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;物理层&lt;/td&gt;
&lt;td&gt;物理+数据链路层&lt;/td&gt;
&lt;td&gt;Link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h1 id=&quot;h1-3-1-ip-&quot;&gt;&lt;a name=&quot;3. 通信要素1：IP和端口号&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3. 通信要素1：IP和端口号&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;IP 地址：InetAddress&lt;/li&gt;&lt;li&gt;唯一的标识 Internet 上的计算机（通信实体）&lt;/li&gt;&lt;li&gt;本地回环地址(hostAddress)：127.0.0.1 主机名(hostName)：localhost&lt;/li&gt;&lt;li&gt;IP地址分类方式1：IPV4 和 IPV6&lt;ul&gt;
&lt;li&gt;IPV4：4个字节组成，4个0-255。大概42亿，30亿都在北美，亚洲4亿。2011年初已 经用尽。以点分十进制表示，如192.168.0.1&lt;/li&gt;&lt;li&gt;IPV6：128位（16个字节），写成8个无符号整数，每个整数用四个十六进制位表示， 数之间用冒号（：）分开&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;IP地址分类方式2：公网地址(万维网使用)和私有地址(局域网使用)。192.168. 开头的就是私有址址，范围即为192.168.0.0–192.168.255.255，专门为组织机 构内部使用 &lt;/li&gt;&lt;li&gt;特点：不易记忆&lt;/li&gt;&lt;li&gt;端口号标识正在计算机上运行的进程（程序）&lt;ul&gt;
&lt;li&gt;不同的进程有不同的端口号 &lt;/li&gt;&lt;li&gt;被规定为一个 16 位的整数 0~65535。&lt;/li&gt;&lt;li&gt;端口分类：&lt;ul&gt;
&lt;li&gt;公认端口：0~1023。被预先定义的服务通信占用（如：HTTP占用端口 80，FTP占用端口21，Telnet占用端口23）&lt;/li&gt;&lt;li&gt;注册端口：1024~49151。分配给用户进程或应用程序。（如：Tomcat占 用端口8080，MySQL占用端口3306，Oracle占用端口1521等）。&lt;/li&gt;&lt;li&gt;动态/私有端口：49152~65535。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;端口号与IP地址的组合得出一个网络套接字：Socket。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-3-1-inetaddress-&quot;&gt;&lt;a name=&quot;3.1 InetAddress类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.1 InetAddress类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Internet上的主机有两种方式表示地址：&lt;/li&gt;&lt;li&gt;域名(hostName)：&lt;a href=&quot;http://www.baidu.com&quot;&gt;www.baidu.com&lt;/a&gt;&lt;/li&gt;&lt;li&gt;IP 地址(hostAddress)：203.112.35.210&lt;/li&gt;&lt;li&gt;InetAddress类主要表示IP地址，两个子类：Inet4Address、Inet6Address。 &lt;/li&gt;&lt;li&gt;InetAddress 类 对 象 含 有 一 个 Internet 主 机 地 址 的 域 名 和 IP 地 址 ： &lt;a href=&quot;http://www.baidu.com&quot;&gt;www.baidu.com&lt;/a&gt; 和 203.112.35.210。&lt;/li&gt;&lt;li&gt;域名容易记忆，当在连接网络时输入一个主机的域名后，域名服务器(DNS) 负责将域名转化成IP地址，这样才能和主机建立连接。 ——-域名解析&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-4-2-&quot;&gt;&lt;a name=&quot;4. 通信要素2：网络协议&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4. 通信要素2：网络协议&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;网络通信协议 计算机网络中实现通信必须有一些约定，即通信协议，对速率、传输代码、代 码结构、传输控制步骤、出错控制等制定标准。&lt;/li&gt;&lt;li&gt;问题：网络协议太复杂 计算机网络通信涉及内容很多，比如指定源地址和目标地址，加密解密，压缩 解压缩，差错控制，流量控制，路由控制，如何实现如此复杂的网络协议呢？&lt;/li&gt;&lt;li&gt;通信协议分层的思想 在制定协议时，把复杂成份分解成一些简单的成份，再将它们复合起来。最常 用的复合方式是层次方式，即同层间可以通信、上一层可以调用下一层，而与 再下一层不发生关系。各层互不影响，利于系统的开发和扩展。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-4-1-tcp-ip-&quot;&gt;&lt;a name=&quot;4.1 TCP/IP协议簇&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.1 TCP/IP协议簇&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;传输层协议中有两个非常重要的协议：&lt;ul&gt;
&lt;li&gt;传输控制协议TCP(Transmission Control Protocol) &lt;/li&gt;&lt;li&gt;用户数据报协议UDP(User Datagram Protocol)。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;TCP/IP 以其两个主要协议：传输控制协议(TCP)和网络互联协议(IP)而得 名，实际上是一组协议，包括多个具有不同功能且互为关联的协议。&lt;/li&gt;&lt;li&gt;IP(Internet Protocol)协议是网络层的主要协议，支持网间互连的数据通信。&lt;/li&gt;&lt;li&gt;TCP/IP协议模型从更实用的角度出发，形成了高效的四层体系结构，即 物理链路层、IP层、传输层和应用层。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-4-2-tcp-udp&quot;&gt;&lt;a name=&quot;4.2 TCP 和 UDP&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.2 TCP 和 UDP&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;TCP协议：&lt;ul&gt;
&lt;li&gt;使用TCP协议前，须先建立TCP连接，形成传输数据通道&lt;/li&gt;&lt;li&gt;传输前，采用“三次握手”方式，点对点通信，是可靠的&lt;/li&gt;&lt;li&gt;TCP协议进行通信的两个应用进程：客户端、服务端。&lt;/li&gt;&lt;li&gt;在连接中可进行大数据量的传输 &lt;/li&gt;&lt;li&gt;传输完毕，需释放已建立的连接，效率低 &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;UDP协议：&lt;ul&gt;
&lt;li&gt;将数据、源、目的封装成数据包，不需要建立连接 &lt;/li&gt;&lt;li&gt;每个数据报的大小限制在64K内&lt;/li&gt;&lt;li&gt;发送不管对方是否准备好，接收方收到也不确认，故是不可靠的&lt;/li&gt;&lt;li&gt;可以广播发送&lt;/li&gt;&lt;li&gt;发送数据结束时无需释放资源，开销小，速度快&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-4-3-socket&quot;&gt;&lt;a name=&quot;4.3 Socket&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.3 Socket&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;利用套接字(Socket)开发网络应用程序早已被广泛的采用，以至于成为事实 上的标准。&lt;/li&gt;&lt;li&gt;网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标 识符套接字。 &lt;/li&gt;&lt;li&gt;通信的两端都要有Socket，是两台机器间通信的端点。&lt;/li&gt;&lt;li&gt;网络通信其实就是Socket间的通信。 &lt;/li&gt;&lt;li&gt;Socket允许程序把网络连接当成一个流，数据在两个Socket间通过IO传输。&lt;/li&gt;&lt;li&gt;一般主动发起通信的应用程序属客户端，等待通信请求的为服务端。&lt;/li&gt;&lt;li&gt;Socket分类：&lt;ul&gt;
&lt;li&gt;流套接字（stream socket）：使用TCP提供可依赖的字节流服务&lt;/li&gt;&lt;li&gt;数据报套接字（datagram socket）：使用UDP提供“尽力而为”的数据报服务&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Socket类的常用构造器：&lt;ul&gt;
&lt;li&gt;public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定 IP 地址的指定端口号。 &lt;/li&gt;&lt;li&gt;public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。 &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Socket类的常用方法：&lt;ul&gt;
&lt;li&gt;public InputStream getInputStream()返回此套接字的输入流。可以用于接收网络消息 &lt;/li&gt;&lt;li&gt;public OutputStream getOutputStream()返回此套接字的输出流。可以用于发送网络消息 &lt;/li&gt;&lt;li&gt;public InetAddress getInetAddress()此套接字连接到的远程 IP 地址；如果套接字是未连接的，则返回 null。 &lt;/li&gt;&lt;li&gt;public InetAddress getLocalAddress()获取套接字绑定的本地地址。 即本端的IP地址 &lt;/li&gt;&lt;li&gt;public int getPort()此套接字连接到的远程端口号；如果尚未连接套接字，则返回 0。 &lt;/li&gt;&lt;li&gt;public int getLocalPort()返回此套接字绑定到的本地端口。 如果尚未绑定套接字，则返回 -1。即本端的 端口号。&lt;/li&gt;&lt;li&gt;public void close()关闭此套接字。套接字被关闭后，便不可在以后的网络连接中使用（即无法重新连接 或重新绑定）。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStream 和 OutputStream。&lt;/li&gt;&lt;li&gt;public void shutdownInput()如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容，则流将 返回 EOF（文件结束符）。 即不能在从此套接字的输入流中接收任何数据。&lt;/li&gt;&lt;li&gt;public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字，任何以前写入的数据都将被发 送，并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流， 则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-5-tcp-&quot;&gt;&lt;a name=&quot;5. TCP网络编程&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5. TCP网络编程&lt;/h1&gt;&lt;h2 id=&quot;h2-5-1-socket-tcp-&quot;&gt;&lt;a name=&quot;5.1 基于Socket的TCP编程&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.1 基于Socket的TCP编程&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Java语言的基于套接字编程分为服务端编程和客户端编程&lt;/li&gt;&lt;li&gt;客户端Socket的工作过程包含以下四个基本的步骤：&lt;ul&gt;
&lt;li&gt;创建 Socket：根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端 响应，则建立客户端到服务器的通信线路。若连接失败，会出现异常。&lt;/li&gt;&lt;li&gt;打开连接到 Socket 的输入/出流： 使用 getInputStream()方法获得输入流，使用 getOutputStream()方法获得输出流，进行数据传输&lt;/li&gt;&lt;li&gt;按照一定的协议对 Socket 进行读/写操作：通过输入流读取服务器放入线路的信息 （但不能读取自己放入线路的信息），通过输出流将信息写入线程。&lt;/li&gt;&lt;li&gt;关闭 Socket：断开客户端到服务器的连接，释放线路&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;服务器程序的工作过程包含以下四个基本的步骤： &lt;ul&gt;
&lt;li&gt;调用 ServerSocket(int port) ：创建一个服务器端套接字，并绑定到指定端口 上。用于监听客户端的请求。&lt;/li&gt;&lt;li&gt;调用 accept()：监听连接请求，如果客户端请求连接，则接受连接，返回通信 套接字对象。&lt;/li&gt;&lt;li&gt;调用 该Socket类对象的 getOutputStream() 和 getInputStream ()：获取输出 流和输入流，开始网络数据的发送和接收。&lt;/li&gt;&lt;li&gt;关闭ServerSocket和Socket对象：客户端访问结束，关闭通信套接字。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-5-2-socket-&quot;&gt;&lt;a name=&quot;5.2 客户端创建Socket对象&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.2 客户端创建Socket对象&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;客户端程序可以使用Socket类创建对象，创建的同时会自动向服务器方发起连 接。Socket的构造器是：&lt;ul&gt;
&lt;li&gt;Socket(String host,int port)throws UnknownHostException,IOException：向服务器(域名是 host。端口号为port)发起TCP连接，若成功，则创建Socket对象，否则抛出异常。 &lt;/li&gt;&lt;li&gt;Socket(InetAddress address,int port)throws IOException：根据InetAddress对象所表示的 IP地址以及端口号port发起连接。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求 &lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;Socket s = new
Socket(“192.168.40.165”,9999);
OutputStream out = s.getOutputStream();
out.write(&amp;quot;hello&amp;quot;.getBytes());
s.close();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-5-3-serversocket-&quot;&gt;&lt;a name=&quot;5.3 服务器建立 ServerSocket 对象&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.3 服务器建立 ServerSocket 对象&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;ServerSocket 对象负责等待客户端请求建立套接字连接，类似邮局某个窗口 中的业务员。也就是说，服务器必须事先建立一个等待客户请求建立套接字 连接的ServerSocket对象。&lt;/li&gt;&lt;li&gt;所谓“接收”客户的套接字请求，就是accept()方法会返回一个 Socket 对象&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-5-4-&quot;&gt;&lt;a name=&quot;5.4 客户端—服务端&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.4 客户端—服务端&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;客户端：&lt;ul&gt;
&lt;li&gt;自定义 &lt;/li&gt;&lt;li&gt;浏览器&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;服务端：&lt;ul&gt;
&lt;li&gt;自定义 &lt;/li&gt;&lt;li&gt;Tomcat服务器&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-6-udp-&quot;&gt;&lt;a name=&quot;6. UDP网络编程&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6. UDP网络编程&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。 &lt;/li&gt;&lt;li&gt;UDP数据报通过数据报套接字 DatagramSocket 发送和接收，系统不保证 UDP数据报一定能够安全送到目的地，也不能确定什么时候可以抵达。&lt;/li&gt;&lt;li&gt;DatagramPacket 对象封装了UDP数据报，在数据报中包含了发送端的IP 地址和端口号以及接收端的IP地址和端口号。&lt;/li&gt;&lt;li&gt;UDP协议中每个数据报都给出了完整的地址信息，因此无须建立发送方和 接收方的连接。如同发快递包裹一样。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-6-1-datagramsocket-&quot;&gt;&lt;a name=&quot;6.1 DatagramSocket 类的常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6.1 DatagramSocket 类的常用方法&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被 绑定到通配符地址，IP 地址由内核来选择。&lt;/li&gt;&lt;li&gt;public DatagramSocket(int port,InetAddress laddr)创建数据报套接字，将其绑定到指定的本地地址。 本地端口必须在 0 到 65535 之间（包括两者）。如果 IP 地址为 0.0.0.0，套接字将被绑定到通配符地 址，IP 地址由内核选择。&lt;/li&gt;&lt;li&gt;public void close()关闭此数据报套接字。&lt;/li&gt;&lt;li&gt;public void send(DatagramPacket p)从此套接字发送数据报包。DatagramPacket 包含的信息指示：将 要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。&lt;/li&gt;&lt;li&gt;public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时，DatagramPacket 的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法 在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的 长度长，该信息将被截短。&lt;/li&gt;&lt;li&gt;public InetAddress getLocalAddress()获取套接字绑定的本地地址。&lt;/li&gt;&lt;li&gt;public int getLocalPort()返回此套接字绑定的本地主机上的端口号。&lt;/li&gt;&lt;li&gt;public InetAddress getInetAddress()返回此套接字连接的地址。如果套接字未连接，则返回 null。&lt;/li&gt;&lt;li&gt;public int getPort()返回此套接字的端口。如果套接字未连接，则返回 -1。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-6-2-datagrampacket-&quot;&gt;&lt;a name=&quot;6.2 DatagramPacket类的常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6.2 DatagramPacket类的常用方法&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;public DatagramPacket(byte[] buf,int length)构造 DatagramPacket，用来接收长 度为 length 的数据包。 length 参数必须小于等于 buf.length。&lt;/li&gt;&lt;li&gt;public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构造数 据报包，用来将长度为 length 的包发送到指定主机上的指定端口号。length 参数必须小于等于 buf.length。 &lt;/li&gt;&lt;li&gt;public InetAddress getAddress()返回某台机器的 IP 地址，此数据报将要发往该 机器或者是从该机器接收到的。 &lt;/li&gt;&lt;li&gt;public int getPort()返回某台远程主机的端口号，此数据报将要发往该主机或 者是从该主机接收到的。&lt;/li&gt;&lt;li&gt;public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区 中的偏移量 offset 处开始，持续 length 长度。&lt;/li&gt;&lt;li&gt;public int getLength()返回将要发送或接收到的数据的长度。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-6-3-udp-&quot;&gt;&lt;a name=&quot;6.3 UDP网络通信&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6.3 UDP网络通信&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;流 程：&lt;ul&gt;
&lt;li&gt;DatagramSocket与DatagramPacket &lt;/li&gt;&lt;li&gt;建立发送端，接收端 &lt;/li&gt;&lt;li&gt;建立数据包 &lt;/li&gt;&lt;li&gt;调用Socket的发送、接收方法 &lt;/li&gt;&lt;li&gt;关闭Socket&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;发送端与接收端是两个独立的运行程序&lt;/li&gt;&lt;li&gt;发送端&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;DatagramSocket ds = null;
try {
    ds = new DatagramSocket();
    byte[] by = &amp;quot;hello,atguigu.com&amp;quot;.getBytes();
    DatagramPacket dp = new DatagramPacket(by, 0, by.length,
            InetAddress.getByName(&amp;quot;127.0.0.1&amp;quot;), 10000);
    ds.send(dp);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (ds != null)
        ds.close();
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;接收端,在接收端，要指定监听的端口。&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;DatagramSocket ds = null;
try {
    ds = new DatagramSocket(10000);
    byte[] by = new byte[1024];
    DatagramPacket dp = new DatagramPacket(by, by.length);
    ds.receive(dp);
    String str = new String(dp.getData(), 0, dp.getLength());
    System.out.println(str + &amp;quot;--&amp;quot; + dp.getAddress());
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (ds != null)
        ds.close();
}&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Mon, 18 Jul 2022 16:54:45 +0800</pubDate></item><item><title>IO流</title><link>https://68686.ltd/?id=56</link><description>&lt;h1 id=&quot;h1-1-file-&quot;&gt;&lt;a name=&quot;1.File类的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.File类的使用&lt;/h1&gt;&lt;h2 id=&quot;h2-1-1-file-&quot;&gt;&lt;a name=&quot;1.1 File类的概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.1 File类的概述&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;File类的一个对象，代表一个文件或一个文件目录(文件夹)&lt;/li&gt;&lt;li&gt;File类声明在java.io包下&lt;/li&gt;&lt;li&gt;File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法，并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容，必须使用IO流来完成。&lt;/li&gt;&lt;li&gt;后续File类的对象常会作为参数传递到流的构造器中，指明读取或写入的”终点”.&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-1-2-file-&quot;&gt;&lt;a name=&quot;1.2 File类的创建&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.2 File类的创建&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;创建方式&lt;ul&gt;
&lt;li&gt;File(String filePath)&lt;ul&gt;
&lt;li&gt;相对路径&lt;/li&gt;&lt;li&gt;绝对路径&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;File(String parentPath, String childPath)&lt;/li&gt;&lt;li&gt;File(File parentFile, String childPath)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;路径分隔符&lt;ul&gt;
&lt;li&gt;windows：\\&lt;/li&gt;&lt;li&gt;unix：/&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-1-3-&quot;&gt;&lt;a name=&quot;1.3 常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.3 常用方法&lt;/h2&gt;&lt;p&gt;File类的获取功能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;public String getAbsolutePath()：获取绝对路径&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public String getPath() ：获取路径&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public String getName() ：获取名称&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public String getParent()：获取上层文件目录路径。若无，返null&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public long length() ：获取文件长度（即：字节数）。不能获取目录的长度。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public long lastModified() ：获取最后一次的修改时间，毫秒值&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;public String[] list() ：获取指定目录下的所有文件或者文件目录的名称数组&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;public File[] listFiles() ：获取指定目录下的所有文件或者文件目录的File数组&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;File类的重命名功能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;public boolean renameTo(File dest):把文件重命名为指定的文件路径&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;File类的判断功能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;public boolean isDirectory()：判断是否是文件目录&lt;/li&gt;&lt;li&gt;public boolean isFile() ：判断是否是文件&lt;/li&gt;&lt;li&gt;public boolean exists() ：判断是否存在&lt;/li&gt;&lt;li&gt;public boolean canRead() ：判断是否可读&lt;/li&gt;&lt;li&gt;public boolean canWrite() ：判断是否可写&lt;/li&gt;&lt;li&gt;public boolean isHidden() ：判断是否隐藏&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;File类的创建功能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;public boolean createNewFile() ：创建文件。若文件存在，则不创建，返回false&lt;/li&gt;&lt;li&gt;public boolean mkdir() ：创建文件目录。如果此文件目录存在，就不创建了。如果此文件目录的上层目录不存在，也不创建。&lt;/li&gt;&lt;li&gt;public boolean mkdirs() ：创建文件目录。如果此文件目录存在，就不创建了。如果上层文件目录不存在，一并创建&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;File类的删除功能&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;public boolean delete()：删除文件或者文件夹&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-2-io-&quot;&gt;&lt;a name=&quot;2.IO流原理及流的分类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.IO流原理及流的分类&lt;/h1&gt;&lt;h2 id=&quot;h2-2-1-io-&quot;&gt;&lt;a name=&quot;2.1 IO原理&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.1 IO原理&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;I/O是Input/Output的缩写， I/O技术是非常实用的技术，用于 处理设备之间的数据传输。如读/写文件，网络通讯等。 &lt;/li&gt;&lt;li&gt;Java程序中，对于数据的输入/输出操作以“流(stream)” 的 方式进行。&lt;/li&gt;&lt;li&gt;java.io包下提供了各种“流”类和接口，用以获取不同种类的 数据，并通过标准的方法输入或输出数据。&lt;/li&gt;&lt;li&gt;输入input：读取外部数据（磁 盘、光盘等存储设备的数据）到 程序（内存）中。&lt;/li&gt;&lt;li&gt;输出output：将程序（内存） 数据输出到磁盘、光盘等存储设 备中。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-2-2-&quot;&gt;&lt;a name=&quot;2.2 流的分类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.2 流的分类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;按操作数据单位不同分为：字节流(8 bit)，字符流(16 bit) &lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;按数据流的流向不同分为：输入流，输出流 &lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;按流的角色的不同分为：节点流，处理流&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;抽象基类&lt;/th&gt;
&lt;th&gt;字节流&lt;/th&gt;
&lt;th&gt;字符流&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;输入流&lt;/td&gt;
&lt;td&gt;InputStream&lt;/td&gt;
&lt;td&gt;Reader&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;输出流&lt;/td&gt;
&lt;td&gt;OutputStream&lt;/td&gt;
&lt;td&gt;Writer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-2-3-io-&quot;&gt;&lt;a name=&quot;2.3 IO流体系&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.3 IO流体系&lt;/h2&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;分类&lt;/th&gt;
&lt;th style=&quot;text-align:left&quot;&gt;字节输入流&lt;/th&gt;
&lt;th&gt;字节输出流&lt;/th&gt;
&lt;th&gt;字符输入流&lt;/th&gt;
&lt;th&gt;字符输出流&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;抽象基类&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;InputStream&lt;/td&gt;
&lt;td&gt;OutputStream&lt;/td&gt;
&lt;td&gt;Reader&lt;/td&gt;
&lt;td&gt;Writer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问文件&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;FileInputStream&lt;/td&gt;
&lt;td&gt;FileOutputStream&lt;/td&gt;
&lt;td&gt;FileReader&lt;/td&gt;
&lt;td&gt;FileWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问数组&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;ByteArrayInputStream&lt;/td&gt;
&lt;td&gt;ByteArrayOutputStream&lt;/td&gt;
&lt;td&gt;CharArrayReader&lt;/td&gt;
&lt;td&gt;CharArrayWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问管道&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;PipedInputStream&lt;/td&gt;
&lt;td&gt;PipedOutputStream&lt;/td&gt;
&lt;td&gt;PipedReader&lt;/td&gt;
&lt;td&gt;PipedWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;访问字符串&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;StringReader&lt;/td&gt;
&lt;td&gt;StringWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;缓冲流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;BufferedInputStream&lt;/td&gt;
&lt;td&gt;BufferedOutputStream&lt;/td&gt;
&lt;td&gt;BufferedReader&lt;/td&gt;
&lt;td&gt;BufferedWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;转换流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;InputStreamReader&lt;/td&gt;
&lt;td&gt;OutputStreamWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;对象流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;ObjectInputStream&lt;/td&gt;
&lt;td&gt;ObjectOutputStream&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;FiterInputStream&lt;/td&gt;
&lt;td&gt;FiterOutputStream&lt;/td&gt;
&lt;td&gt;FiterReader&lt;/td&gt;
&lt;td&gt;FiterWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;打印流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;/td&gt;
&lt;td&gt;PrintStream&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;PrintWriter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;推回输入流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;PushbackInputStream&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;PushbackReader&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;特殊流&lt;/td&gt;
&lt;td style=&quot;text-align:left&quot;&gt;DataInputStream&lt;/td&gt;
&lt;td&gt;DataOutputStream&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;h2-2-4-inputstream-reader&quot;&gt;&lt;a name=&quot;2.4 InputStream和Reader&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.4 InputStream和Reader&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;InputStream 和 Reader 是所有输入流的基类。 InputStream（典型实现：FileInputStream）&lt;ul&gt;
&lt;li&gt;int read()&lt;/li&gt;&lt;li&gt;int read(byte[] b) &lt;/li&gt;&lt;li&gt;int read(byte[] b, int off, int len) &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Reader（典型实现：FileReader）&lt;ul&gt;
&lt;li&gt;int read()&lt;/li&gt;&lt;li&gt;int read(char [] c)&lt;/li&gt;&lt;li&gt;int read(char [] c, int off, int len)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;程序中打开的文件 IO 资源不属于内存里的资源，垃圾回收机制无法回收该资 源，所以应该显式关闭文件 IO 资源。&lt;/li&gt;&lt;li&gt;FileInputStream 从文件系统中的某个文件中获得输入字节。FileInputStream  用于读取非文本数据之类的原始字节流。要读取字符流，需要使用 FileReader&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-4-1-inputstream&quot;&gt;&lt;a name=&quot;2.4.1 InputStream&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.4.1 InputStream&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;int read() 从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因 为已经到达流末尾而没有可用的字节，则返回值 -1。&lt;/li&gt;&lt;li&gt;int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已 经到达流末尾而没有可用的字节，则返回值 -1。否则以整数形式返回实际读取 的字节数。&lt;/li&gt;&lt;li&gt;int read(byte[] b, int off,int len) 将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节，但读取 的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于 文件末尾而没有可用的字节，则返回值 -1。&lt;/li&gt;&lt;li&gt;public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-4-2-reader&quot;&gt;&lt;a name=&quot;2.4.2 Reader&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.4.2 Reader&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;int read() 读取单个字符。作为整数读取的字符，范围在 0 到 65535 之间 (0x00-0xffff)（2个 字节的Unicode码），如果已到达流的末尾，则返回 -1 &lt;/li&gt;&lt;li&gt;int read(char[] cbuf) 将字符读入数组。如果已到达流的末尾，则返回 -1。否则返回本次读取的字符数。&lt;/li&gt;&lt;li&gt;int read(char[] cbuf,int off,int len) 将字符读入数组的某一部分。存到数组cbuf中，从off处开始存储，最多读len个字 符。如果已到达流的末尾，则返回 -1。否则返回本次读取的字符数。&lt;/li&gt;&lt;li&gt;public void close() throws IOException 关闭此输入流并释放与该流关联的所有系统资源。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-2-5-outputstream-writer&quot;&gt;&lt;a name=&quot;2.5 OutputStream和Writer&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.5 OutputStream和Writer&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;OutputStream 和 Writer 也非常相似：&lt;ul&gt;
&lt;li&gt;void write(int b/int c);&lt;/li&gt;&lt;li&gt;void write(byte[] b/char[] cbuf); &lt;/li&gt;&lt;li&gt;void write(byte[] b/char[] buff, int off, int len); &lt;/li&gt;&lt;li&gt;void flush(); &lt;/li&gt;&lt;li&gt;void close(); 需要先刷新，再关闭此流 &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;因为字符流直接以字符作为操作单位，所以 Writer 可以用字符串来替换字符数组， 即以 String 对象作为参数&lt;ul&gt;
&lt;li&gt;void write(String str); &lt;/li&gt;&lt;li&gt;void write(String str, int off, int len); &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;FileOutputStream 从文件系统中的某个文件中获得输出字节。FileOutputStream  用于写出非文本数据之类的原始字节流。要写出字符流，需要使用 FileWriter&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-5-1-outputstream&quot;&gt;&lt;a name=&quot;2.5.1 OutputStream&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.5.1 OutputStream&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;void write(int b) 将指定的字节写入此输出流。write 的常规协定是：向输出流写入一个字节。要写 入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的。 &lt;/li&gt;&lt;li&gt;void write(byte[] b) 将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是：应该 与调用 write(b, 0, b.length) 的效果完全相同。 &lt;/li&gt;&lt;li&gt;void write(byte[] b,int off,int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。 &lt;/li&gt;&lt;li&gt;public void flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节，调用此方法指示应将这些字节立 即写入它们预期的目标。 &lt;/li&gt;&lt;li&gt;public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-5-2-writer&quot;&gt;&lt;a name=&quot;2.5.2 Writer&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.5.2 Writer&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;void write(int c) 写入单个字符。要写入的字符包含在给定整数值的 16 个低位中，16 高位被忽略。 即 写入0 到 65535 之间的Unicode码。&lt;/li&gt;&lt;li&gt;void write(char[] cbuf) 写入字符数组。&lt;/li&gt;&lt;li&gt;void write(char[] cbuf,int off,int len) 写入字符数组的某一部分。从off开始，写入len个字符 &lt;/li&gt;&lt;li&gt;void write(String str) 写入字符串。&lt;/li&gt;&lt;li&gt;void write(String str,int off,int len) 写入字符串的某一部分。&lt;/li&gt;&lt;li&gt;void flush() 刷新该流的缓冲，则立即将它们写入预期目标。&lt;/li&gt;&lt;li&gt;public void close() throws IOException 关闭此输出流并释放与该流关联的所有系统资源。&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-3-&quot;&gt;&lt;a name=&quot;3.字节流(或文件流)&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.字节流(或文件流)&lt;/h1&gt;&lt;h2 id=&quot;h2-3-1-&quot;&gt;&lt;a name=&quot;3.1 读取文件&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.1 读取文件&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;建立一个流对象，将已存在的一个文件加载进流。 &lt;ul&gt;
&lt;li&gt;FileReader fr = new FileReader(new File(“Test.txt”)); &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;创建一个临时存放数据的数组。&lt;ul&gt;
&lt;li&gt;char[] ch = new char[1024]; &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;调用流对象的读取方法将流中的数据读入到数组中。 &lt;ul&gt;
&lt;li&gt;fr.read(ch); &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;关闭资源。&lt;ul&gt;
&lt;li&gt;fr.close();&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;FileReader fr = null;
try {
    fr = new FileReader(new File(&amp;quot;c:\\test.txt&amp;quot;));
    char[] buf = new char[1024];
    int len;
    while ((len = fr.read(buf)) != -1) {
        System.out.print(new String(buf, 0, len));
    }
} catch (IOException e) {
    System.out.println(&amp;quot;read-Exception :&amp;quot; + e.getMessage());
} finally {
    if (fr != null) {
        try {
            fr.close();
        } catch (IOException e) {
            System.out.println(&amp;quot;close-Exception :&amp;quot; + e.getMessage());
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-3-2-&quot;&gt;&lt;a name=&quot;3.2 写入文件&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.2 写入文件&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;创建流对象，建立数据存放文件 &lt;ul&gt;
&lt;li&gt;FileWriter fw = new FileWriter(new File(“Test.txt”)); &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;调用流对象的写入方法，将数据写入流 &lt;ul&gt;
&lt;li&gt;fw.write(“atguigu-songhongkang”); &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;关闭流资源，并将流中的数据清空到文件中。 &lt;ul&gt;
&lt;li&gt;fw.close();&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;FileWriter fw = null;
try {
    fw = new FileWriter(new File(&amp;quot;Test.txt&amp;quot;));
    fw.write(&amp;quot;atguigu-songhongkang&amp;quot;);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fw != null)
        try {
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-3-3-&quot;&gt;&lt;a name=&quot;3.3 注意点&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.3 注意点&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;定义文件路径时，注意：可以用“/”或者“\”。&lt;/li&gt;&lt;li&gt;在写入一个文件时，如果使用构造器FileOutputStream(file)，则目录下有同名文 件将被覆盖。&lt;/li&gt;&lt;li&gt;如果使用构造器FileOutputStream(file,true)，则目录下的同名文件不会被覆盖， 在文件内容末尾追加内容。&lt;/li&gt;&lt;li&gt;在读取文件时，必须保证该文件已存在，否则报异常。&lt;/li&gt;&lt;li&gt;字节流操作字节，比如：.mp3，.avi，.rmvb，mp4，.jpg，.doc，.ppt &lt;/li&gt;&lt;li&gt;字符流操作字符，只能操作普通文本文件。最常见的文本文 件：.txt，.java，.c，.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文 本文件。&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-4-&quot;&gt;&lt;a name=&quot;4.缓冲流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.缓冲流&lt;/h1&gt;&lt;h2 id=&quot;h2-4-1-&quot;&gt;&lt;a name=&quot;4.1 缓冲流的原理&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.1 缓冲流的原理&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;为了提高数据读写的速度，Java API提供了带缓冲功能的流类，在使用这些流类 时，会创建一个内部缓冲区数组，缺省使用8192个字节(8Kb)的缓冲区。&lt;/li&gt;&lt;li&gt;缓冲流要“套接”在相应的节点流之上，根据数据操作单位可以把缓冲流分为： &lt;ul&gt;
&lt;li&gt;BufferedInputStream 和 BufferedOutputStream&lt;/li&gt;&lt;li&gt;BufferedReader 和 BufferedWriter&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;当读取数据时，数据按块读入缓冲区，其后的读操作则直接访问缓冲区&lt;/li&gt;&lt;li&gt;当使用BufferedInputStream读取字节文件时，BufferedInputStream会一次性从 文件中读取8192个(8Kb)，存在缓冲区中，直到缓冲区装满了，才重新从文件中 读取下一个8192个字节数组。&lt;/li&gt;&lt;li&gt;向流中写入字节时，不会直接写到文件，先写到缓冲区中直到缓冲区写满， BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流&lt;/li&gt;&lt;li&gt;关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可，关闭最外层流也 会相应关闭内层节点流&lt;/li&gt;&lt;li&gt;flush()方法的使用：手动将buffer中内容写入文件&lt;/li&gt;&lt;li&gt;如果是带缓冲区的流对象的close()方法，不但会关闭流，还会在关闭流之前刷 新缓冲区，关闭后不能再写出&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-4-2-&quot;&gt;&lt;a name=&quot;4.2 缓冲流的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.2 缓冲流的使用&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;BufferedReader br = null;
        BufferedWriter bw = null;
        try {
// 创建缓冲流对象：它是处理流，是对节点流的包装
            br = new BufferedReader(new FileReader(&amp;quot;d:\\IOTest\\source.txt&amp;quot;));
            bw = new BufferedWriter(new FileWriter(&amp;quot;d:\\IOTest\\dest.txt&amp;quot;));
            String str;
            while ((str = br.readLine()) != null) { // 一次读取字符文本文件的一行字符
                bw.write(str); // 一次写入一行字符串
                bw.newLine(); // 写入行分隔符
            }
            bw.flush(); // 刷新缓冲区
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
// 关闭IO流对象
            try {
                if (bw != null) {
                    bw.close(); // 关闭过滤流时,会自动关闭它所包装的底层节点流
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-5-&quot;&gt;&lt;a name=&quot;5.转换流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.转换流&lt;/h1&gt;&lt;h2 id=&quot;h2-5-1-&quot;&gt;&lt;a name=&quot;5.1 转换流的作用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.1 转换流的作用&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;转换流提供了在字节流和字符流之间的转换 Java API提供了两个转换流： &lt;ul&gt;
&lt;li&gt;InputStreamReader：将InputStream转换为Reader &lt;/li&gt;&lt;li&gt;OutputStreamWriter：将Writer转换为OutputStream &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;字节流中的数据都是字符时，转成字符流操作更高效。&lt;/li&gt;&lt;li&gt;很多时候我们使用转换流来处理文件乱码问题。实现编码和 解码的功能。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-5-2-inputstreamreader&quot;&gt;&lt;a name=&quot;5.2 InputStreamReader&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.2 InputStreamReader&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;实现将字节的输入流按指定字符集转换为字符的输入流。&lt;/li&gt;&lt;li&gt;需要和InputStream“套接”。&lt;/li&gt;&lt;li&gt;构造器&lt;ul&gt;
&lt;li&gt;public InputStreamReader(InputStream in)&lt;/li&gt;&lt;li&gt;public InputSreamReader(InputStream in,String charsetName) &lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;如： Reader isr = new InputStreamReader(System.in,”gbk”);&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-5-3-outputstreamwriter&quot;&gt;&lt;a name=&quot;5.3 OutputStreamWriter&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.3 OutputStreamWriter&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;实现将字符的输出流按指定字符集转换为字节的输出流。&lt;/li&gt;&lt;li&gt;需要和OutputStream“套接”。&lt;/li&gt;&lt;li&gt;构造器&lt;ul&gt;
&lt;li&gt;public OutputStreamWriter(OutputStream out)&lt;/li&gt;&lt;li&gt;public OutputSreamWriter(OutputStream out,String charsetName)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;public void testMyInput() throws Exception {
    FileInputStream fis = new FileInputStream(&amp;quot;dbcp.txt&amp;quot;);
    FileOutputStream fos = new FileOutputStream(&amp;quot;dbcp5.txt&amp;quot;);
    InputStreamReader isr = new InputStreamReader(fis, &amp;quot;GBK&amp;quot;);
    OutputStreamWriter osw = new OutputStreamWriter(fos, &amp;quot;GBK&amp;quot;);
    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(osw);
    String str = null;
    while ((str = br.readLine()) != null) {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }
    bw.close();
    br.close();
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-5-3-&quot;&gt;&lt;a name=&quot;5.3 字符编码&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.3 字符编码&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;编码表的由来 计算机只能识别二进制数据，早期由来是电信号。为了方便应用计算机，让它可以识 别各个国家的文字。就将各个国家的文字用数字来表示，并一一对应，形成一张表。 这就是编码表。&lt;/li&gt;&lt;li&gt;常见的编码表&lt;ul&gt;
&lt;li&gt;ASCII：美国标准信息交换码。&lt;ul&gt;
&lt;li&gt;用一个字节的7位可以表示。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;ISO8859-1：拉丁码表。欧洲码表&lt;ul&gt;
&lt;li&gt;用一个字节的8位表示。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;GB2312：中国的中文编码表。最多两个字节编码所有字符 &lt;/li&gt;&lt;li&gt;GBK：中国的中文编码表升级，融合了更多的中文文字符号。最多两个字节编码 &lt;/li&gt;&lt;li&gt;Unicode：国际标准码，融合了目前人类使用的所有字符。为每个字符分配唯一的 字符码。所有的文字都用两个字节来表示。&lt;/li&gt;&lt;li&gt;UTF-8：变长的编码方式，可用1-4个字节来表示一个字符。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;在Unicode出现之前，所有的字符集都是和具体编码方案绑定在一起的（即字 符集≈编码方式），都是直接将字符和最终字节流绑定死了。&lt;/li&gt;&lt;li&gt;GBK等双字节编码方式，用最高位是1或0表示两个字节和一个字节。&lt;/li&gt;&lt;li&gt;Unicode不完美，这里就有三个问题，一个是，我们已经知道，英文字母只用 一个字节表示就够了，第二个问题是如何才能区别Unicode和ASCII？计算机 怎么知道两个字节表示一个符号，而不是分别表示两个符号呢？第三个，如果 和GBK等双字节编码方式一样，用最高位是1或0表示两个字节和一个字节， 就少了很多值无法用于表示字符，不够表示所有字符。Unicode在很长一段时 间内无法推广，直到互联网的出现。&lt;/li&gt;&lt;li&gt;面向传输的众多 UTF（UCS Transfer Format）标准出现了，顾名思义，UTF8就是每次8个位传输数据，而UTF-16就是每次16个位。这是为传输而设计的 编码，并使编码无国界，这样就可以显示全世界上所有文化的字符了。&lt;/li&gt;&lt;li&gt;Unicode只是定义了一个庞大的、全球通用的字符集，并为每个字符规定了唯 一确定的编号，具体存储成什么样的字节流，取决于字符编码方案。推荐的 Unicode编码是UTF-8和UTF-16。&lt;/li&gt;&lt;li&gt;编码：字符串字节数组&lt;/li&gt;&lt;li&gt;解码：字节数组字符串 转换流的编码应用 &lt;/li&gt;&lt;li&gt;可以将字符按指定编码格式存储  可以对文本数据按指定编码格式来解读  指定编码表的动作由构造器完成&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-6-&quot;&gt;&lt;a name=&quot;6.标准输入、输出流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6.标准输入、输出流&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;System.in和System.out分别代表了系统标准的输入和输出设备&lt;/li&gt;&lt;li&gt;默认输入设备是：键盘，输出设备是：显示器&lt;/li&gt;&lt;li&gt;System.in的类型是InputStream&lt;/li&gt;&lt;li&gt;System.out的类型是PrintStream，其是OutputStream的子类 FilterOutputStream 的子类 &lt;/li&gt;&lt;li&gt;重定向：通过System类的setIn，setOut方法对默认设备进行改变。&lt;ul&gt;
&lt;li&gt;public static void setIn(InputStream in)&lt;/li&gt;&lt;li&gt;public static void setOut(PrintStream out)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;System.out.println(&amp;quot;请输入信息(退出输入e或exit):&amp;quot;);
// 把&amp;quot;标准&amp;quot;输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
    while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --&amp;gt; 阻塞程序
        if (&amp;quot;e&amp;quot;.equalsIgnoreCase(s) || &amp;quot;exit&amp;quot;.equalsIgnoreCase(s)) {
            System.out.println(&amp;quot;安全退出!!&amp;quot;);
            break;
        }
        // 将读取到的整行字符串转成大写输出
        System.out.println(&amp;quot;--&amp;gt;:&amp;quot; + s.toUpperCase());
        System.out.println(&amp;quot;继续输入信息&amp;quot;);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        if (br != null) {
            br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-7-&quot;&gt;&lt;a name=&quot;7.打印流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;7.打印流&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;实现将基本数据类型的数据格式转化为字符串输出 打印流：PrintStream和PrintWriter &lt;/li&gt;&lt;li&gt;提供了一系列重载的print()和println()方法，用于多种数据类型的输出&lt;/li&gt;&lt;li&gt;PrintStream和PrintWriter的输出不会抛出IOException异常&lt;/li&gt;&lt;li&gt;PrintStream和PrintWriter有自动flush功能&lt;/li&gt;&lt;li&gt;PrintStream 打印的所有字符都使用平台的默认字符编码转换为字节。 在需要写入字符而不是写入字节的情况下，应该使用 PrintWriter 类。&lt;/li&gt;&lt;li&gt;System.out返回的是PrintStream的实例&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;PrintStream ps = null;
try {
    FileOutputStream fos = new FileOutputStream(new File(&amp;quot;D:\\IO\\text.txt&amp;quot;));
    // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 &amp;#39;\n&amp;#39; 时都会刷新输出缓冲区)
    ps = new PrintStream(fos, true);
    if (ps != null) {// 把标准输出流(控制台输出)改成文件
        System.setOut(ps);
    }
    for (int i = 0; i &amp;lt;= 255; i++) { // 输出ASCII字符
        System.out.print((char) i);
        if (i % 50 == 0) { // 每50个数据一行
            System.out.println(); // 换行
        }
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
} finally {
    if (ps != null) {
        ps.close();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-8-&quot;&gt;&lt;a name=&quot;8.数据流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;8.数据流&lt;/h1&gt;&lt;ul&gt;
&lt;li&gt;为了方便地操作Java语言的基本数据类型和String的数据，可以使用数据流。 &lt;/li&gt;&lt;li&gt;数据流有两个类：(用于读取和写出基本数据类型、String类的数据） &lt;ul&gt;
&lt;li&gt;DataInputStream 和 DataOutputStream &lt;/li&gt;&lt;li&gt;分别“套接”在 InputStream 和 OutputStream 子类的流上&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;DataInputStream中的方法&lt;ul&gt;
&lt;li&gt;boolean readBoolean() &lt;/li&gt;&lt;li&gt;byte readByte()&lt;/li&gt;&lt;li&gt;char readChar() &lt;/li&gt;&lt;li&gt;float readFloat() &lt;/li&gt;&lt;li&gt;double readDouble() &lt;/li&gt;&lt;li&gt;short readShort() &lt;/li&gt;&lt;li&gt;long readLong() &lt;/li&gt;&lt;li&gt;int readInt() &lt;/li&gt;&lt;li&gt;String readUTF() &lt;/li&gt;&lt;li&gt;void readFully(byte[] b)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;DataOutputStream中的方法&lt;ul&gt;
&lt;li&gt;将上述的方法的read改为相应的write即可。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;DataOutputStream dos = null;
try { // 创建连接到指定文件的数据输出流对象
    dos = new DataOutputStream(new FileOutputStream(&amp;quot;destData.dat&amp;quot;));
    dos.writeUTF(&amp;quot;我爱北京天安门&amp;quot;); // 写UTF字符串
    dos.writeBoolean(false); // 写入布尔值
    dos.writeLong(1234567890L); // 写入长整数
    System.out.println(&amp;quot;写文件成功!&amp;quot;);
} catch (IOException e) {
    e.printStackTrace();
} finally { // 关闭流对象
    try {
        if (dos != null) {
            // 关闭过滤流时,会自动关闭它包装的底层节点流
            dos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;DataInputStream dis = null;
try {
    dis = new DataInputStream(new FileInputStream(&amp;quot;destData.dat&amp;quot;));
    String info = dis.readUTF();
    boolean flag = dis.readBoolean();
    long time = dis.readLong();
    System.out.println(info);
    System.out.println(flag);
    System.out.println(time);
} catch (Exception e) {
    e.printStackTrace();
} finally {
    if (dis != null) {
        try {
            dis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;h1-9-&quot;&gt;&lt;a name=&quot;9.对象流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;9.对象流&lt;/h1&gt;&lt;h2 id=&quot;h2-9-1-&quot;&gt;&lt;a name=&quot;9.1 对象流的说明&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;9.1 对象流的说明&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;ObjectInputStream和OjbectOutputSteam &lt;ul&gt;
&lt;li&gt;用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中，也能把对象从数据源中还原回来。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;序列化：用ObjectOutputStream类保存基本类型数据或对象的机制&lt;/li&gt;&lt;li&gt;反序列化：用ObjectInputStream类读取基本类型数据或对象的机制&lt;/li&gt;&lt;li&gt;ObjectOutputStream和ObjectInputStream不能序列化static和transient修 饰的成员变量&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-9-2-&quot;&gt;&lt;a name=&quot;9.2 对象的序列化&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;9.2 对象的序列化&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流，从 而允许把这种二进制流持久地保存在磁盘上，或通过网络将这种二进制流传 输到另一个网络节点。//当其它程序获取了这种二进制流，就可以恢复成原 来的Java对象&lt;/li&gt;&lt;li&gt;序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据， 使其在保存和传输时可被还原&lt;/li&gt;&lt;li&gt;序列化是 RMI（Remote Method Invoke – 远程方法调用）过程的参数和返 回值都必须实现的机制，而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础&lt;/li&gt;&lt;li&gt;如果需要让某个对象支持序列化机制，则必须让对象所属的类及其属性是可 序列化的，为了让某个类是可序列化的，该类必须实现如下两个接口之一。 否则，会抛出NotSerializableException异常&lt;ul&gt;
&lt;li&gt;Serializable&lt;/li&gt;&lt;li&gt;Externalizable&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量：&lt;ul&gt;
&lt;li&gt;private static final long serialVersionUID;&lt;/li&gt;&lt;li&gt;serialVersionUID用来表明类的不同版本间的兼容性。简言之，其目的是以序列化对象 进行版本控制，有关各版本反序列化时是否兼容。&lt;/li&gt;&lt;li&gt;如果类没有显示定义这个静态常量，它的值是Java运行时环境根据类的内部细节自 动生成的。若类的实例变量做了修改，serialVersionUID 可能发生变化。故建议， 显式声明。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;简单来说，Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时，JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较，如果相同 就认为是一致的，可以进行反序列化，否则就会出现序列化版本不一致的异 常。(InvalidCastException)&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-9-3-&quot;&gt;&lt;a name=&quot;9.3 使用对象流序列化对象&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;9.3 使用对象流序列化对象&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;若某个类实现了 Serializable 接口，该类的对象就是可序列化的：&lt;ul&gt;
&lt;li&gt;创建一个 ObjectOutputStream&lt;/li&gt;&lt;li&gt;调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象&lt;/li&gt;&lt;li&gt;注意写出一次，操作flush()一次&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;反序列化&lt;ul&gt;
&lt;li&gt;创建一个 ObjectInputStream&lt;/li&gt;&lt;li&gt;调用 readObject() 方法读取流中的对象&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;强调：如果某个类的属性不是基本数据类型或 String 类型，而是另一个 引用类型，那么这个引用类型必须是可序列化的，否则拥有该类型的 Field 的类也不能序列化&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;//序列化：将对象写入到磁盘或者进行网络传输。
//要求对象必须实现序列化
//反序列化：将磁盘中的对象数据源读出。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(“data.txt&amp;quot;));
Person p = new Person(&amp;quot;张三&amp;quot;, 18, &amp;quot;浙江&amp;quot;, new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(“data.txt&amp;quot;));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-9-4-serializable-&quot;&gt;&lt;a name=&quot;9.4 Serializable接口&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;9.4 Serializable接口&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;实现了Serializable接口的对象，可将它们转换成一系列字节，并可在以后 完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机 制能自动补偿操作系统间的差异。换句话说，可以先在Windows机器上创 建一个对象，对其序列化，然后通过网络发给一台Unix机器，然后在那里 准确无误地重新“装配”。不必关心数据在不同机器上如何表示，也不必 关心字节的顺序或者其他任何细节。&lt;/li&gt;&lt;li&gt;由于大部分作为参数的类如String、Integer等都实现了 java.io.Serializable的接口，也可以利用多态的性质，作为参数使接口更 灵活。&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-10-&quot;&gt;&lt;a name=&quot;10.随机存取文件流&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;10.随机存取文件流&lt;/h1&gt;&lt;h2 id=&quot;h2-10-1-randomaccessfile-&quot;&gt;&lt;a name=&quot;10.1 RandomAccessFile 类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;10.1 RandomAccessFile 类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;RandomAccessFile 声明在java.io包下，但直接继承于java.lang.Object类。并 且它实现了DataInput、DataOutput这两个接口，也就意味着这个类既可以读也 可以写。 &lt;/li&gt;&lt;li&gt;RandomAccessFile 类支持 “随机访问” 的方式，程序可以直接跳到文件的任意 地方来读、写文件&lt;ul&gt;
&lt;li&gt;支持只访问文件的部分内容 &lt;/li&gt;&lt;li&gt;可以向已存在的文件后追加内容&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;RandomAccessFile 对象包含一个记录指针，用以标示当前读写处的位置。 &lt;/li&gt;&lt;li&gt;RandomAccessFile 类对象可以自由移动记录指针：&lt;ul&gt;
&lt;li&gt;long getFilePointer()：获取文件记录指针的当前位置&lt;/li&gt;&lt;li&gt;void seek(long pos)：将文件记录指针定位到 pos 位置&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;构造器&lt;ul&gt;
&lt;li&gt;public RandomAccessFile(File file, String mode)&lt;/li&gt;&lt;li&gt;public RandomAccessFile(String name, String mode)&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;创建 RandomAccessFile 类实例需要指定一个 mode 参数，该参数指 定 RandomAccessFile 的访问模式：&lt;ul&gt;
&lt;li&gt;r: 以只读方式打开&lt;/li&gt;&lt;li&gt;rw：打开以便读取和写入&lt;/li&gt;&lt;li&gt;rwd:打开以便读取和写入；同步文件内容的更新&lt;/li&gt;&lt;li&gt;rws:打开以便读取和写入；同步文件内容和元数据的更新&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;如果模式为只读r。则不会创建文件，而是会去读取一个已经存在的文件， 如果读取的文件不存在则会出现异常。 如果模式为rw读写。如果文件不 存在则会去创建文件，如果存在则不会创建。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-10-2-&quot;&gt;&lt;a name=&quot;10.2 读取文件内容&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;10.2 读取文件内容&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;RandomAccessFile raf = new RandomAccessFile(“test.txt”, “rw”）;
raf.seek(5);
byte [] b = new byte[1024];
int off = 0;
int len = 5;
raf.read(b, off, len);
String str = new String(b, 0, len);
System.out.println(str);
raf.close();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-10-3-&quot;&gt;&lt;a name=&quot;10.3 写入文件内容&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;10.3 写入文件内容&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;RandomAccessFile raf = new RandomAccessFile(&amp;quot;test.txt&amp;quot;, &amp;quot;rw&amp;quot;);
raf.seek(5);
//先读出来
String temp = raf.readLine();
raf.seek(5);
raf.write(&amp;quot;xyz&amp;quot;.getBytes());
raf.write(temp.getBytes());
raf.close();

RandomAccessFile raf1 = new RandomAccessFile(&amp;quot;hello.txt&amp;quot;, &amp;quot;rw&amp;quot;);
raf1.seek(5);
//方式一：
//StringBuilder info = new StringBuilder((int) file.length());
//byte[] buffer = new byte[10];
//int len;
//while((len = raf1.read(buffer)) != -1){
////info += new String(buffer,0,len);
//info.append(new String(buffer,0,len));
//}
//方式二：
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[10];
int len;
while((len = raf1.read(buffer)) != -1){
    baos.write(buffer, 0, len);
}

raf1.seek(5);
raf1.write(&amp;quot;xyz&amp;quot;.getBytes());
raf1.write(baos.toString().getBytes());
baos.close();
raf1.close();
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2-10-4-&quot;&gt;&lt;a name=&quot;10.4 流的基本应用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;10.4 流的基本应用&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;流是用来处理数据的。&lt;/li&gt;&lt;li&gt;处理数据时，一定要先明确数据源，与数据目的地 &lt;ul&gt;
&lt;li&gt;数据源可以是文件，可以是键盘。&lt;/li&gt;&lt;li&gt;数据目的地可以是文件、显示器或者其他设备。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;而流只是在帮助数据进行传输,并对传输的数据进行处理，比如过滤处理、 转换处理等。&lt;/li&gt;&lt;/ul&gt;
&lt;h1 id=&quot;h1-11-nio-2-path-paths-files-&quot;&gt;&lt;a name=&quot;11.NIO.2中Path、Paths、Files类的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.NIO.2中Path、Paths、Files类的使用&lt;/h1&gt;&lt;h2 id=&quot;h2-11-1-java-nio-&quot;&gt;&lt;a name=&quot;11.1 Java NIO 概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.1 Java NIO 概述&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Java NIO (New IO，Non-Blocking IO)是从Java 1.4版本开始引入的一套新 的IO API，可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目 的，但是使用的方式完全不同，NIO支持面向缓冲区的(IO是面向流的)、基于 通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。&lt;/li&gt;&lt;li&gt;Java API中提供了两套NIO，一套是针对标准输入输出NIO，另一套就是网 络编程NIO。 &lt;ul&gt;
&lt;li&gt;java.nio.channels.Channel&lt;ul&gt;
&lt;li&gt;FileChannel:处理本地文件 &lt;/li&gt;&lt;li&gt;SocketChannel：TCP网络编程的客户端的Channel &lt;/li&gt;&lt;li&gt;ServerSocketChannel:TCP网络编程的服务器端的Channel &lt;/li&gt;&lt;li&gt;DatagramChannel：UDP网络编程中发送端和接收端的Channel&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-11-2-nio-2&quot;&gt;&lt;a name=&quot;11.2 NIO. 2&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.2 NIO. 2&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;随着 JDK 7 的发布，Java对NIO进行了极大的扩展，增强了对 文件处理和文件系统特性的支持，以至于我们称他们为 NIO.2。 因为 NIO 提供的一些功能，NIO已经成为文件处理中越来越重要 的部分。&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-11-3-path-paths-files-api&quot;&gt;&lt;a name=&quot;11.3 Path、Paths和Files核心API&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.3 Path、Paths和Files核心API&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;早期的Java只提供了一个File类来访问文件系统，但File类的功能比较有限，所 提供的方法性能也不高。而且，大多数方法在出错时仅返回失败，并不会提供异 常信息。&lt;/li&gt;&lt;li&gt;NIO. 2为了弥补这种不足，引入了Path接口，代表一个平台无关的平台路径，描 述了目录结构中文件的位置。Path可以看成是File类的升级版本，实际引用的资 源也可以不存在。 &lt;/li&gt;&lt;li&gt;在以前IO操作都是这样写的: &lt;ul&gt;
&lt;li&gt;import java.io.File; &lt;/li&gt;&lt;li&gt;File file = new File(“index.html”);&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;但在Java7 中，我们可以这样写： &lt;ul&gt;
&lt;li&gt;import java.nio.file.Path;  &lt;/li&gt;&lt;li&gt;import java.nio.file.Paths;  &lt;/li&gt;&lt;li&gt;Path path = Paths.get(“index.html”);&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;同时，NIO.2在java.nio.file包下还提供了Files、Paths工具类，Files包含 了大量静态的工具方法来操作文件；Paths则包含了两个返回Path的静态 工厂方法。&lt;/li&gt;&lt;li&gt;Paths 类提供的静态 get() 方法用来获取 Path 对象：&lt;ul&gt;
&lt;li&gt;static Path get(String first, String … more) : 用于将多个字符串串连成路径&lt;/li&gt;&lt;li&gt;static Path get(URI uri): 返回指定uri对应的Path路径&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-11-4-path-&quot;&gt;&lt;a name=&quot;11.4 Path接口&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.4 Path接口&lt;/h2&gt;&lt;p&gt;​    Path 常用方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String toString() ： 返回调用 Path 对象的字符串表示形式&lt;/li&gt;&lt;li&gt;boolean startsWith(String path) : 判断是否以 path 路径开始&lt;/li&gt;&lt;li&gt;boolean endsWith(String path) : 判断是否以 path 路径结束&lt;/li&gt;&lt;li&gt;boolean isAbsolute() : 判断是否是绝对路径&lt;/li&gt;&lt;li&gt;Path getParent() ：返回Path对象包含整个路径，不包含 Path 对象指定的文件路径&lt;/li&gt;&lt;li&gt;Path getRoot() ：返回调用 Path 对象的根路径&lt;/li&gt;&lt;li&gt;Path getFileName() : 返回与调用 Path 对象关联的文件名&lt;/li&gt;&lt;li&gt;int getNameCount() : 返回Path 根目录后面元素的数量&lt;/li&gt;&lt;li&gt;Path getName(int idx) : 返回指定索引位置 idx 的路径名称&lt;/li&gt;&lt;li&gt;Path toAbsolutePath() : 作为绝对路径返回调用 Path 对象&lt;/li&gt;&lt;li&gt;Path resolve(Path p) :合并两个路径，返回合并后的路径对应的Path对象&lt;/li&gt;&lt;li&gt;File toFile(): 将Path转化为File类的对象&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2-11-5-files-&quot;&gt;&lt;a name=&quot;11.5 Files 类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;11.5 Files 类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;java.nio.file.Files 用于操作文件或目录的工具类。&lt;/li&gt;&lt;li&gt;Files常用方法：&lt;ul&gt;
&lt;li&gt;Path copy(Path src, Path dest, CopyOption … how) : 文件的复制&lt;/li&gt;&lt;li&gt;Path createDirectory(Path path, FileAttribute … attr) : 创建一个目录&lt;/li&gt;&lt;li&gt;Path createFile(Path path, FileAttribute … arr) : 创建一个文件&lt;/li&gt;&lt;li&gt;void delete(Path path) : 删除一个文件/目录，如果不存在，执行报错&lt;/li&gt;&lt;li&gt;void deleteIfExists(Path path) : Path对应的文件/目录如果存在，执行删除&lt;/li&gt;&lt;li&gt;Path move(Path src, Path dest, CopyOption…how) : 将 src 移动到 dest 位置&lt;/li&gt;&lt;li&gt;long size(Path path) : 返回 path 指定文件的大小&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Files常用方法：用于判断&lt;ul&gt;
&lt;li&gt;boolean exists(Path path, LinkOption … opts) : 判断文件是否存在&lt;/li&gt;&lt;li&gt;boolean isDirectory(Path path, LinkOption … opts) : 判断是否是目录&lt;/li&gt;&lt;li&gt;boolean isRegularFile(Path path, LinkOption … opts) : 判断是否是文件&lt;/li&gt;&lt;li&gt;boolean isHidden(Path path) : 判断是否是隐藏文件&lt;/li&gt;&lt;li&gt;boolean isReadable(Path path) : 判断文件是否可读&lt;/li&gt;&lt;li&gt;boolean isWritable(Path path) : 判断文件是否可写&lt;/li&gt;&lt;li&gt;boolean notExists(Path path, LinkOption … opts) : 判断文件是否不存在&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Files常用方法：用于操作内容&lt;ul&gt;
&lt;li&gt;eekableByteChannel newByteChannel(Path path, OpenOption…how) : 获取与指定文件的连 接，how 指定打开方式。&lt;/li&gt;&lt;li&gt;DirectoryStream newDirectoryStream(Path path) : 打开 path 指定的目录&lt;/li&gt;&lt;li&gt;InputStream newInputStream(Path path, OpenOption…how):获取 InputStream 对象&lt;/li&gt;&lt;li&gt;OutputStream newOutputStream(Path path, OpenOption…how) : 获取 OutputStream 对象&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
</description><pubDate>Sun, 17 Jul 2022 22:06:01 +0800</pubDate></item><item><title>集合</title><link>https://68686.ltd/?id=53</link><description>&lt;h2 id=&quot;h2--&quot;&gt;&lt;a name=&quot;一、集合框架的概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;一、集合框架的概述&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;集合、数组都是对多个数据进行存储操作的结构，简称Java容器。说明：此时的存储，主要指的是内存层面的存储，不涉及到持久化的存储（.txt,.jpg,.avi，数据库中）&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;数组在存储多个数据方面的特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一旦初始化以后，其长度就确定了。&lt;/li&gt;&lt;li&gt;数组一旦定义好，其元素的类型也就确定了。我们也就只能操作指定类型的数据了。&lt;/li&gt;&lt;li&gt;比如：String[] arr;int[] arr1;Object[] arr2;&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;数组在存储多个数据方面的缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一旦初始化以后，其长度就不可修改。&lt;/li&gt;&lt;li&gt;数组中提供的方法非常有限，对于添加、删除、插入数据等操作，非常不便，同时效率不高。&lt;/li&gt;&lt;li&gt;获取数组中实际元素的个数的需求，数组没有现成的属性或方法可用&lt;/li&gt;&lt;li&gt;数组存储数据的特点：有序、可重复。对于无序、不可重复的需求，不能满足。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id=&quot;h2--&quot;&gt;&lt;a name=&quot;二、集合框架&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;二、集合框架&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Collection接口：单列集合，用来存储一个一个的对象&lt;ul&gt;
&lt;li&gt;List接口：存储有序的、可重复的数据。&lt;ul&gt;
&lt;li&gt;ArrayList、LinkedList、Vector&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Set接口：存储无序的、不可重复的数据 &lt;ul&gt;
&lt;li&gt;HashSet、LinkedHashSet、TreeSet&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;Map接口：双列集合，用来存储一对(key - value)一对的数据  &lt;ul&gt;
&lt;li&gt;HashMap、LinkedHashMap、TreeMap、Hashtable、Properties&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2--collection-&quot;&gt;&lt;a name=&quot;三、Collection接口中的方法的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;三、Collection接口中的方法的使用&lt;/h2&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class CollectionTest {
    public static void main(String[] args) {
        Collection&amp;lt;String&amp;gt; strings = new ArrayList&amp;lt;&amp;gt;();
        Collection&amp;lt;String&amp;gt; tmp = new ArrayList&amp;lt;&amp;gt;();
        //add() 添加
        strings.add(&amp;quot;123&amp;quot;);
        tmp.add(&amp;quot;456&amp;quot;);
        tmp.add(&amp;quot;789&amp;quot;);
        //addAll() 添加另一个集合中的所有元素
        strings.addAll(tmp);
        //size() 获取集合中元素的个数
        System.out.println(strings.size());
        System.out.println(strings);
        //contains() 是否包含某个元素
        System.out.println(strings.contains(&amp;quot;123&amp;quot;));
        //contains() 是否包含另一个集合中的所有元素
        System.out.println(strings.containsAll(tmp));
        //clear() 清空集合
        strings.clear();
        System.out.println(strings);
        //isEmpty() 判断集合中有没有元素
        System.out.println(strings.isEmpty());
        strings.addAll(tmp);
        //remove() 移除指定元素
        strings.remove(&amp;quot;456&amp;quot;);
        System.out.println(strings);
        //removeAll() 从当前集合中移除另一个集合中的所有元素
        strings.removeAll(tmp);
        //hashCode() 返回当前对象的哈希值
        System.out.println(strings.hashCode());
        //equals() 判断两个集合是否相等
        System.out.println(strings.equals(tmp));
        //toArray() 将集合转化为数组
        Object[] objects = strings.toArray();
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;h2--list-&quot;&gt;&lt;a name=&quot;四、List接口的实现类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;四、List接口的实现类&lt;/h2&gt;&lt;h3 id=&quot;h3-arraylist&quot;&gt;&lt;a name=&quot;ArrayList&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;ArrayList&lt;/h3&gt;&lt;h4 id=&quot;h4-arraylist-&quot;&gt;&lt;a name=&quot;ArrayList的源码分析&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;ArrayList的源码分析&lt;/h4&gt;&lt;h5 id=&quot;h5-jdk7&quot;&gt;&lt;a name=&quot;JDK7&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;JDK7&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData&lt;/li&gt;&lt;li&gt;list.add(123);//elementData[0] = new Integer(123);&lt;/li&gt;&lt;li&gt;…&lt;/li&gt;&lt;li&gt;list.add(11);//如果此次的添加导致底层elementData数组容量不够，则扩容。&lt;/li&gt;&lt;li&gt;默认情况下，扩容为原来的容量的1.5倍，同时需要将原有数组中的数据复制到新的数组中。&lt;/li&gt;&lt;li&gt;结论：建议开发中使用带参的构造器：ArrayList list = new ArrayList(int capacity)&lt;/li&gt;&lt;/ul&gt;
&lt;h5 id=&quot;h5-jdk8-arraylist-&quot;&gt;&lt;a name=&quot;JDK8中ArrayList的变化：&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;JDK8中ArrayList的变化：&lt;/h5&gt;&lt;ul&gt;
&lt;li&gt;ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组&lt;/li&gt;&lt;li&gt;list.add(123);//第一次调用add()时，底层才创建了长度10的数组，并将数据123添加到elementData[0]&lt;/li&gt;&lt;li&gt;后续的添加和扩容操作与jdk 7 无异。&lt;/li&gt;&lt;/ul&gt;
&lt;h5 id=&quot;h5-u5C0Fu7ED3&quot;&gt;&lt;a name=&quot;小结&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;小结&lt;/h5&gt;&lt;p&gt;jdk7中的ArrayList的对象的创建类似于单例的饿汉式，而jdk8中的ArrayList的对象的创建类似于单例的懒汉式，延迟了数组的创建，节省内存。&lt;/p&gt;
&lt;h3 id=&quot;h3-linkedlist&quot;&gt;&lt;a name=&quot;LinkedList&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;LinkedList&lt;/h3&gt;&lt;h4 id=&quot;h4-linkedlist-&quot;&gt;&lt;a name=&quot;LinkedList的源码分析&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;LinkedList的源码分析&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性，默认值为null&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;list.add(123);//将123封装到Node中，创建了Node对象。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;其中，Node定义为：体现了LinkedList的双向链表的说法&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;private static class Node&amp;lt;E&amp;gt; {
    E item;
    Node&amp;lt;E&amp;gt; next;
    Node&amp;lt;E&amp;gt; prev;
    Node(Node&amp;lt;E&amp;gt; prev, E element, Node&amp;lt;E&amp;gt; next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-vector&quot;&gt;&lt;a name=&quot;Vector&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;Vector&lt;/h3&gt;&lt;h4 id=&quot;h4-vector-&quot;&gt;&lt;a name=&quot;Vector的源码分析&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;Vector的源码分析&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;jdk7和jdk8中通过Vector()构造器创建对象时，底层都创建了长度为10的数组。&lt;/li&gt;&lt;li&gt;在扩容方面，默认扩容为原来的数组长度的2倍。&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u4E09u8005u7684u5F02u540C&quot;&gt;&lt;a name=&quot;三者的异同&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;三者的异同&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;相同点&lt;ul&gt;
&lt;li&gt;都是实现了List接口&lt;/li&gt;&lt;li&gt;存储数据的特点相同，都是存储有序的、可重复的数据&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;不同点&lt;ul&gt;
&lt;li&gt;ArrayList：线程不安全的，效率高；底层使用Object[]存储&lt;/li&gt;&lt;li&gt;LinkedList：对于频繁的插入删除操作，使用此类效率比ArrayList高；底层使用双向链表存储&lt;/li&gt;&lt;li&gt;Vector：线程安全的，效率低；底层使用Object[]存储&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-list-&quot;&gt;&lt;a name=&quot;List接口中的常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;List接口中的常用方法&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;void add(int index, Object ele):在index位置插入ele元素&lt;/li&gt;&lt;li&gt;boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来&lt;/li&gt;&lt;li&gt;Object get(int index):获取指定index位置的元素&lt;/li&gt;&lt;li&gt;int indexOf(Object obj):返回obj在集合中首次出现的位置&lt;/li&gt;&lt;li&gt;int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置&lt;/li&gt;&lt;li&gt;Object remove(int index):移除指定index位置的元素，并返回此元素&lt;/li&gt;&lt;li&gt;Object set(int index, Object ele):设置指定index位置的元素为ele&lt;/li&gt;&lt;li&gt;List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3--&quot;&gt;&lt;a name=&quot;总结：常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;总结：常用方法&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;增：add(Object obj)&lt;/li&gt;&lt;li&gt;删：remove(int index) / remove(Object obj)&lt;/li&gt;&lt;li&gt;改：set(int index, Object ele)&lt;/li&gt;&lt;li&gt;查：get(int index)&lt;/li&gt;&lt;li&gt;插：add(int index, Object ele)&lt;/li&gt;&lt;li&gt;长度：size()&lt;/li&gt;&lt;li&gt;遍历&lt;ul&gt;
&lt;li&gt;Iterator迭代器方式&lt;/li&gt;&lt;li&gt;增强for循环&lt;/li&gt;&lt;li&gt;普通的循环&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2--set-&quot;&gt;&lt;a name=&quot;五、Set接口的实现类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;五、Set接口的实现类&lt;/h2&gt;&lt;h3 id=&quot;h3-1-set-&quot;&gt;&lt;a name=&quot;1.Set接口的实现类的结构&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.Set接口的实现类的结构&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Collection接口：单列集合，用来存储一个一个的对象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Set接口：存储无序的、不可重复的数据   –&amp;gt;高中讲的“集合”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HashSet：作为Set接口的主要实现类；线程不安全的；可以存储null值&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LinkedHashSet：作为HashSet的子类；遍历其内部数据时，可以按照添加的顺序遍历，对于频繁的遍历操作，LinkedHashSet效率高于HashSet.&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;TreeSet：可以按照添加对象的指定属性，进行排序。&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-set-&quot;&gt;&lt;a name=&quot;2.Set接口的注意事项&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.Set接口的注意事项&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Set接口中没有额外定义新的方法，使用的都是Collection中声明过的方法。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;向Set(主要指：HashSet、LinkedHashSet)中添加的数据，其所在的类一定要重写hashCode()和equals()&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;重写的hashCode()和equals()尽可能保持一致性：相等的对象必须具有相等的散列码&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;重写两个方法的小技巧：对象中用作 equals() 方法比较的 Field，都应该用来计算 hashCode 值。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;Set：存储无序的、不可重复的数据&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;无序性：不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加，而是根据数据的哈希值决定的。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;不可重复性：保证添加的元素按照equals()判断时，不能返回true.即：相同的元素只能添加一个。&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-3-hashset&quot;&gt;&lt;a name=&quot;3.HashSet&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.HashSet&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;添加元素的过程&lt;/li&gt;&lt;li&gt;我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法，计算元素a的哈希值，此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置（即为：索引位置），判断数组此位置上是否已经有元素：&lt;ul&gt;
&lt;li&gt;如果此位置上没有其他元素，则元素a添加成功。 —&amp;gt;情况1&lt;/li&gt;&lt;li&gt;如果此位置上有其他元素b(或以链表形式存在的多个元素），则比较元素a与元素b的hash值：&lt;ul&gt;
&lt;li&gt;如果hash值不相同，则元素a添加成功。—&amp;gt;情况2&lt;/li&gt;&lt;li&gt;如果hash值相同，进而需要调用元素a所在类的equals()方法&lt;ul&gt;
&lt;li&gt;equals()返回true,元素a添加失败&lt;/li&gt;&lt;li&gt;equals()返回false,则元素a添加成功。—&amp;gt;情况3&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;对于添加成功的情况2和情况3而言：元素a 与已经存在指定索引位置上数据以链表的方式存储。&lt;/li&gt;&lt;li&gt;jdk 7 :元素a放到数组中，指向原来的元素。&lt;/li&gt;&lt;li&gt;jdk 8 :原来的元素在数组中，指向元素a&lt;/li&gt;&lt;li&gt;总结：七上八下&lt;/li&gt;&lt;li&gt;HashSet底层：数组+链表的结构。&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-4-linkedhashset&quot;&gt;&lt;a name=&quot;4.LinkedHashSet&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.LinkedHashSet&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;LinkedHashSet的使用&lt;/li&gt;&lt;li&gt;LinkedHashSet作为HashSet的子类，在添加数据的同时，每个数据还维护了两个引用，记录此数据前一个数据和后一个数据。&lt;/li&gt;&lt;li&gt;优点：对于频繁的遍历操作，LinkedHashSet效率高于HashSet&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-5-treeset&quot;&gt;&lt;a name=&quot;5.TreeSet&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.TreeSet&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;向TreeSet中添加的数据，要求是相同类的对象。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;两种排序方式：自然排序（实现Comparable接口） 和 定制排序（Comparator）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;自然排序中，比较两个对象是否相同的标准为：compareTo()返回0.不再是equals()&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;定制排序中，比较两个对象是否相同的标准为：compare()返回0.不再是equals().&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ol&gt;
&lt;h2 id=&quot;h2--map-&quot;&gt;&lt;a name=&quot;六、Map接口的实现类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;六、Map接口的实现类&lt;/h2&gt;&lt;h3 id=&quot;h3-1-map-&quot;&gt;&lt;a name=&quot;1. Map的实现类的结构&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1. Map的实现类的结构&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Map:双列数据，存储key-value对的数据&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HashMap:作为Map的主要实现类；线程不安全的，效率高；存储null的key和value&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;LinkedHashMap:保证在遍历map元素时，可以按照添加的顺序实现遍历。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原因：在原有的HashMap底层结构基础上，添加了一对指针，指向前一个和后一个元素。对于频繁的遍历操作，此类执行效率高于HashMap。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;TreeMap:保证按照添加的key-value对进行排序，实现排序遍历。此时考虑key的自然排序或定制排序，底层使用红黑树&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;Hashtable:作为古老的实现类；线程安全的，效率低；不能存储null的key和value&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Properties:常用来处理配置文件。key和value都是String类型&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;HashMap的底层：数组+链表  （jdk7及之前）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数组+链表+红黑树 （jdk 8）&lt;/li&gt;&lt;li&gt;面试题&lt;ul&gt;
&lt;li&gt;HashMap的底层实现原理？&lt;/li&gt;&lt;li&gt;HashMap 和 Hashtable的异同？&lt;/li&gt;&lt;li&gt;CurrentHashMap 与 Hashtable的异同？&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-2-map-&quot;&gt;&lt;a name=&quot;2.Map结构的理解&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;2.Map结构的理解&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Map中的key:无序的、不可重复的，使用Set存储所有的key  —&amp;gt; key所在的类要重写equals()和hashCode() （以HashMap为例）&lt;/li&gt;&lt;li&gt;Map中的value:无序的、可重复的，使用Collection存储所有的value —&amp;gt;value所在的类要重写equals()&lt;/li&gt;&lt;li&gt;一个键值对：key-value构成了一个Entry对象。&lt;/li&gt;&lt;li&gt;Map中的entry:无序的、不可重复的，使用Set存储所有的entry&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-3-hashmap-&quot;&gt;&lt;a name=&quot;3.HashMap的底层实现原理&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;3.HashMap的底层实现原理&lt;/h3&gt;&lt;p&gt;以jdk7为例说明&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HashMap map = new HashMap():&lt;/li&gt;&lt;li&gt;在实例化以后，底层创建了长度是16的一维数组Entry[] table。&lt;/li&gt;&lt;li&gt;…可能已经执行过多次put…&lt;/li&gt;&lt;li&gt;map.put(key1,value1):&lt;/li&gt;&lt;li&gt;首先，调用key1所在类的hashCode()计算key1哈希值，此哈希值经过某种算法计算以后，得到在Entry数组中的存放位置。&lt;/li&gt;&lt;li&gt;如果此位置上的数据为空，此时的key1-value1添加成功。 —-情况1&lt;/li&gt;&lt;li&gt;如果此位置上的数据不为空，(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据&lt;/li&gt;&lt;li&gt;的哈希值：&lt;/li&gt;&lt;li&gt;如果key1的哈希值与已经存在的数据的哈希值都不相同，此时key1-value1添加成功。—-情况2&lt;/li&gt;&lt;li&gt;如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同，继续比较：调用key1所在类的equals(key2)方法，比较：&lt;/li&gt;&lt;li&gt;如果equals()返回false:此时key1-value1添加成功。—-情况3&lt;/li&gt;&lt;li&gt;如果equals()返回true:使用value1替换value2。&lt;/li&gt;&lt;li&gt;补充：关于情况2和情况3：此时key1-value1和原来的数据以链表的方式存储。&lt;/li&gt;&lt;li&gt;在不断的添加过程中，会涉及到扩容问题，当超出临界值(且要存放的位置非空)时，扩容。默认的扩容方式：扩容为原来容量的2倍，并将原有的数据复制过来。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;jdk8 相较于jdk7在底层实现方面的不同：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;new HashMap():底层没有创建一个长度为16的数组&lt;/li&gt;&lt;li&gt;jdk 8底层的数组是：Node[],而非Entry[]&lt;/li&gt;&lt;li&gt;首次调用put()方法时，底层创建长度为16的数组&lt;/li&gt;&lt;li&gt;jdk7底层结构只有：数组+链表。jdk8中底层结构：数组+链表+红黑树。&lt;ul&gt;
&lt;li&gt;形成链表时，七上八下（jdk7:新的元素指向旧的元素。jdk8：旧的元素指向新的元素）&lt;/li&gt;&lt;li&gt;当数组的某一个索引位置上的元素以链表形式存在的数据个数 &amp;gt;= 8 且当前数组的长度 &amp;gt; =64时，此时此索引位置上的所数据改为使用红黑树存储。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;DEFAULT_INITIAL_CAPACITY : HashMap的默认容量，16&lt;/li&gt;&lt;li&gt;DEFAULT_LOAD_FACTOR：HashMap的默认加载因子：0.75&lt;/li&gt;&lt;li&gt;threshold：扩容的临界值，=容量*填充因子：16 * 0.75 =&amp;gt; 12&lt;/li&gt;&lt;li&gt;TREEIFY_THRESHOLD：Bucket中链表长度大于该默认值，转化为红黑树:8&lt;/li&gt;&lt;li&gt;MIN_TREEIFY_CAPACITY：桶中的Node被树化时最小的hash表容量:64&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-4-linkedhashmap-&quot;&gt;&lt;a name=&quot;4.LinkedHashMap的底层实现原理（了解）&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;4.LinkedHashMap的底层实现原理（了解）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;源码中：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;static class Entry&amp;lt;K,V&amp;gt; extends HashMap.Node&amp;lt;K,V&amp;gt; {
    Entry&amp;lt;K,V&amp;gt; before, after;//能够记录添加的元素的先后顺序
    Entry(int hash, K key, V value, Node&amp;lt;K,V&amp;gt; next) {
        super(hash, key, value, next);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-5-map-&quot;&gt;&lt;a name=&quot;5.Map中定义的方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;5.Map中定义的方法&lt;/h3&gt;&lt;p&gt; 添加、删除、修改操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object put(Object key,Object value)：将指定key-value添加到(或修改)当前map对象中&lt;/li&gt;&lt;li&gt;void putAll(Map m):将m中的所有key-value对存放到当前map中&lt;/li&gt;&lt;li&gt;Object remove(Object key)：移除指定key的key-value对，并返回value&lt;/li&gt;&lt;li&gt;void clear()：清空当前map中的所有数据&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;元素查询的操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Object get(Object key)：获取指定key对应的value&lt;/li&gt;&lt;li&gt;boolean containsKey(Object key)：是否包含指定的key&lt;/li&gt;&lt;li&gt;boolean containsValue(Object value)：是否包含指定的value&lt;/li&gt;&lt;li&gt;int size()：返回map中key-value对的个数&lt;/li&gt;&lt;li&gt;boolean isEmpty()：判断当前map是否为空&lt;/li&gt;&lt;li&gt;boolean equals(Object obj)：判断当前map和参数对象obj是否相等&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;元视图操作的方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set keySet()：返回所有key构成的Set集合&lt;/li&gt;&lt;li&gt;Collection values()：返回所有value构成的Collection集合&lt;/li&gt;&lt;li&gt;Set entrySet()：返回所有key-value对构成的Set集合&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-6-&quot;&gt;&lt;a name=&quot;6.总结：常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;6.总结：常用方法&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;添加：put(Object key,Object value)&lt;/li&gt;&lt;li&gt;删除：remove(Object key)&lt;/li&gt;&lt;li&gt;修改：put(Object key,Object value)&lt;/li&gt;&lt;li&gt;查询：get(Object key)&lt;/li&gt;&lt;li&gt;长度：size()&lt;/li&gt;&lt;li&gt;遍历：keySet() / values() / entrySet()&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2--collections-&quot;&gt;&lt;a name=&quot;七、Collections工具类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;七、Collections工具类&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Collections:操作Collection、Map的工具类&lt;/li&gt;&lt;li&gt;面试题：Collection 和 Collections的区别？&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-1-&quot;&gt;&lt;a name=&quot;1.常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;1.常用方法&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;reverse(List)：反转 List 中元素的顺序&lt;/li&gt;&lt;li&gt;shuffle(List)：对 List 集合元素进行随机排序&lt;/li&gt;&lt;li&gt;sort(List)：根据元素的自然顺序对指定 List 集合元素按升序排序&lt;/li&gt;&lt;li&gt;sort(List，Comparator)：根据指定的 Comparator 产生的顺序对 List 集合元素进行排序&lt;/li&gt;&lt;li&gt;swap(List，int， int)：将指定 list 集合中的 i 处元素和 j 处元素进行交换&lt;/li&gt;&lt;li&gt;Object max(Collection)：根据元素的自然顺序，返回给定集合中的最大元素&lt;/li&gt;&lt;li&gt;Object max(Collection，Comparator)：根据 Comparator 指定的顺序，返回给定集合中的最大元素&lt;/li&gt;&lt;li&gt;Object min(Collection)&lt;/li&gt;&lt;li&gt;Object min(Collection，Comparator)&lt;/li&gt;&lt;li&gt;int frequency(Collection，Object)：返回指定集合中指定元素的出现次数&lt;/li&gt;&lt;li&gt;void copy(List dest,List src)：将src中的内容复制到dest中&lt;/li&gt;&lt;li&gt;boolean replaceAll(List list，Object oldVal，Object newVal)：使用新值替换 List 对象的所有旧值&lt;/li&gt;&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;Collections 类中提供了多个 synchronizedXxx() 方法，该方法可使将指定集合包装成线程同步的集合，从而可以解决多线程并发访问集合时的线程安全问题，返回的List即为线程安全的List&lt;/li&gt;&lt;/ul&gt;
</description><pubDate>Fri, 15 Jul 2022 15:59:33 +0800</pubDate></item><item><title>枚举类与注解</title><link>https://68686.ltd/?id=52</link><description>&lt;h2 id=&quot;h2-u679Au4E3Eu7C7B&quot;&gt;&lt;a name=&quot;枚举类&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;枚举类&lt;/h2&gt;&lt;h3 id=&quot;h3-u679Au4E3Eu7C7Bu7684u4F7Fu7528&quot;&gt;&lt;a name=&quot;枚举类的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;枚举类的使用&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;类的对象只有有限个，确定的，举例如下：&lt;/p&gt;
&lt;p&gt;星期:Monday(星期一)、……、Sunday(星期天)性别:Man(男)、Woman(女)&lt;br&gt;季节:Spring(春节)…….Winter(冬天)&lt;br&gt;支付方式:Cash (现金)、WeChatPay (微信）、Alipay(支付宝)、BankCard(银行卡)、CreditCard(信用卡)&lt;br&gt;就职状态:Busy、Free、 Vocation、Dimission&lt;br&gt;订单状态:Nonpayment（未付款)、Paid (已付款）、Fulfilled(已配货）、Delivered（已发货）、Return(退货)、Checked（已确认)&lt;br&gt;线程状态:创建、就绪、运行、阻塞、死亡&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;当需要定义一组常量时，强烈建议一使用枚举类&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果枚举类中只有一个对象，则可以作为单例模式的实现方式&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u679Au4E3Eu7C7Bu7684u5B9Au4E49&quot;&gt;&lt;a name=&quot;枚举类的定义&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;枚举类的定义&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;方式一：jdk5.0之前，自定义枚举类&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Season {
    //声明Season的属性，并用private final修饰
    private final String seasonName;
    //私有化Season的构造器，并给对象的属性赋值
    private Season(String seasonName) {
        this.seasonName = seasonName;
    }
    //提供当前枚举类的多个对象：public static final
    public static final Season SPRING = new Season(&amp;quot;春天&amp;quot;);
    public static final Season SUMMER = new Season(&amp;quot;夏天&amp;quot;);
    public static final Season AUTUMN = new Season(&amp;quot;秋天&amp;quot;);
    public static final Season WINTER = new Season(&amp;quot;冬天&amp;quot;);
    //提供其他方法
    public String getSeasonName() {
        return seasonName;
    }
    @Override
    public String toString() {
        return &amp;quot;Season{&amp;quot; +
                &amp;quot;seasonName=&amp;#39;&amp;quot; + seasonName + &amp;#39;\&amp;#39;&amp;#39; +
                &amp;#39;}&amp;#39;;
    }
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
        System.out.println(spring.getSeasonName());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;方式二：jdk5.0，可以是哟个enum关键字定义枚举类&lt;/li&gt;&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;//使用enum关键字定义枚举类
//定义的枚举类默认继承于java.lang.Enum
enum Season1 {
    //提供当前类的多个对象，多个对象之间用&amp;quot;,&amp;quot;隔开，末尾用&amp;quot;;&amp;quot;结束
    SPRING(&amp;quot;春天&amp;quot;),
    SUMMER(&amp;quot;夏天&amp;quot;),
    AUTUMN(&amp;quot;秋天&amp;quot;),
    WINTER(&amp;quot;冬天&amp;quot;);
    //声明Season的属性，并用private final修饰
    private final String seasonName;
    //私有化Season的构造器，并给对象的属性赋值，默认为private
    Season1(String seasonName) {
        this.seasonName = seasonName;
    }
    //提供其他方法
    public String getSeasonName() {
        return seasonName;
    }
//    @Override
//    public String toString() {
//        return &amp;quot;Season{&amp;quot; +
//                &amp;quot;seasonName=&amp;#39;&amp;quot; + seasonName + &amp;#39;\&amp;#39;&amp;#39; +
//                &amp;#39;}&amp;#39;;
//    }
    public static void main(String[] args) {
        Season1 summer = Season1.SUMMER;
        System.out.println(summer);
        System.out.println(summer.getSeasonName());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-enum-&quot;&gt;&lt;a name=&quot;Enum类中的常用方法&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;Enum类中的常用方法&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;values()方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是，会有运行时异常llegalArgumentException&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;toString():返回当前枚举类对象常量的名称&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3--enum-&quot;&gt;&lt;a name=&quot;使用enum关键字定义的枚举类实现接口的情况&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;使用enum关键字定义的枚举类实现接口的情况&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;情况一：实现接口，在enum类中实现抽象方法&lt;/li&gt;&lt;li&gt;让枚举类的对象分别实现接口中的抽象方法&lt;/li&gt;&lt;/ul&gt;
&lt;h2 id=&quot;h2--annotation-&quot;&gt;&lt;a name=&quot;注解(Annotation)&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;注解(Annotation)&lt;/h2&gt;&lt;h3 id=&quot;h3-u6CE8u89E3u7684u6982u8FF0&quot;&gt;&lt;a name=&quot;注解的概述&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;注解的概述&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;理解Annotation&lt;ul&gt;
&lt;li&gt;jdk 5.0 新增的功能&lt;/li&gt;&lt;li&gt;Annotation其实就是代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过使用Annotation,程序员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具、开发工具和部署工具可以通过这些补充信息进行验证或者进行部署。&lt;/li&gt;&lt;li&gt;Annotation 可以像修饰符一样被使用,可用于修饰包,类,构造器,方法,成员变量,参数,局部变量的声明,这些信息被保存在Annotation的“name=value”对中。&lt;/li&gt;&lt;li&gt;在JavaSE中，注解的使用目的比较简单，例如标记过时的功能，&lt;br&gt;忽略警告等。在JavaEE/Android中注解占据了更重要的角色，例如用来配置应用程序的任何切面，代替JavaEE旧版中所遗留的繁冗&lt;br&gt;代码和XML配置等。&lt;/li&gt;&lt;li&gt;未来的开发模式都是基于注解的，JPA是基于注解的，&lt;br&gt;Spring2.5以&lt;br&gt;上都是基于注解的，Hibernate3.x以后也是基于注解的，现在的Struts2有一部分也是基于注解的了，注解是一种趋势，一定程度上可以说:框架=注解＋反射＋设计模式。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u6CE8u89E3u7684u4F7Fu7528&quot;&gt;&lt;a name=&quot;注解的使用&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;注解的使用&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;示例一:生成文档相关的注解&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/author&quot; title=&quot;&amp;#64;author&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/author&quot; title=&quot;&amp;#64;author&quot; class=&quot;at-link&quot;&gt;@author&lt;/a&gt;&lt;/a&gt;标明开发该类模块的作者，多个作者之间使用,分割&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/version&quot; title=&quot;&amp;#64;version&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/version&quot; title=&quot;&amp;#64;version&quot; class=&quot;at-link&quot;&gt;@version&lt;/a&gt;&lt;/a&gt;标明该类模块的版本&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/see&quot; title=&quot;&amp;#64;see&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/see&quot; title=&quot;&amp;#64;see&quot; class=&quot;at-link&quot;&gt;@see&lt;/a&gt;&lt;/a&gt;参考转向，也就是相关主题&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/since&quot; title=&quot;&amp;#64;since&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/since&quot; title=&quot;&amp;#64;since&quot; class=&quot;at-link&quot;&gt;@since&lt;/a&gt;&lt;/a&gt;从哪个版本开始增加的&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;@param&lt;/a&gt;&lt;/a&gt;对方法中某参数的说明，如果没有参数就不能写&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;@return&lt;/a&gt;&lt;/a&gt;对方法返回值的说明，如果方法的返回值类型是void就不能写&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;@exception&lt;/a&gt;&lt;/a&gt;对方法可能抛出的异常进行说明，如果方法没有用throws显式抛出的异常就不能写&lt;/li&gt;&lt;li&gt;其中&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;@param&lt;/a&gt;&lt;/a&gt;&lt;/a&gt; &lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;@return&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;和&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;@exception&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;这三个标记都是只用于方法的。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;@param&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;的格式要求:&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;@param&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;形参名形参类型形参说明&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;@return&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;的格式要求: &lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/return&quot; title=&quot;&amp;#64;return&quot; class=&quot;at-link&quot;&gt;@return&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;返回值类型返回值说明&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;@exception&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;的格式要求:&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;@exception&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;异常类型异常说明&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/param&quot; title=&quot;&amp;#64;param&quot; class=&quot;at-link&quot;&gt;@param&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;和&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/exception&quot; title=&quot;&amp;#64;exception&quot; class=&quot;at-link&quot;&gt;@exception&lt;/a&gt;&lt;/a&gt;&lt;/a&gt;可以并列多个&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;示例二:在编译时进行格式检查(JDK内置的三个基本注解)&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/override&quot; title=&quot;&amp;#64;override&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/override&quot; title=&quot;&amp;#64;override&quot; class=&quot;at-link&quot;&gt;@override&lt;/a&gt;&lt;/a&gt;:限定重写父类方法,该注解只能用于方法&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Deprecated&quot; title=&quot;&amp;#64;Deprecated&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Deprecated&quot; title=&quot;&amp;#64;Deprecated&quot; class=&quot;at-link&quot;&gt;@Deprecated&lt;/a&gt;&lt;/a&gt;:用于表示所修饰的元素(类,方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/suppressWarnings&quot; title=&quot;&amp;#64;suppressWarnings&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/suppressWarnings&quot; title=&quot;&amp;#64;suppressWarnings&quot; class=&quot;at-link&quot;&gt;@suppressWarnings&lt;/a&gt;&lt;/a&gt;: 抑制编译器警告&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;示例三:跟踪代码依赖性，实现替代配置文件功能&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u81EAu5B9Au4E49u6CE8u89E3&quot;&gt;&lt;a name=&quot;自定义注解&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;自定义注解&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;定义新的Annotation类型使用&lt;a href=&quot;https://github.com/interface&quot; title=&quot;&amp;#64;interface&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/interface&quot; title=&quot;&amp;#64;interface&quot; class=&quot;at-link&quot;&gt;@interface&lt;/a&gt;&lt;/a&gt;关键字&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;自定义注解自动继承了java.lang.annotation.Annotation接口&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;Annotation 的成员变量在 Annotation定义中以无参数方法的形式来声明。其方法名和返回值定义了该成员的名字和类型。我们称为配置参数。类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组。&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;可以在定义Annotation 的成员变量时为其指定初始值，指定成员变量的初始值可使用default关键字&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果只有一个参数成员，建议使用参数名为value&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果定义的注解含有配置参数，那么使用时必须指定参数值，除非它有默认值。格式是“参数名–参数值”，如果只有一个参数成员，且名称为value，可以省略“value=”&lt;/p&gt;
&lt;/li&gt;&lt;li&gt;&lt;p&gt;没有成员定义的 Annotation称为标记;包含成员变量的Annotation称为元数据Annotation&lt;/p&gt;
&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;  注意:自定义注解必须配上注解的信息处理流程才有意义&lt;/p&gt;
&lt;p&gt;  自定义注解通常会使用两个元注解：Retention、Target&lt;/p&gt;
&lt;h3 id=&quot;h3-jdk-&quot;&gt;&lt;a name=&quot;JDK中的元注解&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;JDK中的元注解&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;JDK的元Annotation用于修饰其他Annotation定义&lt;/li&gt;&lt;li&gt;JDK5.0提供了4个标准的meta-annotation类型，分别是:&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Retention&quot; title=&quot;&amp;#64;Retention&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Retention&quot; title=&quot;&amp;#64;Retention&quot; class=&quot;at-link&quot;&gt;@Retention&lt;/a&gt;&lt;/a&gt;:只能用于修饰一个AnnotatIon的生命周期,&lt;a href=&quot;https://github.com/Rentention&quot; title=&quot;&amp;#64;Rentention&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Rentention&quot; title=&quot;&amp;#64;Rentention&quot; class=&quot;at-link&quot;&gt;@Rentention&lt;/a&gt;&lt;/a&gt;包含一个RetentionPolicy类型的成员变量,使用&lt;a href=&quot;https://github.com/Rentention&quot; title=&quot;&amp;#64;Rentention&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Rentention&quot; title=&quot;&amp;#64;Rentention&quot; class=&quot;at-link&quot;&gt;@Rentention&lt;/a&gt;&lt;/a&gt;时必须为该value成员变量指定值:&lt;ul&gt;
&lt;li&gt;RetentionPolicy.SOURCE:在源文件中有效（即源文件保留），编译器直接丢弃这种策略的注释&lt;/li&gt;&lt;li&gt;RetentionPolicy.CLASS: 在class中有效(即class保留)，当运行Java程序时，JVM不会保留注解。这是默认值&lt;/li&gt;&lt;li&gt;RetentionPolicy.RUNTIME:在运行时有效（即运行时保留），当运行Java程序时, JVM会保留注释。程序可以通过反射获取该注释。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Target&quot; title=&quot;&amp;#64;Target&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Target&quot; title=&quot;&amp;#64;Target&quot; class=&quot;at-link&quot;&gt;@Target&lt;/a&gt;&lt;/a&gt;:用于修饰Annotation定义,用于指定被修饰的Annotation能用于修饰哪些程序元素。&lt;a href=&quot;https://github.com/Target&quot; title=&quot;&amp;#64;Target&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Target&quot; title=&quot;&amp;#64;Target&quot; class=&quot;at-link&quot;&gt;@Target&lt;/a&gt;&lt;/a&gt;也包含一个名为value的成员变量。&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/Documented&quot; title=&quot;&amp;#64;Documented&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/Documented&quot; title=&quot;&amp;#64;Documented&quot; class=&quot;at-link&quot;&gt;@Documented&lt;/a&gt;&lt;/a&gt;:表示所修饰的注解在被javadoc解析时，保留下来&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://github.com/lnherited&quot; title=&quot;&amp;#64;lnherited&quot; class=&quot;at-link&quot;&gt;&lt;a href=&quot;https://github.com/lnherited&quot; title=&quot;&amp;#64;lnherited&quot; class=&quot;at-link&quot;&gt;@lnherited&lt;/a&gt;&lt;/a&gt;:被它修饰的Annotation将具有继承性&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;li&gt;元数据的理解:&lt;br&gt;String name = “zs”;&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u901Au8FC7u53CDu5C04u83B7u53D6u6CE8u89E3u4FE1u606F&quot;&gt;&lt;a name=&quot;通过反射获取注解信息&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;通过反射获取注解信息&lt;/h3&gt;&lt;h3 id=&quot;h3-jdk8-&quot;&gt;&lt;a name=&quot;JDK8中注解的新特性&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;JDK8中注解的新特性&lt;/h3&gt;&lt;h4 id=&quot;h4-u53EFu91CDu590Du6CE8u89E3&quot;&gt;&lt;a name=&quot;可重复注解&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;可重复注解&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;在MyAnnotation中声明&lt;a href=&quot;https://github.com/Repeatable&quot; title=&quot;&amp;#64;Repeatable&quot; class=&quot;at-link&quot;&gt;@Repeatable&lt;/a&gt; (MyAnnotations.class)&lt;/li&gt;&lt;li&gt;MyAnnotation的Target和Retention等元注解和MyAnnotations相同&lt;/li&gt;&lt;/ul&gt;
&lt;h4 id=&quot;h4-u7C7Bu578Bu6CE8u89E3&quot;&gt;&lt;a name=&quot;类型注解&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;类型注解&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;JDK1.8之后，关于元注解&lt;a href=&quot;https://github.com/Target&quot; title=&quot;&amp;#64;Target&quot; class=&quot;at-link&quot;&gt;@Target&lt;/a&gt;的参数类型ElementType枚举值多了两个:TYPE_PARAMETER,TYPE_USE。&lt;/li&gt;&lt;li&gt;在Java 8之前，注解只能是在声明的地方所使用，Java8开始，注解可以应用在任何地方。&lt;ul&gt;
&lt;li&gt;ElementType.TYPE_PARAMETER表示该注解能写在类型变量的声明语句中（如:泛型声明)。&lt;/li&gt;&lt;li&gt;ElementType.TYPE_USE表示该注解能写在使用类型的任何语句中。&lt;/li&gt;&lt;/ul&gt;
&lt;/li&gt;&lt;/ul&gt;
</description><pubDate>Thu, 14 Jul 2022 11:19:30 +0800</pubDate></item><item><title>单例模式</title><link>https://68686.ltd/?id=51</link><description>&lt;h2 id=&quot;h2--singleton-&quot;&gt;&lt;a name=&quot;单例(Singleton)模式&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;单例(Singleton)模式&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。式就像是经典的棋谱，不同的棋局，我们用不同的棋谱，”套路”&lt;/li&gt;&lt;li&gt;所谓类的单例设计模式，就是采取一定的方法保证在整个的软件系统中，对某个类只能存在一个对象实例，并且该类只提供一个取得其对象实例的方法如果我们要让类在一个虚拟机中只能产生一个对象，我们首先必须将类的构造器的访问权限设置为private，这样，就不能用new操作符在类的外部产生类的对象了，但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象，只能调用该类的某个静态方法以返回类内部创建的对象，静态方法只能访问类中的静态成员变量，所以，指向类内部产生的该类对象的变量也必须定义成静态的。&lt;/li&gt;&lt;li&gt;单例模式的优点：由于单例模式只生成一个实例，减少了系统性能开销，当对一个对象的产生需要比较多的资源时，如读取配置、产生其他依赖对象时，则可以通过在应用启动时直接产生一个单例对象，然后永久驻留内存的方式来解决&lt;/li&gt;&lt;li&gt;网站的计数器，一般也是单例模式实现，否则难以同步。&lt;/li&gt;&lt;li&gt;应用程序的日志应用，一般都使用单例模式实现，这一般是由于共享的日志文件一直处于打开状态，因为只能有一个实例去操作，否则内容不好追加。&lt;/li&gt;&lt;li&gt;数据库连接池的设计一般也是采用单例模式，因为数据库连接是一种数据库资源。&lt;/li&gt;&lt;li&gt;项目中，读取配置文件的类，一般也只有一个对象。没有必要每次使用配置文件数据，都生成一个对象去读取。&lt;/li&gt;&lt;li&gt;Application也是单例的典型应用&lt;/li&gt;&lt;li&gt;Windows的Task Manager(任务管理器)就是很典型的单例模式&lt;/li&gt;&lt;li&gt;Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中，回收站一直维护着仅有的一个实例。&lt;/li&gt;&lt;/ul&gt;
&lt;h3 id=&quot;h3-u997Fu6C49u5F0F&quot;&gt;&lt;a name=&quot;饿汉式&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;饿汉式&lt;/h3&gt;&lt;p&gt;好处：饿汉式是线程安全的&lt;/p&gt;
&lt;p&gt;坏处：对象加载时间过长&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Order {
    //1.私有化类的构造器
    private Order() {
    }
    //2.声明当前类的对象,类对象必须是static的
    private static Order instance = new Order();
    //3.声明public、static的方法返回当前类对象
    public static Order getInstance() {
        return instance;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;h3-u61D2u6C49u5F0F&quot;&gt;&lt;a name=&quot;懒汉式&quot; class=&quot;reference-link&quot; href=&quot;#&quot;&gt;&lt;/a&gt;&lt;span class=&quot;header-link octicon octicon-link&quot;&gt;&lt;/span&gt;懒汉式&lt;/h3&gt;&lt;p&gt;好处：延迟对象的创建&lt;/p&gt;
&lt;p&gt;当前写法的坏处：线程不安全&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;class Order1 {
    //1.私有化类的构造器
    private Order1() {
    }
    //2.声明当前类的对象,类对象必须是static的
    private static Order1 instance = null;
    //3.声明public、static的方法返回当前类对象
    public static Order1 getInstance() {
        if (instance == null) {
            instance = new Order1();
        }
        return instance;
    }
}&lt;/code&gt;&lt;/pre&gt;
</description><pubDate>Thu, 14 Jul 2022 11:19:03 +0800</pubDate></item></channel></rss>