Nginx快速上手

Nginx快速上手

根据实际的应用需要,学习要用到的Nginx的知识,以快速上手、理解并掌握

Nginx

一:Nginx简介

包括:Nginx是什么、能干什么、特点

二:Nginx安装和基本使用

包括:源码安装、安装配置选项、基本使用

三:Nginx基本配置

包括:结合示例的配置文件,熟悉Nginx的基本配置

四:学习核心模块、日志模块和事件模块的常用指令

五:学习Http模块的常用配置和指令

Nginx简介

Nginx是什么

Nginx是一款轻量级的Web服务器,也是一款轻量级的反向代理服务器。

Nginx能干什么

Nginx能干的事情很多,这里简要罗列一些:

1:直接支持Rails和PHP的程序

2:作为HTTP反向代理服务器

3:作为负载均衡服务器

4:作为邮件代理服务器

5:帮助实现前端动静分离

……

Nginx特点

高稳定、高性能、资源占用少、功能丰富、模块化结构、支持热部署

Nginx安装

源码安装

演示环境:CentOS6.5

1:需要gcc,系统自带了,没有的话,需要先安装

2:需要pcre,安装的命令示例如下: yum install pcre*

3:需要zlib,安装的命令示例如下:yum install zlib zlib-devel

4:如果需要支持ssl的话,安装OpenSSL,安装的命令示例如下:

yum install openssl openssl-devel

5:上http://nginx.org/去下载源码包,然后进行解压安装,示例如下:

(1)先解压源码包,然后进入到这个包里面

(2)安装命令示例如下:

第一步:./configure –prefix=/usr/common/nginx –withhttp_

stub_status_module –with-http_ssl_module

如果提示确少啥,就加上相应的选项,比如缺少pcre的话,就加上

–with-pcre=/usr/common/temp/pcre-8.34 (当然我们这里是不缺东西的)

第二步:配置后就依次make , make install

常见的Nginx安装配置选项-1

编译参数可能会根据版本的不同进行变化,./configure –help查看编译参数列表,常见的选项如下:

–prefix= - 安装路径,如果没有指定,默认为/usr/local/nginx。

–sbin-path= - nginx可执行命令的文件,如果没有指定,默认为/sbin/nginx。

–conf-path= - 在没有使用-c参数指定的情况下nginx.conf的默认位置,如果没有指定,默认

/conf/nginx.conf。

–pid-path= - nginx.pid的路径,如果没有在nginx.conf中通过“pid”指令指定,默认为

/logs/nginx.pid。

–lock-path= - nginx.lock文件路径,如果没有指定,默认为/logs/nginx.lock。

–error-log-path= - 当没有在nginx.conf中使用“error_log”指令指定时的错误日志位置,

如果没有指定,默认为/logs/error.log。

–http-log-path= - 当没有在nginx.conf中使用“access_log”指令指定时的访问日志位置,

如果没有指定,默认为/logs/access.log。

–user= - 当没有在nginx.conf中使用“user”指令指定时nginx运行的用户,如果没有指定,

默认为“nobody”。

–group= - 当没有在nginx.conf中使用“user”指令指定时nginx运行的组,如果没有指定,

默认为“nobody”。

–builddir=DIR - 设置构建目录。

–with-rtsig_module - 启用rtsig模块。

常见的Nginx安装配置选项-2

–with-select_module –without-select_module - 如果在configure的时候没有发现kqueue, epoll,

rtsig或/dev/poll其中之一,select模块始终为启用状态。

–with-poll_module –without-poll_module - 如果在configure的时候没有发现kqueue, epoll,

rtsig或/dev/poll其中之一,poll模块始终为启用状态。

–with-http_ssl_module - 启用ngx_http_ssl_module,启用SSL支持并且能够处理HTTPS请求。需要

OpenSSL,在Debian系统中,对应的包为libssl-dev。

–with-http_realip_module - 启用ngx_http_realip_module

–with-http_addition_module - 启用ngx_http_addition_module

–with-http_sub_module - 启用ngx_http_sub_module

–with-http_dav_module - 启用ngx_http_dav_module

–with-http_flv_module - 启用ngx_http_flv_module

–with-http_stub_status_module - 启用”server status”(服务状态)页

–without-http_charset_module - 禁用ngx_http_charset_module

–without-http_gzip_module - 禁用ngx_http_gzip_module,如果启用,需要zlib包。

–without-http_ssi_module - 禁用ngx_http_ssi_module

–without-http_userid_module - 禁用ngx_http_userid_module

–without-http_access_module - 禁用ngx_http_access_module

–without-http_auth_basic_module - 禁用ngx_http_auth_basic_module

常见的Nginx安装配置选项-3

–without-http_autoindex_module - 禁用ngx_http_autoindex_module

–without-http_geo_module - 禁用ngx_http_geo_module

–without-http_map_module - 禁用ngx_http_map_module

–without-http_referer_module - 禁用ngx_http_referer_module

–without-http_rewrite_module - 禁用ngx_http_rewrite_module。如果启用,需要PCRE包。

–without-http_proxy_module - 禁用ngx_http_proxy_module

–without-http_fastcgi_module - 禁用ngx_http_fastcgi_module

–without-http_memcached_module - 禁用ngx_http_memcached_module

–without-http_limit_zone_module - 禁用ngx_http_limit_zone_module

–without-http_empty_gif_module - 禁用ngx_http_empty_gif_module

–without-http_browser_module - 禁用ngx_http_browser_module

–without-http_upstream_ip_hash_module - 禁用ngx_http_upstream_ip_hash_module

–with-http_perl_module - 启用ngx_http_perl_module

–with-perl_modules_path=PATH - 为perl模块设置路径

–with-perl=PATH - 为perl库设置路径

–http-client-body-temp-path=PATH - 为http连接的请求实体临时文件设置路径,如果没有指定,默认为/client_body_temp

常见的Nginx安装配置选项-4

–http-proxy-temp-path=PATH - 为http代理临时文件设置路径,如果没有指定,默认为/proxy_temp

–http-fastcgi-temp-path=PATH - 为http fastcgi临时文件设置路径,如果没有指定,默认为/fastcgi_temp

–without-http - 禁用HTTP服务

–with-mail - 启用IMAP4/POP3/SMTP代理模块

–with-mail_ssl_module - 启用ngx_mail_ssl_module

–with-cc=PATH - 设置C编译器路径

–with-cpp=PATH - 设置C预处理器路径

–with-cc-opt=OPTIONS - 变量CFLAGS中附加的参数,用于FreeBSD中的PCRE库,同样需要指定–withcc-

opt=”-I /usr/local/include”,如果我们使用select()函数则需要同时增加文件描述符数量,可

以通过–with-cc-opt=”-D FD_SETSIZE=2048”指定。

–with-ld-opt=OPTIONS - 通过连接器的附加参数,用于FreeBSD中的PCRE库,同样需要指定–withld-

opt=”-L /usr/local/lib”。

–with-cpu-opt=CPU - 指定编译的CPU,可用的值为: pentium, pentiumpro, pentium3, pentium4,

athlon, opteron, amd64, sparc32, sparc64, ppc64

–without-pcre - 禁用PCRE库文件,同时将禁用HTTP rewrite 模块,如果要在”location”指令中使

用正则表达式,同样需要PCRE库。

常见的Nginx安装配置选项-5

–with-pcre=DIR - 设置PCRE库源文件路径。

–with-pcre-opt=OPTIONS - 在编译时为PCRE设置附加参数。

–with-md5=DIR - 设置md5库源文件路径。

–with-md5-opt=OPTIONS - 在编译时为md5设置附加参数。

–with-md5-asm - 使用md5汇编源。

–with-sha1=DIR - 设置sha1库源文件路径。

–with-sha1-opt=OPTIONS - 在编译时为sha1设置附加参数。

–with-sha1-asm - 使用sha1汇编源。

–with-zlib=DIR - 设置zlib库源文件路径。

–with-zlib-opt=OPTIONS - 在编译时为zlib设置附加参数。

–with-zlib-asm=CPU - 为指定的CPU使用zlib汇编源进行优化,可用值为: pentium, pentiumpro。

–with-openssl=DIR - 设置openssl库源文件路径。

–with-openssl-opt=OPTIONS - 在编译时为openssl设置附加参数。

–with-debug - 启用debug记录。

–add-module=PATH - 增加一个在PATH中的第三方模块。

Nginx的基本运行

测试配置文件:

安装路径下的/nginx/sbin/nginx -t

启动:

安装路径下的/nginx/sbin/nginx

停止

安装路径下的/nginx/sbin/nginx -s stop

或者是: nginx -s quit

重启

安装路径下的/nginx/sbin/nginx -s reload

查看进程

ps -ef |grep nginx

安装过后,如果从外面访问不了,多半是被防火墙挡住了,可以关闭掉防火墙:

/sbin/service iptables stop

Nginx的基本配置-1

默认启动Nginx时,使用的配置文件是: 安装路径/conf/nginx.conf 文件

可以在启动nginx的时候,通过-c来指定要读取的配置文件

常见的配置文件有如下几个:

nginx.conf:应用程序的基本配置文件

mime.types:MIME类型关联的扩展文件

fastcgi.conf:与fastcgi相关的配置

proxy.conf:与proxy相关的配置

sites.conf:配置Nginx提供的网站,包括虚拟主机

Nginx的进程结构

启动Nginx的时候,会启动一个Master进程,这个进程不处理任何客户端的

请求,主要用来产生worker进程,一个worker进程用来处理一个request。

Nginx模块分为:核心模块、事件模块、标准Http模块、可选Http模块、邮件模

块、第三方模块和补丁等

Nginx的基本配置-2

Nginx基本模块:所谓基本模块,指的是Nginx默认的功能模块,它们提供的指

令,允许你使用定义Nginx基本功能的变量,在编译的时候不能被禁用,包括:

核心模块:基本功能和指令,如进程管理和安全

事件模块:在Nginx内配置网络使用的能力

配置模块:提供包含机制

常见的核心模块指令,大部分都是放置在配置文件的顶部

具体的指令,请参看nginx的官方文档,非常详细,参见:

http://nginx.org/en/docs/ngx_core_module.html

还有下面这个网站,也是非常不错的:

http://www.howtocn.org/nginx:directiveindex

常见的events模块指令,大部分都是放置在配置文件的顶部

具体的指令,在上面那个文档里面,命令的context为events的就是events

模块指令,只能在events里面使用

Nginx常用的核心模块指令

核心模块指令,重点看看:error_log、include、pid、user、worker_cpu_affinity、

worker_processes

error_log

日志有6个级别:debug|info|notice|warn|error|crit

Nginx支持将不同的虚拟主机的日志记录在不同的地方,如下示例:

http{

error_log logs/http_error.log error;

server{

server_name one;

access_log logs/one_access.log;

error_log logs/one_error.log error;

}

server{

server_name two;

access_log logs/two_access.log;

error_log logs/two_error.log error;

}

}

注意:error_log off不是禁用日志,而是创建一个名为off的日志,要禁用日志,可以这么写:error_log

/dev/null crit;

Nginx常用的日志模块、事件模块指令

日志模块指令,几个都看看

事件模块指令,重点看看:use和worker_connections

Nginx的HTTP基本配置

Nginx的HTTP配置主要包括三个区块,结构如下:

http { //这个是协议级别

include mime.types;

default_type application/octet-stream;

keepalive_timeout 65;

gzip on;

server { //这个是服务器级别

listen 80;

server_name localhost;

location / { //这个是请求级别

root html;

index index.html index.htm;

}

}

}

Nginx的HTTP核心模块,包括大量的指令和变量,大都很重要,具体参见:

http://nginx.org/en/docs/http/ngx_http_core_module.html

Location区段-1

Location区段,通过指定模式来与客户端请求的URI相匹配,基本语法如下:

location [=|~|~*|^~|@] pattern{……}

1:没有修饰符表示:必须以指定模式开始,如:

server {

server_name sishuok.com;

location /abc {

……

}

}

那么,如下是对的:

http://sishuok.com/abc

http://sishuok.com/abc?p1=11&p2=22

http://sishuok.com/abc/

http://sishuok.com/abcde

Location区段-2

2:= 表示:必须与指定的模式精确匹配,如:

server {

server_name sishuok.com;

location = /abc {

……

}

}

那么,如下是对的:

http://sishuok.com/abc

http://sishuok.com/abc?p1=11&p2=22

如下是错的:

http://sishuok.com/abc/

http://sishuok.com/abcde

Location区段-3

3:~ 表示:指定的正则表达式要区分大小写,如:

server {

server_name sishuok.com;

location ~ ^/abc$ {

……

}

}

那么,如下是对的:

http://sishuok.com/abc

http://sishuok.com/abc?p1=11&p2=22

如下是错的:

http://sishuok.com/ABC

http://sishuok.com/abc/

http://sishuok.com/abcde

Location区段-4

4:~* 表示:指定的正则表达式不区分大小写,如:

server {

server_name sishuok.com;

location ~* ^/abc$ {

……

}

}

那么,如下是对的:

http://sishuok.com/abc

http://sishuok.com/ABC

http://sishuok.com/abc?p1=11&p2=22

如下是错的:

http://sishuok.com/abc/

http://sishuok.com/abcde

Location区段-5

5:^~ 类似于无修饰符的行为,也是以指定模式开始,不同的是,如果模式匹配,

那么就停止搜索其他模式了。

6:@ :定义命名location区段,这些区段客户段不能访问,只可以由内部产生的请

求来访问,如try_files或error_page等

查找顺序和优先级

1:带有“=“的精确匹配优先

2:没有修饰符的精确匹配

3:正则表达式按照他们在配置文件中定义的顺序

4:带有“^~”修饰符的,开头匹配

5:带有“~” 或“~*” 修饰符的,如果正则表达式与URI匹配

6:没有修饰符的,如果指定字符串与URI开头匹配

Location区段匹配示例

location = / {

只匹配/ 的查询.

[ configuration A ]

}

location / {

匹配任何以/ 开始的查询,但是正则表达式与一些较长的字符串将被首先匹配。

[ configuration B ]

}

location ^~ /images/ {

匹配任何以/images/ 开始的查询并且停止搜索,不检查正则表达式。

[ configuration C ]

}

location ~* .(gif|jpg|jpeg)$ {

匹配任何以gif, jpg, or jpeg结尾的文件,但是所有/images/ 目录的请求将在Configuration C中处

理。

[ configuration D ]

}

各请求的处理如下例:

■/ → configuration A

■/documents/document.html → configuration B

■/images/1.gif → configuration C

■/documents/1.jpg → configuration D

JDBC

JDBC概述

1.1.1什么是JDBC
JDBC(Java
DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java
API。JDBC是Java访问数据库的标准规范,可以为不同的关系型数据库提供统一访问,它由一组用Java语言编写的接口(大部分)和类组成。

1.1.2什么是数据库驱动
JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
今天我们使用的是mysql的驱动mysql-connector-java-5.1.37-bin.jar

JDBC与数据库驱动的关系:接口与实现的关系。

JDBC规范(掌握四个核心对象):
DriverManager:用于注册驱动
Connection: 表示与数据库创建的连接
Statement: 操作数据库sql语句的对象
ResultSet: 结果集或一张虚拟表

1.2JDBC原理
Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动

JDBC是接口,驱动是接口的实现,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。

1.3JDBC入门案例
1.3.1准备数据
之前我们学习了sql语句的使用,并创建的分类表category,今天我们将使用JDBC对分类表进行增删改查操作。
创建数据库

create database day04;
使用数据库

use day04;
创建分类表

create table category(
cid int PRIMARY KEY AUTO_INCREMENT,
cname varchar(100)
);
初始化数据

insert into category (cname) values(‘家电’);
insert into category (cname) values(‘服饰’);
insert into category (cname) values(‘化妆品’);
1.3.2导入驱动jar包
创建lib目录,用于存放当前项目需要的所有jar包
选择jar包,右键执行build path / Add to Build Path

1.3.3开发步骤

注册驱动.

获得连接.

获得执行sql语句的对象

执行sql语句,并返回结果

处理结果

释放资源.

1.3.4案例实现

@Test
//// 查询所有的分类信息
public void demo1() throws Exception{
// 注意:使用JDBC规范,采用都是 java.sql包下的内容
//1 注册驱动
Class.forName(“com.mysql.jdbc.Driver”);
//2 获得连接
String url = “jdbc:mysql://localhost:3306/mydb”;
Connection conn = DriverManager.getConnection(url, “root”, “root”);
//3获得执行sql语句的对象
Statement stmt = conn.createStatement();
//4执行SQL语句
ResultSet rs = stmt.executeQuery(“select from category”);

//5处理结果集
while(rs.next()){
    // 获得一行数据
    Integer cid = rs.getInt("cid");
    String cname = rs.getString("cname");
    System.out.println(cid + " , " + cname);
}
//6释放资源
rs.close();
stmt.close();
conn.close();

}

1.4API详解

1.4.1API详解:注册驱动

DriverManager.registerDriver(new com.mysql.jdbc.Driver());不建议使用
原因有2个:

导致驱动被注册2次。
强烈依赖数据库的驱动jar
解决办法:
Class.forName("com.mysql.jdbc.Driver");

1.4.2API详解:获得链接

static Connection getConnection(String url, String user, String password)
试图建立到给定数据库 URL 的连接。
参数说明:url 需要连接数据库的位置(网址) user用户名password 密码
例如:getConnection(“jdbc:mysql://localhost:3306/day06”, “root”, “root”);

URL:SUN公司与数据库厂商之间的一种协议。
jdbc:mysql://localhost:3306/day06
协议 子协议IP : 端口号数据库
mysql: jdbc:mysql://localhost:3306/day04或者jdbc:mysql:///day14(默认本机连接)

oracle数据库: jdbc:oracle:thin:@localhost:1521:sid

1.4.3java.sql.Connection接口:一个连接

接口的实现在数据库驱动中。所有与数据库交互都是基于连接对象的。

StatementcreateStatement(); //创建操作sql语句的对象

1.4.4API详解:java.sql.Statement接口: 操作sql语句,并返回相应结果

String sql = “某SQL语句”;
获取Statement语句执行平台:Statement stmt = con.createStatement();
常用方法:
int executeUpdate(String sql); –执行insert update delete语句.
ResultSet executeQuery(String sql); –执行select语句.
boolean execute(String sql); –仅当执行select并且有结果时才返回true,执行其他的语句返回false.

1.4.5API详解:处理结果集(注:执行insert、update、delete无需处理)

ResultSet实际上就是一张二维的表格,我们可以调用其boolean

next()方法指向某行记录,当第一次调用next()方法时,便指向第一行记录的位置,这时就可以使用ResultSet提供的getXXX(int
col)方法(与索引从0开始不同个,列从1开始)来获取指定列的数据:
rs.next();//指向第一行
rs.getInt(1);//获取第一行第一列的数据
常用方法:
Object getObject(int index) / Object getObject(String name) 获得任意对象
String getString(int index)/ String getString(String name) 获得字符串
int getInt(int index)/int getInt(String name) 获得整形
double getDouble(int index)/ double getDouble(String name) 获得双精度浮点型

1.4.6API详解:释放资源

与IO流一样,使用后的东西都需要关闭!关闭的顺序是先得到的后关闭,后得到的先关闭。
rs.close();
stmt.close();
con.close();

1.5JDBC工具类

“获得数据库连接”操作,将在以后的增删改查所有功能中都存在,可以封装工具类JDBCUtils。提供获取连接对象的方法,从而达到代码的重复利用。
该工具类提供方法:public static Connection getConn ()。代码如下:

public class JdbcUtils {

private static String driver = "com.mysql.jdbc.Driver";
private static String url = "jdbc:mysql://localhost:3306/webdb_4";
private static String user = "root";
private static String password = "root";

static{
    try {
        //注册驱动
        Class.forName(driver);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

}

/**

获得连接
@return
@throws SQLException
/
public static Connection getConnection() throws SQLException{
//获得连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}

/**
  释放资源
  @param conn
  @param st
  @param rs
 /
public static void closeResource(Connection conn , Statement st , ResultSet rs){

    if(rs != null){
        try {
            rs.close();
        } catch (SQLException e) {
        }
    }

    if(st != null){
        try {
            st.close();
        } catch (SQLException e) {
        }
    }

    if(conn != null){
        try {
            conn.close();
        } catch (SQLException e) {
        }
    }

}

1.6JDBC增删改查操作

1.6.1插入

@Test
public void demo01(){
//添加

Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
    //1 获得连接
    conn = JdbcUtils.getConnection();

    //操作
    //1) 获得语句执行者
    st = conn.createStatement();
    //2) 执行sql语句
    int r = st.executeUpdate("insert into category(cname) values('测试')");

    //3) 处理结果
    System.out.println(r);

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    //释放资源
    JdbcUtils.closeResource(conn, st, rs);
}

}

1.6.2修改

@Test
public void demo02(){
//修改
Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    st = conn.createStatement();
    int r = st.executeUpdate("update category set cname='测试2' where cid = 4");
    System.out.println(r);

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, st, rs);
}

}

1.6.3删除

@Test
public void demo03(){
//删除
Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    //操作
    st = conn.createStatement();
    int r = st.executeUpdate("delete from category where cid = 4");
    System.out.println(r);

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, st, rs);
}

}

1.6.4通过id查询详情

@Test
public void demo04(){
//通过id查询详情
Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    //操作
    st = conn.createStatement();
    rs = st.executeQuery("select  from category where cid = 30");

    if(rs.next()){
        String cid = rs.getString("cid");
        String cname = rs.getString("cname");
        System.out.println(cid + " @ " + cname );
    } else {
        System.out.println("没有数据");
    }

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, st, rs);
}

}

1.6.5查询所有

@Test
public void demo05(){
//查询所有
Connection conn = null;
Statement st = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    //操作
    st = conn.createStatement();
    rs = st.executeQuery("select 

from category”);

    while(rs.next()){
        String cid = rs.getString("cid");
        String cname = rs.getString("cname");
        System.out.println(cid + " @ " + cname );
    }

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, st, rs);
}

}

1.7预处理对象

1.7.1SQL注入问题

SQL注入:用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义。
假设有登录案例SQL语句如下:
SELECT FROM 用户表 WHERE NAME = 用户输入的用户名 AND PASSWORD = 用户输的密码;
此时,当用户输入正确的账号与密码后,查询到了信息则让用户登录。但是当用户输入的账号为XXX 密码为:XXX’OR ‘a’=’a时,则真正执行的代码变为:
SELECT FROM 用户表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’OR ’a’=’a’;
此时,上述查询语句时永远可以查询出结果的。那么用户就直接登录成功了,显然我们不希望看到这样的结果,这便是SQL注入问题。
为此,我们使用PreparedStatement来解决对应的问题。

1.7.2API详解:预处理对象
preparedStatement:预编译对象,是Statement对象的子类。
特点:
性能高
会把sql语句先编译
能过滤掉用户输入的关键字。

PreparedStatement预处理对象,处理的每条sql语句中所有的实际参数,都必须使用占位符?替换。
String sql = “select from user where username = ? and password = ?”;
PreparedStatement使用,需要通过以下3步骤完成:

PreparedStatement预处理对象代码:

#获得预处理对象,需要提供已经使用占位符处理后的SQL语句

PreparedStatement psmt = conn.prepareStatement(sql)

设置实际参数
void setXxx(int index, Xxx xx) 将指定参数设置指定类型的值
参数1:index 实际参数序列号,从1开始。
参数2:xxx 实际参数值,xxx表示具体的类型。
例如:
setString(2, "1234") 把SQL语句中第2个位置的占位符?替换成实际参数 "1234"

执行SQL语句:
int executeUpdate(); --执行insert update delete语句.
ResultSet executeQuery(); --执行select语句.
boolean execute(); --执行select返回true 执行其他的语句返回false.

1.7.3插入

@Test
public void demo01(){
//添加:向分类表中添加数据
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;

try {
    //1 获得连接
    conn = JdbcUtils.getConnection();
    //2 处理sql语句
    String sql = "insert into category(cname) values(? )";
    //3获得预处理对象
    psmt = conn.prepareStatement(sql);
    //4设置实际参数
    psmt.setString(1,"预处理");
    //5执行
    int r = psmt.executeUpdate();

    System.out.println(r);

} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    //6释放资源
    JdbcUtils.closeResource(conn, psmt, rs);
}
}

1.7.4更新

@Test
public void demo02(){
//修改
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    //1 sql语句
    String sql = "update category set cname = ? where cid = ?";
    //2 获得预处理对象
    psmt = conn.prepareStatement(sql);
    //3设置实际参数
    psmt.setString(1, "测试数据");
    psmt.setInt(2, 4);
    //4执行
    int r = psmt.executeUpdate();
    System.out.println(r);


} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, psmt, rs);
}


}

1.7.5删除

@Test
public void demo03(){
//删除
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    //1 sql语句
    String sql = "delete from category where cid = ?";
    //2 获得预处理对象
    psmt = conn.prepareStatement(sql);
    //3设置实际参数
    psmt.setInt(1, 4);
    //4执行
    int r = psmt.executeUpdate();
    System.out.println(r);


} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, psmt, rs);
}
}

1.7.6查询所有

@Test
public void demo04(){
//查询所有
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    String sql = "select 

from category”;
psmt = conn.prepareStatement(sql);
rs = psmt.executeQuery();
while(rs.next()){
String cname = rs.getString(“cname”);
System.out.println(cname);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally{
JdbcUtils.closeResource(conn, psmt, rs);
}
}

1.7.7通过id查询详情

@Test
public void demo05(){
//通过id查询
Connection conn = null;
PreparedStatement psmt = null;
ResultSet rs = null;

try {
    conn = JdbcUtils.getConnection();

    String sql = "select * from category where cid = ?";
    psmt = conn.prepareStatement(sql);
    psmt.setInt(1, 2);
    rs = psmt.executeQuery();
    if(rs.next()){
        System.out.println("查询到");
    } else {
        System.out.println("查询不到");
    }


} catch (Exception e) {
    throw new RuntimeException(e);
} finally{
    JdbcUtils.closeResource(conn, psmt, rs);
}
}

MySQL基础

##本单元目标
一、为什么要学习数据库
二、数据库的相关概念
DBMS、DB、SQL
三、数据库存储数据的特点
四、初始MySQL
MySQL产品的介绍
MySQL产品的安装 ★
MySQL服务的启动和停止 ★
MySQL服务的登录和退出 ★
MySQL的常见命令和语法规范
五、DQL语言的学习 ★
基础查询 ★
条件查询 ★
排序查询 ★
常见函数 ★
分组函数 ★
分组查询 ★
连接查询 ★
子查询 √
分页查询 ★
union联合查询 √

六、DML语言的学习    ★             
    插入语句                        
    修改语句                        
    删除语句                        
七、DDL语言的学习  
    库和表的管理     √                
    常见数据类型介绍  √          
    常见约束        √            
八、TCL语言的学习
    事务和事务处理                 
九、视图的讲解           √
十、变量                      
十一、存储过程和函数   
十二、流程控制结构       

##数据库的好处
1.持久化数据到本地
2.可以实现结构化查询,方便管理

##数据库相关概念
1、DB:数据库,保存一组有组织的数据的容器
2、DBMS:数据库管理系统,又称为数据库软件(产品),用于管理DB中的数据
3、SQL:结构化查询语言,用于和DBMS通信的语言

##数据库存储数据的特点
1、将数据放到表中,表再放到库中
2、一个数据库中可以有多个表,每个表都有一个的名字,用来标识自己。表名具有唯一性。
3、表具有一些特性,这些特性定义了数据在表中如何存储,类似java中 “类”的设计。
4、表由列组成,我们也称为字段。所有表都是由一个或多个列组成的,每一列类似java 中的”属性”
5、表中的数据是按行存储的,每一行类似于java中的“对象”。

##MySQL产品的介绍和安装

###MySQL服务的启动和停止
方式一:计算机——右击管理——服务
方式二:通过管理员身份运行
net start 服务名(启动服务)
net stop 服务名(停止服务)

###MySQL服务的登录和退出
方式一:通过mysql自带的客户端
只限于root用户

方式二:通过windows自带的客户端
登录:
mysql 【-h主机名 -P端口号 】-u用户名 -p密码

退出:
exit或ctrl+C

###MySQL的常见命令

1.查看当前所有的数据库
show databases;
2.打开指定的库
use 库名
3.查看当前库的所有表
show tables;
4.查看其它库的所有表
show tables from 库名;
5.创建表
create table 表名(

    列名 列类型,
    列名 列类型,
    。。。
);
6.查看表结构
desc 表名;


7.查看服务器的版本
方式一:登录到mysql服务端
select version();
方式二:没有登录到mysql服务端
mysql --version
或
mysql --V

###MySQL的语法规范
1.不区分大小写,但建议关键字大写,表名、列名小写
2.每条命令最好用分号结尾
3.每条命令根据需要,可以进行缩进 或换行
4.注释
单行注释:#注释文字
单行注释:– 注释文字
多行注释:/ 注释文字 /

###SQL的语言分类
DQL(Data Query Language):数据查询语言
select
DML(Data Manipulate Language):数据操作语言
insert 、update、delete
DDL(Data Define Languge):数据定义语言
create、drop、alter
TCL(Transaction Control Language):事务控制语言
commit、rollback

###SQL的常见命令

show databases; 查看所有的数据库
use 库名; 打开指定 的库
show tables ; 显示库中的所有表
show tables from 库名;显示指定库中的所有表
create table 表名(
    字段名 字段类型,    
    字段名 字段类型
); 创建表

desc 表名; 查看指定表的结构
select * from 表名;显示表中的所有数据

##DQL语言的学习

###进阶1:基础查询
语法:
SELECT 要查询的东西
【FROM 表名】;

类似于Java中 :System.out.println(要打印的东西);
特点:
①通过select查询完的结果 ,是一个虚拟的表格,不是真实存在
② 要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数

###进阶2:条件查询
条件查询:根据条件过滤原始表的数据,查询到想要的数据
语法:
select
要查询的字段|表达式|常量值|函数
from

where
条件 ;

分类:
一、条件表达式
    示例:salary>10000
    条件运算符:
    > < >= <= = != <>

二、逻辑表达式
示例:salary>10000 && salary<20000

逻辑运算符:

    and(&&):两个条件如果同时成立,结果为true,否则为false
    or(||):两个条件只要有一个成立,结果为true,否则为false
    not(!):如果条件成立,则not后为false,否则为true

三、模糊查询
示例:last_name like 'a%'

###进阶3:排序查询

语法:
select
    要查询的东西
from
    表
where 
    条件

order by 排序的字段|表达式|函数|别名 【asc|desc】

###进阶4:常见函数
一、单行函数
1、字符函数
concat拼接
substr截取子串
upper转换成大写
lower转换成小写
trim去前后指定的空格和字符
ltrim去左边空格
rtrim去右边空格
replace替换
lpad左填充
rpad右填充
instr返回子串第一次出现的索引
length 获取字节个数

2、数学函数
    round 四舍五入
    rand 随机数
    floor向下取整
    ceil向上取整
    mod取余
    truncate截断
3、日期函数
    now当前系统日期+时间
    curdate当前系统日期
    curtime当前系统时间
    str_to_date 将字符转换成日期
    date_format将日期转换成字符
4、流程控制函数
    if 处理双分支
    case语句 处理多分支
        情况1:处理等值判断
        情况2:处理条件判断

5、其他函数
    version版本
    database当前库
    user当前连接用户

二、分组函数

sum 求和
max 最大值
min 最小值
avg 平均值
count 计数

特点:
1、以上五个分组函数都忽略null值,除了count(*)
2、sum和avg一般用于处理数值型
    max、min、count可以处理任何数据类型
3、都可以搭配distinct使用,用于统计去重后的结果
4、count的参数可以支持:
    字段、*、常量值,一般放1

   建议使用 count(*)

##进阶5:分组查询
语法:
select 查询的字段,分组函数
from 表
group by 分组的字段

特点:
1、可以按单个字段分组
2、和分组函数一同查询的字段最好是分组后的字段
3、分组筛选
        针对的表    位置            关键字
分组前筛选:    原始表        group by的前面        where
分组后筛选:    分组后的结果集    group by的后面        having

4、可以按多个字段分组,字段之间用逗号隔开
5、可以支持排序
6、having后可以支持别名

##进阶6:多表连接查询

笛卡尔乘积:如果连接条件省略或无效则会出现
解决办法:添加上连接条件

一、传统模式下的连接 :等值连接——非等值连接

1.等值连接的结果 = 多个表的交集
2.n表连接,至少需要n-1个连接条件
3.多个表不分主次,没有顺序要求
4.一般为表起别名,提高阅读性和性能

二、sql99语法:通过join关键字实现连接

含义:1999年推出的sql语法
支持:
等值连接、非等值连接 (内连接)
外连接
交叉连接

语法:

select 字段,...
from 表1
【inner|left outer|right outer|cross】join 表2 on  连接条件
【inner|left outer|right outer|cross】join 表3 on  连接条件
【where 筛选条件】
【group by 分组字段】
【having 分组后的筛选条件】
【order by 排序的字段或表达式】

好处:语句上,连接条件和筛选条件实现了分离,简洁明了!

三、自连接

案例:查询员工名和直接上级的名称

sql99

SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m ON e.`manager_id`=m.`employee_id`;

sql92

SELECT e.last_name,m.last_name
FROM employees e,employees m 
WHERE e.`manager_id`=m.`employee_id`;

##进阶7:子查询

含义:

一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询
在外面的查询语句,称为主查询或外查询

特点:

1、子查询都放在小括号内
2、子查询可以放在from后面、select后面、where后面、having后面,但一般放在条件的右侧
3、子查询优先于主查询执行,主查询使用了子查询的执行结果
4、子查询根据查询结果的行数不同分为以下两类:
① 单行子查询
    结果集只有一行
    一般搭配单行操作符使用:> < = <> >= <= 
    非法使用子查询的情况:
    a、子查询的结果为一组值
    b、子查询的结果为空

② 多行子查询
    结果集有多行
    一般搭配多行操作符使用:any、all、in、not in
    in: 属于子查询结果中的任意一个就行
    any和all往往可以用其他查询代替

##进阶8:分页查询

应用场景:

实际的web项目中需要根据用户的需求提交对应的分页查询的sql语句

语法:

select 字段|表达式,...
from 表
【where 条件】
【group by 分组字段】
【having 条件】
【order by 排序的字段】
limit 【起始的条目索引,】条目数;

特点:

1.起始条目索引从0开始

2.limit子句放在查询语句的最后

3.公式:select * from  表 limit (page-1)*sizePerPage,sizePerPage
假如:
每页显示条目数sizePerPage
要显示的页数 page

##进阶9:联合查询

引入:
union 联合、合并

语法:

select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union  【all】
.....
select 字段|常量|表达式|函数 【from 表】 【where 条件】

特点:

1、多条查询语句的查询的列数必须是一致的
2、多条查询语句的查询的列的类型几乎相同
3、union代表去重,union all代表不去重

##DML语言

###插入

语法:
insert into 表名(字段名,…)
values(值1,…);

特点:

1、字段类型和值类型一致或兼容,而且一一对应
2、可以为空的字段,可以不用插入值,或用null填充
3、不可以为空的字段,必须插入值
4、字段个数和值的个数必须一致
5、字段可以省略,但默认所有字段,并且顺序和表中的存储顺序一致

###修改

修改单表语法:

update 表名 set 字段=新值,字段=新值
【where 条件】

修改多表语法:

update 表1 别名1,表2 别名2
set 字段=新值,字段=新值
where 连接条件
and 筛选条件

###删除

方式1:delete语句

单表的删除: ★
delete from 表名 【where 筛选条件】

多表的删除:
delete 别名1,别名2
from 表1 别名1,表2 别名2
where 连接条件
and 筛选条件;

方式2:truncate语句

truncate table 表名

两种方式的区别【面试题】

#1.truncate不能加where条件,而delete可以加where条件

#2.truncate的效率高一丢丢

#3.truncate 删除带自增长的列的表后,如果再插入数据,数据从1开始
#delete 删除带自增长列的表后,如果再插入数据,数据从上一次的断点处开始

#4.truncate删除不能回滚,delete删除可以回滚

##DDL语句

###库和表的管理
库的管理:

一、创建库
create database 库名
二、删除库
drop database 库名

表的管理:

#1.创建表

CREATE TABLE IF NOT EXISTS stuinfo(
    stuId INT,
    stuName VARCHAR(20),
    gender CHAR,
    bornDate DATETIME


);

DESC studentinfo;
#2.修改表 alter
语法:ALTER TABLE 表名 ADD|MODIFY|DROP|CHANGE COLUMN 字段名 【字段类型】;

#①修改字段名
ALTER TABLE studentinfo CHANGE  COLUMN sex gender CHAR;

#②修改表名
ALTER TABLE stuinfo RENAME [TO]  studentinfo;
#③修改字段类型和列级约束
ALTER TABLE studentinfo MODIFY COLUMN borndate DATE ;

#④添加字段

ALTER TABLE studentinfo ADD COLUMN email VARCHAR(20) first;
#⑤删除字段
ALTER TABLE studentinfo DROP COLUMN email;


#3.删除表

DROP TABLE [IF EXISTS] studentinfo;

###常见类型

整型:

小数:
    浮点型
    定点型
字符型:
日期型:
Blob类型:

###常见约束

NOT NULL
DEFAULT
UNIQUE
CHECK
PRIMARY KEY
FOREIGN KEY

##数据库事务

###含义
通过一组逻辑操作单元(一组DML——sql语句),将数据从一种状态切换到另外一种状态

###特点
(ACID)
原子性:要么都执行,要么都回滚
一致性:保证数据的状态操作前和操作后保持一致
隔离性:多个事务同时操作相同数据库的同一个数据时,一个事务的执行不受另外一个事务的干扰
持久性:一个事务一旦提交,则数据将持久化到本地,除非其他事务对其进行修改

相关步骤:

1、开启事务
2、编写事务的一组逻辑操作单元(多条sql语句)
3、提交事务或回滚事务

###事务的分类:

隐式事务,没有明显的开启和结束事务的标志

比如
insert、update、delete语句本身就是一个事务

显式事务,具有明显的开启和结束事务的标志

1、开启事务
取消自动提交事务的功能

2、编写事务的一组逻辑操作单元(多条sql语句)
insert
update
delete

3、提交事务或回滚事务

###使用到的关键字

set autocommit=0;
start transaction;
commit;
rollback;

savepoint  断点
commit to 断点
rollback to 断点

###事务的隔离级别:

事务并发问题如何发生?

当多个事务同时操作同一个数据库的相同数据时

事务的并发问题有哪些?

脏读:一个事务读取到了另外一个事务未提交的数据
不可重复读:同一个事务中,多次读取到的数据不一致
幻读:一个事务读取数据时,另外一个事务进行更新,导致第一个事务读取到了没有更新的数据

如何避免事务的并发问题?

通过设置事务的隔离级别
1、READ UNCOMMITTED
2、READ COMMITTED 可以避免脏读
3、REPEATABLE READ 可以避免脏读、不可重复读和一部分幻读
4、SERIALIZABLE可以避免脏读、不可重复读和幻读

设置隔离级别:

set session|global  transaction isolation level 隔离级别名;

查看隔离级别:

select @@tx_isolation;

##视图
含义:理解成一张虚拟的表

视图和表的区别:

    使用方式    占用物理空间

视图    完全相同    不占用,仅仅保存的是sql逻辑

表    完全相同    占用

视图的好处:

1、sql语句提高重用性,效率高
2、和表实现了分离,提高了安全性

###视图的创建
语法:
CREATE VIEW 视图名
AS
查询语句;

###视图的增删改查
1、查看视图的数据 ★

SELECT * FROM my_v4;
SELECT * FROM my_v1 WHERE last_name='Partners';

2、插入视图的数据
INSERT INTO my_v4(last_name,department_id) VALUES('虚竹',90);

3、修改视图的数据

UPDATE my_v4 SET last_name ='梦姑' WHERE last_name='虚竹';


4、删除视图的数据
DELETE FROM my_v4;

###某些视图不能更新
包含以下关键字的sql语句:分组函数、distinct、group by、having、union或者union all
常量视图
Select中包含子查询
join
from一个不能更新的视图
where子句的子查询引用了from子句中的表

###视图逻辑的更新

#方式一:
CREATE OR REPLACE VIEW test_v7
AS
SELECT last_name FROM employees
WHERE employee_id>100;

#方式二:
ALTER VIEW test_v7
AS
SELECT employee_id FROM employees;

SELECT * FROM test_v7;

###视图的删除
DROP VIEW test_v1,test_v2,test_v3;

###视图结构的查看
DESC test_v7;
SHOW CREATE VIEW test_v7;

##存储过程

含义:一组经过预先编译的sql语句的集合
好处:

1、提高了sql语句的重用性,减少了开发程序员的压力
2、提高了效率
3、减少了传输次数

分类:

1、无返回无参
2、仅仅带in类型,无返回有参
3、仅仅带out类型,有返回无参
4、既带in又带out,有返回有参
5、带inout,有返回有参
注意:in、out、inout都可以在一个存储过程中带多个

###创建存储过程
语法:

create procedure 存储过程名(in|out|inout 参数名  参数类型,...)
begin
    存储过程体

end

类似于方法:

修饰符 返回类型 方法名(参数类型 参数名,...){

    方法体;
}

注意

1、需要设置新的结束标记
delimiter 新的结束标记
示例:
delimiter $

CREATE PROCEDURE 存储过程名(IN|OUT|INOUT 参数名  参数类型,...)
BEGIN
    sql语句1;
    sql语句2;

END $

2、存储过程体中可以有多条sql语句,如果仅仅一条sql语句,则可以省略begin end

3、参数前面的符号的意思
in:该参数只能作为输入 (该参数不能做返回值)
out:该参数只能作为输出(该参数只能做返回值)
inout:既能做输入又能做输出

#调用存储过程
call 存储过程名(实参列表)

##函数

###创建函数

学过的函数:LENGTH、SUBSTR、CONCAT等
语法:

CREATE FUNCTION 函数名(参数名 参数类型,...) RETURNS 返回类型
BEGIN
    函数体

END

###调用函数
SELECT 函数名(实参列表)

###函数和存储过程的区别

        关键字        调用语法    返回值            应用场景
函数        FUNCTION    SELECT 函数()    只能是一个        一般用于查询结果为一个值并返回时,当有返回值而且仅仅一个
存储过程    PROCEDURE    CALL 存储过程()    可以有0个或多个        一般用于更新

##流程控制结构

###系统变量
一、全局变量

作用域:针对于所有会话(连接)有效,但不能跨重启

查看所有全局变量
SHOW GLOBAL VARIABLES;
查看满足条件的部分系统变量
SHOW GLOBAL VARIABLES LIKE '%char%';
查看指定的系统变量的值
SELECT @@global.autocommit;
为某个系统变量赋值
SET @@global.autocommit=0;
SET GLOBAL autocommit=0;

二、会话变量

作用域:针对于当前会话(连接)有效

查看所有会话变量
SHOW SESSION VARIABLES;
查看满足条件的部分会话变量
SHOW SESSION VARIABLES LIKE '%char%';
查看指定的会话变量的值
SELECT @@autocommit;
SELECT @@session.tx_isolation;
为某个会话变量赋值
SET @@session.tx_isolation='read-uncommitted';
SET SESSION tx_isolation='read-committed';

###自定义变量
一、用户变量

声明并初始化:

SET @变量名=值;
SET @变量名:=值;
SELECT @变量名:=值;

赋值:

方式一:一般用于赋简单的值
SET 变量名=值;
SET 变量名:=值;
SELECT 变量名:=值;


方式二:一般用于赋表 中的字段值
SELECT 字段名或表达式 INTO 变量
FROM 表;

使用:

select @变量名;

二、局部变量

声明:

declare 变量名 类型 【default 值】;

赋值:

方式一:一般用于赋简单的值
SET 变量名=值;
SET 变量名:=值;
SELECT 变量名:=值;


方式二:一般用于赋表 中的字段值
SELECT 字段名或表达式 INTO 变量
FROM 表;

使用:

select 变量名

二者的区别:

作用域            定义位置        语法

用户变量 当前会话 会话的任何地方 加@符号,不用指定类型
局部变量 定义它的BEGIN END中 BEGIN END的第一句话 一般不用加@,需要指定类型

###分支
一、if函数
语法:if(条件,值1,值2)
特点:可以用在任何位置

二、case语句

语法:

情况一:类似于switch
case 表达式
when 值1 then 结果1或语句1(如果是语句,需要加分号) 
when 值2 then 结果2或语句2(如果是语句,需要加分号)
...
else 结果n或语句n(如果是语句,需要加分号)
end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)

情况二:类似于多重if
case 
when 条件1 then 结果1或语句1(如果是语句,需要加分号) 
when 条件2 then 结果2或语句2(如果是语句,需要加分号)
...
else 结果n或语句n(如果是语句,需要加分号)
end 【case】(如果是放在begin end中需要加上case,如果放在select后面不需要)

特点:
可以用在任何位置

三、if elseif语句

语法:

if 情况1 then 语句1;
elseif 情况2 then 语句2;
...
else 语句n;
end if;

特点:
只能用在begin end中!!!!!!!!!!!!!!!

三者比较:
应用场合
if函数 简单双分支
case结构 等值判断 的多分支
if结构 区间判断 的多分支

###循环

语法:

【标签:】WHILE 循环条件  DO
    循环体
END WHILE 【标签】;

特点:

只能放在BEGIN END里面

如果要搭配leave跳转语句,需要使用标签,否则可以不用标签

leave类似于java中的break语句,跳出所在循环!!!

JavaNIO


微信公众号:菜鸟永恒



1.Java NIO 简介


2.Java NIO 与IO 的主要区别


3.缓冲区(Buffer)和通道(Channel)


4.文件通道(FileChannel)


5.NIO 的非阻塞式网络通信


选择器(Selector)
SocketChannel、ServerSocketChannel、DatagramChannel


面向流


面向缓冲区

Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。


Java NIO 与IO 的主要区别























IONIO
面向流(Stream Oriented)面向缓冲区(Buffer Oriented)
阻塞IO(Blocking IO)非阻塞IO(NonBlocking IO)
非阻塞IO(NonBlocking IO)

import java.nio.ByteBuffer;

import org.junit.Test;

/
 
 一、缓冲区(Buffer):在 Java NIO 中负责数据的存取。缓冲区就是数组。用于存储不同数据类型的数据
  
 
 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:
  ByteBuffer
 
 CharBuffer
  ShortBuffer
 
 IntBuffer
  LongBuffer
 
 FloatBuffer
  DoubleBuffer
 
 
  上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区
 
 
  二、缓冲区存取数据的两个核心方法:
 
 put() : 存入数据到缓冲区中
  get() : 获取缓冲区中的数据
 
 
  三、缓冲区中的四个核心属性:
 
 capacity : 容量,表示缓冲区中最大存储数据的容量。一旦声明不能改变。
  limit : 界限,表示缓冲区中可以操作数据的大小。(limit 后数据不能进行读写)
 
 position : 位置,表示缓冲区中正在操作数据的位置。
  
 
 mark : 标记,表示记录当前 position 的位置。可以通过 reset() 恢复到 mark 的位置
  
 
 0 <= mark <= position <= limit <= capacity
  
 
 四、直接缓冲区与非直接缓冲区:
  非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
 
 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
 /

public class TestBuffer {

    @Test
    public void test3()
{
        //分配直接缓冲区
        ByteBuffer buf = ByteBuffer.allocateDirect(1024);

        System.out.println(buf.isDirect());
    }

    @Test
    public void test2()
{
        String str = “abcde”;

        ByteBuffer buf = ByteBuffer.allocate(1024);

        buf.put(str.getBytes());

        buf.flip();

        byte[] dst = new byte[buf.limit()];
        buf.get(dst, 02);
        System.out.println(new String(dst, 02));
        System.out.println(buf.position());

        //mark() : 标记
        buf.mark();

        buf.get(dst, 22);
        System.out.println(new String(dst, 22));
        System.out.println(buf.position());

        //reset() : 恢复到 mark 的位置
        buf.reset();
        System.out.println(buf.position());

        //判断缓冲区中是否还有剩余数据
        if(buf.hasRemaining()){

            //获取缓冲区中可以操作的数量
            System.out.println(buf.remaining());
        }
    }

    @Test
    public void test1()
{
        String str = “abcde”;

        //1. 分配一个指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        System.out.println(“—————–allocate()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //2. 利用 put() 存入数据到缓冲区中
        buf.put(str.getBytes());

        System.out.println(“—————–put()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //3. 切换读取数据模式
        buf.flip();

        System.out.println(“—————–flip()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //4. 利用 get() 读取缓冲区中的数据
        byte[] dst = new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst, 0, dst.length));

        System.out.println(“—————–get()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //5. rewind() : 可重复读
        buf.rewind();

        System.out.println(“—————–rewind()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
        buf.clear();

        System.out.println(“—————–clear()—————-“);
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());

        System.out.println((char)buf.get());

    }

}

1-通道(Channel)与缓冲区(Buffer)


通道和缓冲区
Java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到IO 设备(例如:文件、套接字)的连接。若需要使用NIO 系统,需要获取用于连接IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。


缓冲区(Buffer)


 缓冲区(Buffer):一个用于特定基本数据类
型的容器。由java.nio 包定义的,所有缓冲区
都是Buffer 抽象类的子类。


 Java NIO 中的Buffer 主要用于与NIO 通道进行
交互,数据是从通道读入缓冲区,从缓冲区写
入通道中的。


缓冲区(Buffer)
Buffer 就像一个数组,可以保存多个相同类型的数据。根
据数据类型不同(boolean 除外) ,有以下Buffer 常用子类:
 ByteBuffer
 CharBuffer
 ShortBuffer
 IntBuffer
 LongBuffer
 FloatBuffer
 DoubleBuffer
上述Buffer 类他们都采用相似的方法进行管理数据,只是各自
管理的数据类型不同而已。都是通过如下方法获取一个Buffer
对象:


缓冲区的基本属性


Buffer 中的重要概念:
 容量(capacity) :表示Buffer 最大数据容量,缓冲区容量不能为负,并且创
建后不能更改。


 限制(limit):第一个不应该读取或写入的数据的索引,即位于limit 后的数据
不可读写。缓冲区的限制不能为负,并且不能大于其容量。


 位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为
负,并且不能大于其限制


 标记(mark)与重置(reset):标记是一个索引,通过Buffer 中的mark() 方法
指定Buffer 中一个特定的position,之后可以通过调用reset() 方法恢复到这
个position.


缓冲区的基本属性



Buffer 的常用方法



























































方法描述
Buffer clear()清空缓冲区并返回对缓冲区的引用
Buffer flip()将缓冲区的界限设置为当前位置,并将当前位置充值为0
int capacity()返回Buffer 的capacity 大小
boolean hasRemaining()判断缓冲区中是否还有元素
int limit()返回Buffer 的界限(limit) 的位置
Buffer limit(int n)将设置缓冲区界限为n, 并返回一个具有新limit 的缓冲区对象
Buffer mark()对缓冲区设置标记
int position()返回缓冲区的当前位置position
Buffer position(int n)将设置缓冲区的当前位置为n , 并返回修改后的Buffer 对象
int remaining()返回position 和limit 之间的元素个数
Buffer reset()将位置position 转到以前设置的mark 所在的位置
Buffer rewind()将位置设为为0, 取消设置的mark

缓冲区的数据操作


Buffer 所有子类提供了两个用于数据操作的方法:get()
与put() 方法


获取Buffer 中的数据


get() :读取单个字节
get(byte[] dst):批量读取多个字节到dst 中
get(int index):读取指定索引位置的字节(不会移动position)


放入数据到Buffer 中


put(byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src):将src 中的字节写入缓冲区的当前位置
put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动position)


                         直接与非直接缓冲区

字节缓冲区要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java 虚拟机会尽最大努力直接在
此缓冲区上执行本机I/O 操作。也就是说,在每次调用基础操作系统的一个本机I/O 操作之前(或之后),
虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。


直接字节缓冲区可以通过调用此类的allocateDirect() 工厂方法来创建。此方法返回的缓冲区进行分配和取消
分配所需成本通常高于非直接缓冲区。直接缓冲区的内容可以驻留在常规的垃圾回收堆之外,因此,它们对
应用程序的内存需求量造成的影响可能并不明显。所以,建议将直接缓冲区主要分配给那些易受基础系统的
本机I/O 操作影响的大型、持久的缓冲区。一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好
处时分配它们。


直接字节缓冲区还可以通过FileChannel 的map() 方法将文件区域直接映射到内存中来创建。该方法返回
MappedByteBuffer 。Java 平台的实现有助于通过JNI 从本机代码创建直接字节缓冲区。如果以上这些缓冲区
中的某个缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改该缓冲区的内容,并且将会在
访问期间或稍后的某个时间导致抛出不确定的异常。


字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect() 方法来确定。提供此方法是为了能够在
性能关键型代码中执行显式缓冲区管理。


非直接缓冲区


直接缓冲区


通道(Channel)


通道(Channel):由java.nio.channels 包定义
的。Channel 表示IO 源与目标打开的连接。
Channel 类似于传统的“流”。只不过Channel
本身不能直接访问数据,Channel 只能与
Buffer 进行交互。


通道(Channel)




通道(Channel)


Java 为Channel 接口提供的最主要实现类如下:


•FileChannel:用于读取、写入、映射和操作文件的通道。
•DatagramChannel:通过UDP 读写网络中的数据通道。
•SocketChannel:通过TCP 读写网络中的数据。
•ServerSocketChannel:可以监听新进来的TCP 连接,对每一个新进来
的连接都会创建一个SocketChannel。


获取通道


获取通道的一种方式是对支持通道的对象调用
getChannel() 方法。支持通道的类如下:
 FileInputStream
 FileOutputStream
 RandomAccessFile
 DatagramSocket
 Socket
 ServerSocket
获取通道的其他方式是使用Files 类的静态方法newByteChannel() 获
取字节通道。或者通过通道的静态方法open() 打开并返回指定通道。


import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.junit.Test;

/
  一、通道(Channel):用于源节点与目标节点的连接。在 Java NIO 中负责缓冲区中数据的传输。Channel 本身不存储数据,因此需要配合缓冲区进行传输。
 
 
  二、通道的主要实现类
 
     java.nio.channels.Channel 接口:
          |–FileChannel
 
         |–SocketChannel
          |–ServerSocketChannel
 
         |–DatagramChannel
  
 
 三、获取通道
  1. Java 针对支持通道的类提供了 getChannel() 方法
 
         本地 IO:
          FileInputStream/FileOutputStream
 
         RandomAccessFile
  
 
         网络IO:
          Socket
 
         ServerSocket
          DatagramSocket
 
         
  2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()
 
 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()
  
 
 四、通道之间的数据传输
  transferFrom()
 
 transferTo()
  
 
 五、分散(Scatter)与聚集(Gather)
  分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
 
 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中
  
 
 六、字符集:Charset
  编码:字符串 -> 字节数组
 
 解码:字节数组  -> 字符串
  
 
/

public class TestChannel {

    //字符集
    @Test
    public void test6() throws IOException{
        Charset cs1 = Charset.forName(“GBK”);

        //获取编码器
        CharsetEncoder ce = cs1.newEncoder();

        //获取解码器
        CharsetDecoder cd = cs1.newDecoder();

        CharBuffer cBuf = CharBuffer.allocate(1024);
        cBuf.put(“威武!”);
        cBuf.flip();

        //编码
        ByteBuffer bBuf = ce.encode(cBuf);

        for (int i = 0; i < 12; i++) {
            System.out.println(bBuf.get());
        }

        //解码
        bBuf.flip();
        CharBuffer cBuf2 = cd.decode(bBuf);
        System.out.println(cBuf2.toString());

        System.out.println(“——————————————————“);

        Charset cs2 = Charset.forName(“GBK”);
        bBuf.flip();
        CharBuffer cBuf3 = cs2.decode(bBuf);
        System.out.println(cBuf3.toString());
    }

    @Test
    public void test5(){
        Map<String, Charset> map = Charset.availableCharsets();

        Set<Entry<String, Charset>> set = map.entrySet();

        for (Entry<String, Charset> entry : set) {
            System.out.println(entry.getKey() + “=” + entry.getValue());
        }
    }

    //分散和聚集
    @Test
    public void test4() throws IOException{
        RandomAccessFile raf1 = new RandomAccessFile(“1.txt”“rw”);

        //1. 获取通道
        FileChannel channel1 = raf1.getChannel();

        //2. 分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        //3. 分散读取
        ByteBuffer[] bufs = {buf1, buf2};
        channel1.read(bufs);

        for (ByteBuffer byteBuffer : bufs) {
            byteBuffer.flip();
        }

        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
        System.out.println(“—————–”);
        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));

        //4. 聚集写入
        RandomAccessFile raf2 = new RandomAccessFile(“2.txt”“rw”);
        FileChannel channel2 = raf2.getChannel();

        channel2.write(bufs);
    }

    //通道之间的数据传输(直接缓冲区)
    @Test
    public void test3() throws IOException{
        FileChannel inChannel = FileChannel.open(Paths.get(“d:/1.mkv”), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get(“d:/2.mkv”), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

//        inChannel.transferTo(0, inChannel.size(), outChannel);
        outChannel.transferFrom(inChannel, 0, inChannel.size());

        inChannel.close();
        outChannel.close();
    }

    //使用直接缓冲区完成文件的复制(内存映射文件)
    @Test
    public void test2() throws IOException{//2127-1902-1777
        long start = System.currentTimeMillis();

        FileChannel inChannel = FileChannel.open(Paths.get(“d:/1.mkv”), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get(“d:/2.mkv”), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);

        //内存映射文件
        MappedByteBuffer inMappedBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedBuf = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());

        //直接对缓冲区进行数据的读写操作
        byte[] dst = new byte[inMappedBuf.limit()];
        inMappedBuf.get(dst);
        outMappedBuf.put(dst);

        inChannel.close();
        outChannel.close();

        long end = System.currentTimeMillis();
        System.out.println(“耗费时间为:” + (end - start));
    }

    //利用通道完成文件的复制(非直接缓冲区)
    @Test
    public void test1(){//10874-10953
        long start = System.currentTimeMillis();

        FileInputStream fis = null;
        FileOutputStream fos = null;
        //①获取通道
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            fis = new FileInputStream(“d:/1.mkv”);
            fos = new FileOutputStream(“d:/2.mkv”);

            inChannel = fis.getChannel();
            outChannel = fos.getChannel();

            //②分配指定大小的缓冲区
            ByteBuffer buf = ByteBuffer.allocate(1024);

            //③将通道中的数据存入缓冲区中
            while(inChannel.read(buf) != -1){
                buf.flip(); //切换读取数据的模式
                //④将缓冲区中的数据写入通道中
                outChannel.write(buf);
                buf.clear(); //清空缓冲区
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(outChannel != null){
                try {
                    outChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(inChannel != null){
                try {
                    inChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        long end = System.currentTimeMillis();
        System.out.println(“耗费时间为:” + (end - start));

    }

}

认识docker

一、Docker工作原理

二、Docker容器和虚拟机对比

三、镜像容器管理

1、Docker关键组件

2、Docker架构

3、Docker内部组件

镜像(Image)——一个特殊的文件系统
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)

容器(Container)——镜像运行时的实体
容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等

仓库(Repository)——集中存放镜像文件的地方
镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务

Docker采用了C/S架构。客户端和服务端可以运行在一个机器上,也可以通过socket或者RESTful API 来进行通信。
Docker Daemon: 一般在宿主机后台运行,等待接收客户端的消息
Docker Client:则为客户提供一系列可执行的命令, 用户使用这些命令跟docker daemon交互

Docker daemon:
Docker daemmon是Docker架构中的主要用户接口。首先,它提供了API Server用于接收来自Docker client的请求,其后根据不同的请求分发给Docker daemon的不同模块执行相应的工作

Image managerment:
需要创建DOcker容器时,可通过镜像管理(image management)部分的distribution和registry模块从Docker registry中下载镜像,并通过镜像管理的image、reference和layer存储镜像的元数据,通过镜像存储驱动graphdriver将镜像文件存储于具体的文件系统中

Network:
当需要为Docker容器创建网络环境时,通过网络模块network调用libnetwork创建并配置Docker容器的网络环境

Volume:
当需要为容器创建数据卷volume时,则通过volume模块调用某个具体的volumedrive,来创建一个数据卷并负责后续的挂载操作

Execdriver:
当需要限制Docker容器运行资源或执行用户指令操作时,则通过execdrive来完成

Libcontainer:
是对cgroups和namespace的二次封装,execdrive是通过libcontainer来实现对容器的具体管理,包括利用UTS、IPC、PID、Network、Mount、User等namespace实现容器之间的资源隔离和利用cgroups实现对容器的资源限制.当运行容器的命令执行完毕后,一个实际的容器就处于运行状态,该容器具有独立的文件系统、相对安全且相互隔离的运行环境.

1、用户是使用Docker Client与Docker Daemon建立通信,并发送请求给后者

2、Engine执行Docker内部的一系列工作,每一项工作都是以一个Job的形式的存在。

3、Job的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动graphdriver将下载镜像以Graph的形式存储;当需要为Docker创建网络环境时,通过网络管理驱动networkdriver创建并配置Docker容器网络环境;当需要限制Docker容器运行资源或执行用户指令等操作时,则通过execdriver来完成。libcontainer是一项独立的容器管理包,networkdriver以及execdriver都是通过libcontainer来实现具体对容器进行的操作。

1、容器和虚拟机对比

2、Docker的优势

3、Docker的劣势

4、Docker的应用场景

Docker 的的优势:

持续部署和测试
发到产品发布的整个过程中使用相同的容器来确保没有任何差异或者人工干预。Docker可以保证测试环境、开发环境、生产环境的一致性。

可移植性
容器可以移动到任意一台Docker主机上,而不需要过多关注底层系统。

弹性伸缩更快速
配合K8S可以很容易的无状态应用的弹性伸缩,只需要改一个yml的数字即可。利用docker能在几秒钟之内启动大量的容器,这是虚拟机无法办到的,快速启动,秒级和分钟级的对比。

资源利用率高
由于docker不需要Hypervisor实现硬件资源虚拟化,docker容器和内核交互,几乎没有性能损耗,性能优于通过Hypervisor层与内核层的虚拟化。一台机器启动上前台容器也没问题。

对硬件无要求
不需要CPU支持虚拟化

Docker 的的劣势

资源隔离
docker是利用cgroup实现资源隔离的,只能限制资源消耗的最大值,而不能隔绝其他应用程序占用自己的资源; docker属于进程之间的隔离,虚拟机可实现系统级别隔离;

安全性问题
一个用户拥有执行docker的权限,可以删除任何用户创建的容器。

兼容性问题
docker目前还在版本快速更新中,细节功能调整较大,一些核心的模块依赖于高版本的内核,存在兼容性的问题。

1、Docker安装

2、认识镜像和容器

3、镜像容器管理

什么是镜像?
镜像是一个多层的联合只读的文件系统。

什么是容器?
容器是在镜像基础上加上读写层。容器即进程。

构建镜像的过程?
镜像->镜像+可写层+执行命令->commit为新的镜像(新的一层)->镜像+可写层+执行命令->commit为新的镜像(新的一层)->…

典型文件系统启动 :
一个典型的 Linux 文件系统由 bootfs 和 rootfs 两部分组成,

bootfs(boot file system)

主要包含 bootloader 和 kernel,bootloader 主要用于引导加载 kernel,当 kernel 被加载到内存中后 bootfs 会被 umount 掉

rootfs (root file system)

包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件

加载过程:
bootfs 时会先将 rootfs 设为 read-only,然后在系统自检之后将 rootfs 从 read-only 改为 read-write,

Docker文件系统启动:

Docker 在 bootfs 自检完毕之后并不会把 rootfs 的 read-only 改为 read-write,而是利用 union mount(UnionFS 的一种挂载机制)将 image 中的其他的 layer 加载到之前的 read-only 的 rootfs 层之上,每一层 layer 都是 rootfs 的结构,并且是read-only 的。所以,我们是无法修改一个已有镜像里面的 layer 的!只有当我们创建一个容器,也就是将 Docker 镜像进行实例化,系统会分配一层空的 read-write 的 rootfs ,用于保存我们做的修改

Dockerfile

FROM centos

ENV TZ “Asia/Shanghai”

ADD echo.sh /opt/echo.sh

RUN chmod +x /opt/echo.sh

CMD [“/opt/echo.sh”]

docker build -t test -f Dockerfile .

镜像工作原理:
如果运行中的容器修改一个已经存在的文件,那么会将该文件从下面的只读层复制到读写层,只读层的这个文件就会覆盖(隐藏),但还存在。
如果删除一个文件,在最上层会被标记隐藏,实际只读层的文件还存在。
这就实现了文件系统隔离,当删除容器后,读写层的数据将会删除,只读镜像不变。

查看具体的挂载逻辑

[root@centos7 l]# mount|grep overlay

overlay on

/var/lib/docker/overlay2/56375ce93fd54484061ef08a48a7093905be680dd14754642970616127b30fca/merged type overlay (rw,relatime,seclabel,lowerdir=/var/lib/docker/overlay2/l/A6JYT4QIFZMKOPIGY675JWKS7F:/var/lib/docker/overlay2/l/4L4SUINS3DX6XPD5BL2J54JQDT,upperdir=/var/lib/docker/overlay2/56375ce93fd54484061ef08a48a7093905be680dd14754642970616127b30fca/diff,workdir=/var/lib/docker/overlay2/56375ce93fd54484061ef08a48a7093905be680dd14754642970616127b30fca/work)

Overlay和overlay2的区别

overlay: 只挂载一层,其他层通过最高层通过硬连接形式共享(增加了磁盘inode的负担)

overlay2: 逐层挂载(最多128层)

基础镜像的层信息

docker pull centos

tree -L 2 /var/lib/docker/overlay2/

构建后镜像的层信息

cd layer_dockerfile/

docker build -t centos:test -f ./Dockerfile .

tree -L 2 /var/lib/docker/overlay2/

每一层都包含了”该层独有的文件”以及”和其低层共享的数据的连接”,在Docker 1.10之前的版本中,目录的名字和镜像的UUID相同,而Docker 1.10后则采用了新的存储方式,可以看到目录名和下载镜像的UUID并不相同

Diff
存放挂载点的具体的文件内容

Link
对应l目录的链接源的名称

Lower
根没有lower,其它的lower指向的父层的链接

L:
”l“目录包含一些符号链接作为缩短的层标识符. 这些缩短的标识符用来避免挂载时超出页面大小的限制

docker run -idt –name centos_con centos:test /bin/bash

tree -L 2 /var/lib/docker/overlay2/

多出的两层“……”为读写层,“…..-init”为初始层。

初始层:

初始层中大多是初始化容器环境时,与容器相关的环境信息,如容器主机名,主机host信息以及域名服务文件等。

读写层:

所有对容器做出的改变都记录在读写层

Diff
存放挂载点的具体的文件内容

Link
对应l目录的链接源的名称

Lower
根没有lower,其它的lower指向的父层的链接

Merged
如果是读写层会有一个Merged

Commit:容器提交为镜像

docker run -idt –name test centos

Touch liwei

docker commit 6de test2

Create:创建容器但是不启动

docker create –name nginx-con -p80:80 nginx:latest

Start:启动容器

docker start nginx-con

Stop:停止容器

docker stop nginx-con

Kill:杀掉容器,和停止相比不友好

docker kill nginx-con

Pause:暂停容器

docker pause nginx-con

Unpause:恢复暂停的容器

docker unpause nginx-con

Run:创建且启动容器

docker run -idt –restart=always –name nginx_con -v /tmp/:/mnt -p 88:80 -e arg1=arg1 nginx
Docker attach nginx_con
Ctrl+^p+^q

CP:宿主机和容器之间copy文件

docker cp docker_install.sh nginx_con:/opt

docker exec nginx_con ls /opt

docker cp nginx_con:/opt/docker_install.sh ./1.sh

Exec:执行命令,也可附加到容器

docker exec nginx_con ls /opt

Attach:附加到容器

docker attach nginx_con

docker exec -it nginx_con /bin/bash

Logs:查看容器日志

docker logs –f nginx_con

Inspect:查看元数据,可以查看镜像和容器

docker inspect nginx_con

Port:查看容器端口映射

docker port nginx_con

Top:查看容器中正在运行的进程

docker top nginx_con

Ps:查看容器

docker ps

docker ps -a

docker ps -aq

查看正在运行的容器,加上-a查看所有容器(包含停止和暂停状态的容器)

Rm:删除容器

docker rm nginx_con 删除容器

docker rm -f nginx_con 强行删除容器

Export:导出容器

docker pull busybox

docker run -itd busybox

docker export 983989307eef>busybox.tar

Import:导入容器

docker import busybox.tar busybox:1.3

Save:导出镜像

docker save busybox:1.3>busybox1.3.tar

Load:导入镜像

docker load -i busybox1.3.tar

Tag:镜像打标签

docker tag busybox:1.3 192.168.199.160/test/busybox:latest

Build:从dockerfile构建镜像

FROM centos

ENV TZ “Asia/Shanghai”

ADD echo.sh /opt/echo.sh

RUN chmod +x /opt/echo.sh

CMD [“/opt/echo.sh”]

docker build -t centos:test -f Dockerfile .

Pull:从 registry拉取镜像

docker pull nginx 从dockerhub上拉取

docker pull 192.168.199.160/test/nginx:latest 从内网harbor上拉取

Push:推送镜像到仓库[第一次需要输入密码,后续不需要了]

docker login 192.168.199.160

admin

Harbor12345

docker push 192.168.199.160/test/busybox:latest

Info、version、events

docker info 查看docker相关信息信息

Docker version 查看docker相关版本信息

Docker events 查看docker事件

【注】
1、创建容器后防火墙不要再动

2、cmd会被覆盖的问题,需要注意,可能会导致/bin/bash 把启动命令覆盖了,启动不了的问题例如
docker run -idt –restart=always –name nginx_con -v /tmp/:/mnt -p 88:80 -e arg1=arg1 nginx /bin/bash

3、cmd的命令都会在挂在后执行。

Java面试

线程池

首先要明确为什么要使用线程池,使用线程池会带来什么好处?

• 线程是稀缺资源,不能频繁的创建。

• 应当将其放入一个池子中,可以给其他任务进行复用。

• 解耦作用,线程的创建于执行完全分开,方便维护。

线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。
如果每个请求都创建一个线程去处理,那么服务器的资源很快就会被耗尽,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

抽象类与接口区别

1.抽象类可以有构造方法,接口中不能有构造方法。

2.抽象类中可以有普通成员变量,接口中没有普通成员变量!!!!!!!(注意重点在 普通 即 非静态 和 变量!!!!)

3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。

悲观锁和乐观锁使用场景

乐观锁是在应用层加锁,而悲观锁是在数据库层加锁(for update)

乐观锁顾名思义就是在操作时很乐观,这数据只有我在用,我先尽管用,最后发现不行时就回滚。

悲观锁在操作时很悲观,生怕数据被其他人更新掉,我就先将其先锁住,让别人用不了,我操作完成后再释放掉。

悲观锁需要数据库级别上的的实现,程序中是做不到的,如果在长事务环境中,数据会一直被锁住,导致并发性能大大地降低。

一般来说如果并发量很高的话,建议使用悲观锁,否则的话就使用乐观锁。

如果并发量很高时使用乐观锁的话,会导致很多的并发事务回滚、操作失败。

总之,冲突几率大用悲观,小就用乐观。

乐观锁不会发生并发抢占资源

在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这会产生冲突。这就是著名的并发性问题。

1.悲观锁:指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态

2.乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。乐观锁不能解决脏读的问题。

线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

死锁和脏数据就是典型的线程安全问题。

简单来说,线程安全就是: 在多线程环境中,能永远保证程序的正确性。

只有存在共享数据时才需要考虑线程安全问题。

多线程会引出很多难以避免的问题, 如死锁,脏数据,线程管理的额外开销,等等。更大大增加了程序设计的复杂度。

在Java 8 中,如果一个桶中的元素个数超过 TREEIFY_THRESHOLD(默认是 8 ),就使用红黑树来替换链表

java内存区域:

hashmap

key value
初始化容量 1左移4位16容量 2的4次方

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

加载因子系数

1分成4等分0.25 0.25*3=0.75 在容量的3/4(0.75)的时候扩容

static final float DEFAULT_LOAD_FACTOR = 0.75f;

HashMap和Hashtable的区别

HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的

前者允许null作为key,后者不允许null作为key

从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。

在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中,ConcurrentHashMap中默认是把segments初始化为长度为16的数组。通过把整个Map分为N个Segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。

HashMap的底层实现
Java8之前,其底层实现是数组+链表实现,Java8使用了数组+链表+红黑树实现

ConcurrentHashMap的具体实现

1、该类包含两个静态内部类HashEntry(节点)和Segment(桶);前者用来封装映射表的键值对,后者用来充当锁的角色

List有序 可以重复 set无序不可以重复

ArrayList的初始容量10 加载因子0.5

Vector的初始容量10 加载因子1

HashSet的初始容量为16,加载因子0.75

HashMap的初始容量为16,加载因子0.75 扩容增量:原容量的1倍

数据库索引原理

索引原理必须清楚一种数据结构「平衡树」(非二叉),也就是b tree或者 b+ tree,重要的事情说三遍:“平衡树,平衡树,平衡树”。当然, 有的数据库也使用哈希桶作用索引的数据结构 , 然而, 主流的RDBMS都是把平衡树当做数据表默认的索引数据结构的。

线程和进程的区别
1、进程是一个“执行中的程序”,是系统进行资源分配和调度的一个独立单位;

2、线程是进程的一个实体,一个进程中拥有多个线程,线程之间共享地址空间和其它资源

volatile关键字

该关键字可以保证可见性不保证原子性

ThreadLocal关键字

当使用ThreadLocal维护变量时,其为每个使用该变量的线程提供独立的变量副本,所以
每一个线程都可以独立的改变自己的副本,而不影响其他线程对应的副本

线程池

java.util.concurrent.ThreadPoolExecutor类就是一个线程池。客户端调用
ThreadPoolExecutor.submit(Runnabletask)提交任务

JVM划分

1、方法区:常量、静态变量、即时编译器

2、堆内存:垃圾回收的主要场所

3、程序计数器

4、虚拟机栈(栈内存):保存局部变量、基本数据类型变量以及堆内存中某个对象的引用变量

5、本地方法栈

垃圾回收算法有哪些

1、引用计数

2、标记-清除 分两个阶段 第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。

GC经常发生的区域是堆区,堆区还可以细分为新生代、老年代、

内存溢出 内存泄露是导致内存溢出的原因之一

NIO和IO的主要区别

IO 面向流 阻塞IO

NIO 面向缓冲区 非阻塞IO 选择器 选择一个通道

双亲委派模型

(1).BootStrap ClassLoader:启动类加载器,负责加载存放在%JAVA_HOME%\lib目录中的

(2).Extension ClassLoader:扩展类加载器,由sun.misc.Launcher$ExtClassLoader实现,负责加载%JAVA_HOME%\lib\ext目录中的

(3).Application ClassLoader:应用程序类加载器,由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径classpath上所指定的类库

Java8新特性

1、Lambda表达式允许我们将函数当成参数传递给某个方法

2、函数式接口@Functionallnterface来标明该接口是一个函数式接口

3、引入重复注解@Repeatable

4、接口中可以实现方法default方法

5、注解的使用场景拓宽

6、新的包java.time包

dubbo

Provider在容器里面进行启动,启动之后往服务中心进行服务一个注册,然后Consume订阅之前注册过的服务,如果服务发生改变注册中心会通知Consume,Consume拿到订阅关系之后就直接invoke调用Provider同时有个Monitor监控他们的调用情况

会员中心调用订单中心

订单需要提供一个接口给会员调用

Consumer:192.168.0-.192/com.tl.IOrderService?application=UserCp

数据库索引作用 底层数据结构 为什么使用这种结构

索引 是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息

底层数据结构B+树

使用B+树的原因:查找速度快,效率高,

聚集索引和非聚集索引根本区别

是表记录的排列顺序和与索引的排列顺序是否一致

聚集索引表记录的排列顺序和索引的排列顺序一致

非聚集索引制定了表中记录的逻辑顺序,但是记录的物理和索引不一定一致

MyISAM和InnoDB的区别

MyISAM不支持事务,InnoDB是事务类型的存储引擎

MyISAM只支持表级锁InnoDB支持行级锁和表级锁

MyISAM不支持外键InnoDB支持外键

MyISAM支持全文索引InnoDB不支持

MyISAM表不支持事务、不支持行级锁、不支持外键

InnoDB表支持事务、支持行级锁、支持外键

Spring 知识点

Spring 的 IOC 和 AOP

• IOC:控制反转,(解耦合)将对象间的依赖关系交给 Spring 容器,使用配置文件来创建所依赖的对象,由主动创建对象改为了被动方式;

• AOP:面向切面编程,将功能代码从业务逻辑代码中分离出来;

AOP 的实现方式有哪几种?如何选择?

JDK 动态代理实现和 cglib 实现

选择:

  1. 如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP,也可以强制使用 cglib 实现 AOP;

  2. 如果目标对象没有实现接口,必须采用 cglib 库,Spring 会自动在 JDK 动态代理和 cglib 之间转换。

扩展:JDK 动态代理如何实现

JDK 动态代理,只能对实现了接口的类生成代理,而不是针对类,该目标类型实现的接口都将被代理。

原理是通过在运行期间创建一个接口的实现类来完成对目标对象的代理

Mybatis 知识点

关于 MyBatis 主要考察占位符#和 $ 的区别,区别如下:

  1. #符号将传入的数据都当做一个字符串,会对自动传入的数据加一个双引号;
  2. $ 符号将传入的数据直接显示生成 SQL 中;
  3. #符号存在预编译的过程,对问号赋值,防止 SQL 注入;
  4. $符号是直译的方式,一般用在 order by ${列名}语句中;
  5. 能用#号就不要用 $ 符号

Linux常用命令

文件和目录:

pwd 显示当前目录

ls 显示当前目录下的文件和目录:

  1. ls -F 可以区分文件和目录;
  2. ls -a 可以把隐藏文件和普通文件一起显示出来;
  3. ls -R 可以递归显示子目录中的文件和目录;
  4. ls -l 显示长列表
  5. ls -l test 过滤器,查看某个特定文件信息。可以只查看 test 文件的信息

处理文件方面的命令有:touch、cp、 In、mv、rm

处理目录方面的命令:mkdir

查看文件内容:file、cat、more、less、tail、head

eg. 找出进程名中包括 java 的所有进程:ps -ef | grep java

top 命令 实时监测进程

压缩数据

  1. tar -xvf 文件名
  2. tar -zxvf 文件名
  3. tar -cvzf 文件名

结束进程:kill PID 或者 kill all

数据库条件查询

排序查询

/*

排序查询:对查询结构进行排序

关键字:order by 字段名 [ASC|DESC]

ASC:升序(从小到大),默认的

DESC:降序(从大到小)

*/

查询所有记录的name和price,结构按照价格 从大到小进行排序

SELECT pname,price FROM product ORDER BY price DESC;

#1.查询所有商品信息,使用价格排序(降序)

SELECT * FROM product ORDER BY price DESC;

#2.查询所有商品信息,在价格排序(降序)的基础上,以分类排序(降序)

#先将查询结果 在价格上进行降序,如果价格相同再以分类进行降序,如果价格不相同,不在对分类进行排序

SELECT * FROM product ORDER BY price DESC,category_id DESC;

#3.显示商品的价格(去重复),并排序(降序)

SELECT DISTINCT price FROM product ORDER BY price DESC;

聚合查询

/*

聚合查询:

以前的查询都是横向记录查询

而聚合查询是 纵向个数查询

聚合查询的特点:查询到的结构 是单一值

聚合函数:

count:求记录数的聚合函数,count函数会自动忽略空值

以下四个,通常用于数值的计算

max:求最大值

min:求最小值

avg:求平均值

sum:求和

*/

#查询统计多有商品的个数

SELECT COUNT(*) FROM product;

#查询统计一共有多少个分类

SELECT COUNT(category_id) FROM product;

#查询所有商品价格的最大值

SELECT MAX(price) FROM product;

#查询所有商品价格的最小值

SELECT MIN(price) FROM product;

#查询所有商品价格的最平均值

SELECT AVG(price) FROM product;

#查询所有商品价格的总值

SELECT SUM(price) FROM product;

#查询所有商品价格的最大值,最小值,平均值,总和

SELECT MAX(price) AS 最大值,MIN(price) 最小值,AVG(price) 平均值,SUM(price) 总和 FROM product;

分组查询

/*

分组查询:

把查询数据分为几个组

关键字: group by 字段

先分组,再查询,具体查询到的结果数,取决于能分多少组

如果分组后 还有需要条件判断 那么请用having关键字

where和having的区别:

where 写在 基本查询后面的

having 写在 分组查询后面的

where后面是不能写 count sum等聚合函数

having后面可以写count和sum等聚合函数

*/

#查询所有以category_id分组后的价格的总和

具体有多少个总和 取决于可以分多少组

SELECT SUM(price) FROM product GROUP BY category_id;

#0 统计所有商品的个数

SELECT COUNT(*) FROM product;

#1 统计各个分类商品的个数

SELECT COUNT(*) FROM product GROUP BY category_id;

#2 统计各个分类商品的个数,且只显示个数大于1的信息

SELECT category_id, COUNT(*) 个数 FROM product GROUP BY category_id HAVING 个数 > 1;

分页查询

/*

分页查询:

只查询记录中的一部分

关键字: limit 数值1(下标,从0开始),数值2(需要查出来记录数)

*/

SELECT * FROM product LIMIT 0,5;

SELECT * FROM product LIMIT 5,5;

SELECT * FROM product LIMIT 10,5;

查询的公式: 假设每一页我要查询 n条

第1页 limit (1-1)*n,n

第2页 limit (2-1)n,n

第100页 limit (100-1)*n,n

第m页 limit (m-1)*n,n

操作数据库表

/*

DML:数据库操作语言

    主要对表中的数据库进行 增删改

****增:插入一条记录

    insert into 表名 (列名1,列名2..) values (值1,值2..)

    注意: 1.列名可以在表中选择一列或者几列

        2.后面的值 必须和前面的列 一一对应

        3.在SQL中除了int类型的数据,其他数据必须用''或者""引起来

            我们推荐用单引号

        4.如果要插入的数据所有字段都有,那么以上的

           (列名1,列名2..) 可以省略

****删:删除一条记录

    格式: delete from 表名;//删除表中的所有记录

        delete from 表名 where条件;

****改:修改表中的记录信息

    update 表名 set 字段名 = 值;//把所有记录的该列都改了

    update 表名 set 字段名 = 值 where条件;

*/

#向students 表中插入一个学生信息(id:001 name:李四 )

INSERT INTO students (sid,NAME) VALUES (‘001’,’李四’);

#向students 表中插入一个学生信息(id:002 name:王五 age:18 )

INSERT INTO students (sid,NAME,sage) VALUES (‘002’,’王五’,18);

INSERT INTO students VALUES (‘003’,’赵六’,28);

#删除students表中的记录

DELETE FROM students;

#删除某一条记录

DELETE FROM students WHERE sid=3;# 这里条件sid=3不能写成 sid==3,sid.equals(3);

#修改students表,把sage这一列的值 改为20

UPDATE students SET sage=20;

#修改students表,把王五的sage这一列的值 改为 50

UPDATE students SET sage = 50 WHERE NAME=’王五’;# sid = 2

/*

DQL:查询数据库中的数据

    基本格式:

    select [distinct] *|列名1,列名2 from 表名;# 查询表中的所有列数据

    带有条件的查询 用关键字 where

*/

#基本查询

SELECT * FROM product;

SELECT pname,price FROM product;

SELECT price FROM product;

SELECT DISTINCT price FROM product;

#基本查询练习

1.查询所有的商品.

SELECT * FROM product;

2.查询商品名和商品价格.

SELECT pname,price FROM product;

3.去掉价格重复值.

SELECT DISTINCT price FROM product;

4.查询结果是表达式(运算查询):将所有商品的价格+10元进行显示.

SELECT price+10 FROM product;

5.别名查询.使用的关键字是as(as可以省略的).

SELECT price+10 AS 打折价格 FROM product;

SELECT price+10 打折价格 FROM product;

#条件查询练习

#查询商品名称为“花花公子”的商品所有信息:

SELECT * FROM product WHERE pname=’花花公子’;

#查询价格为800商品

SELECT * FROM product WHERE price=800;

#查询价格不是800的所有商品

SELECT * FROM product WHERE price <> 800;

SELECT * FROM product WHERE price > 800 OR price < 800;

SELECT * FROM product WHERE NOT (price=800);

#查询商品价格大于60元的所有商品信息

SELECT * FROM product WHERE price > 60;

SELECT * FROM product WHERE NOT (price <= 60);

#查询商品价格在200到1000之间所有商品

SELECT * FROM product WHERE price >= 200 AND price <=1000;

SELECT * FROM product WHERE price BETWEEN 200 AND 1000;

#查询商品价格是200或800的所有商品

SELECT * FROM product WHERE price IN (200,800);

SELECT * FROM product WHERE price = 200 OR price = 800;

#查询含有’霸’字的所有商品

SELECT * FROM product WHERE pname LIKE ‘%霸%’;

#查询以’香’开头的所有商品

SELECT * FROM product WHERE pname LIKE ‘香%’;

#查询第二个字为’想’的所有商品

SELECT * FROM product WHERE pname LIKE ‘_想%’;

#查询没有分类的商品

SELECT * FROM product WHERE category_id IS NULL;

#SELECT * FROM product where category_id = null;# 错误的 判断空 不能用”=”

#查询有分类的商品

SELECT * FROM product WHERE category_id IS NOT NULL;

#SELECT * FROM product WHERE category_id <> NULL;# 错误的 判断不为空 不用”<>”

#查询所有价格大于2000的电脑商品(catetory_id是c001)

#或者价格大于2000的服装商品(catetory_id是c002)**

SELECT * FROM product WHERE (price > 2000 AND category_id = ‘c001’)

OR (price > 2000 AND category_id = 'c002');

SELECT * FROM product WHERE price > 2000 AND category_id IN (‘c001’,’c002’);

/*

SQL语言的分类:

DDL:数据库定义语言

    他主要是对数据库,数据库表进行创建删除维护等

    关键字:create(创建),alter(修改),drop(删除), show(查)



DCL:数据库操纵/控制语言

    控制数据库访问权限

DML:数据库操作语言

    它主要是对表中的数据进行 增删改

    关键字:insert(增加),delete(删除),update(修改)等



DQL:数据库查询语言

    它主要是对表中的数据进行 查询

    关键字:select(查询) from(从)  where(条件)

*/

#注释

– 注释

SELECT * FROM users;

/*

DDL:数据库定义语言

    主要是操作数据库,操作数据库表

DDL之操作数据库:

    对数据库进行增删改查

****增:创建一个数据库

    create database 数据库名;//默认编号,安装的时候您选择的那个编号

    create database 数据库名 charset 编码;

删:删除一个数据库

    drop database 数据库名;

修:修改数据库名字很麻烦 

    修改正在使用的数据库

    use 数据库名;

查:

  查询正在使用的数据库:

    select database();

  查询所有的数据库

    show databases;

*/

#增:创建一个数据库

CREATE DATABASE db297;

CREATE DATABASE db2971 CHARSET utf8;

#删:删除一个数据库

DROP DATABASE db2971;

#修改正在使用的数据库

USE jjj;

#查询正在使用的数据库

SELECT DATABASE();

#查询所有数据库

SHOW DATABASES;

#查询某一个数据的详细信息

SHOW CREATE DATABASE db297;

/*

DDL之数据库表

对数据库表进行增删改查

****增:创建一个表

create table 表名(

    字段名1 数据类型1(长度) [约束],

    字段名2 数据类型2(长度) [约束]

    );

删:删除表

    drop table 表名;

查:查看表

    desc 表名; 查看一个表的详细信息

    show tables;查看所有的表

改:改名字

    rename table 表名 to 新表名;

*/

#创建一个表,学生表(编号,姓名,年龄)

CREATE TABLE student(

sid INT PRIMARY KEY, -- 主键约束

sname VARCHAR(30),

sage INT

);

#删除 student表

DROP TABLE student;

#查看一个表的详细信息

DESC student;

#查看所有的表

SHOW TABLES;

#修改表的名字

RENAME TABLE student TO students;

/*

DDL之修改表中的列

    增删改查

增:增加一个列

    alter table 表名 add 列名 类型(长度) [约束]

删:删除一个列

    alter table 表名 drop 列名;

查:查看表的数据结构

    desc 表名;

改:修改列

    可以修改的名字,类型,约束等..

    修改列名:

    alter table 表名 change 旧列名 新列名 类型(长度) 约束; 

    修改列的类型和约束

    alter table 表名 modify 列名 类型(长度) 约束;

    修改表的字符集

    alter table 表名 character set 字符集;(一般不修改)

*/

#要给students表 添加一列 “电话”

ALTER TABLE students ADD phone VARCHAR(30);

#要把students表中 “电话”这一列删除

ALTER TABLE students DROP phone;

#查看students表有多少列 分别是什么

DESC students;

#修改列名

ALTER TABLE students CHANGE sname NAME VARCHAR(30);

#修改列的类型和约束

ALTER TABLE students MODIFY NAME VARCHAR(50);

/*

三个知识点:

1.介绍两个约束

    主键约束: primary key 被主键约束的列必须有唯一,而且不能为空

    自动增长约束:auto_increment 被自动增长约束的列,值可以不用管



2.删除表所有数据的两种方式(面试题)

    delete from 表名;# 删除表的所有数据,但是自动增长值不会重置为1

    truncate  table  表名;# 删除表的所有数据,并且重置自动增长值为1

    truncate的底层,是先摧毁表,然后再重建表

3.乱码问题

    在DOS创建 查询uft8编码的数据库数据时,会出现乱码

    解决方案:

        临时方案:

        set names gbk;//临时地把告诉数据库 我们用gbk来查询

        永久方案:修改 my.ini的配置文件(不建议)

*/

#创建一张表,用户(编号,用户名,密码)

CREATE TABLE users(

uid INT PRIMARY KEY AUTO_INCREMENT,# 自动增长默认是从1开始

uname VARCHAR(30),

upass VARCHAR(30)

);

#插入数据

INSERT INTO users (uname,upass) VALUES (‘王老’,’123321’);

INSERT INTO users (uname,upass) VALUES (‘李四’,’123’);

#删除表中的所有数据

DELETE FROM users;

INSERT INTO users (uname,upass) VALUES (‘王五’,’1234’);

#使用truncate删除表中的所有数据

TRUNCATE TABLE users;

INSERT INTO users (uname,upass) VALUES (‘赵六’,’12345’);

/*

1.DDL:操作数据库中对象,数据库,表,列

***创建数据库

    create database 数据库名;# 以默认的编码创建数据库

    create database 数据库名 charset 编码名;

    create database 数据库名 character set 编码名;

***创建表

    create table 表名(

        字段1 数据类型(长度) [约束],

        字段2 数据类型(长度) [约束]

        );    

    约束:

        主键约束:primary key,该列的值必须唯一,并且不为空

        自动增长列约束:auto_increment 该列值交给数据库维护

2.DML:操作数据库表中数据,对数据进行增删改

****添加一条记录

    insert into 表名 (列名1.列名2..) values (值1,值2..);

    注意:

        1.列名和后面的值 必须一一对应

        2.(列名1.列名2..) 这里的列名可以写一个或者多个

        3.如果是全列名,可以省略不写

        4.如果值是数字类型的可以不加'',否则必须加上'' 或者 ""

****删除一条记录

    delete from 表名;# 删除表中所有数据,不会重置自动增长值

    truncate table 表名;#删除表所有数据,先把表摧毁再重建

    delete from 表名 where 条件;# 这里条件和查询共用



****修改一条记录

    update 表名 set 字段名=值,字段名=值;#表中所有记录的字段都修改了

    update 表名 set 字段名=值,字段名=值 where 条件;# 这里条件和查询共用



3.DQL:操作数据库表中数据,对数据进行查询

***基本查询:

    格式:

    select distinct *|字段1,字段2 from 表名;

***条件查询

    比较条件

        >,<,>=,<=,<>,=

        between xx and xx (含头行尾)

        in (值1,值2)

        like '_a%' 模糊查询

        is null ,is not null 判断空或者不空

    逻辑条件

        与 and

        或 or

        非 not

*/

面向对象接口多态

第3天 面向对象


今日内容介绍
 接口
 多态
 笔记本案例
今日学习目标
 写出定义接口的格式
 写出实现接口的格式
 说出接口中成员的特点
 接口和抽象类的区别
 能够说出使用多态的前提条件
 理解多态的向上转型
 理解多态的向下转型
 能够完成笔记本电脑案例(方法参数为接口)


第1章 接口


1.1 接口概念


类:具有相同属性和功能的事物集合


接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。


接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。
请记住:一切事物均有功能,即一切事物均有接口。


1.2 接口的定义


与定义类的class不同,接口定义时需要使用interface关键字。
定义接口所在的仍为.java文件,虽然声明时使用的为interface关键字的编译后仍然会产生.class文件。这点可以让我们将接口看做是一种只包含了功能声明的特殊类。
定义格式:
public interface 接口名 {
抽象方法1;
抽象方法2;
抽象方法3;
}
使用interface代替了原来的class,其他步骤与定义类相同:
 接口中的方法均为公共访问的抽象方法
 接口中无法定义普通的成员变量


1.3 类实现接口


类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用implements。
其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。
格式:
class 实现类 implements 接口 {
重写接口中所有方法
}
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
 接口中定义功能,当需要具有该功能时,可以让类实现该接口,只声明了应该具备该方法,是功能的声明。
 在具体实现类中重写方法,实现功能,是方法的具体实现。
于是,通过以上两个动作将功能的声明与实现便分开了。(此时请重新思考:类是现实事物的描述,接口是功能的集合。)


1.4 接口中成员的特点


1、接口中可以定义成员变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量,其值不能改变。后面我们会讲解static与final关键字
2、接口中可以定义方法,方法也有固定的修饰符,public abstract
3、接口不可以创建对象。
4、实现类必须覆盖掉接口中所有的抽象方法后,实现类才可以实例化。否则实现类是一个抽象类。


interface Demo ///定义一个名称为Demo的接口。
    public static final int NUM = 3;// NUM的值不能改变
    public abstract void show1();
    public abstract void show2();
}

//定义子类去覆盖接口中的方法。类与接口之间的关系是 实现。通过 关键字 implements
class DemoImpl implements Demo //子类实现Demo接口。
    //重写接口中的方法。
    public void show1(){}
    public void show2(){}
}

1.5 接口特点


 接口可以继承接口
如同类继承类后便拥有了父类的成员,可以使用父类的非私有成员。A接口继承B接口后,A接口便拥有了A、B两个接口中所有的抽象方法。
 Java支持一个类同时实现多个接口,或一个接口同时继承多个接口。
 类可以在继承一个类的同时,实现多个接口。
 接口与父类的功能可以重复,均代表要具备某种功能,并不冲突。


1.6 接口和抽象类的区别*


明白了接口思想和接口的用法后,接口和抽象类的区别是什么呢?接口在生活体现也基本掌握,那在程序中接口是如何体现的呢?
通过实例进行分析和代码演示抽象类和接口的用法。
1、举例:
犬:
行为:
吼叫;
吃饭;
缉毒犬:
行为:
吼叫;
吃饭;
缉毒;


2、思考:
由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。
可是当缉毒犬有其他额外功能时,而这个功能并不在这个事物的体系中。这时可以让缉毒犬具备犬科自身特点的同时也有其他额外功能,可以将这个额外功能定义接口中。
如下代码演示:


interface 缉毒{
    public abstract void 缉毒();
}
//定义犬科的这个提醒的共性功能
abstract class 犬科{
public abstract void 吃饭();
public abstract void 吼叫();
}
// 缉毒犬属于犬科一种,让其继承犬科,获取的犬科的特性,
//由于缉毒犬具有缉毒功能,那么它只要实现缉毒接口即可,这样即保证缉毒犬具备犬科的特性,也拥有了缉毒的功能
class 缉毒犬 extends 犬科 implements 缉毒{

    public void 缉毒() {
    }
    void 吃饭() {
    }
    void 吼叫() {
    }
}
class 缉毒猪 implements 缉毒{
    public void 缉毒() {
    }
}

3、通过上面的例子总结接口和抽象类的区别:
相同点:
 都位于继承的顶端,用于被其他类实现或继承;
 都不能直接实例化对象;
 都可以包含抽象方法,其子类都必须覆写这些抽象方法;
区别:
 抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;
 接口只能包含抽象方法;
 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)


 抽象类为继承体系中的共性内容,接口为继承体系外的扩展功能


二者的选用:
 优先选用接口,尽量少用抽象类;
 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;


第2章 多态


2.1 多态概述


多态是继封装、继承之后,面向对象的第三大特性。
现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。


2.2 多态代码体现


Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。


具体格式如下:
父类引用指向子类对象就是多态的定义格式。同一个父类的方法会被不同的子类重写为各自的具体实现。在调用方法时,调用的为各个子类重写后的方法。
父类类型 变量名 = new 子类类型();
变量名.方法名();
此时,虽然该变量指向的是子类对象,但表现为一个父类的形态,可以调用一切父类的方法,子类特有的方法将不能调用。


2.3 多态调用注意事项


 成员变量编译看父类中是否存在,不存在编译失败
 成员变量运行父类中的变量
 成员方法编译看父类中是否存在,不存在编译失败
 成员方法运行子类重写的方法


2.4 多态的好处和弊端(猫狗的案例)


当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。
如:
在Boss类中,有叫员工去工作的方法,当该方法的参数定义为接口时,可以传入任意的子类对象。相比定义多个子类参数,定义多个方法,这样大大提高了代码复用性与扩展性。
class Boss{
public void goToWork(Empolyee e){
e.work();
}
}


所以多态的存在意义(优点)为:
配合继承与方法重写提高了代码的复用性与扩展性,如果没有方法重写,则多态同样没有意义。


多态的弊端: 不能调用子类的特有方法

2.5 向上向下类型转换


多态本身是子类类型向父类类型向上转型的过程。
多态的转型分为向上转型与向下转型两种:
 向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
如:Animal p = new Cat();
 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如:Cat c = (Cat) a; //变量p 实际上指向Cat对象


 instanceof关键字
使用格式:
boolean b = 引用变量 instanceof 类;
if(a instanceof Dog){
Dog d = (Dog)a;
}


第3章 笔记本电脑案例


3.1 案例介绍


定义USB接口(具备开启功能、关闭功能),笔记本要使用USB设备,即笔记本在生产时需要预留可以插入USB设备的USB接口,即就是笔记本具备使用USB设备的功能,但具体是什么USB设备,笔记本并不关心,只要符合USB规格的设备都可以。鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,不然鼠标和键盘的生产出来无法使用


进行描述笔记本类,实现笔记本使用USB鼠标、USB键盘
 USB接口,包含开启功能、关闭功能
 笔记本类,包含运行功能、关机功能、使用USB设备功能
 鼠标类,要符合USB接口
 键盘类,要符合USB接口


3.2 案例需求分析


阶段一:
使用笔记本,笔记本有运行功能,需要笔记本对象来运行这个功能
阶段二:
想使用一个鼠标,又有一个功能使用鼠标,并多了一个鼠标对象。
阶段三:
还想使用一个键盘 ,又要多一个功能和一个对象
问题:每多一个功能就需要在笔记本对象中定义一个方法,不爽,程序扩展性极差。
降低鼠标、键盘等外围设备和笔记本电脑的耦合性。


3.3 实现代码步骤


 定义鼠标、键盘,笔记本三者之间应该遵守的规则
interface USB {
void open();// 开启功能
void close();// 关闭功能
}


 鼠标实现USB规则
class Mouse implements USB {
publicvoid open() {
System.out.println(“鼠标开启”);
}
publicvoid close() {
System.out.println(“鼠标关闭”);
}
}


 键盘实现USB规则
class KeyBoard implements USB {
publicvoid open() {
System.out.println(“键盘开启”);
}
publicvoid close() {
System.out.println(“键盘关闭”);
}
}


 定义笔记本


class NoteBook {
    // 笔记本开启运行功能
    publicvoid run({
        System.out.println(“笔记本运行”);
    }

    // 笔记本使用usb设备,这时当笔记本对象调用这个功能时,必须给其传递一个符合USB规则的USB设备
    publicvoid useUSB(USB usb{
        // 判断是否有USB设备
        if (usb != null) {
            usb.open();
            usb.close();
        }
    }

    publicvoid shutDown({
        System.out.println(“笔记本关闭”);
    }
}

publicclass Test {
    publicstaticvoid main(String[] args{
        // 创建笔记本实体对象
        NoteBook nb = new NoteBook();
// 笔记本开启
        nb.run();

        // 创建鼠标实体对象
        Mouse m = new Mouse();
        // 笔记本使用鼠标
        nb.useUSB(m);

// 创建键盘实体对象
        KeyBoard kb = new KeyBoard();
        // 笔记本使用键盘
        nb.useUSB(kb);

        // 笔记本关闭
        nb.shutDown();
    }
}

多表查询


微信公众号:菜鸟永恒



第1章 多表关系实战


1.1 实战1:省和市
 方案1:多张表,一对多


 方案2:一张表,自关联一对多


1.2 实战2:用户和角色 (比如演员和扮演人物)


 多对多关系
1.3 实战3:角色和权限 (比如公司职位和开除等权限)


 多对多关系


1.4 实战4:客户和联系人(可选)


 一对多:一个客户服务于多个联系人


第2章 多表查询


CREATE TABLE category (
cid int PRIMARY KEY ,
cname VARCHAR(50)
);
CREATE TABLE products(
pid int PRIMARY KEY ,
pname VARCHAR(50),
price INT,
flag VARCHAR(2), #是否上架标记为:1表示上架、0表示下架
category_id int,
CONSTRAINT products_fk FOREIGN KEY (category_id) REFERENCES category (cid)
);


2.1 初始化数据


分类


INSERT INTO category(cid,cname) VALUES(‘1’,’家电’);
INSERT INTO category(cid,cname) VALUES(‘2’,’服饰’);
INSERT INTO category(cid,cname) VALUES(‘3’,’化妆品’);


商品


INSERT INTO products(pid, pname,price,flag,category_id) VALUES(‘1’,’联想’,5000,’1’,1);
INSERT INTO products(pid, pname,price,flag,category_id) VALUES(‘2’,’海尔’,3000,’1’,1);
INSERT INTO products(pid, pname,price,flag,category_id) VALUES(‘3’,’雷神’,5000,’1’,1);


INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘4’,’JACK JONES’,800,’1’,2);
INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘5’,’真维斯’,200,’1’,2);
INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘6’,’花花公子’,440,’1’,2);
INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘7’,’劲霸’,2000,’1’,2);


INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘8’,’香奈儿’,800,’1’,3);
INSERT INTO products (pid, pname,price,flag,category_id) VALUES(‘9’,’相宜本草’,200,’1’,3);


2.2 多表查询



  1. 交叉连接查询(基本不会使用-得到的是两个表的乘积) [了解]
     语法:select from A,B;

  2. 内连接查询(使用的关键字 inner join – inner可以省略)
     隐式内连接:select from A,B where 条件;
     显示内连接:select from A inner join B on 条件;

  3. 外连接查询(使用的关键字 outer join – outer可以省略)
     左外连接:left outer join
     select from A left outer join B on 条件;
     右外连接:right outer join
     select from A right outer join B on 条件;


1.查询哪些分类的商品已经上架


隐式内连接


SELECT FROM category c , products p
WHERE c.cid = p.category_id and p.flag = ‘1’;


内连接


SELECT FROM category c
INNER JOIN products p ON c.cid = p.category_id
WHERE p.flag = ‘1’;


2.查询所有分类的商品个数


左外连接


INSERT INTO category(cid,cname) VALUES(4,’奢侈品’);
SELECT cname,COUNT(category_id) FROM category c
LEFT OUTER JOIN products p
ON c.cid = p.category_id
GROUP BY cname;


2.3 子查询


子查询:一条select语句结果作为另一条select语法一部分(查询条件,查询结果,表等)。
select ….查询字段 … from …表.. where … 查询条件


3 子查询, 查询“化妆品”分类商品详情


隐式内连接


SELECT FROM products p , category c
WHERE p.category_id=c.cid AND c.cname = ‘化妆品’;


子查询


作为查询条件


SELECT FROM products p
WHERE p.category_id =
(
SELECT c.cid FROM category c
WHERE c.cname=’化妆品’
);
##作为另一张表
SELECT
FROM products p ,
(SELECT FROM category WHERE cname=’化妆品’) c
WHERE p.category_id = c.cid;


查询“化妆品”和“家电”两个分类商品详情


SELECT FROM products p
WHERE p.category_id in
(
SELECT c.cid FROM category c
WHERE c.cname=’化妆品’ or c.name=’家电’
);
欢迎关注公众号:菜鸟永恒 点滴记录,共同进步。


立志想成为大牛的菜鸟一枚,将会记录Java技术知识,,不妨来扫描二维码关注一下!


欢迎加小编微信 拉你进新建的技术交流群

听说关注的人都变美变帅了


觉得我的文章写得不错,不妨点一下好看并分享给朋友!