一轮明月照九洲

长将静趣观天地,自有幽怀契古今


  • 首页

  • 关于

  • 分类

  • 归档

用户指南

发表于 2020-07-09

用户指南

一.系统说明:

“船舶靠港选择系统”旨在为船舶提供最优靠港方案。航运行业的迅速发展让航运公司与船舶之间的竞争日夜激烈,用户需求的多样化也直接影响着船舶靠港选择,在一定区域范围内的港口可以根据需求不同选择最合适的停靠港口。该系统可以根据用户输入的不同权重信息为船舶规划出三条航线,船长根据需求从中选择最佳的靠港方案。该系统主要功能就在于根据不同权重因子为船舶提供有选择性的最佳靠港方案,为船舶提供信息服务,也为部分港口增加收益。包括三个模块用户信息管理功能、港口收费计费功能和显示页面功能。

二.使用说明

(1) 登录界面

用户进入登录界面以后,首先需要注册用户名与输入密码,每个用户拥有唯一的登录账号,便于开发人员信息的管理与维护。拥有账号以后可选择登录进入系统也可选择取消退出系统。

(2) 收费计费系统

用户通过账号进入系统后,进入收费计费系统。收费计费系统具有三个子模块,三个子模块的功能分别是工具箱、数据编辑与货物信息。

  1. 工具箱具有基本操作模块、专业功能模块、主题模块、用户管理模块和开发者选项模块。基本操作模块具体包括:放大(点击放大按钮系统会按比例相应的放大地图)、缩小(点击缩小按钮系统按比例缩小地图)、漫游(点击漫游按钮移动鼠标时地图会与鼠标联动)、全图显示(点击全图显示按钮系统恢复初始页面)、地图刷新(点击地图刷新按钮系统会将地图更新)、前一视图(点击按钮系统会返回上一视图)、后一视图(点击后一视图按钮系统会回到相对的下一视图)、打开地图(点击按钮可以打开新的地图)、识别查询(点击按钮系统会跳出相应港口的具体信息)、比例尺(显示当前地图的比例尺信息)。专业功能模块具体包括:信息查询(根据输入的信息查询港口)、鹰眼图(在显示系统的右上角显示鹰眼图,鹰眼图包括整个地图可以与鼠标联动)。用户根据喜好手动选择主题形式,有经典模式、商务模式、时尚模式三种类型。用户管理中,用户可以修改密码和注销当前账户。开发者选项具体包括帮助指南(用户使用系统的指南)、可以给开发者打赏、可以检测当前系统版本、可以升级系统更新版本。

  2. 数据编辑页面

  3. 货物信息页面具有两个子功能,分别为创建新的订单与货物信息。点击创建新的订单,系统会跳出建立货运订单窗口,用户根据要求输入相应的新订单信息点击完成即可创建成功,成功以后的订单信息会自动导入数据库与货物信息中。

(3) 显示系统

系统根据用户输入的权重因子为船舶提供三种靠港方案。用户根据需求选择一种最佳方案与航线并显示在当前屏幕上。

三.系统维护

开发者按时按需对系统进行维护与更新,保证数据库的维护更新系统的运行。

MySQL连接VS 2010

发表于 2020-06-30

C#中出现Unable to connect to any of the specified MySQL hosts.这样的问题主要是因为server的值

server的值应该是localhost而不是(local)

首先需要安装.net连接mysql的驱动,去http://www.mysql.com/downloads/connector/net/下载并安装1.8版本以下的。

然后你就可以在vs的server explorer添加连接了。

然后连接上数据库后一定要添加引用啊

首先还要在刚才的网站下载zip文件 进行把mysql.data.dll 添加到引用

最后 直接引用 using MySql.Data.MySqlClient;

MySQL (四)

发表于 2020-04-22 | 分类于 MySQL

多表查询

事务

DCL

多表查询:

* 查询语法:
    select
        列名列表
    from
        表名列表
    where....
* 准备sql
    # 创建部门表
    CREATE TABLE dept(
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(20)
    );
    INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
    # 创建员工表
    CREATE TABLE emp (
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(10),
        gender CHAR(1), -- 性别
        salary DOUBLE, -- 工资
        join_date DATE, -- 入职日期
        dept_id INT,
        FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
    );
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
* 笛卡尔积:
    * 有两个集合A,B .取这两个集合的所有组成情况。
    * 要完成多表查询,需要消除无用的数据
* 多表查询的分类:
    1. 内连接查询:
        1. 隐式内连接:使用where条件消除无用数据
            * 例子:
            -- 查询所有员工信息和对应的部门信息

            SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;

            -- 查询员工表的名称,性别。部门表的名称
            SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;

            SELECT 
                t1.name, -- 员工表的姓名
                t1.gender,-- 员工表的性别
                t2.name -- 部门表的名称
            FROM
                emp t1,
                dept t2
            WHERE 
                t1.`dept_id` = t2.`id`;


        2. 显式内连接:
            * 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件
            * 例如:
                * SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;    
                * SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`;    

        3. 内连接查询:
            1. 从哪些表中查询数据
            2. 条件是什么
            3. 查询哪些字段
    2. 外链接查询:
        1. 左外连接:
            * 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
            * 查询的是左表所有数据以及其交集部分。
            * 例子:
                -- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
                SELECT     t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
        2. 右外连接:
            * 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
            * 查询的是右表所有数据以及其交集部分。
            * 例子:
                SELECT     * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
    3. 子查询:
        * 概念:查询中嵌套查询,称嵌套查询为子查询。
            -- 查询工资最高的员工信息
            -- 1 查询最高的工资是多少 9000
            SELECT MAX(salary) FROM emp;

            -- 2 查询员工信息,并且工资等于9000的
            SELECT * FROM emp WHERE emp.`salary` = 9000;

            -- 一条sql就完成这个操作。子查询
            SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);

        * 子查询不同情况
            1. 子查询的结果是单行单列的:
                * 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
                * 
                -- 查询员工工资小于平均工资的人
                SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
            2. 子查询的结果是多行单列的:
                * 子查询可以作为条件,使用运算符in来判断
                -- 查询'财务部'和'市场部'所有的员工信息
                SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
                SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
                -- 子查询
                SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');

            3. 子查询的结果是多行多列的:
                * 子查询可以作为一张虚拟表参与查询
                -- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
                -- 子查询
                SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
                WHERE t1.id = t2.dept_id;

                -- 普通内连接
                SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` >  '2011-11-11'

    * 多表查询练习

            -- 部门表
            CREATE TABLE dept (
              id INT PRIMARY KEY PRIMARY KEY, -- 部门id
              dname VARCHAR(50), -- 部门名称
              loc VARCHAR(50) -- 部门所在地
            );

            -- 添加4个部门
            INSERT INTO dept(id,dname,loc) VALUES 
            (10,'教研部','北京'),
            (20,'学工部','上海'),
            (30,'销售部','广州'),
            (40,'财务部','深圳');



            -- 职务表,职务名称,职务描述
            CREATE TABLE job (
              id INT PRIMARY KEY,
              jname VARCHAR(20),
              description VARCHAR(50)
            );

            -- 添加4个职务
            INSERT INTO job (id, jname, description) VALUES
            (1, '董事长', '管理整个公司,接单'),
            (2, '经理', '管理部门员工'),
            (3, '销售员', '向客人推销产品'),
            (4, '文员', '使用办公软件');



            -- 员工表
            CREATE TABLE emp (
              id INT PRIMARY KEY, -- 员工id
              ename VARCHAR(50), -- 员工姓名
              job_id INT, -- 职务id
              mgr INT , -- 上级领导
              joindate DATE, -- 入职日期
              salary DECIMAL(7,2), -- 工资
              bonus DECIMAL(7,2), -- 奖金
              dept_id INT, -- 所在部门编号
              CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
              CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
            );

            -- 添加员工
            INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
            (1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
            (1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
            (1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
            (1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
            (1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
            (1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
            (1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
            (1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
            (1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
            (1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
            (1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
            (1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
            (1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
            (1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);



            -- 工资等级表
            CREATE TABLE salarygrade (
              grade INT PRIMARY KEY,   -- 级别
              losalary INT,  -- 最低工资
              hisalary INT -- 最高工资
            );

            -- 添加5个工资等级
            INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
            (1,7000,12000),
            (2,12010,14000),
            (3,14010,20000),
            (4,20010,30000),
            (5,30010,99990);

            -- 需求:

            -- 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
            /*
                分析:
                    1.员工编号,员工姓名,工资,需要查询emp表  职务名称,职务描述 需要查询job表
                    2.查询条件 emp.job_id = job.id

            */
            SELECT 
                t1.`id`, -- 员工编号
                t1.`ename`, -- 员工姓名
                t1.`salary`,-- 工资
                t2.`jname`, -- 职务名称
                t2.`description` -- 职务描述
            FROM 
                emp t1, job t2
            WHERE 
                t1.`job_id` = t2.`id`;



            -- 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
            /*
                分析:
                    1. 员工编号,员工姓名,工资 emp  职务名称,职务描述 job  部门名称,部门位置 dept
                    2. 条件: emp.job_id = job.id and emp.dept_id = dept.id
            */

            SELECT 
                t1.`id`, -- 员工编号
                t1.`ename`, -- 员工姓名
                t1.`salary`,-- 工资
                t2.`jname`, -- 职务名称
                t2.`description`, -- 职务描述
                t3.`dname`, -- 部门名称
                t3.`loc` -- 部门位置
            FROM 
                emp t1, job t2,dept t3
            WHERE 
                t1.`job_id` = t2.`id` AND t1.`dept_id` = t3.`id`;

            -- 3.查询员工姓名,工资,工资等级
            /*
                分析:
                    1.员工姓名,工资 emp  工资等级 salarygrade
                    2.条件 emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary
                        emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
            */
            SELECT 
                t1.ename ,
                t1.`salary`,
                t2.*
            FROM emp t1, salarygrade t2
            WHERE t1.`salary` BETWEEN t2.`losalary` AND t2.`hisalary`;



            -- 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
            /*
                分析:
                    1. 员工姓名,工资 emp , 职务名称,职务描述 job 部门名称,部门位置,dept  工资等级 salarygrade
                    2. 条件: emp.job_id = job.id and emp.dept_id = dept.id and emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary

            */
            SELECT 
                t1.`ename`,
                t1.`salary`,
                t2.`jname`,
                t2.`description`,
                t3.`dname`,
                t3.`loc`,
                t4.`grade`
            FROM 
                emp t1,job t2,dept t3,salarygrade t4
            WHERE 
                t1.`job_id` = t2.`id` 
                AND t1.`dept_id` = t3.`id`
                AND t1.`salary` BETWEEN t4.`losalary` AND t4.`hisalary`;



            -- 5.查询出部门编号、部门名称、部门位置、部门人数

            /*
                分析:
                    1.部门编号、部门名称、部门位置 dept 表。 部门人数 emp表
                    2.使用分组查询。按照emp.dept_id完成分组,查询count(id)
                    3.使用子查询将第2步的查询结果和dept表进行关联查询

            */
            SELECT 
                t1.`id`,t1.`dname`,t1.`loc` , t2.total
            FROM 
                dept t1,
                (SELECT
                    dept_id,COUNT(id) total
                FROM 
                    emp
                GROUP BY dept_id) t2
            WHERE t1.`id` = t2.dept_id;


            -- 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询

            /*
                分析:
                    1.姓名 emp, 直接上级的姓名 emp
                        * emp表的id 和 mgr 是自关联
                    2.条件 emp.id = emp.mgr
                    3.查询左表的所有数据,和 交集数据
                        * 使用左外连接查询

            */
            /*
            select
                t1.ename,
                t1.mgr,
                t2.`id`,
                t2.ename
            from emp t1, emp t2
            where t1.mgr = t2.`id`;

            */

            SELECT 
                t1.ename,
                t1.mgr,
                t2.`id`,
                t2.`ename`
            FROM emp t1
            LEFT JOIN emp t2
            ON t1.`mgr` = t2.`id`;

事务

1. 事务的基本介绍
    1. 概念:
        *  如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。

    2. 操作:
        1. 开启事务: start transaction;
        2. 回滚:rollback;
        3. 提交:commit;
    3. 例子:
        CREATE TABLE account (
            id INT PRIMARY KEY AUTO_INCREMENT,
            NAME VARCHAR(10),
            balance DOUBLE
        );
        -- 添加数据
        INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);


        SELECT * FROM account;
        UPDATE account SET balance = 1000;
        -- 张三给李四转账 500 元

        -- 0. 开启事务
        START TRANSACTION;
        -- 1. 张三账户 -500

        UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan';
        -- 2. 李四账户 +500
        -- 出错了...
        UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi';

        -- 发现执行没有问题,提交事务
        COMMIT;

        -- 发现出问题了,回滚事务
        ROLLBACK;
    4. MySQL数据库中事务默认自动提交

        * 事务提交的两种方式:
            * 自动提交:
                * mysql就是自动提交的
                * 一条DML(增删改)语句会自动提交一次事务。
            * 手动提交:
                * Oracle 数据库默认是手动提交事务
                * 需要先开启事务,再提交
        * 修改事务的默认提交方式:
            * 查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交  0 代表手动提交
            * 修改默认提交方式: set @@autocommit = 0;


2. 事务的四大特征:
    1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
    2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
    3. 隔离性:多个事务之间。相互独立。
    4. 一致性:事务操作前后,数据总量不变
3. 事务的隔离级别(了解)
    * 概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
    * 存在问题:
        1. 脏读:一个事务,读取到另一个事务中没有提交的数据
        2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
        3. 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
    * 隔离级别:
        1. read uncommitted:读未提交
            * 产生的问题:脏读、不可重复读、幻读
        2. read committed:读已提交 (Oracle)
            * 产生的问题:不可重复读、幻读
        3. repeatable read:可重复读 (MySQL默认)
            * 产生的问题:幻读
        4. serializable:串行化
            * 可以解决所有的问题

        * 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
        * 数据库查询隔离级别:
            * select @@tx_isolation;
        * 数据库设置隔离级别:
            * set global transaction isolation level  级别字符串;

    * 演示:
        set global transaction isolation level read uncommitted;
        start transaction;
        -- 转账操作
        update account set balance = balance - 500 where id = 1;
        update account set balance = balance + 500 where id = 2;

DCL:

* SQL分类:
    1. DDL:操作数据库和表
    2. DML:增删改表中数据
    3. DQL:查询表中数据
    4. DCL:管理用户,授权

* DBA:数据库管理员

* DCL:管理用户,授权
    1. 管理用户
        1. 添加用户:
            * 语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
        2. 删除用户:
            * 语法:DROP USER '用户名'@'主机名';
        3. 修改用户密码:

            UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
            UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';

            SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
            SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');

            * mysql中忘记了root用户的密码?
                1. cmd -- > net stop mysql 停止mysql服务
                    * 需要管理员运行该cmd

                2. 使用无验证方式启动mysql服务: mysqld --skip-grant-tables
                3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就可以登录成功
                4. use mysql;
                5. update user set password = password('你的新密码') where user = 'root';
                6. 关闭两个窗口
                7. 打开任务管理器,手动结束mysqld.exe 的进程
                8. 启动mysql服务
                9. 使用新密码登录。
        4. 查询用户:
            -- 1. 切换到mysql数据库
            USE myql;
            -- 2. 查询user表
            SELECT * FROM USER;

            * 通配符: % 表示可以在任意主机使用用户登录数据库

    2. 权限管理:
        1. 查询权限:
            -- 查询权限
            SHOW GRANTS FOR '用户名'@'主机名';
            SHOW GRANTS FOR 'lisi'@'%';

        2. 授予权限:
            -- 授予权限
            grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
            -- 给张三用户授予所有权限,在任意数据库任意表上

            GRANT ALL ON *.* TO 'zhangsan'@'localhost';
        3. 撤销权限:
            -- 撤销权限:
            revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
            REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';

MySQL (三)

发表于 2020-04-16 | 分类于 MySQL
  1. DQL:查询语句

    1. 排序查询
    2. 聚合函数
    3. 分组查询
    4. 分页查询
    1. 约束
    2. 多表之间的关系
    3. 范式
    4. 数据库的备份和还原

DQL:查询语句

1. 排序查询
    * 语法:order by 子句
        * order by 排序字段1 排序方式1 ,  排序字段2 排序方式2...

    * 排序方式:
        * ASC:升序,默认的。
        * DESC:降序。

    * 注意:
        * 如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件。


2. 聚合函数:将一列数据作为一个整体,进行纵向的计算。
    1. count:计算个数
        1. 一般选择非空的列:主键
        2. count(*)
    2. max:计算最大值
    3. min:计算最小值
    4. sum:计算和
    5. avg:计算平均值


    * 注意:聚合函数的计算,排除null值。
        解决方案:
            1. 选择不包含非空的列进行计算
            2. IFNULL函数

3. 分组查询:
    1. 语法:group by 分组字段;
    2. 注意:
        1. 分组之后查询的字段:分组字段、聚合函数
        2. where 和 having 的区别?
            1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
            2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。

        -- 按照性别分组。分别查询男、女同学的平均分

        SELECT sex , AVG(math) FROM student GROUP BY sex;

        -- 按照性别分组。分别查询男、女同学的平均分,人数

        SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;

        --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
        SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;

        --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
        SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;

        SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;



4. 分页查询
    1. 语法:limit 开始的索引,每页查询的条数;
    2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
        -- 每页显示3条记录 

        SELECT * FROM student LIMIT 0,3; -- 第1页

        SELECT * FROM student LIMIT 3,3; -- 第2页

        SELECT * FROM student LIMIT 6,3; -- 第3页

    3. limit 是一个MySQL"方言"

约束

* 概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。    
* 分类:
    1. 主键约束:primary key
    2. 非空约束:not null
    3. 唯一约束:unique
    4. 外键约束:foreign key

* 非空约束:not null,值不能为null
    1. 创建表时添加约束
        CREATE TABLE stu(
            id INT,
            NAME VARCHAR(20) NOT NULL -- name为非空
        );
    2. 创建表完后,添加非空约束
        ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;

    3. 删除name的非空约束
        ALTER TABLE stu MODIFY NAME VARCHAR(20);


* 唯一约束:unique,值不能重复
    1. 创建表时,添加唯一约束
        CREATE TABLE stu(
            id INT,
            phone_number VARCHAR(20) UNIQUE -- 添加了唯一约束

        );
        * 注意mysql中,唯一约束限定的列的值可以有多个null


    2. 删除唯一约束

        ALTER TABLE stu DROP INDEX phone_number;

    3. 在创建表后,添加唯一约束
        ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;

* 主键约束:primary key。
    1. 注意:
        1. 含义:非空且唯一
        2. 一张表只能有一个字段为主键
        3. 主键就是表中记录的唯一标识

    2. 在创建表时,添加主键约束
        create table stu(
            id int primary key,-- 给id添加主键约束
            name varchar(20)
        );

    3. 删除主键
        -- 错误 alter table stu modify id int ;
        ALTER TABLE stu DROP PRIMARY KEY;

    4. 创建完表后,添加主键
        ALTER TABLE stu MODIFY id INT PRIMARY KEY;

    5. 自动增长:
        1.  概念:如果某一列是数值类型的,使用 auto_increment 可以来完成值得自动增长

        2. 在创建表时,添加主键约束,并且完成主键自增长
        create table stu(
            id int primary key auto_increment,-- 给id添加主键约束
            name varchar(20)
        );


        3. 删除自动增长
        ALTER TABLE stu MODIFY id INT;
        4. 添加自动增长
        ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;


* 外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。
    1. 在创建表时,可以添加外键
        * 语法:
            create table 表名(
                ....
                外键列
                constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
            );

    2. 删除外键
        ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

    3. 创建表之后,添加外键
        ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);


    4. 级联操作
        1. 添加级联操作
            语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称 
                    FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE  ;
        2. 分类:
            1. 级联更新:ON UPDATE CASCADE 
            2. 级联删除:ON DELETE CASCADE 

数据库的设计

1. 多表之间的关系
    1. 分类:
        1. 一对一(了解):
            * 如:人和身份证
            * 分析:一个人只有一个身份证,一个身份证只能对应一个人
        2. 一对多(多对一):
            * 如:部门和员工
            * 分析:一个部门有多个员工,一个员工只能对应一个部门
        3. 多对多:
            * 如:学生和课程
            * 分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
    2. 实现关系:
        1. 一对多(多对一):
            * 如:部门和员工
            * 实现方式:在多的一方建立外键,指向一的一方的主键。
        2. 多对多:
            * 如:学生和课程
            * 实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
        3. 一对一(了解):
            * 如:人和身份证
            * 实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。

    3. 案例
        -- 创建旅游线路分类表 tab_category
        -- cid 旅游线路分类主键,自动增长
        -- cname 旅游线路分类名称非空,唯一,字符串 100
        CREATE TABLE tab_category (
            cid INT PRIMARY KEY AUTO_INCREMENT,
            cname VARCHAR(100) NOT NULL UNIQUE
        );

        -- 创建旅游线路表 tab_route
        /*
        rid 旅游线路主键,自动增长
        rname 旅游线路名称非空,唯一,字符串 100
        price 价格
        rdate 上架时间,日期类型
        cid 外键,所属分类
        */
        CREATE TABLE tab_route(
            rid INT PRIMARY KEY AUTO_INCREMENT,
            rname VARCHAR(100) NOT NULL UNIQUE,
            price DOUBLE,
            rdate DATE,
            cid INT,
            FOREIGN KEY (cid) REFERENCES tab_category(cid)
        );

        /*创建用户表 tab_user
        uid 用户主键,自增长
        username 用户名长度 100,唯一,非空
        password 密码长度 30,非空
        name 真实姓名长度 100
        birthday 生日
        sex 性别,定长字符串 1
        telephone 手机号,字符串 11
        email 邮箱,字符串长度 100
        */
        CREATE TABLE tab_user (
            uid INT PRIMARY KEY AUTO_INCREMENT,
            username VARCHAR(100) UNIQUE NOT NULL,
            PASSWORD VARCHAR(30) NOT NULL,
            NAME VARCHAR(100),
            birthday DATE,
            sex CHAR(1) DEFAULT '男',
            telephone VARCHAR(11),
            email VARCHAR(100)
        );

        /*
        创建收藏表 tab_favorite
        rid 旅游线路 id,外键
        date 收藏时间
        uid 用户 id,外键
        rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
        */
        CREATE TABLE tab_favorite (
            rid INT, -- 线路id
            DATE DATETIME,
            uid INT, -- 用户id
            -- 创建复合主键
            PRIMARY KEY(rid,uid), -- 联合主键
            FOREIGN KEY (rid) REFERENCES tab_route(rid),
            FOREIGN KEY(uid) REFERENCES tab_user(uid)
        );


2. 数据库设计的范式
    * 概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求

        设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
        目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。

    * 分类:
        1. 第一范式(1NF):每一列都是不可分割的原子数据项
        2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
            * 几个概念:
                1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
                    例如:学号-->姓名。  (学号,课程名称) --> 分数
                2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
                    例如:(学号,课程名称) --> 分数
                3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
                    例如:(学号,课程名称) -- > 姓名
                4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
                    例如:学号-->系名,系名-->系主任
                5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
                    例如:该表中码为:(学号,课程名称)
                    * 主属性:码属性组中的所有属性
                    * 非主属性:除过码属性组的属性

        3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

数据库的备份和还原

1. 命令行:
    * 语法:
        * 备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
        * 还原:
            1. 登录数据库
            2. 创建数据库
            3. 使用数据库
            4. 执行文件。source 文件路径
2. 图形化工具:

MySQL (二)

发表于 2020-04-10 | 分类于 MySQL

DQL:查询表中的记录

* select * from 表名;

语法:

select
    字段列表
from
    表名列表
where
    条件列表
group by
    分组字段
having
    分组之后的条件
order by
    排序
limit
    分页限定

基础查询

1. 多个字段的查询
    select 字段名1,字段名2... from 表名;
    * 注意:
        * 如果查询所有字段,则可以使用*来替代字段列表。
2. 去除重复:
    * distinct
3. 计算列
    * 一般可以使用四则运算计算一些列的值。(一般只会进行数值型的计算)
    * ifnull(表达式1,表达式2):null参与的运算,计算结果都为null
        * 表达式1:哪个字段需要判断是否为null
        * 如果该字段为null后的替换值。
4. 起别名:
    * as:as也可以省略

条件查询

1. where子句后跟条件
2. 运算符
    * > 、< 、<= 、>= 、= 、<>
    * BETWEEN...AND  
    * IN( 集合) 
    * LIKE:模糊查询
        * 占位符:
            * _:单个任意字符
            * %:多个任意字符
    * IS NULL  
    * and  或 &&
    * or  或 || 
    * not  或 !

        -- 查询年龄大于20岁

        SELECT * FROM student WHERE age > 20;

        SELECT * FROM student WHERE age >= 20;

        -- 查询年龄等于20岁
        SELECT * FROM student WHERE age = 20;

        -- 查询年龄不等于20岁
        SELECT * FROM student WHERE age != 20;
        SELECT * FROM student WHERE age <> 20;

        -- 查询年龄大于等于20 小于等于30

        SELECT * FROM student WHERE age >= 20 &&  age <=30;
        SELECT * FROM student WHERE age >= 20 AND  age <=30;
        SELECT * FROM student WHERE age BETWEEN 20 AND 30;

        -- 查询年龄22岁,18岁,25岁的信息
        SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
        SELECT * FROM student WHERE age IN (22,18,25);

        -- 查询英语成绩为null
        SELECT * FROM student WHERE english = NULL; -- 不对的。null值不能使用 = (!=) 判断

        SELECT * FROM student WHERE english IS NULL;

        -- 查询英语成绩不为null
        SELECT * FROM student WHERE english  IS NOT NULL;



        -- 查询姓马的有哪些? like
        SELECT * FROM student WHERE NAME LIKE '马%';
        -- 查询姓名第二个字是化的人

        SELECT * FROM student WHERE NAME LIKE "_化%";

        -- 查询姓名是3个字的人
        SELECT * FROM student WHERE NAME LIKE '___';


        -- 查询姓名中包含德的人
        SELECT * FROM student WHERE NAME LIKE '%德%';

排序查询

* 语法:order by 子句
    * order by 排序字段1 排序方式1 ,  排序字段2 排序方式2...

* 排序方式:
    * ASC:升序,默认的。
    * DESC:降序。

* 注意:
    * 如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件。

聚合函数:将一列数据作为一个整体,进行纵向的计算。

1. count:计算个数
    1. 一般选择非空的列:主键
    2. count(*)
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值


* 注意:聚合函数的计算,排除null值。
    解决方案:
        1. 选择不包含非空的列进行计算
        2. IFNULL函数

分组查询:

1. 语法:group by 分组字段;
2. 注意:
    1. 分组之后查询的字段:分组字段、聚合函数
    2. where 和 having 的区别?
        1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
        2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。

    -- 按照性别分组。分别查询男、女同学的平均分

    SELECT sex , AVG(math) FROM student GROUP BY sex;

    -- 按照性别分组。分别查询男、女同学的平均分,人数

    SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;

    --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
    SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;

    --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
    SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;

    SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;

分页查询

1. 语法:limit 开始的索引,每页查询的条数;
2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
    -- 每页显示3条记录 

    SELECT * FROM student LIMIT 0,3; -- 第1页

    SELECT * FROM student LIMIT 3,3; -- 第2页

    SELECT * FROM student LIMIT 6,3; -- 第3页

3. limit 是一个MySQL"方言"                

MySQL(一)

发表于 2020-04-01 | 分类于 MySQL
  1. 数据库的基本概念
  1. MySQL数据库软件

    1. 安装
    2. 卸载
    3. 配置
  2. SQL

数据库的基本概念

1. 数据库的英文单词: DataBase 简称 : DB
2. 什么数据库?
    * 用于存储和管理数据的仓库。

3. 数据库的特点:
    1. 持久化存储数据的。其实数据库就是一个文件系统
    2. 方便存储和管理数据
    3. 使用了统一的方式操作数据库 -- SQL


4. 常见的数据库软件
    * 参见《MySQL基础.pdf》

MySQL数据库软件

1. 安装
    * 参见《MySQL基础.pdf》
2. 卸载
    1. 去mysql的安装目录找到my.ini文件
        * 复制 datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
    2. 卸载MySQL
    3. 删除C:/ProgramData目录下的MySQL文件夹。

3. 配置
    * MySQL服务启动
        1. 手动。
        2. cmd--> services.msc 打开服务的窗口
        3. 使用管理员打开cmd
            * net start mysql : 启动mysql的服务
            * net stop mysql:关闭mysql服务
    * MySQL登录
        1. mysql -uroot -p密码
        2. mysql -hip -uroot -p连接目标的密码
        3. mysql --host=ip --user=root --password=连接目标的密码
    * MySQL退出
        1. exit
        2. quit

    * MySQL目录结构
        1. MySQL安装目录:basedir="D:/develop/MySQL/"
            * 配置文件 my.ini
        2. MySQL数据目录:datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
            * 几个概念
                * 数据库:文件夹
                * 表:文件
                * 数据:数据

SQL

1.什么是SQL?
    Structured Query Language:结构化查询语言
    其实就是定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。

2.SQL通用语法
    1. SQL 语句可以单行或多行书写,以分号结尾。
    2. 可使用空格和缩进来增强语句的可读性。
    3. MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
    4. 3 种注释
        * 单行注释: -- 注释内容 或 # 注释内容(mysql 特有) 
        * 多行注释: /* 注释 */

3. SQL分类
    1) DDL(Data Definition Language)数据定义语言
        用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter 等
    2) DML(Data Manipulation Language)数据操作语言
        用来对数据库中表的数据进行增删改。关键字:insert, delete, update 等
    3) DQL(Data Query Language)数据查询语言
        用来查询数据库中表的记录(数据)。关键字:select, where 等
    4) DCL(Data Control Language)数据控制语言(了解)
        用来定义数据库的访问权限和安全级别,及创建用户。关键字:GRANT, REVOKE 等

DDL:操作数据库、表

1. 操作数据库:CRUD
    1. C(Create):创建
        * 创建数据库:
            * create database 数据库名称;
        * 创建数据库,判断不存在,再创建:
            * create database if not exists 数据库名称;
        * 创建数据库,并指定字符集
            * create database 数据库名称 character set 字符集名;

        * 练习: 创建db4数据库,判断是否存在,并制定字符集为gbk
            * create database if not exists db4 character set gbk;
    2. R(Retrieve):查询
        * 查询所有数据库的名称:
            * show databases;
        * 查询某个数据库的字符集:查询某个数据库的创建语句
            * show create database 数据库名称;
    3. U(Update):修改
        * 修改数据库的字符集
            * alter database 数据库名称 character set 字符集名称;
    4. D(Delete):删除
        * 删除数据库
            * drop database 数据库名称;
        * 判断数据库存在,存在再删除
            * drop database if exists 数据库名称;
    5. 使用数据库
        * 查询当前正在使用的数据库名称
            * select database();
        * 使用数据库
            * use 数据库名称;


2. 操作表
    1. C(Create):创建
        1. 语法:
            create table 表名(
                列名1 数据类型1,
                列名2 数据类型2,
                ....
                列名n 数据类型n
            );
            * 注意:最后一列,不需要加逗号(,)
            * 数据库类型:
                1. int:整数类型
                    * age int,
                2. double:小数类型
                    * score double(5,2)
                3. date:日期,只包含年月日,yyyy-MM-dd
                4. datetime:日期,包含年月日时分秒     yyyy-MM-dd HH:mm:ss
                5. timestamp:时间错类型    包含年月日时分秒     yyyy-MM-dd HH:mm:ss    
                    * 如果将来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值

                6. varchar:字符串
                    * name varchar(20):姓名最大20个字符
                    * zhangsan 8个字符  张三 2个字符


        * 创建表
            create table student(
                id int,
                name varchar(32),
                age int ,
                score double(4,1),
                birthday date,
                insert_time timestamp
            );
        * 复制表:
            * create table 表名 like 被复制的表名;          
    2. R(Retrieve):查询
        * 查询某个数据库中所有的表名称
            * show tables;
        * 查询表结构
            * desc 表名;
    3. U(Update):修改
        1. 修改表名
            alter table 表名 rename to 新的表名;
        2. 修改表的字符集
            alter table 表名 character set 字符集名称;
        3. 添加一列
            alter table 表名 add 列名 数据类型;
        4. 修改列名称 类型
            alter table 表名 change 列名 新列别 新数据类型;
            alter table 表名 modify 列名 新数据类型;
        5. 删除列
            alter table 表名 drop 列名;
    4. D(Delete):删除
        * drop table 表名;
        * drop table  if exists 表名 ;
  • 客户端图形化工具:SQLYog

DML:增删改表中数据

1. 添加数据:
    * 语法:
        * insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
    * 注意:
        1. 列名和值要一一对应。
        2. 如果表名后,不定义列名,则默认给所有列添加值
            insert into 表名 values(值1,值2,...值n);
        3. 除了数字类型,其他类型需要使用引号(单双都可以)引起来
2. 删除数据:
    * 语法:
        * delete from 表名 [where 条件]
    * 注意:
        1. 如果不加条件,则删除表中所有记录。
        2. 如果要删除所有记录
            1. delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操作
            2. TRUNCATE TABLE 表名; -- 推荐使用,效率更高 先删除表,然后再创建一张一样的表。
3. 修改数据:
    * 语法:
        * update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];

    * 注意:
        1. 如果不加任何条件,则会将表中所有记录全部修改。

JavaSE基础 (三十五)

发表于 2020-03-30 | 分类于 java

网络编程

主要内容

  • 软件架构CS/BS
  • 网络通信三要素
  • TCP通信
  • Socket套接字
  • ServerSocket

教学目标

  • 能够辨别UDP和TCP协议特点
  • 能够说出TCP协议下两个常用类名称
  • 能够编写TCP协议下字符串数据传输程序
  • 能够理解TCP协议下文件上传案例
  • 能够理解TCP协议下案例2

第一章 网络编程入门

1.1软件结构

  • C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。

B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。

1.2 网络通信协议

  • 网络通信协议:通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
  • TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。

上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。

1.3 协议分类

通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。

java.net 包中提供了两种常见的网络协议的支持:

  • UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

    由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

    但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。

UDP通信图解

特点:数据被限制在64kb以内,超出这个范围就不能发送了。

数据报(Datagram):网络传输的基本单位

  • TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

    在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。

    • 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
      • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
      • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
      • 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。

​ 完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。

1.4 网络编程三要素

协议

  • 协议:计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。

IP地址

  • IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。

IP地址分类

  • IPv4:是一个32位的二进制数,通常被分为4个字节,表示成a.b.c.d 的形式,例如192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。

  • IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。

    为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。

常用命令

  • 查看本机IP地址,在控制台输入:
1
ipconfig
  • 检查网络是否连通,在控制台输入:
1
2
ping 空格 IP地址
ping 220.181.57.216

特殊的IP地址

  • 本机IP地址:127.0.0.1、localhost 。

端口号

网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分这些进程呢?

如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。

  • 端口号:用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

利用协议+IP地址+端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。

第二章 TCP通信程序

2.1 概述

TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。

两端通信时步骤:

  1. 服务端程序,需要事先启动,等待客户端的连接。
  2. 客户端主动连接服务器端,连接成功才能通信。服务端不可以主动连接客户端。

在Java中,提供了两个类用于实现TCP通信程序:

  1. 客户端:java.net.Socket 类表示。创建Socket对象,向服务端发出连接请求,服务端响应请求,两者建立连接开始通信。
  2. 服务端:java.net.ServerSocket 类表示。创建ServerSocket对象,相当于开启一个服务,并等待客户端的连接。

2.2 Socket类

Socket 类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。

构造方法

  • public Socket(String host, int port) :创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为回送地址。

    小贴士:回送地址(127.x.x.x) 是本机回送地址(Loopback Address),主要用于网络软件测试以及本地机进程间通信,无论什么程序,一旦使用回送地址发送数据,立即返回,不进行任何网络传输。

构造举例,代码如下:

1
Socket client = new Socket("127.0.0.1", 6666);

成员方法

  • public InputStream getInputStream() : 返回此套接字的输入流。

    • 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。
    • 关闭生成的InputStream也将关闭相关的Socket。
  • public OutputStream getOutputStream() : 返回此套接字的输出流。

    • 如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。
    • 关闭生成的OutputStream也将关闭相关的Socket。
  • public void close() :关闭此套接字。

    • 一旦一个socket被关闭,它不可再使用。
    • 关闭此socket也将关闭相关的InputStream和OutputStream 。
  • public void shutdownOutput() : 禁用此套接字的输出流。

    • 任何先前写出的数据将被发送,随后终止输出流。

    2.3 ServerSocket类

ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。

构造方法

  • public ServerSocket(int port) :使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。

构造举例,代码如下:

1
ServerSocket server = new ServerSocket(6666);

成员方法

  • public Socket accept() :侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。

2.4 简单的TCP网络程序

TCP通信分析图解

  1. 【服务端】启动,创建ServerSocket对象,等待连接。
  2. 【客户端】启动,创建Socket对象,请求连接。
  3. 【服务端】接收连接,调用accept方法,并返回一个Socket对象。
  4. 【客户端】Socket对象,获取OutputStream,向服务端写出数据。
  5. 【服务端】Scoket对象,获取InputStream,读取客户端发送的数据。

到此,客户端向服务端发送数据成功。

自此,服务端向客户端回写数据。

  1. 【服务端】Socket对象,获取OutputStream,向客户端回写数据。
  2. 【客户端】Scoket对象,获取InputStream,解析回写数据。
  3. 【客户端】释放资源,断开连接。

客户端向服务器发送数据

服务端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ServerTCP {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动 , 等待连接 .... ");
// 1.创建 ServerSocket对象,绑定端口,开始等待连接
ServerSocket ss = new ServerSocket(6666);
// 2.接收连接 accept 方法, 返回 socket 对象.
Socket server = ss.accept();
// 3.通过socket 获取输入流
InputStream is = server.getInputStream();
// 4.一次性读取数据
// 4.1 创建字节数组
byte[] b = new byte[1024];
// 4.2 据读取到字节数组中.
int len = is.read(b);
// 4.3 解析数组,打印字符串信息
String msg = new String(b, 0, len);
System.out.println(msg);
//5.关闭资源.
is.close();
server.close();
}
}

客户端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ClientTCP {
public static void main(String[] args) throws Exception {
System.out.println("客户端 发送数据");
// 1.创建 Socket ( ip , port ) , 确定连接到哪里.
Socket client = new Socket("localhost", 6666);
// 2.获取流对象 . 输出流
OutputStream os = client.getOutputStream();
// 3.写出数据.
os.write("你好么? tcp ,我来了".getBytes());
// 4. 关闭资源 .
os.close();
client.close();
}
}

服务器向客户端回写数据

服务端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class ServerTCP {
public static void main(String[] args) throws IOException {
System.out.println("服务端启动 , 等待连接 .... ");
// 1.创建 ServerSocket对象,绑定端口,开始等待连接
ServerSocket ss = new ServerSocket(6666);
// 2.接收连接 accept 方法, 返回 socket 对象.
Socket server = ss.accept();
// 3.通过socket 获取输入流
InputStream is = server.getInputStream();
// 4.一次性读取数据
// 4.1 创建字节数组
byte[] b = new byte[1024];
// 4.2 据读取到字节数组中.
int len = is.read(b);
// 4.3 解析数组,打印字符串信息
String msg = new String(b, 0, len);
System.out.println(msg);
// =================回写数据=======================
// 5. 通过 socket 获取输出流
OutputStream out = server.getOutputStream();
// 6. 回写数据
out.write("我很好,谢谢你".getBytes());
// 7.关闭资源.
out.close();
is.close();
server.close();
}
}

客户端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ClientTCP {
public static void main(String[] args) throws Exception {
System.out.println("客户端 发送数据");
// 1.创建 Socket ( ip , port ) , 确定连接到哪里.
Socket client = new Socket("localhost", 6666);
// 2.通过Scoket,获取输出流对象
OutputStream os = client.getOutputStream();
// 3.写出数据.
os.write("你好么? tcp ,我来了".getBytes());
// ==============解析回写=========================
// 4. 通过Scoket,获取 输入流对象
InputStream in = client.getInputStream();
// 5. 读取数据数据
byte[] b = new byte[100];
int len = in.read(b);
System.out.println(new String(b, 0, len));
// 6. 关闭资源 .
in.close();
os.close();
client.close();
}
}

第三章 综合案例

3.1 文件上传案例

文件上传分析图解

  1. 【客户端】输入流,从硬盘读取文件数据到程序中。
  2. 【客户端】输出流,写出文件数据到服务端。
  3. 【服务端】输入流,读取文件数据到服务端程序。
  4. 【服务端】输出流,写出文件数据到服务器硬盘中。

基本实现

服务端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class FileUpload_Server {
public static void main(String[] args) throws IOException {
System.out.println("服务器 启动..... ");
// 1. 创建服务端ServerSocket
ServerSocket serverSocket = new ServerSocket(6666);
// 2. 建立连接
Socket accept = serverSocket.accept();
// 3. 创建流对象
// 3.1 获取输入流,读取文件数据
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
// 3.2 创建输出流,保存到本地 .
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
// 4. 读写数据
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}
//5. 关闭 资源
bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
}
}

客户端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class FileUPload_Client {
public static void main(String[] args) throws IOException {
// 1.创建流对象
// 1.1 创建输入流,读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg"));
// 1.2 创建输出流,写到服务端
Socket socket = new Socket("localhost", 6666);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

//2.写出数据.
byte[] b = new byte[1024 * 8 ];
int len ;
while (( len = bis.read(b))!=-1) {
bos.write(b, 0, len);
bos.flush();
}
System.out.println("文件发送完毕");
// 3.释放资源

bos.close();
socket.close();
bis.close();
System.out.println("文件上传完毕 ");
}
}

文件上传优化分析

  1. 文件名称写死的问题

    服务端,保存文件的名称如果写死,那么最终导致服务器硬盘,只会保留一个文件,建议使用系统时间优化,保证文件名称唯一,代码如下:

1
2
FileOutputStream fis = new FileOutputStream(System.currentTimeMillis()+".jpg") // 文件名称
BufferedOutputStream bos = new BufferedOutputStream(fis);
  1. 循环接收的问题

    服务端,指保存一个文件就关闭了,之后的用户无法再上传,这是不符合实际的,使用循环改进,可以不断的接收不同用户的文件,代码如下:

1
2
3
4
5
// 每次接收新的连接,创建一个Socket
while(true){
Socket accept = serverSocket.accept();
......
}
  1. 效率问题

    服务端,在接收大文件时,可能耗费几秒钟的时间,此时不能接收其他用户上传,所以,使用多线程技术优化,代码如下:

1
2
3
4
5
6
7
8
9
while(true){
Socket accept = serverSocket.accept();
// accept 交给子线程处理.
new Thread(() -> {
......
InputStream bis = accept.getInputStream();
......
}).start();
}

优化实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class FileUpload_Server {
public static void main(String[] args) throws IOException {
System.out.println("服务器 启动..... ");
// 1. 创建服务端ServerSocket
ServerSocket serverSocket = new ServerSocket(6666);
// 2. 循环接收,建立连接
while (true) {
Socket accept = serverSocket.accept();
/*
3. socket对象交给子线程处理,进行读写操作
Runnable接口中,只有一个run方法,使用lambda表达式简化格式
*/
new Thread(() -> {
try (
//3.1 获取输入流对象
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
//3.2 创建输出流对象, 保存到本地 .
FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fis);) {
// 3.3 读写数据
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}
//4. 关闭 资源
bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}

信息回写分析图解

前四步与基本文件上传一致.

  1. 【服务端】获取输出流,回写数据。
  2. 【客户端】获取输入流,解析回写数据。

回写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class FileUpload_Server {
public static void main(String[] args) throws IOException {
System.out.println("服务器 启动..... ");
// 1. 创建服务端ServerSocket
ServerSocket serverSocket = new ServerSocket(6666);
// 2. 循环接收,建立连接
while (true) {
Socket accept = serverSocket.accept();
/*
3. socket对象交给子线程处理,进行读写操作
Runnable接口中,只有一个run方法,使用lambda表达式简化格式
*/
new Thread(() -> {
try (
//3.1 获取输入流对象
BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
//3.2 创建输出流对象, 保存到本地 .
FileOutputStream fis = new FileOutputStream(System.currentTimeMillis() + ".jpg");
BufferedOutputStream bos = new BufferedOutputStream(fis);
) {
// 3.3 读写数据
byte[] b = new byte[1024 * 8];
int len;
while ((len = bis.read(b)) != -1) {
bos.write(b, 0, len);
}

// 4.=======信息回写===========================
System.out.println("back ........");
OutputStream out = accept.getOutputStream();
out.write("上传成功".getBytes());
out.close();
//================================

//5. 关闭 资源
bos.close();
bis.close();
accept.close();
System.out.println("文件上传已保存");
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}

客户端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class FileUpload_Client {
public static void main(String[] args) throws IOException {
// 1.创建流对象
// 1.1 创建输入流,读取本地文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test.jpg"));
// 1.2 创建输出流,写到服务端
Socket socket = new Socket("localhost", 6666);
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());

//2.写出数据.
byte[] b = new byte[1024 * 8 ];
int len ;
while (( len = bis.read(b))!=-1) {
bos.write(b, 0, len);
}
// 关闭输出流,通知服务端,写出数据完毕
socket.shutdownOutput();
System.out.println("文件发送完毕");
// 3. =====解析回写============
InputStream in = socket.getInputStream();
byte[] back = new byte[20];
in.read(back);
System.out.println(new String(back));
in.close();
// ============================

// 4.释放资源
socket.close();
bis.close();
}
}

3.2 模拟B\S服务器(扩展知识点)

模拟网站服务器,使用浏览器访问自己编写的服务端程序,查看网页效果。

案例分析

  1. 准备页面数据,web文件夹。

    复制到我们Module中,比如复制到day08中

  2. 我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static void main(String[] args) throws IOException {
    ServerSocket server = new ServerSocket(8000);
    Socket socket = server.accept();
    InputStream in = socket.getInputStream();
    byte[] bytes = new byte[1024];
    int len = in.read(bytes);
    System.out.println(new String(bytes,0,len));
    socket.close();
    server.close();
    }

  3. 服务器程序中字节输入流可以读取到浏览器发来的请求信息

GET/web/index.html HTTP/1.1是浏览器的请求消息。/web/index.html为浏览器想要请求的服务器端的资源,使用字符串切割方式获取到请求的资源。

1
2
3
4
5
6
7
8
//转换流,读取浏览器请求第一行
BufferedReader readWb = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String requst = readWb.readLine();
//取出请求资源的路径
String[] strArr = requst.split(" ");
//去掉web前面的/
String path = strArr[1].substring(1);
System.out.println(path);

案例实现

服务端实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class SerDemo {
public static void main(String[] args) throws IOException {
System.out.println("服务端 启动 , 等待连接 .... ");
// 创建ServerSocket 对象
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
// 转换流读取浏览器的请求消息
BufferedReader readWb = new
BufferedReader(new InputStreamReader(socket.getInputStream()));
String requst = readWb.readLine();
// 取出请求资源的路径
String[] strArr = requst.split(" ");
// 去掉web前面的/
String path = strArr[1].substring(1);
// 读取客户端请求的资源文件
FileInputStream fis = new FileInputStream(path);
byte[] bytes= new byte[1024];
int len = 0 ;
// 字节输出流,将文件写会客户端
OutputStream out = socket.getOutputStream();
// 写入HTTP协议响应头,固定写法
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
// 必须要写入空行,否则浏览器不解析
out.write("\r\n".getBytes());
while((len = fis.read(bytes))!=-1){
out.write(bytes,0,len);
}
fis.close();
out.close();
readWb.close();
socket.close();
server.close();
}
}

访问效果

  • 火狐

小贴士:不同的浏览器,内核不一样,解析效果有可能不一样。

发现浏览器中出现很多的叉子,说明浏览器没有读取到图片信息导致。

浏览器工作原理是遇到图片会开启一个线程进行单独的访问,因此在服务器端加入线程技术。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class ServerDemo {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8888);
while(true){
Socket socket = server.accept();
new Thread(new Web(socket)).start();
}
}
static class Web implements Runnable{
private Socket socket;

public Web(Socket socket){
this.socket=socket;
}

public void run() {
try{
//转换流,读取浏览器请求第一行
BufferedReader readWb = new
BufferedReader(new InputStreamReader(socket.getInputStream()));
String requst = readWb.readLine();
//取出请求资源的路径
String[] strArr = requst.split(" ");
System.out.println(Arrays.toString(strArr));
String path = strArr[1].substring(1);
System.out.println(path);

FileInputStream fis = new FileInputStream(path);
System.out.println(fis);
byte[] bytes= new byte[1024];
int len = 0 ;
//向浏览器 回写数据
OutputStream out = socket.getOutputStream();
out.write("HTTP/1.1 200 OK\r\n".getBytes());
out.write("Content-Type:text/html\r\n".getBytes());
out.write("\r\n".getBytes());
while((len = fis.read(bytes))!=-1){
out.write(bytes,0,len);
}
fis.close();
out.close();
readWb.close();
socket.close();
}catch(Exception ex){

}
}
}

}

访问效果:

图解:

JavaSE基础 (三十四)

发表于 2020-03-30 | 分类于 java

Annocation(注释 注解)

注释

单行注释 //
多行注释 /*   */
文档注释 /**  */

注解

注解的写法

@xxxx[{一些信息}]

注解放在哪里

类的上面  属性的上面 方法的上面 构造方法的上面 参数的前面

注解的作用

1.用来充当注释的作用(仅仅是一个文字说明)@deprecated
2.用来做代码的检测(验证)    @Override
3.可以携带一些信息(内容)    文件.properties.xml 注解

Java中有一些写好的注解给我们使用

@Deprecated   用来说明方法是废弃的
@Override     用来做代码检测 检测此方法是否重写
@SuppressWarnings(信息)  String[]  {""}如果数组内的元素只有一个长度  可以省略()
unused   变量定义后未被使用
serial   类实现了序列化接口  不添加序列化ID号
rawtypes  集合没有定义泛型
deprecation 方法以废弃
*unchecked  出现了泛型问题 可以不检测
all       包含以上所有(不推荐)

注解中可以携带信息 可以不携带

信息不能随便写 信息类型只能是如下的类型
1.基本数据类型
2.String数据类型
3.枚举数据类型
4.注解类型@
5.数组类型[] 数组的内部需要加上的四种类型

如何自己描述一个注解类型

1.通过@interface 定义个新的注解类型
2.发现写法与接口非常相似
  可以描述public static final 的属性 比较少见
  可以描述public abstract的方法 方法返回值必须有 返回值类型是如上那些
3.我们自己定义的注解拿来使用
     光定义还不够还需要做很多细致的说明(需要利用java提供好的注解来说明)
     元注解(也是注解 不是拿来使用的 是用来说明注解)
     @target 描述当前的注解可以放在哪里写
     @retention 描述当前的这个注解存在什么作用域中的
            源代码文件---->编译---->字节码文件---->加载--->内存执行
            SOURCE                    CLASS                  RUNTIME
     @Inherited  描述当前这个注解能否被子类对象继承
     @Document   描述这个注解能否生成文档

我们使用自己描述的注解

问题1.注解里面描述了一个方法 方法没有参数 方法有返回值String[]
     使用注解的时候让我们传递参数
     理解为 注解的方法做事 将我们传递的参数 搬运走了给了别人
问题2.使用别人写好的注解不用写方法名 我们自己定义的方法必须写名字
     如果我们自己定义的注解 只有一个方法名字叫value
     在使用的时候可以省略方法名
     如果传递的信息是一个数组 数组内只有一个元素 可以省略{}
     如果是两个以上 每一个方法必须写名字

JavaSE基础 (三十三)

发表于 2020-03-30 | 分类于 java

反射 reflect

类是用来描述一组类

反射机制认为是描述一组类

Class           用来描述类本身
Package         用来描述所属类的包
Field           用来描述类中的属性
Method          用来描述类的方法
Constructor     用来描述类中的构造方法
Annotation      用来描述类中的注解

如何获取Class

如何下三种方式
Class    clazz = Class.for.Name("包名.类名")
Class    clazz = 类名.class;
Class    clazz = 对象.getClass();//Object类中的方法

Class中的常用方法

int result = getModifiers();   获取类的修饰符(权限 特征)
每一个修饰符 用一个整数来进行表示
0开始----0-1-2-4-8-16-32--64-128-256-512-1024
0---默认不写  1---public         2---private    4---protected 8---static
16---final    32----sychronized  64---voliate   128---trasnsient
256---native  512---interface    1024---abstract
String name = getName(); 获取名字
String simpleName = getSimpleName();简单名
Package p = getPackage();
p.getName();
Class sclazz = getSuperClass();//获取父类
Class[] classes = getModifiers();
Object obj = newInstance();//默认调用无参构造方法创建对象
Filed f = getField("属性名");
Field[] fs = getField();
如上两个方法只能获取公有属性 但是包含继承过来父类属性
getDeclaredField("属性")
Field[] fs =getDeclaredFields();
如上的两个方法能获取公有和私有的属性 但是只能获取本类中的属性

Filed类中常用方法

int = getModifers()
Class = getType()
String = getName()
操作属性 向里面存值
set(对象,值)
操作属性 从里面取值
值 = get(对象)
对象 =new():// 创建对象空间 当前对象空间有自己的一套元素(属性 方法)    
setAccessable(true)可以修改属性设备操作

如何操作类中的方法

Class类中的方法
Method m = class.getMethod("方法名",Class...参数类型);  获取公有方法(自己类+父类)
Method[] = clazz.getMethod();获取所有的方法(公有 自己+父类)
Method = getDeclaredMethod("方法名字",参数类型class...)获取所有的方法(自己类 公有 私有)
Method = getDeclaredMethods()   获取全部的方法(自己类 公有加私有)
获取构造方法
Construct = clazz.getConstructor(class...参数类型)

Method类中常用的方法

int mm = m.getModififers()//获取方法的修饰符(权限+特征)
Class mrt = m.getReturnType();//获取返回值数据类型
String mn= m.getName();//获取方法名字
Class[] mpts = m.getParameterTypes();//获取方法参数列表的类型
Class[] mets = m.getExceptionTypes();//获取方法抛出异常类型
如何操作方法
调用方法 让他执行一次
Object result = invoke(对象.执行方法需要传递的所有参数)
若方法是私有方法 不许与操作
可以设置setAccessable(True)设置方法使用权准入

Constructor类中的常用方法

con.getModifiers();
con.getName();
con.getParamenterTyper();
con.getExceptionTypers()
如何操作构造方法
执行一次 创建对象
Object = newInstance(执行构造方法时的所有参数);

JavaSE基础 (三十二)

发表于 2020-03-29 | 分类于 java

主线程 系统线程

用户线程 main

守护线程 GC

线程——操作系统级别 CPU

如何在Java中创建线程 让线程执行 多线程

掌握线程的几种不同状态以及如何切换

new      start()     cpu分配run()      wait()       exception over 
创建线程----就绪状态---执行状态------等待/挂起----------异常/消亡
               |                          |
               |__________________________|
                      notify/notifyAll唤醒

实现线程的过程

自己描述一个类

基础父类Thead

重写run方法

new一个线程对象 调用start方法让线程进入就绪状态

实现线程的过程

自己描述一个类

实现一个父类接口Runnerable

重写run方法

new一个线程对象 需要创建Thead将自己包起来 然后调用Start()

生产者和消费者模型

通过这个模型 成功演示出 线程的安全问题

两个消费者 同时访问一个仓库对象 仓库内只有一个元素的时候
两个消费者 并发访问 会有可能产生抢夺资源的问题

自己解决一下线程安全的问题

让仓库对象被线程访问的时候 仓库对象被锁定
仓库对象只能被一个线程所访问 其他对象处于等待状态
特征修饰符
synchronized 同步 一个时间段只有一个线程能访问

线程安全锁两种写法

将synchronized关键字 放在方法结构上

public synchronized void get(){}
锁定的是调用方法时的那个对象

将synchronized关键字 方法在(构造方法内部)的内部

public void get(){
     好多代码
     synchronzied(对象){
     好多代码
     }
     好多代码
}

我们觉得return 不是很好

应该让线程的不同状态来回切换
执行 等待 执行 等待
wait() Object类中的方法
对象.wait();
对象.wait();不是当前对象wait 
             访问当前这个对象的线程wait
 notify
 notifyAll
 p.setPriority(10);   p.getPriorty();
 产生一个类似假死的状态
 所有的线程进入等待状态 没有线程做事

笔试题

程序 进程 线程 概念的区别

线程的创建方式

线程的几种状态 如何切换

sleep方法 和 wait方法区别

1.类    Thead类                       Object类
2.调用  静态 类名                       对象
3,理解  那个位置调用                    对象调用的方法
      那个线程等待                    访问对象的其他线程等待
4.唤醒   不需要别人                     需要其他对象调用notiy唤醒
5.锁    不会释放锁                      等会会释放锁

比较重要的方法 join

设计一个模型
1.有两个线程 One   Two  Two加入到One里面
2.设计模型的时候  two线程在one的run里面创建 保证两个先后顺序
3.Two.join();  无参数==0; 有参数==2000
         two.join(0){
         while(two.isAlive()){
                    Two.wait(0);//访问Two的线程进入等待状态
         }

死锁

一旦对象锁定 不释放的情况下其他对象都需要等待 有可能会产生死锁的效果

哲学家就餐问题

解决死锁的问题

礼让—–>产生时间差

不要产生公用对象问题

12…5
我与我周旋

我与我周旋

48 日志
5 分类
2 标签
RSS
GitHub E-Mail
© 2020 我与我周旋
本站访客数:
博客全站共37.9k字