LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

P-luminary

油画项目(介于JavaWeb与数据库之间)

2023/10/1

企业门户网站项目实战

  • 需求说明与环境准备
  • 实现前端展示模块
  • 实现后台数据管理模块

(MVC架构模型) 数据显示与数据处理分开

Model模型 View试图 Controller控制器 [是一种设计理念]

Model - 模型
  • 模型(Model)负责生产业务需要的数据 Service[结尾]实现业务逻辑

    public class MathService{
        public List square(int max){
            List list = new ArrayList();
            for(int i = 0; i <= max; i++){
                long result = i * i;
                list.add(result);
            }
            return list;
        }
    }
    
Controller - 控制器 (Java Web领域是WebServlet)
  • 控制器(Controller)是界面(View)与模型(Model)的粘合剂
@WebServlet("/square")
public class HelloWord  extends HttpServlet{
    public void doGet(HttpServletRequest req, HttpServletResponse res){
        //1.接收数据 接收web的http请求参数
        int max = Integer.parselnt(request.getParameter("max"));
        //2.调用处理类(模型)进行处理 
        MathService ms = new MathService();
        List list = ms.square(max);//前台调用的参数传入square方法中
        req.setAttribute("squareList", list);//执行结果放到请求属性中
        //3.跳转界面(View)
        request.getRequestDispatcher("/result.jsp").forward(req,res);
    }
}
View - 视图
  • 试图(View)用于展示最终结果
URL:http://localhost:8080/square?max=100
----------------------------------------
<ul>
<c:foreach items = "${squareList}" var = "r" varStatus = "idx">
   <li>${idx.index}的平方是${r}</li>
</<c:foreach>
</ul>

MVC架构模式优点

  • 保证了软件层面的解耦,同时保障了团队合作的组织架构

  • 软件团队分工合作,成员各司其职 通过controller联合

  • 组件可灵活替代,互不影响

通过控制器将前端传入的参数进行接收调用后台的模型产生结果,结果通过控制器保存在当前的属性中在视图中进行展现

工程结构与开发规约

工程结构
mgallery - eclipse工程项目
 /src - java源代码目录
 /WebContent - Web资源目录
 /css - css文件目录
 /js - js文件目录
 /image - 图片资源目录
 /upload - 上传文件目录
 /WEB-INF   //jsp数据来自controller 不允许在web中直接访问 要从控制器跳转
   /jsp - jsp页面目录
   /lib - jar文件目录
   /classes - 编译后的class目录
   /web.xml web描述符文件
包结构
com.imooc.mgallery //逆命名法
    /controller - 存放Servlet控制器类 //承上启下接收参数 调用逻辑 返回处理结果
    /service - 存放处理逻辑类model[伪数据库] //完成业务逻辑 service与dao进行传递调用
    /dao - Data Access Object 数据访问对象类 数据读写的java类 数据来自xml文件
    /entity - 存放实体类 JavaBean java中的简单对象
    /utils - 通用工具类 底层通用的工具类或方法

Dao类[Data Access Object]

  • XxxDao类只负责对数据进行读取、写入操作
  • 只负责对数据 增、删、改、查
示例:PaintingDao //针对油画数据进行增删改查
public class PaintingDao{
    public void append(){...} //新增数据
    public void update(){...} //修改数据
    public void delete(){...} //删除数据
    public void findAll(){...} //查询数据
}

Service与Dao的关系

  • Service负责进行流程处理,需**持久化[java处理在内存中 存储在数据库防止丢失]**时调用Dao
  • Dao只负责单纯对数据进行增删改查操作

JavaBean

  • 对一种类的使用形式的统称

  • JavaBean是一种Java中可重用组件

  • JavaBean不是技术,而是一种Java类的格式要求

  • JavaBean在Java领域非常常见,通常用于存储数据

JavaBean格式要求

  • 类必须是pubilc并提供默认构造函数
  • 所有属性private私有化
  • 属性通过getter与setter方法进行读写
public class Painting{//类公有化
    public Painting(){...}; //提供默认构造函数,可不写
    private Integer id; //属性私有
    private String pname;
    public Integer getId(){return id;} //getter获取属性值
    public void setId(Integer id){this.id = id;} //setter设置属性值
    public String getPname(){return pname;}
    public void setPname(String pname){this.pname = pname;}
}

创建mgallery工程(ViewControllerServiceDao){VCSD}

  • 开发PaintingDao读取XML数据,实现分页操作
  • 开发PaintingService服务类,对PaintingDao进行调用
  • 开发PaintingController控制器,调用PaintingService
  • 重写index.html,利用JSP技术读取分页数据

关键类与方法

  • XmlDataSource类 - XML数据源工具类,简化Dao提取操作
  • PaintingDao.pagination() - 数据分页查询方法
  • PageModel类 - 分页结果的包装类

Dom4j

  • Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台
  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象
Dom4j开发流程回顾
  • SAXReader.read()读取XML文件,得到Document对象
  • document.selectNodes()利用Xpath得到XML节点集合
  • 遍历XML节点,包装成JavaBean或者集合对象返回

开发XmlDataSource (utils)

【详细】IntelliJ IDEA: 无法创建Java Class文件_idea 不能新建java class-CSDN博客
IDEA部署Tomcat提示:Warning no artifacts configured-CSDN博客
Class.getResource(“xxx.css”)得到值为null-CSDN博客

utils/XmlDataSource.java
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;
    static{ //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        System.out.println(dataFile);
          //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            for (Node node : nodes){
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }
    /**
     *  获取所有油画Painting对象
     * @return Painting List
     */
    public static List<Painting> getRawData(){
        return data;
    }
    public static void main(String[] args) {
//        new XmlDataSource();
        List<Painting> ps = XmlDataSource.getRawData();
        System.out.println(ps);
    }
}
META-INF/painting.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 数据文件 -->
<root>
    <painting id="1">
        <pname>古典油画鉴赏1</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/1.jpg</preview>
        <description>古典油画鉴赏1的描述文本</description>
    </painting>
    <painting id="2">
        <pname>古典油画鉴赏2</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/2.jpg</preview>
        <description>古典油画鉴赏2的描述文本</description>
    </painting>
    <painting id="3">
        <pname>古典油画鉴赏3</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/3.jpg</preview>
        <description>古典油画鉴赏3的描述文本</description>
    </painting>
    <painting id="4">
        <pname>古典油画鉴赏4</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/4.jpg</preview>
        <description>古典油画鉴赏4的描述文本</description>
    </painting>
    <painting id="5">
        <pname>古典油画鉴赏5</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/5.jpg</preview>
        <description>古典油画鉴赏5的描述文本</description>
    </painting>
    <painting id="6">
        <pname>古典油画鉴赏6</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/6.jpg</preview>
        <description>古典油画鉴赏6的描述文本</description>
    </painting>
    <painting id="7">
        <pname>古典油画鉴赏7</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/7.jpg</preview>
        <description>古典油画鉴赏7的描述文本</description>
    </painting>
    <painting id="8">
        <pname>古典油画鉴赏8</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/8.jpg</preview>
        <description>古典油画鉴赏8的描述文本</description>
    </painting>
    <painting id="9">
        <pname>古典油画鉴赏9</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/9.jpg</preview>
        <description>古典油画鉴赏9的描述文本</description>
    </painting>
    <painting id="9">
        <pname>古典油画鉴赏9</pname>
        <category>1</category>
        <price>3800</price>
        <preview>/upload/9.jpg</preview>
        <description>古典油画鉴赏9的描述文本</description>
    </painting>
    <painting id="10">
        <pname>抽象派油画鉴赏1</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/10.jpg</preview>
        <description>抽象派油画鉴赏1的描述文本</description>
    </painting>
    <painting id="11">
        <pname>抽象派油画鉴赏2</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/11.jpg</preview>
        <description>抽象派油画鉴赏2的描述文本</description>
    </painting>
    <painting id="12">
        <pname>抽象派油画鉴赏3</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/12.jpg</preview>
        <description>抽象派油画鉴赏3的描述文本</description>
    </painting>
    <painting id="13">
        <pname>抽象派油画鉴赏4</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/13.jpg</preview>
        <description>抽象派油画鉴赏4的描述文本</description>
    </painting>
    <painting id="14">
        <pname>抽象派油画鉴赏5</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/14.jpg</preview>
        <description>抽象派油画鉴赏5的描述文本</description>
    </painting>
    <painting id="15">
        <pname>抽象派油画鉴赏6</pname>
        <category>2</category>
        <price>3800</price>
        <preview>/upload/15.jpg</preview>
        <description>抽象派油画鉴赏6的描述文本</description>
    </painting>
    
</root>
entity/Painting.java
public class Painting{
    private Integer id;
    private String pname;
    private Integer category;
    private Integer price;
    private String preview; //油画图片地址
    private String description; //描述
    ......
}

开发PageMode1 (utils)

utils/PageModel.java
public class PageModel {
    private int page;
    private int totalPages;
    private int rows; //每页几条数据
    private int totalRows; //原始数据多少条
    private int pageStartRow; //当前页是从第几行开始的
    private int pageEndRow; //到第几行结束的 结束行号
    private boolean hasNextPage; //是否下一页(尾页)
    private boolean hasPreviousPage; //是否上一页(首页)
    private List pageData; //当前页面数据

    public PageModel() {
    }

    /**
     * 初始化PageMode1对象,计算分页属性
     * @param page
     * @param rows
     * @param data
     */
    public PageModel(List data, int page, int rows) {
        this.page = page;
        this.rows = rows;
        totalRows = data.size();
        //总页数计算规则:总行数/每页记录数,能整除页数取整,不能整除向上取整
        //18/6=3 | 2/6≈3.33 向上取整=4  intValue()得到整数部分
        //Nath.ceil向上取整  Math.floor浮点数向下取整
        //小细节: 20/6≈3.33 但是totalRows 和 rows都是整数 20/6=3 向上取整还是3
        //仅需在一个后面×1f即可解决问题 (rows*1f)默认类型转换返回浮点数
        totalPages = new Double(Math.ceil(totalRows/(rows * 1f))).intValue();
        pageStartRow = (page - 1) * rows; //0
        pageEndRow = page * rows; //6
        //totalRows:20 | totalPage:4 | rows:6
        //pageEndRow=4*6=24>20 执行subList()抛出下标越界异常
        if (pageEndRow > totalRows){
            pageEndRow = totalRows; //让20作为结束行号 不会越界
        }
        pageData = data.subList(pageStartRow, pageEndRow); //得到分页数据
        if (page > 1) {
            hasPreviousPage = true;
        }else {
            hasPreviousPage = false;
        }
        if (page < totalPages) { //判断是否存在下一页
            hasNextPage = true;
        }else {
            hasNextPage = false;
        }
    }

    public static void main(String[] args) {
        List sample = new ArrayList();
        for (int i = 1; i < 100; i++) {
            sample.add(i);
        }
        PageModel pageModel = new PageModel(sample,6,8);
        System.out.println(pageModel.getPageData()); //当前页的list集合
        System.out.println(pageModel.getTotalPages());
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow()) ;
    }
+getter and setter 

油画数据分页设计思路

@Param注解的使用方法

@Param的作用就是給参数命名,比如在mapper里面某个方法A(int id),当添加注解后A(@Param(“userId”) int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。若在SQL中,通过#{userId}进行取值給SQL的参数赋值。

快速添加注解只需要在前面 /** + 回车

油画数据分页展示

开发PaintingDao与PaintingService
service/PaintingDao.java
package com.example.mgallery.dao;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;
import com.example.mgallery.utils.XmlDataSource;

import java.util.List;

//获得最原始的 对其进行分页处理
public class PaintingDao {
    public PageModel pagination(int page, int rows){
        //Painting油画对象集合
        List<Painting> list = XmlDataSource.getRawData();
        //PageModel分页处理得到分页数据及分页附加
        PageModel pageModel = new PageModel(list,page,rows);
        return pageModel;
    }
}
service/PaintingService
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows){
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        return paintingDao.pagination(page, rows); //调用并返回
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
开发PaintingController控制器
controller/PaintingController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/page")
public class PaintingController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public PaintingController(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收Http数据
        String page = req.getParameter("p"); //页号
        String rows = req.getParameter("r"); //每页记录数
        if (page == null){
            page = "1"; //没传入则默认查询第一个
        }if (rows == null){
            rows = "6"; //每页默认显示六条数据
        }
        //2.调用Service方法,得到处理结果
        PageModel pageModel = paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/src/main/webapp/index.jsp").forward(req,resp); //跳转jsp
    }
}

前台门户首页(由静态转换为动态)

HTML转换JSP变更流程
  • 打开HTML文件,在首行增加 < %@page contentType % >
  • 更改扩展名从 .html.jsp,移动到WEB-INF/jsp目录下
  • 提示:JSP不要实用<%%>代码块,实用EL+JSTL提取数据

输入(http://localhost:8080/)不能正常访问,但是添加项目名后可以访问-CSDN博客
实例化servlet类[web.LoginServlet]异常 servle-CSDN博客

WEB-INF/jsp/index.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css\common.css">
    <script type="text/javascript" src="js\js1.js"></script>
</head>
<body>
<div class="header">
    <div class="logo">
        <img src="image\logo.png">
    </div>
    <div class="menu" onclick="show_menu()" onmouseleave="show_menu1()">
        <div class="menu_title" ><a href="###">内容分类</a></div>
        <ul id="title">
            <li>现实主义</li>
            <li>抽象主义</li>
        </ul>
    </div>
    <div class="auth">
        <ul>
            <li><a href="#">登录</a></li>
            <li><a href="#">注册</a></li>
        </ul>
    </div>
</div>
<div class="content">
    <div class="banner">
        <img src="image/welcome.png" class="banner-img">
    </div>
    <div class="img-content">
        <ul>
            <c:forEach items="${pageModel.pageData}" var="painting"> <!--pageModel.java-->
                <li>
                    <img src="${painting.preview}" class="img-li">  <!--painting.java-->
                    <div class="info">
                        <h3>${painting.pname}</h3>
                        <p>
                                ${painting.description}
                        </p>
                        <div class="img-btn">
                            <div class="price"><fmt:formatNumber pattern="¥0.00" value="${painting.price}"></fmt:formatNumber></div>
                            <a href="#" class="cart">
                                <div class="btn">
                                    <img src="image/cart.svg">
                                </div>
                            </a>
                        </div>
                    </div>
                </li>
            </c:forEach>
        </ul>
    </div>
    <div class="page-nav">
        <ul>
            <li><a href="/mgallery/page?p=1">首页</a></li> <!--当前页减一就是上一页 否则就是1-->
            <li><a href="/mgallery/page?p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上一页</a></li>
            <c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
                <li><span ${pno==pageModel.page?"class='first-page'":""}> <!--选中的页才有⚪圈圈 三目运算符不满足产生空字符串-->
                    <a href="/mgallery/page?p=${pno}">
                            ${pno}
                    </a></span></li>
            </c:forEach>
            <li><a href="/mgallery/page?p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下一页</a></li>
            <li><a href="/mgallery/page?p=${pageModel.totalPages}">尾页</a></li>
        </ul>
    </div>
</div>
<div class="footer">
    <p><span>P_luminary</span>©2023.10.3 POWERED BY GITHUB.COM</p>
</div>
</body>
</html>

实现分类查询功能 [改造代码]

从Dao层面进行对数据的过滤

PaintingDao.java
package com.example.mgallery.dao;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;
import com.example.mgallery.utils.XmlDataSource;

import java.util.ArrayList;
import java.util.List;

//获得最原始的 对其进行分页处理
public class PaintingDao {
    public PageModel pagination(int page, int rows){
        //Painting油画对象集合
        List<Painting> list = XmlDataSource.getRawData();
        //PageModel分页处理得到分页数据及分页附加
        PageModel pageModel = new PageModel(list,page,rows);
        return pageModel;
    }

    public PageModel pagination(int catagory, int page, int rows){ //int catagory添加Dao层对数据进行筛选
        List<Painting> list = XmlDataSource.getRawData();
        List<Painting> categoryList = new ArrayList();
        for (Painting p : list){
            //如果等于从外侧添加的筛选条件 则添加categoryList内
            if (p.getCategory() == catagory){
                categoryList.add(p);
            }
        }
        PageModel pageModel = new PageModel(categoryList,page,rows);
        return  pageModel;
    }
}
----------------------------------------------------------------------
PaintingService.java
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows, String...category){  //最后一个是添加 可选参数(可能/不一定出现一个或多个)
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        if (category.length==0 || category[0] == null){
        return paintingDao.pagination(page, rows); //调用并返回
        }else { //程序进行可选调用 两个不同路径 尽量不要去修改类结构
            return paintingDao.pagination(Integer.parseInt(category[0]), page, rows);
        }
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
---------------------------------------------------------------------
PaintingController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class PaintingController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public PaintingController(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1.接收Http数据
        String page = req.getParameter("p"); //页号
        String rows = req.getParameter("r"); //每页记录数
        String category = req.getParameter("c");
        if (page == null){
            page = "1"; //没传入则默认查询第一个
        }if (rows == null){
            rows = "6"; //每页默认显示六条数据
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(page), Integer.parseInt(rows), category);
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/index.jsp").forward(req,resp); //跳转jsp
    }
}
---------------------------------------------------------------------
webapp/WEB-INF/jsp/index.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" type="text/css" href="css\common.css">
    <script type="text/javascript" src="js\js1.js"></script>
</head>
<body>
<%--JSTL混合书写判断 判断c是否存在 存在追加链接--%>
<c:if test="${param.c != null}">
    <c:set var = "categoryParam" value="&c=${param.c}"></c:set>
</c:if>
<c:if test="${param.c == null}">
    <c:set var = "categoryParam" value=""></c:set>
</c:if>
<div class="header">
    <div class="logo">
        <img src="image\logo.png">
    </div>
    <div class="menu"   onclick="show_menu()" onmouseleave="show_menu1()">
        <div class="menu_title" ><a href="###">内容分类</a></div>
        <ul id="title">
            <li><a href="/mgallery/page?c=1">现实主义</a></li>
            <li><a href="/mgallery/page?c=2">抽象主义</a></li>
        </ul>
    </div>
    <div class="auth">
        <ul>
            <li><a href="#">登录</a></li>
            <li><a href="#">注册</a></li>
        </ul>
    </div>
</div>
<div class="content">
    <div class="banner">
        <img src="image/welcome.png" class="banner-img">
    </div>
    <div class="img-content">
        <ul>
            <c:forEach items="${pageModel.pageData}" var="painting">
                <li>
                    <img src="${painting.preview}" class="img-li">
                    <div class="info">
                        <h3>${painting.pname}</h3>
                        <p>
                                ${painting.description}
                        </p>
                        <div class="img-btn">
                            <div class="price"><fmt:formatNumber pattern="¥0.00" value="${painting.price}"></fmt:formatNumber></div>
                            <a href="#" class="cart">
                                <div class="btn">
                                    <img src="image/cart.svg">
                                </div>
                            </a>
                        </div>
                    </div>
                </li>
            </c:forEach>
        </ul>
    </div>
    <div class="page-nav">
        <ul>
            <li><a href="/mgallery/page?p=1${categoryParam}">首页</a></li> <!--当前页减一就是上一页 否则就是1-->
            <li><a href="/mgallery/page?p=${pageModel.hasPreviousPage?pageModel.page-1:1}${categoryParam}">上一页</a></li>
            <c:forEach begin="1" end="${pageModel.totalPages}" var="pno" step="1">
                <li><span ${pno==pageModel.page?"class='first-page'":""}> <!--选中的页才有⚪圈圈 三目运算符不满足产生空字符串-->
                     <%--  c不存在,则href="/mgallery/page?p=1"  c存在,测href="/mgallery/page?p=1&c=1"  --%>
                    <a href="/mgallery/page?p=${pno}${categoryParam}">
                            ${pno}
                    </a></span></li>
            </c:forEach>
            <li><a href="/mgallery/page?p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}${categoryParam}">下一页</a></li>
            <li><a href="/mgallery/page?p=${pageModel.totalPages}${categoryParam}">尾页</a></li>
        </ul>
    </div>
</div>
<div class="footer">
    <p><span>P_luminary</span>©2023.10.3 POWERED BY GITHUB.COM</p>
</div>
</body>
</html>
利用默认首页跳转到任何地址上 [常用开发技巧]
index.html
<script>
    window.location.href="/page"
</script>

实现后台数据管理

前台与后台的区别
前台系统 后台系统
开放度 对外开放 不对外开放
面向群体 客户 企业内部人员
功能 提供查询与交互 管理前台数据
设计侧重点 良好的用户体验 严密的业务逻辑
访问量
典型案例 猫眼电影网 [后台有评论审核系统] XX公司无纸质化办公系统
后台实现功能

油画列表 油画预览 删除油画 修改油画 新增与上传油画

实现油画列表功能

重用pagination方法实现分页列表

原有的PaintingController不可以继续被用 术业有专攻 要再创建一个使用

ManagementController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();
    public ManagementController() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        String method = req.getParameter("method");
        if (method.equals("list")){ //去显示分页的数据
            this.list(req, resp);
        } else if (method.equals("delete")) {
            // this.delete(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p==null){
            p = "1";
        }
        if (r==null){
            r = "6";
        }
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel", pageModel);
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req, resp);
    }
}
management.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>门户数据管理平台</title>
    <style>
        body{
            margin: 0 auto;
        }
        a{
            text-decoration: none;
        }
        .left{
            float: left;
        }
        .right{
            float: right;
        }
 
        .pg_header{
            position: fixed;
            top: 0;
            width: 100%;
            height: 48px;
            background-color: lightseagreen;
            color: white;
            z-index: 10;
        }
        .pg_header .logo{
            height: 48px;
            width: 200px;
            text-align: center;
            color: white;
        }
        .pg_header .logo a img{
            height: 48px;
            width: 200px;
            cursor: pointer;
        }
        .pg_header .person_info{
            position: relative;
            height: 48px;
            width: 160px;
            /*text-align: center;*/
        }
        .pg_header .person_info img{
            border: 0;
            height: 48px;
            width: 50px;
            /*使用border-radius可以定义边框的圆角程度*/
            border-radius: 50%;
        }
        .pg_header .person_info .info{
            position: absolute;
            width: 150px;
            background-color: lightseagreen;
            top: 50px;
            z-index: 20;
            display: none;
        }
        .pg_header .person_info .info a{
            display: block;
            color: white;
            padding: 5px;
        }
        .pg_header .person_info:hover .info{
            display: block;
        }
        .pg_header .icons{
            line-height: 48px;
            padding: 0 20px 0 5px;
        }
        .pg_header .icons:hover{
            background-color: lightseagreen;
        }
        .pg_header .icons span{
            padding: 1px 5px;
            line-height: 1px;
            border-radius: 50%;
            background-color: red;
            font-size: 12px;
        }
        .pg_content .menu{
            position: absolute;
            top: 50px;
            left: 0;
            bottom: 0;
            width: 300px;
            border:0px;
            border-right: 1px solid #ccc;
        }
        .pg_content .content{
            position: absolute;
            top: 50px;
            right: 0;
            bottom: 0;
            left: 302px;
            overflow: auto;
            min-width: 980px;
            z-index: 8;
            border:0px;
            overflow: hidden;
        }
        .menu_item{
            display: block;
            padding: 10px 20px;
            border-bottom: 1px solid #ccc;
            font-size: 20px; 
            color: #666666;
        }
        
        .menu_item:hover{
            color: white;
            background: lightseagreen;
        }
    </style>
</head>
<body>
    <!-- 顶端导航栏 -->
    <div class="pg_header">
        <!-- Logo与名称 -->
        <div class="logo left">
            <a href="javascript:void(0)" target="_blank">
                <img src="image/logo_1.png">    
            </a>
            
        </div>
        
        <!-- 用户头像与菜单 -->
        <div class="person_info right" style="vertical-align: top;" >
            <img src="image/head.png">
            <span style="line-height: 50px;vertical-align: top;">小企鹅</span>
            <div class="info">
                <a href="javascript:void(0)">我的信息</a>
                <a href="javascript:void(0)">修改密码</a>
                <a href="javascript:void(0)">注销</a>
            </div>
        </div>
        <div class="icons right">
            <i class="far fa-bell"></i>
        </div>
    </div>
    <div class="pg_content">
        <!-- 左侧功能区菜单 -->
        <div class="menu">
            <a href = "#" class="menu_item" target="ifcontent">油画列表</a>
            <a href = "#" class="menu_item" target="ifcontent">新增作品</a>
        </div>
        <!-- 主体框架 -->
        <div class="content">
             <iframe name="ifcontent" style="width:100%;height:100%;overflow-y: hidden;border:0px;min-width: 800px;" src=""></iframe>
        </div>
    </div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>门户数据管理平台</title>
    <style>
        body{
            margin: 0 auto;
        }
        a{
            text-decoration: none;
        }
        .left{
            float: left;
        }
        .right{
            float: right;
        }

        .pg_header{
            position: fixed;
            top: 0;
            width: 100%;
            height: 48px;
            background-color: lightseagreen;
            color: white;
            z-index: 10;
        }
        .pg_header .logo{
            height: 48px;
            width: 200px;
            text-align: center;
            color: white;
        }
        .pg_header .logo a img{
            height: 48px;
            width: 200px;
            cursor: pointer;
        }
        .pg_header .person_info{
            position: relative;
            height: 48px;
            width: 160px;
            /*text-align: center;*/
        }
        .pg_header .person_info img{
            border: 0;
            height: 48px;
            width: 50px;
            /*使用border-radius可以定义边框的圆角程度*/
            border-radius: 50%;
        }
        .pg_header .person_info .info{
            position: absolute;
            width: 150px;
            background-color: lightseagreen;
            top: 50px;
            z-index: 20;
            display: none;
        }
        .pg_header .person_info .info a{
            display: block;
            color: white;
            padding: 5px;
        }
        .pg_header .person_info:hover .info{
            display: block;
        }
        .pg_header .icons{
            line-height: 48px;
            padding: 0 20px 0 5px;
        }
        .pg_header .icons:hover{
            background-color: lightseagreen;
        }
        .pg_header .icons span{
            padding: 1px 5px;
            line-height: 1px;
            border-radius: 50%;
            background-color: red;
            font-size: 12px;
        }
        .pg_content .menu{
            position: absolute;
            top: 50px;
            left: 0;
            bottom: 0;
            width: 300px;
            border:0px;
            border-right: 1px solid #ccc;
        }
        .pg_content .content{
            position: absolute;
            top: 50px;
            right: 0;
            bottom: 0;
            left: 302px;
            overflow: auto;
            min-width: 980px;
            z-index: 8;
            border:0px;
            overflow: hidden;
        }
        .menu_item{
            display: block;
            padding: 10px 20px;
            border-bottom: 1px solid #ccc;
            font-size: 20px;
            color: #666666;
        }

        .menu_item:hover{
            color: white;
            background: lightseagreen;
        }
    </style>
</head>
<body>
<!-- 顶端导航栏 -->
<div class="pg_header">
    <!-- Logo与名称 -->
    <div class="logo left">
        <a href="javascript:void(0)" target="_blank">
            <img src="image/logo_1.png">
        </a>

    </div>

    <!-- 用户头像与菜单 -->
    <div class="person_info right" style="vertical-align: top;" >
        <img src="image/head.png">
        <span style="line-height: 50px;vertical-align: top;">小企鹅</span>
        <div class="info">
            <a href="javascript:void(0)">我的信息</a>
            <a href="javascript:void(0)">修改密码</a>
            <a href="javascript:void(0)">注销</a>
        </div>
    </div>
    <div class="icons right">
        <i class="far fa-bell"></i>
    </div>
</div>
<div class="pg_content">
    <!-- 左侧功能区菜单 -->
    <div class="menu">
        <a href = "/mgallery/management?method=list" class="menu_item" target="ifcontent">油画列表</a>
        <a href = "#" class="menu_item" target="ifcontent">新增作品</a>
    </div>
    <!-- 主体框架 -->
    <div class="content">
        <iframe name="ifcontent" style="width:100%;height:100%;overflow-y: hidden;border:0px;min-width: 800px;" src="/mgallery/management?method=list"></iframe>
    </div>
</div>
</body>
</html>

利用SweetAlert实现预览功能 [美观对话框]

正确使用 equals 方法避免产生空指针异常 - 简书 (jianshu.com)

list.jsp
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>油画列表</title>
    <script src="js\jquery-3.4.1.min.js" type="text/javascript"></script>
    <script src="js\sweetalert2.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="css\list.css">
    <script type="text/javascript">
        //对话框显示预览 // 变成jquary对象 对自定义属性名进行提取
        function showPreview(previewObj){
            var preview = $(previewObj).attr("data-preview");
            var pname = $(previewObj).attr("data-pname");
            Swal.fire({
                title: pname,
                html : "<img src='" + preview + "' style='width:361px;height:240px'>",
                showCloseButton: true,
                showConfirmButton: false
            })
        }
    </script>
</head>
<body>
    <div class="container">
        <fieldset>
            <legend>油画列表</legend>
            <div style="height: 40px">
                <a href="#" class="btn-button">新增</a>
            </div>
            <!-- 油画列表 -->
            <table cellspacing="0px">
                <thead>
                    <tr style="width: 150px;">
                        <th style="width: 100px">分类</th>
                        <th style="width: 150px;">名称</th>
                        <th style="width: 100px;">价格</th>
                        <th style="width: 400px">描述</th>
                        <th style="width: 100px">操作</th>
                    </tr>
                </thead>
                <c:forEach items="${pageModel.pageData }" var="painting">
                    <tr>
                        <td>
                            <c:choose>
                                <c:when test="${painting.category==1 }">现实主义</c:when>
                                <c:when test="${painting.category==2 }">抽象主义</c:when>
                                <c:otherwise>未知的类型</c:otherwise>
                            </c:choose>
                        </td>
                        <td>${painting.pname }</td>
                        <td><fmt:formatNumber pattern="¥0.00" value="${painting.price }"></fmt:formatNumber> </td>
                        <td>${painting.description }</td>
                        <td> <!-- 自定义用 data-preview data-pname    this就是指向这个超链接的本身 -->
                            <a class="oplink" data-preview="${painting.preview}" data-pname = "${painting.pname}" href="javascript:void(0)" onclick="showPreview(this)">预览</a>
                            <a class="oplink" href="#">修改</a>
                            <a class="oplink" href="#">删除</a>
                        </td>
                    </tr>
                </c:forEach>
            </table>
            <!-- 分页组件 -->
            <ul class="page">
                <li><a href="/mgallery/management?method=list&p=1">首页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上页</a></li>
                <c:forEach begin="1" end="${pageModel.totalPages }" step="1" var="pno">
                    <li ${pno==pageModel.page?"class='active'":""}>
                        <a href="/mgallery/management?method=list&p=${pno }">${pno }</a>
                    </li>
                </c:forEach>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.totalPages}">尾页</a></li>
            </ul>
        </fieldset>
    </div>

</body>
</html>

处理文件上传页面 [表单校验 文件上传 处理XML文件]

  • 利用Apache Commons FileUpload组件实现上传功能
  • 封装可重用的js脚本解决表单校验问题
  • 基于Dom4j对XML文件进行追加操作
create.jsp
<!-- 新增油画页面 -->
<%@page contentType = "text/html;charset=utf-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>新增油画</title>
<link rel="stylesheet" type="text/css" href="css\create.css">
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
    <div class="container">
        <fieldset>
            <legend>新增油画</legend>
            <form action="" method="post"
                autocomplete="off" enctype="multipart/form-data">
                <ul class="ulform">
                    <li>
                        <span>油画名称</span>
                        <span id="errPname"></span>
                        <input id="pname" name="pname" />
                    </li>
                    <li>
                        <span>油画类型</span>
                        <span id="errCategory"></span>
                        <select id="category" name="category">
                            <option value="-1">请选择油画类型</option>
                            <option value="1">现实主义</option>
                            <option value="2">抽象主义</option>
                        </select>
                    </li>
                    <li>
                        <span>油画价格</span>
                        <span id="errPrice"></span>
                        <input id="price" name="price"/>
                    </li>
                    <li>
                        <span>作品预览</span>
                        <span id="errPainting"></span>
                        <input id="painting" name="painting" type="file" 
                            style="padding-left: 0px;" accept="image/*" />
                    </li>

                    <li>
                        <span>详细描述</span>
                        <span id="errDescription"></span>
                        <textarea
                            id="description" name="description"></textarea>
                    </li>
                    <li style="text-align: center;">
                        <button type="submit" class="btn-button">提交表单</button>
                    </li>
                </ul>
            </form>
        </fieldset>
    </div>

</body>
</html>
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
}

文件上传必要前提条件

  • form表单method = “post 因为二进制文件不可以放在url中传递
  • form表单enctype = “multipart/form-data 允许保存二进制数据存放在请求体中发送到服务端
    enctype=”application/x-www-form-urlencoded 采用url编码的方式以字符串的形式将数据保存在请求体中发送到服务器
  • form表单持有file类型input进行文件选择

Apache Commons FileUpload [简化文件上传]

在java服务器端完成上传文件的处理
  • FileUpload组件提供了java文件上传底层支持

FileItem其本质就是对前台数据进行封装

ManagementController.java
package com.example.mgallery.controller;

import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Dom4j写操作开发流程

  • **SAXReader.read()**读取XML文件,得到Document对象
  • p=root,addElement(“painting”) - 创建新的节点
  • p.addElement(“pname”) - 创建新的子节点
  • document.write(writer) - 向XML写入新的节点
请求转发
请求转发是一种服务器端的行为,通过request对象来完成操作。当客户端发送请求以后,对应的Servlet会做出业务逻辑处理,然后调用forward()方法,将请求发送到服务器中另外的Servlet中去。

实现方法:
request.getRequestDispatcher("URL地址").forward(request,response);
响应重定向
响应重定向是一种客户端的行为,通过response对象来完成操作。当客户端第一次发出请求后,服务器中Servlet接收请求以后,通过调用sendRedirect()方法指定另一个Servlet,此时客户端会根据路径再次发出请求访问下一个Servlet,服务器再次接收请求后做出响应返回给客户端。

实现方法:
response.sendRedirect("URL地址")
请求转发与响应重定向的区别
1、请求转发以后浏览器URL地址栏不变,响应重定向以后浏览器URL地址栏发生改变
2、请求转发是服务器端行为,响应重定向是客户端行为
3、请求转发只做了一次访问请求,得到一次响应;响应重定向是做了两次请求,得到两次响应
4、请求转发是在服务器内部进行的,所以不可以跨域访问;响应重定向可以做到跨域访问
5、请求转发可以使用request作用域共享数据;响应重定向不可以使用request作用域,但是可以使用session域共享资源
XmlDataSource.java[新增了append追加操作]
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;

    static { //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        reload(); //在所有的写入操作以后都要写入
    }
    private static void reload(){
        //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            System.out.println(dataFile);
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            data.clear(); //清空 在空的数据基础上重新添加
            for (Node node : nodes) {
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取所有油画Painting对象
     *
     * @return Painting List
     */
    public static List<Painting> getRawData() {
        return data;
    }

    //Dom4j实现XML追加操作
    public static void append(Painting painting) { //末尾追加新的油画
        //1.读取XML文档,得到Document对象
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //2.创建新的painting节点
            Element root = document.getRootElement();//得到原始文档xml根节点 <root>
            Element p = root.addElement("painting");//创建一个新的子节点
            //3.创建painting节点的各个子节点
            p.addAttribute("id", String.valueOf(data.size() + 1)); //生成新的id对象
            p.addElement("pname").setText(painting.getPname()); //返回新的节点 设置其文本值
            p.addElement("category").setText(painting.getCategory().toString());
            p.addElement("price").setText(painting.getPrice().toString());
            p.addElement("preview").setText(painting.getPreview());
            p.addElement("description").setText(painting.getDescription());
            //4.写入XML,完成追加操作
            writer = new OutputStreamWriter(new FileOutputStream(dataFile), "UTF-8");
            document.write(writer); //向目标dataFile原始xml中进行新节点的更新
            System.out.println(dataFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) { //write有开就有关 已经被实例化
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace(); //遇到异常 打印堆栈
                }
            }
            //清空 在空的数据基础上重新添加 无论成功与否都会重新加载数据 如果照片没找到 就清空数据
            reload();
        }

    }

    public static void main(String[] args) {
//        new XmlDataSource();
//        List<Painting> ps = XmlDataSource.getRawData();
//        System.out.println(ps);
        Painting p = new Painting();
        p.setPname("油画测试");
        p.setCategory(1);
        p.setPrice(4000);
        p.setPreview("upload/10.jpg");
        p.setDescription("测试油画描述");
        XmlDataSource.append(p);
    }
}
ManagementController.java[新增了create方法]
package com.example.mgallery.controller;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            Painting painting = new Painting();//进行封装
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                    switch (fi.getFieldName()) { //得到字段名
                        case "pname":
                            painting.setPname(fi.getString("UTF-8"));
                            break;
                        case "category":
                            painting.setCategory(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "price":
                            painting.setPrice(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "description":
                            painting.setDescription(fi.getString("UTF-8"));
                            break;
                        default:
                            break;
                    }
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                    painting.setPreview("upload/" + fileName + suffix); //形成一个完整的可以访问的url地址
                }
            }
            paintingService.create(painting); //新增功能
            //若弹出另一个页面进行另外一个操作 新页面以后的后续操作 和前面的操作紧密联系的 用请求转发 当前请求給下一个功能继续操作
            resp.sendRedirect("/mgallery/management?method=list");//响应重定向跳转列表页继续相应的处理 跟前面的新增功能联系不那么紧密
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

开发表单校验JS脚本[可重用的表单校验]

  • 以独立的js脚本文件存储可被不同表单项重用的校验规则
  • 每一个校验逻辑均独立存储
  • 可自定义触发时机,例如:失去焦点、选择时、提交前…
实现思路
/*
检查是否为空 input表单域选择器    errSelector错误提示选择器    true-校验成功 false-校验失败
*/
function checkEmpty(input, errSelector){
    var val = $(input).val();
    if($.trim(val)==""){
        switchValid(false, input, errSelector, "请输入内容")
        return false;
    }else{
        switchValid(true, input, errSelector);
        return true;
    }
}
调用方法1 失去焦点的时候触发onblur
<span>详细描述</span>
<span id="errDescription"></span>
<textarea id="description" name="description" onblur="checkEmpty('#description','#errDescription')"></textarea>
调用方法2 在提交前组合校验
<script type="text/javascript">
    function checkSubmit(){ //提交前表单校验
        var result = true;
        var r1 = checkPname('#pname','#errPname',-1); //检查名称
        var r2 = checkCategory('#category','#errCategory'); //检查分类
        var r3 = checkPrice('#price','#errPrice'); //检查价格
        var r4 = checkFile('#painting', '#errPainting'); //检查上传文件
        var r5 = checkEmpty('#description', '#errDescription'); //检查描述
        if(r1 && r2 && r3 && r4 && r5){//所有检查通过允许提交表单
            return true;
        }else{
            return false;
        }
    }
</script>
validation.js 且在create.jsp中把引用的标签打上js状态 onblur/onchange
/**
 * 隐藏、显示错误信息
 * @param onOff true 验证成功, 隐藏错误     false 校验失败,显示错误
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @param message 错误信息
 * @returns
 */
function switchValid(onOff, input, errSelector, message) {
    if (onOff == false) {
        $(errSelector).text(message);
        $(input).addClass("error_input"); //错误标红
        $(errSelector).addClass("error_message");
    } else {
        $(errSelector).text("");
        $(input).removeClass("error_input"); //错误标红
        $(errSelector).removeClass("error_message");
    }
}

/**
 * 检查是否为空
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkEmpty(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    if ($.trim(val) == "") { //将字符串前后空格删掉
        switchValid(false, input, errSelector, "请输入内容"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

function checkCategory(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    if (val == -1) {
        switchValid(false, input, errSelector, "请选择油画类型"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

/**
 * 价格必须是整数
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkPrice(input, errSelector) {
    var val = $(input).val(); //获取当前选择的数值
    var regex = /^[1-9][0-9]*$/   //利用正则表达式进行校验信息
    if (!regex.test(val)) {
        switchValid(false, input, errSelector, "无效的价格"); //非空校验失败时显示错误
        return false;
    } else {
        switchValid(true, input, errSelector); //正确情况
        return true;
    }
}

/**
 * 上传文件必须是图片
 * @param input 表单域选择器
 * @param errSelector 错误提示选择器
 * @returns true-校验成功 false-校验失败
 */
function checkFile(input, errSelector) {
    if (checkEmpty(input, errSelector) == false) {
        return false;
    }
    var val = $(input).val().toLowerCase()//小写转换 PNG png
    if (val.length < 4) { //x.xxxx
        switchValid(false, input, errSelector, "请选择有效的图片"); //非空校验失败时显示错误
        return false;
    }
    suffix = val.substring(val.length - 3); //拿到最后的扩展名
    if (suffix == "jpg" || suffix == "png" || suffix == "gif") {
        switchValid(true.input, errSelector);
        return true;
    }else {
        switchValid(false, input, errSelector, "请选择有效的图片");
        return false;
    }
}

实现新增油画功能[表单submit全验证]

添加一个全局验证
create.jsp
<!-- 新增油画页面 -->
<%@page contentType="text/html;charset=utf-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>新增油画</title>
    <link rel="stylesheet" type="text/css" href="css\create.css">
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="js/validation.js"></script>
    <script type="text/javascript">
        function checkSubmit() {
            var result = true;
            var r1 = checkEmpty("#pname", "#errPname");
            var r2 = checkCategory('#category', '#errCategory');
            var r3 = checkPrice('#price', '#errPrice');
            var r4 = checkFile('#painting', '#errPainting');
            var r5 = checkEmpty('#description', '#errDescription');
            if (r1 && r2 && r3 && r4 && r5) {
                return true;
            }else {
                return false;
            }
        }
    </script>
</head>
<body>
<div class="container">
    <fieldset>
        <legend>新增油画</legend>
        <form action="/mgallery/management?method=create" method="post"
              autocomplete="off" enctype="multipart/form-data" onsubmit="return checkSubmit()">
            <ul class="ulform">
                <li>
                    <span>油画名称</span>
                    <span id="errPname"></span>
                    <input id="pname" name="pname" onblur="checkEmpty('#pname','#errPname')"> <!--在失去焦点时触发-->
                </li>
                <li>
                    <span>油画类型</span>
                    <span id="errCategory"></span>
                    <select id="category" name="category" onchange="checkCategory('#category','#errCategory')">
                        <option value="-1">请选择油画类型</option>
                        <option value="1">现实主义</option>
                        <option value="2">抽象主义</option>
                    </select>
                </li>
                <li>
                    <span>油画价格</span>
                    <span id="errPrice"></span>
                    <input id="price" name="price" onblur="checkPrice('#price','#errPrice')">
                </li>
                <li>
                    <span>作品预览</span>
                    <span id="errPainting"></span>
                    <input id="painting" name="painting" type="file"
                           style="padding-left: 0px;" accept="image/*"
                           onchange="checkFile('#painting','#errPainting')"/>
                    <%--    accept="image/*" 默认保留所有图片格式的文件--%>
                </li>

                <li>
                    <span>详细描述</span>
                    <span id="errDescription"></span>
                    <textarea
                            id="description" name="description"
                            onblur="checkEmpty('#description','#errDescription')"></textarea>
                </li>
                <li style="text-align: center;">
                    <button type="submit" class="btn-button">提交表单</button>
                </li>
            </ul>
        </form>
    </fieldset>
</div>

</body>
</html>
management.html  和  list.jsp 添加href超链接 
客户点击新增的时候可以跳转到新增页面 
<a href = "/mgallery/management?method=show_create"></a>

实现修改页表单数据回填

修改实现思路
  • 修改与新增的最大不同是在修改前要加载原有数据
  • 在修改页面放置hidden隐藏域保存id编号,随表单提交
  • 对XML更新时,先按id获取原始记录,在此基础上覆盖更新

Dao service

PaintingService.java
package com.example.mgallery.service;

import com.example.mgallery.dao.PaintingDao;
import com.example.mgallery.entity.Painting;
import com.example.mgallery.utils.PageModel;

import java.util.List;
//完成业务逻辑 service与dao进行传递调用
public class PaintingService {
    private PaintingDao paintingDao = new PaintingDao();
    public PageModel pagination(int page, int rows, String...category){  //最后一个是添加 可选参数(可能/不一定出现一个或多个)
        if (rows == 0){
            throw new RuntimeException("无效的rows参数");
        }
        if (category.length==0 || category[0] == null){
        return paintingDao.pagination(page, rows); //调用并返回
        }else { //程序进行可选调用 两个不同路径 尽量不要去修改类结构
            return paintingDao.pagination(Integer.parseInt(category[0]), page, rows);
        }
    }

    public void create(Painting painting){
        paintingDao.create(painting);
    }
    // 按编号查询油画 id油画编号 return油画对象
    public Painting findById(Integer id){
        Painting p = paintingDao.findById(id);
        if (p==null){
            throw new RuntimeException("[id=]" + id + "]油画不存在");
        }
        return p;
    }

    public static void main(String[] args) {
        PaintingService paintingService = new PaintingService();
        PageModel pageModel = paintingService.pagination(2, 6);//每页显示六条
        List<Painting> paintingList = pageModel.getPageData();
        for (Painting painting : paintingList){
            System.out.println(painting.getPname());
        }
        System.out.println(pageModel.getPageStartRow() + ":" + pageModel.getPageEndRow());

    }
}
update.jsp
<%@page contentType="text/html;charset=utf-8" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!-- 修改油画页面 -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>作品更新</title>
    <link rel="stylesheet" type="text/css" href="css\create.css">
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="js/validation.js"></script>
    <script type="text/javascript">
        <!-- 提交前表单校验 -->
        function checkSubmit() {
            var result = true;
            var r1 = checkEmpty("#pname", "#errPname");
            var r2 = checkCategory('#category', '#errCategory');
            var r3 = checkPrice('#price', '#errPrice');
            var r4 = checkFile('#painting', '#errPainting');
            var r5 = checkEmpty('#description', '#errDescription');
            if (r1 && r2 && r3 && r4 && r5) {
                return true;
            } else {
                return false;
            }
        }
        //整个html被解析完后执行代码
        $(function(){//前面的被html解释完才执行 后面的EL表达式jsp渲染在服务器端产生 油画类型默认产生
            $("#category").val(${painting.category})
        })
    </script>
</head>
<body>
<div class="container">
    <fieldset>
        <legend>作品名称</legend>
        <form action="[这里写更新URL]" method="post"
              autocomplete="off" enctype="multipart/form-data"
              onsubmit="return checkSubmit()">
            <ul class="ulform">
                <li>
                    <span>油画名称</span>
                    <span id="errPname"></span>
                    <input id="pname" name="pname" onblur="checkEmpty('#pname','#errPname')" value="${painting.pname}"/>
                </li>
                <li>
                    <span>油画类型</span>
                    <span id="errCategory"></span>
                    <select id="category" name="category" onchange="checkCategory('#category','#errCategory')"
                            value="${painting.category}">
                        <option value="-1">请选择油画类型</option>
                        <option value="1">现实主义</option>
                        <option value="2">抽象主义</option>
                    </select>
                </li>
                <li>
                    <span>油画价格</span>
                    <span id="errPrice"></span>
                    <input id="price" name="price" onblur="checkPrice('#price','#errPrice')" value="${painting.price}"/>
                    </li>
                    <li>
                        <span>作品预览</span>
                        <span id=" errPainting"></span><br/>
                    <img id="preview" src="${painting.preview}" style="width:361px;height:240px"/><br/>
                    <input id="painting" name="painting" type="file" style="padding-left:0px;" accept="image/*"/>
                </li>
                <li>
                    <span>详细描述</span>
                    <span id="errDescription"></span>
                    <textarea
                            id="description" name="description"
                            onblur="checkEmpty('#description','#errDescription')"
                    >
                        ${painting.description}
                    </textarea>
                </li>
                <li style="text-align: center;">
                    <button type="submit" class="btn-button">提交表单</button>
                </li>
            </ul>
        </form>
    </fieldset>
</div>

</body>
</html>
ManagmentController.java
package com.example.mgallery.controller;

import com.example.mgallery.entity.Painting;
import com.example.mgallery.service.PaintingService;
import com.example.mgallery.utils.PageModel;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

//实现增删改查
@WebServlet("/management")
public class ManagementController extends HttpServlet {
    private PaintingService paintingService = new PaintingService();

    public ManagementController() {
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=utf-8");
        //通过method参数区分不同的操作
        String method = req.getParameter("method");
        if(Objects.equals(method,"list")) {//分页查询列表
            this.list(req,resp);
            //正确使用 equals 方法避免产生空指针异常https://www.jianshu.com/p/306de20dd228
        } else if (Objects.equals(method,"delete")) {
            //
        } else if (Objects.equals(method,"show_create")) {
            this.showCreatePage(req,resp); //带show的一定是跳转页面
        } else if (Objects.equals(method, "create")) {
            this.create(req, resp);
        } else if (Objects.equals(method,"show_update")) {
            this.showUpdatePage(req, resp);
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req,resp);
    }

    // 控制器代码的实现
    private void list(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String p = req.getParameter("p");
        String r = req.getParameter("r");
        if (p == null) {
            p = "1";
        }
        if (r == null) {
            r = "6";
        }
        //2.调用Service方法,得到处理结果      增加了按类型筛选category
        PageModel pageModel = paintingService.pagination(Integer.parseInt(p), Integer.parseInt(r));
        req.setAttribute("pageModel",pageModel);//数据解耦最关键的一步
        //3.请求转发至对应JSP(view)进行数据展现
        req.getRequestDispatcher("/WEB-INF/jsp/list.jsp").forward(req,resp); //跳转jsp
    }
    //显示新增页面
    private void showCreatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); //请求转发
    }
    //新增油画方法
    private void create(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*文件上传时的数据处理与标准表单完全不同
        String pname = req.getParameter("pname"); form表单enctype = "multipart/form-data"运用后 无法得到字符串格式的数据
        System.out.println(pname);
        req.getRequestDispatcher("/WEB-INF/jsp/create.jsp").forward(req, resp); 请求转发*/
        //1.初始化FileUpload组件 包含表单项的数据对象
        FileItemFactory factory = new DiskFileItemFactory();
        /**
         * FileItemFactory 用于将前端表单的数据转换为一个个FileItem对象
         * ServletFileUpload 是为FileUpload组件提供Java web的Http请求解析
         */
        ServletFileUpload sf = new ServletFileUpload(factory);
        //2.遍历所有FileItem
        try {
            List<FileItem> formData = sf.parseRequest(req);//将表单数据转换为FileItem对象 和前台输入项一一对应
            Painting painting = new Painting();//进行封装
            //区分哪个是普通对象 哪个是文件对象
            for (FileItem fi : formData){
                if (fi.isFormField() == true){//普通输入框
                    System.out.println("普通输入项:" + fi.getFieldName() + ":" + fi.getString("UTF-8"));
                    switch (fi.getFieldName()) { //得到字段名
                        case "pname":
                            painting.setPname(fi.getString("UTF-8"));
                            break;
                        case "category":
                            painting.setCategory(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "price":
                            painting.setPrice(Integer.parseInt(fi.getString("UTF-8")));
                            break;
                        case "description":
                            painting.setDescription(fi.getString("UTF-8"));
                            break;
                        default:
                            break;
                    }
                }else { //文件上传框
                    System.out.println("文件上传项:" + fi.getFieldName()); //没有文本数值不用输出
                    //3.文件保存到服务器目录 已经确定了文件上传项
                    String path = req.getServletContext().getRealPath("/upload");
                    System.out.println("上传文件目录:" + path);
//                    String fileName = "test.jpg";
                    String fileName = UUID.randomUUID().toString();//随机生成文件名 根据计算机的特性 生成随机唯一字符串
                    //fi.getName()得到原始文件名, 截取最后一个"."后所有字符串,例如:wxml.jpg -> .jpg
                    String suffix = fi.getName().substring(fi.getName().lastIndexOf("."));
                    fi.write(new File(path, fileName + suffix)); //传入文件对象会自动的帮我们把客户端上传的文件传到服务器某个文件中
                    painting.setPreview("upload/" + fileName + suffix); //形成一个完整的可以访问的url地址
                }
            }
            paintingService.create(painting); //新增功能
            //若弹出另一个页面进行另外一个操作 新页面以后的后续操作 和前面的操作紧密联系的 用请求转发 当前请求給下一个功能继续操作
            resp.sendRedirect("/mgallery/management?method=list");//响应重定向跳转列表页继续相应的处理 跟前面的新增功能联系不那么紧密
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //显示更新页面
    private void showUpdatePage(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String id = req.getParameter("id");//前台传来的id号
        Painting painting = paintingService.findById(Integer.parseInt(id));
        req.setAttribute("painting", painting); //将得到的放入其中
        req.getRequestDispatcher("/WEB-INF/jsp/update.jsp").forward(req,resp);
    }
}
list.jsp
<ul class="page">
                <li><a href="/mgallery/management?method=list&p=1">首页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasPreviousPage?pageModel.page-1:1}">上页</a></li>
                <c:forEach begin="1" end="${pageModel.totalPages }" step="1" var="pno">
                    <li ${pno==pageModel.page?"class='active'":""}>
                        <a href="/mgallery/management?method=list&p=${pno }">${pno }</a>
                    </li>
                </c:forEach>
                <li><a href="/mgallery/management?method=list&p=${pageModel.hasNextPage?pageModel.page+1:pageModel.totalPages}">下页</a></li>
                <li><a href="/mgallery/management?method=list&p=${pageModel.totalPages}">尾页</a></li>
            </ul>

利用Dom4j对XML进行更新

XmlDataSource.java
package com.example.mgallery.utils;

import com.example.mgallery.entity.Painting;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;

//用于将XML文件解析为Java对象
public class XmlDataSource {
    //通过static静态关键字保证数据全局唯一
    private static List data = new ArrayList(); //油画集合
    private static String dataFile;

    static { //程序运行以后去得到类路径目录下的/painting.xml的路径地址
        dataFile = XmlDataSource.class.getResource("/painting.xml").getPath();
        reload(); //在所有的写入操作以后都要写入
    }
    private static void reload(){
        //若得到特殊字符进行编码转换 空格 c:\new style\painting.xml
        try {
            URLDecoder.decode(dataFile, "UTF-8");
            System.out.println(dataFile);
            //利用Dom4j对XML进行解析 读取XML
            SAXReader reader = new SAXReader();
            //1.获取Document文档对象
            Document document = reader.read(dataFile);
            //2.Xpath得到XML节点集合 获取多个xml节点
            List<Node> nodes = document.selectNodes("/root/painting");
            data.clear(); //清空 在空的数据基础上重新添加
            for (Node node : nodes) {
                Element element = (Element) node;
                String id = element.attributeValue("id");//获得id
                String pname = element.elementText("pname");//获得子节点
                Painting painting = new Painting();
                painting.setId(Integer.parseInt(id));
                painting.setPname(pname);
                painting.setCategory(Integer.parseInt(element.elementText("category")));
                painting.setPrice(Integer.parseInt(element.elementText("price")));
                painting.setPreview(element.elementText("preview"));
                painting.setDescription(element.elementText("description"));
                data.add(painting);//将List data 保存油画的完整信息

                System.out.println(id + ";" + pname);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取所有油画Painting对象
     *
     * @return Painting List
     */
    public static List<Painting> getRawData() {
        return data;
    }

    //Dom4j实现XML追加操作
    public static void append(Painting painting) { //末尾追加新的油画
        //1.读取XML文档,得到Document对象
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //2.创建新的painting节点
            Element root = document.getRootElement();//得到原始文档xml根节点 <root>
            Element p = root.addElement("painting");//创建一个新的子节点
            //3.创建painting节点的各个子节点
            p.addAttribute("id", String.valueOf(data.size() + 1)); //生成新的id对象
            p.addElement("pname").setText(painting.getPname()); //返回新的节点 设置其文本值
            p.addElement("category").setText(painting.getCategory().toString());
            p.addElement("price").setText(painting.getPrice().toString());
            p.addElement("preview").setText(painting.getPreview());
            p.addElement("description").setText(painting.getDescription());
            //4.写入XML,完成追加操作
            writer = new OutputStreamWriter(new FileOutputStream(dataFile), "UTF-8");
            document.write(writer); //向目标dataFile原始xml中进行新节点的更新
            System.out.println(dataFile);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) { //write有开就有关 已经被实例化
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace(); //遇到异常 打印堆栈
                }
            }
            //清空 在空的数据基础上重新添加 无论成功与否都会重新加载数据 如果照片没找到 就清空数据
            reload();
        }

    }

    public static void main(String[] args) {
//        new XmlDataSource();
//        List<Painting> ps = XmlDataSource.getRawData();
//        System.out.println(ps);
        Painting p = new Painting();
        p.setPname("油画测试");
        p.setCategory(1);
        p.setPrice(4000);
        p.setPreview("upload/10.jpg");
        p.setDescription("测试油画描述");
        XmlDataSource.append(p);
    }

    /**
     * 更新对应id的XML油画数据
     * @param painting 要更新的油画数据
     * @throws IOException
     */
    public static void update(Painting painting){
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            //节点路径[@属性名=属性值]
            // /root/paintin[@id=x]  根节点
            List<Node> nodes = document.selectNodes("/root/painting[@id=" + painting.getId() + "]");
            if (nodes.size() == 0){
                throw new RuntimeException("id=" + painting.getId() + "编号油画不存在");
            }
            Element p = (Element) nodes.get(0); //唯一的节点提取出来
            p.selectSingleNode("pname").setText(painting.getPname()); //得到指定标签名的唯一节点 指定油画更新id操作
            p.selectSingleNode("category").setText(painting.getCategory().toString());
            p.selectSingleNode("price").setText(painting.getPrice().toString());
            p.selectSingleNode("preview").setText(painting.getPreview());
            p.selectSingleNode("description").setText(painting.getDescription());
            writer = new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");
            document.write(writer);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            reload(); //对原始集合进行重载更新
        }
    }

    /**
     * 按id号删除XML油画数据
     * @param id 油画id
     * @throws IOException
     */

    public static void delete(Integer id) {
        SAXReader reader = new SAXReader();
        Writer writer = null;
        try {
            Document document = reader.read(dataFile);
            List<Node> nodes = document.selectNodes("/root/painting[@id=" + id + "]");
            if(nodes.size() == 0) {
                throw new RuntimeException("id=" + id + "编号油画不存在");
            }
            Element p = (Element)nodes.get(0);
            p.getParent().remove(p);
            writer = new OutputStreamWriter(new FileOutputStream(dataFile),"UTF-8");
            document.write(writer);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            if(writer!=null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            reload();
        }
    }
}


项目总结

CRUD增删改查

工程结构
mgallery - eclipse工程项目
 /src - java源代码目录
 /WebContent - Web资源目录
 /css - css文件目录
 /js - js文件目录
 /image - 图片资源目录
 /upload - 上传文件目录
 /WEB-INF   //jsp数据来自controller 不允许在web中直接访问 要从控制器跳转
   /jsp - jsp页面目录
   /lib - jar文件目录
   /classes - 编译后的class目录
   /web.xml web描述符文件
包结构[src目录下 根据MVC进行结构划分]
com.imooc.mgallery //逆命名法
    /controller - 存放Servlet控制器类 //承上启下接收参数 调用逻辑 返回处理结果
    /service - 存放处理逻辑类model //完成业务逻辑 service与dao进行传递调用
    /dao - Data Access Object 数据访问对象类 数据读写的java类 数据来自xml文件
    /entity - 存放实体类 JavaBean java中的简单对象
    /utils - 通用工具类 底层通用的工具类或方法

Dao类[Data Access Object]

  • XxxDao类只负责对数据进行读取、写入操作
  • 只负责对数据 增、删、改、查
示例:PaintingDao //针对油画数据进行增删改查
public class PaintingDao{
    public void append(){...} //新增数据
    public void update(){...} //修改数据
    public void delete(){...} //删除数据
    public void findAll(){...} //查询数据
}

Service与Dao的关系

  • Service负责进行流程处理,需**持久化[java处理在内存中 存储在数据库防止丢失]**时调用Dao
  • Dao只负责单纯对数据进行增删改查操作
  • Service允许单向调用Dao,反向不允许

JavaBean

  • 对一种类的使用形式的统称

  • JavaBean是一种Java中可重用组件

  • JavaBean不是技术,而是一种Java类的格式要求

  • JavaBean在Java领域非常常见,通常用于存储数据

JavaBean格式要求

  • 类必须是pubilc并提供默认构造函数
  • 所有属性private私有化
  • 属性通过getter与setter方法进行读写
public class Painting{//类公有化
    public Painting(){...}; //提供默认构造函数,可不写
    private Integer id; //属性私有
    private String pname;
    public Integer getId(){return id;} //getter获取属性值
    public void setId(Integer id){this.id = id;} //setter设置属性值
    public String getPname(){return pname;}
    public void setPname(String pname){this.pname = pname;}
}

创建mgallery工程 实现思路

  • 开发PaintingDao读取XML数据,实现分页操作
  • 开发PaintingService服务类,对PaintingDao进行调用
  • 开发PaintingController控制器,调用PaintingService
  • 重写index.html,利用JSP技术读取分页数据

关键类与方法[对于前台来说]

  • XmlDataSource类[全局有且只有一份数据保存在内存中] - XML数据源工具类,简化Dao提取操作
  • PaintingDao.pagination() - 数据分页查询方法
  • PageModel类 - 分页结果的包装类 {分页处理的核心对象}

Dom4j

  • Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台
  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象

Dom4j开发流程回顾

  • SAXReader.read()读取XML文件,得到Document对象
  • document.selectNodes()利用Xpath得到XML节点集合
  • 遍历XML节点,包装成JavaBean或者集合对象返回

开发XmlDataSource (utils)

油画数据分页设计思路

​ [视图 控制器 模型]

浏览器会发送请求查询指定的数据 PaintingController得到分页信息以后 再调用PaintingService 再调用PaintingDao 首先从PaintingDao调用XmlDataSource得到xml所有数据集合传入到PageModel进行分页 再返回PaintingDao-PaintingService-PaintingController放在当前的请求中,将请求跳转至index.jsp对数据进行渲染返回浏览器。

前台系统与后台系统区别

前台系统 后台系统
开放度 对外开放 不对外开放
面向群体 客户 企业内部人员
功能 提供查询与交互 管理前台数据
设计侧重点 良好的用户体验 严密的业务逻辑
访问量
典型案例 猫眼电影网 [后台有评论审核系统] XX公司无纸质化办公OA系统
后台实现功能

油画列表 油画预览 删除油画 修改油画 新增与上传油画

SweetAlert对话框

替代了传统的alert函数 有精心设计的对话框

新增功能实现难点

表单校验、文件上传、处理XML文件

文件上传:Apache Commons FileUpload

文件上传必要前提条件

文件上传必要前提条件

  • form表单method = “post 因为二进制文件不可以放在url中传递
  • form表单enctype = “multipart/form-data 允许保存二进制数据存放在请求体中发送到服务端
    enctype=”application/x-www-form-urlencoded 采用url编码的方式以字符串的形式将数据保存在请求体中发送到服务器
  • form表单持有file类型input进行文件选择

//1.初始化FileUpload组件 包含表单项的数据对象 每个输入项解析成FileItem
//2.ServletFileUpload* *是为FileUpload组件提供Java webHttp请求解析 将factory传入
//3.遍历所有FileItem 判断各种框 isFormField(true/false)来走是普通框还是文件框 文件框先把文件保存到服务器 UUID使文件名不重复

可重用的表单js

封装成JavaScript函数 在validation.js中呈现 可以自定义触发条件 别忘了还有个全局触发条件function写上面

实现新增油画功能

在xml中执行写入操作 利用Dom4j写操作开发
  • SAXReader.read()读取XML文件,得到Document对象
  • p=root.addElement(“painting”) - 创建新的节点
  • p.addElement(“pname”) - 创建新的子节点
  • document.write(write) - 向XML写入新的节点

实现修改与删除功能

所有更新逻辑都是在原始数据基础上覆盖更新,通过id号得到原始的旧数据,对旧的对象进行update

客户端采用Ajax方式提交Http请求
Controller方法处理后不再跳转任何jsp,而是通过响应输出JSON格式字符串
Tips:作为Ajax与服务器交互后,得到的不是整页HTML,而是服务器处理后的数据

阅读全文

JSON,JQuery,Ajax,Freemarker,百度Echarts,正则表达,过滤器,监听与实践

2023/9/20

JSON (JavaScript Object Notation) [JavaScript对象表示法]

  • 掌握JSON语法的书写规则
  • 掌握JSON与JavaScript的交互技巧
  • 掌握JSON与Java之间的序列化与反序列化

JSON是轻量级的文本数据交换格式,独立于语言,具有自我描述性,更易理解,已经逐渐替代了XML

{
    "sites":[
        {"name":"慕课网","url":"www.imooc.com"},
        {"name":"百度","url":"www.baidu.com"}
    ]
}

JSON语法规则

  • 数据由键(key)/值(value)描述,由逗号分隔
  • 大括号代表一个完整的对象,拥有多个键/值对
  • 中括号保存数组,多个对象之间使用逗号分割

所有的key和value都要用双引号进行标注

JSON存储员工信息

[
  {
    "empno": 7369,
    "ename": "李宁",
    "job": "软件工程师",
    "hiredate": "2017-05-12",
    "salary": 13000,
    "dname": "研发部"
  },
  {
    "empno": 8848,
    "ename": "小明",
    "job": "销售总监",
    "hiredate": "2022-4-23",
    "salary": 8000,
    "dname": "人事部",
    "customers": [
      {
        "cname": "李东"
      },
      {
        "cname": "刘楠"
      }
    ]
  }
]
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
        var json = [
            {
                "empno": 7369,
                "ename": "李宁",
                "job": "软件工程师",
                "hiredate": "2017-05-12",
                "salary": 13000,
                "dname": "研发部"
            },
            {
                "empno": 8848,
                "ename": "小明",
                "job": "销售总监",
                "hiredate": "2022-4-23",
                "salary": 8000,
                "dname": "人事部",
                "customers": [
                    {
                        "cname": "李东"
                    },
                    {
                        "cname": "刘楠"
                    }
                ]
            }
        ];
        //在浏览器控制台中对json内容进行输出
        console.log(json);
        for (var i = 0; i < json.length; i++){
            var emp = json[i];
            document.write("<h1>");
            document.write(emp.empno);
            document.write("," + emp.ename);
            document.write("," + emp.job);
            document.write("," + emp.hiredate);
            document.write("," + emp.salary);
            document.write("," + emp.dname);
            document.write("</h1>");
            if (emp.customers != null){
                document.write("<h2>"+emp.ename+"的顾客是")
                for (var j = 0; j < emp.customers.length; j++){
                    var customer = emp.customers[j];
                    document.write(customer.cname + ",");
                }
                document.write("</h2>");
            }
        }
    </script>
</head>
<body>

</body>
</html>

JSON与字符串相互转换

  • JSON.parse()方法字符串转换为JSON对象
  • JSON.stringify()方法JSON对象转换为字符串
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
       var str = "{\"class_name\" : \"五年fdhgfs级三班\"}"; <!--字符串 斜杠进行原意的输出-->
       var json = JSON.parse(str);
       console.log(str);
       console.log(json);
       document.write("班级:" + json.class_name);
    </script>
</head>
<body>

</body>
</html>

JS中JSON转为字符串

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript">
       var json1 = "{\"class_name\" : \"五年级三班\"}"; <!--字符串 斜杠进行原意的输出-->
       var str1 = JSON.stringify(json1);
       console.log(json1);
       console.log(str1);
       var json2 = {};
       json2.class_name = "五年级五班";
       json2.floor = "团委楼四层";
       json2.teacher = "王义夫"
       console.info(json2);
    </script>
</head>
<body>

</body>
</html>

JSON与Java交互

JSON工具包:json与java之间的互相转换

  • FastJson是阿里巴巴著名的JSON序列化与反序列化工具包
  • FastJson国内拥有大量使用者,拥有API简单,效率高等优点
https://repo1.maven.org/maven2/com/alibaba/fastjson/ 下载fastjson
或者配置maven依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>x.x.x</version>
</dependency>

FastJson序列化与JSON注解

public class Employee{
    private Integer empno;
    private String ename;
    private String job;
    //日期格式化
    @JSONField(name="hiredate", format="yyyy-MM-dd HH:mm:ss")
    private Date hdate;
    private Float salary;
    @JSONField(serialize = false) //不对dname进行序列化
    private String dname;
    ......
}
public class FastJsonSample1{
    public static void main(String[] args){
        Employee employee = new Employee();
        employee.setEmpno(4488);
        employee.setEname("王晓东");
        employee.setJob("客户经理");
        employee.setSalary(1000f);
        employee.setDname("市场部");
        //用日期来对时间进行初始化
        --------------//对应上方日期格式化@JSONField-------------------
        Calendar c = Calendar.getInstance();
        c.set(2019,0,30,0,0,0);
        employee.setHdate(c.getTime());//获取所需要的日期对象
        -------------------------------------------------------------
        //FastJSON中提供了JSON对象,完成对象对JSON字符串的转换
        String json = JSON.toJSONString(employee);
        System.out.println(json);
        //                            ↓↓ 原始字符串 反序列化类 ↓↓
        Employee emp = JSON.parseObject(json, Employee.class); //转换为何种对象
        //JSNO.parse()方法将字符串转换为JSON对象
        System.out.println(emp.getEname());
    }
}

FastJSON对象数组序列化与反序列化

JSON序列化与反序列化的用途:数据传输和数据保存 [A电脑中有100个员工信息,通过JSON把数据变成字符串发送給另一个系统,再进行转换]

public class FastJsonSample2{
    public static void main(String[] args){
        List emplist = new ArrayList();
        for(int i = 1; i <= 100; i++){
            Employee employee = new Employee();
            employee.setEmpno(4488 + i);
            employee.setEname("员工" + i);
            emplist.add(employee);
        }
        String json = JSON.toJSONString(emplist);
        System.out.println(json);
        List<Employee> emps = JSON.parseArray(json, Employee.class);
        for(Employee e : emps){
            System.out.println(e.getEmpno() + ":" + e.getEname());
        }
    }
}

JSON教程 https://www.cainiaojc.com/json/json_objects.html
JSON用于与Web服务器交换数据。数据发送到Web服务器时,数据必须是字符串

JSON格式几乎与JavaScript对象相同 
在JSON中,键必须是字符串,并用双引号引起来 {"name":"Seagull"}
在JavaScript中,键可以是字符串数字或标识符 {name : "Seagull"} 
JSON字符串类型 {"name":"Seagull"}
JSON数字类型 {"age":22}
JSON布尔类型 {"isAdult":true}
JSON对象类型 {"user":{"name:""Seagull","age":22,"city":"New Delhi"}} 
JSON数组类型 {"user":["Seagull","Cynthia","Tarush"]} 
//来自服务器的JSON
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = function() {
   if (this.readyState = 4 && this.status = 200) {
      var myObj = JSON.parse(this.responseText);
      document.getElementById("output").innerHTML = myObj.name;
   }
};
httpRequest.open("GET", "demo.json", true);
httpRequest.send();

**JSON.parse()**方法解析JSON字符串来构造JavaScript值或该字符串描述
**JSON.stringify()**方法将JavaScript对象或值转换为JSON字符串

解析日期parse()
<!DOCTYPE html>
<html>
<title>JSON.parse() 日期与json转换示例 - 基础教程(cainiaojc.com)</title>
<body>
<p>如果需要包含日期,则将其写为字符串,然后稍后将其转换回日期对象:</p>
<p id="output"></p>
<script>
var myJSON = '{"name":"Seagull", "birth":"1997-11-10", "city":"New Delhi"}';
var myObj = JSON.parse(myJSON);
myObj.birth = new Date(myObj.birth);
document.getElementById("output").innerHTML = myObj.name + " DOB is, " + myObj.birth;
</script>
</body>
</html> 
/*如果需要包含日期,则将其写为字符串,然后稍后将其转换回日期对象:
Seagull DOB is, Mon Nov 10 1997 08:00:00 GMT+0800 (中国标准时间)*/ 
解析日期stringify()
<!DOCTYPE html>
<html>
<title>JSON.stringify() 将日期对象转换为字符串示例 - 基础教程(cainiaojc.com)</title>
<body>
<p> JSON.stringify()方法会将任何日期对象转换为字符串:</p>
<p id="output"></p>
<script>
var myObj = { name: "Seagull", today: new Date(), city : "New Delhi" };
var myJSON = JSON.stringify(myObj);
document.getElementById("output").innerHTML = myJSON;
</script>
</body>
</html>
/*JSON.stringify()方法会将任何日期对象转换为字符串:
{"name":"Seagull","today":"2023-10-19T09:21:17.353Z","city":"New Delhi"*/

Java中的this.对象不会被函数内部修改 去寻找定义的值
this可以去调到外面的东西 解决了变量名一样的冲突
this区分成员变量和局部变量
this调用成员方法

Java中的static是公共的变量池


jQuery[主流的JavaScript库]与Ajax

  • 了解jQuery3的基本使用方法
  • 掌握Ajax处理流程与实现流程
  • 掌握jQuery中Ajax方法的使用

jQuery是一个轻量级JS库,使用十分简单;jQuery的核心是选择器用于获取页面元素

jQuery选择器 [先引用]

//引用jQuery
<script type="text/javascript">
    $("span").css("color","red");
    //简化形式 中间加JSON表达式
    $("a").css({"color" : "red", "font-size" : "30px", "font-weight" : "bold", "font-style" : "italic"})
    //为选中的选择器增添类效果 为li增加两个css类
    $("li").addClass("highlight myclass");
    $("p").removeClass("myclass") //移除类
    var color = $("a").css("color","orange");
    // alert(color)



### 设置元素内容

- **val()** 获取或设置输入项的值
- **text()** 获取或设置元素的纯文本
- **html()** 获取或设置元素内部的HTML

```html
.. <input type = "text" name = "uname" value = "admin"/> ..//将初始uname值变admin..
.. <span class = "myclass">我是myclass类的span标签</span> ..
<script type="text/javascript" src="js/jquery-3.1.1.js" ></script>
<script type = "text/javascript">
    //获取或设置文本输入框的内容
    $("input[name='uname']").val("administrator");
    
    //text与html方法最大的区别在于对文本中的html标签是否进行转义
    var vspan = $("span.myclass").text("<b>锄禾日当午</b>");
    alert(vspan);//锄禾日当午 纯文本中带了<b>标签
    
    var vspan = $("span.myclass").html("<b>锄禾日当午</b>");  
    alert(vspan);//<b>锄禾日当午</b>
</script>

jquery事件处理方法

  • on(“click”, function) 为选中的页面元素绑定单机事件
  • click(function) 是绑定事件的简写形式
  • 处理方法中提供了event参数包含了事件的相关信息
鼠标事件 键盘事件 表单事件 文档/窗口事件
click keypress
(键盘完整按下弹起)
submit
(表单提交)
load
(文档加载)
dblclick(双击) keydown
(键盘按下)
change
(表单发生变化)
resize
(文档窗口产生变化)
mouseenter
(滑鼠移动)
keyup
(键盘弹起)
focus
(表单输入项获得焦点)
scroll
(文档窗口滚动变化)
mouseleave
(滑鼠移出)
blur
(表单输入项失去焦点)
unload
(文档窗口关闭/卸载)
mouseover
(滑鼠移动过程)
.. <p class = "myclass">我是拥有myclass的p标签</p> ..
.. <input type = "text" name = "uname" value = "admin"/> ..
<script type = "text/javascript" src = "js/jquery-3.3.1.js"></script>
<script type = "text/javascript">
    $("p.myclass").on("click", function(){
    //$(this)是指当前事件产生的对象$("p.myclass")
    $(this).css("background-color", "yellow");
})
    $("span.myclass").click(function(){
    $(this).css("background-color", "lightgreen");                      
}) 
    
    $("input[name='uname']").keypress(function(event){
        console.log(event); //打印事件 F12 每次输入按键的Console
        $(this).css("color", "red");
    })
    
</script>
----------------------------------------------------------------
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn,staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
 $("p").click(function()){
    <!--动作触发后执行的代码-->
    #(this).hide();
});
});
</script>
    </head>
    <body>
        <p>
            如果你点我,我就会消失。
        </p>
        <p>
            点我消失!
        </p>
    </body>

jQuery hide()和show()

使用hide()和show()方法来隐藏和显示HTML元素

$("#hide").click(function(){
    $("p").hide();
});
$("#show").click(function(){
    $("p").show();
});
隐藏速度

$(selector).hide(speed,callback);

$(selector).show(speed,callback);

<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<script src="https://cdn,staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
$(document).ready(function(){
$("button").click(function(){
    $("p").hide(1000);
 });
});
</script>
    </head>
    <body>
      <button>隐藏</button>
      <p> 这是个段落</p>
      <p> 小段落</p>
    </body>

jQuery选择器实验室

sample2.html
<!DOCTYPE html >
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery实验室</title>

<style>
.myclass {
    font-style: italic;
    color: darkblue;
}
/* 高亮css类 */
.highlight {
    color: red;
    font-size: 30px;
    background: lightblue;
}
</style>

</head>

<body>
    <div class="section">
        <h2>jQuery选择器实验室</h2>
        <input style="height: 24px" id="txtSelector" />
        <button id="btnSelect" style="height: 30px">选择</button>
        <hr />
        <div>
            <p id="welcome">欢迎来到选择器实验室</p>
            <ul>
                <li>搜索引擎:<a href="http://www.baidu.com">百度</a> <span> <a
                        style="color: darkgreen" href="http://www.so.com">360</a>
                </span>
                </li>
                <li>电子邮箱:<a href="http://mail.163.com">网易邮箱</a> <span> <a
                        style="color: darkgreen" href="http://mail.qq.com">QQ邮箱</a>
                </span>
                </li>
                <li>中国名校:<a href="http://www.tsinghua.edu.cn">清华大学</a> <span>
                        <a style="color: darkgreen" href="https://www.pku.edu.cn/">北京大学</a>
                </span>
                </li>
            </ul>

            <span class="myclass ">我是拥有myclass类的span标签</span>

            <p class="myclass">我是拥有myclass的p标签</p>
            <form id="info" action="#" method="get">
                <div>
                    用户名:<input type="text" name="uname" value="admin" /> 密码:<input
                        type="password" name="upsd" value="123456" />
                </div>
                <div>
                    婚姻状况: <select id="marital_status">
                        <option value="1">未婚</option>
                        <option value="2">已婚</option>
                        <option value="3">离异</option>
                        <option value="4">丧偶</option>
                    </select>
                </div>
                <div class="left clear-left">
                    <input type="submit" value="提交" /> <input type="reset" value="重置" />
                </div>
            </form>
        </div>
    </div>
    <script type="text/javascript" src="js/jquery-3.1.1.js" ></script>
    <script type="text/javascript">
        /*
            id选择器使用"#id值"进行选择
            css选择器使用".css类名"进行选择
            $(".myclass").addClass("highlight");
        */
        document.getElementById("btnSelect").onclick = function(){
            var selector = document.getElementById("txtSelector").value;
            //jquery选择器方法 选择器表达式
            $("*").removeClass("highlight") //在增加高亮之前 在当前页面将所有的类移除
            $(selector).addClass("highlight"); //对前面所选中的元素追加css类
        }
    </script>
</body>
</html>

Ajax(Asynchronous JavaScript And XML)介绍

Ajax(异步的 JavaScriptXML)

  • Ajax可以**在不刷新页面的前提下,进行页面布局更新,与后台交互**
    [不对整个页面刷新 只对局部数据更新刷新]
  • Ajax不是新技术,不是W3C的标准

Ajax的使用流程

  • 创建XmlHttpRequest对象[用于后台与服务器交换数据 是Ajax的核心]
  • 发送Ajax请求
  • 处理服务器响应
创建XmlHttpRequest对象
// 1.创建XmlHttpRequest对象
     var xmlhttp;
      if(windows.XMLHttpRequest){
       xmlhttp = new XMLHttpRequest();
    }else{
       xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
发送Ajax请求
  • xmlhttp.open() 用于创建请求
//创建请求
xmlhttp.open("Get","http://localhost/test?name=admin",true);
//发送到服务器
xmlhttp.send();
处理服务器响应
  • xmlhttp.onreadystatechange() 事件用于监听Ajax的执行过程
  • xmlhttp.readyState = “number 属性说明XMLHttpRequest当前状态
  • xmlhttp.status 属性服务器响应状态码,200:成功 404:未找到
readyState指 说明
readyState = 0 请求未初始化
readyState = 1 服务器连接已建立
readyState = 2 请求已被接收
readyState = 3 请求正在处理
readyState = 4 响应文本已被接收
完整编写
<body>
    <input id="btnLoad" type="button" value="加载">
    <div id="divContent"></div>
    <script>
        document.getElementById("btnLoad").onclick = function(){
            //1.创建XmlHttpRequest对象
            var xmlhttp;
            if(windows.XMLHttpRequest){
                xmlhttp = new XMLHttpRequest();
            }else{
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            console.log(xmlhttp); //在控制台console里有数据
            //2.发送Ajax请求 //F12中Network content有Response返回
            xmlhttp.open("GET", "/json_war_exploded/news.html", true)
            //3.处理服务器响应
            xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
                if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var responseText = xmlhttp.responseText;
                //对服务器结果进行处理 
                alert(t);
                //点击加载后 数据输出在页面的div上
                document.getElementById("divContent").innerHTML = t;
                }
            }
        }
    </script>
</body>
ContentServlet.java
@WebServlet("/content")
public class ContentServlet extends HttpServlet{
    public ContentServlet(){
        super();
    }
    //用Ajax进行请求的时候不进行任何页面跳转而是直接输出想产生的数据结果[一般用JSON传递]
    protected void doGet(HttpServletReq req, HttpServletRespon res){
        res.getWritre().println("<b style = 'color:red'>I'm server content</b>");
    }
}

利用Ajax实现新闻列表[案例]

需要下载fast json-1.2.52.jar

News.java
public class News(){
    private String title;
    private String date;
    private String source;
    private String content;
    Getter and Setter + Constructor
}
NewsListServlet.java
@WebServlet("/news_list")
public class NewsListServlet extends HttpServlet{
    public NewsListServlet(){
        super();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        List list = new ArrayList();
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        list.add(new News("TIOBE:2023年9月份日本排放核污染水","2020-9","TIOBE","..."));
        //用于把List或者java对象給json序列化生成对应的字符串
        String json = JSON.toJSONString(list); //提供的java对象
        System.out.println(json);
        resp.setContentType("text/html;charset=UTF-8");
        resp.getWriter().println(json);
    }
}
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
</head>
<body>
<div id="container">
</div>
<script type="text/javascript">
        //1.创建XmlHttpRequest对象
        var xmlhttp;
        if(window.XMLHttpRequest){
            xmlhttp = new XMLHttpRequest();
        }else{
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }
        //2.发送Ajax请求 //F12中Network content有Response返回
        //true代表异步执行 false代表同步执行
        xmlhttp.open("GET", "/json_war_exploded/news_list", false)
        //同步:在网络发布的过程中 数据没有返回的话会一直处于阻塞的状态(前面的没做完后面的不让做)
        xmlhttp.send();
        console.log("请求发送完成");
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            //获取响应体的文本
            var text = xmlhttp.responseText;
            //对服务器结果进行处理
            console.log(text);
            var json = JSON.parse(text);//内置的json对象
            console.log(json);
            var html = "";
            for (var i = 0; i < json.length; i++) {
                var news = json[i];
                html = html + "<h1>" + news.title + "</h1>";
                html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                html = html + "<hr/>"
            }
            document.getElementById("container").innerHTML = html;
        }
        /*3.处理服务器响应 异步[一直往下进行 onreadystatechange数据监控]
        xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
            if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var text = xmlhttp.responseText;
                //对服务器结果进行处理
                console.log(text);
                var json = JSON.parse(text);//内置的json对象
                console.log(json);
                var html = "";
                for (var i = 0; i < json.length; i++) {
                    var news = json[i];
                    html = html + "<h1>" + news.title + "</h1>";
                    html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                    html = html + "<hr/>";
                }
                //对html进行动态加载
                document.getElementById("container").innerHTML = html;
            }
        }*/
</script>
</body>
</html>

同步与异步

xmlhttp.open(“GET”, “/json_war_exploded/news_list”, false)

**同步(false)**:程序会阻塞 进入等待的状态 数据不返回时程序是不会往下进行的
异步(true)[推荐]:程序不会阻塞 程序依旧往下进行 数据返回是通过触发onreadystatechange数据监控进行处理的

//    2.发送Ajax请求 //F12中Network content有Response返回
        //true代表异步执行 false代表同步执行
        xmlhttp.open("GET", "/json_war_exploded/news_list", false)
        //同步:在网络发布的过程中 数据没有返回的话会一直处于阻塞的状态(前面的没做完后面的不让做)
        xmlhttp.send();
        console.log("请求发送完成");
        if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            //获取响应体的文本
            var text = xmlhttp.responseText;
            //对服务器结果进行处理
            console.log(text);
            var json = JSON.parse(text);//内置的json对象
            console.log(json);
            var html = "";
            for (var i = 0; i < json.length; i++) {
                var news = json[i];
                html = html + "<h1>" + news.title + "</h1>";
                html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                html = html + "<hr/>"
            }
            document.getElementById("container").innerHTML = html;
        }
        /*3.处理服务器响应 异步[一直往下进行 onreadystatechange数据监控]
        xmlhttp.onreadystatechange = function(){
            //响应已被接收且服务器处理成功时才执行
            if(xmlhttp.readyState == 4 && xmlhttp.status == 200){
                //获取响应体的文本
                var text = xmlhttp.responseText;
                //对服务器结果进行处理
                console.log(text);
                var json = JSON.parse(text);//内置的json对象
                console.log(json);
                var html = "";
                for (var i = 0; i < json.length; i++) {
                    var news = json[i];
                    html = html + "<h1>" + news.title + "</h1>";
                    html = html + "<h2>" + news.date + "&nbsp" + news.source + "</h2>";
                    html = html + "<hr/>";
                }
                //对html进行动态加载
                document.getElementById("container").innerHTML = html;
            }
        }*/

<button type = "button" onclick="loadXMLDoc()">修改内容</button>
<script>
/* XMLHttpRequest 用于在后台与服务器交换数据。
这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
所有现代浏览器(IE7+、Firefox、Chrome、Safari 以及 Opera)均内建 XMLHttpRequest 对象。
*/ 
function loadXMLDoc(){
    //AJAX脚本执行
    var xmlhttp;
    if(windows.XMLHttpRequest){
        xmlhttp=new XMLHttpRequest();
    }else{
        xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    }
}
</script>

/*
如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:

open(method,url,async)
method:请求的类型;GET 或 POST
url:文件在服务器上的位置
async:true(常用异步)或 false(同步)

对于 web 开发人员来说,发送异步请求是一个巨大的进步。
很多在服务器执行的任务都相当费时。AJAX 出现之前,这可能会引起应用程序挂起或停止。
通过 AJAX,JavaScript 无需等待服务器的响应,而是:
在等待服务器响应时执行其他脚本
当响应就绪后对响应进行处理
*/ 

xmlhttp.open("GET","ajax_info.html",true);
xmlhttp.send(); //send(string) 仅用于POST请求 

/*
若需要来自服务器的响应,请使用XMLHttpRequest对象的responseText或responseXML属性 
responseText => 获得字符串形式的相应数据 如果来自服务器的响应并非XML
responseXML => 获得XML形式的相应数据 如果来自服务器的响应式XML 
*/ 
// responseText 
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
// responseXML
xmlDoc=xmlhttp.responseXML;
txt="";
x=xmlDoc.getElementsByTagName("ARTIST");
    for (i=0;i<x.length;i++)
    {
        txt=txt + x[i].childNodes[0].nodeValue + "<br>";
    }
document.getElementById("myDiv").innerHTML=txt;
/*onreadystatechange事件中我们规定当服务器响应已做好被处理的准备时所执行的任务
0:请求未初始化  1:服务器连接已建立  2:请求已接收  3:请求处理中  4:请求已完成且响应已就绪 
 */ 
 //如果您的网站上存在多个 AJAX 任务,那么您应该为创建 XMLHttpRequest对象编写一个标准的函数
 //并为每个 AJAX 任务调用该函数 
 xmlhttp.onreadystatechange=function()
{
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
    }
}

jQuery对Ajax的支持

  • jQuery对Ajax进行封装,提供了**$.ajax()**方法
  • 语法:**$.ajax(options)**
常用设置项 说明
url 发送请求地址
type 请求类型get|post
data 向服务器传递的参数
dataType 服务器响应的数据类型
text|json|xml|html|jsonp|script
success 接收响应时的处理函数
error 请求失败时的处理函数
jquery_news.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $.ajax({
                "url": "/json_war_exploded/news_list",
                "type": "get",
                "data": "t=tiobe",//多个传参的参数用json格式写
                //"data": {"t":"tiobe", "abc":"123", "uu":"777"}
                //但是实际过程中jquery会以url形式传参 t=tiobe&abc=123&uu=777
                "dataType": "json", //解析成json文件
                "success": function (json){
                    console.log(json);
                    for (var i = 0; i < json.length; i++){
                        $("#container").append("<h1>" + json[i].title + "</h1>"); //append追加
                    }
                },   //ajax的核心信息 ↓
                "error": function (xmlhttp, errorText){
                    console.log(xmlhttp);
                    console.log(errorText);
                    if(xmlhttp.status == "405"){
                        alert("无效的请求方式");
                    }else if(xmlhttp.status == "404"){
                        alert("未找到URL资源");
                    }else if(xmlhttp.status == "500"){
                        alert("服务器内部错误,请联系管理员");
                    }else{
                        alert("产生异常,请联系管理员");
                    }
                }
            })
        })
    </script>
</head>
<body>
    <div id = "container"></div>
</body>
</html>

实现二级联动菜单 [省 市 县]

ChannelServlet.java
package com.example.json;

import com.alibaba.fastjson.JSON;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/channel")
public class ChannelServlet extends HttpServlet {
    public ChannelServlet() {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String level = req.getParameter("level");
        String parent = req.getParameter("parent");
        List chlist = new ArrayList();
        if(level.equals("1")) {
            chlist.add(new Channel("ai" , "前沿/区块链/人工智能"));
            chlist.add(new Channel("web" , "前端/小程序/JS"));
        }else if(level.equals("2")) {
            if(parent.equals("ai")) {
                chlist.add(new Channel("micro" , "微服务"));
                chlist.add(new Channel("blockchain" , "区块链"));
                chlist.add(new Channel("other" , "..."));
            }else if(parent.equals("web")){
                chlist.add(new Channel("html" , "HTML"));
                chlist.add(new Channel("css" , "CSS"));
                chlist.add(new Channel("other" , "..."));
            }
        }
        //json序列化
        String json = JSON.toJSONString(chlist);
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().println(json);
    }
}
cascade_menu.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
    <script type="text/javascript">
        $(function(){
            $.ajax({
                "url" : "/json_war_exploded/channel",
                "data" : {"level" : "1"},
                "type" : "get" ,
                "dataType" : "json" ,
                "success" : function(json){
                    console.log(json);
                    for(var i = 0 ; i < json.length ; i++){
                        var ch = json[i];
                        //append是在组件内部进行追加内容
                        $("#lv1").append("<option value='" + ch.code + "'>" + ch.name + "</option>")
                    }
                }
            })
        })

        $(function(){ //on是绑定事件 change是当变化时候发生function
            $("#lv1").on("change" , function(){
                var parent = $(this).val();//val()获取输入项的值
                console.log(parent);
                $.ajax({
                    "url" : "/json_war_exploded/channel" ,
                    "data" : {"level" : "2" , "parent" : parent}, //parent 一级频道的value值
                    "dataType" : "json" ,
                    "type" : "get" ,
                    "success" : function(json){
                        console.log(json);
                        //移除所有lv2下的原始option选项
                        $("#lv2>option").remove();
                        for(var i = 0 ; i < json.length ; i++){
                            var ch = json[i];
                            $("#lv2").append("<option value='" + ch.code +"'>" + ch.name + "</option>")
                        }
                    }
                })
            })
        })
    </script>
</head>
<body>
<select id="lv1" style="width:200px;height:30px">
    <option selected="selected">请选择</option>
</select>
<select id="lv2" style="width:200px;height:30px"></select>
</body>
</html>

正则表达式

  • 正则表达式是检查、匹配字符串的表达式
  • 正则表达式是描述规则、主流语言都有良好支持
  • 字符串校验、查找与替换是正则表达式主要使用场景
正则表达式的案例
  • 检查输入的身份证号是否合法(15位、18位)
  • 示例:13010220200328091x
正则表达式:(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)

字符范围匹配

正则表达式 说明 正确 错误
A 精准匹配单个字符 A a
x|y 允许出现的2个字符 y n
[xyz] 字符集合,允许出现集合内任意单个字符 z c
[a-z] [A-Z] [0-9] 字符范围 a D 8 A a A
[^xyz] [‘^0-9] 集合内字符不允许出现 0 A y 8
训练题目
  • 精准匹配字符串”hallo” 或者 “hello” => h**[ae]**llo
  • 请匹配数字范围(0570-0579) => 057[0-9]
  • 单选题只允许输入ABCD其中一项 => [ABCD] / [A-D]

元字符

正则表达式 说明 正确 错误
\d 匹配任意单个数字 8 i
\D 匹配\d规则之外的任意单个字符 i 8
\w 匹配任意单个字母数字下划线 Y &
\W 匹配\w之外的任意单个字符 & Y
\s 匹配单个空格 x
\n 匹配单个换行符 x
. 匹配任意单个字符(换行符除外)
\。 特殊字符,只匹配 “.” . 1
训练题目
  • 请匹配数字(3213.383219)

    \d\d\d\d\.\d\d\d\d\d\d\d
    
  • 匹配杭州与宁波的座机号码(0571|0574-XXXXXXXX)

    057[14]-\d\d\d\d\d\d\d\d
    
  • 请匹配18位身份证号 [第一位1~6和8]

    [1234568]\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d\d[0-9xX]
    

多次重复匹配

正则表达式 说明 正确 错误
A{3} 精准N次匹配 AAA AA
A{3,} 最少出现N AAA AA
\d{3,5} 约定出现最少次数与最大次数 1234 12
\d* 可以出现零次至无限次,相当于{0,} 1234
\d+ 最少出现一次, 相当于{1,} 12
\d? 最多出现一次, 相当于{0,1} 1 12
联系题目
  • 验证短信验证码(6位数字) => \d[6]

  • 请匹配全国座机号(区号3或4位-电话号码7或8位) => \d[3,4]-\d[7,8]

  • 请匹配英文姓名(例如:James Watson)

    [A-Z][a-z]{1,}\s[A-Z][a-z]{0,}
    [A-Z][a-z]{1,}\s[A-Z][a-z]*
    ^[A-Z][a-z]{1,}+\s[A-Z][a-z]*$
    

定位匹配

正则表达式 说明 正确 错误
^A.* 头匹配 ABC CBA
**.A$* 尾匹配 CBA ABC
^A.*A$ 全字匹配 ACCCA ACCCB
abb123123ab => ^ab.*ab$

贪婪模式[默认匹配规则]

  • 在满足条件的情况下尽可能匹配到字符串
  • 示例:111222333 正则:\d{6,8}
  • 匹配结果:11122233

非贪婪模式

  • 在满足条件的情况下尽可能匹配到字符串

  • 示例:111222333 正则:\d{6,8}?

  • 匹配结果:111222

贪婪模式举例
<a href = "www.baidu.com"> 百度 </a> <a href =  "www.baidu.com">新浪</a>
规则: 想搜索"www.baidu.com" 仅此一个从"开始 从"结束 中间的片段
正则表达式: ".*" 由于默认是贪婪模式 它会找到的结果如下
"www.baidu.com"> 百度 </a> <a href = “www.sina.com”

改成非贪婪模式 正则表达式:".*?"
"www.baidu.com"  "www.baidu.com"

表达式分组

  • 分组将”正则”分组为多个子表达式
  • 示例:abababcdcdcd
  • 正则表达式:(ab){3}(cd){3}
训练题目
  • 匹配验证码(4位或6位)

    (^\d{4}$)|(^\d{6}$)
    
  • 匹配车牌号(冀B-U888G)

    ^([冀黑粤晋][A-Z])-([A-Z0-9]{5})$
    
  • 中文名字匹配 [Unicall码] [张三…]

    ^[\u4e00-\u9fa5]{2,8}$
    
  • 中英文名字匹配

    (^[\u4e00-\u9fa5]{2,8}$)|(^[A-Z][a-z]*$)
    

正则表达式验证JavaScript表单

<body>
    <form action="#" method="post" id="frmInfo">
        <div id="err" style="color : red">
            
        </div> 
        <div>
            姓名: <input id="name" name="name"/>
        </div>
        <div>
            身份证: <input id="idno" name="idno"/>
        </div>
        <div>
            <input type="submit"/>
        </div>
    </form>
    <script type="text/javascript">
        document.getElementById("frmInfo").onsubmit = function(){
            //在JS中定义正则表达式对象只需要在 /正则表达式/
            var regex1 = /^[\u4e00-\u9fa5]{2,8}$/
            var regex2 = /^rehextal
            var name = document.getElementById("name").value;
            var idno = document.getElementByid("inamd")
            if(regex1.test(name)==false); //返回true或false校验是否成功
                document.getElementById("err").innerHTM="无效姓名";
                return false;
            }else if(regx2.test(idno) == false){
                document.getElementById("err").innerHTM="无效身份证号";
            }else{
                alert("验证通过准备提交")
                return true;
            }
    </script>
</body>

Java中web页面信息提取

sample.html
<!DOCTYPEhtml>
<html>
<head>
<meta charset="UTF-8">
<title>国际主要城市</title>
</head>
<body>
    <h1>国际主要城市</h1>
    <ul>
        <li>纽约NewYork</li>
        <li>伦敦London</li>
        <li>东京Tokyo</li>
        <li>巴黎Paris</li>
        <li>香港HongKong</li>
        <li>新加坡Singapore</li>
        <li>悉尼Sydney</li>
        <li>米兰Milano</li>
        <li>上海Shanghai</li>
        <li>北京Beijing</li>
        <li>马德里Madrid</li>
        <li>莫斯科Moscow</li>
        <li>首尔Seoul</li>
        <li>曼谷Bangkok</li>
        <li>多伦多Toronto</li>
        <li>布鲁塞尔Brussels</li>
        <li>芝加哥Chicago</li>
        <li>吉隆坡KualaLumpur</li>
        <li>孟买Mumbai</li>
        <li>华沙Warsaw</li>
        <li>圣保罗SaoPaulo</li>
        <li>苏黎世Zurich</li>
        <li>阿姆斯特丹Amsterdam</li>
        <li>墨西哥城MexicoCity</li>
        <li>雅加达Jakarta</li>
        <li>都柏林Dublin</li>
        <li>曼谷Bangkok</li>
        <li>台北Taipei</li>
        <li>伊斯坦布尔Istanbul</li>
        <li>里斯本Lisbon</li>
        <li>罗马Rome</li>
        <li>法兰克福Frankfurt</li>
        <li>斯德哥尔摩Stockholm</li>
        <li>布拉格Prague</li>
        <li>维也纳Vienna</li>
        <li>布达佩斯Budapest</li>
        <li>雅典Athens</li>
        <li>加拉加斯Caracas</li>
        <li>洛杉矶LosAngeles</li>
        <li>新西兰NewZealand</li>
        <li>圣地亚哥SanDiego</li>
        <li>布宜诺斯艾利斯BuenosAires</li>
        <li>华盛顿Washington</li>
        <li>墨尔本Melbourne</li>
        <li>约翰内斯堡Johannesburg</li>
        <li>亚特兰大Atlanta</li>
        <li>巴塞罗那Barcelona</li>
        <li>旧金山SanFrancisco</li>
        <li>马尼拉Manila</li>
        <li>波哥大Bogota</li>
        <li>特拉维夫TelAviv-Yafo</li>
        <li>新德里NewDelhi</li>
        <li>迪拜Dubai</li>
        <li>布加勒斯特Bucharest</li>
        <li>奥斯陆Oslo</li>
        <li>柏林Berlin</li>
        <li>赫尔辛基Helsinki</li>
        <li>日内瓦Geneva</li>
        <li>利雅得Riyadh</li>
        <li>哥本哈根Copenhagen</li>
        <li>汉堡Hamburg</li>
        <li>开罗Cairo</li>
        <li>卢森堡Luxembourg</li>
        <li>班加罗尔Bangalore</li>
        <li>达拉斯Dallas</li>
        <li>科威特城Kuwaitcity</li>
        <li>波士顿Boston</li>
        <li>慕尼黑Munich</li>
        <li>迈阿密Miami</li>
        <li>利马Lima</li>
        <li>基辅Kiev</li>
        <li>休斯顿Houston</li>
        <li>广州Guangzhou</li>
        <li>贝鲁特Beirut</li>
        <li>卡拉奇Karachi</li>
        <li>索菲亚Sophia</li>
        <li>蒙得维的亚Montevideo</li>
        <li>里约热内卢RioDEJaneiro</li>
        <li>胡志明市HoChiMinhCity</li>
        <li>蒙特利尔Montreal</li>
        <li>内罗毕Nairobi</li>
        <li>巴拿马城Panamacity</li>
        <li>金奈Chennai</li>
        <li>布里斯班Brisbane</li>
        <li>卡萨布兰卡Casablanca</li>
        <li>丹佛Denver</li>
        <li>基多Quito</li>
        <li>斯图加特Stuttgart</li>
        <li>温哥华Vancouver</li>
        <li>麦纳麦MaiNaMai</li>
        <li>危地马拉市Guatemalacity</li>
        <li>开普敦CapeTown</li>
        <li>圣何塞SanJose</li>
        <li>西雅图Seattle</li>
        <li>深圳Shenzhen</li>
        <li>珀斯Perth</li>
        <li>加尔各答Calcutta</li>
        <li>安特卫普Antwerp</li>
        <li>费城Philadelphia</li>
        <li>鹿特丹Rotterdam</li>
        <li>拉各斯Lagos</li>
        <li>波特兰Portland</li>
        <li>底特律Detroit</li>
        <li>曼彻斯特Manchester</li>
        <li>惠灵顿Wellington</li>
        <li>里加Riga</li>
        <li>爱丁堡Edinburgh</li>
        <li>圣彼得堡StPetersburg</li>
        <li>圣迭戈SanDiego</li>
        <li>伊斯兰堡Islamabad</li>
        <li>伯明翰Birmingham</li>
        <li>多哈Doha</li>
        <li>阿拉木图AlmaAtaAlmaty</li>
        <li>卡尔加里Calgary</li>
    </ul>
</body>
</html>
RegexSample.java
package com.imooc.regex;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexSample {
    public static void main(String[] args) {
        StringBuilder content = new StringBuilder();
        try {//读取文件
            FileInputStream fis = new FileInputStream("D:/workspace/regex/WebContent/sample.html");
            InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
            BufferedReader bufferedReader = new BufferedReader(isr);//缓冲提高读取效率
            String lineText = "";//每一行
            while((lineText = bufferedReader.readLine()) != null) {//读取完成
//                System.out.println(lineText);
                content.append(lineText + "\n");//追加到后面完整字符串
            }
            bufferedReader.close();
            System.out.println(content);
        
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        //1.创建正则表达式对象 用group进行分组提取
        //[正则表达式字符串] \\两个斜杠是原意输出 +至少出现一次
        Pattern p = Pattern.compile("<li>([\\u4e00-\\u9fa5]{2,10})([a-zA-Z]+)</li>");
        //2.匹配正则表达式
        Matcher m = p.matcher(content);
        //3.查找匹配的结果
        while(m.find()) {//原始字符串中进行查找 有返回true 无返回false
//            System.out.println(m.group(0)); //完整字符串
            String chs = m.group(1); //group分组
            String eng = m.group(2);
            System.out.println(chs + "-" + eng);
        }
    }
}

初始过滤器-Filter [机场检查]

  • 过滤器(Filter)是J2EE Servlet模块下的组件
  • Filter的作用是对URL进行统一的拦截处理
  • Filter通常用于应用程序层面进行全局处理

开发过滤器三要素

  • 任何过滤器都要实现 javax.servlet.Filter 接口
  • 在Filter接口的doFIlter()方法中编写过滤器的功能代码
  • 在web.xml中对过滤器进行配置,说明拦截URL的范围
MyFirstFilter.java
public class MyFirstFilter implements Filter{
   public void destroy(){}
   public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("过滤器已生效");
       chain.doFilter(req, resp);
   }
   public void init(FilterConfig filterConfig)thorws ServletException{}
}
<filter>
    <filter-name>MyFirstFilter</filter-name>
    <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFirstFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>

<!-- 
    filter-mapping标签用于说明过滤器对URL应用的范围
    1. filter-name 过滤器名称与filter.filter-name保持一致
    2. url-pattern 说明过滤器作用范围 "/*"代表对所有url进行过滤
-->
<body>
    我是默认首页
</body>
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletReq req, HttpServletResp resp){
        resp.setContentType("text/html;charset=utf-8");
        resp.getWriter().println("Hello World!!!")
    }
}

过滤器的生命周期

  • 初始化init() - Filter.init()
  • 提供服务 - Filter.doFilter()
  • 销毁 - Filter.destroy()

过滤器特性

  • 过滤器对象在Web应用启动时被创建且全局唯一
  • 唯一的过滤器对象在并发环境中采用”多线程“提供服务

过滤器两种开发方式

过滤器的配置形式
<filter>
   <filter-name>MyFirstFilter</filter-name>
   <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>MyFirstFilter</filter-name>
   <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
过滤器的注解形式
@WebFilter(filterName = "MyAnnoationFilter", urlPatterns = "/*")
public class MyAnnoationFilter implements Filter{
    
}

MyAnnoationFilter.java      //自定义过滤器名称  设置去过滤哪些url
@@WebFilter(filterName = "MyAnnoationFilter", urlPatterns = "/*")
public class MyAnnoationFilter implements Filter{
    public void destroy(){}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
        System.out.println("过滤器已生效");
        chain.doFilter(req, resp);
    }
    public void init(FilterConfig filterConfig)thorws ServletException{}
}

配置与注解如何选择

  • 配置形式维护性更好,适合应用全局过滤 [中, 大型项目]
  • 注解形式开发体验更好,适合于小型项目敏捷开发

Web中文乱码的解决 [需要强制转换]

  • GET请求-server.xml增加URIEncoding = “UTF-8”;
  • POST请求-使用request.setCharacterEncoding(“UTF-8”);
  • 响应-response.setContentType(“text/html; charset = UTF-8”);
CharacterEncodingFilter.java
public class CharacterEncodingFilter implements Filter{
    public void destroy(){}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       //上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
        HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
        req.serCharacterEncoding("UTF-8");
        HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
        res.serContentType("text/html;charset=UTF-8");
        chain.doFilter(req, response);
    }
    public void init(FilterConfig filterConfig)thorws ServletException{}
}
ServletRequest[最顶级] 和 HTTPServletRequest[需继承最顶级]关系所在

web.xml
<filter>
    <filter-name>MyFirstFilter</filter-name>
    <filter-class>filter.MyFirstFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>MyFirstFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>filter.CharacterEncodingFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>

====================================================================

<!--或者在CharacterEncodingFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="CharacterEncodingFilter", urlPatterns="/*")
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletReq req, HttpServletResp resp){
        resp.getWriter().println("你好!世界!!")
    }
}

过滤器开发技巧

过滤器参数化
  • 过滤器为了增强灵活性,允许配置信息放在web.xml
  • 在web.xml中配置**< init-param >**设置过滤器参数
优化字符集过滤器 [为了不去修改java代码]
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value> <!--方便参数发生变化-->
    </init-param>
    <init-param>
        <param-name>v1</param-name>
        <param-value>GBK</param-value>
    </init-param>
    ......
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern> //对所有url进行拦截
</filter-mapping>
-------------------------------------------------------------
<!--或者在CharacterEncodingFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="CharacterEncodingFilter", urlPatterns="/*",initParams={
    @WebInitParam(name="encoding", value="UTF-8"),
    @WebInitParam(name="p1", value="v1")
})
-------------------------注意init与doFilter----------------------------
public class CharacterEncodingFilter implements Filter{
    private String encoding; //类中的全局私有变量
    public void init(FilterConfig filterConfig)thorws ServletException{
        encoding=filterConfig.getInitParameter("encoding");
        System.out.println(encoding); //用debug打开
    }
    public void doFilter(ServletRequest req, ServeltResponse resp, FilterChain chain)throws IOException,ServletException{
       //上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
        HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
        req.setCharacterEncoding("encoding");
        HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
        res.setContentType("text/html;charset=" + encoding);// GBK/UTF-8或者其他
        chain.doFilter(req, response);
    }
}

url-pattern设置过滤范围

url-pattern常用写法
  • /index.jsp - 执行资源精准匹配
  • /servlet/* - 以前缀进行模糊匹配
  • *.jsp - 以后缀进行模糊匹配
SampleServlet1.java
@WebServlet("/servlet/sample1")
public class SampleServlet1 extends HttpServlet{
   public SampleServlet1(){}
   protected void doGet(HttpServletRequest req,HttpServletResponse resp){
       resp.getWriter().println("I'm" + this.getClass().getSimpleName());
   }
}
UrlPatternFilter.java
public class UrlPatternFilter implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
//上面的不是Httpservlet要自己写HttpServlet并导入 解决post请求中的中文乱码
    HttpServletRequest req = (HttpServletRequest)request;//解决中文乱码问题
    HttpServletResponse res = (HttpServletResponse)response;//解决中文乱码问题
}
<filter>
    <filter-name>UrlPatternFilter</filter-name>
    <filter-class>filter.UrlPatternFilter</filter-class>
</filter>
    <filter-mapping> <!--  <url-pattern>/test.jsp</url-pattern> <!--只对其进行过滤 -->
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>
    ......

在控制台Console显示:拦截到http://localhost:8080/url-pattern/test.jsp 因为这是精准匹配 需要换成 /*

/ 映射的问题

  • / 指映射Web应用根路径, 且只会对Servlet生效
  • 默认首页index.jsp会让 / 失效
  • //* 含义不同, 前者指向根路径, 后者代表所有
SampleServlet2.java
@WebServlet("/")
public class SampleServlet2 extends HttpServlet{
    public SampleServlet1(){}
    protected void doGet(HttpServletRequest req,HttpServletResponse resp){
        resp.getWriter().println("I'm" + this.getClass().getSimpleName());
    }
}
<filter>
    <filter-name>UrlPatternFilter</filter-name>
    <filter-class>filter.UrlPatternFilter</filter-class>
</filter>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>/servlet/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>UrlPatternFilter</param-name>
        <url-pattern>*.jsp</url-pattern>
    </filter-mapping>
    ......
-------------------------------------------------------------
<!--或者在UrlPatternFilter上方进行注解 必须是全局唯一-->
@WebFilter(filterName="UrlPatternFilter", urlPatterns={
    "/","/servlet/*","*.jsp"
})

这个url-pattern 中的**/** 只会映射到根路径的SampleServlet2

@WebServlet(“/“)
public class SampleServlet2 extends HttpServlet{

Web.xml中默认配置了首页为index.jsp 如果想对默认首页拦截需要写
< url-pattern> ***/** < /url-pattern> 或 < url-pattern> /index.jsp < /url-pattern>
默认首页优先级比servlet要高

过滤链

过滤链开发注意事项
  • 每一个过滤器应具有单独职能
  • 过滤器的执行顺序以**< filter-mapping >**的前后顺序为准
  • 调用**chain.doFilter()**将请求向后传递
FilterA.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter A");
       chain.doFilter(req, resp);
    }
    public void destroy(){}

FilterB.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter B");
       chain.doFilter(req, resp);
    }
    public void destroy(){}

FilterC.java
public class FilterA implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       System.out.println("I'm Filter C");
       chain.doFilter(req, resp);
    }
    public void destroy(){}
<filter>
    <filter-name>FilterA</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
<filter>
    <filter-name>FilterB</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
<filter>
    <filter-name>FilterC</filter-name>
    <filter-class>filter.FilterA</filter-class>
</filter>
    <filter-mapping>
        <param-name>FilterA</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>FilterB</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
        <param-name>FilterC</param-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
---------------------不推荐使用注解--------------------------
用注解方式需要在每个Filter.java上面添加注解 
@WebFilter(filterName="FilterA", urlPatterns="/*")
public class FilterA implements Filter{
    //按照字母表升序排序 且不区分大小写
}
HelloServlet.java
@WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        response.getWriter().print("Hello World!");
        Sysotem.out.println("Hello World!")
    }
}

Console显示:顺序由决定
I’m Filter A //chain.doFilter(req, resp); 请求顺着过滤链往下走
I’m Filter B
I’m Filter C //过滤链没有格外的过滤器了 回到HelloServlet.java最终处理
Hello World!
//servlet处理完以后按照原先的顺序 逆向由Hello、C、B、A顺序返回

刻意去控制过滤器访问 VPN 仅限中国访问 如果是中国地址就调用chain.doFilter进行放行 其本质是防火墙

多端设备自动匹配(Pc端 移动端)

设备适配过滤器
index.html
自动脑补绘制两个html首页
一个是电脑端的图片展示
一个是手机端的图片展示
DeviceAdapterFilter.java
public class DeviceAdapterFilter implements Filter{
    public void init(FilterConfig filterConfig)thorws ServletException{}
    public void doFilter(ServletRequest req, ServeltResponse resp)throws IOException,ServletException,FilterChain chain{
       HttpServletRequest req = (HttpServletRequest)request;//强制类型转换
          HttpServletResponse res = (HttpServletResponse)response;
       /*
       /index.html
        PC: /desktop/index.html
        MOBILE: /mobile/index.html

       /test.html
        PC: /desktop/test.html
        MOBILE: /mobile/test.html
       */
       String uri = req.getRequestURI();
       System.out.println("URI: " + uri);
       if(uri.startsWith("/desktop") || uri.startsWith("/mobile")){
           chain.doFilter(req, resp);
       }else {    //读取客户端请求头 所有字符串转成小写
           String userAgent = req.getHeader("user-agent").toLowerCase(); 
           String targetURL = "";
           if(userAgent.indexOf("android")!=-1 || userAgent.indexOf("iphone")!=-1){
               targetURI = "/mobile" + uri;
               System.out.println("移动端设备正在访问, 重新跳转URI: " + targetURI)
               res.sendRedircet(targetURI); //向/mobile对应页面进行获取
           }else {
               targetURI = "/desktop" + uri;
                System.out.println("PC端设备正在访问, 重新跳转URI: " + targetURI)
               res.sendRedircet(targetURI); //向/mobile对应页面进行获取
           }
       }
    }
    public void destroy(){}
}
<filter>
    <filter-name>DeviceAdapterFilter</filter-name>
    <filter-class>filter.DeviceAdapterFilter</filter-class>
</filter>
    <filter-mapping>
        <param-name>DeviceAdapterFilter</param-name>
        <url-pattern>*.html</url-pattern> <!--对其进行过滤-->
    </filter-mapping>

URI: /index.html
PC端设备正在访问, 重新跳转URI: /desktop/index.html
URI: /desktop/index.html

++++++++++++++++++++++++++++++++++++++++++++

URI: /index.html
移动端设备正在访问, 重新跳转URI: /mobile/index.html
URI: /mobile/index.html

监听器、Freemarker

监听器:对Web应用对象的行为进行监控 [触发事件后进行捕获] ★★★
Freemarker[模板引擎]:模板脚本+数据来实现最终数据的产生 ★★★★★

监听器

生活中的”监听器”:汽车自动刹车系统 自动检测前方障碍物触发自动刹车;实时监控电表水表 钱不足直接断电

监听器 - Listener
  • 监听器(Listener)是J2EE Servlet模块下的组件
  • Listener的作用对Web应用对象的行为进行监控
  • 通过Listener监听自动触发指定的功能代码

三种监听对象

  • ServletContext - 对全局ServletContext及其属性进行监听
  • HttpSession - 对用户会话及其属性操作进行监听
  • ServletRequest - 对请求及属性操作进行监听
过滤器与监听器的区别
  • 过滤器(Filter)的职责是对**URL进行过滤拦截**, 是主动的执行
  • 监听器(Listener)的职责是对**Web对象进行监听**, 是被动触发

开发监听器三要素

  • 实现XxxListener接口, 不同接口对应不同监听对象
  • 实现每个接口中独有的方法, 实现触发监听的后续操作
  • 在web.xml中配置**< listener >**使监听器生效

``

第一个监听器 [全局推荐使用配置形式]

FirstListener.java //Debug启动
@WebListener //启动时tomcat会自动扫描
public class FirstListener implements ServletContextListener{
    //项目初始化所触发
    public void contextInitialized(ServletContextEvent sce){
        System.out.println("ServletContext已初始化");
    }
    //上下文被销毁时所触发 关闭时自动销毁
    public void contextDestoryed(ServletContextEvent sce){
       System.out.println("ServletContext已销毁");
    }
}
<listener>
    <listener-class>com.imooc.listener.FirstListener</listener-class>
</listener>

内置对象监听接口

  • ServletContextListener - 监听ServletContext对象创建、销毁等操作
  • HttpSessionListener - 监听HttpSession对象创建、销毁等操作
  • ServletRequestListener - 监听HttpServletRequest对象创建、销毁等操作
<listener>
    <listener-class>com.example.json.WebListener</listener-class>
</listener>
HelloServlet.java //设置属性的时候会触发WebListener
WebServlet("/hello")
public class HelloServlet extends HttpServlet{
    public HelloServlet(){}
    protected void doGet(HttpServletRequest req,HttpServletResponse resp){
        resp.getWriter().println("Hello World");
        req.getServletContext().setAttribute("sc-attr1","sc-attr-value1");
        req.getSession().setAttribute("session-attr1","session-attr-value1");
        req.setAttribute("request-attr1","request-attr-value1");
    }
}
WebListener.java
package com.example.json;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

public class WebListener implements ServletContextListener, HttpSessionListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext已初始化");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("ServletContext已被销毁");
    }

    @Override
    public void sessionCreated(HttpSessionEvent httpSessionEvent) {
        HttpSession session = httpSessionEvent.getSession();
        System.out.println("Session已被创建, SessionId:" + session);
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
        System.out.println("Session已被销毁");
    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
        System.out.println("HttpServletRequest已被销毁");
    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest)servletRequestEvent.getServletRequest();
        //请求初始化完毕
        System.out.println("HttpServletRequest已被创建, URI" + request.getRequestURI());
    }
}

//session被创建了以后 第二次从同样的浏览器发出的请求覆盖了sessionid(3min后过期可人为而改变) 隐藏且可用
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@71ed02bc
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@194a08e0
HttpServletRequest已被销毁

属性监听接口 [了解]

  • ServletContextAttributeListener - 监听全局属性操作
  • HttpSessionAttributeListener - 监听用户会话属性操作
  • ServletRequestAttributeListener - 监听请求属性操作
web.xml 和 helloServlet.java的代码都一样
WebAttributeListener.java
package com.example.json;

import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;

public class WebAttributeListener implements ServletContextAttributeListener, HttpSessionAttributeListener, ServletRequestAttributeListener {
    @Override
    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
        System.out.println("ServletContext新增属性:" + servletContextAttributeEvent.getName() + "->" + servletContextAttributeEvent.getValue());
    }

    @Override
    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {

    }

    @Override
    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {

    }

    @Override
    public void attributeAdded(ServletRequestAttributeEvent servletRequestAttributeEvent) {
        System.out.println("ServletRequestAttributeEvent新增属性:" + servletRequestAttributeEvent.getName() + "->" + servletRequestAttributeEvent.getValue());

    }

    @Override
    public void attributeRemoved(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    @Override
    public void attributeReplaced(ServletRequestAttributeEvent servletRequestAttributeEvent) {

    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent httpSessionBindingEvent) {
        System.out.println("HttpSessionBindingEvent新增属性:" + httpSessionBindingEvent.getName() + "->" + httpSessionBindingEvent.getValue());
    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent httpSessionBindingEvent) {

    }

    @Override
    public void attributeReplaced(HttpSessionBindingEvent httpSessionBindingEvent) {

    }
}

ServletContext已初始化
[2023-09-28 04:44:52,590] Artifact json:war exploded: Artifact is deployed successfully
[2023-09-28 04:44:52,590] Artifact json:war exploded: Deploy took 394 milliseconds
HttpServletRequest已被创建, URI/json_war_exploded/
ServletContext新增属性:org.apache.jasper.runtime.JspApplicationContextImpl->org.apache.jasper.runtime.JspApplicationContextImpl@7753fcd4
ServletContext新增属性:org.apache.jasper.compiler.ELInterpreter->org.apache.jasper.compiler.ELInterpreterFactory$DefaultELInterpreter@3b424c58
ServletContext新增属性:org.apache.jasper.compiler.StringInterpreter->org.apache.jasper.compiler.StringInterpreterFactory$DefaultStringInterpreter@15bdbfaf
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@6baa8579
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/
Session已被创建, SessionId:org.apache.catalina.session.StandardSessionFacade@2843a202
HttpServletRequest已被销毁
HttpServletRequest已被创建, URI/json_war_exploded/hello
ServletContext新增属性:sc-attr1->sc-attr-value1
HttpSessionBindingEvent新增属性:session-attr1->session-attr-value1
ServletRequestAttributeEvent新增属性:request-attr1->request-attr-value1
HttpServletRequest已被销毁

监听器的应用场景

请求流量分析 [以图表形式展现]

流程:在应用启动的时候将两个List初始化 一个保存时间一个保存数值 分情况 若时间不存在初始化1 若存在则在原始+1进行更新操作

web.xml
<listener>
  <listener-class>com.example.json.RequestTotalListener</listener-class>
</listener>
RequestTotalListener.java
package com.example.json;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RequestTotalListener implements ServletContextListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        List timeList = new ArrayList(); //时间数据
        List valueList = new ArrayList(); //具体时间访问量数据
        servletContextEvent.getServletContext().setAttribute("timeList", timeList);//得到最原始的context
        servletContextEvent.getServletContext().setAttribute("valueList", valueList);//启动ServletContext时自动启动倆请求
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        //请求初始化 每来一个新的请求监听器都会执行此方法 记录某一时间点的访问量
        //TimeList:10:02  10:03  10:04  10:05
        //ValueList: 5       7     10    1+1=2
        List<String> timeList = (List)servletRequestEvent.getServletContext().getAttribute("timeList");
        List<Integer> valueList = (List)servletRequestEvent.getServletContext().getAttribute("valueList");
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); //按照小时分钟来取
        String time = sdf.format(date); //date对象传入
        if (timeList.indexOf(time) == -1) { //查找对应的数据在集合中是否存在
            //若时间不存在 则追加时间
            timeList.add(time);
            valueList.add(1);//当前有1个请求被创建
            servletRequestEvent.getServletContext().setAttribute("timeList", timeList);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }else { //假设时间存在 不增加新数据 在原有数据加1
            int index = timeList.indexOf(time); //10:05返回索引值3
            int value = valueList.get(index);
            valueList.set(index, value+1);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }
    }
}
RequestTotalServlet.java
package com.imooc.total;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;

/**
 * Servlet implementation class RequestTotalServlet
 */
@WebServlet("/rt")
public class RequestTotalServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public RequestTotalServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = request.getServletContext();//得到全局context对象
        List<String> timeList = (List)context.getAttribute("timeList");//提取之前创建的
        List<Integer> valueList = (List)context.getAttribute("valueList");
        response.setContentType("text/html;charset=utf-8");
        /*response.getWriter().println(timeList.toString()); //打印输出时间数值数据
        response.getWriter().println("<br/>");
        response.getWriter().println(valueList.toString());*/
        
        Map result = new HashMap();
        result.put("timeList", timeList);
        result.put("valueList", valueList);
        String json = JSON.toJSONString(result);
        response.getWriter().println(json);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}
text3.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test Page</title>
</head>
<body>
<h1>I'm test page 3</h1>
</body>
</html>

<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript" src="js/echarts.min.js"></script>
<script type="text/javascript" src="js/jquery-3.1.1.js"></script>
</head>
<body>
   <div id="main" style="width: 600px; height: 400px;"></div>
   <script type = "text/javascript">
     //基于准备好的dom, 初始化echarts实例
    var myChart = echarts.init(document.getElementById('main'));
     //指定图表的配置项和数据
    var option = {
        title:{
            text:'Echarts 入门示例'
        },
        tooltip: {},
        legend: {
            data: ['销量']
        },
        xAxis: {
            data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
        },
        yAxis: {},
        series:[{
            name: '销量',
            type: 'bar',
            data: [5,20,36,10,10,20]
        }]
    };
       //使用刚指定的配置项和数据显示图表
       myChart.setOption(option);
    </script>
</body>

入门百度Echarts组件

利用Ajax和Jquery进行前后端通信

Apache ECharts

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script type="text/javascript" src="js/echarts.min.js"></script>
    <script type="text/javascript" src="js/jquery-3.1.1.js"></script>
</head>
<body>
<div id="main" style="width: 600px; height: 400px;"></div>
<script type="text/javascript">
    //每一秒钟向服务器查询一次数据把变化数据重新写入图表
//需要Javascript定时器完成
    function showChat(){//显示图表
        //基础的通讯部分
    $.ajax({
        url:"./rt",
        type:"get",
        dataType:"json",
        success:function(json){
            console.log(json.timeList);
            console.log(json.valueList);
    //基于准备好的dom, 初始化echarts实例
    var myChart = echarts.init(document.getElementById('main'));
    //指定图表的配置项和数据
    var option = {
        title: {
            text: '请求流量分析统计'
        },
        tooltip: {},
        legend: {
            data: ['访问量']
        },
        xAxis: {
           // data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
            data: json.timeList
        },
        yAxis: {},
        series: [{
            name: '访问量',
            type: 'line', //柱状图  line是折线图
           // data: [5, 20, 36, 10, 10, 20] //图中展示的数值 与商法data对应
            data: json.valueList
        }]
    };
    //激活使用刚指定的配置项和数据显示图表
    myChart.setOption(option);
        }
    })
}
       window.setInterval("shwoChart()", 1000);//间隔某时长去执行指定代码
    //每秒钟通过shwoChart()向服务器"./rt"发送请求 对此请求进行排除用RequestTotalListener去改写
</script>
</body>
</html>
RequestTotalListener.java
package com.example.json;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class RequestTotalListener implements ServletContextListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        List timeList = new ArrayList(); //时间数据
        List valueList = new ArrayList(); //具体时间访问量数据
        servletContextEvent.getServletContext().setAttribute("timeList", timeList);//得到最原始的context
        servletContextEvent.getServletContext().setAttribute("valueList", valueList);//启动ServletContext时自动启动倆请求
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }

    @Override
    public void requestDestroyed(ServletRequestEvent servletRequestEvent) {

    }

    @Override
    public void requestInitialized(ServletRequestEvent servletRequestEvent) {
        HttpServletRequest request = (HttpServletRequest)servletRequestEvent.getServletRequest();
        String url = request.getRequestURL().toString();
        if (url.endsWith("/rt") == true){
            return; //将rt的url排除之外
        }
        //请求初始化 每来一个新的请求监听器都会执行此方法 记录某一时间点的访问量
        //TimeList:10:02  10:03  10:04  10:05
        //ValueList: 5       7     10    1+1=2
        List<String> timeList = (List)servletRequestEvent.getServletContext().getAttribute("timeList");
        List<Integer> valueList = (List)servletRequestEvent.getServletContext().getAttribute("valueList");
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm"); //按照小时分钟来取
        String time = sdf.format(date); //date对象传入
        if (timeList.indexOf(time) == -1) { //查找对应的数据在集合中是否存在
            //若时间不存在 则追加时间
            timeList.add(time);
            valueList.add(1);//当前有1个请求被创建
            servletRequestEvent.getServletContext().setAttribute("timeList", timeList);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }else { //假设时间存在 不增加新数据 在原有数据加1
            int index = timeList.indexOf(time); //10:05返回索引值3
            int value = valueList.get(index);
            valueList.set(index, value+1);
            servletRequestEvent.getServletContext().setAttribute("valueList", valueList);
        }
    }
}
RequestTotalServlet.java
package com.example.json;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;

/**
 * Servlet implementation class RequestTotalServlet
 */
@WebServlet("/rt")
public class RequestTotalServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public RequestTotalServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        ServletContext context = request.getServletContext();
        List<String> timeList = (List)context.getAttribute("timeList");
        List<Integer> valueList = (List)context.getAttribute("valueList");
        response.setContentType("text/html;charset=utf-8");
        /*response.getWriter().println(timeList.toString());
        response.getWriter().println("<br/>");
        response.getWriter().println(valueList.toString());*/ //将对象进行封装
        
        Map result = new HashMap();
        result.put("timeList", timeList);
        result.put("valueList", valueList);
        String json = JSON.toJSONString(result); //将java对象转换为json字符串
        response.getWriter().println(json);
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }
}

静态数据预加载

通过监听器在上下文初始化的时候放在系统的全局属性中

Channel.java
public class Channel{
    private String channelName;
    private String url;
    ......
}
StaticDataListener.java
package com.imooc.listener;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.imooc.listener.entity.Channel;

public class StaticDataListener implements ServletContextListener{

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        List list  = new ArrayList();
        list.add(new Channel("免费课程" , "http://www.imooc.com/1"));
        list.add(new Channel("实战课程" , "http://www.imooc.com/2"));
        list.add(new Channel("就业班" , "http://www.imooc.com/3"));
        sce.getServletContext().setAttribute("channelList", list);
        //一次性写入到全局属性种
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        // TODO Auto-generated method stub
        
    }

}
web.xml
<listener>
    <listener-class>listener.StaticDataListener</listener-class>
</listener>
index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<c:forEach items="${applicationScope.channelList }" var="c"> <!--有效数据提取-->
    <a href="${c.url }">${c.channelName }</a> | 
</c:forEach>
<hr/>
</body>
</html>

FreeMarker(FTL)模板引擎[可以替代jsp]

FreeMarker是一款用java语言编写的模版引擎,它虽然不是web应用框架,但它很合适作为web应用框架的一个组件。

特点:
1.轻量级模版引擎,不需要Servlet环境就可以很轻松的嵌入到应用程序中
2.能生成各种文本,如html,xml,java
3.入门简单,它是用java编写的,很多语法和java相似

基础语法及入门基础

freemarker模板文件(*.ftl)的基本组成部分
1.文本:直接输出的内容部分
2.注释:不会输出的内容,格式为 <#– 注释内容 –>
3.取值(插值):代替输出数据模型的部分,格式为${数据模型}或#{数据模型}
4.ftl指令:Freemarker指令,类似于HTML标记。

字符输出
$(emp.name?if_exists)  //变量存在,输出该变量,否则不输出
${emp.name!}       //变量存在,输出该变量,否则不输出
${emp.name?default("xxx")} // 变量不存在,取默认值xxx
${emp.name!"xxx"}           // 变量不存在,取默认值xxx
常用内部函数
${"123<br>456"?html}   // 对字符串进行HTML编码,对html中特殊字符进行转义
${"str"?cap_first}    // 使字符串第一个字母大写 
${"Str"?lower_case}    // 将字符串转换成小写
${"Str"?upper_case}   // 将字符串转换成大写
${"str"?trim}            // 去掉字符串前后的空白字符
字符串的两种拼接方法
${"hello${emp.name!}"} //输出hello+变量名
${"hello"+emp.name!}   //使用+号来连接,输出hello+变量名
截取子串
可以通过如下语法来截取子串:
<#assign str = "abcdefghijklmn"/>
// 方法1${str?substring(0,4)} // 输出abcd
// 方法2${str[0]}${str[4]} // 结果是ae
${str[1..4]}     // 结果是bcde// 返回指定字符的索引${str?index_of("n")}
日期输出
${emp.date?string('yyyy-MM-dd')} //日期格式
数字输出(以数字20为例)
${emp.name?string.number} //输出20
${emp.name?string.currency} //¥20.00
${emp.name?string.precent} //20%
${1.222?int}          // 将小数转为int,输出1
<#setting number_format="percent"/> // 设置数字默认输出方式('percent',百分比)
<#assign answer=42/>          // 声明变量 answer 42
#{answer}          // 输出 4,200%
${answer?string}      // 输出 4,200%
${answer?string.number}   // 输出 42
${answer?string.currency} // 输出 ¥42.00
${answer?string.percent}  // 输出 4,200%
#{answer}         // 输出 42
比较运算符
表达式中支持的比较运算符有如下几个:= 或 == :判断两个值是否相等.
!= :判断两个值是否不等.
> 或 gt :判断左边值是否大于右边值
>= 或 gte :判断左边值是否大于等于右边值
< 或 lt :判断左边值是否小于右边值
<= 或 lte :判断左边值是否小于等于右边值
算术运算符
FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % 注意:
(1)运算符两边必须是数字
(2)使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如:${3 + "5"},结果是:35
FreeMarker中的运算符优先级如下(由高到低排列):

①、一元运算符:!
②、内建函数:?
③、乘除法:*, / , %
④、加减法:- , +
⑤、比较:> , < , >= , <= (lt , lte , gt , gte)
⑥、相等:== , = , !=
⑦、逻辑与:&&
⑧、逻辑或:||
⑨、数字范围:..实际上,我们在开发过程中应该使用括号来严格区分,这样的可读性好,出错少

if逻辑判断(注意:elseif 不加空格)
<#if condition>
...
<#elseif condition2>
...
<#elseif condition3>
...
<#else>
...
</#if>
switch(条件可为数字,可为字符串)
<#switch value>
 <#case refValue1>
....
<#break> 
<#case refValue2> 
....
<#break>
 <#case refValueN>
 ....
<#break>
 <#default>
 ....
 </#switch>
集合 & 循环
//遍历集合
<#list empList! as emp>
    ${emp.name!}    
</#list>

//使用<#break>跳出循环
<#if emp_index = 0><#break></#if>

//集合长度判断 
<#if empList?size!=0></#if> //判断=的时候,注意只要一个=符号,而不是==
<#assign l=0..100/>       // 定义一个int区间的0~100的集合,数字范围也支持反递增,如100..2
<#list 0..100 as i>   // 等效于java for(int i=0; i <= 100; i++)
  ${i}
</#list>

// 截取子集合:
empList[3..5] //返回empList集合的子集合,子集合中的元素是empList集合中的第4-6个元素

// 创建集合:
<#list ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期天"] as x>

// 集合连接运算,将两个集合连接成一个新的集合
<#list ["星期一","星期二","星期三"] + ["星期四","星期五","星期六","星期天"] as x>

// 除此之外,集合元素也可以是表达式,例子如下:
[2 + 2, [1, 2, 3, 4], "whatnot"]

// seq_contains:判断序列中的元素是否存在
<#assign x = ["red", 16, "blue", "cyan"]> 
${x?seq_contains("blue")?string("yes", "no")}    // yes
${x?seq_contains("yellow")?string("yes", "no")}  // no
${x?seq_contains(16)?string("yes", "no")}        // yes
${x?seq_contains("16")?string("yes", "no")}      // no

// seq_index_of:第一次出现的索引
<#assign x = ["red", 16, "blue", "cyan", "blue"]> 
${x?seq_index_of("blue")}  // 2

// sort_by:排序(升序)
<#list movies?sort_by("showtime") as movie></#list>

// sort_by:排序(降序)
<#list movies?sort_by("showtime")?reverse as movie></#list>

// 具体介绍:
// 不排序的情况:
<#list movies as moive>
  <a href="${moive.url}">${moive.name}</a>
</#list>
//要是排序,则用
<#list movies?sort as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>
    
/ 这是按元素的首字母排序。若要按list中对象元素的某一属性排序的话,则用
<#list moives?sort_by(["name"]) as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>

//这个是按list中对象元素的[name]属性排序的,是升序,如果需要降序的话,如下所示:
<#list movies?sort_by(["name"])?reverse as movie>
  <a href="${movie.url}">${movie.name}</a>
</#list>
    
//Map对象 创建map
<#assign scores = {"语文":86,"数学":78}>

// Map连接运算符
<#assign scores = {"语文":86,"数学":78} + {"数学":87,"Java":93}>

// Map元素输出
emp.name       // 全部使用点语法
emp["name"]    // 使用方括号

// FreeMarker支持如下转义字符:
\" :双引号(u0022)
\' :单引号(u0027)
\\ :反斜杠(u005C)
\n :换行(u000A)
\r :回车(u000D)
\t :Tab(u0009)
\b :退格键(u0008)
\f :Form feed(u000C)
\l :<
\g :>
\a :&
\{ :{
\xCode :直接通过4位的16进制数来指定Unicode码,输出该unicode码对应的字符.
    
// include指令的作用类似于JSP的包含指令:
<#include "/test.ftl" encoding="UTF-8" parse=true>

// 在上面的语法格式中,两个参数的解释如下:
encoding="GBK"  // 编码格式
parse=true    // 是否作为ftl语法解析,默认是true,false就是以文本方式引入,
注意:在ftl文件里布尔值都是直接赋值的如parse=true,而不是parse="true"
    
//import指令
类似于jsp里的import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件
<#import "/libs/mylib.ftl" as my>
上面的代码将导入/lib/common.ftl模板文件中的所有变量,
交将这些变量放置在一个名为com的Map对象中,"my"在freemarker里被称作namespace

数据 + 模板 = 结果

数据 [Java代码]
User user = new User();
user.setName(“张三”);

模板 [HTML简化]
< span >
${user.name}
< /span >

结果
< span >
张三
< /span >

  • Freemarker是免费开源的模板引擎技术
  • Freemarker脚本为(Freemarker Template Language)
  • Freemarker提供了大量内建函数来简化开发

JSP与Freemarker

JSP Freemarker
官方标准
执行方式 编译型 解释型
执行效率
开发效率
扩展能力
数据提取 JSTL + EL 内置标签

前端工程师把数据写在ftl 然后后端工程师拿着名称去添加后台数据

package com.example.json;


import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class FreemarkerSample1 {
    public static void main(String[] args) throws IOException, TemplateException {
        //1. 加载模板
        Configuration confige = new Configuration(Configuration.VERSION_2_3_32);
                     // FreemarkerSample1类所在包中加载ftl所在文件
        confige.setClassForTemplateLoading(FreemarkerSample1.class, "");
        Template t = confige.getTemplate("sample1.ftl");
        //2. 创建数据
        Map<String,Object> data = new HashMap<String, Object>();
        data.put("site","百度");
        data.put("url","https://www.abc.com");
        data.put("data",new Date());
        data.put("number",83719.88321);
        Map info = new HashMap();
        info.put("cpu","i5");
        Computer c1 = new Computer("123456","ALIENWARE",1,"李四",new Date(), 12900f)
        data.put("computer",c1);
        //3. 产生输出    向目标输出
        t.process(data, new OutputStreamWriter(System.out));
    }
}
${site}
${url}
<#--类似于EL表达式-->
Computer.java
public class Computer{
    private String sn; //序列号
    private String mode1; //型号
    private int state; //状态 1-在用 2-闲置 3-报废
    private String user; //使用人
    private Date dog; //采购日期
    private Float price; //购买价格
    private Map info; //电脑配置信息
    ......
}
sample1.ftl
<#-- Freemarker取值 -->
${site}
${url}

<#-- !默认值 -->
${author!"不存在的属性"}

<#-- ?string格式化输出 -->
${date?string("yyyy年MM月dd日 HH:mm:ss SSS")}
${number?string("0.00")}

SN:${computer.sn}
型号:${computer.mode1}
状态:${computer.state}
用户:${computer.user}
采购时间:${computer.dop?string("yyyy年MM月dd日")}
采购价格:${computer.price?string("0.00")}

//对map的提取
CPU:${computer.info["cpu"]}
内存:${computer.info["memory"]!"无内存信息"}

FTL(FreeMarker Template Language)取值

  • ${属性名} 取值,可对属性进行计算

  • ${属性名!默认值} 使用默认值[解决空值异常]
    ${author!”不存在的属性”} => 不存在的属性

  • ${属性名?string} 格式化输出
    ${data?string(“yyyy年MM月dd日 HH:mm:ss SSS”)}

    ${number?string(“0.00”)} 小数部位保留两位

if分支判断
<#if 条件1>
  条件1成立执行代码
<#if 条件2>
  条件2成立执行代码
<#if 条件3>
  条件3成立执行代码
<#else>
  其他情况下执行代码
switch分支判断
<#switch value>
  <#case refValue1>
    ...
    <#break>
  <#case refValue2>
    ...
    <#break>
  <#case refValueN>
    ...
    <#break>
<#default>
...
</#switch>

sample1.ftl
<#-- Freemarker取值 -->
${site}
${url}

<#-- !默认值 -->
${author!"不存在的属性"}

<#-- ?string格式化输出 -->
${date?string("yyyy年MM月dd日 HH:mm:ss SSS")}
${number?string("0.00")}
<#if computer.sn == "1234567">
重要设备
</#if>

SN:${computer.sn}
型号:${computer.mode1}
状态:${computer.state}

<#if computer.state == 1>
状态:正在使用
<#elseif computer.state == 2>
状态:闲置
<#elseif computer.state == 3>
状态:已作废
</#if>

<#if computer.user??> //??代表判断对象是否为空,true不为空,false为空
用户:${computer.user}
</#if>
--------------------------------------------------------
<#switch computer.state>  //同一个属性的不同值进行判断
    <#case1>
        状态:正在使用
        <#break>
    <#case2>
        状态:闲置
        <#break>
    <#case3>
        状态:已作废
        <#break>
    <#default>
        状态:无效状态
--------------------------------------------------------
采购时间:${computer.dop?string("yyyy年MM月dd日")}
采购价格:${computer.price?string("0.00")}

//对map的提取
CPU:${computer.info["cpu"]}
内存:${computer.info["memory"]!"无内存信息"}

list迭代列表

<#list students as stu>
  <li>${stu_index}-${stu.name}</li>
</#list>
list迭代Map
<#list map?keys as key>
  ${key}:${map[key]}
</#list>
FreemarkerSample2.java
package com.example.json;


import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class FreemarkerSample1 {
    public static void main(String[] args) throws IOException, TemplateException {
        //1. 加载模板
        Configuration confige = new Configuration(Configuration.VERSION_2_3_32);
          // FreemarkerSample1类所在包中加载ftl所在文件
        confige.setClassForTemplateLoading(FreemarkerSample2.class, "");
        Template t = confige.getTemplate("sample1.ftl"); //得到模板对象
        //2. 创建数据
        Map<String,Object> data = new HashMap<String,object>();
        List<Computer> computers = new ArrayList();
        computers.add(new Computer("123456","ALIENWARE",2,null,new Date(), 12900f, new HashMap()));
        computers.add(new Computer("123456","HP XXX",1,"张三",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("1233145","DELL XXX",3,"李四",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("1234536","ACER XXX",1,"王五",new Date(), 12900f, new HashMap()));
        computers.add(new Computer("12344456","MSI XXX",1,"赵六",new Date(), 12900f, new HashMap()));
        data.put("computers", computers);
        //LinkedHashMap可以保证数据按存放顺序进行提取
        Map computerMap = new LinkedHashMap();
        for(Computer c : computers){
            computerMap.put(c.getSn(), c);
        } //下方新增
        //3. 产生输出    向目标输出
        t.process(data, new OutputStreamWriter(System.out));
    }
}
sample2.ftl
<#list computers as c> //迭代循环 用变量c
序号:${c_index} //迭代变量 index保存了循环的索引,从0开始 +1则从1开始
SN:${c.sn}
型号:${c.mode1}
<#switch c.state>  //同一个属性的不同值进行判断
    <#case1>
        状态:正在使用
        <#break>
    <#case2>
        状态:闲置
        <#break>
    <#case3>
        状态:已作废
        <#break>
    <#default>
        状态:无效状态
</#switch>
<#if c.user??>
状态:${c.state}
</#if>
用户:${c.user}
采购时间:${c.dop?string("yyyy-MM-dd")}
采购价格:${c.price?string("0.00")}
</#list>
-------------------------------------------------------------
<#list computer_map?keys as k >
${k}-${computer_map[k].model}
${computer_map[k].price?string("0.00")}
</#list>

Freemarker内建函数[看成java代码 就是点”.”换成了问号”?”]

freemarker.foofun.cn 在线学习

函数名 说明 示例
lower_case/upper_case 大小写转换 “abcde”?upper_case
cap_first 首字母大写 “”abcde””?cap_first
index_of 查找字符索引 “abcde”?index_of(“b”)
length 返回字符串长度 “abcde”?length
round/floor/ceiling 四舍五入/下取整/上取整 pi?floor
size 得到集合元素总数 students?size
first/last 获取第一个/最后一个元素 students?first
sort_by 按某个属性对集合排序 list?sort_by(“time”)
example:
Map<String,Object> data = new HashMap<String,Object>();
data.put("name", "jackson");
----------------------------
${name?cap_first}
//违禁字母屏蔽
${words?replace("blood", "******")}
${words?index_of("blood")}
//三目运算 
${(words?index_of("blood") != -1)?string("包含敏感词汇","不包含敏感词汇")}

公司共有${computers?size}台电脑
//集合排序 返回一个新的集合
<#list computers?sort_by("price") as c >
    ${c.sn}-${c.price}
</#list>
//集合排序{降序}
<#list computers?sort_by("price")?reverse as c >
    ${c.sn}-${c.price}
</#list>

Freemarker与Servlet整合

web.xml
<servlet>
  <servlet-name>freemarker</servlet-name>
  <servlet-class>freemarkerServlet</servlet-class>
  <init-param>
      <param-name>TemplatePath</param-name>
    <param-value>/WEB-INF/ftl</param-value>
  </init-param>
</servlet>
<servlet-mapping>
  <servlet-name>freemarker</servlet-name>
  <url-pattern>*.ftl</url-pattern> //地址映射 输入这样的后缀时自动去/WEB-INF/ftl查找
</servlet-mapping>
test.ftl
这是一个测试FTL文件

localhost:8080/fm-web/test.ftl
employee.ftl
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>员工列表</title>
    <link href="./css/bootstrap.css" type="text/css" rel="stylesheet"/>
    <script type="text/javascript" src="./js/jquery-1.11.1.min.js"></script>
    <script type="text/javascript" src="./js/bootstrap.js"></script>

    <style type="text/css">
        .pagination {
            margin: 0px
        }

        .pagination > li > a, .pagination > li > span {
            margin: 0 5px;
            border: 1px solid #dddddd;
        }

        .glyphicon {
            margin-right: 3px;
        }

        .form-control[readonly] {
            cursor: pointer;
            background-color: white;
        }
        #dlgPhoto .modal-body{
            text-align: center;
        }
        .preview{

            max-width: 500px;
        }
    </style>
    <script>
        $(function () {
            
            $("#btnAdd").click(function () {
                $('#dlgForm').modal()
            });
        })
    </script>
</head>
<body>

<div class="container">
    <div class="row">
        <h1 style="text-align: center">IMOOC员工信息表</h1>
        <div class="panel panel-default">
            <div class="clearfix panel-heading ">
                <div class="input-group" style="width: 500px;">
                    <button class="btn btn-primary" id="btnAdd"><span class="glyphicon glyphicon-zoom-in"></span>新增
                    </button>
                </div>
            </div>

            <table class="table table-bordered table-hover">
                <thead>
                <tr>
                    <th>序号</th>
                    <th>员工编号</th>
                    <th>姓名</th>
                    <th>部门</th>
                    <th>职务</th>
                    <th>工资</th>
                    <th>&nbsp;</th>
                </tr>
                </thead>
                <tbody>
//<c:forEach items="${applicationScope.employees}" var="emp" varStatus="idx">
                <#list employee_list as emp>
                <tr>
                    <td>${emp_index + 1}</td>
                    <td>${emp.empno?string("0")}</td> //整数输出用0初始化
                    <td>${emp.ename}</td>
                    <td>${emp.department}</td>
                    <td>${emp.job}</td>
//<td style="color: red;font-weight: bold">¥<fmt:formatNumber value="${emp.salary}" pattern="0,000.00"></fmt:formatNumber></td>
                    <td style="color: red;font-weight: bold">¥${emp.salary?string("0.00")}</td>
                </tr>
                </c:forEach>
            </table>
        </div>
    </div>
</div>

<!-- 表单 -->
<div class="modal fade" tabindex="-1" role="dialog" id="dlgForm">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span>
                </button>
                <h4 class="modal-title">新增员工</h4>
            </div>
            <div class="modal-body">
                <form action="/untitled_war_exploded/create" method="post" >
                    <div class="form-group">
                        <label for="empno">员工编号</label>
                        <input type="text" name="empno" class="form-control" id="empno" placeholder="请输入员工编号">
                    </div>
                    <div class="form-group">
                        <label for="ename">员工姓名</label>
                        <input type="text" name="ename" class="form-control" id="ename" placeholder="请输入员工姓名">
                    </div>
                    <div class="form-group">
                        <label>部门</label>
                        <select id="dname" name="department" class="form-control">
                            <option selected="selected">请选择部门</option>
                            <option value="市场部">市场部</option>
                            <option value="研发部">研发部</option>
                            <option value="后勤部">后勤部</option>
                        </select>
                    </div>

                    <div class="form-group">
                        <label>职务</label>
                        <input type="text" name="job" class="form-control" id="sal" placeholder="请输入职务">
                    </div>

                    <div class="form-group">
                        <label for="sal">工资</label>
                        <input type="text" name="salary" class="form-control" id="sal" placeholder="请输入工资">
                    </div>

                    <div class="form-group" style="text-align: center;">
                        <button type="submit" class="btn btn-primary">保存</button>
                    </div>
                </form>
            </div>

        </div><!-- /.modal-content -->
    </div><!-- /.modal-dialog -->
</div><!-- /.modal -->
</body>
</html>
Employee.java
public class Employee {
    private Integer empno;
    private String ename;
    private String department;
    private String job;
    private Float salary;
    ...
}
ListServlet.java
package com.imooc.freemarker.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ListServlet
 */
@WebServlet("/list")
public class ListServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ListServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List list = new ArrayList();
        list.add(new Employee(7731,"张三" , "市场部" , "客户代表" , 8000f));
        list.add(new Employee(8871,"李四" , "研发部" , "运维工程师" , 7000f));
        request.getServletContext().setAttribute("employee_list", list);
        request.getRequestDispatcher("/employee.ftl").forward(request, response);
    }
}
阅读全文

XML_Schema dtd Dom4j XPath

2023/9/12

XML(Extensible Markup Language)

XML可扩展标记语言,编写XML就是编写标签,与HTML非常类似,扩展名.xml。有良好的人机可读性

hr.xml
<employee>
 <name>张三</name>
 <age>31</age>
 <height>178</height>
</employee>
  • XML与HTML非常相似,都是编写标签
  • XML没有预定义标签,HTML存在大量预定义标签
  • XML重在保存与传输数据,HTML用于显示信息

XML用途

① 程序中有各种设置项提取出来存放在XML文件中,利用XML进行程序的设计

web.xml - web应用配置文件
<web-app>
 <servlet>
  <servlet-name>InitTest</servlet-name>
  <servlet-class>moreservlets.InitServlet</servlet-class>
   <init-param>
    <param-name>param1</param-name>
    <param-value>value1</param-value>
   </init-param>
   <init-param>
    <param-name>param2</param-name>
    <param-value>2</param-value>
   </init-param>
 </servlet>
</web-app>

② 用于保存程序产生的数据 [简单的变成可以把数据库导成xml]

hr.xml
<employee>
 <name>张三</name>
 <age>31</age>
 <height>178</height>
 <salary>7800</salary>
</employee>

③ 网络间的数据传输

webservice底层soap协议
<Envelop>
<Body>
<m:reversexmlns:m="urn:strings-com:IString">
<theString>Hello,World</theString>
</m:reversexmlns:m>
</Body>
</Envelop>

XML文档结构

  • 第一行必须是XML声明

    XML声明说明XML文档的基本信息,包括版本号与字符集,写在XML第一行

    <?xml version="1.0" encoding="UTF-8"?>
    version代表版本号1.0
    encoding UTF-8设置字符集,用于支持中文
    <!-- 人力资源管理系统 -->
    <hr>
      <employee no="3309">
        <name>张三</name>
        <age>31</age>
        <salary>4000</salary>
        <department>
          <dname>会计部</dname>
          <addresss>XX大厦-B103</addresss>
        </department>
      </employee>
    
      <employee no="3310">
        <name>李四</name>
        <age>23</age>
        <salary>4000</salary>
        <department>
          <dname>工程部</dname>
          <addresss>XX大厦-B104</addresss>
        </department>
      </employee>
    </hr>
    
  • 有且只有一个根节点

  • XML标签的书写规则与HTML相同

合法的标签名

标签名要有意义
建议使用英文,小写字母,单词之间使用”-“分割

适当的注释与缩进

适当的注释与缩进可以让XML文档更容易阅读

合理使用属性

标签属性用于描述标签不可或缺的信息
对标签分组或者为标签设置Id时常用属性表示

<shop-cart>
<item sn="771938" category="电器">
 <name>XXX空调</name>
 <price>2000.00</price>
 <num>1</num>
</item>
<item sn="890321" category="食品">
 <name>法式面包</name>
 <price>10.00</price>
 <num>5</num>
</item>
</shop-cart>
特殊字符与CDATA标签

标签体中,出现”<”、”>”特殊字符,会破坏文档结构

错误的XML
<question> 1+4<3是否正确?</question>

解决方案①:使用实体引用

实体引用 对应符号 说明
& It; < 小于
& gt; > 大于
& amp; & 和号
& apos; 单引号
& quot; 双引号
修改后的XML
<question> 1+4&lt;3是否正确?</question>

解决方案②:使用CDATA标签
CDATA指的是不应由XML解析器进行解析的文本数据
从”< ![CDATA[“开始,到”]] >“结束

<lesson>
<content>
 <![CDATA[
 本节我们来学习html中a标签的使用:
 <body>
  <a href="index.html">首页</a>
 </body>
 ]]>
</content>
</lesson>
有序的子元素

在XML多层嵌套的子元素中,标签前后顺序应保持一致
<…> </…>

XML语义约束之DTD

XML文档结构正确,但可能不是有效的

  • 例如,员工档案XML中绝不允许出现 “植物品种” 标签。XML语义约束就是用于规定XML文档中允许出现哪些元素
  • XML语义约束由两种定义方式:DTDXML Schema

DTD(Document Type Definition, 文档类型定义) 是一种简单易用的语义约束方式
DTD文件的扩展名为.dtd [用于说明HTML中拥有哪些节点可以出现]

hr.dtd
<!ELEMENT hr (employee+)>
<!ELEMENT employee (name,age,salary,department)>
<!ATTLIST employee no CDATA "">
<!ELEMENT name (#PCDATA)>
...
定于hr节点下只允许出现1个employee子节点
<!ELEMENT hr (employee)>

employee节点下必须包含以下四个节点,且按顺序出现
<!ELEMENT employee (name,age,salary,department)
    
定义name标签体只能是文本,#PCDATA代表文本元素
<!ELEMENT name (#PCDATA)>
DTD定义节点数量
  • 如果某个子节点需要多个重复出现,则需要在子节点后增加相应的描述符
    [后面带个+号 最少出现一个]
hr节点下最少出现1个emploee子节点    [+]
<!ELEMENT hr (employee+)>

hr节点下可出现0..n个employee子节点  [*]
<!ELEMENT hr (employee*)>

hr节点下最多出现1个emploee子节点    [?]
<!ELEMENT hr (employee?)>
XML引用DTD文件
  • 在XML中使用**< !DOCTYPE >**标签来引用DTD文件
书写格式:
<!DOCTYPE 根节点 SYSTEM "dtd文件路径">
示例:
<!DOCTYPE hr SYSTEM "hr.dtd">
案例 [XML像是描述 dtd像是约束 ]
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE hr SYSTEM "hr.dtd">
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
<?xml version="1.0" encoding="UTF-8" ?>
  <!ELEMENT hr (employee+)>
  <!ELEMENT employee (name,age,salary,department)>
  <!--前后顺序必须匹配 一一对应-->
  <!ATTLIST employee no CDATA "">
  <!ELEMENT name (#PCDATA)>
  <!ELEMENT age (#PCDATA)>
  <!ELEMENT salary (#PCDATA)>
  <!ELEMENT department (dname,address)>
  <!ELEMENT dname (#PCDATA)>
  <!ELEMENT address (#PCDATA)>
  <!--里面是纯文本节点 department有两个子节点-->

XML Schema(比dtd更高级)

  • XML Schema比DTD更为复杂,提供了多个功能
  • XML Schema提供了数据类型、格式限定、数据范围等特性
  • XML Schema是W3C标准
<?xml version="1.0" encoding="UTF-8" ?>
<hr xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="hr.xsd">
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
  <element name="hr">
<!--complexType标签含义是复杂节点,包含子节点时必须使用这个标签-->
    <complexType>
      <sequence>
        <!-- 节点最少出现一次  -->
        <element name="employee" minOccurs="1">
          <complexType>
            <sequence>
              <element name="name" type="string"></element>
              <element name="age">
                <simpleType>
                  <restriction base="integer"> <!--给年龄做限定-->
                    <minInclusive value="18"></minInclusive>
                    <maxInclusive value="60"></maxInclusive>
                  </restriction>
                </simpleType>
              </element>
              <element name="department">
                <complexType>
                  <sequence>
                    <element name="dname" type="string"></element>
                    <element name="address" type="string"></element>
                  </sequence>
                </complexType>
              </element>
            </sequence>
          </complexType>
        </element>
      </sequence>
      <!--no在任何employee节点下必须存在-->
      <attribute name="no" type="string" use="required"></attribute>
    </complexType>
  </element>
</schema>

DOM文档对象模型

DOM(Document Object Model)定义了访问和操作XML文件的标准方法,DOM把XML文档作为树结构来看,能够通过DOM树来读写所有元素。

Dom4j[以java的形式解析xml]

Dom4j是一个易用的、开源的库,用于解析XML。它应用于Java平台,具有性能优异、功能强大和极其易用的特点。

  • Dom4j将XML视为Document对象
  • XML标签被Dom4j定义为Element对象
Dom4j对xml的解析读取和遍历

[原则是按照documents根节点和子节点依次类推的顺序对其进行分析、解析、提取]

<?xml version="1.0" encoding="UTF-8" ?>
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>4000</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.util.List;

public class HrReader{
  public void readXml() throws DocumentException {
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    //SAXReader类是读取XML文件的核心类,用于将XML解析后
      SAXReader reader = new SAXReader();
      Document document = reader.read(file);
      //获取XML文档的根节点,即hr标签
      Element root = document.getRootElement();
      //elements方法用于获取指定的标签集合
      List<Element> employees = root.elements("employee");
      for (Element employee : employees){
       /* //element方法用于获取唯一的子节点对象
        Element name = employee.element("name");
        //getText()方法用于获取标签文本
        String empName = name.getText();
        System.out.println(empName);
        */
        System.out.println(employee.elementText("age"));
        System.out.println(employee.elementText("salary"));
        Element department = employee.element("department");
        System.out.println(department.elementText("dname"));
        System.out.println(department.element("address").getText());
        Attribute att = employee.attribute("no"); /*获取对应文本*/
        System.out.println(att.getText());
        System.out.println("====================");
        }
      }

  public static void main(String[] args) throws DocumentException {
    HrReader reader = new HrReader();
    reader.readXml();
  }
}
Dom4j更新(写入)XML
已追加写入的信息
<hr>
 <employee no="3311">
    <name>李铁柱</name>
    <age>28</age>
    <salary>3600</salary>
    <department>
      <dname>人事部</dname>
      <address>XX大厦-B105</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

public class HrWriter {
  public void weiteXml(){
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    SAXReader reader = new SAXReader();
    try {
      Document document = reader.read(file);
      Element root = document.getRootElement();
      //创建employee子节点 全新空子节点
      Element employee = root.addElement("employee");
      employee.addAttribute("no","3311");
      Element name = employee.addElement("name");
      name.setText("李铁柱");
      employee.addElement("age").setText("28");
      employee.addElement("salary").setText("3600");
      Element department = employee.addElement("department");
      department.addElement("dname").setText("人事部");
      department.addElement("address").setText("XX大厦-B105");
      //内存中组织的dom模型重新写入到对应文件中
      Writer writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
      document.write(writer);
      writer.close();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    HrWriter hrWriter = new HrWriter();
    hrWriter.weiteXml();
  }
}

XPath路径表达式

  • XPath路径表达式是XML文档中查找数据的语言
  • 掌握XPath可以极大的提高在读取数据时的开发效率
  • 学习XPath本质就是掌握各种形式表达式的使用技巧
最常用的基本表达式
表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
路径表达式 结果
bookstore 选取bookstore元素的所有子节点
/bookstore 选取根元素bookstore
注释:假如路径起始于正斜杠(/), 则此路径始终代表到某元素的绝对路径
bookstore/book 选取属于bookstore的子元素的所有book元素
//book 选取所有book子元素,而不管它们在文档中的位置
bookstore//book 选取属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么位置
//@lang 选取名为lang的所有属性
XPath谓语表达式
路径表达式 结果
/bookstore/book[1] 选取属于bookstore子元素的第一个book元素
/bookstore/book[last()] 选取属于bookstore子元素的最后一个book元素
/bookstore/book[position()<3] 选取最前面的两个属于bookstore元素的子元素的book元素
//title[@lang] 选取所有拥有名为lang的属性的title元素
/bookstore/book[price>35.00] 选取bookstore元素的所有book元素,且其中的price元素的值须大于35.00
/bookstore/book[price>35.00]/title 选取bookstore元素中的book元素的所有title元素,且其中的price元素的值必须大于35.00

XPath实验室 [优先使用 查询数据]

Jaxen介绍
  • Jaxen是一个Java编写的开源的XPath库。这是适合多种不同的对象模型,包括DOM,XOM,dom4j和JDOM
  • Dom4j底层一来Jaxen实现XPath查询
<?xml version="1.0" encoding="UTF-8"?>
<!-- 人力资源管理系统 -->
<hr>
  <employee no="3309">
    <name>张三</name>
    <age>31</age>
    <salary>4000</salary>
    <department>
      <dname>会计部</dname>
      <address>XX大厦-B103</address>
    </department>
  </employee>

  <employee no="3310">
    <name>李四</name>
    <age>23</age>
    <salary>3200</salary>
    <department>
      <dname>工程部</dname>
      <address>XX大厦-B104</address>
    </department>
  </employee>

  <employee no="3311">
    <name>李铁柱</name>
    <age>28</age>
    <salary>3600</salary>
    <department>
      <dname>人事部</dname>
      <address>XX大厦-B105</address>
    </department>
  </employee>
</hr>
package src;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.util.List;

public class XPathTestor {
  public void xpath(String xpathExp){
    String file = "D:\\JetBrains\\IdeaProjects\\Test_html\\src\\hr.xml";
    SAXReader reader = new SAXReader();
    try {
      Document document = reader.read(file);
      //执行xpath表达式 Node不仅查询标签还可以查询属性
      List<Node> nodes = document.selectNodes(xpathExp);
      for (Node node : nodes){
        //转换成常用的Element对象 父类node强转
        Element emp = (Element)node;
        System.out.println(emp.attribute("no"));
        System.out.println(emp.elementText("name"));
        System.out.println(emp.elementText("age"));
        System.out.println(emp.elementText("salary"));
        System.out.println("===========================");
      }
    } catch (DocumentException e) {
      throw new RuntimeException(e);
    }
  }

  public static void main(String[] args) {
    XPathTestor testor = new XPathTestor();
    //单斜杠要按照根目录一级一级搜索
//    testor.xpath("/hr/employee");
    //双斜杠 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置
//    testor.xpath("//employee");
//    testor.xpath("//employee[salary<4000]");
//    testor.xpath("//employee[name='李铁柱']");
//    testor.xpath("//employee[@no=3309]");
//    testor.xpath("//employee[1]");
//    testor.xpath("//employee[last()]");
//    testor.xpath("//employee[position()<6]"); 当前位置小于6
//    testor.xpath("//employee[1] | //employee[3]");
  }
}
阅读全文

Java复习款练习题

2023/8/22

Collection集合习题

练习:Collection集合统计元素出现次数

给定以下代码,请定义方法listTest()统计集合中指定元素出现的次数,如”a”: 2,”b”: 2,”c” :1, “xxx”:0

   public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("a");
        list.add("a");
        list.add("b");
        list.add("b");
        list.add("c");
        System.out.println("a:"+listTest(list, "a"));
        System.out.println("b:"+listTest(list, "b"));
        System.out.println("c:"+listTest(list, "c"));
        System.out.println("xxx:"+listTest(list, "xxx"));
    }
    private static int listTest(Collection<String> list, String s){
        int count = 0;
        for (String string : list){
            if (s.equals(string)){
                count++;
            }
        }
        return count;
    }

练习:Collection集合数组转集合

定义一个方法,要求此方法把int数组转成存有相同元素的集合(集合里面的元素是Integer),并返回。

    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5,6};
        ArrayList<Integer> arrayList = listTest(arr);
        System.out.println(arrayList);
    }
    public static ArrayList<Integer> listTest(int[] arr){
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (Integer a : arr){
            list.add(a);
        }
        return list;
    }

练习:Collection集合集合转数组

定义一个集合,并把集合(集合里面的元素是Integer)转成存有相同元素的数组,并将结果输出在控制台。(可以使用Object[]数组类型接收转换的数组)

    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(100);
        list.add(200);
        list.add(300);
        Object[] obj = list.toArray();
        for (int i = 0; i < obj.length; i++) {
            System.out.println(obj[i]);
        }
    }

public Object[] toArray()

以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。 因此,调用者可以自由地修改返回的数组。

练习:Collection集合contains()方法使用

定义一个方法listTest(ArrayList < String > a1, String s),要求使用contains()方法判断a1集合里面是否包含s。

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("itcast");
        list.add("itheima");
        list.add("java");
        System.out.println(listTest(list,"Java"));
    }

    private static boolean listTest(ArrayList<String> a1, String s){
        if (a1.contains(s)){
            return true;
        }
        return false;
    }

练习:Collection集合isEmpty()方法的使用

定义一个方法listTest(ArrayList< String > a1), 要求使用isEmpty()判断a1里面是否有元素。

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("1");
        System.out.println(listTest(list));
    }
    public static boolean listTest(ArrayList<String>a1){
        if (a1.isEmpty()){
            return true;
        }
        return false;
    }

练习:简述迭代器的实现原理

当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。

Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,在调用Iterator的next()方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。

练习:Collection集合返回首次出现索引

定义一个方法listTest(ArrayList< Integer > a1, Integer s),要求返回s在a1里面第一次出现的索引,如果s没出现过返回-1。

    public static void main(String[] args) {
        //定义集合,添加数据
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        System.out.println(listTest(list,5));
    }
    private static int listTest(ArrayList<Integer>a1, Integer s){
        for (int i = 0; i < a1.size(); i++) {
            if (a1.get(i).equals(s)){
                return i;
            }
        }
        return -1;
    }

File类&递归&FileFilter习题

练习:检查文件是否存在,文件的创建

描述:检查D盘下是否存在文件a.txt,如果不存在则创建该文件。

操作步骤:
1.使用绝对路径创建对象关联到D盘的a.txt。
2.通过文件对象方法判断文件是否存在。
3.不存在则调用创建文件的方法创建文件。

    public static void main(String[] args) throws IOException {
        File f = new File("D:\\Clash\\aaa.txt");
        if (!f.exists()){
            f.createNewFile();
        }
    }

练习:单极文件夹的创建

描述:在D盘下创建一个名为bbb的文件夹。

操作步骤:
1.创建文件对象指定路径为d:/bbb
2.调用文件对象创建文件夹的方法

    public static void main(String[] args) throws IOException {
        File f = new File("D:\\Clash\\aaa");
        // File f = new File("D:\\Clash\\aaa\\bbb");
        f.mkdir();
        // f.mkdirs(); 创建多级文件夹
    }

练习:删除文件和文件夹

描述:将D盘下a.txt文件删除。将D盘下aaa文件夹删除,要求文件夹aaa是一个空文件夹。

操作步骤:

1.创建文件对象关联路径:d:/a.txt
2.调用文件对象删除文件的方法
3.创建文件对象关联路径:d:/aaa
4 调用文件对象删除文件夹的方法.

    public static void main(String[] args) {
        // 创建文件对象
        File f = new File("d:/a.txt");
        // 删除文件
        f.delete();
        
        // 创建文件夹对象
        File dir = new File("d:/aaa");
        // 删除文件夹
        dir.delete();
    }

获取文件信息:文件名,文件大小,文件的绝对路径,文件的父路径

描述:获取D盘aaa文件夹中b.txt文件的文件名,文件大小,文件的绝对路径和父路径等信息,并将信息输出在控制台。

操作步骤:

    public static void main(String[] args) {
        // 创建文件对象
        File f = new File("D:\\Clash\\a.txt");
        // 获得文件名
        String filename = f.getName();
        // 获得文件大小
        long filesize = f.length();
        // 获得文件的绝对路径
        String path = f.getAbsolutePath();
        // 获得父文件夹路径,返回字符串
        String parentPath = f.getParent();
        // 获得父文件夹路径,返回文件对象
        File parentFile = f.getParentFile();
        // 输出信息
        System.out.println("文件名:" + filename);
        System.out.println("文件大小:" + filesize);
        System.out.println("文件路径:" + path);
        System.out.println("文件父路径:" + parentPath);
        System.out.println("文件父路径:" + parentFile);
    }

练习:文件夹或文件的判断

描述:

1.判断File对象是否是文件,是文件则输出:xxx是一个文件,否则输出:xxx不是一个文件。
2.判断File对象是否是文件夹,是文件夹则输出:xxx是一个文件夹,否则输出:xxx不是一个文件夹。(xxx是文件名或文件夹名)

操作步骤:

1.创建两个文件对象分别关联到不同的文件,比如:d:/a.txt,d:/aaa
2.调用文件对象的判断是否是文件或是否是文件夹的方法
3.获得文件名,根据判断结果输出信息。

    public static void main(String[] args) {
       File f = new File("D:\\Clash\\a.txt");
       if (f.isFile()){
           System.out.println(f.getName() + "是文件");
       }else {
           System.out.println(f.getName() + "不是文件");
       }

       File f2 = new File("D:\\Clash");
       if (f2.isDirectory()){
           System.out.println(f2.getName() + "是文件夹");
       }else{
           System.out.println(f2.getName() + "不是文件夹");
       }
    }

练习:文件夹的获取方式

描述:

获取指定文件夹下所有的文件,并将所有文件的名字输出到控制台。
注意:不包含子文件夹下的文件

操作步骤:

1.创建文件对象关联到指定文件夹,比如:c:/aaa
2.调用文件对象的listFiles方法获得文件数组
3.遍历文件数组将每一个文件的名字输出到控制台

    public static void main(String[] args) {
        File f = new File("D:\\Clash");
        File[] files = f.listFiles();
        for (File file : files){
            System.out.println(file.getName());
        }
    }

List集合&Set集合习题

练习:List接口的特点

简述List接口的特点

★ 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的读取顺序按照11、22、33的顺序完成的
★ 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)
★ 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素

练习:hashCode和equals方法

请简述HashSet去除重复元素的原理

★ 调用被添加元素的hashCode(), 和HashSet中已有元素的hasCode比较是否相同
★ 如果不同,直接存储
★ 如果相同,调用equals方法比较是否相同
★ 不相同,直接存储元素
★ 相同,认为是同一元素,不存储

练习:数据结构

简述常见的数据结构中元素的存储特点

★ 栈:stack,又称堆栈,对元素的存取特点是先进后出。即,存进去的元素,要在后它后面的元素一次取出后,才能取出该元素
★ 队列:queue,简称队,对元素的存取特点是先进先出。即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素
★ 数组:Array,是有序的元素序列,对元素的存储特点是:
1,查找元素快:通过索引,可以快速访问指定位置的元素
2.增删元素慢
(1).指定索引位置增加元素:需要创建一个新数组,将指定元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置
(2).指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中
★ 链表:linkedlist 对元素的存取有如下的特点:
**(1).**多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的 右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
**(2).**查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素。
(3).增删元素快:
增加元素:只需要修改连接下个元素的地址即可。
删除元素:只需要修改连接下个元素的地址即可。

练习:Comparable和Comparator比较器

简述 Comparable Comparator 两个接口的区别

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能再类中实现compareTo()一次,不能经常修改的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sortArrays.sort 从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

练习:LinkedList方法的使用

根据要求练习LinkedList方法:

(1).基本方法:add, set, get, remove, clear, size等方法;
(2).特有方法:addFirst, addLast, getFirst, getLast, removeFirst, removeLast, push, pop, clear等方法。

(1).基本方法
public static void main(String[] args) {
        // 1.创建LinkedList
        LinkedList<String> arr = new LinkedList<String>();

        // 2.使用add方法添加元素
        arr.add("西门吹雪");
        arr.add("西门吹雪");
        arr.add("西门吹雪");
        arr.add("西门吹风");
        arr.add("西门吹水");

        // 3.使用add方法在指定索引添加元素
        arr.add(2, "西门吹雨");

        // 4.使用set方法修改指定位置索引
        arr.set(0, "东门");

        for (String str : arr) {
            System.out.println(str);
        }
        System.out.println("--------------");
        // 5.使用get方法获取指定索引的元素
        System.out.println(arr.get(1));

        // 6.使用size方法获取集合大小
        System.out.println(arr.size());

        // 7.使用remove方法删除指定索引的元素
        arr.remove(3);

        // 8.使用clear清空集合中的元素
        arr.clear();
        System.out.println(arr);
    }
}
(2).特有方法
public static void main(String[] args) {
        // 1.创建LinkedList
        LinkedList<String> linked = new LinkedList<String>();

        // 2.使用add方法添加元素
        linked.add("周杰伦");
        linked.add("周星驰");
        linked.add("周华健");
        linked.add("周润发");

        // 3.使用addFirst添加元素到集合最前面
        linked.addFirst("周传雄");

        // 4.使用addLast添加元素到集合最后面
        linked.addLast("周渝民");

        System.out.println(linked);

        // 5.使用getFirst获取集合第一个元素
        System.out.println(linked.getFirst());

        // 6.使用getLast获取集合最后一个元素
        System.out.println(linked.getLast());

        // 7.使用removeLast删除集合第一个元素
        String first = linked.removeFirst();
        System.out.println(first);

        // 8.使用removeLast删除集合最后一个元素
        String last = linked.removeLast();
        System.out.println(last);
        System.out.println(linked);


        // 9.使用pop弹出第一个元素
        String p = linked.pop();
        System.out.println(p);

        // 10.使用push在集合开头插入元素
        linked.push("周立波");
        System.out.println(linked);

        // 11.使用clear清空集合
        linked.clear();
        System.out.println(linked);
    }
}

练习:HashSet存储自定义类型

定义人类,包含姓名和年龄属性。创建4个人存储到HashSet中,姓名和年龄相同的人看做同一人不存储。

// 1.定义Person类.包好姓名年龄属性,重写hashCode()和equals()方法
public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
ublic class HashSetTest01 {
    public static void main(String[] args) {
        // 2.创建HashSet用于存储Person类型
        HashSet<Person> hashSet = new HashSet<Person>();

        // 3.添加多个Person到HashSet中
        hashSet.add(new Person("王昭君", 21));
        hashSet.add(new Person("西施", 21));
        hashSet.add(new Person("杨玉环", 20));
        hashSet.add(new Person("貂蝉", 19));
        hashSet.add(new Person("杨玉环", 20));
        hashSet.add(new Person("貂蝉", 19));

        // 4.遍历获取HashSet中的内容
        for (Person p : hashSet) {
            System.out.println(p);
        }
    }
}

练习:List集合元素替换

向list集合添加姓名{张三,李四,王五,二丫,钱六,孙七}, 将二丫替换为王小丫

public class ListTest01 {
    public static void main(String[] args) {
        //1.创建List集合对象
        List<String> list = new ArrayList<>();
        //2.存入数据
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("二丫");
        list.add("钱六");
        list.add("孙七");
        //3.遍历集合,找到"二丫",便将其替换为"王小丫"
        //利用普通for循环遍历List集合
        for(int i = 0;i<list.size();i++) {
            //获取当前元素
            String thisName = list.get(i);
            //如果当前元素是"二丫"
            if("二丫".equals(thisName)) {
                //将其改为"王小丫"
                list.set(i, "王小丫");
            }
        }
        System.out.println(list);
    }
}

//   使用增强for获取LinkedHashSet中的元素
        for (String str : list){
           if ("二丫".equals(str)){
                System.out.println(list);
           }
        }

练习:LinkedHashSet基本使用

使用LinkedHashSet存储以下元素:”王昭君”,”王昭君”,”西施”,”杨玉环”,”貂蝉”。使用迭代器和增强for循环遍历LinkedHashSet。

public class LinkedHashSetTest01 {
    public static void main(String[] args) {
        // 1.创建LinkedHashSet
        LinkedHashSet<String> lhSet = new LinkedHashSet<String>();
        // 2.使用add方法添加元素到LinkedHashSet
        lhSet.add("王昭君");
        lhSet.add("王昭君");
        lhSet.add("王昭君");
        lhSet.add("西施");
        lhSet.add("杨玉环");
        lhSet.add("貂蝉");
        // 3.使用迭代器获取LinkedHashSet中的元素
        Iterator<String> iterator = lhSet.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        // 4.使用增强for获取LinkedHashSet中的元素
        System.out.println("---------------------");
        for (String string : lhSet) {
            System.out.println(string);
        }
    }
}

练习:Collections工具类使用

ArrayList集合中有如下内容: {33,11,77,55},使用Collections.sort()对ArrayList集合中的数据进行排序,并打印出排序后的结果

public class CollectionsTest01 {
    public static void main(String[] args) {
        // 1.创建ArrayList
        ArrayList<Integer> arr = new ArrayList<Integer>();

        // 2.使用add方法添加{33,11,77,55}四个元素
        arr.add(33);
        arr.add(11);
        arr.add(77);
        arr.add(55);

        // 3.调用Collections的sort方法,对集合排序
        Collections.sort(arr);

        // 4.使用增强for遍历ArrayList集合
        for (Integer integer : arr) {
            System.out.println(integer);
        }
    }
}

Map集合习题

练习:Map接口的特点

请简述Map 的特点

★ Map每个元素由键与值两部分组成
★ Map键不能重复,每个键对应一个值
★ 键和值可以为null

练习:Entry键值对对象

说出Entry键值对对象遍历Map集合的原理

Map中存放的是两种对象,一种称为Key(键),一种称为value(值),它们在Map中是一一对应关系,这一种对象又称做Map中的一个Entry(项)。Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每个键值对(Entry)对象中获取对应的键与对应的值。

练习:Map接口中的常用方法

请使用Map集合的方法完成添加元素,根据键删除,以及根据键获取值操作。
    public static void main(String[] args) {
        HashMap<String, String> hm = new HashMap<String, String>();
        hm.put("黄晓明", "Baby");
        hm.put("邓超", "孙俪");
        hm.put("李晨", "范冰冰");
        hm.put("大黑牛", "范冰冰");
        String v1 = hm.put("李晨", "白百合");
        String string = hm.get("大黑牛");
        String v2 = hm.remove("大黑牛");
        System.out.println(v2);
        System.out.println(hm);
    }
//  范冰冰  {邓超=孙俪, 李晨=白百合, 黄晓明=Baby}

练习:Map接口中的方法

往一个Map集合中添加若干元素。获取Map中的所有value,并使用增强for和迭代器遍历输出每个value。

public static void main(String[] args) {
        // 1.创建HashMap
        HashMap<String, String> hm = new HashMap<String, String>();

        // 2.使用put添加元素
        hm.put("黄晓明", "Baby");
        hm.put("邓超", "孙俪");
        hm.put("李晨", "范冰冰");
        hm.put("大黑牛", "范冰冰");

        // 3.使用Map的values方法获取到所有的value
        Collection<String> values = hm.values();

        // 4.使用增强for获取每个value
        for (String value : values) {
            System.out.println(value);
        }

        System.out.println("----------------");
        // 5.使用迭代器获取每个value
        Iterator<String> itr = values.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }
    }

练习:HashMap存储键是自定义对象是String

请使用Map集合存储自定义数据类型Car做键,对应的价格做值。并使用keySet和entrySet两种方式遍历Map集合。

        // 1.定义汽车类.包含名称和价格属性,重写hashCode和equals方法
    public class Car {
        private String name;
        private String color;
        ...
    }
---------------------------------------------------
    public static void main(String[] args) {
         // 2.创建HashMapkey保存汽车对象,value是汽车价格
        HashMap<Car, Integer> hm = new HashMap<>();

        // 3.添加汽车到HashMap中
        Car c1 = new Car("长安奔奔", "黄色");
        Car c3 = new Car("奇瑞QQ", "黑色");
        Car c2 = new Car("铃木奥拓", "白色");

        hm.put(c1, 10000);
        hm.put(c2, 20000);
        hm.put(c3, 30000);

         // 4.使用keySet方式遍历Map
        Set<Car> keySet = hm.keySet();
        for (Car c : keySet) {
        // 根据key获取value
            Integer value = hm.get(c);
            System.out.println(c.getName() + "," + c.getColor() + " - " + value);
        }

        System.out.println("-------------");

        // 5.使用entrySet方式遍历Map
        Set<Map.Entry<Car, Integer>> entrySet = hm.entrySet();
        for (Map.Entry<Car, Integer> entry : entrySet) {
            Car key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key.getName() + "," + key.getColor() + " - " + value);
        }
    }

public Set<Map.Entry<K,V>> entrySet()
返回此地图中包含的映射的Set视图。 该集合由地图支持,因此对地图的更改将反映在集合中,反之亦然。 如果在集合中的迭代正在进行时修改映射(除了通过迭代器自己的remove操作,或者通过迭代器返回的映射条目上的setValue操作),迭代的结果是未定义的。 该组支持元件移除,即从映射中相应的映射,经由Iterator.removeSet.removeremoveAllretainAllclear操作。 它不支持addaddAll操作。

练习:Map集合的使用(一)

现在有一个map集合如下:
     Map<Integer,String> map = new HashMap<Integer, String>();
     map.put(1, "张三丰");
     map.put(2, "周芷若");
     map.put(3, "汪峰");
     map.put(4, "灭绝师太");

要求:

1.遍历集合,并将序号与对应人名打印。
2.向该map集合中插入一个编码为5姓名为李晓红的信息
3.移除该map中的编号为1的信息
4.将map集合中编号为2的姓名信息修改为”周林”

    public static void main(String[] args) {
        // 1.定义HashMap,编号作为key,姓名作为value
        Map<Integer, String> map = new HashMap<Integer, String>();
        // 2.使用put方法添加元素
        map.put(1, "张三丰");
        map.put(2, "周芷若");
        map.put(3, "汪峰");
        map.put(4, "灭绝师太");
        // 3.使用keySet+增强for迭代map中的元素,并打印
        Set<Integer> keySet = map.keySet();
        for (Integer key : keySet) {
            String value = map.get(key);
            System.out.println(key + " -- " + value);
        }
        // 4.使用put向该map集合中插入一个编码为5姓名为李晓红的信息
        map.put(5, "李晓红");
        // 5.使用remove移除该map中的编号为1的信息
        map.remove(1);
        // 6.使用put将map集合中编号为2的姓名信息修改为"周林"
        map.put(2, "周林");
        System.out.println(map);
    }

练习:Map集合的使用(二)

有2个数组
第一个数组内容为:[黑龙江省,浙江省,江西省,广东省,福建省],
第二个数组为:[哈尔滨,杭州,南昌,广州,福州],将第一个数组元素作为key
第二个数组元素作为value存储到Map集合中。如{黑龙江省=哈尔滨, 浙江省=杭州, …}

    public static void main(String[] args) {
        // 1.定义第一个数组arr1
        String[] arr1 = {"黑龙江省", "浙江省", "江西省", "广东省", "福建省"};
        // 2.定义第二个数组arr2
        String[] arr2 = {"哈尔滨", "杭州", "南昌", "广州", "福州"};

        // 3.创建HashMap,key存放省,value存放市
        HashMap<String, String> hm = new HashMap<>();

        // 4.使用普通for循环遍历arr1
        for (int i = 0; i < arr1.length; i++) {
        // 5.根据索引到arr1中获取到省
            String key = arr1[i];
        // 6.根据索引到arr2中获取到省会城市
            String value = arr2[i];

        // 7.将省和省会城市添加到HashMap中
            hm.put(key, value);
        }
        // 8.输出HashMap中的内容
        System.out.println(hm);
    }

Math类习题

练习:实现字符串123反转

使用字符数组保存原始字符,利用Random类生成随机索引。

        public class Test1 {
            public static void main(String[] args) {
                Scanner scanner = new Scanner(System.in);
                String next = scanner.next();
                System.out.println("录入的字符串:" + next);
                String s = reverseStr(next);
                System.out.println("反转的字符串:"+ s);
            }
        
            public static String reverseStr(String  str){
                String s = "";
                char[] chars = str.toCharArray();
                for (int i = chars.length - 1; i >= 0; i--) {
                    s +=chars[i] ;
                }
                return s;
            }
        }

toCharArray() 方法将字符串转换为字符数组

练习:键盘录入QQ号判断正确性

必须是5-12位数字,0不能开头

public class Test1 {
    public static void main(String[] args) {
        //1.键盘输入一个qq号码字符串
        Scanner sc = new Scanner(System.in);
        String qq = sc.next();
        //2.调用checkQQ (String qq)方法内实现验证。
        boolean isOK = checkQQ(qq);
        //3.打印验证的结果
        System.out.println("这个QQ号码是否正确:" + isOK);
    }

    /*
     * 定义方法:checkQQ (String qq)方法内实现验证
     * 指定方法的名称:checkQQ
     * 指定方法的参数:String qq
     * 指定方法的返回值:boolean
     */
    public static boolean checkQQ(String qq) {
        //1.验证字符串的长度5-12位之间;
        if (qq.length() < 5 || qq.length() > 12) {
            return false; //说明qq号码的长度不正确
        }
        //2.验证首位字符不能是字符0;只能是字符'1'--'9'
        if (qq.charAt(0) == '0') {
            return false;
        }
        //3.验证字符串中的每个字符都必须是数字字符‘0’-‘9’之间的字符
        for (int i = 0; i < qq.length(); i++) {
            char ch = qq.charAt(i);
            //判断字符是否在 数字字符‘0’-‘9’之间的字符
            if (ch < '0' || ch > '9') {
                return false;//说明qq号码中含有非数字字符
            }
        }
        //4.上述验证都通过了,说明qq号码是正确的
        return true;
    }
 }

练习:大小写字符转换并统计次数

键盘录入一个大字符串,再录入一个小字符串。统计小字符串在大字符串中出现的次数。

   /*
* 分析以下需求,并用代码实现
1.键盘录入一个大字符串,再录入一个小字符串
2.统计小字符串在大字符串中出现的次数
3.代码运行打印格式:
请输入大字符串:woaiheima,heimabutongyubaima,wulunheimahaishibaima,zhaodaogongzuojiushihaoma
请输入小字符串:heima
控制台输出:小字符串heima,在大字符串woaiheima,heimabutongyubaima,wulunheimahaishibaima,zhaodaogongzuojiushihaoma中共出现3次
             */

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        // 1.键盘录入一个大字符串,再录入一个小字符串
        System.out.print("请输入大字符串:");
        String big = sc.nextLine();
        System.out.print("请输入小字符串:");
        String small = sc.nextLine();
        // 2.统计小字符串在大字符串中出现的次数
        int count = getCount(big, small);
        // 3.代码运行打印格式:
        System.out.println("小字符串" + small + ",在大字符串中共出现" + count + "次");
    }

    /*
     * 方法功能:统计小字符串在大字符串中出现的次数
     * 参数:big 代表大字符串
     * 参数:small 代表小字符串
     * 返回值:小字符串在大字符串中出现的次数
     */
    public static int getCount(String big, String small) {
        int index = 0;
        int count = 0;
        /*
         * indexOf(String str, int fromIndex)
         * 该方法作用:从fromIndex位置开始查找,字符串str第一次出现的位置;若没找到,放回-1
         */
        while ((index = big.indexOf(small, index)) != -1) {
            index++;
            count++;
        }
        return count;
    }

练习:随机小数保留两位

生成一个随机100内小数,转换为保留两位小数的字符串,不考虑四舍五入的问题。

    public static void main(String[] args) {
        double random = Math.random()*100;
        System.out.println("随机数为:");
        System.out.println(random);
        String str = random + " ";
        int index = str.indexOf(".");
        String substring = str.substring(0, index + 3);
        System.out.println("转换为");
        System.out.println(substring);
    }
// substring(int strat, int end)中第一个参数是开始位置,第二个参数是结束位置.

练习:筛选字符串

定义ArrayList集合,存入多个字符串。长度大于5的字符串,打印删除后的集合。

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("bca");
        list.add("dadfa");
        list.add("dddaaa");
        list.add("你好啊");
        list.add("我来啦,你干嘛呢");
        list.add("别跑啊");
        System.out.println("源字符串:");
        System.out.println(list);
        delStrsFromList01(list);
    }
    private static void delStrsFromList01(ArrayList<String> list){
        ArrayList<String> list2 = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            String str = list.get(i);
            if (str.length() > 3){
                list2.add(str);
            }
        }
        for (Object str : list2){
            list.remove(str);
        }
        System.out.println("新字符:" + list);
    }

练习:回文字符串

判断回文字符串。如果一个字符串,从前向后读和从后向前读,都是一个字符串,称为回文串,比如mom,dad,noon。

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String next = scanner.next();
        boolean p = isP(next);
        System.out.println(
                "回文数:" + p
        );
    }

    public static boolean isP(String str) {
        int start = 0;
        int end = str.length() - 1;
        while (start < end) {
            if (str.charAt(start) != str.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。

练习:模拟简单计算器

模拟简单计算器,可以运算+,—,*,/,%。

  • 接收三个参数,一个整数,一个运算符,另一个整数。( ‘5’ ‘+’ ‘7’ )
  • 计算出运算结果。
  • 无法运算时,返回null。
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int a = scanner.nextInt();
        String next = scanner.next();
        int b = scanner.nextInt();
        String count = count(a, next, b);
        System.out.println(a +next +b +"="+count);
    }

    public static String count(int a, String op , int b ){
        int r=0;
        if ("+".equals(op)){
            r = a+b;
        }else  if ("-".equals(op)){
            r = a-b;
        }else  if ("*".equals(op)){
            r = a*b;
        }else  if ("/".equals(op)){
            r = a/b;
        }else  if ("%".equals(op)){
            r = a%b;
        }else {
            return null;
        }
        return r+"";
    }

练习:密码是否合法

校验密码是否合法。合法返回true

  • 必须至少8个字符。
  • 必须至少2个大写字符。超越两个就要依次遍历 for循环
  • 必须只有字母和数字。
    public static void main(String[] args) {
        String s = "qweRY123";
        System.out.println(s+"密码是否合法"+isTrue(s));
    }
    private static boolean isTrue(String s){
        if (s.length()<8){
            return false;
        }

        int countA = 0;
        char[] chars = s.toCharArray();
        for (int i = 0; i < s.length(); i++) {
            char ch = chars[i];
            //2个大写字母
            if (ch >= 'A' && ch <= 'Z'){
                countA++;
            }
            //字母数字
            if ((ch < '0'|| ch>'9') && (ch < 'A'|| ch>'Z')&&(ch < 'a'|| ch>'z')) {
                return false;
            }
        }
        if (countA < 2){
            return false;
        }
        return true;
    }

练习:模拟用户登录

模拟用户登录。

  • 定义用户类,属性为用户名和密码。
  • 使用集合存储多个用户对象。
  • 录入用户和密码,对比用户信息,匹配成功登录成功,否则登录失败。
  • 登录失败时,当用户名错误,提示没有该用户。
  • 登录失败时,当密码错误时,提示密码有误。

jack-1234 rose-5678 tom-0000
请输入用户名:rose
请输入密码:5678
登录结果:登录成功

public class User {
    private String username;
    private String pwd;
}
------------------------------------------
public class Test {
    static ArrayList<User> list = new ArrayList<>();
    static {
        list.add(new User("jack", "1234"));
        list.add(new User("rose", "5678"));
        list.add(new User("tom", "0000"));
        for (int i = 0; i < list.size(); i++) {
            list.get(i).show();
        }
    }
    public static void main(String[] args) {
        
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        User u = new User(username, password);
        String login = login(u);
        System.out.println("登录结果:" + login);
    }
    public static String login(User user) {
        String msg = "";
        String n = user.getUsername();
        String p = user.getPwd();
        for (int i = 0; i < list.size(); i++) {
            User u = list.get(i);
            String name = u.getUsername();
            String pwd = u.getPwd();
            if (name.equals(n)){
                if (pwd.equals(p)){
                    return "登录成功";
                }else {
                    return "密码错误";
                }
            }else {
                msg = "用户名不存在";
                continue;
                }
            }
        return msg;
        }
    }

static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。[ 在此代码内适用于login方法中的list.size() ]
static代码块只在类加载时执行,类是用类加载器来读取的,类加载器是带有一个缓存区的,
它会把读取到的类缓存起来,所以在一次虚拟机运行期间,一个类只会被加载一次,这样的话静态代码块只会运行一次

Object类&Date类&Calender(日期)类&StringBuilder类

练习:简述String和Object中的equals

简述String类中的equals方法 与 Object类中的equals方法的不同点

String类中的equals方法是用来判断两个对象的内容是否相同,而Object 类中的equals方法是用来判断两个对象是否是同一个对象,所谓同一个对象指的是内存中的同一块存储空间。

练习:Object类的toString方法

    public class ToStringTest{
        static int i = 1;
        public static void main(String args[]){
            System.out.println("love " + new ToStringTest());//love java
            ToStringTest a = new ToStringTest();
            a.i++;
            System.out.println("me " + a.i);//me 2
        }
        public String toString(){
            System.out.print("I ");//I
            return "java ";
        }
    }

运行结果:I love java me 2
原因:当执行代码的时候,首先加载静态变量,然后执行main方法,由于main方法内部第一行代码为输出语句,里面new了此类对象,当执行此行代码时会先创建了本类的对象,由于此类重写了toString方法,会先执行toString方法的打印输出,然后返回“java ”,再执行main方法第一行打印输出。在Java中“System.out.println(类对象名);”实际输出的是该对象的toString()方法返回的字符串,即括号中的内容等价于类对象名.toString(),toString方法的好处是在碰到println方法的时候会被自动调用,不用显示的写出来。

练习:Object类equals方法

看下列程序,不运行说结果,写出答案后,并在IntelliJ IDEA中运行看看自己给的答案与运行结果是否正确,并分析原因。

    (1)
        String s1 = new String("abc");
        String s2 = "abc";
        System.out.println(s1 == s2);         //false
        System.out.println(s1.equals(s2));  //true
    (2)
        String s1 = "abc";
              String s2 = "abc";
        System.out.println(s1 == s2);         //true
        System.out.println(s1.equals(s2));     //true
    (3)
        String s1 = "a" + "b" + "c";
              String s2 = "abc";
        System.out.println(s1 == s2);        //true
        System.out.println(s1.equals(s2));     //true
    (4)
        String s1 = "ab";
             String s2 = "abc";
             String s3 = s1 + "c";
        System.out.println(s3 == s2);             //false
              System.out.println(s3.equals(s2));  //true

练习:StringBuilder类与String类的区别

简述StringBuilder类与String类的区别

String类的对象内容不可改变,所以每当进行字符串拼接时,总是会在内存中创建一个新的对象,所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对系统性能产生影响

StringBuilder又称为可变字符序列,是JDK5.0中新增加的一个类,它是一个类似于String的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。即它是一个容器,容器中可以装很多字符串,并且能够对其中的字符串进行各种操作。它的内部拥有一个数组用来存放字符串内容,进行字符串拼接时,直接在数组中加入新内容,StringBuilder会自动维护数组的扩容

练习:Date类的使用

获取当前的日期, 并把这个日期转换为指定格式的字符串, 如2088-08-08 08:08:08

    public static void main(String[] args) {
        //获取当前日期对象 now;
        Date now = new Date();
        //创建SimpleDateFormat对象 df,并制定日期格式
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //调用df的format(Date  date) 方法,传入now; 接收返回的字符串
        String datestr = df.format(now);
        //打印这个字符串
        System.out.println(datestr);
    }

练习:DateFormat类方法的使用

使用SimpleDateFormat类,把2018-03-04转换为2018年03月04日

    public static void main(String[] args) throws ParseException {
        //创建SimpleDateFormat对象df1,指定日期模式为yyyy-MM-dd
        SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
        //调用df1的parse(String str)方法传入2018-03-04,得到对应日期类型
        Date date = df1.parse("2018-03-04");
        //创建日期格式化对象df2,在获取格式化对象时可以指定风格
        DateFormat df2 = new SimpleDateFormat("yyyy年MM月dd日");
        //调用df2的format(Date date) 传入刚才转换的日期
        String str = df2.format(date);
        System.out.println(str);
    }

public Date parse(String text, ParsePosition pos)
从字符串中解析文本,产生一个Date

练习:Calendar类方法的使用

用程序判断2018年2月14日是星期几。

public static void main(String[] args) {
        //创建Calendar对象
        Calendar c = Calendar.getInstance();
        //将给定的日历字段设置到Calendar对象中
        c.set(Calendar.YEAR, 2018);
        c.set(Calendar.MONTH, 1);
        c.set(Calendar.DATE, 14);
        //设置年
        int year = c.get(Calendar.YEAR);
        //设置月
        int month = c.get(Calendar.MONTH)+1;
        //设置日
        int date = c.get(Calendar.DATE);
        //设置星期
        char week = getWeek(c.get(Calendar.DAY_OF_WEEK));
        //输出结果
        System.out.println(year+"年"+month+"月"+date+"日是星期"+week);
    }
    //定义方法,获取星期汉字
    public static char getWeek(int a){
        char[] c = {' ','日','一','二','三','四','五','六'};
        return c[a];
    }
}

Random类&ArrayList集合习题

练习:随机验证码

  • 随机生成十组六位字符组成的验证码。
  • 验证码由大小写字母、数字字符组成。

开发提示:使用字符数组保存原始字符,利用Random类生成随机索引。

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            String s = verifyCode();
            System.out.println("随机验证码:" + s);
        }
    }
    public static String verifyCode(){
        char[] arr = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
        Random random = new Random();
        String code = "";
        for (int i = 0; i < 6; i++) {
            int index = random.nextInt(arr.length);
            code  += arr[index];
        }
        return code;
    }

练习:输入学生信息保存到集合

键盘录入学生信息,保存到集合中。

  • 循环录入的方式,1:表示继续录入,0:表示结束录入。
  • 定义学生类,属性为姓名,年龄,使用学生对象保存录入数据。
  • 使用ArrayList集合,保存学生对象,录入结束后,遍历集合。
public class Student {
    private String name;
    private int age;
     public void show(){
        System.out.println("姓名: "+ name + " " + "年龄: " + age);
    }
}
----------------------------------
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            ArrayList<Student> list = new ArrayList<>();
            while (true) {
                System.out.println("1.录入信息 0.退出");
                int i = scanner.nextInt();
                switch (i) {
                    case 1:
                        inputStu(list , scanner);
                        break;
                    case 0:
                        System.out.println("录入完毕");
                }
                if (i == 0){
                    break;
                }
            }
    
            for (int i = 0; i < list.size(); i++) {
                Student student = list.get(i);
                student.show();
            }
        }
    
        private static void inputStu(ArrayList<Student> list , Scanner sc) {
            System.out.println("请输入姓名:");
            String name = sc.next();
            System.out.println("请输入年龄:");
            int age = sc.nextInt();
            Student student = new Student(name, age);
            list.add(student);
        }
    }

练习:随机数 次数打印

统计数字出现次数。

  • 定义getNumList方法,随机生成100个数字,数字范围从1到10。
  • 定义printCount方法,统计每个数字出现的次数并打印到控制台。
    public class Test4 {
        public static void main(String[] args) {
            ArrayList<Integer> numList = getNumList();
            //  统计字符数组中字母出现次数
            printCount(numList);
        }
        public static void printCount(ArrayList<Integer> list) {
            int[] count = new int[10];
            // 对应保存数字出现的次数
            for (int i = 0; i < list.size(); i++) {
                int c = list.get(i);
                count[c-1]++;
            }
            // 打印数字和次数
            for (int i = 0 ; i < count.length; i++) {
                    System.out.println("数字:"+(i+1) + "--" + count[i]+"次");
            }
        }
        public static ArrayList<Integer> getNumList() {
            ArrayList<Integer> list = new ArrayList<>();
            Random r = new Random();
            for (int i = 0; i < 100; i++) {
                int x = r.nextInt(10) + 1;
                list.add(x);
            }
            return list;
        }
    }    

练习:需求实现

模拟统计班级考试分数分布情况,分别统计100-80,79-60,59-40,39-0各个阶段的人数。

  • 定义getScoreList方法,随机生成50个数字,数字范围从0到100。
  • 定义countScore方法,统计各个阶段的分数个数。
  • 定义printCount方法,打印各个阶段的统计结果。
    public static void main(String[] args) {
        ArrayList<Integer> scoreList = getScoreList(); //获取随机分数
        ArrayList<Integer> countList = countScore(scoreList); //定义计数的变量
        printCount(countList);
    }
    public static ArrayList<Integer> countScore(ArrayList<Integer> scoreList) {
        ArrayList<Integer> countList = new ArrayList<>();
        int count100 = 0;
        int count79 = 0;
        int count59 = 0;
        int count39 = 0;

        for (int i = 0; i < scoreList.size(); i++) {
            Integer score = scoreList.get(i);
            if (score <= 100 && score >= 80) {
                count100++;
            } else if (score <= 79 && score >= 60) {
                count79++;
            } else if (score <= 59 && score >= 40) {
                count59++;
            } else {
                count39++;
            }
        }

        countList.add(count100);
        countList.add(count79);
        countList.add(count59);
        countList.add(count39);

        return countList;
    }

    private static ArrayList<Integer> getScoreList(){
        ArrayList<Integer> list = new ArrayList<>();
        Random r = new Random();
        for (int i = 0; i < 50; i++) {
            int x = r.nextInt(100);
            list.add(x);
        }
        return list;
    }

    private static void printCount(ArrayList<Integer> countList) {
        int start = 100;
        int end = 80;
        for (int i = 0; i < countList.size(); i++) {
            Integer integer = countList.get(i);
            System.out.println(start + "\t分 --" + end + " \t分:" + integer+"人");
            if (i == 0){
                start-=21;
                end -=20;
            }else if (i == countList.size()-2){
                start-=20;
                end-=40;
            }else {
                start -= 20;
                end -= 20;
            }
        }
    }

练习:添加移除展示元素

自定义MyList类,实现存取元素的功能。

  • 定义add方法,可以保存元素,添加MyList尾部。
  • 定义remove方法,可以获取到最后添加的元素,并从MyList中移除该元素。
  • 定义show方法,可以展示MyList中的元素。
public static void main(String[] args) {
        MyList myList = new MyList();
        for (int i = 0; i < 3; i++) {
            myList.add(i);
        }
        System.out.println("添加元素后:");
        myList.show();

        Integer remove = myList.remove();
        System.out.println("获取元素:");
        System.out.println(remove);
        System.out.println("获取元素后:");
        myList.show();
    }
}

class MyList {
    ArrayList<Integer> ml = new ArrayList<>();

    public void add(Integer i) {
        ml.add(i);
    }

    public Integer remove() {
        Integer remove = ml.remove(ml.size() - 1);
        return remove;
    }

    public void show() {
        System.out.println(ml);
    }
}

线程&同步习题

练习:多线程开启

请描述Thread类中的start()方法与run()方法的区别

线程对象调用run()方法不开启线程,仅是对象调用方法。
线程对象调用start()方法开启线程,并让jvm调用run()方法在开启的线程中执行。

练习:创建多线程

请描述创建线程的两种方法
  • 将类声明为Thread的子类

①.定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
②.创建Thread子类的实例,即创建了线程对象
③.调用线程对象的start()方法来启动该线程

  • 声明一个类实现Runnable接口

①.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
②,创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,Thread对象才是真正的线程对象
③.调用线程对象的start()方法来启动线程

练习:多线程

请编写程序,分别打印主线程的名称和子线程的名称

要求使用两种方式实现:
第一种方式:继承Thread类。
第二种方法:实现Runnable接口。

操作步骤描述

  • 继承Thread类

①.定义一个子线程的类,继承Thread类
②.在子线程类中重写run方法,在run方法中打印子线程的名称
③.定义一个测试类
④.在main方法中打印主线程的名称
⑤.在main方法中创建子线程对象
⑥.调用子线程对象的start方法,开启子线程

// 1.定义一个子线程的类,继承Thread类;
public class SubThread extends Thread{
// 2.在子线程类中重写run方法,在run方法中打印子线程的名称;
    public void run(){
    // 打印子线程的名称
      System.out.println("subThread:" + Thread.currentThread().getName());
    }
}
// 3.定义一个测试类
public class ThreadDemo{
    public static void main(String[] args){
// 4.在main方法中打印主线程的名称;
    System.out.println("main:" + Thread.currentThread().getName());
// 5.在main方法中创建子线程对象;
    SubThread st = new SubThread();
// 6.调用子线程对象的start方法,开启子线程。
    st.start();
    }
}
  • 实现Runnable接口

①.定义一个子任务类,实现Runnable接口
②.在子任务中重写run方法,在run方法中打印子线程的名称
③.定义一个测试类
④.在main方法中打印主线程的名称;
⑤.在main方法中创建一个子任务对象;
⑥.在main方法中创建一个Thread类的对象,并把子任务对象传递给Thread类的构造方法;
⑦.调用Thread类对象的start方法开启子线程;

// 1.定义一个子任务类,实现Runnable接口。
public class SubRunnable implements Runnable{
    @Override
    public void run() {
// 2.在子任务类中重写run方法,在run方法中打印子线程的名称。
    System.out.println("SubRunnable:"+ Thread.currentThread().getName());
// 3.定义一个测试类。
   }
}
public class RunnableDemo {
    public static void main(String[] args) {
// 4.在main方法中打印主线程的名称。
    System.out.println("RunnableDemo:"+ Thread.currentThread().getName());
// 5.在main方法中创建一个子任务对象。
    SubRunnable r = new SubRunnable();
// 6.在main方法中创建一个Thread类的对象,并把子任务对象传递给Thread类的                         构造方法。
    Thread t = new Thread(r);
// 7.调用Thread类对象的start方法开启子线程。
    t.start();
 }
}

练习:实现Runnable接口的优势

请描述实现Runnable接口比继承Thread类所具有的优势:

①.适合多个相同的程序代码的线程去共享同一个资源
②.可以避免java中的单继承的局限性
③.增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和数据独立。
④.线程池只能放入实现Runnable或callable类线程,不能直接放入继承Thread的类

练习:多线程

创建多线程对象,开启多线程。在子线程中输出1-100之间的偶数,主线程输出1-100之间的奇数

自定义线程类

public class MyThread extends Thread {
    /**
     * 重写run方法,完成该线程执行的逻辑
     */
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println("子线程打印输出偶数:" + i);
            }
        }
    }
    public class Test11 {
        public static void main(String[] args) {
            //创建自定义线程对象
            MyThread mt = new MyThread();
            //开启线程
            mt.start();
            //在主方法中执行for循环
            for (int i = 1; i <= 100; i++) { 
                if (i % 2 == 1) {
                    System.out.println("主线程打印输出奇数:" + i);
                }
            }
        }
    }

练习:线程状态

请描述在线程的生命周期中, 有几种状态呢 ?

1.NEW(新建) 线程刚被创建,但是并未启动。

2.Runnable(可运行)
线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。

3.Blocked(锁阻塞)
当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。

4.Waiting(无限等待)
一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。

5.Timed Waiting(计时等待)
同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。

6.Teminated(被终止)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

线程池&lambda表达式习题

练习:线程池概念

请描述什么是线程池

线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复用户创建线程而消耗过多的资源

练习:线程池优点

请描述合理利用线程池能够带来的三个好处

1,降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可以执行多个任务
2.提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行
3.提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

练习:Lambda表达式

请列举Lambda语法的省略规则

在Lambda标准格式的基础上,使用省略句写法的规则为:
1.小括号内参数的类型可以省略
2.如果小括号内有且只有一个参数,则小括号可以省略
3.如果大括号内有且只有一个语句,则无论是否有返回值,都可以省略大括号、return、关键字以及语句分号

练习:Lambda表达式

请列举Lambda表达式的3个组成部分,并解释说明

Lambda标准格式Lambda省去面向对象的条条框框,格式由3个部分组成:一些参数、一个箭头、一段代码
Lambda表达式的标准格式
1.小括号内的语法与传统方法参数列表一致:无参数则留空,多个参数则用逗号分割
2.”—>”是新引入的语法格式,代表指向动作
3.大括号内的语法与传统方法体要求基本一致

练习:Lambda表达式

请描述Lambda的使用前提

Lambda的语法非常简介,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:
1.使用Lambda必须具有接口,且要求接口中有且只有一个抽象方法。无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才能使用Lambda。
2.使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例

练习:多线程

代码实现打印输出1-99
public class Test06 {
    public int start = 1;
    public int end = 99;

public static void main(String[] args) {
    new Test06().method();
 }

public void method() {
    //插入代码处
         Runnable a = () -> {
      for (int i = start; i <end; i++) {
        System.out.println(i);
      }
    };
      Thread t = new Thread(a);
      t.start();
     }
}

练习:多线程

请问该程序的运行结果是什么? 如有问题,请说明原因。
public class Test07implements Runnable {
    public static void main(String[] args) {
      Thread t = new Thread(new Test07());
      t.start();
      }

public void run(int num) {
    for (int i = 0; i < num; i++) {
       System.out.println(i);
    }
  }
}

在编译时期就会报错

​ Test类没有重写Runnable接口中的run()方法

​ public void run(int num)不是Runnable接口中的run()方法。

注意:Runnable接口中的run()方法,参数列表为空,不带参数。

练习:线程池练习

使用线程池创建多线程。模拟同学找老师学习Java。

1.创建线程池对象,包含2个线程。从线程池中获取线程对象,然后调用MyRunnable中的run()。
2.在MyRunnable实现类中,首先在控制台打印需求,“我需要一个老师”。模拟需要2秒钟时间老师可以过来指导学生,并在控制台打印老师的姓名。最后,在控制台打印“教我java,教完后,老师回到了办公室”;

class Test implements Runnable{
    @Override
    public void run() {
        System.out.println("我要一个老师");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("老师来了" + Thread.currentThread().getName());
        System.out.println("教我Java,教完后,老师回到了办公室");
    }
}

class ThreadPoolDemo{
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        Test r = new Test();
        service.submit(r);//线程1
        service.submit(r);//线程2
        service.submit(r);//线程3
    }
}

练习:Lambda(无参无返回)

给定一个导演 Director接口,内含唯一的抽象方法makeMovie,且无参数、无返回值,使用lambda表达式在Test中完成调用。

interface Director{
    void makeMovie();
}
public class Test {
    public static void main(String[] args) {
        // TODO 请使用Lambda【省略格式】调用invokeDirect方法
        invokeDirect(()-> System.out.println("拍电影啦"));
    }
    private static void invokeDirect(Director director){
        director.makeMovie();
    }
}

练习:Lambda(有参有返回)

给定一个计算器 Calculator 接口,内含抽象方法 calc (减法),其功能是可以将两个数字进行相减,并返回差值。使用Lambda表达式在Test中完成调用

interface Calculator{
    int calc(int a, int b);
}
public class Test {
    public static void main(String[] args) {
        // TODO 请分别使用Lambda【标准格式】及【省略格式】调用invokeCalc方法来计算130-120的结果ß
        invokeCalc(130, 120, (int a, int b) -> {
            return a - b;
        }); //标准格式
        invokeCalc(130,120,(a,b)-> a-b); //省略模式
    }
    private static void invokeCalc(int a, int b, Calculator calculator){
        int result = calculator.calc(a,b);
        System.out.println("结果是:" + result);
    }
}

Stream流

练习一:Pedicate接口使用

请在测试类main方法中完成以下需求

已知有Integer[] arr = {-12345, 9999, 520, 0,-38,-7758520,941213}

a) 使用lambda表达式创建Predicate对象p1,p1能判断整数是否是自然数(大于等于0)

b) 使用lambda表达式创建Predicate对象p2,p2能判断整数的绝对值是否大于100

c) 使用lambda表达式创建Predicate对象p3,p3能判断整数是否是偶数

遍历arr,仅利用已创建的Predicate对象(不使用任何逻辑运算符),完成以下需求

​ i. 打印自然数的个数

​ ii. 打印负整数的个数

​ iii. 打印绝对值大于100的偶数的个数

​ iv. 打印是负整数或偶数的数的个数

public static void main(String[] args) {
        Integer[] arr = {-12345, 9999, 520, 0,-38,-7758520,941213};

        //a)   使用lambda表达式创建Predicate对象p1,p1能判断整数是否是自然数
        Predicate<Integer> p1 = (s) -> s>=0;
        //b)   使用lambda表达式创建Predicate对象p2,p2能判断整数的绝对值是否大于100
        Predicate<Integer> p2 = (s) -> Math.abs(s)>100;
        //c)   使用lambda表达式创建Predicate对象p3,p3能判断整数是否是偶数
        Predicate<Integer> p3 = (s) -> s%2==0;

        //e)   遍历arr,仅利用已创建的Predicate对象(不使用任何逻辑运算符),完成以下需求
        int count1 = 0;
        int count2 = 0;
        int count3 = 0;
        int count4 = 0;
        for (Integer i : arr) {
            //统计自然数个数
            if (p1.test(i)){
                count1++;
            }
            //统计负整数个数
            if (p1.negate().test(i)){
                count2++;
            }
            //统计绝对值大于100的偶数个数
            if (p2.and(p3).test(i)){
                count3++;
            }
            //统计是负整数或偶数的数的个数
            if (p1.negate().or(p3).test(i)){
                count4++;
            }
        }
        //分别打印结果
        System.out.println("自然数的个数为:"+count1);
        System.out.println("负整数的个数为:"+count2);
        System.out.println("绝对值大于100的偶数的个数为:"+count3);
        System.out.println("是负整数或偶数的数的个数为:"+count4);
    }

练习:Function接口使用

[这是一个功能界面,因此可以用Lambda表达式或方法引用的赋值对象]

Interface Function<T,R>
T:函数输入的类型
R:函数结果的类型

Interface Map<K,V>
K:由此地图维护的键的类型
V:映射值的类型

1.使用lambda表达式分别将以下功能封装到Function对象中

a) 求Integer类型ArrayList中所有元素的平均数

b) 将Map < String,Integer > 中value存到ArrayList < Integer >中

2.已知学生成绩如下

姓名 成绩
岑小村 59
谷天洛 82
渣渣辉 98
蓝小月 65
皮几万 70

3.以学生姓名为key成绩为value创建集合并存储数据,使用刚刚创建的Function对象求学生的平均成绩

    public static void main(String[] args) {
        //1.   使用lambda表达式分别将以下功能封装到Function对象中
        //a)   求Integer类型ArrayList中所有元素的平均数
        Function<ArrayList<Integer>,Integer> f1 = (list)->{
            Integer sum = 0;
            for (Integer i : list) {
                sum+=i;
            }
            return sum/list.size();
        };

        //b)   将Map<String,Integer>中value存到ArrayList<Integer>中
        Function<Map<String,Integer>,ArrayList<Integer>> f2 = (map)->{
            /*ArrayList<Integer> list = new ArrayList<>();
            for (String s : map.keySet()) {
                Integer i = map.get(s);
                list.add(i);
            }*/
            Collection<Integer> values = map.values();
            ArrayList<Integer> list = new ArrayList<>();
            list.addAll(values);
            return list;
        };
        //2 将学生姓名和成绩封装到map中
        Map<String,Integer> map = new HashMap<String, Integer>();
        map.put("岑小村", 59);
        map.put("谷天洛", 82);
        map.put("渣渣辉", 98);
        map.put("蓝小月", 65);
        map.put("皮几万", 70);

        //利用Function求平均成绩
        Integer avg = f2.andThen(f1).apply(map);
        System.out.println("学生平均成绩为:"+avg);
    }

练习:获取流

简述单列集合、双列集合、数组分别如何获取Stream流对象,并进行演示

1.java.util.Collection接口中加入了default方法 stream()获取流对象,因此其所有实现类均可通过此方式获取流

2.java.util.Map接口想要获取流,先通过KeySet()、values()或entrySet()方法获取键、值或键值对的单列集合,再通过stream()获取流对象

3.数组获取流,使用Stream接口中的静态方法of(T...values)获取流
public static void main(String[] args) {
  List<String> list = new ArrayList<>();
  Stream<String> stream1 = list.stream();
  Set<String> set = new HashSet<>();
  Map<String, String> map = new HashMap<>();

  Stream<String> stream2 = set.stream();
  Stream<String> keyStream = map.keySet().stream();
  Stream<String> valueStream = map.values().stream();
  Stream<Map.Entry<String,String>>entryStream = map.entrySet().stream();

  String[] array = {"东邪", "西毒", "南帝", "北丐", "中神通"};
  Stream<String> stream = Stream.of(array);
}

练习:过滤[filter]、结果收集(数组)

有如下7个元素黄药师,冯蘅,郭靖,黄蓉,郭芙,郭襄,郭破虏,使用Stream将以郭字开头的元素存入新数组

public class Test {
    public static void main(String[] args) {
          Stream<String> stream = Stream.of("黄药师", "冯蘅", "郭靖", "黄蓉", "郭芙", "郭襄", "郭破虏");
        String[] guos = stream.filter(s -> s.startsWith("郭")).toArray(String[]::new);
     }
}
// filter 返回由与此给定谓词匹配的此流的元素组成的流。
// toArray 返回一个包含此流的元素的数组 结果: 一个包含此流的元素的数组

练习:n 取用前几个[limit]、跳过前几个[skip]

已知ArrayList集合中有如下元素{陈玄风、梅超风、陆乘风、曲灵风、武眠风、冯默风、罗玉风},使用Stream
1,取出前2个元素并在控制台打印输出。
2.取出后2个元素并在控制台打印输出。

import java.util.ArrayList;
    public class Test04 {
      public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("陈玄风");
        list.add("梅超风");
        list.add("陆乘风");
        list.add("曲灵风");
        list.add("武眠风");
        list.add("冯默风");
        list.add("罗玉风");

        list.stream().limit(2).forEach(System.out::println);
        list.stream().skip(list.size() - 2).forEach(System.out::println);
    }
}

练习:n 映射[map]、逐一消费[forEach]

有如下整数1,-2,-3,4,-5
使用Stream取元素绝对值并打印

import java.util.stream.Stream;
   public class Test {
      public static void main(String[] args) {
          Stream<Integer> stream = Stream.of(1, -2, -3, 4,-5);
        stream.map(Math::abs).forEach(System.out::println);
     }
}

练习:组合[concat]、结果收集[list]

已知数组arr1中有如下元素{郭靖,杨康},arr2中有如下元素{黄蓉,穆念慈},使用Stream将二者合并到List集合

import java.util.stream.Stream;
  public class Test {
    public static void main(String[] args) {
          Stream<String> streamA = Stream.of("郭靖", "杨康");
        Stream<String> streamB = Stream.of("黄蓉", "穆念慈");
        List<String> strList = Stream.concat(streamA, streamB).collect(Collectors.toList());
     }
}

练习:获取并发流

请分别写出获取并发流的两种方式。

public class Test {
    public static void main(String[] args) {
          Collection<String> coll = new ArrayList<>();
          Stream<String> parallelStream1 = coll.parallelStream();
        Stream<Integer> parallelStream2 = Stream.of(100, 200, 300, 400).parallel();
     }
}

函数式接口习题

练习:函数式接口

①. 定义一个函数式接口CurrentTimePrinter,其中抽象方法void printCurrentTime(),使用注解@FunctionalInterface
②. 在测试类中定义static void showLongTime(CurrentTimePrinter timePrinter),该方法的预期行为是使用timePrinter打印系统当前毫秒值
③. 测试showLongTime(),通过lambda表达式完成需求

@FunctionalInterface
public interface CurrentTimePrinter
{
    void printCurrenTime();
}

public class Test01 {
    public static void main(String[] args) {
        showLongTime(()->System.out.println(System.currentTimeMillis()));
    }

    public static void showLongTime(CurrentTimePrinter timePrinter){
        timePrinter.printCurrentTime();
    }
}

练习:函数式接口

①. 定义一个函数式接口IntCalc,其中抽象方法int calc(int a , int b),使用注解@FunctionalInterface
②. 在测试类中定义static void getProduct(int a , int b ,IntCalc calc), 该方法的预期行为是使用calc得到a和b的乘积并打印结果
③. 测试getProduct(),通过lambda表达式完成需求

IntCalc接口:

@FunctionalInterface
 public interface IntCalc {
   int calc(int a, int b);
 }

测试类:

public class Test02 {
   public static void main(String[] args) {
     getProduct(2,3,(a,b)->a*b);
   }
   public static void getProduct(int a, int b, IntCalc intCalc){
     int product = intCalc.calc(a,b);
     System.out.println(product);

   }
}

练习:静态方法引用

①. 定义一个函数式接口NumberToString,其中抽象方法String convert(int num),使用注解@FunctionalInterface
②. 在测试类中定义static void decToHex(int num ,NumberToString nts), 该方法的预期行为是使用nts将一个十进制整数转换成十六进制表示的字符串,**tips:已知该行为与Integer类中的toHexString方法一致**
③. 测试decToHex (),使用方法引用完成需求

interface NumberToString{
    String convert(int num);
}

public class Test {
    public static void main(String[] args) {
        decToHex(999, Integer::toHexString);
    }

    public static void decToHex(int num ,NumberToString nts){
        String convert = nts.convert(num);
        System.out.println(convert);
    }

}

字节流&字符流&Properties集合习题

练习:字节输出流写出字节数据

利用字节输出流一次写一个字节的方式,向D盘的a.txt文件输出字符‘a’

操作步骤:

1.创建字节输出流FileOutputStream对象并指定文件路径。
2.调用字节输出流的write(int byte)方法写出数据

public class Test01_01 {
    public static void main(String[] args) throws IOException {
        // 1.创建字节输出流FileOutputStream对象并指定文件路径。
        FileOutputStream fos = new FileOutputStream("d:/a.txt");
        // 2.调用字节输出流的write(byte[] buf)方法写出数据。
        byte[] buf = "i love java".getBytes();
        // 2.调用字节输出流的write(int byte)方法写出数据
        fos.write(97);
        // 3.关闭流
        fos.close();
    }
}

练习:文件的续写和换行输出

在D盘下,有一c.txt 文件中内容为:HelloWorld
在c.txt文件原内容基础上,添加五句 I love java,而且要实现一句一行操作(注:原文不可覆盖)。利用字节输出流对象往C盘下c.txt文件输出5句:”i love java”

操作步骤:

1.利用两个参数的构造方法创建字节输出流对象,参数一指定文件路径,参数二指定为true
2.调用字节输出流的write()方法写入数据,在每一行后面加上换行符:”\r\n”

public class Test01_03 {
    public static void main(String[] args) throws IOException{
        // 1.创建字节输出流FileOutputStream对象并指定文件路径,并追加方式
        FileOutputStream fos = new FileOutputStream("c:/c.txt",true);
        // 2.调用字节输出流的write方法写出数据
        // 2.1 要输出的字符串
        String content = "i love java \r\n";
        for (int i = 0; i< 5; i++) {
            fos.write(content.getBytes());
        }
        // 3.关闭流
        fos.close();
    }
}
/*
    定义变量接收读取的字节
        int len = -1;
        // 循环从流中读取数据
        while((len = fis.read()) != -1) {
        System.out.print(new String(buffer,0,len));
}

*/

练习:字节流复制文件

描述: 利用字节流将E盘下的a.png图片复制到D盘下(文件名保存一致)

要求:一次读写一个字节的方式

操作步骤:

1.创建字节输入流对象关联文件路径:E盘下的a.png
2.创建字节输出流对象关联文件路径:D盘下的a.png
3.使用循环不断从字节输入流读取一个字节,每读取一个字节就利用输出流写出一个字节。
4.关闭流,释放资源

public class Test01_06 {
    public static void main(String[] args) throws IOException {
        // 创建字节输入流对象并关联文件
        FileInputStream fis = new FileInputStream("e:/a.png");
        // 创建字节输出流对象并关联文件
        FileOutputStream fos = new FileOutputStream("d:/a.png");
        // 定义变量接收读取的字节数
        int len = -1;
        // 循环读取图片数据
        while((len = fis.read()) != -1) {
            // 每读取一个字节的数据就写出到目标文件中
            fos.write(len);
        }
        // 关闭流
        fis.close();
        fos.close();
    }

练习:IO对象Properties结合使用,设置properties文件

我有一个文本文件score.txt,我知道数据是键值对形式的,但是不知道内容是什么。
请写一个程序判断是否有”lisi”这样的键存在,如果有就改变其实为”100”
score.txt文件内容如下:
zhangsan = 90 lisi = 80 wangwu = 85
操作步骤:
1.创建一个空的Properties集合
2.读取数据到集合中
3.遍历集合,获取到每一个key
4.判断当前的key 是否为 “lisi”,如果是就把”lisi”的值设置为100
5.把集合中所有的信息,重新存储到文件中

void store(OutputStream out, String comments)
此适合使用load(InputStream)方法加载到Properties表中的格式,将此Propeirties表中的属性列表(键和元素对写入输出流)
public class Test02_06 {
    public static void main(String[] args) throws IOException {
//1:创建一个空的集合
        Properties prop = new Properties();
//2:读取数据到集合中
        prop.load(new FileInputStream("score.txt"));
//3:遍历集合,获取到每一个key
        Set<String> keys = prop.stringPropertyNames();
//获取到每一个key
        for (String key : keys) {
//4:判断当前的key 是否为 "lisi"
            if ("lisi".equals(key)) {
//把"lisi"的值设置为100
                prop.setProperty(key, "100");
            }
        }
//把集合中所有的信息,重新存储到文件中
        prop.store(new FileOutputStream("score.txt"), "haha");
    }
}

缓冲流&转换流习题

练习:高效字节输出流写出字节数据

描述: 利用高效字节输出流往C盘下的d.txt文件输出一个字节数。

操作步骤:

1.创建字节输出流对象关联文件路径
2.利用字节输出流对象创建高效字节输出流对象
3.调用高效字节输出流对象的write方法写出一个字节
4.关闭高效流,释放资源。

public class Test01_01 {
    public static void main(String[] args) throws IOException {
        // 创建字节输出流FileOutputStream对象并指定文件路径。
        FileOutputStream fos = new FileOutputStream("c:\\d.txt");
        // 利用字节输出流创建高效字节输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 调用高效字节输出流对象的write(int byte)方法写出一个字节数据
        bos.write(97);
        // 关闭流
        bos.close();
    }
}

练习:高效字节输出流写出字节数组数据

描述: 利用高效字节输出流往C盘下的e.txt文件写出一个字节数组数据,如写出:”i love java”

操作步骤:

1.创建字节输出流对象关联文件路径
2.利用字节输出流对象创建高效字节输出流对象
3.定义字符串存放要输出的数据,然后将字符串转换为字节数组。
4.调用高效字节输出流对象的write方法将字节数组输出。
5.关闭高效流。

public class Test01_02 {
    public static void main(String[] args) throws IOException {
        // 创建字节输出流FileOutputStream对象并指定文件路径。
        FileOutputStream fos = new FileOutputStream("c:\\e.txt");
        // 利用字节输出流创建高效字节输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 调用高效字节输出流对象的write(byte[] buff)方法写出一个字节数据
        bos.write("i love java".getBytes());
        // 关闭流
        bos.close();
    }
}

练习:高效流文件复制

描述: 利用高效字节输入流和高效字节输出流完成文件的复制。

要求:

1.将C盘下的c.png文件复制到D盘下
2.一次读写一个字节数组方式复制

操作步骤:

①.创建字节输入流对象并关联文件路径
②.利用字节输入流对象创建高效字节输入流对象
③.创建字节输出流对象并关联文件路径
④.利用字节输出流对象创建高效字节输出流对象
⑤.创建字节数组用来存放读取的字节数
⑥.利用高效字节输入流循环读取文件数据,每读取一个字节数组,利用高效字节输出流对象将字节数组的内容输出到目标文件中。直到读取到文件末尾。
⑦.关闭高效流对象

public class Test01_03 {
    public static void main(String[] args) throws IOException{
        // 创建字节输入流对象并关联文件路径
        FileInputStream fis = new FileInputStream("c:\\c.png");
        // 利用字节输出流对象创建高效字节输出流对象
        BufferedInputStream bis = new BufferedInputStream(fis);
        // 创建字节输出流对象并指定文件路径。
        FileOutputStream fos = new FileOutputStream("d:\\c.png");
        // 利用字节输出流创建高效字节输出流对象
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 定义字节数组接收读取的字节
        byte[] buffer = new byte[1024];
        // 定义变量接收读取的字节数
        int len = -1;
        // 循环读取图片数据
        while((len = bis.read(buffer)) != -1) {
            // 每读取一个字节的数据就写出到目标文件中
            bos.write(buffer,0,len);
        }
        // 关闭流
        bis.close();
        bos.close();
    }
}

练习:高效字符流和集合的综合使用

描述:

分析以下需求,并用代码实现
实现一个验证码小程序,要求如下:
① 在项目根目录下新建一个文件:data.txt,键盘录入3个字符串验证码,并存入data.txt中,要求一个验证码占一行;
② 键盘录入一个需要被校验的验证码,如果输入的验证码在data.txt中存在:在控制台提示验证成功,如果不存在控制台提示验证失败

public class Test {
    public static void main(String[] args) throws IOException {
        writeString2File();
        verifyCode();
    }

    private static void writeString2File() throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter(new File("D:\\Clash\\a.txt")));
        String line = null;
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < 3; i++) {
            System.out.println("请输入第"+(i+1)+"个字符串验证码");
            line = sc.nextLine();
            bw.write(line);
            bw.newLine();
        }
        bw.close();
    }

    private static void verifyCode() throws IOException {
        ArrayList<String> list = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader(new File("D:\\Clash\\a.txt")));
        String line = null;
        while(null!=(line = br.readLine())){
        list.add(line);
    }
        br.close();
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个验证码");
        String code = sc.nextLine();
        if (list.contains(code)){
            System.out.println("验证成功");
        }else {
            System.out.println("验证失败");
        }
    }
}

练习:转换输出流的使用

描述: 现有一字符串:”我爱Java”。将该字符串保存到当前项目根目录下的a.txt文件中。

要求:使用gbk编码保存。
注意:idea的默认编码是utf-8,所以可以通过fileàsettingsàfile encodings设置为gbk格式,否则打开a.txt文件看到的将会是乱码。

操作步骤:

1.创建文件字节输出流关联目标文件
2.根据文件字节输出流创建转换输出流对象,并指定编码字符集为:gbk
3.调用流对象的方法将字符串写出到文件中。
4.关闭流并释放资源。

public class Test01_05 {
    public static void main(String[] args) throws IOException{
        // 要保存的字符串
        String content = "我爱Java";
        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("a.txt");
        // 创建转换输出流对象
        OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk");
        // 调用方法写出数据
        osw.write(content);
        // 关闭流释放资源
        osw.close();
    }
}

练习:转换输入流的使用

描述: 利用转换输入流将当前项目根目录下使用gbk编码的a.txt文件的内容读取出来,并打印在控制台上。

要求:不能出现乱码的情况。
操作步骤:

1.创建字节输入流对象指定文件路径。
2.根据字节输入流对象创建转换输入流对象并指定字符集编码为:gbk
3.调用转换输入流对象的读取方法读取内容
4.关闭流释放资源

public class Test01_06 {
    public static void main(String[] args) throws IOException{
        // 创建字节输入流对象并关联文件
        FileInputStream fis = new FileInputStream("a.txt");
        // 创建转换输入流对象
        InputStreamReader isr = new InputStreamReader(fis,"gbk");
        // 定义字符数组存放读取的内容
        char[] buffer = newchar[1024];
        // 定义变量接收读取的字符个数
        intlen = -1;
        while((len = isr.read(buffer)) != -1) {
            System.out.print(new String(buffer,0,len));
        }
        // 关闭流
        isr.close();
    }
}

继承&抽象类习题

概念

什么叫做类与类的继承,作用是什么?

就是子类继承父类的属性和行为,使得子类对象具有与父亲相同的属性、相同的行为;
作用是子类复用父类的内容

继承后,父类与子类之间,各成员有什么样的影响?

成员变量:
不重名,就没有影响;重名,就近使用,使用super分区父类变量

构造方法:
无影响,但是子类构造方法默认调用父类构造方法

成员方法:
不重名,没有影响;重名,子类重写父类方法

子类中,如何调用父类的成员?如何使用本类的成员?

父类成员方法:super.方法名
父类非私有成员变量:super.变量名
子类成员方法:this.方法名
子类成员变量:this.变量名

抽象方法与普通成员方法有什么区别?

抽象方法使用abstract关键字修饰,没有方法体;成员方法有方法体

抽象类与普通类有什么区别?

方法:
抽象类可以包含抽象方法和成员方法;普通类不可以包含抽象方法,只有成员方法

对象:
抽象类不可以创建对象;普通类可以创建对象

练习:语法练习

  • 语法点:继承,抽象类

  • 输出A类中numa:10、B类中numb:20、C类中numc:30

abstract  class A{
    int numa = 10;
    public abstract void showA();
}

abstract class B extends A{
    int numb = 20;
    public abstract void showB();
}

class C extends B{
    int numc = 30;

    @Override
    public void showA() {
        System.out.println("A类中numa:"+numa);
    }

    @Override
    public void showB() {
        System.out.println("B类中numb:"+numb);

    }
    public void showC(){
        System.out.println("C类中numc:"+numc);
    }
}
public class Test {
    public static void main(String[] args) {
        C c = new C();
        c.showA();
        c.showB();
        c.showC();
    }
}

练习:语法练习

  • 语法点:继承,抽象类
  • 输出动物种类:鸭子,年龄:2岁、 入院原因:感冒、 症状为:发烧

编写步骤:

  1. 模拟农学院动物医疗系统信息。
  2. 定义抽象家禽类(Poultry)
    1. 私有成员变量:动物种类(name),症状(symptom),年龄(age), 病因(illness)
    2. 提供空参和带参构造方法
    3. 成员方法:
      1. 抽象方法症状(showSymptom)
      2. 普通方法基本信息(showMsg)
      3. 提供setXxx和getXxx方法
  3. 定义普通鸭子类(Duck)
    1. 提供空参和带参构造方法
    2. 重写showSymptom方法,打印症状信息。
public class Test3 {
    public static void main(String[] args) {
        Duck duck = new Duck("鸭子", "感冒", "发烧", 2);
        duck.showMsg();
        duck.showSymptom();
    }
}

/*
1.定义抽象家禽类(Poultry)
*/
abstract class Poultry {
    //    i.成员变量(私有):

    private String name;
    private String illness;

    // 症状(symptom)
    private String symptom;
    //    年龄(age)
    private int age;

    //    ii.成员方法:  showSymptom
    public abstract void showSymptom();

    // 成员方法:  showMsg
    public void showMsg() {
        System.out.print("动物种类:" + name);
        System.out.println(",年龄:" + age + "岁");
        System.out.println("入院原因:" + illness);
    }

    //    iii.提供空参和带参构造方法
    public Poultry() {
        super();
    }

    public Poultry(String name, String illness, String symptom, int age) {
        this.name = name;
        this.illness = illness;
        this.symptom = symptom;
        this.age = age;
    }

    //    iv.提供setXxx和getXxx方法
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getIllness() {
        return illness;
    }

    public void setIllness(String illness) {
        this.illness = illness;
    }

    public String getSymptom() {
        return symptom;
    }

    public void setSymptom(String symptom) {
        this.symptom = symptom;
    }
}

// Duck 类

class Duck extends Poultry {

    public Duck() {

    }

    public Duck(String name, String illness, String symptom, int age) {

        super(name, illness, symptom, age);

    }

    @Override

    public void showSymptom() {

        System.out.println("症状为:" + getSymptom());

    }

}

练习:语法练习

  • 语法点:继承

  • 输出:王小平老师,讲授Java课、 李小乐同学,考试得了90分

编写步骤:

  1. 模拟教学管理系统师生信息。
  2. 定义Person类。
    1. 属性:姓名、年龄
    2. 构造方法:无参构造方法,有参构造方法
    3. 成员方法:getXxx方法,setXxx方法,显示基本信息showMsg方法
  3. 定义Teacher类,继承Person
    1. 属性:学科
    2. 构造方法:无参构造方法,有参构造方法
    3. 成员方法:getXxx方法,setXxx方法,讲课方法
  4. 定义Student类,继承Person
    1. 属性:分数
    2. 构造方法:无参构造方法,有参构造方法
    3. 成员方法:getXxx方法,setXxx方法,考试方法
public class Test {

    public static void main(String[] args) {
        //        i.创建老师对象t,并把名称赋值为”王小平”,年龄赋值为30,工资赋值为8000
        Teacher t = new Teacher("王小平", 30, "Java");
        //        iii.调用老师对象t的讲解方法
        t.teach();

        //        iv.创建学生对象 s,并把名称赋值为”李小乐”,年龄赋值为14,成绩赋值为90分.
        Student s = new Student("李小乐", 14, 90);
        //        vi.调用学生对象 s 的考试方法
        s.exam();
    }
}

class Person {
    // 名称(name)
    private String name;
    //    年龄(age)
    private int age;

    //    空参构造
    public Person() {
    }
    //  带参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // setXxx和getXxx方法
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
/*
 2.定义老师类(Teacher),继承Person类
 */
class Teacher extends Person {
    //    course(科目)
    private String course;
    //    空参构造
    public Teacher() {
    }
    //    带参构造方法
    public Teacher(String name,int age, String course) {
        super(name,age);
        this.course = course;
    }

    //    提供setXxx和getXxx方法
    public String getCourse() {
        return course;
    }
    public void setCourse(String course) {
        this.course = course;
    }

    public void teach() {
        System.out.println(getName() +"老师,讲授"+course +"课");
    }
}
/*
 3.定义学生类(Student),继承Person类
 */
class Student extends Person {
    //    score(成绩)
    private int score;
    //    无参构造
    public Student() {
        super();
    }
    //    带参构造
    public Student(String name, int age,int score) {
        super(name, age);
        this.score = score;
    }

    //    提供setXxx和getXxx方法
    public int getScore() {
        return score;
    }
    public void setScore(int score) {
        this.score = score;
    }

    public void exam(){
        System.out.println(getName()+"同学,考试得了:"+ score +"分");
    }
}

练习:语法练习

  • 语法点:继承

  • 编写步骤

    1. 模拟汽车网站信息。
    2. 定义汽车Auto类
      1. 属性:品牌,车长,价格
    3. 定义SUV继承Auto类
      1. 属性:小型车车长标准值:4295,中型车车长标准值:5070。
      2. 定义判断车型方法
        1. 判断小型车:小于小型车车长标准值
        2. 判断大型车:大于中型车车长标准值
        3. 判断中型车:大于小型车车长标准值并且小于等于中型车车长标准值
    4. 测试类中,创建若干SUV对象,保存到集合,遍历集合,输出中型SUV。
public class Test5 {
    public static void main(String[] args) {
        // 创建SUV对象
        SUV suv1 = new SUV(5079, 750000);
        SUV suv2 = new SUV(4813, 760000);
        SUV suv3 = new SUV(4270, 127800);
        SUV suv4 = new SUV(4545, 188800);

        //添加到集合中
        ArrayList<SUV> list = new ArrayList<>();
        list.add(suv1);
        list.add(suv2);
        list.add(suv3);
        list.add(suv4);

        // 遍历集合,查询中型SUV
        for (int i = 0; i < list.size(); i++) {
            SUV suv = list.get(i);
            if (suv.midSUV()){
                suv.showMsg();
            }
        }
    }
}
// 定义汽车类
class Auto {
    private String type;
    private double length;
    private double price;

    public Auto() {
    }

    public Auto(String type, double length, double price) {
        this.type = type;
        this.length = length;
        this.price = price;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public double getLength() {
        return length;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public void showMsg() {
        System.out.println("车型:" + type);
        System.out.println("\t价格:" + price);
        System.out.println("\t车长:" + length);

    }

}

// 定义SUV类
class SUV extends Auto {
    // 车长标准
    private int miniLength = 4295;
    private int midLength = 5070;

    public SUV(double length, double price) {
        super("SUV", length, price);
    }
    // 判断 小型车
    public boolean miniSUV() {
        return getLength() <= miniLength;
    }

    // 判断 大型车
    public boolean largeSUV() {
        return getLength() > midLength;
    }

    // 判断 中型车
    public boolean midSUV() {
        return getLength() > miniLength && getLength() <= midLength;
    }
}

接口&多态

概念辨析

什么是接口,如何定义接口?
  • 接口,是java语言中一种类型,是方法的集合
  • 使用interface关键字定义接口,其中可以定义抽象方法,默认方法,私有方法,静态方法等方法
什么叫做多态,条件是什么?

一类事物的行为,具有多种表现形式
条件:

  • 继承或实现[二选一]
  • 方法的重新
  • 父类引用指向子类对象
使用多态特性,带来了什么样的好处?

增强方法的扩展性和复用性

使用多态特性,注意什么样的弊端?

由于类型的提升,导致调用子类对象特有的方法,必须向下转型。

练习:接口

输出AAAA\n BBBB

编写步骤:

  1. 定义接口A,普通类B实现接口A
  2. A接口中,定义抽象方法showA。
  3. A接口中,定义默认方法showB。
  4. B类中,重写showA方法
  5. 测试类中,创建B类对象,调用showA方法,showB方法。
interface A{
    public abstract void showA(); //抽象方法
    public default void showB(){ //默认方法
        System.out.println("BBB");
    }
}

class B implements A{
    @Override
    public void showA() {
        System.out.println("AAAA");
    }
}

public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.showA();
        b.showB();

    }
}

练习:接口

输出AAA\n BBBB BBBB BBBB BBBB\n CCCC CCCC CCCC CCCC

编写步骤:

  1. 定义接口A,普通类B实现接口A。
  2. A接口中,定义抽象方法showA。
  3. A接口中,定义私有方法show10(String str),循环打印10次str。
  4. A接口中,定义默认方法showB10,showC10,分别调用show10方法,传入参数。
  5. 测试类中,创建B对象,调用showA方法,showB10方法,showC10方法
interface AA{
    public abstract void showA();
    private void show10(String str){
        for (int i = 0; i < 10; i++) {
            System.out.print(str + " ");
        }
        System.out.println();
    }
    public default void show10B(){
        show10("BBBB");
    }
    public default void show10C(){
        show10("CCCC");
    }
}
class BB implements AA{
    @Override
    public void showA() {
        System.out.println("AAAA");
    }
}

public class Test {
    public static void main(String[] args) {
        BB b = new BB();
        b.showA();
        b.show10B();
        b.show10C();
    }
}

练习:接口,静态

  • 编写步骤
  1. 定义接口A,普通类B实现接口A。
  2. A接口中,定义抽象方法showA。
  3. A接口中,定义私有静态方法show10(String str),循环打印10次str。
  4. A接口中,定义静态方法showB(),showC(),分别调用show10方法,传入参数。
  5. B类中,定义静态方法showD
  6. 测试类中,使用A接口,调用静态showB()方法,showC()方法,
  7. 测试类中,使用B类,调用showA方法,showD方法。
interface AAA{
    public abstract void showA();

    public static void showB() {
        System.out.println("static BBBB");
        show10("BBBB");
    }
    public static void showC(){
        System.out.println("static CCCC");
        show10("CCCC");
    }
    private static void show10(String str){
        for (int i = 0; i < 10; i++) {
            System.out.println(str + " ");
        }
        System.out.println();
    }
}

class BBB implements AAA{

    @Override
    public void showA() {
        System.out.println("AAA");
    }
    public void showD(){
        System.out.println("DDDD");
    }
}

public class Test {
    public static void main(String[] args) {
        AAA.showB();
        AAA.showC();
        BBB bbb = new BBB();
        bbb.showA();
        bbb.showD();
    }
}

练习:接口,多态

输出:star:星星一闪一闪亮晶晶\n =======\n sun:太阳引着9大行星旋转\n sun:光照八分钟,到达地球

编写步骤

  1. 定义接口Universe,提供抽象方法doAnything。
  2. 定义普通类Star,提供成员发光shine方法
  3. 定义普通类Sun,继承Star类,实现Universe接口
  4. 测试类中,创建Star对象,调用shine方法
  5. 测试类中,多态的方式创建Sun对象,调用doAnything方法,向下转型,调用shine方法。
interface Universe{
    public abstract void doAnything();
}
class Star{
    public void shine(){
        System.out.println("star:星星一闪一闪亮晶晶");
    }
}
class Sun extends Star implements  Universe{
    @Override
    public void doAnything() {
        System.out.println("sun:太阳吸引着9大行星旋转");
    }
    @Override
    public void shine() {
        System.out.println("sun:光照八分钟,到达地球");
    }
}

public class Test {
    public static void main(String[] args) {
        Star s = new Star();
        s.shine();
        System.out.println("====================");
        Universe universe = new Sun();
        universe.doAnything();
        Sun sun = (Sun)universe;
        sun.shine();
    }
}

内部类

练习:需求实现

  • 定义HandleAble接口,具备一个处理字符串数字的抽象方法方法HandleString(String num)。

    • 处理方式1:取整数部分。
    • 处理方式2:保留指定位小数,四舍五入。
  • 开发提示:

    • 匿名内部类[接口不能带方法体可以匿名内部类]的方式,调用所有抽象方法
interface HandleAble{
    String handleString(String str);
}

public class Test {
    public static void main(String[] args) {
        String str = "23.23456789";
        System.out.println("原字符串是:" + str);
        HandleAble s1 = new HandleAble() {
            @Override
            public String handleString(String str) {
                return str.substring(0,str.indexOf("."));
            }
        };
        System.out.println("取整后:" + s1.handleString(str));

        int num = 4;

        HandleAble s2 = new HandleAble() {
            @Override
            public String handleString(String str) {

                int i = str.indexOf(".") + num + 1;
                char c = str.charAt(i);
                //System.out.println(c);

                if (c <= '4') {
                    return str.substring(0, i).toString();
                } else {
                    char c1 = (char) (str.charAt(str.indexOf(".") + num) + 1);
                    return str.substring(0, i - 1) + c1;

                }
            }
        };
        String sss = s2.handleString(str);
        System.out.println("保留" + num + "位小数后:" + sss);
    }
}

练习:需求实现

  • 模拟上课出勤情况。
  • 定义学生类:

    • 属性:姓名,出勤。
    • 提供基本的构造方法和get方法,set方法。
  • 定义讲师类:

    • 属性:姓名。
    • 提供基本的构造方法和get方法,set方法
    • 成员方法:点名方法,设置每一位的学生出勤情况。假设,小明今日未出勤。
  • 定义课程类:

    • 属性:课程名称,讲师,学生集合。
    • 提供基本的构造方法和get方法,set方法
    • 成员方法:show方法,打印课程信息,老师姓名,学生是否上课情况。

课程名称:Java
授课老师:张老师
上课:小红
上课:小亮
旷课:小明

public class Test {
    public static void main(String[] args) {
        Student s = new Student("小红");
        Student s1 = new Student("小亮");
        Student s2 = new Student("小明");
        ArrayList<Student> arr = new ArrayList<>();
        arr.add(s);
        arr.add(s1);
        arr.add(s2);
        Teacher t = new Teacher("张老师");
        Course course = new Course("java",t,arr);
        t.dianming(arr);
        course.show();

    }
}

class Student{
    private String name;
    private boolean come;

    public Student() {
    }

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public boolean isCome() {
        return come;
    }

    public void setCome(boolean come) {
        this.come = come;
    }
}
class Teacher{
    private String name;
    // 点名方法,设置每一位的学生出勤情况。假设,小明今日未出勤
    public void dianming(ArrayList<Student> arr){
        for (int i = 0; i < arr.size(); i++) {
            Student student = arr.get(i);
            if (!student.getName().equals("小明")){
                student.setCome(true);
            }
        }
    }
    public Teacher() {
    }

    public Teacher(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
class Course{
    private String name;
    private Teacher t;
    private ArrayList<Student> arr;

    public Course() {
    }

    public Course(String name, Teacher t, ArrayList<Student> arr) {
        this.name = name;
        this.t = t;
        this.arr = arr;
    }
    public void show(){
        System.out.println("课程名称:" + name);
        System.out.println("授课老师:" + t.getName());
        for (int i = 0; i < arr.size(); i++) {
            Student student = arr.get(i);
            String name = student.getName();
            if (student.isCome()) {
                System.out.println("上课: " + name);
            } else {
                System.out.println("旷课: " + name);
            }
        }
    }
}

练习:需求实现

  • 模拟接待员接待用户,根据用户id,给用户分组。
  • 定义接口Filter:
    • 提供抽象方法filterUser(User u)
  • 定义用户类:
    • 属性:用户类型,用户id
    • 提供基本的构造方法和get方法,set方法
  • 定义接待员类:
    • 属性:接口Filter
    • 提供基本的构造方法和get方法,set方法
    • 成员方法:接待用户方法,设置用户类型。
  • 测试类:
    • 初始化50个User对象,id为1-50。
    • 创建三个接待员对象。
      • 第一个接待员,设置接待规则,将10-19号用户类型设置为v1。
      • 第二个接待员,设置接待规则,将20-29号用户类型设置为v2。
    • 遍历用户集合,给用户分区。
public class Test {
    public static void main(String[] args) {

        ArrayList<User> ulist = new ArrayList<>();
        for (int i = 1; i <= 50; i++) {
            ulist.add(new User(i));
        }

        System.out.println("未分组:");
        System.out.println(ulist);

        Reception r1 = new Reception();
        Reception r2 = new Reception();
        Reception r3 = new Reception();
        r1.setF(new Filter() {
            @Override
            public void filterUser(User u) {
                if (u.getId() >= 10 && u.getId() < 20)
                    u.setType("v1");
            }
        });

        r2.setF(new Filter() {
            @Override
            public void filterUser(User u) {
                if (u.getId() >= 20 && u.getId() < 30)
                    u.setType("v2");
            }
        });

        for (int i = 0; i < ulist.size(); i++) {
            User user = ulist.get(i);
            r1.recept(user);
            r2.recept(user);
            r3.recept(user);
        }
        System.out.println("已分组:");
        for (int i = 0; i < ulist.size(); i++) {
            User user = ulist.get(i);
            if (i % 9 == 0) {
                System.out.println();
            }
            System.out.print(user + " ");
        }

    }
}

class Reception {

    Filter f;

    public Filter getF() {
        return f;
    }

    public void setF(Filter f) {
        this.f = f;
    }

    public void recept(User u) {
        if (u.getType() != null)
            return;
        if (f != null) {
            f.filterUser(u);
            return;
        } else {
            u.setType("A");
        }
    }
}

class User {

    private String type;

    private int id;

    public User(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return id + "-" + type;
    }
}

interface Filter {
    public abstract void filterUser(User u);
}

练习:需求实现

  • 模拟工人挑苹果。

  • 定义苹果类:

    • 属性:大小,颜色。
    • 提供基本的构造方法和get方法,set方法
  • 定义接口CompareAble:

    • 定义默认方法compare,挑选较大苹果。
  • 定义接口实现类Compare。

  • 定义工人类:

    • 成员方法:挑选苹果Apple pickApple(CompareAble,Apple a1,Apple a2)。
  • 测试类:

    • 创建Worker对象。
    • 创建两个Apple对象,一个Apple(5,”青色”),一个Apple(3,”红色”)
    • 默认挑选大的苹果,打印苹果信息。
    • 指定颜色挑选,通过匿名内部类实现。
  • 代码实现,效果所示:

默认挑大的:
5.0 - 青色
挑红的:
3.0 - 红色

interface CompareAble{
    default Apple compare(Apple a1, Apple a2){
        return a1.getSize() > a2.getSize() ? a1 : a2;
    }
}

class Apple{
    private double size;
    private String color;

    public Apple() {
    }

    public Apple(double size, String color) {
        this.size = size;
        this.color = color;
    }

    public double getSize() {
        return size;
    }

    public void setSize(double size) {
        this.size = size;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "size=" + size +
                ", color='" + color + '\'' +
                '}';
    }
}

class Worker{
    public Apple pickApple(CompareAble c, Apple a1, Apple a2){
        Apple compare = c.compare(a1,a2);
        return compare;
    }


public class Test {
    public static void main(String[] args) {
        Worker worker = new Worker();
        Apple apple1 = new Apple(5, "青色");
        Apple apple2 = new Apple(3, "红色");
        System.out.println("默认挑大的:");
        Apple apple = worker.pickApple(new Com(), apple1, apple2);
        System.out.println(apple);

        System.out.println("挑红的:");
        Apple apple3 = worker.pickApple(new Com(){
            @Override
            public Apple compare(Apple a1, Apple a2) {
                return "红色".equals(a1.getColor()) ? a1 : a2;
            }
        },apple1,apple2);
        System.out.println(apple3);
    }
    }
}

class Com implements CompareAble {

}

数组习题

练习:需求实现

模拟在一副牌中,抽取第1张,第5张,第50张扑克牌。

输出:黑桃A 黑桃5 方片J

public class Test {
    public static void main(String[] args) {
        String[] poker = getPoker();
// 抽取指定的三种扑克牌
        int num1 = 1;
        int num2 = 5;
        int num3 = 50;
        String[] pk3 = get3(poker , num1 ,num2,num3);
        // 打印抽取的牌
        for (int i = 0; i < pk3.length; i++) {
            System.out.print(pk3[i] + " ");
        }
    }
    private static String[] get3(String[] poker, int i, int i2, int i3){
        String[] pk3 = new String[3];
        pk3[0] = poker[i - 1];
        pk3[1] = poker[i2 - 1];
        pk3[2] = poker[i3 - 1];
        return pk3;
    }
    private static String[] getPoker(){
        String[] colors={"黑色","红桃","梅花","方块"};
        String[] nums={"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
        String[] poker = new String[54];
        int index = 0;
        for (int i = 0; i < colors.length; i++) {
            for (int j = 0; j < nums.length; j++) {
                poker[index] = colors[i] + nums[j];
                index++;
            }
        }
        poker[52] = "小王";
        poker[53] = "大王";

        return poker;
    }
}

练习:需求实现

定义equals方法,比较数组内容是否完全一致。

开发提示:

  • 长度一致,内容一致,定义为完全一致。
public class Test {
    public static void main(String[] args) {

        int[] arr = {1,2,3,4,3,2,1};
        int[] arr2 = {1,2,3,4,3,2,1};
        System.out.println(" 是否一致:" +equals(arr ,arr2));

    }
    //  比较数组的内容
    public static boolean equals(int[] arr1, int[] arr2) {
        // 长度不同,返回false
        if (arr1.length != arr2.length) {
            return false;
        }

        //
        for (int i = 0; i < arr1.length; i++) {
            // arr1[i] 和 arr2[i]比较
            /*
             * 所有元素都相同才相同,也就是只要有一个不同,这两个数组就是不同
             */
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }
}

异常&线程习题

练习:异常的体系

1.请描述异常的继承体系
异常继承体系:异常的根类是java.lang.Throwable。其下有两个子类:java.lang.Errorjava.util.Exception
Exception又分为编译时期异常:checked异常
与运行时期异常:runtime异常

2.请描述你对错误(Error)的理解
Error:表示不可修复的恶性的错误,只能通过修改菜吗规避错误的产生,通常是系统级别的,所以很严重。

3.描述你对异常(Expection的理解)
Exception:表示可修复的良性(相对于错误)的异常,异常产生后程序员可以并且通过代码的方式修正,使程序继续运行,是必须要处理的。

4.描述你对运行时异常(RuntimeException)的理解
运行时期异常:runtime异常。在运行时期,检查异常. 在编译时期,运行异常不会编译器检测(不报错)

练习:throw与throws的区别

1.请描述throw的使用位置,作用是什么?
throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,后面语句都不执行

2.请描述throws的使用位置,作用是什么?
throws关键字通常被应用在声明方法时,用来指定可能抛出的异常。多个异常可以使用逗号隔开。当在主函数中调用该方法时,如果发生异常,就会将异常对象抛给方法调用处

练习:异常的处理方式

1.异常处理方式有几种,分别是什么
异常的处理方式有两种,分别是使用 throwstry...catch...finally

2.详细阐述每种方式对异常是如何处理的
throws用在方法的声明上后接异常类名,是把异常抛给调用者进行处助理
try…catch…finally是捕获异常,自己处理,处理完毕后面的程序可以继续运行
try代码块中是可能出现异常的代码
catch代码块,是遇到异常,对异常进行处理的代码
finally代码块无论是否发生异常,都必须执行的代码,用于释放资源

练习:常见异常,及产生原因

请列举常见异常,并说明产生原因

NullPointerException:空指针异常
当应用试图在要求使用对象的方法使用了null时,抛出该异常;譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度

ArrayIndaexOutOfBoundsException:数组索引越界异常
当对数组的索引值为负数或大于等于数组大小时抛出此异常。

ArithmeticException:算术运算异常
程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不是有不妥了

NumberFormatException:数字格式异常
当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常

练习:并行、并发概念

请简单描述什么是并行,什么是并发?

并行:指两个或多个事件在同一时刻发生(同时发生)。
并发:指两个或多个事件在同一个时间段内发生。

你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不 支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。

并发的关键是你有处理多个任务的能力,不一定要同时。
并行的关键是你有同时处理多个任务的能力。
它们最关键的点就是:是否是『同时』

练习:进程概念、线程概念、线程与进程联系

请描述什么是进程,什么是线程,进程与线程之间的关系,并举例说明

进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

一个程序运行后至少有一个进程,一个进程中可以包含多个线程, 但一个进程中至少包含一个线程。比如使用迅雷软件下载网络文件时,同时下载多个文件,就使用到了多线程下载。

练习:自定义异常类

请使用代码实现
每一个学生(Student)都有学号,姓名和分数,分数永远不能为负数
如果老师给学生赋值一个负数,抛出一个自定异常

// 1.定义异常类NoScoreException,继承RuntimeException 提供空参和有参构造方法
public class NoScoreException extends RuntimeException {
    //  空参构造
    public NoScoreException() {
            super();
    }
    // 有参构造
    public NoScoreException(String message) {
            super(message);
     }
/* 2.定义学生类(Student)
   a)属性:name,score
   b)提供空参构造
   c)提供有参构造;
    i.使用setXxx方法给名称和score赋值
   d)提供setter和getter方法
    ii.在setScore(int score)方法中
    1.首先判断,如果score为负数,就抛出NoScoreException,异常信息为:分数不能为负数:xxx.
    2.然后在给成员score赋值.*/
public class Student {
    private String name;
    private int score;
    // 空参构造
    public Student() {
        super();
    }
    // c)提供有参构造;
// i.使用setXxx方法给名称和score赋值
    public Student(String name,int score){
        setName(name);
        setScore(score);
    }
// d)提供setter和getter方法

    public String getName() {
        return name;
    }

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

    public int getScore() {
        return score;
    }
    // i.在setScore(int score)方法中
    public void setScore(int score) {
// 1.首先判断,如果score为负数,就抛出NoScoreException,异常信息为:分数不能为负数:xxx.
    if(score <0){
       throw new NoScoreException(":分数不能为负数:"+score);
    }
// 2.然后在给成员score赋值.
        this.score = score;
    }
}
    /*
3.定义测试类Test9
 a)提供main方法,在main方法中
  i.使用满参构造方法创建Student对象,分数传入一个负数,运行程序
  ii.由于一旦遇到异常,后面的代码的将不在执行,所以需要注释掉上面的代码
  iii.使用空参构造创建Student对象
  iv.调用setScore(int score)方法,传入一个正数,运行程序
  v.调用setScore(int score)方法,传入一个负数,运行程序
 */
public class Test9 {
    public static void main(String[] args) {
//  i.使用满参构造方法创建Student对象,分数传入一个负数,运行程序
//  Student s = new Student("景甜", -10);
//  ii.由于一旦遇到异常,后面的代码的将不在执行,所以需要注释掉上面的代码

//  iii.使用空参构造创建Student对象
        Student s = new Student();
//  iv.调用setScore(int score)方法,传入一个正数,运行程序
        s.setScore(100);
//  v.调用setScore(int score)方法,传入一个负数,运行程序
        s.setScore(-5);
    }
}

网络通信概述&TCP协议习题

练习:ip地址和端口号概念

描述:
一、请写出IP地址的概念:
IP地址:互联网协议地址(Internet Protocol Address), 俗称IP.IP地址用来给一个网络中的计算机设备做唯一的编号.

二、请写出端口号的概念:
端口号: 端口号用来给计算机里的应用程序(进程)做唯一的标识,用2个字节表示的整数,取值范围0~65535.

练习:UDP协议

判断下列说法是否正确:( X )

由于UDP面向无连接的协议,可以保证数据完整性,因此在传输重要数据时采用UDP协议.
判断错误, 因为面向无连接,容易丢失包,所以不能保证数据完整.

练习:TCP协议

TCP协议中”三次握手”,第一次握手指的是什么:

第一次握手:客户端向服务器发送请求,等待服务器确认

练习:TCP网络协议

需求说明:创建新项目,按以下要求编写代码:

在项目下创建TCP 服务器端 端口号为8888
1: 等待客户端连接 如果有客户端连接 获取到客户端对象
2: 获取到客户端对象之后 当前在服务器读取数据客户端传送数据

public class TCPServer {
   public static void main(String[] args) throws Exception {
      //1创建服务器对象 
      ServerSocket  ss = new ServerSocket(8888);
      //2等待客户端连接   如果有客户端连接  获取到客户端对象 
      Socket socket = ss.accept();
      //3当前在服务器中  要读取数据  需要输入流  流由谁提供 客户端
      InputStream in = socket.getInputStream();//获取输入流
      //4:读数据
      int len;
      byte[] buffer = new byte[1024];
      while((len=in.read(buffer))!=-1){
          System.out.println(new String(buffer, 0, len));
      }
      //释放资源
      in.close();
//       ss.close();服务器一般不会关闭
   }
}
需求说明:创建新项目,按以下要求编写代码:

在项目下创建TCP 客户端
访问之前创建的服务器端,服务器端ip127.0.0.1 端口号8888
1: 客户端连接服务器,并发送 hello.服务器,我是客户端.
2: 开启上一题服务器,等待客户端连接,客户端连接并发送数据

public class TCPClient {
  public static void main(String[] args) throws Exception {
      //创建 Socket客户端对象
      Socket socket = new Socket("127.0.0.1", 8888);
      //写数据  需要输出流  谁提供 客户端
      OutputStream out = socket.getOutputStream();
      //写数据
      out.write("hello.服务器,我是客户端.".getBytes());
      //释放资源
      out.close();
      socket.close();
  }
}
阅读全文

java复习款

2023/8/13

Object类、常用API

public String toString():返回该对象的字符串表示
public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”
    
java.util.Date类表示特定的瞬间,精确到毫秒
    public Date():分配Date对象并初始化此对象,以表示分配它的时间(精确到秒)
    public Date(long date):分配Date对象并初始化此对象,以表示从标准基准时间1970年1月1日以来的指定毫秒数
    public long getTime():把日期对象转换成对应的时间毫秒值
    
java.text.DateFormat类是日期/时间格式化子类的抽象类
    public SimpleDateFormate(String pattern):用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat["yyyy-MM-dd HH:mm:ss" => 2018-01-16 15:06:38]
    public String format(Date date):将Date对象格式化为字符串
    public Date parse(String source):将字符串解析为Date对象
请使用日期时间相关的API,计算出一个人已经出生了多少天。

思路:

1.获取当前时间对应的毫秒值

2.获取自己出生日期对应的毫秒值

3.两个时间相减(当前时间– 出生日期)

代码实现:

public static void function() throws Exception {
    System.out.println("请输入出生日期 格式 YYYY-MM-dd");
    // 获取出生日期,键盘输入
    String birthdayString = new Scanner(System.in).next();
    // 将字符串日期,转成Date对象
    // 创建SimpleDateFormat对象,写日期模式
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    // 调用方法parse,字符串转成日期对象
    Date birthdayDate = sdf.parse(birthdayString);    
    // 获取今天的日期对象
    Date todayDate = new Date();    
    // 将两个日期转成毫秒值,Date类的方法getTime
    long birthdaySecond = birthdayDate.getTime();
    long todaySecond = todayDate.getTime();
    long secone = todaySecond-birthdaySecond;    
    if (secone < 0){
        System.out.println("还没出生呢");
    } else {
        System.out.println(secone/1000/60/60/24);
    }
}

Calendar类

java.util.Calendar
    public static Calendar getInstance():使用默认时区和语言环境获得一个日历
    public int get(int field):返回給定日历字段的值
    public void set(int field, int value):将给定的日历字段设置为定值
    public abstract void add(qint field, int amount):根据日历的规则,为給定的日历字段添加或减去指定的时间量
    public Date getTime():返回一个表示此Calendar时间值(从历元到现在的毫秒偏移量)的Da
public static void main(String[] args) {
    get方法
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH)+1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
        System.out.println(year+"年"+month+"月"+day+"日");
    }

    set方法
public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, 2020);
        System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); 
                        // 2020年1月17日
    }
    
    add方法
public static void main(String[] args) {
        Calendar cal = Calendar.getInstance();
        System.out.print(year + "年" + month + "月" + dayOfMonth + "日");
                            // 2018年1月17日
        // 使用add方法
        cal.add(Calendar.DAY_OF_MONTH, 2); // 加2天
        cal.add(Calendar.YEAR, -3); // 减3年
        System.out.print(year + "年" + month + "月" + dayOfMonth + "日"); 
                            // 2015年1月18日; 
    }

    getTime方法

      

CurrentTimeMillis方法

验证for循环打印数字1-9999所需要使用的时间(毫秒)
public class SystemTest1 {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            System.out.println(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("共耗时毫秒:" + (end - start));
    }
}
public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另一个数组中。(数组拷贝)

StringBuilder类

StringBuilder又称为可变字符序列,它是一个类似于 String 的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。它的内部拥有一个数组用来存放字符串内容,进行字符串拼接时,直接在数组中加入新内容。StringBuilder会自动维护数组的扩容。

备注:StringBuilder已经覆盖重写了Object当中的toString方法。
public StringBuilder():构造一个空的StringBuilder容器
public StringBuilder(String str):构造一个StringBuilder容器,并将字符串添加进去
public StringBuilder append(...):添加任意类型数据的字符串形式,并返回当前对象自身
public String toString():将当前StringBuilder对象转换为String对象
append方法
public static void main(String[] args) {
        //创建对象
        StringBuilder builder = new StringBuilder();
        //public StringBuilder append(任意类型)
        StringBuilder builder2 = builder.append("hello");
        //对比一下
        System.out.println("builder:"+builder);
        System.out.println("builder2:"+builder2);
        System.out.println(builder == builder2); //true
        // 可以添加 任何类型
        builder.append("hello");
        builder.append("world");
        builder.append(true);
        builder.append(100);
        // 在我们开发中,会遇到调用一个方法后,返回一个对象的情况。然后使用返回的对象继续调用方法。
        // 这种时候,我们就可以把代码现在一起,如append方法一样,代码如下
        //链式编程
        builder.append("hello").append("world").append(true).append(100);
        System.out.println("builder:"+builder);
    }

toString方法
通过toString方法,StringBuilder对象将会转换为不可变的String对象。如:
public static void main(String[] args) {
        // 链式创建
        StringBuilder sb = new StringBuilder("Hello").append("World").append("Java");
        // 调用方法
        String str = sb.toString();
        System.out.println(str); // HelloWorldJava
    }

基本类型转换位String

- public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
- public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
- public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
- public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
- public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
- public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
- public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出java.lang.NumberFormatException异常。
public class Demo18WrapperParse {
    public static void main(String[] args) {
        int num = Integer.parseInt("100");
    }
}

Collection、泛型

集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection双列集合java.util.Map,今天我们主要学习Collection集合。集合本身是一个工具,它存放在java.util包中。在Collection接口定义着单列集合框架中最最共性的内容。

public boolean add(E e):把给定的对象添加到当前集合中
public void clear():清空集合中所有的元素
public boolean remove(E e):把给定的对象在当前集合中删掉
public boolean contains(E e):判断当前集合中是否包含给定的对象
public boolean isEmpty():判断当前集合是否位空
public int size():返回集合中元素的个数
public Object[] to Array():把集合中的元素,存储到数组中
public static void main(String[] args) {
        Collection<String> coll = new ArrayList<String>();
        coll.add("一");
        coll.add("二");
        coll.add("三");
        System.out.println(coll);                //[一, 二, 三]
        System.out.println(coll.contains("二"));     //true
        System.out.println(coll.isEmpty());  //false
        System.out.println(coll.size());     //3
        System.out.println(coll.remove("二")); // true
        System.out.println(coll);                  //[一,二]
        System.out.println(coll.contains("二"));  //false

        Object[] objects = coll.toArray(); //0 1
        for (int i = 0; i < objects.length; i++) {
            System.out.println(i);
        }
    }

Iterator迭代器

JDK专门提供了一个接口java.util.IteratorIterator接口也是Java集合中的一员,但它与CollectionMap接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。

public E next():返回迭代的下一个元素
public boolean hasNext():如果仍有元素可以迭代,则返回true
tips::在进行集合元素取出时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会发生java.util.NoSuchElementException没有集合元素的错误。
public static void main(String[] args) {
        // 使用多态方式 创建对象
        Collection<String> coll = new ArrayList<String>();
        // 添加元素到集合
        coll.add("串串星人");
        coll.add("吐槽星人");
        coll.add("汪星人");
        //遍历
        //使用迭代器 遍历   每个集合对象都有自己的迭代器
        Iterator<String> it = coll.iterator();
        //  泛型指的是 迭代出 元素的数据类型
        while(it.hasNext()){ //判断是否有迭代元素
            String s = it.next();//获取迭代出的元素
            System.out.println(s);
        }
      }

增强for

它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

for(元素的数据类型 变量 : collection集合or数组){
  //操作代码
}
遍历集合
public static void main(String[] args) {        
        Collection<String> coll = new ArrayList<String>();
        coll.add("小河神");
        coll.add("老河神");
        coll.add("神婆");
        //使用增强for遍历
        for(String s :coll){//接收变量s代表 代表被遍历到的集合元素
            System.out.println(s);
    }
}

泛型

修饰符 class 类名<代表泛型的变量> { }
class ArrayList<E>{
    public boolean add(E e) { }
    public E get(int index) { }
}

在创建对象的时候确定泛型
ArrayList<String> list = new ArrayList<String>();
class ArrayList<Integer>{
    public boolean add(Integer e) { }
    public Integer get(int index) { }
}
举例自定义泛型类
public class MyGenericClass<MVP>{
    //没有MVP类型,在这里代表 未知的一种数据类型 未来传递什么就是什么类型
    private MVP mvp;
    public void setMVP(MVP mvp){
        this.mvp = mvp;
    }
    public MVP getMVP(){
        return mvp;
    }
}

public class GenericClassDemo {
      public static void main(String[] args) {         
         // 创建一个泛型为String的类
         MyGenericClass<String> my = new MyGenericClass<String>();        
         // 调用setMVP
         my.setMVP("大胡子登登");
         // 调用getMVP
         String mvp = my.getMVP();
         System.out.println(mvp);
         //创建一个泛型为Integer的类
         MyGenericClass<Integer> my2 = new MyGenericClass<Integer>(); 
         my2.setMVP(123);         
         Integer mvp2 = my2.getMVP();
    }
}

含有泛型的方法

修饰符 <代表泛型的变量> 返回值类型 方法名(参数){  }

public class MyGenericMethod {      
    public <MVP> void show(MVP mvp) {
        System.out.println(mvp.getClass());
    }
    
    public <MVP> MVP show2(MVP mvp) {    
        return mvp;
    }
}

含有泛型的接口

1、定义类时确定泛型的类型
修饰符 interface接口名<代表泛型的变量> {  }

public interface MyGenericInterface<E>{
    public abstract void add(E e);
    
    public abstract E getE();  
}

泛型E的值就是String类型
public class MyImp1 implements MyGenericInterface<String> {
    @Override
    public void add(String e) {
        // 省略...
    }

    @Override
    public String getE() {
        return null;
    }
}

2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
public class MyImp2<E> implements MyGenericInterface<E> {
    @Override
    public void add(E e) {
            // 省略...
    }

    @Override
    public E getE() {
        return null;
    }
}

/*
 * 使用
 */
public class GenericInterface {
    public static void main(String[] args) {
        MyImp2<String>  my = new MyImp2<String>();  
        my.add("aa");
    }
}

泛型通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过**通配符<?>**表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

public static void main(String[] args) {
    Collection<Intger> list1 = new ArrayList<Integer>();
    getElement(list1);
    Collection<String> list2 = new ArrayList<String>();
    getElement(list2);
}
public static void getElement(Collection<?> coll){}
//?代表可以接收任意类型
通配符高级使用—-受限泛型

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

泛型的上限

  • 格式类型名称 <? extends 类 > 对象名称
  • 意义只能接收该类型及其子类

泛型的下限

  • 格式类型名称 <? super 类 > 对象名称
  • 意义只能接收该类型及其父类型

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类

public static void main(String[] args) {
    Collection<Integer> list1 = new ArrayList<Integer>();
    Collection<String> list2 = new ArrayList<String>();
    Collection<Number> list3 = new ArrayList<Number>();
    Collection<Object> list4 = new ArrayList<Object>();
    
    getElement(list1);
    getElement(list2);//报错
    getElement(list3);
    getElement(list4);//报错
  
    getElement2(list1);//报错
    getElement2(list2);//报错
    getElement2(list3);
    getElement2(list4);
  
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}
Java.util.Collections类下有一个静态的shuffle()方法,如下:

1)static void shuffle(List<?> list)  使用默认随机源对列表进行置换,所有置换发生的可能性都是大致相等的。

2)static void shuffle(List<?> list, Random rand) 使用指定的随机源对指定列表进行置换,所有置换发生的可能性都是大致相等的,假定随机源是公平的。

扑克牌案例分析

  • 准备牌:

    牌可以设计为一个ArrayList,每个字符串为一张牌。
    每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
    牌由Collections类的shuffle方法进行随机排序。

  • 发牌

    将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

  • 看牌

    直接打印每个集合。

import java.util.ArrayList;
import java.util.Collections;

public class Poker {
    public static void main(String[] args) {
        /*
        * 1: 准备牌操作
        */
        //1.1 创建牌盒 将来存储牌面的 
        ArrayList<String> pokerBox = new ArrayList<String>();
        //1.2 创建花色集合
        ArrayList<String> colors = new ArrayList<String>();

        //1.3 创建数字集合
        ArrayList<String> numbers = new ArrayList<String>();

        //1.4 分别给花色 以及 数字集合添加元素
        colors.add("♥");
        colors.add("♦");
        colors.add("♠");
        colors.add("♣");

        for(int i = 2;i<=10;i++){
            numbers.add(i+"");
        }
        numbers.add("J");
        numbers.add("Q");
        numbers.add("K");
        numbers.add("A");
        //1.5 创造牌  拼接牌操作
        // 拿出每一个花色  然后跟每一个数字 进行结合  存储到牌盒中
        for (String color : colors) {
            //color每一个花色 
            //遍历数字集合
            for(String number : numbers){
                //结合
                String card = color+number;
                //存储到牌盒中
                pokerBox.add(card);
            }
        }
        //1.6大王小王
        pokerBox.add("小☺");
        pokerBox.add("大☠");      
        // System.out.println(pokerBox);
        //洗牌 是不是就是将  牌盒中 牌的索引打乱 
        // Collections类  工具类  都是 静态方法
        // shuffer方法   
        /*
         * static void shuffle(List<?> list) 
         *     使用默认随机源对指定列表进行置换。 
         */
        //2:洗牌
        Collections.shuffle(pokerBox);
        //3 发牌
        //3.1 创建 三个 玩家集合  创建一个底牌集合
        ArrayList<String> player1 = new ArrayList<String>();
        ArrayList<String> player2 = new ArrayList<String>();
        ArrayList<String> player3 = new ArrayList<String>();
        ArrayList<String> dipai = new ArrayList<String>();      

        //遍历 牌盒  必须知道索引   
        for(int i = 0;i<pokerBox.size();i++){
            //获取 牌面
            String card = pokerBox.get(i);
            //留出三张底牌 存到 底牌集合中
            if(i>=51){//存到底牌集合中
                dipai.add(card);
            } else {
                //玩家1   %3  ==0
                if(i%3==0){
                      player1.add(card);
                }else if(i%3==1){//玩家2
                      player2.add(card);
                }else{//玩家3
                      player3.add(card);
                }
            }
        }
        //看看
        System.out.println("令狐冲:"+player1);
        System.out.println("田伯光:"+player2);
        System.out.println("绿竹翁:"+player3);
        System.out.println("底牌:"+dipai);  
    }
}

List、Set、数据结构、Collections

List集合特有的方法都是跟索引相关
public void add(int index, E element):将指定的元素,添加到集合中的指定位置上
public E get(int index):返回集合中指定位置的元素
public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素
public E set(int index,E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素
public static void main(String[] args) {
        // 创建List集合对象
        List<String> list = new ArrayList<String>();
        
        // 往 尾部添加 指定元素
        list.add("图图");
        list.add("小美");
        list.add("不高兴");
        
        System.out.println(list);
        // add(int index,String s) 往指定位置添加
        list.add(1,"没头脑");
        
        System.out.println(list);
        // String remove(int index) 删除指定位置元素  返回被删除元素
        // 删除索引位置为2的元素 
        System.out.println("删除索引位置为2的元素");
        System.out.println(list.remove(2));
        
        System.out.println(list);
        
        // String set(int index,String s)
        // 在指定位置 进行 元素替代(改) 
        // 修改指定位置元素
        list.set(0, "三毛");
        System.out.println(list);
        
        // String get(int index)  获取指定位置元素
        
        // 跟size() 方法一起用  来 遍历的 
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        //还可以使用增强for
        for (String string : list) {
            System.out.println(string);
        }      
    }

List(ArrayList、LinkedList)的子类

java.util.ArrayList 集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
java.util.LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。

public void addFirst(E e):将指定元素插入此列表的开头
public void addLast(E e):将指定元素添加到此列表的结尾
public E getFirst():返回此列表的第一个元素
public E removeFirst():移除并返回此列表的第一个元素
public E removeLast():移除并返回此列表的最后一个元素
public E pop():从此列表所表示的堆栈处弹出一个元素
public void push(E e):将元素推入此列表所表示的堆栈
public boolean isEmpty():如果列表不包含元素,则返回true

Set(HashSet、LinkedHashSet)

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。

Set集合有多个子类,这里我们介绍其中的java.util.HashSetjava.util.LinkedHashSet这两个集合。

Set集合取出元素的方式可以采用:迭代器、增强for

HashSet是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于:hashCodeequals方法。

HashSet存储自定义类型元素

给HashSet中存放自定义类型元素时,需要重写对象中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中的对象唯一。

可变参数

修饰符 返回值类型 方法名(参数类型[] 形参名){  }
public static int getSum(int[] arr){
    int sum = 0;
    for(int a : arr){
        sum += a;
    }
    return sum;
}

Collections(高效添加元素)

java.utils.Collections是集合工具类
public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素
public static void shuffle(List<?> list):打乱集合顺序
public static <T> void sort(List<T> list):将集合中元素按照默认规则排序
public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。
 public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        //原来写法
        //list.add(12);
        //list.add(14);
        //list.add(15);
        //list.add(1000);
        //采用工具类 完成 往集合中添加元素  
        Collections.addAll(list, 5, 222, 1,2);
        System.out.println(list);
        //排序方法 
        Collections.sort(list);
        System.out.println(list);
    }
}

Comparator比较器

public int compare(String o1, String o2):比较两个参数的顺序
return o1 - o2 (正数)
public static void main(String[] args) {
   ArrayList<String> arr = new ArrayList<String>();
   Collections.addAll(arr,"cba","aba","sba","nba");
   Collections.sort(arr, new Comparator<String>() {
     @Override
     public int compare(String o1, String o2) {
         return o1.charAt(0)-o2.charAt(0);
     }
  });
  System.out.println(arr);
}
public class Student_test implements Comparable<Student_test> {
    @Override
    public int compareTo(Student_test o) {
        return this.age-o.age;
    }
Getter Setter toString 
--------------------------------------------------------------
public class Comparable_test {
    public static void main(String[] args) {
        ArrayList<Student_test> list = new ArrayList<Student_test>();
        list.add(new Student_test("rose",18));
        list.add(new Student_test("jack",16));
        list.add(new Student_test("abc",16));
        list.add(new Student_test("ace",17));
        list.add(new Student_test("mark",16));
        for(Student_test student : list){
            System.out.println(student);
        }
    }
}

如果在使用的时候,想要独立的定义规则去使用 可以采用Collections.sort(List list,Comparetor c)方式,自己定义规则:

Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o2.getAge()-o1.getAge();//以学生的年龄降序
    }
});

Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='jack', age=16}
Student{name='abc', age=16}
Student{name='mark', age=16}

如果想要规则更多一些,可以参考下面代码:

Collections.sort(list, new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
       // 年龄降序
    int result = o2.getAge()-o1.getAge();//年龄降序

    if(result==0){//第一个规则判断完了 下一个规则 姓名的首字母 升序
     result = o1.getName().charAt(0)-o2.getName().charAt(0);
    }
     return result;
  }
});

Student{name='rose', age=18}
Student{name='ace', age=17}
Student{name='abc', age=16}
Student{name='jack', age=16}
Student{name='mark', age=16}

Map集合

java.util.Map接口

Collection接口定义了单列集合规范 每次存储一个元素 单个元素
Map接口定义了双列集合的规范 每次存储一对儿元素(Key Value)

  • Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
  • Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
  • Collection中的集合称为单列集合,Map中的集合称为双列集合。
  • 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保持一致,由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法

LinkedHashMap<K,V>: HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

public V put(K key, V value):把指定的键与指定的值添加到Map集合中
public V remove(Object key):把指定的键所对应的键值对元素 在Map集合中删除,返回被删除元素的值
public V get(Object key):根据指定的键,在Map集合中获取对应的值
boolean containsKey(Object key) 判断集合中是否包含指定的键。
public Set<K> keySet(): 获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)
    
使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中; 
若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。 

Map集合遍历 键找值 方式

键找值方式:即通过元素中的键,获取键所对应的值

分析步骤:

  1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法: keyset()
  2. 遍历键的Set集合,得到每一个键
  3. 根据键,获取键所对应的值。方法: get(K key)
public static void main(String[] args) {
        HashMap<String,String> map = new HashMap<String,String>();
        map.put("胡歌", "霍建华");
        map.put("郭德纲", "于谦");
        map.put("薛之谦", "大张伟");
        Set<String> keys = map.keySet();
        for (String key : keys){
            String value = map.get(key);
            System.out.println(key+" "+value);
        }
    }

Map集合遍历键值对方式

键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。

操作步骤与图解:

  1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示:entrySet()

  2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。

  3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示:getkey() getValue()

public static void main(String[] args) {
        // 创建Map集合对象 
        HashMap<String, String> map = new HashMap<String,String>();
        // 添加元素到集合 
        map.put("胡歌", "霍建华");
        map.put("郭德纲", "于谦");
        map.put("薛之谦", "大张伟");

        // 获取 所有的 entry对象  entrySet
        Set<Entry<String,String>> entrySet = map.entrySet();

        // 遍历得到每一个entry对象
        for (Entry<String, String> entry : entrySet) {
               // 解析 
            String key = entry.getKey();
            String value = entry.getValue();  
            System.out.println(key+"的CP是:"+value);
        }
    }

LinkedHashMap

我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢? 在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。

 public static void main(String[] args) {
        LinkedHashMap<String,String> map = new LinkedHashMap<String,String>();
        map.put("邓超", "孙俪");
        map.put("李晨", "范冰冰");
        map.put("刘德华", "朱丽倩");
        Set<Map.Entry<String,String>> entrySet = map.entrySet();
        for (Map.Entry<String,String> entry : entrySet){
            System.out.println(entry.getKey()+entry.getValue());
        }
    }

Map集合练习

需求:

计算一个字符串中每个字符出现次数。

分析:

  1. 获取一个字符串对象
  2. 创建一个Map集合,键代表字符,值代表次数
  3. 遍历字符串得到每个字符。
  4. 判断Map中是否有该键。
  5. 如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储。
  6. 打印最终结果
public static void main(String[] args) {
        //友情提示
        System.out.println("请录入一个字符串:");
        String line = new Scanner(System.in).nextLine();
        // 定义 每个字符出现次数的方法
        findChar(line);
    }
    private static void findChar(String line) {
        //1:创建一个集合 存储  字符 以及其出现的次数
        HashMap<Character, Integer> map = new HashMap<Character, Integer>();
        //2:遍历字符串
        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);
            //判断 该字符 是否在键集中
            if (!map.containsKey(c)) {//说明这个字符没有出现过
                //那就是第一次
                map.put(c, 1);
            } else {
                //先获取之前的次数
                Integer count = map.get(c);
                //count++;
                //再次存入  更新
                map.put(c, ++count);
            }
        }
        System.out.println(map);
    }

JDK9对集合添加的优化

Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。

public class HelloJDK9 {  
    public static void main(String[] args) {  
        Set<String> str1=Set.of("a","b","c");  
        //str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合  
        System.out.println(str1);  
        Map<String,Integer> str2=Map.of("a",1,"b",2);  
        System.out.println(str2);  
        List<String> str3=List.of("a","b");  
        System.out.println(str3);  
    }  
} 

1: of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如    HashSet,ArrayList等待;
2: 返回的集合是不可变的;

模拟斗地主洗牌发牌

案例规则
  1. 组装54张扑克牌将
  2. 54张牌顺序打乱
  3. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
  4. 查看三人各自手中的牌(按照牌的大小排序)、底牌

规则:手中扑克牌从大到小的摆放顺序:大王,小王,2,A,K,Q,J,10,9,8,7,6,5,4,3

案例需求分析
  1. 准备牌:

​ 完成数字与纸牌的映射关系:

​ 使用双列Map(HashMap)集合,完成一个数字与字符串纸牌的对应关系(相当于一个字典)。

  1. 洗牌:

​ 通过数字完成洗牌发牌

  1. 发牌:

​ 将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

​ 存放的过程中要求数字大小与斗地主规则的大小对应。

​ 将代表不同纸牌的数字分配给不同的玩家与底牌。

  1. 看牌:

​ 通过Map集合找到对应字符展示。

​ 通过查询纸牌与数字的对应关系,由数字转成纸牌字符串再进行展示

public static void main(String[] args) {
        /*
         * 1组装54张扑克牌
         */
        // 1.1 创建Map集合存储
        HashMap<Integer, String> pokerMap = new HashMap<Integer, String>();
        // 1.2 创建 花色集合 与 数字集合
        ArrayList<String> colors = new ArrayList<String>();
        ArrayList<String> numbers = new ArrayList<String>();

        // 1.3 存储 花色 与数字
        Collections.addAll(colors, "♦", "♣", "♥", "♠");
        Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        // 设置 存储编号变量
        int count = 1;
        pokerMap.put(count++, "大王");
        pokerMap.put(count++, "小王");
        // 1.4 创建牌 存储到map集合中
        for (String number : numbers) {
            for (String color : colors) {
                String card = color + number;
                pokerMap.put(count++, card);
            }
        }
        /*
         * 2 将54张牌顺序打乱
         */
        // 取出编号 集合
        Set<Integer> numberSet = pokerMap.keySet();
        // 因为要将编号打乱顺序 所以 应该先进行转换到 list集合中
        ArrayList<Integer> numberList = new ArrayList<Integer>();
        numberList.addAll(numberSet);

        // 打乱顺序
        Collections.shuffle(numberList);

        // 3 完成三个玩家交替摸牌,每人17张牌,最后三张留作底牌
        // 3.1 发牌的编号
        // 创建三个玩家编号集合 和一个 底牌编号集合
        ArrayList<Integer> noP1 = new ArrayList<Integer>();
        ArrayList<Integer> noP2 = new ArrayList<Integer>();
        ArrayList<Integer> noP3 = new ArrayList<Integer>();
        ArrayList<Integer> dipaiNo = new ArrayList<Integer>();

        // 3.2发牌的编号
        for (int i = 0; i < numberList.size(); i++) {
            // 获取该编号
            Integer no = numberList.get(i);
            // 发牌
            // 留出底牌
            if (i >= 51) {
                dipaiNo.add(no);
            } else {
                if (i % 3 == 0) {
                    noP1.add(no);
                } else if (i % 3 == 1) {
                    noP2.add(no);
                } else {
                    noP3.add(no);
                }
            }
        }

        // 4 查看三人各自手中的牌(按照牌的大小排序)、底牌
        // 4.1 对手中编号进行排序
        Collections.sort(noP1);
        Collections.sort(noP2);
        Collections.sort(noP3);
        Collections.sort(dipaiNo);

        // 4.2 进行牌面的转换
        // 创建三个玩家牌面集合 以及底牌牌面集合
        ArrayList<String> player1 = new ArrayList<String>();
        ArrayList<String> player2 = new ArrayList<String>();
        ArrayList<String> player3 = new ArrayList<String>();
        ArrayList<String> dipai = new ArrayList<String>();

        // 4.3转换
        for (Integer i : noP1) {
            // 4.4 根据编号找到 牌面 pokerMap
            String card = pokerMap.get(i);
            // 添加到对应的 牌面集合中
            player1.add(card);
        }

        for (Integer i : noP2) {
            String card = pokerMap.get(i);
            player2.add(card);
        }
        for (Integer i : noP3) {
            String card = pokerMap.get(i);
            player3.add(card);
        }
        for (Integer i : dipaiNo) {
            String card = pokerMap.get(i);
            dipai.add(card);
        }

        //4.5 查看
        System.out.println("令狐冲:"+player1);
        System.out.println("石破天:"+player2);
        System.out.println("鸠摩智:"+player3);
        System.out.println("底牌:"+dipai);
    }

异常体系

异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error(工程师不能处理,只能尽量避免)与java.lang.Exception,平常所说的异常指java.lang.Exception(由于使用不当导致,可以避免的)。

Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。

Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。

    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

  • public String getMessage():获取发生异常的原因。

    提示给用户的时候,就提示错误原因。

  • public String toString():获取异常的类型和异常描述信息(不用)。

异常的处理

Java异常处理的五个关键字:try、catch、finally、throw、throws

throw new 异常类名(参数)
throw new NullPointerExcerption("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("该索引所在数组不存在,已超出范围");

如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续讲问题声明出去,使用throws声明处理。

public static int getElement(int[] arr, int index){
        if (index < 0 || index > arr.length - 1){
            throw new ArrayIndexOutOfBoundsException("越界");
        }
        int element = arr[index];
        return element;
    }

    public static void main(String[] args) {
        int[] arr = {1,3,6,8,10};
        int index = 5;
        int element = getElement(arr, index);
        System.out.println(element);
    }

声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{   }    

throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

public class ThrowsDemo2 {
    public static void main(String[] args) throws IOException {
        read("a.txt");
    }

    public static void read(String path)throws FileNotFoundException, IOException {
        if (!path.equals("a.txt")) {//如果不是 a.txt这个文件 
            // 我假设  如果不是 a.txt 认为 该文件不存在 是一个错误 也就是异常  throw
            throw new FileNotFoundException("文件不存在");
        }
        if (!path.equals("b.txt")) {
            throw new IOException();
        }
    }
}

捕获异常try…catch

如果异常出现的话,会立刻终止程序,所以我们得处理异常:

  1. 该方法不处理,而是声明抛出,由该方法的调用者来处理(throws)。
  2. 在方法中使用try-catch的语句块来处理异常。

try-catch的方式就是捕获异常。

  • 捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
try{
    编写可能会出现异常的代码
}catch(异常类型 e){
    处理异常的代码
}
//记录日志/打印异常信息/继续抛出异常

try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。

注意:try和catch都不能单独使用,必须连用。

public String getMessage():获取异常的描述信息,提示给用户的时候提示错误原因
public String toString():获取异常的类型和异常描述信息
public void printStoackTralce():打印异常的跟踪栈信息并输出到控制台
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

finally代码块

因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

try…catch….finally:自身需要处理异常,最终还得关闭资源。[注意:finally不能单独使用。]

try{
     编写可能会出现异常的代码
}catch(异常类型A  e){  当try中出现A类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}catch(异常类型B  e){  当try中出现B类型异常,就用该catch来捕获.
     处理异常的代码
     //记录日志/打印异常信息/继续抛出异常
}
注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出。

  • 如果finally有return语句,永远返回finally中的结果,避免该情况.

  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。

  • 父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

自定义异常练习

// 业务逻辑异常
public class RegisterException extends Exception {
    /**
     * 空参构造
     */
    public RegisterException() {
    }

    /**
     *
     * @param message 表示异常提示
     */
    public RegisterException(String message) {
        super(message);
    }
}
====================================================
public class Demo {
    // 模拟数据库中已存在账号
    private static String[] names = {"bill","hill","jill"};
   
    public static void main(String[] args) {     
        //调用方法
        try{
              // 可能出现异常的代码
            checkUsername("nill");
            System.out.println("注册成功");//如果没有异常就是注册成功
        }catch(RegisterException e){
            //处理异常
            e.printStackTrace();
        }
    }

    //判断当前注册账号是否存在
    //因为是编译期异常,又想调用者去处理 所以声明该异常
    public static boolean checkUsername(String uname) throws LoginException{
        for (String name : names) {
            if(name.equals(uname)){//如果名字在这里面 就抛出登陆异常
                throw new RegisterException("亲"+name+"已经被注册了!");
            }
        }
        return true;
    }
}

创建线程类

Java使用java.lang.Thread类代表线程

Java中通过继承Thread类来创建启动多线程的步骤如下:

  1. 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
  2. 创建Thread子类的实例,即创建了线程对象
  3. 调用线程对象的start()方法来启动该线程
public class Demo01 {
    public static void main(String[] args) {
        //创建自定义线程对象
        MyThread mt = new MyThread("新的线程!");
        //开启新线程
        mt.start();
        //在主方法中执行for循环
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程!"+i);
        }
    }
}
自定义线程类
public class MyThread extends Thread {
    //定义指定线程名称的构造方法
    public MyThread(String name) {
        //调用父类的String参数的构造方法,指定线程的名称
        super(name);
    }
    /**
     * 重写run方法,完成该线程执行的逻辑
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+":正在执行!"+i);
        }
    }
}

继承Thread类方式

完成操作过程中用到了java.lang.Thread

public Thread():分配一个新的线程对象
public Thread(String name):分配一个指定名字的新的线程对象
public Thread(Runnable target):分配一个带有指定目标新的线程对象
public Thread(Runnable target, String name):分配一个带有指定目标新的线程对象并指定名字
    
常用方法:
public String getName():获取当前线程名称
public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法
public void run():此线程要执行的任务在此处定义代码
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停
public static Thread currentThread():返回对当前正在执行的线程对象的引用

实现Runnable接口方式

采用java.lang.Runnable类,只需要重写run方法即可
步骤如下:
1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
2.创建Runnable实现类的实例,并以此实例为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
3.调用线程对象的start()方法来启动线程

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
        System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        MyThread mr = new MyThread();
        Thread t = new Thread(mr, "小白");
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("旺财" + i);
        }
    }
}

通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程 代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread 对象的start()方法来运行多线程代码。 实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现 Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程 编程的基础。

Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。 而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。

Thread和Runnable的区别

如果一个类继承Thread, 则不适合资源共享。但是如果实现了Runnable接口的话,则很容易的实现资源共享

实现Runnable接口比继承Thread类所具有的优势:

1.适合多个相同的程序代码的线程去共享同一个资源
2.可以避免java中的单继承的局限性
3.增加程序的健壮性,实现解耦操作,代码额可以被多个线程共享, 代码和线程独立
4.线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类

在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用 java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进 程。

线程同时安全

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样 的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

public class Ticket implements Runnable{
    private int ticket = 100;
    @Override
    public void run() {
        while(true){
            if (ticket > 0){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
                String name = Thread.currentThread().getName();
                System.out.println(name + ticket--);
            }
        }
    }
}

public class test {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread t1 = new Thread(my,"窗口1 ");
        Thread t2 = new Thread(my,"窗口2 ");
        Thread t3 = new Thread(my,"窗口3 ");
        t1.start();
        t2.start();
        t3.start();
    }
}
怎么使用Java线程同步机制 ?

1.同步代码块
2.同步方法
3.锁机制

同步代码块

  • 同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
synchronized(同步锁){
    需要同步操作的代码
}
------------------------------------------------------------------
public void run() {
        while(true){
            synchronized (lock){
                if (ticket > 0){
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    String name = Thread.currentThread().getName();
                    System.out.println(name + ticket--);
                }
            }
       }

同步方法

  • 同步方法:synchronized 修饰的方法, 叫做同步方法. 保证A线程执行该方法的时候, 其他线程只能在方法外等着
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

Lock锁

java.util.concurrent.locks.Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法 具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁释放锁方法化了

public class test {
    public static void main(String[] args) {
        MyRunnable my = new MyRunnable();
        Thread t1 = new Thread(my,"窗口1 ");
        Thread t2 = new Thread(my,"窗口2 ");
        Thread t3 = new Thread(my,"窗口3 ");
        t1.start();
        t2.start();
        t3.start();
    }
}
----------------------------------------------
public class MyRunnable implements Runnable{
    private int ticket = 100;
    Lock lock = new ReentrantLock();
     @Override
      public void run() {
        while(true){
            lock.lock();
            if (ticket > 0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                String name = Thread.currentThread().getName();
                System.out.println(name + ticket--);
            }
            lock.unlock();
        }
    }
}

线程状态概述

java.lang.Thread.State这个枚举中给出了六种线程状态:

线程状态 导致状态发生条件
New(新建) 线程刚被创建, 但是并未启动, 还没条用start方法
Runnable(可运行) 线程可以在java虚拟机中运行的状态, 可能正在运行自己代码, 也可能没有, 这取决于操作系统处理器
Blocked(锁阻塞) 当一个线程试图获取一个对象锁, 而该对象锁被其他的线程持有, 则该线程进入Blocked状态; 当该线程持有锁时, 该线程将变成Runnable状态
Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时, 该线程进入Waiting状态。进入这个状态后是不能自动唤醒的, 必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
TimedWaiting(计时终止) 同waiting状态, 有几个方法有超时参数, 调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法又Thread.sleep、Object.wait
Teminated(被终止) 因为run方法正常退出而死亡, 或者没有因为捕获的异常终止了run方法而死亡

一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify()方法 或 Object.notifyAll()方法。

其实waiting状态并不是一个线程的操作,它体现的是多个线程间的通信,可以理解为多个线程之间的协作关系, 多个线程会争取锁,同时相互之间又存在协作关系。就好比在公司里你和你的同事们,你们可能存在晋升时的竞 争,但更多时候你们更多是一起合作以完成某些任务。

当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入 了Waiting(无限等待)状态,同时失去了同步锁。假如这个时候B线程获取到了同步锁,在运行状态中调用了 notify()方法,那么就会将无限等待的A线程唤醒。注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入 Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)

public class test {
    public static Object obj = new Object();

    public static void main(String[] args) {
// 演示waiting
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "=== 获取到锁对象,调用wait方法,进入waiting状态,释放锁对象");
                            obj.wait(); //无限等待
//obj.wait(5000); //计时等待, 5秒 时间到,自动醒来
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "=== 从waiting状态醒来,获取到锁对象,继续执行了");
                    }
                }
            }
        }, "等待线程").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
// while (true){ //每隔3秒 唤醒一次
                try {
                    System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 等待3秒钟");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj) {
                    System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 获取到锁对象,调用notify方法,释放锁对象");
                    obj.notify();
                }
            }
// }
        }, "唤醒线程").start();
    }
}

等待唤醒机制

1.wait: 线程不再活动, 不再参与调度, 进入wait set中, 因此不会浪费CPU资源, 也不会去竞争锁, 这时的线程状态即是Waiting。它还要等着别的线程执行一个特别的动作, 也即是”通知(notify)“在这个对象上等待的线程从wait set中释放出来, 重新进入到调度队列(ready queue)中
2.notify: 选取所通知对象的wait set中的一个线程释放:例如, 餐馆有空位置后, 等候就餐最久的顾客最先入座
3.notifyAll: 释放所通知的对象的wait set上的全部线程

哪怕只通知了一个等待的线程, 被通知线程也不能立即恢复执行, 因为它当初终端的地方是在同步块内, 而此刻它偶已经不持有锁, 所以她需要再次尝试去获取锁(可能面临其他线程的竞争), 成功后才能在当初调用wait方法之后的地方恢复执行

  • 如果能获取锁, 线程就从Waiting状态变成Bunnable状态
  • 否则, 从wait set出来, 又进入entry set, 线程就从Waiting状态变成了Blocked状态
调用wait和notify方法需要注意的细节

1.wait方法与notify方法必须由同一锁对象调用 因为对应锁对象可以通过notify唤醒使用同一锁对象调用的wait方法后的线程
2.wait方法与notify方法是属于Object类的方法的 因为锁对象是可以是任意对象,而任意对象的所属类都是继承了Object类的
3.wait方法与notify方法必须要在同步代码块或者是同步函数中使用 因为必须通过锁对象调用这2个方法

void notify():唤醒在此对象监视器上等待的单个线程
void notifyAll():唤醒在此对象监视器上等待的所有线程
void wait():导致当前的线程等待, 直到其他线程调用此对象的notify()方法或者notifyAll()方法
void wait(long timeout):导致当前的线程等待, 直到其他线程调用此对象的notify()方法或notifyAll()方法, 或者指定的时间过完
void wait(long timeout, intik nanos):导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法,或者其他线程打断了当前线程,或者指定的时间过完。
  • wait( ),notify( ),notifyAll( )都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。
  • 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常
  • 当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。
  • 在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知
  • 调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。
  • notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)
  • notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)
  • 假设有三个线程执行了obj.wait(),那么obj.notifyAll()则能全部唤醒thread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获取obj锁,因此,thread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。
  • 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行

线程池

Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService

Executoturs类中有个创建线程池的方法:
public static ExecutorService nweFixedThreadPool(int nThreads):返回线程池对象(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)

获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:
public Future<?> submit(Runnable task):获取线程池中的某一个线程对象并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建于使用

使用线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接口子类对象。(task)
  3. 提交Runnable接口子类对象。(take task)
  4. 关闭线程池(一般不做)。
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,交完后,教练回到了游泳池");
    }
}
==========================================================
public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
        // 创建Runnable实例对象
        MyRunnable r = new MyRunnable();

        //自己创建线程对象的方式
        // Thread t = new Thread(r);
        // t.start(); ---> 调用MyRunnable中的run()

        // 从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(r);
        // 再获取个线程对象,调用MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        // 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
        // 将使用完的线程又归还到了线程池中
        // 关闭线程池
        //service.shutdown();
    }
}

Lambda表达式

强调做什么,而不是以什么形式做

面向对象的思想:

​ 做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.

函数式编程思想:

​ 只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下:

public class Demo01Runnable {
    public static void main(String[] args) {
        // 匿名内部类
        Runnable task = new Runnable() {
            @Override
            public void run() { // 覆盖重写抽象方法
                System.out.println("多线程任务执行!");
            }
        };
        new Thread(task).start(); // 启动线程
    }
}

本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。

代码分析

对于Runnable的匿名内部类用法,可以分析出几点内容:

  • Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;
  • 为了指定run的方法体,不得不需要Runnable接口的实现类;
  • 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;
  • 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
  • 而实际上,似乎只有方法体才是关键所在

上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

public class Demo02LambdaRunnable {
    public static void main(String[] args) {
        new Thread(() -> System.out.println("多线程任务执行!")).start(); // 启动线程
    }
}

传统代码

public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println("多线程任务执行!");
    }
}

public class Demo03ThreadInitParam {
    public static void main(String[] args) {
        Runnable task = new RunnableImpl();
        new Thread(task).start();
    }
}

使用匿名内部类

匿名内部类的好处与弊端
一方面,匿名内部类可以帮我们省去实现类的定义;另一方面,匿名内部类的语法——确实太复杂了!

public class Demo04ThreadNameless {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("多线程任务执行!");
            }
        }).start();
    }
}

即制定了一种做事情的方案(其实就是一个函数):

  • 无参数:不需要任何条件即可执行该方案。
  • 无返回值:该方案不产生任何结果。
  • 代码块(方法体):该方案的具体执行步骤。

同样的语义体现在Lambda语法中,要更加简单:

() -> System.out.println("多线程任务执行!")
  • 前面的一对小括号即run方法的参数(无),代表不需要任何条件;
  • 中间的一个箭头代表将前面的参数传递给后面的代码;
  • 后面的输出语句即业务逻辑代码。

Lambda标准格式

Lambda省去面向对象的条条框框,格式由3个部分组成:

  • 一些参数
  • 一个箭头
  • 一段代码

Lambda表达式的标准格式为:

(参数类型 参数名称) -> { 代码语句 }

格式说明:

  • 小括号内的语法与传统方法参数列表一致:无参数则留空;多个参数则用逗号分隔。
  • ->是新引入的语法格式,代表指向动作。
  • 大括号内的语法与传统方法体要求基本一致。

Lambda的参数和返回值

需求:
使用数组存储多个Person对象
对数组中的Person对象使用Arrays的sort方法通过年龄进行升序排序

当需要对一个对象数组进行排序时,Arrays.sort方法需要一个Comparator接口实例来指定排序的规则。假设有一个Person类,含有String nameint age两个成员变量:

public class Person { 
    private String name;
    private int age;
    
    // 省略构造器、toString方法与Getter Setter 
}

import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
    public static void main(String[] args) {
          // 本来年龄乱序的对象数组
        Person[] array = {
            new Person("古力娜扎", 19),
            new Person("迪丽热巴", 18),
               new Person("马尔扎哈", 20) };

          // 匿名内部类
        Comparator<Person> comp = new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge() - o2.getAge();
            }
        };
        Arrays.sort(array, comp); // 第二个参数为排序规则,即Comparator接口实例

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

这种做法在面向对象的思想中,似乎也是“理所当然”的。其中Comparator接口的实例(使用了匿名内部类)代表了“按照年龄从小到大”的排序规则。

代码分析

下面我们来搞清楚上述代码真正要做什么事情。

  • 为了排序,Arrays.sort方法需要排序规则,即Comparator接口的实例,抽象方法compare是关键;
  • 为了指定compare的方法体,不得不需要Comparator接口的实现类;
  • 为了省去定义一个ComparatorImpl实现类的麻烦,不得不使用匿名内部类;
  • 必须覆盖重写抽象compare方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;
  • 实际上,只有参数和方法体才是关键
Lambda写法
import java.util.Arrays;

public class Demo07ComparatorLambda {
    public static void main(String[] args) {
        Person[] array = {
              new Person("古力娜扎", 19),
              new Person("迪丽热巴", 18),
              new Person("马尔扎哈", 20) };

        Arrays.sort(array, (Person a, Person b) -> {
              return a.getAge() - b.getAge();
        });

        for (Person person : array) {
            System.out.println(person);
        }
    }
}

省略规则

在Lambda标准格式的基础上,使用省略写法的规则为:

  1. 小括号内参数的类型可以省略;
  2. 如果小括号内有且仅有一个参,则小括号可以省略;
  3. 如果大括号内有且仅有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

备注:掌握这些省略规则后,请对应地回顾本章开头的多线程案例。

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法
    无论是JDK内置的RunnableComparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
  2. 使用Lambda必须具有上下文推断
    也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

File类

public File(String pathname):通过将给定的路径名字字符串转化为抽象路径名来创建新的File实例
public File(String parent, String child):从父路径名字符串和子路径名字符串创建新的File实例
public File(File parent, String child):从父抽象路径名和子路径名字符串创建新的File实例
// 文件路径名
String pathname = "D:\\aaa.txt";
File file1 = new File(pathname); 

// 文件路径名
String pathname2 = "D:\\aaa\\bbb.txt";
File file2 = new File(pathname2); 

// 通过父路径和子路径字符串
 String parent = "d:\\aaa";
 String child = "bbb.txt";
 File file3 = new File(parent, child);

// 通过父级File对象和子路径字符串
File parentDir = new File("d:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child);

小贴士:
1. 一个File对象代表硬盘中实际存在的一个文件或者目录。
2. 无论该路径下是否存在文件或者目录,都不影响File对象的创建。
常用方法
public String getAbsolutePath():返回此File的绝对路径名字符串
public String getPath():将此File转换为路径名字符串
public String getName():返回由此File表示的文件或目录的名称
public long length():返回由此File表示的文件的长度
public class FileGet {
    public static void main(String[] args) {
        File f = new File("d:/aaa/bbb.java");     
        System.out.println("文件绝对路径:"+f.getAbsolutePath());
        System.out.println("文件构造路径:"+f.getPath());
        System.out.println("文件名称:"+f.getName());
        System.out.println("文件长度:"+f.length()+"字节");

        File f2 = new File("d:/aaa");     
        System.out.println("目录绝对路径:"+f2.getAbsolutePath());
        System.out.println("目录构造路径:"+f2.getPath());
        System.out.println("目录名称:"+f2.getName());
        System.out.println("目录长度:"+f2.length());
    }
}
输出结果:
文件绝对路径:d:\aaa\bbb.java
文件构造路径:d:\aaa\bbb.java
文件名称:bbb.java
文件长度:636字节

目录绝对路径:d:\aaa
目录构造路径:d:\aaa
目录名称:aaa
目录长度:4096
API中说明:length(),表示文件的长度。但是File对象表示目录,则返回值未指定。
绝对路径和相对路径
  • 绝对路径:从盘符开始的路径,这是一个完整的路径。
  • 相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中经常使用。
public class FilePath {
    public static void main(String[] args) {
          // D盘下的bbb.java文件
        File f = new File("D:\\bbb.java");
        System.out.println(f.getAbsolutePath());
          
        // 项目下的bbb.java文件
        File f2 = new File("bbb.java");
        System.out.println(f2.getAbsolutePath());
    }
}
输出结果:
D:\bbb.java
D:\idea_project_test4\bbb.java
判断功能的方法
public boolean exists():此File表示的文件或目录是否真实存在
public boolean isDirectory():此File表示的是否为目录
public boolean isFile():此File表示的是否为文件
创建删除功能的方法
public boolean createNewFile():当且仅当具有该名称的文件尚不存在时候,创建一个新的空文件
public boolean delete():删除由此File表示的文件或目录
public boolean mkdir():创建由此File表示的目录
public boolean mkdirs():创建由此File表示的目录,包括任何必须旦不存在的父目录
public class FileCreateDelete {
    public static void main(String[] args) throws IOException {
        // 文件的创建
        File f = new File("aaa.txt");
        System.out.println("是否存在:"+f.exists()); // false
        System.out.println("是否创建:"+f.createNewFile()); // true
        System.out.println("是否存在:"+f.exists()); // true
        
         // 目录的创建
          File f2= new File("newDir");    
        System.out.println("是否存在:"+f2.exists());// false
        System.out.println("是否创建:"+f2.mkdir());    // true
        System.out.println("是否存在:"+f2.exists());// true

        // 创建多级目录
          File f3= new File("newDira\\newDirb");
        System.out.println(f3.mkdir());// false
        File f4= new File("newDira\\newDirb");
        System.out.println(f4.mkdirs());// true
      
          // 文件的删除
           System.out.println(f.delete());// true
      
          // 目录的删除
        System.out.println(f2.delete());// true
        System.out.println(f4.delete());// false
    }
}
API中说明:delete方法,如果此File表示目录,则目录必须为空才能删除。
目录的遍历
public String[] list():返回一个String数组,表示该FIle目录中的所有子文件或目录
public File[] listFiles():返回一个File数组,表示该FIle目录中的所有的子文件或目录
public class FileFor {
    public static void main(String[] args) {
        File dir = new File("d:\\java_code");
      
          //获取当前目录下的文件以及文件夹的名称。
        String[] names = dir.list();
        for(String name : names){
            System.out.println(name);
        }
        //获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息
        File[] files = dir.listFiles();
        for (File file : files) {
            System.out.println(file);
        }
    } //打印全文件地址名称

递归

递归打印多级目录

分析:多级目录的打印,就是当目录的嵌套。遍历之前,无从知道到底有多少级目录,所以我们还是要使用递归实现。

代码实现

public class DiGuiDemo2 {
    public static void main(String[] args) {
          // 创建File对象
        File dir  = new File("D:\\aaa");
          // 调用打印目录方法
        printDir(dir);
    }

    public static void  printDir(File dir) {
          // 获取子文件和目录
        File[] files = dir.listFiles();
          // 循环打印
          /*
            判断:
            当是文件时,打印绝对路径.
            当是目录时,继续调用打印目录的方法,形成递归调用.
          */
        for (File file : files) {
            // 判断
            if (file.isFile()) {
                  // 是文件,输出文件绝对路径
                System.out.println("文件名:"+ file.getAbsolutePath());
            } else {
                  // 是目录,输出目录绝对路径
                System.out.println("目录:"+file.getAbsolutePath());
                  // 继续遍历,调用printDir,形成递归
                printDir(file);
            }
        }
    }
}

文件搜索案例

搜索D:\aaa 目录中的.java 文件。

分析

  1. 目录搜索,无法判断多少级目录,所以使用递归,遍历所有目录。
  2. 遍历目录时,获取的子文件,通过文件名称,判断是否符合条件。

代码实现

public class test {
    public static void main(String[] args) {
        File file = new File("D:\\NotePad++");
        printFile(file);
    }
    public static void printFile(File file){
        File[] files = file.listFiles();
        for (File f1 : files){
            if (f1.isFile()){
                if (f1.getName().endsWith(".xml")){
                    System.out.println("文件名:"+f1.getAbsolutePath());
                }
            }else {
                printFile(f1);
            }
        }
    }
}

文件过滤器优化

java.io.FileFilter是一个接口,是File的过滤器。 该接口的对象可以传递给File类的 listFiles(FileFilter) 作为参数, 接口中只有一个方法。

boolean accept(File pathname):测试pathname是否应该包含在当前File目录中,符合则返回true。

分析

  1. 接口作为参数,需要传递子类对象,重写其中方法。我们选择匿名内部类方式,比较简单。
  2. accept方法,参数为File,表示当前File下所有的子文件和子目录。保留住则返回true,过滤掉则返回false。保留规则:
    1. 要么是.java文件。
    2. 要么是目录,用于继续遍历。
  3. 通过过滤器的作用,listFiles(FileFilter)返回的数组元素中,子文件对象都是符合条件的,可以直接打印。

代码实现:

public class DiGuiDemo4 {
    public static void main(String[] args) {
        File dir = new File("D:\\aaa");
        printDir2(dir);
    }
  
    public static void printDir2(File dir) {
          // 匿名内部类方式,创建过滤器子类对象
        File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".java")||pathname.isDirectory();
            }   //public boolean isDirectory():此File表示的是否为目录
        });
          // 循环打印
        for (File file : files) {
            if (file.isFile()) {
                System.out.println("文件名:" + file.getAbsolutePath());
            } else {
                printDir2(file);
            }
        }
    }
}      

Lambda优化

分析:FileFilter是只有一个方法的接口,因此可以用lambda表达式简写。

lambda格式:

()->{ }

代码实现:

public static void printDir3(File dir) {
      // lambda的改写
    File[] files = dir.listFiles(f ->{ 
          return f.getName().endsWith(".java") || f.isDirectory(); 
    });
      
    // 循环打印
    for (File file : files) {
        if (file.isFile()) {
            System.out.println("文件名:" + file.getAbsolutePath());
          } else {
            printDir3(file);
          }
    }
}

字节流、字符流

顶级父类们
输入流 输出流
字节流 字节输入流 InputStream 字节输出流 OutputStream
字符流 字符输入流 Reader 字符输出流 Writer
字节输出流 OutputStream类

java.io.OutputStream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

public void close():关闭此输出流并释放与此流相关联的任何系统资源
public void flush():刷新此输出流并强制任何缓冲的输出字节被写出
public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流
public void write(byte[] b, in off, int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
public abstract void write(int b):将指定的字节输出流
// close方法,当完成流的操作时,必须调用此方法,释放系统资源。
字节输出流 FileOutputStream类

OutputStream有很多子类,我们从最简单的一个子类开始。
java.io.FileOutputStream 类是文件输出流,用于**将数据写出到文件**。

public FileOutputStream(File file):创建文件输出流以写入由指定的File对象表示的文件
public FileOutputStream(String name):创建文件输出流以指定的名称写入文件

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

public class FileOutputStreamConstructor throws IOException {
    public static void main(String[] args) {
           // 使用文件名称创建流对象
        FileOutputStream fos = new FileOutputStream("D:\Clash\a.txt");
        // 普通创建
        File file = new File("a.txt");
        FileOutputStream fos = new FileOutputStream(file);
    }
}

写出字节数据

写出字节write(int b) 方法,每次可以写出一个字节数据,代码使用演示:

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("D:\\Clash\\a.txt");
    fos.write(97); //a
    fos.close();
}
// 1. 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。
// 2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

写出字节数组write(byte[] b),每次可以写出数组中的数据,代码使用演示:

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("D:\\clash\\a.txt");
    yte[] b = "黑马程序员".getBytes();
    fos.write(b);
    fos.close();
}

数据追加续写

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?

public FileOutputStream(File file, boolean append):创建文件输出流以写入由指定的File对象表示的文件
public FileOutputStream(String name, boolean append):创建文件输出流以指定的名称写入文件

这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("D:\\Clash\\a.txt", true);
    byte[] b = "abcde".getBytes();
    fos.write(b);
    fos.close();
}
  • 回车符\r和换行符\n
    • 回车符:回到一行的开头(return)。
    • 换行符:下一行(newline)。
  • 系统中的换行:
    • Windows系统里,每行结尾是 回车+换行 ,即\r\n
    • Unix系统里,每行结尾只有 换行 ,即\n
    • Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一。

字节输入流InputStream

java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

public void close():关闭此输入流并释放与此流相关的任何系统资源
public abstract int read():从输入流读取数据的下一个字节
public int read(byte[] b):从输入流中读取一些字节数,并把它们存储到字节数组b中
close方法,当完成流的操作时,必须调用此方法,释放系统资源。

FileInputStream类

java.io.FileInputStream 类是文件输入流,从文件中读取字节。

构造方法
FileInputStream(File file):通过打开与实际文件的链接来创建一个FileInputStream,该文件由文件系统中的File对象 file命名
FileInputStream(String name):通过打开与实际文件的链接来创建一个FileInputStream,该文件由文件系统中的路径名 name命名。  
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException 。

读取字节read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1,代码使用演示:

public static void main(String[] args) throws IOException {
   FileInputStream fis = new FileInputStream("D:\\Clash\\a.txt");
   int b;
   while((b=fis.read())!= -1){
      System.out.println((char)b);
   }
    fis.close();
}
//1. 虽然读取了一个字节,但是会自动提升为int类型。
//2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

使用字节数组读取read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1 ,代码使用演示:

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("D:\\Clash\\a.txt");
    int len;
    byte[] b = new byte[2];
    while((len = fis.read(b)) != -1){
         System.out.println(new String(b,0,len));
    }
    fis.close();
 }
实现资源的复制
public static void main(String[] args) throws IOException {
    // 1.创建流对象
    // 1.1 指定数据源
    FileInputStream fis = new FileInputStream("D:\\Clash\\a.txt");
    // 1.2 指定目的地
    FileOutputStream fos = new FileOutputStream("D:\\7-Zip\\c.txt");
    // 2.读写数据
    // 2.1 定义数组
    byte[] b = new byte[1024];
    // 2.2 定义长度
    int len;
    // 2.3 循环读取
    while((len = fis.read(b))!=-1){
    // 2.4 写出数据
        fos.write(b,0,len);
    }
    // 3.关闭资源
    fos.close();
    fis.close();
}
// 流的关闭原则:先开后关,后开先关。

FileReader类

FileReader(File file):创建一个新的FileReader,给定要读取的File对象
FileReader(String fileName):创建一个新的FileReader,给定要读取的文件的名称
public static void main(String[] args) throws IOException {
    FileReader fr = new FileReader("D:\\Clash\\a.txt");
    int len;
    char[] cbuf = new char[1024];
    while((len = fr.read(cbuf))!=-1){
        System.out.println(new String(cbuf,0,len));
    }
    fr.close();
}

字符输出流Writer

java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

void write(int c):写入单个字符
void write(char[] cbuf):写入字符数组
abstract void weite(char[] cbuf, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数
void write(String str):写入字符串
void write(String str, int off, int len):写入字符串的某一部分,off字符串的开始索引,len写的字符个数
void flush():刷新该流的缓冲
void close():关闭此流,但要先刷新它

FileWriter类

java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

FileWriter(File file):创建一个新的FileWriter,给定要读取的File对象
FileWeiter(String fileName):创建一个新的FileWeiter,给定要读取的文件的名称
public static void main(String[] args) throws IOException {
            // 使用File对象创建流对象
        File file = new File("a.txt");
        FileWriter fw = new FileWriter(file);
      
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("b.txt");
    }
}

关闭和刷新

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。

  • flush :刷新缓冲区,流对象可以继续使用。
  • close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了。

即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源。

写出其他数据

写出字符数组write(char[] cbuf)write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream

写出字符串write(String str)write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便

续写和换行:操作类似于FileOutputStream。

字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流

字符:是指计算机中使用的字母、数字、字和符号,包括:1、2、3、A、B、C、~!·#¥%……—*()——+等等。在ASCII编码中,一个英文字母字符存储需要1个字节。

字节:计算机存储容量基本单位是字节(Byte),音译为拜特,8个二进制位组成1个字节,一个标准英文字母占一个字节位置,一个标准汉字占二个字节位置。计算机存储容量大小以字节数来度量。

异常的处理(回顾)

之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用try...catch...finally 代码块,处理异常部分,代码使用演示:

public static void main(String[] args) throws IOException {
        FileWriter fw = null;
        try {
            fw = new FileWriter("D:\\Clash\\a.txt");
            fw.write("黑马程序员");
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try{
                if (fw != null){
                    fw.close();
                }
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }

try (创建流对象语句,如果多个,使用’;’隔开) {
// 读写数据
} catch (IOException e) {
e.printStackTrace();
}

public class HandleException2 {
    public static void main(String[] args) {
          // 创建流对象
        try ( FileWriter fw = new FileWriter("fw.txt"); ) {
            // 写出数据
            fw.write("黑马程序员"); //黑马程序员
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

JDK9中try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,我们来了解一下格式。

public class TryDemo {
    public static void main(String[] args) throws IOException {
           // 创建流对象
        final  FileReader fr  = new FileReader("in.txt");
        FileWriter fw = new FileWriter("out.txt");
           // 引入到try中
        try (fr; fw) {
              // 定义变量
            int b;
              // 读取数据
              while ((b = fr.read())!=-1) {
                // 写出数据
                fw.write(b);
              }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Properties类

构造方法

  • public Properties() :创建一个空的属性列表。

基本的存储方法

public Object setProperty(String key, String value):保存一对属性
public String getProperty(String key):使用此属性列表中指定的键搜索属性值
public Set<String> stringPropertyNames():所有键的名称的集合
public class ProDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties properties = new Properties();
        // 添加键值对元素
        properties.setProperty("filename", "a.txt");
        properties.setProperty("length", "209385038");
        properties.setProperty("location", "D:\\a.txt");
        // 打印属性集对象
        System.out.println(properties);
        // 通过键,获取属性值
        System.out.println(properties.getProperty("filename"));
        System.out.println(properties.getProperty("length"));
        System.out.println(properties.getProperty("location"));

        // 遍历属性集,获取所有键的集合
        Set<String> strings = properties.stringPropertyNames();
        // 打印键值对
        for (String key : strings ) {
              System.out.println(key+" -- "+properties.getProperty(key));
        }
    }
}
输出结果:
{filename=a.txt, length=209385038, location=D:\a.txt}
a.txt
209385038
D:\a.txt
filename -- a.txt
length -- 209385038
location -- D:\a.txt

与流相关的方法

public void load(InputStream inStream):从字节输入流中读取键值对

参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式:

filename=a.txt
length=209385038
location=D:\a.txt

加载代码演示:

public class ProDemo2 {
    public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties pro = new Properties();
        // 加载文本中信息到属性集
        pro.load(new FileInputStream("read.txt"));
        // 遍历集合并打印
        Set<String> strings = pro.stringPropertyNames();
        for (String key : strings ) {
              System.out.println(key+" -- "+pro.getProperty(key));
        }
     }
}
输出结果:
filename -- a.txt
length -- 209385038
location -- D:\a.txt

小贴士:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。

缓冲流

字节缓冲流 BufferedInputStream, BufferedOutputStream
字符缓冲流 BufferedReader, BudfferedWeiter

字节缓冲流

public BufferedInputStream(InputStream in):创建一个新的缓冲输入流
public BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
public static void main(String[] args) throws FileNotFoundException {
          // 记录开始时间
        long start = System.currentTimeMillis();
        // 创建流对象
        try (
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"));
        ){
              // 读写数据
            int len;
            byte[] bytes = new byte[8*1024];
            while ((len = bis.read(bytes)) != -1) {
                bos.write(bytes, 0 , len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 记录结束时间
        long end = System.currentTimeMillis();
        System.out.println("缓冲流使用数组复制时间:"+(end - start)+" 毫秒");
    }
缓冲流使用数组复制时间:666 毫秒

字符缓冲流

public BufferedReader(Reader in):创建一个新的缓冲输入流
public BufferedReader(Writer out):创建一个新的缓冲输出流

字节(Byte) 是计量单位,表示数据量多少,是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于八位。
**字符(Character) ** 是计算机中使用的字母、数字、字和符号,比如’A’、’B’、’$’、’&’等。

特有方法
BufferedReader:public String readLine():读一行文字
bufferedWeiter:public void newLine():写一行行分隔符,由系统属性定义符号
readLine代码展示
public static void main(String[] args) throws IOException {
           // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
        // 定义字符串,保存读取的一行文字
        String line  = null;
          // 循环读取,读取到最后返回null
        while ((line = br.readLine())!=null) {
            System.out.print(line);
            System.out.println("------");
        }
        // 释放资源
        br.close();
    }
newLine代码展示
public static void main(String[] args) throws IOException  {
          // 创建流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));
          // 写出数据
        bw.write("黑马");
          // 写出换行
        bw.newLine();
        bw.write("程序");
        bw.newLine();
        bw.write("员");
        bw.newLine();
        // 释放资源
        bw.close();
    }
}
输出效果:
黑马
程序
员

练习: 文本排序

请将文本信息恢复顺序。

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

案例分析

  1. 逐行读取文本信息。
  2. 解析文本信息到集合中。
  3. 遍历集合,按顺序,写出文本信息。

案例实现

 public static void main(String[] args) throws IOException {
        // 创建map集合,保存文本数据,键为序号,值为文字
        HashMap<String, String> lineMap = new HashMap<>();

        // 创建流对象
        BufferedReader br = new BufferedReader(new FileReader("in.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));

        // 读取数据
        String line  = null;
        while ((line = br.readLine())!=null) {
            // 解析文本
            String[] split = line.split("\\.");
            // 保存到集合
            lineMap.put(split[0],split[1]);
        }
        // 释放资源
        br.close();

        // 遍历map集合
        for (int i = 1; i <= lineMap.size(); i++) {
            String key = String.valueOf(i);
            // 获取map中文本
            String value = lineMap.get(key);
              // 写出拼接文本
            bw.write(key+"."+value);
              // 写出换行
            bw.newLine();
        }
        // 释放资源
        bw.close();
    }
  • Integer valueOf(int i):返回一个表示指定的 int 值的 Integer 实例。
  • **Integer valueOf(String s):**返回保存指定的 String 的值的 Integer 对象。
  • Integer valueOf(String s, int radix): 返回一个 Integer 对象,该对象中保存了用第二个参数提供的基数进行解析时从指定的 String 中提取的值。

OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法
OutputStreamWriter(OutputStream in): 创建一个使用默认字符集的字符流
OutputStreamWriter(OutputStream in, String charsetName): 创建一个指定字符集的字符流
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
public static void main(String[] args) throws IOException {
          // 定义文件路径
        String FileName = "E:\\out.txt";
          // 创建流对象,默认UTF8编码
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
        // 写出数据
          osw.write("你好"); // 保存为6个字节
        osw.close();
        // 定义文件路径
        String FileName2 = "E:\\out2.txt";
         // 创建流对象,指定GBK编码
        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
        // 写出数据
          osw2.write("你好");// 保存为4个字节
        osw2.close();
 }

ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

public ObjectOutputStream(OutputStream out):创建一个指定OutputStream的ObjectOutputStream。

ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

  • public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream。

反序列化操作1

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

  • public final Object readObject () : 读取一个对象。

对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
  • 该类包含未知数据类型
  • 该类没有可访问的无参数构造方法

Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

序列化操作

  1. 一个对象要想序列化,必须满足两个条件:
  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。

一、是什么

序列化:就是将对象转化成字节序列的过程。

反序列化:就是讲字节序列转化成对象的过程。

对象序列化成的字节序列会包含对象的类型信息、对象的数据等,说白了就是包含了描述这个对象的所有信息,能根据这些信息“复刻”出一个和原来一模一样的对象。

二、为什么

那么为什么要去进行序列化呢?有以下两个原因

  1. 持久化:对象是存储在JVM中的堆区的,但是如果JVM停止运行了,对象也不存在了。序列化可以将对象转化成字节序列,可以写进硬盘文件中实现持久化。在新开启的JVM中可以读取字节序列进行反序列化成对象。
  2. 网络传输:网络直接传输数据,但是无法直接传输对象,可在传输前序列化,传输完成后反序列化成对象。所以所有可在网络上传输的对象都必须是可序列化的。

三、怎么做

怎么去实现对象的序列化呢?

Java为我们提供了对象序列化的机制,规定了要实现序列化对象的类要满足的条件和实现方法。

  1. 对于要序列化对象的类要去实现Serializable接口或者Externalizable接口
  2. 实现方法:JDK提供的ObjectOutputStream和ObjectInputStream来实现序列化和反序列化

下面分别实现Serializable和Externalizable接口来演示序列化和反序列化

public class Employee implements java.io.Serializable {
    public String name;
    public String address;
    public transient int age; // transient瞬态修饰成员,不会被序列化
    public void addressCheck() {
          System.out.println("Address  check : " + name + " -- " + address);
    }
}

public final void writeObject (Object obj) : 将指定的对象写出。
    
public class SerializeDemo{
       public static void main(String [] args)   {
        Employee e = new Employee();
        e.name = "zhangsan";
        e.address = "beiqinglu";
        e.age = 20; 
        try {
              // 创建序列化流对象
          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));
            // 写出对象
            out.writeObject(e);
            // 释放资源
            out.close();
            fileOut.close();
            System.out.println("Serialized data is saved"); // 姓名,地址被序列化,年龄没有被序列化。
        } catch(IOException i)   {
            i.printStackTrace();
        }
       }
}
输出结果:
Serialized data is saved

练习:序列化集合

  1. 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。
  2. 反序列化list.txt ,并遍历集合,打印对象信息。

案例分析

  1. 把若干学生对象 ,保存到集合中。
  2. 把集合序列化。
  3. 反序列化读取时,只需要读取一次,转换为集合类型。
  4. 遍历集合,可以打印所有的学生信息

案例实现

public class SerTest {
    public static void main(String[] args) throws Exception {
        // 创建 学生对象
        Student student = new Student("老王", "laow");
        Student student2 = new Student("老张", "laoz");
        Student student3 = new Student("老李", "laol");

        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(student);
        arrayList.add(student2);
        arrayList.add(student3);
        // 序列化操作
        // serializ(arrayList);
        
        // 反序列化  
        ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));
        // 读取对象,强转为ArrayList类型
        ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
        
          for (int i = 0; i < list.size(); i++ ){
              Student s = list.get(i);
            System.out.println(s.getName()+"--"+ s.getPwd());
          }
    }

    private static void serializ(ArrayList<Student> arrayList) throws Exception {
        // 创建 序列化流 
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
        // 写出对象
        oos.writeObject(arrayList);
        // 释放资源
        oos.close();
    }
}

PrintStream类

构造方法

  • public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流。

构造举例,代码如下:

PrintStream ps = new PrintStream("ps.txt");

改变打印流向

System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,我们就可以玩一个”小把戏”,改变它的流向。

public class PrintDemo {
    public static void main(String[] args) throws IOException {
        // 调用系统的打印流,控制台直接输出97
        System.out.println(97);
      
        // 创建打印流,指定文件的名称
        PrintStream ps = new PrintStream("ps.txt");
          
          // 设置系统的打印流流向,输出到ps.txt
        System.setOut(ps);
          // 调用系统的打印流,ps.txt中输出97
        System.out.println(97);
    }
}

Socket类

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

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

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

构造举例,代码如下:

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() : 禁用此套接字的输出流。   
  - 任何先前写出的数据将被发送,随后终止输出流。 

ServerSocket类

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

构造方法
ServerSocket类:这个类实现了服务器套接字,该对象等待通过网络的请求。
ServerSocket server = new ServerSocket(6666);
成员方法
public Socket accept():侦听并接受链接,返回一个新的Socket对象,用于和客户端实现通信,该方法一直阻塞直到建立链接

客户端向服务器发送数据

服务端实现:

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();
    }
}

客户端实现:

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();
    }
}

服务器向客户端回写数据

服务端实现:

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();
    }
}

客户端实现:

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();
    }
}

函数式接口

函数式接口在Java中是指:有且仅有一个抽象方法的接口

函数式接口,即适用于函数式编程场景的接口。而Java中的函数式编程体现就是Lambda,所以函数式接口就是可 以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导。

备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实 底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部 类的“语法糖”,但是二者在原理上是不同的。

修饰符 interface 接口名称 {
    public abstract 返回值类型 方法名称(可选参数信息);
    // 其他非抽象方法内容
}

由于接口当中抽象方法的 public abstract 是可以省略的,所以定义一个函数式接口很简单:

public interface MyFunctionalInterface{
    void myMethod(); //省略public abstract
}

@FunctionalInterface注解

@Override注解的作用类似,引入了一个新的注解@FunctionalInterface 该注解可用于上一个接口的定义上

@FunctionalInterface
public interface MyfunctionalInterface{
    void myMethod();
}

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注 意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:

public class Demo09FunctionalInterface{
    //使用自定义的函数式接口方法
    private static void deSomething(MyfunctionalInterfalce inter){
        inter.myMethod(); //调用自定义的函数式接口方法
    }
    
    public static void main(String[] args){
        //调用使用函数式接口的方法
        doSomething(()->System.out,println("Lambda执行啦!"));
    }
}

Lambda的延迟

public static void log(int level, MessageBuilder builder){
        if (level == 1){
            System.out.println(builder.buildMessage());
        }
    }

    public static void main(String[] args){
        String msgA = "Hello";
        String msgB = "world";
        String msgC = "Java";
        log(2,() -> {
            System.out.println("Lambda执行啦!");
            return msgA+msgB+msgC;
        });
    }
}

从结果中可以看出,在不符合级别要求的情况下,Lambda将不会执行。从而达到节省性能的效果。

扩展:实际上使用内部类也可以达到同样的效果,只是将代码操作延迟到了另外一个对象当中通过调用方法 来完成。而是否调用其所在方法是在条件判断之后才执行的。

使用Lambda作为参数和返回值

如果抛开实现原理不说,Java中的Lambda表达式可以被当作是匿名内部类的替代品。如果方法的参数是一个函数 式接口类型,那么就可以使用Lambda表达式进行替代。使用Lambda表达式作为方法参数,其实就是使用函数式 接口作为方法参数。
例如 java.lang.Runnable 接口就是一个函数式接口,假设有一个 startThread 方法使用该接口作为参数,那么就 可以使用Lambda进行传参。这种情况其实和 Thread 类的构造方法参数为 Runnable 没有本质区别。

public class DemoRunnable{
    private static void startThread(Runnable task){
        new Thread(task).start();
    }
    public static void main(String[] args){
        startThread(()->System.out.println("线程任务执行!"));
    }
}

类似地,如果一个方法的返回值类型是一个函数式接口,那么就可以直接返回一个Lambda表达式。当需要通过一 个方法来获取一个 java.util.Comparator 接口类型的对象作为排序器时, 就可以调该方法获取

private static Comparable<String> newComparator(){
    return(a,b) -> b.length() - a.length();
    // 其中直接return一个Lambda表达式即可
}

public static void main(String[] args) {
    String[] array = {"abc","ab","abcd"};
    System.out.println(Arrays.toString(array));
    Arrays.sort(array,newComparator());
    System.out.println(Arrays.toString(array));
}

常用函数式接口

Supplier接口

java.util.function.Supplier<T>接口仅包含一个无参的方法:T get() 用来获取一个泛型参数指定类型的对象数据。由于这是一个函数式接口,也就意味着对应Lambda表达式需要”对外提供“一个符合泛型类型的对象数据

public class Demo08Supplier {
    private static String getString(Supplier<String> function) {
    return function.get();
}
public static void main(String[] args) {
    String msgA = "Hello";
    String msgB = "World";
    System.out.println(getString(() ‐> msgA + msgB));
    }
}

求数组元素最大值

题目

使用 Supplier 接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值。
提示:接口的泛型请使用 java.lang.Integer 类。

解答
public class Demo02Test {
//定一个方法,方法的参数传递Supplier,泛型使用Integer
    public static int getMax(Supplier<Integer> sup){
    return sup.get();
}
public static void main(String[] args) {
    int arr[] = {2,3,4,52,333,23};
    //调用getMax方法,参数传递Lambda
    int maxNum = getMax(()‐>{
    //计算数组的最大值
    int max = arr[0];
    for(int i : arr){
        if(i>max){
     max = i;
     }
    }
     return max;
   });
    System.out.println(maxNum);
    }
}

Consumer接口

java.util.function.Consumer 接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据, 其数据类型由泛型决定。

抽象方法:accept

Consumer接口中包含抽象方法void accept(T t), 意为消费一个指定泛型的数据

private static void consumeString(Consumer<String> function){
    function.accept("Hello!");
}
public static void main(String[] args) {
    consumeString(s -> System.out.println(s));
}
默认方法:andThen

如果一个方法的参数和返回值全都是 Consumer 类型,那么就可以实现效果:消费数据的时候,首先做一个操作, 然后再做一个操作,实现组合。而这个方法就是 Consumer 接口中的default方法 andThen 。下面是JDK的源代码:

default Consumer<T> andThen(Consumer<? super T> after) {
    Objects.requireNonNull(after);
    return (T t) ‐> { accept(t); after.accept(t); };
}

要想实现组合,需要两个或多个Lambda表达式即可,而 andThen 的语义正是“一步接一步”操作。例如两个步骤组合的情况:

private static void consumeString(Consumer<String> one, Consumer<String>two){
        one.andThen(two).accept("hello");
}

    public static void main(String[] args) {
        consumeString(
                s-> System.out.println(s.toUpperCase()),
                s -> System.out.println(s.toLowerCase())
    );
}
// 运行结果将会首先打印完全大写的HELLO,然后打印完全小写的hello。当然,通过链式写法可以实现更多步骤的组合。

格式化打印信息

题目

下面的字符串数组当中存有多条信息,请按照格式“ 姓名:XX。性别:XX。”的格式将信息打印出来。要求将打印姓 名的动作作为第一个 Consumer 接口的Lambda实例,将打印性别的动作作为第二个 Consumer接口的Lambda实 例,将两个 Consumer 接口按照顺序“拼接”到一起。

public static void main(String[] args) {
    String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男" };
}
解答
import java.util.function.Consumer;
public class test {
    private static void printInfo(Consumer<String>one, Consumer<String>two, String[] array){
        for (String info : array){
        one.andThen(two).accept(info);
        }
    }

    public static void main(String[] args) {
        String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男"};
        printInfo(s-> System.out.print("姓名:"+s.split(",")[0]),
                  s-> System.out.println("。性别:"+s.split(",")[1]+"。"),
                  array
                  );
    }
}

Predicate接口

有时候我们需要对某种类型的数据进行判断,从而得到一个boolean值结果。这时可以使用 java.util.function.Predicate 接口。

public class Demo15PredicateTest{
    private static void metho(Predicate<String> predicate){
        boolean veryLong = predicate.test("HelloWorld");
    }
    public static void main(String[] args){
        method(s -> s.length() > 5);
    }
}
// 条件判断的标准是传入的Lambda表达式逻辑,只要字符串长度大于5则认为很长。
默认方法:and

既然是条件判断,就会存在与、或、非三种常见的逻辑关系。其中将两个 Predicate 条件使用“与”逻辑连接起来实 现“并且”的效果时,可以使用default方法 and 。其JDK源码为:

defalult Predicate<T> and(Predkicate<? super T> other){
    Object.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}

如何判断一个字符串既包含大写”H”,又包含大写”W”

public class test {
   private static void method(Predicate<String>one, Predicate<String>two){
       boolean isValid = one.and(two).test("HelloWorld");
       System.out.println("是否符合?"+isValid);
   }

    public static void main(String[] args) {
        method(s -> s.contains("H"), s -> s.contains("o"));
    }
} // 是否符合?true
默认方法:or
defalult Predicate<T> or (Predkicate<? super T> other){
    Object.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}

如何判断一个字符串既包含大写”H”,又包含大写”W”

public class test {
   private static void method(Predicate<String>one, Predicate<String>two){
       boolean isValid = one.and(two).test("HelloWorld");
       System.out.println("是否符合?"+isValid);
   }

    public static void main(String[] args) {
        method(s -> s.contains("H"), s -> s.contains("o"));
    }
} // 是否符合?true
默认方法:negate (“非”[取反])
default Predicate<T> negate(){
    return (t) -> !test(t);
}

从现实中很容易看出,它是执行了test方法之后,对结果boolean值进行”!”取反而已。一定要在test方法调用之前调用negate方法,正如andor方法一样

public class Demo17PredicateNegate{
    private static void methodW(Predicate<String> predicate){
        boolean veryLong = predicate.negate().test("HelloWorld");
        System.out.println("字符串很长吗:" + veryLong);
    }
    public static void main(String[] args){
        method(s -> s.length() < 5);
    }
}

集合信息筛选

题目

数组当中有多条“姓名+性别”的信息如下,请通过 Predicate 接口的拼装将符合要求的字符串筛选到集合 ArrayList 中,需要同时满足两个条件:

  1. 必须为女生;
  2. 姓名为4个字。
public class DemoPredicate {
    public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
    }
}
解答
 public static void main(String[] args) {
        String[] array = {"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
        List<String> list = filter(array,
                s -> "女".equals(s.split(",")[1]),
                s -> s.split(",")[0].length() == 4);
        System.out.println(list);
    }
 private static List<String> filter(String[] array, Predicate<String>one, Predicate<String>two){
       List<String> list = new ArrayList<>();
       for (String info : array){
           if (one.and(two).test(info)){
               list.add(info);
           }
       }
       return list;
 }

Stream流高级改造

public static void main(String[] args) {
        String[] array = { "迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" };
        Arrays.stream(array)
                .filter(s -> "女".equals(s.split(",")[1]))
            //  .filter(s -> s.split(",")[1].startsWith("女"))
                .filter(s -> s.split(",")[0].length()==3)
                .forEach(System.out::println);
}

Stream流式思想改造

 public static void main(String[] args) {
        Stream<String>original = Stream.of("迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女" );
        Stream<String>result1 = original.filter(s -> s.split(",")[1].startsWith("女"));
        Stream<String>result2 = result1.filter(s -> s.split(",")[0].length()==3);
        result2.forEach(System.out::println);
    }

Function接口

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者成为后置条件。

抽象方法:apply

apply Function 接口中最主要的抽象方法为:R apply(T t) ,根据类型T的参数获取类型R的结果。 使用的场景例如:将 String 类型转换为 Integer 类型。

private static void method(Function<String,Integer> function){
     int num = function.apply("10");
     System.out.println(num + 10);
}

public static void main(String[] args) {
     method(s -> Integer.parseInt(s));
}

默认方法:andThen

Function接口中有一个默认的andThen方法,用来进行组合操作

default <V> Function<T,V> andThen(Function<? super R, ? extends V> after){
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

该方法同样用于“先做什么,再做什么”的场景,和 Consumer 中的 andThen 差不多:

public class Demo12FunctionAndThen {
    private static void method(Function<String, Integer> one, Function<Integer, Integer> two) {
        int num = one.andThen(two).apply("10");
        System.out.println(num + 20);
}
    public static void main(String[] args) {
        method(str‐>Integer.parseInt(str)+10, i ‐> i *= 10);
    }
}

第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一 起。

private static void method(Function<String, Integer> one, Function<Integer, Integer> two) {
        int num = one.andThen(two).apply("10");
        System.out.println(num + 20);
}
public static void main(String[] args) {
        method(str‐>Integer.parseInt(str)+10, i ‐> i *= 10);
}

第一个操作是将字符串解析成为int数字,第二个操作是乘以10。两个操作通过 andThen 按照前后顺序组合到了一 起。

请注意,Function的前置条件泛型和后置条件泛型可以相同。

自定义函数模型拼接

题目

请使用 Function 进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
String str = "赵丽颖,20"

1.将字符串截取数字年龄部分,得到字符串;
2.将上一步的字符串转换成为int类型的数字;
3.将上一步的int数字累加100,得到结果int数字。

解答
private static int getAgeNum(String str, Function<String, String>one, Function<String, Integer>two, Function<Integer, Integer> three){
    return one.andThen(two).andThen(three).apply(str);
}

public static void main(String[] args) {
    String str = "赵丽颖,20";
    int age = getAgeNum(str,
         s -> s.split(",")[1],
         s -> Integer.parseInt(s),
         n -> n += 100);
    System.out.println(age);
}

Stream流、方法引用

说到Stream便容易想到I/O Stream,而实际上,谁规定“流”就一定是“IO流”呢?在Java 8中,得益于Lambda所带 来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端。

Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),这点此前已经结合内部类进行 了对比说明。现在,我们仔细体会一下上例代码,可以发现:

  • for循环的语法就是“怎么做”
  • for循环的循环体才是“做什么”

为什么使用循环?因为要进行遍历。但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理,而并不是从 第一个到最后一个顺次处理的循环。前者是目的,后者是方式。

试想一下,如果希望对集合中的元素进行筛选过滤:
1.将集合A根据条件一过滤为子集B;
2.然后再根据条件二过滤为子集C。

每当我们需要对集合中的元素进行操作的时候,总是需要进行循环、循环、再循环。这是理所当然的么?不是。循 环是做事情的方式,而不是目的。另一方面,使用线性循环就意味着只能遍历一次。如果希望再次遍历,只能再使 用另一个循环从头开始。

Stream更优写法

直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印。代码 中并没有体现使用线性循环或是其他任何算法进行遍历,我们真正要做的事情内容被更好地体现在代码中。

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::println);

    }

“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何 元素(或其地址值)。

Stream(流)是一个来自数据源的元素队列
  • 元素是特定类型的对象,形成一个队列。java中的stream并不会存储元素,而是按需计算
  • 数据源流的来源:可以是集合,数组

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代:以前对集合遍历都是通过Iterator或者增强for的方式,显示的在集合外部进行迭代,这叫做外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法forEach(System.out::println)

当使用一个流的时候,通常包括三个步骤:获取一个数据源(source) → 数据转换 → 执行操作获取想要的结果,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道

获取流

java.util.stream.Stream<T>是最常用的流接口
获取方式:
**1.**所有的Collection集合都可以通过stream默认方法获取流
2.Stream接口的静态方法of可以获取数组对应的流

根据Collection获取流

首先java.util.Collection接口中加入了default方法stream用来获取流,所以其所有实现类均可获取流

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    // ...
    Stream<String> stream1 = list.stream();

    Set<String> set = new HashSet<>();
    // ...
    Stream<String> stream2 = set.stream();

    Vector<String> vector = new Vector<>();
    // ...
    Stream<String> stream3 = vector.stream();
    }
根据Map获取流

Java.util.Map接口不是Collection的子接口,且其K-V数据结构不符合流元素的单一特征,所以获取对应的流 需要分key、valueentry等情况:

public class Demo05GetStream {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        // ...
        Stream<String> keyStream = map.keySet().stream();
        Stream<String> valueStream = map.values().stream();
        Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
    }
}

根据数组获取流

如果使用的不是集合或映射而是数组,由于数组对象不可能添加默认方法,所以 Stream 接口中提供了静态方法 of ,使用很简单:

public static void main(String[] args) {
     String[] array = { "张无忌", "张翠山", "张三丰", "张一元" };
     Stream<String> stream = Stream.of(array);
}

备注: of 方法的参数其实是一个可变参数,所以支持数组。

流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种:

  • 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方 法均为延迟方法。)
  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调 用。本小节中,终结方法包括 count 和 forEach 方法。

逐一处理:forEach

虽然方法名字叫 forEach,但是与for循环中的“for-each”昵称不同。
该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。

void forEach(Consumer<? super ?> action);  

复习Consumer接口

java.util.function.Consumer<T>接口是一个消费型接口。
Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。
    
import java.util.stream.Stream;
  public class Demo12StreamForEach {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("张无忌", "张三丰", "周芷若");
        stream.forEach(name‐> System.out.println(name));
    }
}

过滤:filter

可以通过 filter 方法将一个流转换成另一个子集流。方法签名:

Stream<T> filer(Predicate<? super T> predicate);

该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件。

复习Predicate接口

此前我们已经学习过 java.util.stream.Predicate 函数式接口,其中唯一的抽象方法为:

boolean test(T t);

该方法会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的filter方法将会留用元素;如果结果为false,那么filter方法将会舍弃元素。

基本使用

Stream流中的 filter 方法基本使用的代码如:

public class Demo07StreamFilter {
    public static void main(String[] args) {
        Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
        Stream<String> result = original.filter(s ‐> s.startsWith("张"));
    }
}

映射:map

如果需要将流中的元素映射到另一个流中,可以使用map方法

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

复习Function接口

此前我们已经学习过 java.util.stream.Function 函数式接口,其中唯一的抽象方法为:

R apply(T t);

这可以将一种T类型转换成R类型,这种转换的动作,叫做映射

基本使用

Stream流中的 map 方法基本使用的代码如:

public static void main(String[] args) {
    Stream<String> original = Stream.of("10","22","452");
    Stream<Integer> result = original.map(str -> Integer.parseInt(str));
    result.forEach(System.out::println);
}

这段代码中,map 方法的参数通过方法引用,将字符串类型转换成为了int类型(并自动装箱为 Integer 类对 象)。

统计个数

正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来数一数其中的元素个数:

long count();

该方法返回一个long值代表元素个数

public static void main(String[] args) {
    Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
    Stream<String> result = original.filter(s ‐> s.startsWith("张"));
    System.out.println(result.count()); // 2
}

取用前几个:limit

limit 方法可以对流进行截取,只取用前n个。方法签名

Stream<T> limit(long maxSize);

参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。基本使用:

public static void main(String[] args) {
    Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
    Stream<String> result = original.limit(2);
    result.forEach(System.out::println); // 张无忌 张三丰
    System.out.println(result.count()); // 2
    }
}

跳过前几个:skip

如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:
如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。基本使用:

public class Demo11StreamSkip {
    public static void main(String[] args) {
         Stream<String> original = Stream.of("张无忌", "张三丰", "周芷若");
         Stream<String> result = original.skip(2);
         result.forEach(System.out::println); // 周芷若
}

组合:concat

如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)
// 这是一个静态方法,与 java.lang.String 当中的 concat 方法是不同的。
public static void main(String[] args) {
    Stream<String> streamA = Stream.of("张无忌");
    Stream<String> streamB = Stream.of("张翠山");
    Stream<String> result = Stream.concat(streamA, streamB);
    }
}

集合元素处理(Stream方式)

题目(Stream流式处理方式)

第一个队伍只要名字为3个字的成员姓名;第一个队伍筛选之后只要前3个人;
第二个队伍只要姓张的成员姓名;第二个队伍筛选之后不要前2个人
将两个队伍合并为一个队伍;根据姓名创建Person对象;打印整个队伍的Person对象信息。

public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class test {
    public static void main(String[] args) {
        List<String> one = new ArrayList<>();
        one.add("迪丽热巴");
        one.add("宋远桥");
        one.add("苏星河");
        one.add("石破天");
        one.add("石中玉");
        one.add("老子");
        one.add("庄子");
        one.add("洪七公");

        List<String> two = new ArrayList<>();
        two.add("古力娜扎");
        two.add("张无忌");
        two.add("赵丽颖");
        two.add("张三丰");
        two.add("尼古拉斯赵四");
        two.add("张天爱");
        two.add("张二狗");

        // 第一个队伍只要名字为3个字的成员姓名;第一个队伍筛选之后只要前3个人;
        Stream<String> streamOne = one.stream().filter(s -> s.length() == 3).limit(3);
        // 第二个队伍只要姓张的成员姓名;第二个队伍筛选之后不要前2个人;
        Stream<String> streamTwo = two.stream().filter(s -> s.startsWith("张")).skip(2);
        // 将两个队伍合并为一个队伍;根据姓名创建Person对象;打印整个队伍的Person对象信息。
        Stream.concat(streamOne, streamTwo).map(Person::new).forEach(System.out::println);

    }
}

Lambda方法引用

请注意其中的双冒号 :: 写法,这被称为“方法引用”,而双冒号是一种新的语法。

简单的函数式接口以应用Lambda表达式:
@FunctionalInterface
public interface Printable {
    void print(String str);
}
private static void printString(Printable data){
    data.print("Hello, World!");
}
public static void main(String[] args){
    printString(System.out::println);
}

方法引用符

引出:我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑?

双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者

例如上例中, System.out 对象中有一个重载的 println(String) 方法恰好就是我们所需要的。那么对于 printString 方法的函数式接口参数,对比下面两种写法,完全等效:

  • Lambda表达式写法:s -> System.out.println(s);
  • 方法引用写法:System.out::println

第一种语义是指:拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。
第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代Lambda。两种写法的执行效果完全一 样,而第二种方法引用的写法复用了已有方案,更加简洁。

注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常

三种主要使用情况:

情况1:对象名::实例方法名
情况2:类名::静态方法名
情况3:类名::实例方法名

【方法引用】Java语法中的双冒号::到底是啥意思?_哔哩哔哩_bilibili
lambda的双冒号是什么意思一个视频简简单单说清楚_哔哩哔哩_bilibili
【java面试技巧】双冒号之方法引用大家快来看看吧_哔哩哔哩_bilibili

interface A{
    int method(String str);
}

public class Test {
    public static void main(String[] args) {
        A a1 = str -> Integer.valueOf(str);
        System.out.println(a1.method("111"));

        A a2 = Integer::valueOf;
        System.out.println(a2.method("444"));

        A a = new A() { //因为A是接口所以直接new不了 加上大括号 匿名内部类
            @Override
            public int method(String str) {
                return new Integer(str); //封装 拆箱
            }
        };
        A a3 = Integer::new;
        System.out.println(a3.method("666"));
    }
}

推导与省略

如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都 将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。
函数式接口是Lambda的基础,而方法引用是Lambda的孪生兄弟。 下面这段代码将会调用 println 方法的不同重载形式,将函数式接口改为int类型的参数:

@FunctionalInterface
public interface PrintableInteger {
    void print(int str);
}
-------------------------------------------------------------
public class Demo03PrintOverload {
    private static void printInteger(PrintableInteger data) {
        data.print(1024);
}
public static void main(String[] args) {
    printInteger(System.out::println);
}
    // 这次方法引用将会自动匹配到 println(int) 的重载形式。

通过类名称引用静态方法

由于在 java.lang.Math 类中已经存在了静态方法 abs ,所以当我们需要通过Lambda来调用该方法时,有两种写法。首先是函数式接口:

@FuctionalInterface
public interface Calcable {
    double calc(int num);
}
private static void method(int num, Calcable lambda){
     System.out.println(lambda.calc(num));
}

public static void main(String[] args) {
     method(10, Math::sqrt);
    // method(‐10, n ‐> Math.abs(n)); 舍弃
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: n -> Math.abs(n)
  • 方法引用: Math::abs

通过super引用成员方法

如果存在继承关系,当Lambda中需要出现super调用时,也可以使用方法引用进行替代。首先是函数式接口:

public interface Greetable {
    void greet();
}

然后是父类Human内容

public class Human {
    public void sayHello(){
        System.out.println("Hello");
    }
}

最后是子类Man的内容,其中使用了Lambda写法

public class Man extends Human {
    @Override
    public void sayHello() {
        System.out.println("大家好,我是Man!");
    }
    //定义方法method,参数传递Greetable接口
    public void method(Greetable g){
        g.greet();
    }

    public void show(){
        method(super::sayHello);
    }

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: () -> super.sayHello()

  • 方法引用: super::sayHello

通过this引用成员方法

this代表当前对象,如果需要引用的方法就是当前类中的成员方法,那么可以使用**“this::成员方法”**的格式来使用方 法引用。首先是简单的函数式接口:

@FunctionalInterface
public interface Richable {
    void buy();
}

下面是一个丈夫 Husband 类:

public class Husband {
    private void marry(Richable lambda) {
        lambda.buy();
    }
    public void beHappy() {
        marry(() ‐> System.out.println("买套房子"));
    }
}

开心方法 beHappy 调用了结婚方法 marry ,后者的参数为函数式接口 Richable ,所以需要一个Lambda表达式。 但是如果这个Lambda表达式的内容已经在本类当中存在了,则可以对Husband丈夫类进行修改:

public class Husband {
    private void buyHouse() {
        System.out.println("买套房子");
    }
private void marry(Richable lambda) {
        lambda.buy();
    }
public void beHappy() {
        marry(() ‐> this.buyHouse());
    }
}

如果希望取消掉Lambda表达式,用方法引用进行替换,则更好的写法为:

public class Husband {
    private void buyHouse() {
        System.out.println("买套房子");
    }
private void marry(Richable lambda) {
        lambda.buy();
    }
public void beHappy() {
        marry(this::buyHouse);
    }
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: () -> this.buyHouse()
  • 方法引用: this::buyHouse

类的构造器引用

由于构造器的名称与类名完全一样,并不固定。所以构造器引用使用 类名称::new 的格式表示。首先是一个简单 的Person类:

public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

然后是用来创建 Person 对象的函数式接口:

public interface PersonBuilder {
    Person buildPerson(String name);
}

要使用这个函数式接口,可以通过Lambda表达式:

public class Demo09Lambda {
    public static void printName(String name, PersonBuilder builder) {
        System.out.println(builder.buildPerson(name).getName());
    }
    public static void main(String[] args) {
        printName("赵丽颖", name ‐> new Person(name));
    }
}

但是通过构造器引用,有更好的写法:

public class Demo10ConstructorRef {
    public static void printName(String name, PersonBuilder builder) {
           System.out.println(builder.buildPerson(name).getName());
    }
    public static void main(String[] args) {
        printName("赵丽颖", Person::new);
    }
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: name -> new Person(name)
  • 方法引用: Person::new

数组的构造器引用

数组也是 Object 的子类对象,所以同样具有构造器,只是语法稍有不同。如果对应到Lambda的使用场景中时, 需要一个函数式接口:

@FunctionalInterface
public interface ArrayBuilder {
    int[] buildArray(int length);
}

在应用该接口的时候,可以通过Lambda表达式:

public class Demo11ArrayInitRef {
    private static int[] initArray(int length, ArrayBuilder builder) {
        return builder.buildArray(length);
    }
    public static void main(String[] args) {
        int[] array = initArray(10, length ‐> new int[length]);
    }
}

但是更好的写法是使用数组的构造器引用:

public class Demo12ArrayInitRef {
    private static int[] initArray(int length, ArrayBuilder builder) {
        return builder.buildArray(length);
    }
    public static void main(String[] args) {
        int[] array = initArray(10, int[]::new);
    }
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: length -> new int[length]
  • 方法引用: int[]::new
阅读全文

软件设计师考点

2023/5/6

考试占比

考试占比

考试占比2

报名

报名条件:凡遵守中华人民共和国宪法和各项法律,恪守职业道德,具有一定计算机技术应用能力的人员,均可报考软件设计师。也就是说不限制考生的学历、专业、工作经验及年限、年龄等。【点击查看软件设计师报名条件详解

报名时间:上半年一般在==3月份报名==,下半年在==8月份报名==,各地报名具体时间不一样,考生届时需要多留意当地报名时间。【点击查看各地软件设计师报名时间

考试时间:上半年一般是==5月份开考==,下半年一般是==11月份开考==
上午基础知识科目考试为9:00-11:30,下午应用技术科目考试时间为14:00-16:30

报名方式:考生自己在当地规定时间内进入软考办官网,即中国计算机技术职业资格网的报名入口进行报名。【点击查看软件设计师报名入口

报名费用:各地不同,一般在100-160元之间,具体以当地为准,其中辽宁不收取报名费(大连考区除外)【点击查看报名费用

成绩查询:一般是考后40天左右公布成绩,成绩公布后大家可以在软考办官网进行查询

考试证书:两科均通过分数线即可领取证书,各地领证时间不同,上半年证书一般集中在9-10月份发放,下半年证书一般集中在次年2-3月份发放。错过证书领取时间,证书由发证机构代为保管,考生可咨询相关领取事宜,但是超过5年没领取的证书将被销毁。软件设计师证书可以在中国计算机技术职业资格网和中国人事考试考试网的“证书查询”栏目进行查询。点击查看详情

进制转换

二进制转八进制 八进制转十六进制
二进制转十进制 (分权相乘 整数AND小数)
十进制转二进制 (短除法)

原码 反码 补码 转码

负数

  • 原码(数字首 负数为1 正数为0)

    • +9 = **0**000 1001
    • -9 = **1**000 1001
    • Min → 1111 1111 = —127
    • Max → 0111 1111 = +127
    • 缺点:0有两种表示方式:+0=00000000 -0=10000000[零的二义性给机器判断带来了麻烦] ****
    • 原码的数值0有两种表示方式 +0 -0

    比如:1-2=-3 → ∵ +1+(-2) → +1 -> 0000 000$1_原$ ; -2 -> 1000 001$0_原$

    ∴ 0000 0001 + 1000 0010 = 1000 001$1_原$ = -3

    为了解决这个问题,科学家使用补码

    -33的原码是 *1010 0001* 反码是 1101 1110

  • 反码

    • 原码的符号位不变,其余位取相反得到
    • 反码存在的意义就是为了由原码计算补码方便
  • 补码

    • 反码+1得到
    • 优点

    1、在补码表示中,0有唯一的编码 0 = 0000 0000

    2、用10000000表示 -128,比原码可多表示一个编码

    3、利用补码可以方便地进行运算

正数

  • 原码=反码=补码

☆☆ 正数:正数的原码、反码、补码都一样 ☆☆

☆☆ 负数:负数将原码的符号位保持不变,数值位各位取反再末位加1,就可以将原码转换为补码 ☆☆

@@ 计算机中常采用原码、反码、补码和移码表示数据,其中±0编码相同的是补码和移码[这里一定要考虑正数和负数]

@@ 负数-5在计算机中的补码是 ?

直接从原码变补码、补码变原码 口诀:从右向左复制,直到有1被赋值 其余取相反(符号位 => 第一位不变)

  • 移码

一般运用于浮点运算中的阶码,在补码的基础上把首位取反

数值1 (正数前三都相同) 数值-1
原码 0000 0001 1000 0001
反码 0000 0001 1111 1110
补码 0000 0001 1111 1111
移码 1000 0001 0111 1111

表示范围

整数
原码 -[2$^{(n-1)}-1$]  ~  2$^{(n-1)}-1$
反码 -[2$^{(n-1)}-1$]  ~  2$^{(n-1)}-1$
补码 -[2$^{(n-1)}$]   ~   2$^{(n-1)}-1$ [少占用一个+0 -0]

浮点数运算

浮点数表示:
N = M * $R^e$
其中M称为尾数,e是指数,R为基数

步骤:对阶 → 尾数计算 → 结果格式化

小阶数朝着大阶数
1000 + 119 => 1.0 × 1$0^3$ + 1.19 × 1$0^2$ => 1.0 × 1$0^3$ + 0.119 × 1$0^3$ = 1.119 × 1$0^3$

结果格式化:如果答案是 0.1119 × 1$0^3$ 把结果变成 1.119 × 1$0^2$
确保尾数的第一个位置不能是0, 也不能是1以上的;最多是一位

计算机结构

==硬件 = 主机 + 外设==

==主机 = CPU (CPU = 运算器 + 控制器) + 内存==

==外设 = 输入设备 + 输出设备 + 外存==

==存储器 = 内存 + 外存==

运算器
① 算术逻辑单元ALU
② 累加寄存器AC
③ 数据缓冲寄存器DR [对内存储器读取的时候 有暂存的作用]
④ 状态条件寄存器PSW [存储运算过程中的标志位状态信息]

控制器
① 程序计数器PC
② 指令寄存器IR
③ 指令译码器
④ 时序部件

计算机体系结构分类 — Flynn

体系结构类型 结构 关键特征 代表
单指令流单数据流 SISD 控制部分:一个
处理器:一个
主存模块:多个
单处理器系统(单片机)
单指令流多数据流 SIMD 控制部分:一个
处理器:多个
主存模块:多个
各处理器以异步的形式执行同一条指令 并行处理机
阵列处理机
超级向量处理机
多指令流单数据流 MISD 控制部分:多个
处理器:一个
主存模块:多个
被证明不可能,至少是不实际 目前没有,有文献称流水线计算机为此类
多指令流多数据流 MIMD 控制部分:多个
处理器:多个
主存模块:多个
能够实现作业、任务、指令等各级全面并行 多处理系统
多计算机

CISC与RISC

指令系统类型 指令 寻址方式 实现方式 其他
CISC(复杂) 数量多,使用频率差别大,可变长格式 支持多种 微程序控制技术(微码) 研制周期长
RISC(精简)主流 数量少,使用频率接近,定长格式,大部分微单周期指令
操作寄存器,只有Load/Store操作内存
支持方式少 大量增加了通用寄存器(增加速度);
硬布线逻辑控制为主
适合采用流水线
优化编译,有效支持高级语言

CISC(Complex Instruction Set Computer, 复杂指令集计算机) 进一步增强原有指令的功能,用更为复杂的新指令取代原先由软件子程序完成的功能,实现软件功能的硬化,导致机器的指令系统越来越庞大而复杂。

RISC(Reduced Instruction Set Computer, 精简指令集计算机) 通过减少指令总数和简化指令功能,降低硬件设计的复杂度,使指令能单周期执行,并通过优化编译,提高指令的执行速度,采用硬线控制逻辑,优化编译程序。

流水线(计算)[运用于工业 可节省时间]

流水线是指在程序执行时多条指令重叠进行操作的一种准并行处理实现技术。各种部件同时处理事针对不同指令而言的,它们可同时为多条指令的不同部分进行工作,以提高各部件的利用率和指令的平均执行速度。

执行一条指令分为 取指分析执行

未使用流水线执行指令情况
1 2 3
1 2 3
1 2 3
使用流水线执行指令情况
1 2 3
1 2 3
1 2 3
  • 流水线周期取指+分析+执行为运算时间最长的一段
  • 流水线运算时间计算公式为:
    ①条指令运算总时长 + (指令条数-1) × 流水线周期
    ① 理论公式:**(t1 + t2 +…+ tk) + (n - 1) × △t**
    ② 实践公式:**(k + n-1) × △t**
若指令流水线把一条指令分为取指、分析和执行三部分,且三部分的实践分别是取指2ns,分析2ns,执行1,ns。那么,流水线周期是多少? 100条指令全部执行完毕需要的时间是多少?
取指 1 2 3 . . n
分析 1 2 3 . . n
执行 1 2 3 . . n

答:1.周期最长的时间是2ns,所以流水线周期是2ns每一个流水线的周期就会完成一条指令的运行
2.(2+2+1) + (100-1) × 2 = 203理论公式①计算 / [3分三段+ (100 - 1) ] × 2 = 204实践公式②计算

  • 流水线吞吐率计算
    是指在单位时间内流水线所完成的任务数量或输出的结果数量。

    流水线吞吐率基本公式:TP = $\frac{指令条数}{流水线运算时间}$
    流水线最大吞吐率:TPmax = Limn→∞$\frac{n}{(k+n-1)△t}$=$\frac{1}{△t}$

==根据上述题目== 可以算出 TP = $\frac{100}{203}$; TPmax = $\frac{1}{2}$

  • 流水线加速度比
    是指完成同样一批任务,不使用流水线所用的时间与使用流水线所用的时间之比称为流水线的加速比
    S = $\frac{不使用流水线执行时间}{使用流水线执行时间}$

==根据上述题目==
不使用流水线执行时间:(2+2+1) × 100 = 500ns
使用流水线执行时间:203ns
S = $\frac{500}{203}$

  • 流水线的效率
    是指流水线的设备利用率。在时空图上,流水线的效率定义为n个任务占用的时空区与k个流水段总的时空区之比
    E = $\frac{n个任务占用的时空区}{k个流水段总的时空区}$ = $\frac{T0}{KTk}$
入 → S1(△t) → S2(△t) → S3(△t) → S4(3△t) → 出

↑ 此方法效率不高,因为在S4所需要的时间太长,导致其他的会遭到空闲状态

横轴是时间△t;纵轴是空间

S4 1 1 1 2 2 2 3 3 3 4 4 4
S3 1 2 3 4
S2 1 2 3 4
S1 1 2 3 4

n个任务占用的时空区:(△t + △t + △t + 3△t) × 4
k个流水段总的时空区:15△t × 4
E = $\frac{(△t + △t + △t + 3△t) × 4}{15△t × 4}$

层次化存储结构

容量最下层的最大 向上依次递减

Cache — 概念

Cache的功能:提高CPU数据输入输出的速率,突破冯·诺依曼瓶颈,即CPU存储系统数据传送带宽限制
在计算机的存储系统体系中,Cache是访问速度最快的层次
使用Cache改善系统性能的依据是程序的局部性原理

如果以h代表对Cache的访问命中率t1表示Cache的周期时间t2表示主存储器周期时间,以读操作为例,使用”Cache + 主存储器“的系统的平均周期为t3,则 t3 = h × t1 + (1 - h) × t2,其中(1-h)又称为失效率(未命中率)
若h=95%, t1=1ns, t2=1ms=1000ns ===> t3 = 1ns × 95% + (1 - 95%) × 1000ns = 50.95ns

CPU在读取数据中首先对Cache中读取,如果读到了则表示对该数据的命中,如果Cache中没有我们需要的数据,CPU会在内存里去调

局部性原理

时间局部性
空间局部性(适用于数组)
工作集理论:工作集是进程运行时被频繁访问的页面集合短时间不被替换成cache

int i, s = 0;  ==>  全局变量直接调用Cache
for(i = 1; i < 1000; i++)   ==> 在内存中循环100w次
    for(j = 1; j < 1000; j++)
        s+=j;
cout << s << end;

内存+外存

  • 内存

    • ROMBIOS 只读存储器
      ROM-Read Only Memory只读存储器。断电后信息不丢失,如计算机启动用的BIOS芯片。
      MROM(Mask ROM, 掩模式ROM)
      PROM 可编程只读存储器
      EPROM 可擦写可编程只读存储器
      EEPROM 点可擦可编程只读存储器
      Flash Memory 闪存 ≈ SSD
    • RAM随机存储器
      DRAM(动态存储器) 不断刷新 速度比SRAM慢 价格低 常用于主存储器
      SRAM(静态存储器 ) 不需刷新 速度比DRAM快 价格贵 cache属于SRAM
      CPU → Cache → 内存 → 外存
  • 外存

    • 硬盘:机械硬盘HDD(SATA、IDE、SCSI接口) 固态硬盘SSD(SATA接口)
    • 光盘:CD/VCD:650MB、CD-ROM(只读光盘)、CD-R(一次性写入、永久读)、CD-RW(可重复擦写光盘)
      DVD:4.7GB、DVD-ROM、DVD-R、DVD-RW

主存 — 编址

8 × 4 位的存储器 ==> 8个地址空间,每一个存储空间存储了4个bit位的信息

000 这一存储空间 存放了4个 bit的 信息容量
001
010
011
100
101
110
111

@@ 内存地址从AC000H到C7FFFH,共有 1C000/ $2^{10}$=112 K个地址单位
如果该内存地址按字(16bit)编址,由28片存储器芯片构成。已知构成此内存的芯片每片有16K个内存单元,则该芯片每个存储单元存储 4

原理是后面地址-前面地址+1; C7FFFH + 1 - AC000H = 1C000H
K = $2^{10}$B ==> 共有 1C000/ $2^{10}$=112K 个地址单位

用一系列的芯片组成这样 112K×16bit位 的内存块,需要28个芯片,每个芯片是16K个存储单元**$\frac{112K×16}{28×16K×a}$ = 1**,比值是1因为用这些芯片去成立这个空间。解得a=4

磁盘结构与参数

存取时间 = 寻道时间 + 等待时间(平均定位时间 + 转动延迟)
注意:寻道时间是指磁头移动到磁道所需的时间;等待时间为等待读写的扇区转到磁头下方所用的时间。

  • 硬盘

机械硬盘(HDD):存储介质 磁介质;参数:[主慈善]**磁(道)头数、柱面数、扇区(**硬盘的基本读写单位,大小是512B);

硬盘容量 = 磁头数 * 柱面数 * 扇区数 * 512B
性能指标:存储容量、转速、访问时间、传输速率、缓存等
[硬盘内部结构按扇区、磁道、柱面的格式组织存储信息]

接口:SATA、IDE、SCSI、光纤通道

固态硬盘(SSD):存储介质(Flash Memory闪存) 接口 SATA等

旋转的延迟时间是磁盘转一圈的时间

@@ 假设某磁盘的每个磁道划分成11个物理块,每块存放1个逻辑记录。逻辑记录R0,R1,…… ,R9,R10存放在同一个磁道上,记录的存放顺序如下表所示:

物理块 1 2 3 4 5 6 7 8 9 10 11
逻辑记录 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11

如果磁盘的旋转周期为33ms,磁头当前处在R0的开始处。若系统使用单缓存区顺序处理这些记录,每个记录处理时间为3ms,则处理这11个记录的最长时间为366ms;若对信息存储进行优化分布后,处理11个记录的最少时间为66ms

一圈11个记录 转一圈33ms,则每个记录旋转的时间是3ms,读取一个记录的时间是3ms;告诉单缓存区,转到R0时处理R0需要3ms因为是单缓存区,不能同时进行,必须等R0处理完成才能进入下一个任务处理[处理的途中磁盘仍在转动,磁头仍在向前],而一个记录正好也需要3ms,所以当处理完R0时,磁头已经到达了R2的位置,所以要再转一圈才能去处理R1,以此类推,除了最后一个R10就可以找到规律,把R0处理完,再等到指针走到R1的位置的时候转了一圈加一条记录的时间 = [33+3] = 36ms,以此类推R0 ~ R9都是这样处理的,所以处理R0~R9共需要 36ms × 10,最后一个R10,把它读取出来3ms,再处理完3ms。综上一共需要 (33+3)×10+6 = 366ms

优化处理:当处理完R0的时候,磁头正好下一位置是R1,而不用再去等它转动一个周期后;优化后的图为右侧图二!旋转+处理=(3ms+3ms)×10=66ms

总线

根据总线所处的位置不同,总线通常被分成三种类型,分别是:

内部总线

是指微机内部各个外围的芯片处理器之间的总线 属于芯片级别

系统总线

是指各个插件板系统板之间的总线 属于插件版级别

  • ==数据总线(DB)==:用来传输数据信息 双向传输
  • ==地址总线(AB)==:用来传输数据地址 单向传输

地址总线的位数决定了CPU可直接寻址的内存空间大小

若地址总线为n位,寻址空间为2的n次方个B,如微机的地址总线为16位,则最大寻址空间为64KB

  • ==控制总线(CB)==:用来传输控制信号
外部总线

是指微机外部设备的总线

系统可靠性分析 — 串联系统与并联系统

串联】输入 → R1 → R2 → … → Rn → 输出
R = R1 × R2 × … × Rn
λ = λ1 + λ2 + … + λn

并联

     →R1

输入 →R2 → 输出
→Rn

R = 1 - (1 - R1) × (1 - R2) × … × (1 - Rn)
**μ = **$\frac{1}{\frac{1}{λ}\quad\sum_{j=1}^{n}\frac{1}{j}}$

模冗余系统与混合系统

​ →R1 →↓

输入 →R2 → 表决器 → 输出

​ →Rm →↑

**R = **$\quad\sum_{j=1}^{n}{C{^j}{_m}×R{0}{^i}(1-R{0})^{m-i}}$

串并联

​ |— R —| |—R—|
—R —|— R —|—— | |——
​ |— R —| |—R—|

R×(1-(1-R$)^3$×(1-(1-R$)^2$))

差错控制

什么是码距?

一个编码系统的码距是整个编码系统中任意(所有)两个码字的最小距离
例:
若用1位长度的二进制编码。若A=1,B=0。这样A,B之间的最小码距为1
若用2位长度的二进制编码。若以A=11,B=00为例,A,B之间的最小码距为2
若用3位长度的二进制编码。可选用111,000作为合法编码。A,B之间的最小码距为3[检错可以进一步提高码距]

码距与检错、纠错有何关系?

① 在一个码组内为了检测e个误码, 要求最小码距d应该满足:d>=e+1
② 在一个码组内为了纠正 t个误码, 要求最小码距d应该满足:d>=2t+1

校验码 — 循环冗余校验码CRC

什么是模2除法?(异或运算相同为0,不同为1)

模2除法是指在做除法运算的过程中不计其进位的除法

加0,多项式阶数为r(等于多项式位数减1),则加r个0
eg:要发送数据1101011011,采用CRC校验,生成多项式10011(加四个0),则最终发送数据为?
11010110110000 ÷ 10011 => 采用异或运算
[待发送的信息补零 ÷ 多项式系数]
仅仅采用了CRC检验,如果检测到一个错误,则丢弃帧。缺重传机制,数据链路层的传输还不是可靠的传输


CRC编码 = 原始报文 后面补上 CRC校验码
注意:最后得到的位数必然是校验码位数,不够的需要补0
这里得到的结果是11,但是需要凑位数,所以校验码=0011

校验码 — 海明校验码

求信息1011的海明码[$2^r$ ≥ 4信息位 + r校验位 + 1全部正确]
n+r个数 有 $2^r$ ≥ n信息位 + r校验位 + 1全部正确个错
确定校验码为3位:$2^3$ ≥ 4 + 3 + 1;分别放在$2^0$=1、$2^1$=2、$2^2$=4 位
[校验位/位置 $2^n$;奇偶校验(0√ 1×);检验2位的错误, 纠正1位的错误]
② 列出校验位公式
7=$2^2$+$2^1$+$2^0$,6=$2^2$+$2^1$,5=$2^2$+$2^0$,3=$2^1$+$2^0$
$r_2$=$I_4$⊕$I_3$⊕$I_2$
$r_1$=$I_4$⊕$I_3$⊕$I_1$
$r_0$=$I_4$⊕$I_2$⊕$I_1$
③ 根据公式得r2=0,r1=0,r0=1
④ 将数据加入表格

7 6 5 4 3 2 1 位数
I4 I3 I2 I1 信息位
1 0 1 r2 1 r1 r0 校验位
7 6 5 4 3 2 1 位数
1 0 1 1 信息位
0 0 1 校验位
@@ 求信息1010海明校验码

$2^r$ ≥ n + r+ 1 => 4+r+1=> r=3 ==> n+r=4+3=7 下列表画7列
$2^n $=$2^0$,$2^1$,$2^2$,$2^3$

①001 ②010 ③011 ④100 ⑤101 ⑥110 ⑦111 位数
001 010 011 100 101 110 111 二进制
P1($2^0$) P2($2^1$) 1 P3($2^2$) 0 1 0 校验位
①001 ②010 ③011 ④100 ⑤101 ⑥110 ⑦111 位数
1 0 1 **1 **↑ 0 1 0 校验位

P1寻找位数二进制后尾是1的 => ③⑤⑦ => 1⊕0⊕0 => 1
P2第二位有1 => ③⑥⑦ => 1⊕1⊕0 => 0
P3最高位有1 => ⑤⑥⑦ => 0⊕1⊕0 => 1
故海明码:1011010

@@ 求信息D8—D1的10101011海明校验码

$2^r$ ≥ n + r+ 1 => n=8, 解得r=4; 一共要画8+4=12列
r=4 ==> $2^n$=$2^0$,$2^1$,$2^2$,$2^3$

位数
1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 二进制
1 0 1 0 P4($2^3$) 1 0 1 P3($2^2$) 1 P2($2^1$) P1($2^0$) 校验位

P1 => ③⑤⑦⑨⑪ => 1⊕1⊕1⊕0⊕0 => 1
P2 => ③⑥⑦⑩⑪ => 1⊕0⊕1⊕1⊕0 => 1
P3 => ⑤⑥⑦⑫ => 1⊕0⊕1⊕1 => 1
P4 => ⑨⑩⑪⑫ => 0⊕1⊕0⊕1 => 0
故海明校验码为:101001011111

操作系统基本原理

操作系统 — 概述:
  • 管理系统的硬件、软件、数据资源
  • 控制程序运行
  • 人机之间的接口
  • 应用软件与硬件之间的接口[API接口]

操作系统 — 管理职能:

  • 进程管理
    • 进程的状态
    • 前趋图
    • PV操作
    • 死锁问题
  • 存储管理
    • 段页式存储
    • 页面置换算法
  • 文件管理
    • 索引文件
    • 位示图
  • 作业管理
  • 设备管理
  • 微内核操作系统
    • 虚设备与SPOOLING技术

所属范围:应用程序语言处理程序{操作系统[计算机硬件]}】

三态→五态,时间片轮转

进程管理 — 前趋图

A:绞肉 B:切葱末 C:切姜末 D:搅拌 E:包饺子
A → B → C → D → E
A↘
B → D → E
C↗

进程管理 — 进程的同步与互斥

互斥:千军万马过独木桥
同步:(小明步行, 小红自行车从A到B点) 速度有差异,在一定情况停下等待
互斥反义词是共享,同步反义词是异步
进程的互斥:是指当有若干个进程都要使用某一共享资源时,任何时刻最多只允许一个进程去使用该资源,其他要使用它的进程必须等待,直到该资源的占用着释放了该资源。
进程的同步:是指在并发进程之间存在这一种制约关系,一个进程依赖另一个进程的消息,当一个进程没有得到另一个进程的消息时应等待,直到消息到达才被唤醒。

进程管理 — PV操作

PV操作是一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关
P表示通过的意思,V表示释放的意思
P操作可以看作是获得或者请求、消耗一个信号量
V操作可以看作是释放或者发送一个信号量

P操作会阻塞;
V操作会唤醒P操作;
P操作与V操作成对出现;

int f1=0; //表示进程P1是否执行完毕

main()
{
cobegin
p1();

​ coend
}

p1() {

v(f1);
v(f1);
}

临界资源:诸进程间需要互斥方式对其进行共享的资源,如打印机、磁带机等。
临界区:每个进程中访问临界资源的那段代码称为临界区
信号量:信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。

P(S)操作:S-=1 → S<0 →T 进程队列 orF向下进行
V(S)操作:S+=1 → S≤0 →T 进程队列 orF向下进行

P(S)、V(S)中的S就是信号量
T–进程操作阻塞(不会往下执行)
F–继续循环操作(执行下面的内容)

单缓冲区生产者、消费者问题PV原语描述:S1初值1,S2初值0
生产者:(先执行) 消费者:
生产一个产品; P(S2); [S2=0]
P(S1); [S1=0, S1=-1] 从缓冲区取产品;
送产品到缓冲区; V(S1);
V(S2); [S2=1] 消费产品;

付款要双方的配合,会有同步的关系;假设没有这个操作;若先进行收银员操作,则没有购书者先购书,收银员无法操作。此时b1有个P操作,P操作需要付款V操作来唤醒;所以一开始的PV是同一信号量P(S1), V(S1);对于购书者有个等待阻塞操作,等待收银员P(S2), V(S2)。【V(S1)唤醒P(S1),收费后由收银员的V(S2)唤醒付款的P(S2)】
n+1个人进来的话会被阻塞。

注意:
①P操作具备阻塞的职能,V操作不具备阻塞能力。
②PV操作关键要找到约束,找到谁是因变量,谁是自变量,谁约束谁。【PV操作是成对出现的】
③一对儿PV操作信号量是相同的。
看题发现已经存在了一对PV操作,信号量为Sn,Sn的值为n,很容易想到这对PV操作的作用就是控制进入书店的人数的。当人数达到n了,阻止人继续进入书店,这种状态直到有人付款离开书店为止。

再来看题发现还有两对PV操作,首先通过图能知道购书者和收银员之间存在约束关系,收银员要等待购书者付款才能工作,购书者要等待收银员反馈才能离开书店。那么这两对PV操作就是控制这个约束的。

先从购书者分析,购书者开始付款了,收银员才能开始收款,所以a1与b1之间是一对PV操作,进一步分析,如果没有购书者付款,那么收银员是不能执行收款操作的,也就是说收银员进程应该及时阻塞,故b1应该是P(S1),a1应该是V(S1)。

购书者发起付款操作的时候,不能马上离开书店,因为他要等收银员的反馈,付款成功拿到 小票才能离开,也就是说购书者在等待收银员反馈的时候应该及时阻塞住,故a2与b2之间是一对PV操作,且a2是P(S2),对应的b2就是V(S2)。

再检查一遍看看是否合理呢?

从收银员角度开始,假设没有人付款,P操作能及时阻塞,使得收银员进程不会进入收费状态,所以b1位置放P操作没有问题,进一步分析信号量S1的初始值应该为0,经过b1后S1为-1,阻塞。

从购书者角度看,开始付款的时候激活收银员进程,所以a1是V操作也没有问题,通过a1的操作,S1信号量又为0了,收银员进程可以进行收费了。

再返回收银员角度,收费完毕后要给购书者信号,购书者从才能离开,所以b2为V操作没有问题。

从购书者角度看,a2应该是P操作,应该及时阻塞住。进一步分析,S2信号量也应该是0才符合要求。

😁总结
PV操作从做题的角度出发,我认为首先要找到约束关系,就是谁和谁是一对约束,第二步确定这对约束谁是P谁是V(用反证方法推一下看看),最后一步考虑信号量,信号量为多少取决于是否马上阻塞还是说执行几次后再阻塞,这个要结合具体问题具体分析。

(53条消息) 软考必考题型之PV操作_pv操作中p和v各代表什么_du-hyper的博客-CSDN博客
(53条消息) 软考–快速掌握操作系统的PV操作_pv操作 软考_韦_恩的博客-CSDN博客

箭头的起始位置是V操作,箭头的终止位置是P操作

进程管理 — 死锁问题

进程管理是操作系统的核心,但如果设计不当,就会出现死锁的问题。如果一个进程在等待一件不可能发生的事,则进程就死锁了。而如果一个或多个进程产生死锁,就会造成系统死锁。

资源都被分配且无法得到资源释放

例题:系统有3个进程:A、B、C。这3个进程都需要5个系统资源。如果系统至少有多少个资源,则不可能发生死锁[有多少资源 无论怎么分配都不会产生死锁]

进程A 进程B 进程C

如果每个进程需要n个资源,总共有k个进程 =>分配情况:k × (n-1) + 1
答:每个进程都需要5个资源,共有3个进程 => n = 5; k = 3 ==> 3 × (5 - 1) + 1 = 12+1 =13

打破互斥:让大家同时共享资源
打破保持和等待:得不到相应的资源就会把资源分享出去,不会霸占并等待别人给与
打破不剥夺:不去抢别人分配到的资源

银行家算法:分配资源的原则

√ 当一个进程对资源的最大需求量不超过系统重的资源数时可以接纳该进程
√ 进程可以分期请求资源,但请求的总数不能超过最大需求量
√ 当系统现有的资源不能满足尚需资源数时,对进程的请求可以推迟分配,但总能使进程在有限的时间里得到资源

@@ 例:假设系统中有三类互斥资源R1、R2、R3,可用资源数分别是9、8、5。在T0时刻系统中有P1、P2、P3、P4、P5五个进程,这些进程对资源的最大需求量和已分配资源数如下所示,如果进程按序列执行,那么系统状态是安全的不发生死锁
【列表口诀: 需要 ()(经)子来分娩】
剩下的资源:总资源 — 已分配资源

A. P1→P2→P4→P5→P3 B. P2→P4→P5→P1→P3
C. P2→P1→P4→P5→P3 D. P4→P2→P4→P1→P3

  • 若先不执行P2,先执行P1,则右图R1还需资源数还需要5个,则你R1剩下的2个不够分配到,会发生进程死锁。即第一个先执行 P2。
  • 若先执行P2,再执行P1,则我们发现进程P1需要R1资源为5,我们能提供的R1资源为4,所以序列无法进行下去,为不安全序列
    只要现有资源可以满足需要资源的分配即可True,执行完后释放资源给下一步进程使用

存储管理 — 分区存储组织

某计算机系统的内存大小为128k,采用可变分区分配进行内存分配,当前系统的内存分块情况如下图所示,现有作业4申请内存9k,几种不同的存储分配算法在分配中,会产生什么样的结果呢?

最佳适应算法:分配任务空间时。任务由闲余空间从小到大依次尝试是否能成功分配。若小闲余空间能分配就分配。不可则进入下一个闲余空间进行空间区域切割划分。缺陷:内存的碎块(几k的闲余空间);
最差适应算法:则逆过来表示(先考虑从大的块中分配出来)
循环首次适应法:(分配较均匀)

存储管理 — 页式存储组织

逻辑地址页号对应物理地址块号(通过查表可得出);其页内地址都是一样的(调用的时候以页为单位, 偏移量不会有太大的变化)

例:进程P有6个页面,页号分别为0-5,页面大小为4K,页面转换表如下所示。表中状态位等于10分别表示页面在内存不在内存。假设系统给进程P分配了4个存储块,进程P要访问的逻辑地址为十六进制5A29H,那么该地址经过变换后,其物理地址应为十六进制(6A29H);如果进程P要访问的页面4不在内存,那么应该淘汰页号为(1)的页面。

页号 页帧号 状态位 访问位 修改位
0 2 1 1 0
1 3 1 0 1
2 5 1 1 0
3 0 0 0
4 0 0 0
5 6 1 1 1

(1) A. 1A29H B. 3A29H C. 5A29H D. 6A29H
(2) A. 0 B. 1 C. 2 D. 5

4k = 4×1024 = 2$^{2+10}$ = $2^{12}$ 说明一个页内地址是12位,高于12位的就是页号
由于进制P要访问的逻辑地址是十六进制5A29H,则一个十六进制位 = 4个二进制位
说明有3个位是页内地址 => A 2 9;所以页内地址无需求,是为A29
页号5需要查表观测它的页帧号是6;然后与后面拼接起来 为6A29H
将要淘汰的页号一定是在存在的里面选出 存在的才能淘汰,则1代表存在;
就要从页号0 1 2 5中淘汰一个,看其访问位,刚刚访问过的是1不能淘汰,
只能淘汰访问位是0的,综上所述,应该淘汰1页号

存储管理 — 段式存储组织

存储管理 — 段页式存储组织

先查段表 查完段表 查页表

存储管理 — 快表

快表是一块小容量的相联存储器(Associative Memory), 由高速缓存器组成,速度快,并且可以从硬件上保证按内容并行查找,一般用来存放当前访问最频繁的少数活动页面的页号。
若将这些东西放在内存当中则称为慢表(放在内存)、快表(放在Cache)

存储管理 — 页面置换算法(运用于分层的置换体系中)

页面淘汰算法
☆ 最优(Optimal, OPT) 算法
☆ 随机(RAND) 算法
★ 先进先出(FIFO) 算法:有可能产生”抖动”

抖动:给你更多的资源去处理反而效率降低了
例如432143543215序列用3个页面,比4个缺页要少
缺页:当加入内存中的页面序列是全新版本(内存中没有),则内存内部需要这个版本,因为缺少这个版本
第八-九列 编号为4的程序页不缺页(没有往前推进的原因是因为内存中还存有4的残渣[第八列];有3的残渣[第九列])

(9次) 4 3 2 1 4 3 5 4 3 2 1 5
1 4 4 4 3 2 1 4 4 4 3 5 5
2 3 3 2 1 4 3 3 3 5 2 2
3 2 1 4 3 5 5 5 2 1 1
缺页
(10次) 4 3 2 1 4 3 5 4 3 2 1 5
1 4 4 4 4 4 4 3 2 1 5 4 3
2 3 3 3 3 3 2 1 5 4 3 2
3 2 2 2 2 1 5 4 3 2 1
4 1 1 1 5 4 3 2 1 5
缺页

横向代表:访问的页面序列
纵向代表:内存的几号页面(第一列是编号为4的程序页)

LRU最近被访问的则不需要被淘汰,淘汰掉最久没有被访问到的
FIFO:先入先出原则

★ 最近最少使用(LRU) 算法:不会”抖动”

根据局部性原理,刚刚访问过的资源是不会被淘汰出去的

每读一次程序块,先在内存上面查一下表之后读取相应的内存块,所以每一个内存块需要进行两次内存的访问。总共有⑥个块,会产生12次内存的访问

A块在2号页有一半,在3号页面也有一半。所以总的缺页次数是5次;对于指令而言无论占用几个块都会一次性调用不会产生两次垄断,只有一次缺页垄断;∴swap A,B 一次;A上半页一次, 下半页一次;B上半页一次, 下半页一次;1+2+2=5次;1k有1024个单元(字节)

文件管理 — 索引文件结构

一个物理盘块是1K大,一个地址4Byte,除一下,每一个盘块可以存256个地址; ↓ ↓ ↓ ↓ ↓ ↓

File1的信息是二级地址索引表

操作系统 — 文件和树型目录结构

文件属性:R 只读文件属性 A 存档属性 S 系统文件 H 隐藏文件
文件名的组成:驱动号、路径、主文件名、扩展名
绝对路径:是从盘符开始的路径
相对路径:是从当前路径开始的路径
若当前目前位:D1,要求F2路径
则:绝对路径:/D1/W1/F2; 相对路径:W2/F2

文件管理 — 空闲存储文件的管理

空闲区表法(空闲文件目录)、空闲链表法、**位示图法**、成组链接法

(1) (4195+1) / 32 = 131.125 ≈ 第132字
(2) 用物理块毫无疑问是要制成1的 所以AC排除,131×32=4192 (0→4191),故第132字中:
第0位置→4192 第1位置→4193 第2位置→4194 第3位置→4195
第多少个字是从1开始算, 多少位置是从第0个位置开始算。

第1字 1 0 1 0 0 … 1 1
第2字 0 1 1 0 … 0 1
第3字 1 1 1 1 0 … 1 0
… …
第n字 0 0 0 1 1 … 0 0

设备管理 — 数据传输控制方式

设备管理 — 虚设备与SPOOLING技术

要输出输入的先缓存起来(输入井 输出井)

微内核操作系统

数据库系统

三级模式 — 两级映射

内模式:管我们如何去存储这些数据 也叫存储模式,对应于物理级
概念模式:表之间有一定的关联 逻辑模式 对应概念级
外模式:对应着数据库里的视图(对数据更灵活的控制) 子模式或用户模式,对应用户级
期间都存在着映射(起到逻辑独立性/物理独立性)

什么是外模式?
1)数据库的用户使用的局部数据的逻辑结构和特征的描述
2)数据库用户的数据视图,是与某一应用程序有关的数据的逻辑表示。(如应用程序A只能看见其相对于的外模式1,应用程序B只能看见其相对于的外模式2,不能看见不属于自己的外模式。相当于是模式的一个子集)

外模式的地位:介于模式与应用之间。

模式与外模式的关系:一对多
1)外模式是模式的子集
2)一个数据库可以有多个外模式,反应了不同的用户的应用需求、看待数据的方式、对数据保密的要求。
3)对于模式中的同一个数据,不同外模式可以对数据的长度、类型等有不同的定义。

外模式与应用的关系:一对多。
1)同一外模式可以为某一个用户的多个应用系统所使用
2)但一个应用程序只能使用一个外模式

外模式的用途:
1)保证数据库安全,每个用户只能看见自己对应外模式的数据
2)保证数据独立性。

总结:外模式是模式的一部分,是部分用户看到的数据库的样子。

内模式:
1)数据物理结构和存储方式的描述
2)是数据在数据库内部的表示方式
Ⅰ.记录的存储方式:如顺序存储,按B树结构存储,Hash存储)
Ⅱ.索引的组织方式:B+树索引,hash索引,Join index索引
Ⅲ.数据是否压缩存储
Ⅳ.数据是否加密
注:一个数据库只有一个内模式。

总结:内模式处于最底层,是对数据在数据库底层的存储的描述。

数据库特点:

数据结构化、数据的共享、冗余度低、数据独立性高、数据由DBMS统一管理和控制

数据库设计过程

E-R模型

概念模型 面向用户 用户角度出发 用户分析
逻辑结构/模型
物理结构/模型(数据库对象) 跟计算机系统发生联系

主码:[也称住关键字,它能够唯一标识一个元组。码可以是一个属性或属性组]

框框表示实体[客观存在并相互区别的事物]
椭圆表示属性[实体所具有的特征]
菱形表示联系[实体与实体之间的关系]
域:属性值的取值范围
实体型:用实体名及其属性名集合来描述同类实体也称为实体型 学生(学号, 姓名, 性别)
实体之间有各种各样的连信息(一对一联系1:1、一对多联系1:n、多对多联系m:n)

1个学生对应N个课程,1个课程对应M个学生;所以学生和课程是N:M的关系

集成的方法:

多个局部E—R图一次集成
逐步集成,用累加的方式一次继承两个局部E—R图

集成产生的冲突及解决办法:

属性冲突:包括属性域冲突和属性取值冲突
命名冲突:包括同名异义和异名同义
结构冲突:包括同一对象在不同应用中具有不同的抽象,以及同一实体在不同局部E—R图中所包含的属性个数和属性排列次序不完全相同

关系代数

基本运算

并 交 差 笛卡尔积 投影 选择 联接

★★★★ 考点 ★★★★

规范化理论—函数依赖

规范化理论—价值与用途

非规范化的关系模式,可能存在的问题包括:数据冗余,更新异常,插入异常,删除异常

超键可能存在冗余属性
候选键不存在冗余属性

规范化理论—求候选键

√ 将关系模式的函数依赖关系用”有向图“的方式表示
√ 找入度为0的属性,并以该属性集合为起点,尝试遍历有向图,若能正常遍历图中所有结点,则该属性集即为关系模式的候选键
√ 若入度为0的属性集不能遍历图中所有结点,则需要尝试性的将一些中间结点(既有入度,也有出度的结点)并入入度为0的属性集中,直至该集合能遍历所有结点,集合为候选键

规范化理论—范式

主属性(SNO CNO):属性属于候选键的一部分 (在任何一个候选关键字里出现过的都是主属性)
没有非主属性 肯定满足第二范式第三范式

规范化理论—综合例题

规范化理论—模式分解

当范式级别不够的时候把模式进行拆分 这样范式级别就可以上升

并发控制—基本概念

数据库完整性约束

√ 实体完整性约束:约束主键(主键为空 没有输入具体值)
√ 参照完整性约束:填入的数据必须是按照表里主键的内容(允许为空)
√ 用户自定义完整性约束:(年龄不允许输入负数 或者200以上的值)

触发器(较复杂的要求):可以写脚本来约束数据库数据的要求

数据库安全

措施 说明
用户标识和鉴定 最外层的安全保护措施,可以使用用户账户,口令及随机数检验等方式
存取控制 对用户进行授权,包括操作类型(如查找, 插入, 删除, 修改等动作)和数据对象(主要是数据范围)的权限
密码存储和传输 对远程终端信息用密码传输
视图的保护 对视图进行授权
审计 使用一个专用文件(日志)或数据库,自动将用户对数据库的所有操作记录下来

数据备份

√ 冷备份也称为静态备份,是将数据库正常关闭,在停止状态,将数据库的文件全部备份(复制)下来。
√ 热备份也称为动态备份,是利用备份软件,在数据库正常运行的状态下,将数据库中的数据文件备份出来

备份方式 / 优缺点 优点 缺点
冷备份 非常快速的备份方法只需要复制文件;容易归档(简单复制即可);容易恢复到某个时间点上(只需要将文件再复制回去);能与归档方法相结合,做数据库”最佳状态“的恢复;低度维护,高度安全 单独使用时,只能提供到某一个时间点上的恢复;再实施备份的全过程中,数据库必须要作备份二不能做其他工作;若磁盘空间有限只能复制到磁带等其他外部存储设备上,速度会很慢;不能按表或按用户恢复
热备份 可再表空间或数据库文件级备份,备份的时间短;备份时数据库仍可使用;可达到秒级恢复(恢复到某一时间点上);可对几乎所有数据库实体做恢复;恢复是快速的 不能出错,否则后果严重;如我热备份不成功所得结果不可用于时间点的恢复;因难于维护,所以要特别小心,不允许”以失败告终“

√ 完全备份:备份所有数据
√ 差量备份:仅备份上一次完全备份之后变化的数据
√ 增量备份:备份上一次备份之后变化的数据

若在周一的增量备份后系统出现故障,首先恢复周日完整版,再其上恢复周一增量版
如果周三的增量备份系统出现了故障,首先恢复周日完整版,再其上恢复周一周二周三增量版
这样恢复太麻烦,所以提出了差量备份, 周四的差量备份直接针对周日的差量变化
如果周四的增量备份系统出现了故障,首先恢复周日完整版,再其上恢复周四的差量版

  • 静态海量存储:在系统中无运行事务时进行, 每次存储全部数据库

  • 静态增量存储:在系统中无运行事务时进行,每次只存储上一个转储后更新过的数据库

  • 动态海量存储:转储期间允许对数据库进行存取或修改,每次转储全部数据库

  • 动态增量转储:转储期间允许对数进行存取或修改,每次只转储上一次转储后更新过的数据

日志文件:事务日志是针对数据库改变所做的记录,它可以记录针对数据库的任何操作(增删改查),并将记录结果保存在独立的 (先写日志 再写数据文件)

故障关系 故障原因 解决办法
事务本身的可预期故障 本身逻辑 再程序中预先设置Rollback语句
事务本身的不可预期故障 算数溢出, 违反存储保护 由DBMS的恢复子系统通过日志,撤销事务对数据库的修改,回退到事务初始状态
系统故障 系统停止运转 通常采用检查点法
介质故障 外存被破坏 一般使用日志重做业务

数据仓库与数据挖掘

数据仓库是面向各各主题的;会记录一些集成式的数据(报表)

反规范化(牺牲空间+规范化程度来换时间)

由于规范化会使表不断的拆分,从而导致数据表过多。这样虽然减少了数据冗余提高了增、删、改的速度,但会增加查询的工作量。系统需要进行多次连接,才能进行查询操作,使得系统效率大大下降。

技术手段

√ 增加派生性冗余列 (在成绩表里添加 姓名/课程名 能够快速查到哪个人成绩多少分)
√ 增肌冗余列
√ 重新组表(依据查询效率的原则)
√ 分割表(从效率角度看进行垂直分割、水平分割)

大数据 4V

Volume 数据量 Velocity 速度 Variety 多样性 Value 价值

比较维度 传统数据 大数据
数据量 GB或TB PB级或以上
数据分析需求 现有数据的分析与检测 深度分析(关联分析、回归分析)
硬件平台 高端服务器 集群平台
大数据处理系统应该具有的重要特征

高度可拓展性
高性能
高度容错
支持异构环境
较短的分析延迟
易用且开放的接口
较低成本
向下兼容性

计算机网络

中继器:类似于烽火狼烟
网桥:链接两个同类型的设备
交换机:用来链接多个设备

按分布范围分

局域网LAN 城域网MAN 广域网WAN 因特网

按拓扑结构分

总线型 星型 环型

层次 名称 主要功能 主要设备及协议
7 应用层 实现具体的应用功能 POP3、FTP、HTTP、Telnet、SMTP、DHCP、TFTP、SNMP、DNS
6 表示层 数据的格式与表达、加密、压缩 POP3、FTP、HTTP、Telnet、SMTP、DHCP、TFTP、SNMP、DNS
5 会话层 建立、管理和终止会话 POP3、FTP、HTTP、Telnet、SMTP、DHCP、TFTP、SNMP、DNS
4 传输层 端到端的连接 TCP、UDP
3 网络层 分组传输和路由选择 三层交换机、路由器、ARP、RARP、IP、ICMP、IGMP
2 数据链路层 传送以帧为单位的信息 网桥、交换机、网卡、PPTP、L2TP、SLIP、PPP
1 物理层 二进制传输 中继器、集线器

TCP/IP 协议体系结构
(1) 采用 分层结构,将复杂的大问题,划分成若干个简单的小问题
(2) TCP/IP从下向上,依次划分为 “网络接口层、网络层 、传输层、应用层”
【注意】”网络接口层” 对应了 OSI的 物理层和数据链路层
(3) 常用协议
① 数据链路层 [PPP 点对点协议 PPPoE 基于 局域网的PPP协议 over Ethernet]
用于 建立、维护 数据链路(数据通道)
② 网络层
IP:互联网协议,是所有通信必须使用的协议;作用:保证数据到达正确的目的地
ARP:地址解析协议,实现 局域网 中主机的 IP地址转网卡的物理地址
ICMP:互联网控制报文协议,功能:差错控制和查询主机
RARP:逆向ARP,局域网中 物理地址 转 虚拟IP地址

ping 127.0.0.1 回环测试本地主机内部网络状态是否正常

③ 传输层
TCP:传输控制协议,面向连接、可靠通信协议;功能:保证到达目的地的数据是正确的
UDP:用户数据报协议,无连接、不可靠通信协议,用于 大数据传输(流媒体):音频、视频
④ 应用层
HTTP 超文本传输协议:用于访问网络
HTTPS 加密/安全超文本传输协议
SMTP/MIME 简单邮件传输协议:用于发送电子邮件
POP3/IMAP 邮局协议第三版:用于接收电子邮件
FTP 文件传输协议:用于 上传下载文件
TELNET 远程登录协议
DNS 域名解析协议:实现 中英文域名 转换为 数字的IP地址

DHCP + DNS协议

网络规划与设计

★★★ 非常重要 IP地址相关计算题 ★★★

IP地址和子网掩码换算

已知ip地址和子网位数,例如:C网192.168.1.53/27, [子网位数是27]

1.具体的子网掩码
2.子网数
3.最大可容纳主机数
4.可用的主机数
5.网络地址
6.广播地址
7.地址范围
8.主机号

一、如何求具体的子网掩码(根据默认子网掩码和给出的子网位数求):

**1.**C网默认的子网掩码是:255.255.255.0
转换成二进制是:11111111.11111111.11111111.00000000
(1代表网络号,0代表主机号)
前24位是1,代表网络号,后8位是0,代表主机号
==已知子网位数是27==,代表网络号向主机号借用了3位(8+8+8+3)
得:11111111.11111111.11111111.11100000
把二进制转换为十进制:255.255.255.224
求出192.168.1.53/27对应的子网掩码是255.255.255.224

二、子网数

1.网络号向主机号借了3位(27-24),得**$2^3$=8个**

三、最大可容纳主机数(根据求出的子网掩码和给出的子网位数求)1.由一可知子网掩码是:255.255.255.254,转换为二进制是11111111.11111111.11111111.11100000
1代表网络号,0代表主机号,有5个0,得最大可容纳主机数是2^5=32

四、可用的主机数

由三可知,最大可容纳主机数是32个,32-2广播地址+网络地址=30

五、网络地址(把IP地址和子网掩码进行与运算)

192.168.1.53/27, 把IP地址和子网掩码进行与运算,IP地址转换为二进制:11000000.10101000.00000001. 0011010153;
子网掩码255.255.255.254转换为二进制:11111111.11111111.11111111.11100000,
进行与运算得:
IP地址: 11000000.10101000.00000001. 00110101
子网掩码: 11111111.11111111.11111111. 11100000(子网掩码连续全1的是网络地址,后面的是主机地址)
11000000.10101000.00000001. 00100000=====转换为十进制得:192.168.1.32
所以网络地址是192.168.1.32

六、广播地址(网络地址中的网络地址部分不变,主机地址变为全1,结果就是广播地址)

1.网络地址: 11000000.10101000.00000001. 00100000(标黄部分是网络地址),变为1得:11000000.10101000.00000001. 00111111

2.广播地址转换为十进制:192.168.1.63

七、地址范围

1.网络地址+1——-广播地址-1,得192.168.1.33—-192.168.1.62

八、主机号(子网掩码取反再和IP做与运算)

将一个网络划分为多个子网(取部分主机号当子网号)
将多个网络合并为一个大的网络(取部分网络号当主机号)

例1,将B类IP地址168.195.0.0划分为27个子网,子网掩码为多少?
十进制 二进制
168.195.0.0 1010 1000 1100 0011 0000 0000 0000 0000

$2^R$ ≥ N;R=5时 N=32 > 27
网络位要向主机位借5位
该例中需27个子网,按公式,需借5位

子网掩码:1111 1111 1111 1111 1111 1000 0000 0000 → 255.255.248.0每个子网能容纳的有效主机数为$2^{11}-2$=2046台
只要划分子网 就是变化的子网掩码

例2,将B类IP地址168.195.0.0划分称若干个子网,每个子网内有主机700台,则子网掩码为多少?(这两个子网掩码是根据不同的条件而得到的)

$2^R$ ≥ N;N=700;R=10 也就是剩余10个零地址位(网络位)只要有10位就够了
子网掩码:1111 1111 1111 1111 1111 11
00
0000 0000 → 255.255.252.0

无分类编址(无类域间路由)

IP地址 :: = {<网络前缀>, <主机号>}
128.14.32.0/20 表示的地址块共有$2^{12}$个地址
这个地址块的起始地址是128.14.32.0
在不需要指出地址块的起始地址时,也可将这样的地址快简称位 “/20地址块”
128.14.32.0/20 地址块的最小地址:128.14.32.0
128.14.32.0/20 地址块的最大地址:128.14.47.255
全0和全1的主机号地址一般不使用

例3,分配给某公司网络的地址块是210.115.192.0/20,该网络可以被划分位(16)个C类子网;

C类地址 24个子网位 8个主机位,题目是/20 前面20位为网络号;证明还要从主机号里拿出4个位来做子网号;4个位得到的子网数量是16个

特殊含义的IP地址

HTML

无线网

优势

灵活性 移动性 成本低 容易扩充

接入方式

有接入点模式 无接入点模式(对等网模式)

无线局域网(WLAN, 802.11, Wi-Fi)
无线城域网(WMAN, 802.16, WiMax)
无线广域网(WWAN, 3G/4G)
无线个人网(WPAN, 802.15, Bluetooth)

有线接入

公用交换电话网络(PSTN) 原始拨号上网 pose机+传真
数字数据网(DDN) 数字专用网(专线)
综合业务数字网(ISDN) 允许打电话的时候上网
非对称数字用户线路(ADSL) 老旧小区电话线部署
同轴光纤技术(HFC) 家里有线电视

无线接入

IEEE 802.11(WiFi)
IEEE 802.15(蓝牙Bluetooth)
红外(IRDA)
WAPI

3G/4G

WCDMA
CDMA2000
TD-SCDMA (国外多 速率低 功耗大)
LTE-Advanced
WirelessMAN-Advanceed(802.16m) (WiMAX)

IPV6

IPV6是设计用于替代现行版本IP协议(IPV4)的下一代IP协议。

(1) IPV6地址长度位128位,地址空间增大了$2^{96}$倍
(2) 灵活的IP报文头部格式。使用一系列固定格式的扩展头部取代了IPV4中可变长度的选项字段。IPV6中选项部分的出现方式也有所变化,使用路由器可以简单路过选项而不做任何处理,加快了报文处理速度
(3) IPv6简化了报文头部格式,字段只有8个,加快报文转发,提高了吞吐量
(4) 提高安全性。身份认证和隐私权是IPv6的关键特性
(5) 支持更多的服务类型
(6) 允许协议继续演变,增加新的功能,使之适应未来技术的发展

**单播地址(Unicast)**:用于单个接口的标识符
**任播地址(Anycast)**:泛播地址。一组接口的标识符,IPv4广播地址
**组播地址(Multicast)**:IPv6中的组播在功能上与IPv4中的组播类似

信息系统安全属性

安全属性

保密性:最小授权原则、防暴露、信息加密、物理加密
完整性:安全协议、校验码、密码校验、数字签名、公证
可用性:综合保障(IP过滤、业务流控制、路由选择控制、审计跟踪)
不可抵赖性:数字签名

加密技术

信息摘要(不能用作加密算法 因为不能解密) (无法篡改)

单向散列函数(单向Hash函数)、固定长度的散列值
常用的消息摘要算法有MD5、SHA等,市场上广泛使用的MD5,SHA算法的散列值分别为128和160位,由于SHA通常采用的密钥长度较长,因此安全性高于MD5

数字签名

数字证书:识别人的身份

数字信封与PGP

发送方将原文用对称密钥加密传输,而将对称密钥用接收公钥加密发送給对方
接受方收到电子信封,用自己的私钥解密信封,取出对称密钥解密得原文

PGP可用于电子邮件,也可以用于文件存储。采用了杂合算法,包括IDEA、RSA、MD5、ZIP数字压缩算法

PGP承认两种不同的证书格式:PGP证书和X.509证书

PGP证书包含PGP版本号、证书持有者的公钥、证书持有者的信息、证书拥有者的数字签名、证书的有效期、密钥首选的对称加密算法

X.509证书包含证书版本、证书的序列号、签名算法标识、证书有效期、以下数据:证书发行商名字、证书主题名、主体公钥信息、发布者的数字签名

练习题 — 设计邮件加密系统

要求邮件以**加密方式传输(加密解密技术 ),邮件最大附件内容可达500MB( 对称加密 ),发送者不可抵赖** ( 数字签名 ),若邮件被第三方截获,**第三方无法篡改** ( 信息摘要技术 )。

网络安全 — 各个网络层次的安全保障

网络安全 — 网络威胁与攻击

防火墙技术

数据结构与算法基础

数组与矩阵、线性表、广义表、树与二叉树、图、排序与查找算法基础及常见的算法

数组

数组类型 存储地址计算
一维数组a[n] a[i]的存储地址为:a + i * len
二维数组a[m] [n] a[i] [j]的存储地址(按行存储):a + ( i * n + j) * len
a[i] [j]的存储地址(按列存储):a + ( j * m + i) * len
1 a[0] [0] 2 a[0] [1] 3 a[0] [2]
4 a[1] [0] 5 a[1] [1] 6 a[1] [2]
7 a[2] [0] 8 a[2] [1] 9 a[2] [2]

已知5行5列的二维数组a中的各元素占两个字节,求元素a[2] [3]按优先存储的存储地址?

a[i] [j]的存储地址(按行存储):a + ( i * n + j) * len
(2 * 5 + 3) * 2,a[2] [3]是存储的第14个元素,由于编号是从0开始算的,有13个偏移量
∴ a + 13 * 2 = 每一个元素所占的字节数 a[0] [0] = a

稀疏矩阵

数据结构

数据结构是指互相之间存在一种或多种特定关系的数据元素的集合
包含 包含线性结构

线性结构 O-O-O-O
非线性结构 树 + 图

线性表

顺序表 + 链表(单链表、循环列表、双向链表)

顺序存储与链式存储对比

性能类别 具体项目 顺序存储 链式存储
空间性能 存储密度 =1,更优 (1=100%) < 1 (有节点专门存地址信息)
容量分配 事先确定 动态变化,更优 (动态改变分配)
时间性能 查找运算 O(n/2) O(n/2)
读运算 O(1),更优 O([n+1]/2), 最好情况为1,最坏情况为n (需要一格一格的移)
插入运算 O(n/2),最好情况为O,最坏情况为n O(1),更优 (局部小外科手术)
删除运算 O([n-1]/2) O(1),更优 (局部小外科手术)

线性表—队列与栈

广义表

树与二叉树

二叉树遍历

反向构造二叉树

树转二叉树

查找二叉树

最优二叉树(哈夫曼树)

线索二叉树

平衡二叉树

树和图的最大区别:树是没有环路的

完全图

邻接图

图的遍历(深度+广度)

图 — 拓扑排序

图的最小生成树 — 普里姆算法

算法基础

算法的特性

有穷性:执行有穷步之后结束
确定性:算法中每一条指令都必须有确切的含义,不能含糊不清
输入:(>=0)
输出:(>=1)
有效性:算法的每个步骤都能有效执行并能得到确定的结果。例a=0, b/a就无效

算法的复杂度

时间复杂度:是指程序运行从开始到结束所需要的时间。通常分析时间复杂度的方法是从算法中选取一种对于所研究的问题来说是基本运算的操作,以该操作重复执行的次数作为算法的时间量度。一般来说,算法中原操作重复执行的次数是规模n的某个函数T(n)。由于许多情况下要精密计算T(n)是困难的,因此引入了渐进时间复杂度在数量上估计一个算法的执行时间。其定于如下:
如果存在两个常数c和m,对于所有的n,当n≥m时有f(n) ≤ cg(n), 则有f(n) = O(g(n)),也就是说,随着n的增大,f(n)逐进地不大于g(n)。例如,
一个程序的实际执行时间为T(n)=3$n^3$+2$n^2$+n, 则T(n) = O($n^3$)
[以最高的时间复杂度为准!三重for循环时间复杂度是O($n^3$)]

常见的对算法执行所需时间的量度:
O(1) < O(lo$g_2$n) < O(n) < O(nlo$g_2$n) < O($n^2$) < O($n^3$) < O($2^n$)

log的由来:排序二叉树 找结点 有7个结点的完全二叉树,向下比较 最多比较3次(二叉树的层数) 最坏的情况多少层就比较多少次 所以有lo$g_2$n,n就是结点数量

**空间复杂度:**是指对一个算法在运行过程中临时占用存储空间大小的度量。一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小

查找 — 顺序查找与二分查找

查找 — 散列表

排序

直接插入排序

希尔排序(效率高于直接插入排序)

直接选择排序

堆排序(适合于:不需要求全部 求前一部分)

冒泡排序

快速排序

归并排序

基数排序

算法排序汇总

程序设计语言与语言处理程序

编译过程

文法定义

有限自动机与正规式

程序语言基础—表达式

函数调用—传值与传址

程序语言基础—各种程序语言特点

Fortran语言(科学计算,执行效率高)
Pascal语言(为教学而开发的,表达能力强,Delphi)
C语言(指针操作能力强,高效)
Lisp语言(函数式程序语言,符号处理,人工智能)
C++语言(面向对象,高效)
Java语言(面向对象,中间代码,跨平台)
C#语言(面向对象,中间语言,.Net)
Prolog语言(逻辑推理,简洁性,表达能力,数据库和专家系统)

法律法规 — 课程内容提要

从所涉及的法律法规角度

著作权法、计算机软件保护条例、商标法、专利法

@@ 著作权因作品的完成而自动产生,不必履行任何形式的登记或注册手续,也不论其是否已经发便,所以甲对该软件作品享有著作权,乙未经甲的许可擅自使用甲的软件作品的行为,侵犯了甲的软件著作权

@@ 关于软件著作权产生的时间是自作品完成创作之日

从试题考点分布的角度

保护期限、知识产权人确定、侵权判断

知识产权
著作权及邻接权、专利权、工业品外观设计权、商标权、地理标志权、集成电路布图设计权

著作权:一般保护作者的利益
邻接权:别人盗写我出的书,不仅破坏了我的权益,还有**出版商的权益
地理标志权
新疆**哈密瓜 只有在标志的地理位置生产(这一区域都拥有)

法律法规 — 保护期限, 知识产权人

侵权判定

中国公民、法人或者其他组织的作品,无论是否发表,都享有著作权
开发软件所用的思想、处理过程、操作方法或者数学概念不受保护
著作权法不适用于下列情形:

√ 法律、法规、国家机关的决议、决定、命令和其他具有立法、行政、司法性质的文件,及其官方正式译文

√ 时事新闻

√ 历法、通用数表、通用表格和公式

不侵权 侵权
√ 个人学习、研究或欣赏
√ 适当引用
√ 公开演讲内容
√ 用于教学或科学研究
√ 复制馆藏作品
√ 免费表演他人作品
√ 室外公共场所艺术品临摹、绘画、摄影、录像
√ 将汉语作品译成少数民族语言作品或盲文出版
√ 未经许可,发表他人作品
√ 未经合作作者许可,将与他人合作创作的作品当作自己单独创作的作品发表的
√ 未参加创作,在他人作品署名
√ 歪曲、篡改他人作品
√ 剽窃他人作品的
√ 使用他人作品,未付报酬
√ 未经出版者许可,使用其出版的图书、期刊的版式设计的

标准化基础知识—标准的分类

√ 国际标准:ISO、IEC等国际标准化组织
√ 国家标准:GB—中国、ANSI—美国、BS—英国、JIS—日本
√ 区域标准:又称为地区标准,如PASC—太平洋地区标准会议、CEN—欧洲标准委员会、ASAC—亚洲标准咨询委员会、ARSO—非洲地区标准化组织
√ 行业标准:GJB—中国军用标准、MIT-S—美国军用标准、IEEE—美国电气电子工程协会
√ 地方标准:国家的地方一级行政机构制订的标准
√ 企业标准
√ 项目规范

→ 国标、国外标准代号:标准代号+专业类号+顺序号+年代号
→ 我国国家标准代号:强制性代号为GB、推荐性标准代号为GB/T、指导性标准代号为GB/Z、实物标准代号GSB

→ 行业标准代号:由汉语拼音大写字母组成(如电子行业为SJ)
→ 地方标准代号:由DB加上省级行政区划代码的前两位
→ 企业标准代号:由Q加上企业代号组成

多媒体基础

人耳:20Hz—20kHz 小于20Hz是次声波 大于20kHz是超声波
说话:300—3400Hz
乐器:20Hz —20kHz
白噪音:20 ~ 20kHz

采样:采样频率、采样精度、采样频率应为声音最高频率的2倍

采样点密集程度越高,时间间隔越短,还原度越好
采样精度画格子的数量 y轴平行于x做直线

图像相关概念

色彩三要素色

亮度是光作用于人眼时所引起的明亮程度的感觉,它与被观察物体的发光强度有关。
色相是当人眼看一种或多种波长的光时所产生的彩色感觉,它反映颜色的种类,是决定颜色的基本特性。
饱和度是指颜色的纯度,也可以叫做纯度、彩度或浓度等,即掺入白光的程度,或者是指颜色的深浅程度。

HSB彩色模式
是根据日常生活中人眼的视觉特征而制定的一套色彩模式。HSB颜色模式以色相、饱和度和亮度描述颜色的基本特征。

CMY颜色模式
是采用**青(Cyan、品红或洋红(Magenta)、黄(Yellow)**3种基本颜色按一定比例合成颜色的方法。颜色的产生是来自于照射在颜料上反射回来的光线。
图像打印输出时用CMY颜色模式。

Lab颜色模式
分别用**亮度或光亮度分量(Luminosity)和两个色度分量(a、b)**来表示颜色
L表示亮度。L的值域由0到100,L=50时,相当于50%的黑。
a表示从洋红色至绿色的范围,
b表示从黄色至蓝色的范围,
a和b的值域是由+127至-128。

索引颜色模式
最多使用256种颜色,当图像被转换为索引颜色模式时,通常会构建一个调色板存放图像中的颜色并编制颜色索引。

位图模式
位图模式的图像只有黑色与白色两种像素,每个像素用1位二进制数表示,”0”表示黑色,“1”表示白色。

灰度模式
用单一色相表现图像,最多使用256级。图像中的每个像素有一个0(黑色)~255(白色)之间的亮度值。此外,灰度值也可以用黑色油墨覆盖的百分比来表示(0%表示白色,100%表示黑色)。

颜色深度
位图图像中各像素的颜色信息是用二进制数据描述的。
色彩由颜色深度决定,不是分辨率
二进制的位数就是位图图像的颜色深度。颜色深度决定了图像中可以呈现的颜色的最大数目。

分辨率:
图像分辨率(Image Resolution):指单位图像线性尺寸中所包含的像素数目,通常以**像素/英寸(pixel per inch,ppi)**为计量单位。

彩色空间:RGB彩色显示器、YUV(电视, 兼容方案)、CMY[K]、HSV(HSB)艺术家角度

光的颜色采取叠加原理
印刷的三原色采取相减原理

媒体的种类

感觉媒体,指通过人的感觉器官能直接感受的媒体,如视觉。
表示媒体,用于传播和表达感觉媒体的中介媒体,是信息的表示和表现形式,如JPG编码
表现媒体(显示媒体),是进行信息输入和输出的一类媒体,如显示器
传输媒体,是用于通信传输的信息载体,如双绞线
存储媒体,是存放表示媒体的物理实体,如光盘

感觉媒体:听觉、触觉、嗅觉;
表示媒体:文字、数字、音频、视频、编码方式;
表现媒体(显示媒体):输入显示媒体键盘、鼠标和麦克风、输出显示媒体显示器、打印机和音箱
存储媒体:存储数据的物理设备,硬盘、磁盘、光盘U盘
传输媒体:存储数据的物理载体,电缆、光缆和交换设备

  • D/A转换,数字音频模拟化输出
计算
  • 波形音频文件容量的计算 容量=声道数 * 采样频率 * 量化位数 * 长度 / 8
    其中频率单位Hz,量化单位bit,长度秒,容量单位字节B
  • 波形音频文件码率的计算 码率=声道数 * 采样频率 * 量化位数 码率的单位是bps
常见波形文件格式
  • ==.wav== 微软和IBM共同开发的PC标准音频格式,未压缩,声音达到CD音质,码率约为1.4Mb/s,Windows XP录音机默认音频格式
  • .mp3 有损压缩,最常用 互联网、MP3音乐
  • ==.wma==微软公司的有损压缩,压缩比高于MP3,Win7录音机默认格式
  • .m4a 苹果公司的无损压缩
  • .flac 无损压缩,高品质数字音乐
  • .ape 无损压缩音频格式

@@ WAV文件称为波形声音文件,其音质与CD差不多
@@ MP3文件能够达到很高的压缩比,并能保持较高的音质;通常那个说WAV文件比MP3文件大[比WAV/CD好]
@@ MIDI不能从CD、磁带、麦克风等录制MIDI文件;通常MIDI文件小于MP3

MIDI格式
  • MIDI乐器数字接口,垫桌子音乐制造商们建立的通信标准
  • MIDI传输的不是声音信号,记录声音的信息,是在线音乐的一组指令,是音符、控制参数等指令,它指示MIDI设备要做什么,怎么做; 如演奏哪个音符,音量多大等
  • .mid 或 .midi

==知识点总结==

图像

  • 位图:.bmp不压缩 .gif无损压缩 .jpg/jpeg有损压缩 .png无损压缩 .tif
  • 矢量图:.swf .ai .dwg .cdr

波形

  • .wav .mp3有损压缩 .wma有损压缩:录音机(微软)

  • .m4a无损压缩(苹果) .flac无损压缩(高质量数字音乐) .ape无损压缩音频

  • MIDI [.mid/.midi]乐谱

视频编码

  • .mpeg/.mp4 H.26X .avi .asf .wmv .rm/.rmvb .flv .mkv .mov

流媒体

  • RM、WMV、ASF

音频信息在计算机中的表示

波形数字音频

音频信息处理的流程
  • A/D转换[采样],模拟音频的数字化【影响数字化质量的主要原因】

多媒体相关计算问题

图像容量计算640(水平像素)×480(垂直像素)

颜色深度的单位是 “位 bit “图像容量的单位是字节B; 1 Byte = 8 Bits

条件 示例
知道像素,位数 每个像素为16位,图像位640×480像素,求容量:
640×480×16÷8=614400B
知道像素,色数 640×480像素,256色的图像,求容量:
640×480×log2(256)÷8=307200B
音频容量计算

音频容量 = 声道数 * 采样频率(Hz) * 量化位数 * 长度 / 8

视频容量计算

视频容量 = 每帧图像容量(Byte) * 每秒帧数 * 时间 + 音频容量 * 时间

常见的多媒体标准

==@@ 存储动画的文件格式有FLC、GIF、SWF==

==@@ 网络视频格式包括MOV、RM、ASF、WMV==

==@@ 多媒体视频图像文件格式有AVI、MPG、ASF、MP4==

==@@ 声音、音频文件格式有WAV、WMA、MP3、MIDI、RA、APE==

==@@ 属于图像文件格式有GIF、BMP、JPG、PNG、TIF==

==.wma==微软公司的有损压缩,压缩比高于MP3,Win7录音机默认格式

@@ 位图(Bitmap)=> BMP

@@ 语音识别技术体现了多媒体技术人工智能技术相结合

数据压缩基础

空间冗余(几何冗余)、时间冗余、视觉冗余、信息熵冗余、结构冗余、知识冗余

空间冗余:图片大面积相同色(白色),记录哪些信息是白色
时间冗余:固定在一个界面跳舞时,后面的物体不会动,墙面不会动。不动的记录下来。有变动的就分析
视觉冗余:jpeg人眼视觉识别盲区(视觉边界点压缩)
信息熵冗余:不同的信息编码冗余度不一样,通过合理的信息编码
结构冗余:某个部件有大量冗余,地砖花纹一样
知识冗余:可以通过知识分析得到的信息

有损压缩与无损压缩

无损压缩编码法(Lossless compression coding):也称之为**冗余压缩法熵编码法**
有损压缩编码法(Loss compression coding):**熵压缩法**

jpeg属于有损(比较高的压缩比),无法还原成原始图(位图)

软件开发模型

瀑布模型、演化模型、增量模型、螺旋模型、快速原型模型、喷泉模型、V模型
迭代模型/迭代开发方法、快速应用开发、构件组成模型/基于构件的开发方法、敏捷开发方法、模型驱动的开发方法、基于架构的开发方法

@@ 在面向对象技术构建软件系统时,很多敏捷方法都建议的一种重要的设计活动是重构,它是一种重新组织的技术,可以简化构件的设计而无需改变其功能或行为

瀑布模型(SDLC)

原型、演化模型、增量模型

螺旋模型

V模型 喷泉模型 RAD

构件组装模型(CBSD)

统一过程

敏捷开发方法

信息系统开发方法

需求分类与需求获取

结构化设计:基本原则、内聚与耦合

结构化设计 — 系统结构/模块结构

软件测试 — 测试原型与类型

软件测试 — 测试用例设计

软件测试 — 测试阶段

McCabe复杂度

McCabe度量法概念:

McCabe度量法是通过定义环路复杂度,建立程序复杂度的度量,他是基于一个程序模块的程序图中环路的个数。计算G的环路复杂型有两种方法:
第一种是V(G)=m-n+2(m值得是有向弧数,也就是箭头的个数,n指的是结点个数
另外一种求法就是闭合区域的个数+1

系统运行与维护

软件过程改进—CMMI

项目管理

风险

风险是指”损失或伤害的可能性”

软件风险一般包含不确定性损失救火危机管理是对不适合但经常采用的软件风险管理策略已知风险未知风险是对软件风险进行分类的一种方式员工预算是在识别项目风险时需要识别的因素

项目风险(关心未来)、技术风险(关心变化)、商业风险(关心选择)
风险曝光度(Risk Exposure):计算方法是风险出现的概率 × 风险可能造成的损失
假设正在开发的软件项目可能存在一个未被发现的错误,而这个错误出现的概率是0.5%,给公司造成的损失将是1000000元,那么这个错误的风险曝光度就应该为1000000×0.5%=5000元

需求开发,需求分析,OOA — 相关概念

面向对象设计 — 设计原则

单一职责原则:设计目的单一的类 (单一会降低程序的耦合度)
开放—封闭原则: 对扩展开放,对修改封闭 (用新的类去解决问题 不去修改[容易引入错误影响原先])
李氏(Liskov)替换原则:子类可以替换父类 (不要盲目修改父类 不要去重载)
接口隔离原则:使用多个专门的接口比使用单个的总接口要好
组合重用原则:要尽量使用组合,而不是继承关系达到重用的目的
**迪米特(Demeter)原则(最少知识法则)**:一个对象应当对其他对象又尽可能少的了解

UML

设计模式的概念

**√ 架构模式**:软件设计中的高层决策,例如C/S结构就属于架构模式,架构模式反映了开发软件系统过程中所作的基本设计决策
**√ 设计模式**:主要关注软件系统的设计,与具体的实现语言无关
**√ 惯用法**:是最底层的模式,关注软件系统的设计与实现,实现时通过某种特定的程序设计语言来描述构件与构件之间的关系。每种编程语言都有它自己特定的模式,即语言的惯用法。例如引用—计数就是C++语言中的一种惯用法

面向对象 — 设计模式的分类

面向对象 — 创建型模式

设计模式名称 简要说明
Abstract Factory 抽象工厂模式 提供一个接口,可以创建一系列相关或相互依赖的对象,而无需置顶它们具体的类
Builder 构建器模式 将一个复杂类的表示与其构造相分离,使得相同的构造过程能够得出不同的表示
Factory Method 工厂方法模式 定义一个创建对象的接口,但由子类决定需要实例化哪一个类。工程方法使得子类实例化的过程推迟
Prototype 原型模式 用原型实例指定创建对象的类型,并且通过拷贝这个原型来创建新的对象
Singleton 单例模式 保证一个类只有一个实例,并提供一个访问它的全局访问点

面向对象 — 结构型模式

设计模式名称 简要说明 速记关键字
Adapter 适配器模式 将一个类的接口转换成用户希望得到的另一种接口。它使原来不相容的接口得以协同工作 转换接口
Bridge 桥接模式 将类的抽象部分和它的实现部分分离开来,使它们可以独立地变化 继承树拆分
Composite 组合模式 将对象组合成树型结构以表示”整体—部分”的层次结构,使得用户对单个对象和组合对象的使用具有一致性 树形目录结构
Decorator 装饰模式 动态地给一个对象添加一些额外的职责。它提供了用子类扩展功能的一个灵活的代替,比派生一个子类更加灵活 附加职责
Facade 外观模式 定义一个高层接口,为子系统中的一组接口提供一个一致的外观,从而简化了该子系统的使用 对外统一接口
Flyweight 享元模式 提供支持大量细粒度对象共享的有效方法
Proxy 代理模式 为其他对象提供一种代理以控制这个对象的访问

面向对象 — 行为型模式

设计模式名称 简要说明 速记关键字
Chain of Responsibility 职责链模式 通过给多个对象处理请求的机会,减少请求的发送者与接收者之间的耦合。将接收对象链接起来,在链中传递请求,直到有一个对象处理这个请求 传递职责
Command 命令模式 将一个请求封装为一个对象,从而可用不同的请求对客户进行参数化,将请求排队或记录请求日志,支持可撤销的操作 日志记录,可撤销
Interpreter 解释器模式 给定一种语言,定义它的文法表示,并定义一个解释器,该解释器用来根据文法表示来解释语言中的句子
Iterator 迭代器模式 提供一种方法来顺序访问一个聚合对象中的各个元素,而不需要暴露该对象的内部表示
Mediator 中介者模式 用一个中介对象来封装一系列的对象交互。它使各对象不需要显式地互相条用,从而达到低耦合,还可也独立地改变对象间的交互 不直接引用
Memento 备忘录模式 在不破坏封装性的前提下,捕捉一个对象的内部状态,并在该对象之外保存这个状态,从而可以在以后将该对象恢复到原先保存的状态
Observer 观察者模式 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
State 状态模式 允许一个对象在其内部状态改变时改变它的行为 状态变成类
Strategy 策略模式 定义一系列算法,把它们一个个封装起来,并且使它们之间可相互替换,从而让算法可以独立于使用它的用户而变化 多方案切换
Template Method 模板方法模式 定义一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重新定义算法的某些特定步骤
Visitor 访问者模式 表示一个作用于某对象结构中的各元素的操作,使得在不改变各元素的类的前提下定义作用于这些元素的新操作

数据流图(DFD)

数据流图基本概念

数据字典

符号 含义 举例说明
= 被定义为
+ x=a+b, 表示x由a和b组成
[… , …]或[… | …] x=[a,b], x=[a|b], 表示x由a或由b组成
{…} 重复 x={a}, 表示x由0个或多个a组成
(…) 可选 x=(a), 表示a可在x中出现, 也可以不出现

机票 = 姓名 + 日期 + 航班号 + 起点 + 终点 + 费用
航班号 = “Y7100”..”Y8100”
终点 = [长沙|上海|北京|西安]

选择四个其中的一个终点

数据流图平衡原则

父图与子图之间的平衡、子图内平衡

答题技巧

试题1

第一问解析
第二问解析
第三问解析

试题二

试题2第一问、第二问
试题2第三问 (顶层和0层进行匹配)、第四问(从题干推导)

数据库设计过程

ER模型 — 实体间联系类型

数据库设计答题技巧

详细分析试题说明
熟练掌握基本知识

试题1

试题2

解析

UML建模

用例图、类图与对象图、顺序图、活动图、状态图、通信图、构件图

用例图

类图与对象图

顺序图

活动图

状态图

通信图 (顺序图+通信图=交互图)

区别:顺序图会强调时间顺序

试题1

解析

试题2

第一问
第二、三问

★★数据结构及算法应用★★(下午题)

分治法、回溯法、贪心法、动态规划法

分治法(★★ 分解 解决 合并 ★★) [一般用到递归]

对于一个规模为n的问题,若该问题可以容易地解决 (比如说规模n较小) 则直接解决;否则将其分解为k个规模较小的子问题,这些子问题相互独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解

★ 该问题的规模缩小到一定的程度就可以容易地解决
★ 该问题可以分解为若干个规模较小的相同子问题
★ 利用该问题解决出的子问题的解可以合并为该问题的解
★ 该问题所分解出的各个子问题是相互独立的

递归,就是在运行的过程中调用自己
int F(int n)
{
    if(n==0) return 1;
    if(n==1) return 1;
    if(n>1) return F(n-1)+F(n-2);
}

分治法 → 二分查找

function Binary_Search(L,a,b,x){
    if(a > b) return(-1);
    else{
        m=(a+b)/2;
        if(x==L[m]) return(m);
        else if(x > L[m])
            return(Binary_Search(L,m+1,b,x));
        else
            return(Binary_Search(L,a,m-1,x));
    }
}

回溯法

贪心法

动态规划法

数据结构算法例题

试题1
解析
试题2及解析

★★★★★面向对象程序设计(下午题)★★★★★

C++ 类与派生类的定义

class 类名{
    public:
        公有数据成员或公有函数成员的定义;
    protected:
        保护数据成员或保护函数成员的定义;
    private:
        私有数据成员或私有函数成员的定义;
};
class 派生类名:继承方法1 基类名1,继承方法2 基类名2,...{
    public:
        派生类的公有数据和函数
    protected:
        派生类的保护数据和函数
    private:
        派生类的私有数据和函数
};
在类外定义函数体的格式如下:
返回值类型 类名 :: 成员函数名(形参表){
    函数体;
}
::是类的作用域分辨符,用在此处,放在类名后成员函数前,表明后面的成员东西函数属于前面的那个类

C++构造函数与析构函数

构造函数相对于一般函数来说,具有如下特殊的性质:
1.构造函数的函数名必须与定义它的类同名
2.构造函数没有返回值。如果在构造函数前加void是错误的
3.构造函数被声明定义为公有函数
4.构造函数在建立对象时由系统自动调用

构造函数相对于一般函数来说,具有如下特殊的性质:
1.析构函数没有任何参数,不能被重载,但是可以是虚函数,一个类只有一个析构函数
2.析构函数没有返回值
3.析构函数名与类名相同,但在类名前加一个逻辑非运算符 “~” 以表示与构造函数对比区别
4.析构函数一般由用户自己定义,在对象消失时由系统自动调用,如果用户没有定义析构函数,系统将自动生成一个不做任何事的默认析构函数

对象指针的语法定义形式如下:
    类名 *对象指针名;
对象引用的定义形式如下:
    类名 &对象引用名 = 被引用对象;

注意:通过对象名或对象引用访问对象的成员,使用的运算符是 “.” 而使用对象指针访问对象成员,使用的运算符是 “->”

对象指针名 -> 数据成员名或 : 对象指针名 -> 成员函数名(参数表) 

C++虚函数

虚函数定义的一般语法形式如下:
virtual 函数类型 函数名(形参表){
    函数体;
}
纯虚函数定义形式如下:
virtual 函数名 = 0;

JAVA 类的定义

类的定义格式如下:
[import包]
[类修饰符] class xxxclass [extends超类] [implements接口]{
    public:
        公有数据成员或公有函数成员的定义;
    protected:
        保护数据成员或保护函数成员的定义;
    private:
        私有数据成员或私有函数成员的定义;
}
说明:
import包:引入包中的类
类修饰符:主要由四个修饰符(public、abstract、final、private)
class为关键字, xxxclass为类名,命名遵循Java标识符的命名规则
extends为继承关键字,implements为接口关键字
    
抽象类定义
abstract class Shape{
    abstract public void draw() [定义了draw抽象方法]
}

class Rectangle extends Shape{}
通过extends可以看出来Shape不是接口而是抽象类
import java.util.*;
(1) class Beverage{ //饮料
    String description = "Unknown Beverage";
    public (2) (){return description;}
        public (3);
}
abstract class CondimentDecorator extends Beverage{
    //配料
    (4);
}

(1) abstract
(2) String getDescription
(3) abstract int cost()
(4) Beverage beverage

JAVA 接口的定义

interface IFactory{}
class SqlServerFactory implements IFactory
通过implements实现关键字来反推interface关键字

面向程序设计

试题1及解析
试题2及解析
阅读全文

二十大知识点

2023/3/22

大会主题

高举中国特色社会主义伟大旗帜,全面贯彻新时代中国特色社会主义思想,弘扬伟大建党精神,自信自强、守正创新、着力奋发、勇毅前行,为全面建设社会主义现代化国家、全面推进中华民族伟大复兴而团结奋斗

==三个务必==

务必不忘初心、牢记使命,务必谦虚谨慎、艰苦奋斗,务必敢于斗争、善于斗争

==十年来经历了哪三件大事==[多]

迎来了中国共产党成立一百周年
中国特色社会主义进入新时代
完成脱贫攻坚、全面建成小康社会的历史任务,实现第一个百年奋斗目标。

五年来的大事[多]

坚持加强党的全面领导和党中央集中统一领导
统筹疫情防控和经济社会发展取得重大积极成果
香港局势实现由乱到治的重大转折
坚持开展反分裂、反干涉重大斗争
牢牢掌握了我国发展和安全主动权

开辟马克思主义中国化时代化新境界

两个”行” [选]

中国共产党为什么能,中国特色社会主义为什么好,归根到底是马克思主义行,是中国化时代化的马克思主义行

六个”必须坚持”(习近平新时代中国特色社会主义思想的方法论和世界观)

必须坚持人民至上、坚持自信自立、坚持守正创新、坚持问题导向、坚持系统观念、坚持胸怀天下

新时代新征程中国共产党的历史任务

中心任务

团结带领全国各族人民全面建成社会主义现代化强国、实现第二个百年奋斗目标中国式现代化全面推进中华民族伟大复兴

中国式现代化

是中国共产党领导的社会主义现代化
是人口规模巨大的现代化
是全体人民共同富裕的现代化
是物质文明和精神文明相协调的现代化
是人与自然和谐共生的现代化
是走和平发展道路的现代化

本质要求

坚持中国共产党领导
坚持中国特色社会主义
实现高质量发展,发展全过程人民民主
丰富人民精神世界,实现全体人民共同富裕
促进人与自然和谐共生,
推进构建人类命运共同体,构造人类文明形形态

全面建成社会主义现代化强国

重大原则

坚持和加强党的全面领导
坚持中国特色社会主义道路
坚持以人民为中心的发展思想
坚持深化改革开放
坚持发扬斗争精神

加快构建新发展格局,着力推动高质量发展

高质量发展

是全面建设社会主义现代化国家的首要任务
没有坚实的物质技术基础,就不能全面建成社会主义现代化强国

构建高水平社会主义市场经济体制

建设现代化产业体系
全面推进乡村振兴
促进区域协调发展
推进高水平对外开放

实施科教兴国战略,强化现代化建设人才支撑

教育、科技、人才是全面建设社会主义现代化国家的基础性、战略性支撑
必须坚持科技是第一生产力、人才是第一资源、创新是第一动力,深入实施科教兴国、人才强国、创新驱动发展战略,开辟发展新领域新赛道,不断塑造发展新动能新优势

发展全过程人民民主,保障人民当家做主

人民民主是社会主义的生命、是全面建设社会主义现代化国家的应有之义

积极发展基层民主

人民当家做主最有效、最广泛的途径

坚持全面依法治国,推进法治中国建设

全面依法治国[多]

关系党执政兴国
关系人民幸福安康
关系党和国家长治久安

增进民生福祉,提高人民生活品质

为民造福是立党为公、执政为民的本质要求

==青年强,则国家强【截图】==

==材料分析题整理==


政府工作报告[2023.4.3]

2022年

全国国内生产总值增长3% GDP
城镇新增就业1206万人
年末城镇调查失业率降到5.5%
居民消费价格上涨2% CPI
粮食产量1.37万亿斤,增产74亿斤

确保粮食安全的底线

国内生产总值121万亿元 (跨越了121万亿人民币 18万亿美元)
五年来经济增长5.2% 十年平均增长6.2%

十年来取得的成就(五位一体+四个全面)

2023年

预计GDP增长5%
预计CPI居民消费价格上涨2%
粮食产量保持在1.37万亿斤以上

23年经济工作的总基调

稳字当头,稳中求进

党建工作的总基调

全面从严治党

积极的财政政策要加力提效
稳健的货币政策要精准有力
产业政策要发展和安全并举
科技政策要聚焦自立自强
社会政策要兜牢民生底线

高频词语

人类命运共同体的提出[10周年]

2013年3月23日
构建人类命运共同体,核心就是建设==持久和平[政治]、普遍安全[安全]、共同繁荣[经济]、开放包容[文化]、清洁美丽[生态]==的世界。**[平安繁荣节 -> 平,安,繁,容,洁]**

构建人类命运共同体的价值支撑

全人类共同价值:和平、发展、公平、正义、民主、自由

构建人类命运共同体的目标

建设持久和平、普遍安全、共同繁荣、开放包容、清洁美丽的世界

如何推动构建人类命运共同体

和平问题为主体提出了以下5个内涵
(1) ==政治上,要相互尊重、平等协商,坚决摒弃冷战思维和强权政治==,走对话而不对抗、结伴而不结盟的国与国交往新路。
(2) ==安全相处之道,要坚持以对话方法解决争端、以协商方法化解分歧==,统筹应对传统和非传统安全威胁,反对一切形式的恐怖主义。
(3) ==经济怎么做,要同舟共济,促进贸易和投资自由化便利化、推动经济全球化朝着更加开放、包容、普惠、平衡、共赢的方向发展。==[以世界贸易组织促进]
(4) ==文化上,要尊重世界文明多样性,促进文明交流、加强文明互鉴实现文明共存==。
(5) ==生态上,要坚持环境友好,合作应对气候变化,保护好人类赖以生存的地球家园==。

构建人类命运共同体的中国行动 (推动新型国际关系)

坚定奉行独立自主的和平外交政策

构建人类命运共同体的生动实践

[丝绸之路经济带、21世纪海上丝绸之路]

“一带一路”:倡议:从愿景
原则:共商共建共享
理念:丝绸之路的精神:和平合作 开放包容 互学互鉴 合作共赢
努力实现政策沟通、设施联通、贸易畅通、资金融通、民心相通;这五通->全方位推动务实合作,打造政治互信、经济融合、文化包容利益共同体、责任共同体和命运共同体

乡村振兴

总目标:农业农村的现代化
总方针:坚持农业农村优先发展[]
总要求:产出兴旺,生态宜居、乡风文明、治理有效、生活富裕
制度保障:建立健全城乡融合发展体制机制和政策体制

乡村振兴战略时间表

第二步 到2035年农村振兴取得决定性进展,农业农村现代化基本实现
第三步 到2050年农村全面振兴、国家强,农村兴

乡村振兴

产业、组织、文化、生态、人才振兴

==推进乡村振兴守住两条底线==

牢牢守住保障国家粮食安全底线
牢牢守住不发生规模性返贫底线

乡村振兴如何做

坚持农业农村优先发展
加快建设农业强国
全方位夯实粮食安全根基
树立大食物观
发展农村特色产业
巩固拓展脱贫攻坚成果
统筹乡村基础设施和公共服务布局
巩固和完善农村基本经济制度
深化农村土地制度改革
保障进城落户农民合法土地权益
完善农业支持保障制度

高质量发展

==为什么推动高质量发展?==[新发展阶段 新发展理念 新发展格局 高质量发展(三新一高)]
保持经济持续健康发展的必然要求
适应我国社会主要矛盾变化全面建成小康社会全面建设社会主义现代化国家的必然要求
遵循经济规律发展的必然要求

我们要实现怎样的高质量发展

创新 协调 绿色 开放 共享(新发展理念)

推动高质量发展的五个着力点

构建高水平社会主义市场经济体制
建设现代化产业体系
全面推进乡村振兴
促进区域协调发展
推进高水平对外开放

美丽中国

为什么要重视生态文明建设?

在”五位一体”总体布局中,生态文明建设
在新发展理念中,绿色是其中一项
在三大攻坚战中,污染防治是其中一战

生态文明建设要遵循原则 => 习近平生态文明思想主要内容

两山理论核心内容:经济发展与保护环境的关系

美丽中国建设评估指标体系

空气清新、水体洁净、土壤安全、人居整洁、生态良好

美丽中国建设行动指南

加快发展方式绿色转向
深入推进环境污染防治
提升生态系统多样性、稳定性、持续性
积极稳妥推进碳达峰碳中和

可持续发展道路是一条生存发展、生活富裕、生态良好的文明发展道路

====

==导入二十大报告==

党的创新理论武装全党是党的思想建设的根本任务
党的思想建设的首要任务是:坚定理想信念
为民造福是立党为公、执政为民的本质要求
腐败是危害党的生命力和战斗力的最大毒瘤,反腐败是最彻底的自我革命
高质量发展:是全面建设社会主义现代化国家的首要任务,要坚持以推动高质量发展为主体。
推动经济社会发展绿色化、低碳化实现高质量发展的关键环节
人民民主是社会主义的生命,是全面建设社会主义现代化国家的应有之义
全过程人民民主是社会主义民主政治的本质属性,是最广泛,最真实,最管用的民主。
协商民主实践全过程人民民主的重要形式基层民主全过程人民民主的重要体现
法律政府建设是全面依法治国的重点任务和主体工程
公正司法是维护社会公平正义的最后一道防线
法治社会是构建法治国家的基础
意识形态工作是为国家立心、为民族立魂的工作
我们要坚持马克思主义在意识形态领域指导地位的根本制度

====

全过程民主

根本保证:中国共产党的领导
制度载体:人民代表大会制度
重要形式:协商民主
重要体现:基层民主

最广泛[人人享有] 最真实[国家治理高效] 最有用[社会和谐稳定] 全过程人民民主

怎样判断一个国家是不是民主

人民民主有没有投票权:要看人民有没有广泛参与权
要看权利运行规则和程序是否民主,更要看权利是否真正受到人民监督和制约

怎样发展全过程人民民主

加强人民当家做主
全面发展协商民主
积极发展基层民主
巩固和发展最广泛的爱过统一战线

人民当家做主的制度

人民代表大会制度 根本政治制度
社会主义制度 民族区域自治制度 基本制度×3

新发展格局

为什么要构建新发展格局 p15

构建…做到什么?

高水平自立自强 是新发展格局最本质特征
经济循环畅通无阻 是新发展格局的关键
解放和发展生产力 是新发展格局的重点

==★★★★★ 中国式现代化!!==

要什么样的现代化?怎样实现现代化?

要坚守人民至上理念,突出现代化方向的人民性
要秉承独立自主原则,探索现代化道路的多样性
要树立守正创新意识,保持现代化程序的持续性
要保持奋发有为姿态,确保现代化领导的坚定性

贡献

为人类社会现代化理论和实践创新出新的贡献
促进人类文明的进步

文明倡导什么样的现代化文明?

共同倡导尊重世界文化多样性
共同倡导弘扬全人类共同价值,和平、发展、公平、正义、民主、自由
重视文明传承和创新
加强国际人文交流合作

深化党中央机构改革

组建中央金融委员会
组建中央科技委员会
组建中央社会工作部

深化国务院机构改革

重新组建科学技术部

新成立:国家金融监督管理总局

时事热点

奋进新时代提出

为实现中华民族伟大复兴提供了更为完善的制度保证、更为坚实的物质基础、更为主动的精神力量

习近平新疆考察:以民族平等、民族团结、民族区域自治、各民族共同繁荣发展为主体内容的民族理论和民族政策

==北京东奥精神:胸怀大局、自信开放、迎难而上、追求卓越、共创未来==

习近平在陕西延安和河南安阳考察

习近平在当二十大后首次国内考察,考察全面乡村振兴内容;延安和建党精神(红旗渠)

中华优秀传统文化是我们党创新理论的,我们推进马克思主义中国化时代化的途径是两个相结合

党的七大在党的历史上具有重要里程碑意义,标志着我们党在政治上思想上组织上走向了成熟

国内考察最重要的是==延安精神==:正确的政治方向、解放思想实事求是的思想路线、全心全意为人民服务的根本宗旨、自力更新艰苦奋斗的创业精神

国外考察最重要的是访问俄罗斯二十大的第一次出访:==友谊之旅、合作之旅、和平之旅==

共产党人的政治灵魂:对共产主义的信仰,对中国特色社会主义的信念。
在新时代坚定信仰信念,最重要的就是:坚定中国特色社会主义道路自信、理论自信、制度自信、文化自信。

我们党最鲜明的品格:勇于自我革命
党的政治建设的首要任务:保证全党服从中央,维护党中央权威和集中统一领导
中国共产党领导是中国特色社会主义最本质的特征,是中国特色社会主义的最大优势
中国共产党的精神之源:伟大建党精神。即坚持真理、坚守理念,践行初心,担当使命,不怕牺牲,英勇斗争,对党忠诚,不负人民

中央政治局体系学习

实现碳达峰碳中和是贯彻新发展理念。

中国共产党领导和我国社会主义制度,决定了我国人权事业的社会主义性质,决定了我们能够保证人民当家做主
人民性是中国人权发展道路最显著的特征。
生存权、发展权是首要的人权
生存是享有一切人权的基础,人民幸福生活是最大的人权
提高一体推进不敢腐、不能腐。不想腐能力和水平,全面打赢反腐败斗争攻坚战、持久战
始终坚持严的主基调不动摇
深入贯彻实施新时代强军战略:要把能打仗、大胜仗作为人才工作出发点和落脚点

乡村振兴与共同富裕

牢牢守住保障国家粮食安全和不发生规模性返贫的底线
总目标 总方针 总要求 制度保障

乡村建设是实施乡村振兴战略的重要任务,也是国家现代化建设的重点
image-20230405091456563

共同富裕是全体人民的共同富裕,是人民群众物质生活和精神生活都富裕
共同富裕是一个长远目标,不同人群不仅实现富裕的程度有高低
共同富裕是社会主义的本质要求
是中国式现代化的重要特征
实现共同富裕不仅是经济问题,而且是关系党的执政基础的重大政治问题
要坚持以人民为中心的发展思想,在高质量发展中促进共同富裕。适应我国社会主要矛盾变化,更好满足人民日益增长的美好生活须要

实现共同富裕要鼓励勤劳创新富裕;坚持基本经济制度;尽力而为量力而行;坚持循序渐进

重要重要文件合集

《关于加快建设全国统一大市场的意见》
建设全国统一大市场是构建新发展格局的基础支撑和内在要求
4个工作原则:立足内需,畅通循环;立破并举、完善制度、有效市场、有为政府、系统协同、稳妥推进

政治协商:人民政协政治协商

文化是国家和民族之魂,也是国家治理之魂
以推动文化高质量发展为主体,以深化文化领域供给侧结构性改革为主线

实现网络空间创新发展、安全有序、平等尊重、开发共享的目标
吧网络空间建设成为造福全人类的发展共同体、安全共同体、责任共同体、利益共同体

重大周年纪念

共青团:中国共产主义青年团
习近平给共青团提了4个希望:坚持为党育人;承担责任;心系广大青年;用于自我革命

坚持党的领导是共青团区别于其他青年组织的根本特质和鲜明优势
听党话、跟党走始终是共青团坚守的政治生命
坚定不移跟党走,为党和人民奋斗,是共青团的初心使命
要立足党的事业后续有人这一根本大计,牢牢把握培养社会主义建设者和接班人是根本任务

追求进步,是青年最宝贵的特质,也是党和人民最殷切的希望

白皮书
矢志不渝跟党走是中国青年百年奋斗的最宝贵经验
红色基因是中国青年百年奋斗的最宝贵财富

中央统一战线工作会议

统一战线的定位:统一战线是党克敌制胜、执政兴国的重要法宝,是团结海内外全】体中华儿女实现中华民族伟大复兴的重要法宝,必须长期坚持
决定党和人民事业成败的关键,是最大的政治
统战工作的本质要求:大团结大联合
统战工作的关键:坚持求同存异,发扬”团结-批评-团结”的优良传统
铸牢中华民族共同体意识为党的民族工作主线

庆祝中国人民解放军建军95周年系列活动

2022年7月27日,中央军委颁授**”八一勋章”**
杜富国、钱七虎、聂海胜获得八一勋章

一国两制与祖国统一

必须全面准确贯彻”一国两制”方针
坚持全面中央管制权和保障特别行政区高度自治权相统一
必须落实”爱国者治港”
必须保持相关的独特定位和优势

怎么做?提高治理水平、不断增强发展动能、切实排解民生忧难、共同维护和谐稳定,”一国两制”方针是一个完整体系
中央政府特别行政区拥有全面管制权,这是特别行政区高度自治源头

九二共识

推动两岸关系和平发展、推动祖国和平统一经常,是中华儿女的愿望1验证阶段圆满手工**
@@,是一国两制的根本主张
“台独”分裂行径是祖国统一的最大障碍,是民族复兴的严重隐患

一国两制是对马克思主义国家学说的创造性发展
一国两制的提出是为了实现和维护国家统一
永恒的主题、港澳立身之本

重大科技成就

神舟十三号(翟志刚、王亚平、叶光富)顺利返回,标志着我国空间站关键

神州十四号(陈东、刘洋)

image-20230405102159792

习近平线下参加第一次元首峰会;

巴厘岛峰会以“共同复苏、强劲主题”
元首外交中美关系”指南针”、”定盘量”对两国关系发展着补补课替代的部分
正确看待对方内外政策和战略意图是处理好彼此关系的一个重要前提
中美建交三点原则:相互尊重,和平共处,合作共赢

台湾问题是中国核心利益的核心,是中美关系政治基础中的基础,是中美关系第一条不可逾越的红线

image-20230405104946456

帽子题

社会主义民主政治的本质和核心:人民当家做主
社会主义民主政治最鲜明的特点:人民代表大会制度(人民当家做主的重要途径和实现形式)
集中全党全国人民集体智慧、实现科学决策、民主决策的基本原则和主要途径民主集中制
我国政治制度的一大优势,中国共产党领导的政党制度的基本特色:共产党领导,多党派合作;共产党执政,多党派参政
全过程人民民主制度载体:人民代表大会制度
人民当家做主最有效、最广泛的途径:基层群众自治
全过程人民民主的重要体现:基层群众自治
全面依法治国的总抓手:建设中国特色社会主义法治体系
中国特色社会主义法治体系的前提,法治国家法治政府法治社会的制度基础:完善的法律法规体系
建设中国特色社会主义法治体系的重点:高效的法治实施体系
宪法法律有效实施的重要保障,加强对权利运行制约和监督的迫切要求:严密的法治监督体系
依法治国的主体和力量源泉:人民
中国特色社会主义法治之魂:党的领导
治国之重器:法律
法治的龙头环节:立法
法治生命线,司法的灵魂,司法活动最高的价值追求:公正
社会主义法治的根本要求:党的领导
社会主义法治的基本属性,社会主义法治的基本要求:平等
治国理政的基本方式:法治
重要方式:德治
权利保障的前提和基础:宪法保障
保障权利保障的重要条件:立法保护
权利保障的关键环节:行政保护
权利保障的最后防线:司法保护
全面依法治国的根本目的:依法保障人民的权益
人享有其他各项权利的前提:生命权
人们一切行动和生活的前提条件:人身自由权
人生观的核心:人生目的
人的本质:一切社会关系的总和
世界观决定人生观、价值观
个人与社会的关系最根本的是个人利益与社会利益的关系
代表人类社会迄今最先进的人生追求:服务人民、奉献社会
评价人生价值的根本尺度:看一个人的实践活动是否符合社会发展的客观规律,是否促进历史进步
人民的精神世界的核心——精神之钙:理想信念
兴国之魂:中国精神
检验一个人对祖国忠诚程度的试金石:对骨肉同胞的爱
在当代中国爱国主义本质:爱党爱国爱社会主义高度统一
新时代爱国主义主题:实现中华民族伟大复兴
新时代爱国主义着力点:维护祖国统一
文化软实力的竞争本质上是不同文化所代表的核心价值观的竞争
文化软实力的灵魂:核心价值观
社会主义核心价值观历史底蕴的集中体现:深深根植于中华优秀传统文化
中华文化的精髓:中华传统美德
革命道德的灵魂:坚持社会主义、共产主义理想信念和不屈不挠的精神
贯穿中国革命道德始终的一根红线:全心全意为人民服务
我国公民道德建设的重点:诚实守信
社会主义道德建设的落脚点:个人品德
志愿服务精神的精髓:奉献精神


1981年,十一届六中全会 《关于建国以来若干历史问题的决议》 实事求是,群众路线,独立自主
中华民族精神核心:爱国主义
独立自主:中华民族精神之魂、

==新民主主义==

中国革命的依据、基础、前提:认清国情—《中国革命中国共产党》
中国革命的首要问题:分清敌友
中国革命的根本任务:推开三座大山
帝国主义:首要对象,造成一切灾难的总根源
中国革命的中心问题:无产阶级领导权问题
新民主主义革命理论的核心问题:无产阶级领导权问题
新旧民主革命区别的根本标志:无产积极领导权问题
新旧民主革命的分水岭:五四革命 (时间跨度)
中国革命的基本问题:农民问题
中国革命的基本内容:土地革命
中国革命的实质:无产阶级领导的农民革命
新民主主义战争的实质:无产积极领导的农民战争
无产阶级领导权的关键:建立工农联盟的统一战线
中国革命的最基本动力:无产阶级
中国革命的主力军、同盟军:农民阶级
中国革命的同盟者:城市小资产阶级
中国革命的动力之一(两面性):民族资产阶级
无产积极领导权实现的关键:巩固工农联盟为基础的统一战线
无产阶级领导权的中心问题:农民问题
中国革命的特点和优点:武装斗争
中国革命急剧特色政策:保护民族工商业
具有双重革命性特征:没收官僚垄断资本归新民主主义国家所有
中国革命的战略阵地:农民革命根据地

社会主义改造理论

新民主主义社会经济领导地位:国营经济
新民主主义社会经济主体地位:个人(体)经济
农业的社会主义改造道路:走互助合作的道路
农业的社会主义改造原则:自愿互利、典型示范、国家帮助
农业的社会主义改造方针:积极引导、稳步前进
农业的社会主义改造步骤:循序渐进
资本主义工商业的改造方式:和平赎买
资本主义工商业过渡形式:由低到高的国家资本主义
一化三改(一体两翼):社会主义工业化(主体),对全体农业、手工业、资本主义工商业社会主义改造(两翼)

社会主义建设道路初步探索的理论成果

中国共产党探索社会主义建设的良好开端:《论十大关系》
马克思主义与中国实际第二次结合开始:《论十大关系》
《论十大关系》提出的基本方针:调动一切积极因素为社会主义事业服务
中国特色社会主义建设道路 -> 提出了有别于苏联的中国工业化道路
《关于正确处理人民内部矛盾》正确处理人民内部矛盾是明确提出了有别于苏联的中国工业化道路
工业化总方针:农业为基础工业为主导,农轻重为序
政治思想领域:团结批评团结
经济利益领域:统筹兼顾
八字方针
党群:民主集中制
中共八大会议后经济政策方针:即反保守又反冒进,在综合平衡中稳步前进
社会主义基本矛盾的性质:人民利益一致基础上的非对抗性矛盾——又相适应又相矛盾
解决社会主义基本矛盾的途径和方法:经过社会主义制度本身来解决
走中国工业化道路的根本原因:实现民族独立国家富强的内在要求和必要条件
一化三改的实质:将生产资料私有制转变公有制
第四章帽子 人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民人民

刘少奇 使社会主义经济既有计划性又有多样性和灵活性的主张,以及按经济办法管理经济的思想
陈云 要建立“适合于我国情况和人民需要的社会主义的市场”的思想
毛泽东 “两参一改三结合”
邓小平 关于整顿工业企业,改善和加强企业管理,实行职工代表大会制度等观点
毛泽东 毛泽东 提出在社会主义经济占优势的条件下“可以消灭了资本主义,又搞资本主义(发展经济)”。
朱德 朱德提出了要注意发展手工业和农业多种经营的思想
陈云 ==陈云提出了“三个主体,三个补充“的设想==,即在工商业经营方面,国家经济和集体经济是工商业的主体,一定数量的个体经济是国家经济和集体经济的补充;以计划生产为主体,以自由生产为补充;以国家市场为主体,以自由市场为补充

邓小平理论

十一届三中全会:伟大转折会议,开启了社会主义建设新时期
党的十二大第一次明确提出了:建设有中国特色的社会主义的基本命题
党的十三大:标志着邓小平理论轮廓形成
党的十三大:第一次系统论论述中国特色社会主义理论
92南巡讲话:标志着邓小平理论走向成熟
党的十四大:邓小平—改革开放和现代化建设总设计师
党的十五大:首次提出邓小平理论,邓小平理论写入党章
1999年,邓小平理论写入宪法
十一届六中全会《关于建国以来》
社会主义本质:解放生产力,发展生产力,消灭剥削,消除两极分化,最终达到共同富裕
社会主义本质中根本任务:解放生产力,发展生产力
社会主义本质中根本目标:最终达到共同富裕
社会主义本质中根本要求:消灭剥削,消除两极分化
社会主义初级阶段基本路线:党和国家生命线,人民幸福线
社会主义初级阶段基本路线的中心任务:经济建设
社会主义初级阶段基本路线的根本动力:坚持改革开放
社会主义初级阶段基本路线的根本立足点:自力更生艰苦创业
社会主义初级阶段基本路线的立国之本:四项基本原则
社会主义初级阶段基本路线的强国之路:改革开放
社会主义社会发展的直接动力:改革
第一次提出“三个代表”重要思想:江泽民在广东考察
第一次提出:建设怎么样的党,怎样建设党的问题:全国党校工作会议
“三个代表”重要思想历史起点、逻辑起点:四个如何认识
第一次全面论述三个代表重要思想的科学内涵、基本内容:建设80周年讲话
三个代表重要思想的关键:与时俱进
三个代表重要思想的核心:保持党的先进性
三个代表重要思想的本质:执政为民
三个代表重要思想写入党章:党的十六大
三个代表重要思想:立党之本、执政之基、力量之源
党执政兴国的第一要务:发展
首次提出发展观:胡锦涛在广东考察
党的十七大第一次写入党章
十八大也写入党章
首次提出科学发展观:十六届三中全会
科学发展观初步形成的标志:人口资源环境座谈会
科学发展观趋于成熟:党的十七大(写入党章)
科学发展第一要义:发展
科学发展观核心立场:以人为本
科学发展观基本要求:全面协调可持续
科学发展观根本方法:统筹兼顾
科学发展观回答的时代课题:要实现什么发展,怎样发展的问题
科学发展观精神实质:解放思想实事求是,与时俱进求真务实
中国特设社会主义理论体系:党十七大
党的先进性的集中体现,根本要求:必须代表先进生产力的发展要求、党代表先进生产力
先进生产力的集中体现,重要标志:科学技术
在改革中我们必须坚持的两大根本原则:以社会主义公有制经济体,共同富裕
我国经济体制改革的核心问题、关键:如何正确认识和处理(政府)与市场的关系
和平统一,一国两制的核心/发展两岸关系和实现和平统一的政治基础:坚持一个中国原则
习近平新时代中国特色社会主义思想的核心要义:坚持和发展中国社会主义
中国特色社会主义事业总布局:“五位一体”;战略布局:“四个全面”
全面社会改革总目标:完善和发展中国特色社会主义制度、推进
习思想的总任务:建成社会主义现代化强国 实现中华民族伟大复兴
全面深化改革总目标:完善和发展中国特色社会主义制度、推进国家治理能力治理体系现代化
全面推进依法治国的总目标:建设中国特色社会主义法治体系、建立社会主义法制国家
党在新时代的强军目标:建设一支听党指挥、能打胜仗、作风优良的人民军队,把人民军队建设成世界一流军队
中国特色社会主义最本质的特征:中国共产党的领导
中国特色社会主义制度的最大优势:中国共产党领导
习思想始终的一根红线:人民至上
共产党不可战胜的强大精神力量:敢于斗争,敢于胜利
中华民族的根与魂:优秀传统文化
新发展理念的根与魂:为人民谋幸福、为民族谋复兴
社会主义民主政治发展的必然要求,推进政治文明建设必须遵循的基本方针
我国社会主义政治文明区别于资本主义政治文明的本质特征:坚持党的领导、人民当家做主、依法治国有机统一
总体国家安全:人民安全是宗旨、政治安全是根本、经济安全是基础、军事,文化,社会安全是保障:国家利益至上是准则;国际安全是依托
引领发展的第一动力:创新
持续健康发展的内在要求:协调
永续发展的必要条件:绿色
国家繁荣发展的必由之路:开放
中国特色社会主义的本质要求:共享
新发展格局的关键:经济循环、畅通无阻
新发展格局最本质的特征:高水平的自立自强
现代化经济体系的坚实基础/着力点/国家经济的立身之本/财富创造的根本源泉:实体经济
现代化经济体系的战略支撑:创新驱动发展战略
发展是第一要务,人才是第一资源,创新是第一动力,科技是第一生产力
现代化经济体系的重要基础:乡村振兴战略
人民当家做主和依法治国的根本保证:党的领导
社会主义民主政治的本质特征:人民当家作主
党领导人民治理国家的基本方式/国家长久治安的重要保障:依法治国
[今年也是]深化党和国家机构改革,全面提高国家治理能力和治理水平:==以加强党的全面领导为统领,以国家治理体系和治理能力现代化为导向,以推进党和国家机构职能优化协同高效为着力点==
中国社会主义民主政治的特有形式:社会主义协商民主
社会主义民主最基本的体现,人民政协的主要职能:政治协商、民主监督、参政议政
民族区域自治的核心:保障少数民族当家做主,管理本民族本地区事物的权利
党的宗教工作的根本方向和目的:积极引导宗教与社会主义社会相适应
长期以来特别是党的十八大依赖推进”一国两只”的成功经验:把维护中央对香港澳门特别行政区全面管辖治权和保障特别行政区高度自治权有机结合起来,落实好中央依法行使权利和特别行政区旅行主体责任
香港澳门的立身之本,保护香港澳门国际地位和解决香港澳门各种问题的金钥匙:发展
两岸关系的政治基础:一国中国原则
两岸关系的关键:坚持”九二共识”
推动两岸关系和平发展的着眼点和落脚点:为两岸同胞谋福祉
一国国家重要的稳定器:核心价值观
人民幸福之基,社会和谐之本:民生
最大的民主:就业—民生工程,民心工程,根基工程
民生治本,改善民主、实现发展成果由人民共享最重要最直接的方式:收入分配
最基本的民生:公共安全
老百姓解决温饱后的第一需求,最基本的发展环境:平安
生态文明的核心:坚持人与自然和谐共生
人与自然相处首要态态度:尊重自然;基本原则:顺应自然;首要责任:保护自然
全面建成小康社会的首要战役:防范化解重大风险攻坚战
全面建成小康社会的底线任务:打赢脱贫攻坚战
决定当代中国命运的关键一招/关键抉择,当代中国发展进步的活力之源,党和人民事业大踏步赶上时代的重要法宝,坚持和发展中国特色社会主义实现中华民族伟大复兴的必由之路,当代中国最鲜明的特色;
中国共产党最鲜明的旗帜:改革开放
改革开放的初心和使命:为中国人民谋幸福,为中华民族谋复兴
推动人类社会向前发展的根本动力:变革创新
推动我国经济发展(国内):改革开放
解决中国现实问题的根本途径:全面深化改革
==全面深化改革的出发点和落脚点:促进社会公平,增进人民福祉
全面深化改革的根本目的:解放发展生产力,促进社会公平正义==
我国改革开放之所以取得巨大成就,关键在于把党的基本路线作为党和国家的生命线,始终坚持以经济建设为中心同四项基本原则、改革开放这两个基本点统一于中国特色社会主义伟大实践
改革成效的评价标准:是否促进经济社会发展,是否给人民群众带来时事政治的获得感
==国家繁荣富强的根本出路:全面开放
国家繁荣富强的必由之路:改革开放==
正确处理改革发展稳定的关系的结合点:改善人民生活
中国特色社会主义的本质要求和重要保障:全面依法治国
建立严密的法治监督体系的重点:规范和约束公权力
能不能做到依法治国,关键在于党:能否坚持依法执政、各级政府能不能依法行政
法治建设的目标:法制国家;法制国家的主题工程:法治政府;法制国家的基础:法治社会
新时代党的建设的目的,新时代党的建设的根本原则:坚持和加强党的全面领导
根本方针:坚持党要管党,全面从严治党
全民从严治党中,”全面”是基础,”严”是关键,”治”是要害重点是抓关键少数,核心:坚持和加强党的领导
政党第一属性:政治属性; 政治建设:根本性建设
政治建设的首要任务:保证全党服从中央,坚持党中央权威和集中统一领导
党的基础性建设:思想建设
党的思想建设首要任务:坚定理念信念
党的思想建设根本任务:用党的创新理论武装全党
作风建设的核心:保持党同人民群众的血肉联系
全面从严治党的治本之策:加强党的纪律建设
全面从严治党的根本之策/长远之策:加强党的制度建设
十九大 从严治党的重中之重:廉政建设和反腐败斗争
二十大 最彻底的自我革命:反腐败斗争
我党最鲜明的品格:勇于自我革命
跳出历史周期的答案:民主、人民监督、敢于自我革命
强军之魂、建军之本:党对军队的绝对领导
强军之要:能打仗、打胜仗
强军之策、兴国之举:军民融合发展
立军之本:政治建军
我军鲜明特点和政治优势:作风优良
人民军队完全区别与一切旧军队的政治特质和本质优势:党对军队的绝对领导—根本原则
我军的最大优势、最大特色,我军同一切其他性质军队的最大区别:政治工作
党对军队绝对领导的最高实现形式,确保国家长治久安的”定海神针”:军委主席负责制
基本军事制度:党对军队绝对领导
我军发展壮大、制胜未来的关键一招:改革
我党建军治军的基本方略:依法治军、从严治军
军队建设的出发点和落脚点:提高战斗力
人民军队的出发点和落脚点:全心全意为人民服务
建设世界一流军队的力量基础:构建中国特色现代军事力量体系
军队全面深化改革:军委管总、战区主战、军种主建;
实现富国强军统一的重要途径:走军民融合发展的路子
实现强国强军统一的重要政治保证,我当我军特有的政治优势:军政军民融合
军政军民团结优良传统:军爱民、民拥军
中国外交政策的宗旨:维护世界和平、促进共同发展
新型国际关系:特别是新在合作共赢
中国倡导建立相互尊重、公平正义、合作共赢的新型国际关系
新型国际关系核心:维护联合国宪章的宗旨和原则,维护不干涉别国内政和尊重国家主权、独立、领土完整等国际关系基本准则,维护联合国以及安理会对世界和平承担的首要责任,开展对话、合作而不是对抗,实现共赢、共赢而不是单赢
我国外交工作的基本出发点和落脚点:坚决维护国家主权、安全、发展利益
外交工作布局:大国是关键,周边是首要,发展中国家是基础,多边是舞台
周边外交的方针:与邻为善、与临为伴
周边外交理念:亲诚惠容
全球治理观/一带一路的原则:工商、共建、共享;
基层治理的新格局:共建、共治、共享
“一带一路”的宗旨/理念/丝绸之路精神:和平合作、开放包容、互学互鉴、互利共赢
“一带一路”的内容:政策沟通、设施联通、贸易畅通、资金融通、民心相通:责任共同体、利益共同体、命运共同体
人类命运共同体思想核心:持久和平、普遍安全、共同繁荣、开放包容、清洁美丽的世界
党和国家根本所在命脉所在,全国各族人民利益所系幸福所系:坚持党的领导
中国特色社会主义最本质的特征。中国特色社会主义制度最大优势,发挥中国特色社会主义制度优势的根本保障:党的领导
中国特色社会主义制度优势之源:党的自身优势
改革开放以来党和人民历经千辛万苦、付出巨大代价取得的根本成就:中国特色社会主义
中国共产党的历史使命:”四个伟大”:伟大梦想是目标指引前进方向;伟大斗争是手段激发前进动力;伟大工程是保障提供前进保证;伟大事业中国特色社会主义事业是主题开辟前进道路;决定作用——党的建设伟大工程
开辟中国特色社会主义道路、开创中国特色社会主义新理论的宣言书:《解放思想、实事求是、团结一致向前看》
深化党和国家机构改革的重要任务:转变政府职能,优化政府机构设置和职能配置
新时代”三农”工作的总抓手:实施乡村振兴战略
乡村振兴战略总目标:推进农业农村现代化
十四五规划中”三农”工作摆首要位置:巩固脱贫攻坚成果,防止大规模返贫
乡村振兴战略的总要求:产业兴旺、生态宜居、乡风文明、治理有效、生活富裕
乡村振兴战略的总方针:坚持农业农村优先发展
农村振兴战略的制度保障:
十四五发展规划中经济工作的主题:高质量发展
实现经济高质量发展的主线:供给侧结构性改革
供给侧结构性改革的重点:解放和发展生产力
供给侧结#构性改革的关键:理解结构性


材料分析题技巧

原因题 理解题 措施题
提问线索 为什么/必要性/依据 如何理解/认识/看待 如何弘扬/践行/做到
审读材料 阅读材料,确定考察主体(关键词)
说明”关键词”定义/内涵
从各领域强调”关键词”重要性 如:
中华民族伟大复兴
生态文明建设
法制国家建设……
将问题进行句式分解:
1.分别解释句中的考点概念、内涵
2.结合材料说明概念意义、作用或者联系与区别。做出总结
#核心:找出关键词关键词与关键词之间的关系
阅读材料,总结材料体现措施;联系考点理论思考解决问题措施;
按照题目要求,从:个人、家庭、社会、国家等角度谈谈如何采取措施;最后强调要落实
答题模板 从各领域强调”关键词”重要性 如:
中华民族伟大复兴、生态文明建设、法制国家建设等等
1.结合材料说明概念A和概念B的含义
2.结合材料,解读概念A与B的重要性,联系和区别
3.结合材料强调结论
1.作为大学生(个人)应该怎么做(从材料中启发)
2.国家/社会/政府应该怎样(弘扬美德, 完善法律)以便营造材料
证明题 概念题 原因题
提问线索 结合材料,说明理想信念的力量 本质是什么,含义是什么 为什么/必要性/依据
审读材料 理想信念的力量是教材的知识点,可以直接背+材料对应内容 对材料进行归纳总结 阅读材料,确定考察主体(关键字),寻找主观依据
结合材料及社会现实,寻找客观依据
从以往角度分析其历史原因
从解决问题的现实问题解答现实原因
答题模板 考核要点:考核知识点+材料
结合材料强调结论(改问句为肯定)
考察的就是我们归纳总结能力 主观原因:知识点+材料
客观原因:知识点+材料
历史原因:联想+材料
结合材料强调结论(改问句为肯定)

==面对 理想与现实 当代青年怎么做?【万能句】==

学习上
刻苦钻研、不畏艰难、孜孜不倦地学习理论和专业知识,不断提高思想道德和专业知识水平

生活上
艰苦朴素、勤俭节约,抵制和反对铺张奢华的思想和生活作风 奢靡

工作上
奋发图强、不怕困难、不避艰难,努力完成各项任务

① 如何理解”给人温暖就是给自己幸福”?把关键词解释一下、放回原文解释一下

人生价值包括:人的自我价值与社会价值
分别解释了…叫做人的自我价值…
人的自我价值与社会价值之间的关系

② 怎样理解诚信及其道德力量?[与诚信相关的所有知识点扩展…]

诚信是传统美德 职业道德
社会主义核心价值观
诚信:诚实守信
诚信是讲信誉守规则

解释A与B;论证A与B的关系;结合材料强调结论

写一条 用一点材料放里面,在哪个地方体现了职业道德规范…

一定要分点写

③ 如何理解全面依法治国是建设社会主义国家的应有之义?

全面依法治国的意义

全面依法治国是坚持和发展中国特色社会主义的本质要求和重要保障
全面依法治国是实现国家治理体系和治理能力现代化的必然要求
全面依法治国事关我们党执政兴国,事关人民幸福安康,事关党和国家长治久安。
全面依法治国也是实现中华民族伟大复兴中国梦的必然要求

④ 怎样看待”理想很丰满,现实很骨感”这种说法?

问态度,先表态 => 想法比较片面
==1.辩证看待理想与现实的矛盾。理想与现实是对立统一的。==
2.实现理想的长期性、艰巨性和曲折性
理想受现实的规定和制约,是在对现实认识的基础上发展起来的
==3.艰苦奋斗是实现理想的重要条件。==
理想与现实的关系:理想高于现实,是现实的升华,理想来源于现实,是对现实的反应,理想可以转化为现实。
理想和现实存在着对立的一面,二者的矛盾与冲突,属于==“应然”“实然”==的矛盾。

⑤ 我们应如何从自身做起,构建文明的公共生活秩序?

公共秩序、生活秩序 → 道德素质、法治素养
增强自身的社会公德意识和法律意识
学习和把握公共生活中的道德与法律规范,提升自身文明素质
养成遵守社会公德和遵纪守法的良好行为习惯
从身边小事做起,落实落细自己的道德实践经验和法律实践

⑥ 如何通过好家风的传承弘扬社会主义核心价值观?

与社会 与社会主义核心价值观的关系 核心价值观是社会主流价值观
说明家风是形成社会主流价值的内容
个人:传承弘扬好的家风;树立人生正确的三观
国家/社会:营造良好的社会氛围、加大宣传

⑦ 如何理解”伟大革命精神跨越时空、永不过时”? 怎样”把红色基因传承好,确保红色江山永不变色”?
⑧ 为什么文明出行”既是道德的呼唤,也是法律的要求?”

道德要求 法律要求
文明出行和道德、法律如何产生的关系呢?
文明出行是公共生活的一部分,是公共秩序的一部分 => 要道德 要法律
维护社会秩序的两种基本手段:道德和法律。公共生活中的道德和法律所追求的目标是一致的,都是通过规范人们的做法来维护公共生活中的秩序,实现经济社会的稳定和发展。
法律支撑和保障、为它所指引的!

措施题:

==当代大学生怎么做==:结合材料

1.提高相关意识
2.加强学习
3.养成习惯
4.养成担当责任
5.从自身做起,积极参加社会活动

==全社会该怎么做的问题==:结合材料

1.个人传承弘扬好的做法
2.家庭应该怎么做
3.社会营造氛围,加大宣传
4.国家完善法律法规,积极引导,典型示范。


红色文化

红色资源

是中国共产党领导人民进行革命、建设、改革实践中形成和凝聚的物质、制度、精神载体。红色资源是宝贵的革命历史文化遗产,是党史学习教育鲜活生动的教科书

红色基因

对于共产党人的人生而言,红色基因是信仰,目光远大,追求高远;红色基因是忠诚,爱党爱国,矢志不渝;红色基因是追求,勇于拼搏,自强不息;红色基因是忘我,无私奉献、无怨无悔。这基因让青春常驻,让生命之花绽放

红色基因内容

@@ “三个代表”重要思想是中国特色社会主义划时代的标志
@@ 科学发展观第一要义发展

阅读全文

(铭升)菲儿精华

2023/3/8

名词

主要考法:词义辨析
次要考法:语法

1. ‘s和of的所属关系

‘s 所有格主要用于表示有生命的人或动物 名词的所属关系(the boy’s mother,Jack’s new car) ,
of所有格主要用于表示无生命的名词的所属关系(the colour of the car, the price of honour)。
有of 考白字的(中英文擦边球) 漂亮 没有所属关系时不用的 或者做状语!

2. 名词作定语时要用单数,但man、woman除外

==名词做定语时,确实一般都是单数的==,因为这个时候的名词相当于形容词,而形容词是没有复数形式的。 所以我们看到了an apple tree而不是an apples tree,a department store而不是a departments store(虽然一个百货商店会有很多个departments)

充当定语的名词应始终前置

牢记这一点将有助于你理解名词充当定语时所组成短语的含义,例如:

race horse 比赛用马
horse race 赛马比赛
boat race 划船比赛
love story 爱情故事
war story 战争故事
tennis ball 网球
tennis shoes 网球鞋
bicycle shop 自行车商店
computer exhibition 电脑展

3. 单复同形常考词 (单独使用时候是单数)

fish、deer、sheep、Chinese、corps、counsel、Japanese、means、series、news、species、works

4. 只能复数格式的常考词

noodles、vegetables、snacks、thanks、spirits、sports、celebrations、congratulations(congratulation on 恭喜某人…)

5. A PAIR OF 成双成对 (几套几把 按套/把 来定)

A pair of 在英语需要成双成对的表达: a pair of glasses (一副眼镜) a pair of scissors (一把剪刀) a pair of spectacles (一副眼镜) a pair of trousers (一条裤子) a pair of pants (一条裤子) a pair of pliers(一把剪钳)

6. 专有名词,the United States[书名、电影名、故事、人、事…]

一般来讲,专有名词前面不用定冠词the,但江河海洋,山脉群岛地理名称前要用定冠词
例如:the Yangtze River长江两个以上的普通名词组成的专有名词前,一般要用定冠词。
谓语动词的原型是复数 谓语动词加s 是单数 was

7. 所属的共同拥有和独自拥有

如果两人共有,就是Tom and Peter’s room,在后面的人名后面加’s,room为单数。
如果指两人各自拥有的,就是Tom’s and Peter’s rooms,在两个人的人名后面都加’s,room为复数。
表示有生命的东西的名词及某些表示时间、距离、星球、世界、国家等无生命的东西的名词后加’s来表示所有关系,叫做名词所有格。
有the的不能用双数所有格 of前面是the后面不用双重所有格

8. The后不用双重所有格,其余后可以

双重所有格,也就是将 ‘s 所有格 与 of所有格 结合在一起了;
我们来举个栗子:
你要开家长会了,你爸却没空。但是你爸有很多朋友,并且your father’s friends are all very kind, 他们都非常友好。于是你爸就叫他一个朋友去家长会了。第二天老师问你,谁来参加你的家长会呀?你怎么回答?回答:我爸一朋友~ a friend of my father’s friends,我爸众多朋友中的一个来开~但是这里有个单词friend重复了,读上去也很拗口,怎么搞呢?为了避免名词的重复,我用双重所有格:变成a friend of my father’s. 省略掉friends。
a friend of my father,含糊不清,a friend也许是你爸最好的朋友,也许是你爸唯一的朋友,也许是你爸朋友中的一个。而a friend of my father’s 双重所有格,表达的就是,我爸朋友们中的一个。

冠词

判断方法:答案都是冠词
考点:

1. 特指和泛指(那个,一个[翻译])

不定冠词(a/an)表示泛指任何一,每一 a different kind表示泛指
定冠词(the)表示特指、专指、类指;独一无二的;表示人的某个器官手上用the
零冠词表示泛指人或事物、类指。

knowledge不可数名词 前面不能加不定冠词(a/an)

in public 在公众场合
the public 公众
in use 在用着
单数可数名词表示泛指时,前面要用不定冠词a(an),
表示特指时,前面要用定冠词the;
不可数名词前不能用a(an)修饰,
He is a factory worker. 他是一名工人。
No one can see air. 没有人能看见空气。

2. 固定搭配

80%正确方式
介词+名词 (一般名词前面没有冠词)
burst into laught 名词在介词之后 介词和名词之间没有冠词

动词+名词 (因词而异)
名词翻译为 一个、一项、一种 => 不定冠词

3. 抽象概念具体化

抽象名词就是表示状态,品质,情感没有实物的名词。比如responsibility(责任), law(法律),love(爱情),peace(和平);具体化就是前面可加冠词(a/an),变成可数名词。

一般说来抽象名词为不可数名词,但当抽象名词表示具体的东西时,可用作可数名词且词义发生变化,叫抽象名词具体化。主要类型如下:
① 抽象名词表示具有某种特性、状态、感情情绪的人或事。如:
抽象名词(不可数) 具体化(个体名词,可数名词)
in surprise惊讶地 a surprise一件令人惊讶的事
win success获得成功 a success一个(件)成功的人(事)
win honor赢得荣誉 an honor一个(件)引以为荣的(事)
Failure(失败)is the mother of success a failure失败者

4. 发音 s,l,m

冠词确定a/an 后面的元音/辅音字母
张嘴的一刻 牙齿嘴巴舌头没有任何的阻止和碰触 都是元音

代词

特点:答案均为代词答案
考点:

1. every≥3, each>2, both2, all=3, neither=2, either=2 混考

every 每个加单数名词 [后面不能直接跟of] 强调整体
each 每个加名词单数 强调个体
both 两者都 加复数名词
all 三者以上的都加复数名词
neither 两者都不 加单数名词
either 两者中任意一个加单数名词
any 许多加复数名词

2. someone, anyone, everyone, every one, no one, none 混考

前三个在含义上

  • someone专用来特指某人的;
    anyone是指任何人,并不是特指某一个人;
    everyone是指每个人。

前三个在用法上

  • someone通常用来表示有人,某人。
    anyone是通常来表示无论谁,并没有限定范围的,不了解所描述的范围。
    everyone是通常表示人人

no one = nobody, 语气比none强,后面不能接of构成的短语。none(人、物)可以接of构成的短语
someone, anyone和 everyone分别相当于 somebody,anybody和 everybody这些代词只能指人,后面不跟介词 of构成的短语;no one后可跟 of 短语,但只能说 no one of sb.,不能说 no one of sth.

none和 some one, every one, any one可以指人,也可以指物,根据后面介词 of的宾语或上下文而定

none与no one的区别
none既可以指人也可以指物,no one=nobody,只能指人
none强调数量,no one不强调数量
none作主语时,谓语动词用单数或复数均可,no one作主语时,谓语动词只能用单数

every oneeveryone的区别
everyone 只能用来指人,其意思相当于 everybody,在它后面不能跟介词of
every one是两个词,既可用来指人,也可用来指物,等于each one,后面可跟介词of

3. Other, the other, others, the others, another 混考

有s的后面不再跟s 没s的后面一般要跟名词
范围能数的清楚的加the 数不清的不加the
the other 如果范围 一方 另一方
喝了一瓶再拿一瓶 one and take other one

  • other表示“别的,其它的”,它不能单独使用,后面必须要接可数名词的复数形式
    如:
    He studies English, math and some other subjects. 他学习英语、数学和一些别的科目。
    这句话中,就算前面没有some,other后面也只能用复数形式。

  • the other多指“两者中的另一个”,也可以指三者及以上中剩下的最后一个。可以独立使用,也可以在后面接上名词。
    如:
    (1)One of the twins likes apples, the other likes oranges. 这对双胞胎一个喜欢苹果,另一个喜欢桔子。 双胞胎是两个人,所以这是表示两者中的另一个。
    (2)Four of the five boys are good at English, but the other isn’t. 这五个男孩中,有四个人英语都学得好,但另外那一个学得不好。本句就不止两个人,但只要是剩下的一个,都使用the other. 当然,我们也可以在the other后面加上名词boy.

  • others 表复数,泛指“别人;别的东西”,它就相当于other+名词复数,因此它的后面不能再接名词。如果句中前面出现了some,后面通常就会用others,即构成“Some…others…”的结构, 这时也可以翻译成“有的……有的……”。
    如:
    (1)Some students are dancing, others are singing. 一些学生在跳舞,其他的在唱歌。
    这句话也可以翻译成“学生们有的在跳舞,有的在唱歌”。Others就相当于other students.
    (2)Tom likes helping others. 汤姆喜欢帮助别人。

  • the others 也表复数, 特指“别人;别的东西”,它相当于the other+名词复数,它后面不接名词
    Others是泛指,而the others是特指,怎么理解呢?如果排除一个不确定的数量,剩下的就用others,如上面的some students, some就是一个不确定的数量,所以剩下的数量也就不确定了,于是就用others来泛指。如果排除一个确定的数量,剩下的就应该用the others来特指。如:
    Five students are dancing, the others are singing. 有五个学生在跳舞,其他的在唱歌。
    本句中排除的学生是5个,这是一个确定的数量,于是后面也就用the others 来特指。当然,the others 也可以写成the other students.

  • another表三者及以上中的“再一,另一”个,后接单数名词。这个单词本来就是“an other”两个单词的连写,所以它的前面不可能再用上冠词an.
    如:
    (1)Have an another cup of coffee, please. 请再喝一杯咖啡。(错误,不能用an)
    (2)Have another cup of coffee, please.请再喝一杯咖啡。(正确)
    本句中如果用the other来代替another, 那意思就暗含这个主人一共只有两杯咖啡,客人喝了一杯,再请他喝另外的那一杯。意思当然就不对了

    another后还可以接“数词+名词复数”,这时是把“数词+名词复数作为一个整体来看待的
    如:
    She had to stay here for another ten days。她只好又在这儿呆了十天。
    本句的意思暗含她原来就在这里已经住了若干天了,现在不得不又住一个十天。

4. it[优先考虑], that, one, ones, those的辨析

few 后面可数
little 后面不可数
有a就有一点点
没a就一点也没有

  • it特指,代替前文提到过的事物。(同类同物)时间、地点、温度、想指代的都可以用it、形式主语、形式宾语、强调句
    如:He is eating a banana. It looks delicious.他正在吃香蕉,这香蕉看起来很美味。
    The baby is crying,it looks so sad.这个婴儿正在哭泣,它看起来如此伤心。

  • that特指,代替不可数名词单数名词,指物。同类不同一个时
    如:The weather in Wuhan is hotter than that of Dongguan.(that = the weather)武汉的天气比东莞的更热。

  • one泛指,代替单数名词,可指人或物。(同类异物) 泛指一个
    如:I don’t like this book,I’d like a more interesting one.(one=book)我不喜欢这本书,我想要一本更有趣的。
    I like the bananas,please give me one more.我喜欢香蕉,请再给我一个。
    Among all people here,Tom is the most humorous one.这里所有人中,汤姆是最幽默的一个。

  • ones泛指,代替复数名词,可指人或物
    如:There were a few young people with some old ones in the house.(ones=people)有几个年轻人和一些老年人在那座房子里。

  • those特指,代替复数名词。
    如:The machines are better than those we produced last year.(those=the machines=the ones)这些机器比我们去年生产的要好。

5. It的特殊用法
  • 作为代词

  • t作为形式主语

    S+V+OS+V+P句型中,如果我们将名词从句置于主语位,则比较容易出现句子“头重脚轻”的情况,例如:

    That we stay away from the crowded city after feeling tired is a good idea.
    (在感到疲倦后远离拥挤的城市是一个好主意。)

    在上例中,我们须读到句尾“is a good idea”才能够弄清楚这是一个S+V+P的结构,而且如果主语更长的话,理解难度将会呈倍数增长。

    语言追求的是简洁高效的表达,上句的结构显然与这种初衷相违背,形式主语it就是用于解决这一问题的:

    It is a good idea that we stay away from the crowded city after feeling tired.

    将位于主语的名词从句置于句尾,再将形式主语it置于主语位置,这样名词从句就与句子的其它成分隔离开了

    此时我们只需读到“It is a good idea”就能明白这是一个S+V+P句型,整个句子的结构变得更加清晰。

  • it作为形式宾语

    S+V+O+C句型中,我们会用到形式宾语it。

    形式宾语的用法和形式主语类似,例如:

    We consider that we sign a contract in a few days desirable.
    (我们认为在几天内签个合同是有必要的。)

    因为S+V+O+C句型中的宾语是夹在谓语和补语之间,如果处于宾语位置的名词从句过长,读者就很难在短时间内搞懂句子的结构。

    所以,我们可以把形式宾语it置于宾语位置,并将事实上的宾语置于句尾
    We consider it desirable that we sign a contract in a few days.

  • “dummy it”

    这种情况下的it没有任何词义,只是在句中起到填补主语位置的作用,例如:

    It is dark outside.
    (外面天黑了。)

    It seems that he has been there for years.
    (似乎他已经在那里很多年了。)

  • it用于构成强调句

    it的另一种用法是构成强调句,强调句的常见形式为:

    It + be动词 + 强调部分 + that ….

    如果我们分别强调上句的①、②、③部分,可以得到:

    Tom opened the box with a knife

    It was Tom that opened the box with a knife.
    (用小刀打开了那个盒子的是Tom。)

    It was the box that Tom opened with a knife.
    (Tom用小刀打开的是那个盒子。)

    It was with a knife that Tom opened the box.
    (Tom打开那个盒子是用的一把小刀。)

形容词和副词

特点:答案为形容词或副词
考点:

1. 词义辨析
2. 形容词的比较级(as原形good/wellas…, more than)

times 只能三倍以上
twice, double 可以两倍
倍数 + 长度宽度尺码… + of =比较级
同级对比 北京比上海 头发和头发比
more than that of(同级对比)

英语中的形容词通常用三种形式来表达事物的等级差别,分别是原级、比较级和最高级。大部分形容词的比较级和最高级是通过变化词尾来实现的,属于规则变化,但也有少数是不规则变化。
英语中有些形容词说明形状、材质等,还有形容词没有程度可分其本身就表示某种程度,故而没有比较级和最高级

more than

人口是大小 不是多少 population as large as

  • 表示“比……更多”

此时more为many或much的比较级,表示数量,后接名词。
I made more mistakes than you.
我犯的错误比你多。

Last year there were more births than deaths.
去年的出生人数多于死亡人数。

若more受much或many的修饰,则应分别与不可数和可数名词连用。

His car cost much more money than mine.
他的小汽车所花的钱比我的多得多。

There are many more people than we expected.
比我们想象的人要多得多。

  • 表示“比…更”

此时more后接多音节形容词或副词,构成比较级,表示对两者进行比较。

He is more careful than the others.
他比其他人更仔细。

Travelling by train is more relaxing than driving.
乘火车旅行比开汽车轻松得多。

This company is more concerned with quality than with quantity.
这家公司对质量比对产量更关心。

  • 表示“与其说……不如说”

此时不是对两个对象进行比较,而是对同一个人或物在两个不同方面进行比较或取舍,此时不论形容词或副词是单音节、双音节还是多音节,一律用more…than…。

He is more lucky than clever.
与其说他聪明,不如说他幸运。

He was more frightened than hurt.
他伤倒不算什么,可受惊不小。

It is more grey than brown.
与其说它是棕色的,倒不如说是灰色的。

He is more (a) scholar than (a) teacher.
与其说他是位教师,不如说是位学者。 

(1) 异类同质比较:即指两个不同的人或事物(异类)在同一方面(同质)进行比较。

He is stronger than me.
他比我强壮。  

Our country is more powerful than theirs.
我们的国家比他们的国家更强大。

(2) 同类异质比较:即指同一个人或事物(同类)在两个不同的方面(异质)进行比较(注意译文):

He is more brave than wise.
他有勇无谋。  

He’s more short than fat.
与其说他胖,不如说他矮。  

She is more lucky than clever.
与其说她聪明,不如说她幸运。  

as原形as…

  • 基本模式:as+形容词原级+as-分句

1.主句主语与as-分句主语不同,比较项目相同,此时as-分句有所省略。如:
Jack is as clever as Tom.

2.主句主语与as-分句主语相同,但比较项目不同,此时as-分句不可用省略句。如:
The girl is as brilliant as she is beautiful. (这个女孩既漂亮又聪明。)

3.主句主语与as-分句主语不同,比较项目也不同,此时as-分句不可用省略句。如:
Her uncle was as base and unworthy as her father had been upright and honorable.
(她叔叔卑鄙龌龊,不像她父亲那样正直可敬。)

4.基本模式的否定形式:
Jack is not as clever as Tom. 或 Jack is less clever than Tom.

  • 变体模式:

as many/much+名词+as-分句:

She has eaten as many apples as her sister.
He took as much water as he needed.

as+形容词原级+名词词组+as-分句:
Lucy is as clever a girl as her sister.

变体模式的否定形式:
He didn’t take as much water as he needed.

需要注意的是,同一句话,采取基本模式和变体模式,它们的含义基本相同,但句子的侧重点有所不同。例如:

基本模式:I haven’t seen a car as old as this for years.(重点在名词car)
变体模式:I haven’t seen as old a car as this for years.(重点在形容词old)

3. 形容词的比较级和倍数考点(倍数+完整比较级)
  • 倍数 + 比较级 + than 表示的是多几倍

He is two years younger than you. 他比你小两岁。

  • 倍数 + as + 形容词或副词的原级 + as + 其

  • 倍+the+名词+of

4. 特殊格式

① more and more “越来越…”
② The more…, the more… “越…, 越…” the不可以省略
the 比较级 of the two (最高级形式表比较级) “两者中比较…的一个”
比较级 与 明确的范围 前面加the
序数词可以修饰最高级
one of 后用最高级

very 不能放在动词前
a lone 单独的
lonely 孤单的

动词时态/语态

特点:1.答案是不同的动词时,考词义辨析
2.答案是不同的时态时,考语法
解法:

1. 浏览题目,有没有特殊格式(虚拟/by)
2. 留意答和原文是否有will的将来时,小心主将从现

if -> 主将从现
[主将从现] IF条件状语从句、时间状语从句、让步状语从句
主句是祈使句时相当于将来时
判断是否为虚拟语气找关键词、找主从句、找时间状语时态对应、是否时态杂糅
如果没有now的表明现在时,就不要想太多啦[过,过]

if从句 主句
现在事实相反 did/were [wcms 厕所没水]
would/should/could/might + do(动词原形)
过去事实相反 had done would/should/could/might + have done
将来事实相反 did/were
to do
should do
would/should/could/might + do(动词原形)
3. 顺序:一被[被动]二单[三单]三时态 (根据句意选答案)

主动被动、单数复数
三单:指第三人称单数,指he, she, it

4. 出现答案有had done,务必保证过去之前
5. 出现答案有have been doing(完成进行时) 答案是(have done)时,务必牢记一直在做没间断
6. be doing 现在正在进行时 ; be being done 现在进行时被动 [==轻易不出现 一出现就是答案==]
7. 过去时,过去完成时

介词

特点:答案均为介词
考点:固定搭配 (读句意,确定答案)

1. 单个介词的使用

under正下方 below下方就行
over正上方 about上方就行
through 纵穿
pass在前面穿过

on, in, at 的时间选择

介词:形容词/特定/范围 用on[节日day] at[节日不带day] On a clod afternoon in December
in 用一段较长的时间段前面,包括年份,月份,季节等。
on 主要是用在具体的一天前面,包括星期几和几月几号。时间在一天之内 特指的 特定的
at + 时间点 / at noon, at night, at midnight

大于1天用in,等于1天用on,小于1天用at
特例
in the morning/afternoon/evening,
on a cold/warm/hot morning/afternoon/evening,
on the morning of July 4th

② at the end, in the end, at the beginning, in the beginning 区别

at the end 更多的是用它的字面意思,表示在某个名词的结尾或末端,这个名词可以是一个物理对象,一段时间,一个事件,一个地方,或者更抽象的东西,比如一个人的耐心,
in the end 中的 end 是指一般的“end”区域,是一个习惯用语,意为 “最后,终于,到头来”
at the end 后面要接 of 介词短语,而 in the end 单独在句中作状语,通常用逗号与后面的隔开。
The heroes celebrated at the end of their journey.
In the end, what really matters in a friendship is trust.

at the beginning 常常跟of 短语连用
At the beginning of this century a great many Europeans went to live in the USA.
本世纪初许多欧洲人去美国居住。

in the beginning 通常单独用作时间状语或定语,而不和of短语连用,意为“起初;在开始的时候” 暗含着后来又变化的意思
In the beginning we thought we’d better get it all arranged. 在开始的时候,我们以为不会把它全安排好。

③ in, on, by 加交通工具

by bus 用车的方式乘坐
==铁包肉用in(bus) take 肉包铁用on(自行车) ride==
除了自行车[ride bike] 用动词时 一律用take
on foot 步行 on farm 在农场

④ in, after, later 表示 “…后” 的区别

after过去为起点,表示过去一段时间之后,常用于过去时态的句子中。
可以用:时间段+later 或者 after+时间段。

表示某时间点之后 用:after+时间点
after ten o’clock:十点之后
after April 23rd:4月23号之后

表示某时间点之前 用:before+时间点
before ten o’clock:十点之前
before April 23rd:4月23号之前

Two hours later, Jcak came back with an axe. 两小时之后,杰克拿着一把斧头回来了。
She went after three days.她是三天以后走的

in现在为起点,表将来一段时间以后,常用于将来时态的句子中,
I will go to London in two weeks. 我两周之后去伦敦
She will go in three days.她三天以后要走

on, over, above, under, below 混考

on表示在一物体上,强调两物相接触
She put her coat on the bed. 她把大衣放在床上

over也表示在一物体上,但强调覆盖这一物体。试比较
She put her coat over the sleeping baby. 她把大衣盖在那正在睡觉的孩子身上。

on仅表示“处于……之上”,是静态
he farmers walked to their fields with hoes on their shoulders.
农民们肩上扛着锄头,向田地走去。(仅表示锄头所处的位置。是静态,用on,不用over. )

over表示“越过”某一高度,具有动态之意
They dragged heavy stones with ropes over their shoulders.
他们肩背绳索拖拽巨石。(绳在肩膀两边弯下去,不用on)

above表示位置高于某人或某物。但不一定是正上方,反义词是below.

belowunder 两者都可表示 “低于
under 主要表示垂直在下的正下方,两物体可以接触,也可以间隔一定距离。
I out the money under the mattress. 我把钱放在床垫下面
表示正下方,两者都可用;不表示正下方,则通常用below
I out the money under the mattress 我把钱放在床垫下面

belowunder均可表示数量方面的“少于”,但在现代英语中,以用 under 为多见
below 表示“少于”,主要用于表示温度、高度以及有纵向标准可比的情况

beneath 可以用来替换under,但是在抽象含义时,最好用beneath
He would think it beneath him to tell a lie. 他认为说谎有失他的身份。
She married beneath her. 她嫁给了一个比她地位低的人。

⑥ across,through,over,past 表”通过”区别

over 在某物上方“通过、越过”,与表面不接触

在表示“从……上面”穿过的时候,across 和 over 可以互换,但是表示“翻过”的时候,只用 over
The Sawyers are walking over the bridge.(索耶一家正在过桥)
The dog jumped over the fence into the garden.(那条狗跳过篱笆进入了花园)

through 从某一个物体的里面或者一定范围内穿过、横过,往往是穿过一个有“框”的东西

比如门窗、树林、隧道等等。
The thieves climbed into the kitchen through the window. (小偷们通过窗户翻进了厨房)
After walking through the forest, they arrived at the small village. (穿过森林,他们到了那个小山村)

across 表示从某一个物体的表面横过、穿过用

往往是从上面跨过,比如跑过草地、跨过马路等等。
The dog is running across the grass after the cat. (那只狗正穿过草地追那只猫咪)
Can you swim across the river?(你能游到河的对岸吗?)

past 从旁边经过,可以和含有动词 pass 的句子替换。

A girl went past me in a hurry. (一个女孩匆匆忙忙从我身边走过)
A girl passed me in a hurry. (一个女孩从我身边匆匆经过)

⑦ in,with,by 表”用”的区别

in:用语言或原材料

with: 带有,附带→手里拿着→用(工具物质做某事)[真真实实的东西]。

常用于人手可持握操作的工具
cut it with a knife. 用刀切
write it down with a pen. 用笔写

by:从…旁边经过→通过→通过…方式/工具/手段/媒介。

常用于交通工具抽象动作
by foot/car/bus/train/plane. 走路、坐车、搭公交、乘火车、坐飞机。
You can speak Engllish well only by practicing. 只有通过练习你才能说好英语。

through:从…中穿过→通过某物→通过某人、某媒介、某行为(作方式状语)

强调经历了某个过程或具有较强的中介性
make progress through efforts. 通过努力取得进步。(经历过程)
look through a telescope. 用望远镜看. (目光从一端进入,从另一端穿出)
hear sth. through sb. 通过某人听说某事。(中介性)

扩展1:by表抽象的方式、动作。而with表具体的工具,有形的手段。

He killed the spider by hitting it. 他把蜘蛛打死了。
He killed the spider with a flapper. 他用苍蝇拍把蜘蛛拍死了。

扩展2:在被动句中,by指行为者(人或物), with表工具或物料

He was killed by a heavy stone. 他被一块儿石头砸死了。
He was killed with a heavy stone. 他被人用一块儿石头砸死了。

⑧ 门牌,路名,地名的介词

in:“在某个区域”。

in the market, in the field, in the street。把“大街” 也视为一个区域。
in用在大地名前,城市或比城市更大

on:“在某物的上面”。(与之接触)

on在水边,江河湖海之滨

at:“在附近;在一边/一旁”。

He lives at 152 Base Street. at用在小地名前,比城市小

但是若指……范围之内,还是要用in,
尽管不是大地名.in the school,in the office
街道门牌号码前用at,
若仅仅是街道,都可用,in the street,on the street

⑨ Die of, die from, made of, made from区别

die of内因 用于死因存在与人体身体之内[illness]
die form外因 用于死因由环境造成的[earthquake]

made from看不出原材料made of看得出原材料 区别,是基于 from 和 of 的基础认知含义
from 指从哪里来,
of 表示某种隶属或从属关系。
一座桥是 made from stone,讲造桥用到的物料来源
石头是 made of stone,讲构建成桥的组分或者成分

⑩ except, except for, except that, but, besides, beside的区别

你是我不是except, except for, except that

你是我也是besides, beside

besides表示除……外(还有),是包括在内的意思【加法的概念】。
例:Besides tomatoes, I also like potatoes. (包括tomatoes)
beside”是介词,有两个意思,“在…旁边常用来表示位置和方向。”和“与…相比”
He is standing beside the tree. 他站在在树的旁边。
My handwriting looks so childish beside yours. 和你的字相比,我的看起来很幼稚。

except与except for均表示除去,是不包括的意思【减法的概念】。
但两者从语法角度有区别,不能随意互换。
*** 区别一:**except for能放句首,但except不能。
例:Except for Jim, who is unwell, they are all ready to leave for America tomorrow. (不包括Jim)

*** 区别二:**except for由于有介词for,故后面只能接名词/动名词或名词性短语;
except后面既可以接名词或名词性短语,也可以接从句、介词短语甚至动词。
例1. I like all vegetables except (for) tomatoes. (不包括tomatoes)
例2. You are allowed to smoke everywhere except in the lobby. (不包括in the lobby)
例3. The dish you cooked was great except that it could use more salt.(这里不要直译,可译为“你做的这道菜很好吃,就是味道可以稍微再咸一点。”)

except that 只可惜
The regretful thing was the awful weather that degraded the nice scenery.
只可惜天公不作美,总是阴雨连绵,影响了景致。

⑪ by + 时间的特殊用法

by后加某一时间,表示到某时为止,不能加某一时间段. [有最后期限的意思]

by 指“在……前(时间);截至(到)……”:

How many English books had you read by the end of last year? 到去年年底以前你…

by后加过去时间,用于主句是过去完成时
By the time he was 7,he had learnt English himself. 到7岁的时候,他就已经自学了英语

by后加现在时间,用于主句是现在完成时
I have done my homework by the time you come. 你回来以前我就把我的回家作业做完了。

by后加将来时间,且从句事件发生在主句事件之前,主句动词可用一般时态。
Can you reply to me by tomorrow? 你能在明天之前答复我吗?

by now 就是现在完成时
by + 将来时间,就是将来完成
by+ 过去时间,就是过去完成

by作介词时
  • 靠近, 在…旁边;
    A dog sits by her. 一条狗挨着她坐
  • 被, 表被动;
    I was told the truth by him. 我被他告知了真相
  • 由于; (置于不带the的名词前,表示原因)
    I found my notebook by chance. 我碰巧找到我的笔记本
  • 在…期间, 处于某种情况;
    to go on a trip by day. 在白天旅行
  • 经过
    He walked by me without speaking. 他一言不发地从我身边走过
  • 从…看;按照
    By my watch it is two o’clock. 我的手表上显示的是两点钟。

连词看此意

特点:答案均是连词
考点:
一般考上下文逻辑关系,看句意给答案
特殊答案:

While 当…是,区别when;
while巴拉巴拉 主句pia结束性动作
when pia 主句巴拉巴拉延续动词、正在进行.
as….as…
as pia 主句pia

表示相对关系, “而”;表示让步, “尽管” 一般是加在两个句子的中间起转折作用。while用作连词时,表示“在…(过程)中,在…期间”。常用来引导[时间状语从句],当主句的主语和while所引导的从句的主语一致时,while从句中的主语、谓语往往可以省去。

unless 除非, 否则

The company won’t keep going unless we can get some loan.除非我们能搞到贷款,否则公司就维持不下去了。

until:直到…时

Until 小心前面考有没有not 意为直到,主句一般为延续性动词
till用作连词用于肯定句时表示“直到…为止”,指某一动作或状态一直持续到till后面的句子所表示的时间为止,这时主句的谓语动词是[延续性动词]
He banged on the door until she let him in. 他砰砰打门,一直到她开门让他进去为止。
will not … until

as 头, 第二, 中间

as 用于引出一个持续性动词表示“在…期间”时,其谓语通常只能是那些含有动作(action)和发展(development) 意味的动词,一般不能是那些不用于进行时态的动词(如 be, seem, love, want, agree, see, know, have 等),

since:自…以后

since一般用作介词,从句一般都是表因为
since用作介词的意思是“从…以来,自从…之后”,其宾语常指过去的一个时间点,其含意通常指持续到说话时刻的动作或情况开始于什么时候。

as soon as:一…就… 主要是强调主句和从句的动作发生的的时间具有同步性

I recognized him as soon as I saw his back. 我一看到后身,就认出是他。

once:一旦

Once you start a task,you must bear it through.你一旦开始一件任务,就必须把它完成。

数词

特点:答案包含各种类型的数字
考点:

① 形容词比较级的倍数关系

倍数+比较级的顺序关系 倍数和数字+完整的比较级关系 倍数+as…as
比较级考点:同级比较、倍数与比较级
形容词倍数关系 倍数+完整的比较级
Peter’s jacket looked just the same as Jack’s, but it cost twice as much as his.

② 当hundred, thousand, million, billion前面有基数词时

用单数形式,词尾不加 -s; 前面有many, several, a few修饰时,仍用单数形式

③ 表示概述时

ten, hundred, thousand, billion的复数形式加of,后面接复数名词

④ every和数词连用

可表示”每隔;每逢“,在基数词后用复数可数名词,在序数词后用单数可数名词
“every other/second + 单数名词” 表 “每隔一…”

基数词:指描述事物数量多少的词。 序数词:指表示顺序的数词
(1-10)基数词):one. two. three. four. five. six. seven. eight. nine. ten.
(1-10)序数词) :first. second. third. fourth. fifth. sixth. seventh. eighth. ninth. tenth.

⑤ 编码排序

(1) 表示数目:the first lessonthe序数词, lesson one基数词
(2) 模糊数字s +of:Hundreds of数以百计的 Thousands of数以千计的
(3) “基数词 + 连字符 + 名词” 可以构成复合形容词表示数量,此时名词应用单数形式,
相当于“基数词+复数名词的所有格”
a ten-minute walk十分钟的步行路程 a three-week holiday 为期三周的假期
(4) 分子基, 分母序, 分子大于1, 分母加s 1/3 one-third 2/3 two-thirds

非谓语动词

特点:答案全部或部分为非谓语动词
做题流程:

① 确定是否有非谓语答案(所在句中是否有谓语动词)
② 若为非谓语动词,判断成分

​ (1) 有逗号,无连词或无主语或均无,作状语
​ 放在开头,先确定是否为目的状语
​ ing和ed选择取决于逻辑主语

​ (2) 无逗号的整句,则为其他成分
​ a. 主语:不定式或动名词(特指泛指)
​ b. 宾语:不定式或动名词(固定搭配) (做没做)
​ c. 表语:均可(乾坤大挪移) 不能移用分词(ing物,ed人) 能移另外二(没做要做to do)
​ d. 宾语补足语:均可
​ -普通动词后:to do
​ -感官类,使役类动词后:均可
​ △ 要做没做,主动做过 to do
​ △ 正在做ing【首选ing】
​ △ 被动或且被动ed have sth done
​ -get, find, keep, leave 后均可,但必须用to do
​ -make oneself done

to在原句里出现一般是介词 doing

feel like doing

can not like doing sth
人mean todo(打算做) 物mean doing(事意味着)
stop to do停下来去做 doing停下在做的
go on with sth go on to do继续去做另一件事 doing继续原来的事
continue 同理
放在前面作定语不是ing 就是ed
ing表示主动或且进行 ed表示被动或且完成
已经落在地上的叶子 完成

被动完成ed 主动进行ing 正在进行ing 要做没做主动做完todo

宾语补足语:感官类 使役类(have let make) to 省略
have表达意思是 have sth to do have sth done

remember todo没做/doing做过
regret 遗憾todo 后悔doing

todo 一般都在动词后面

情态动词

特点:答案中均含有情态动词
考点

① 基本含义考法(要啥给啥)
表推测(can, could, may, might, must, should)

​ (1) 肯定(can陪葬) (看可能性大小)(CSmust) 肯定玩CSm必须滴

==can, could, must, should 用在肯定句中表示理论上的可能性,”经验之谈”
[万能陪葬can] [can只能用于天下真理]==

说的事情让别人高兴 –> should (70%)
说的事情可能让别人不高兴 –> might

​ (2) 否定(can系列,不可能)(may系列,可能不)(CCMM) 不可以操操妹妹

==can, could, may, might 用在否定句中表推测,翻译成”不可能“==

​ (3) 疑问(can系列)

==can, could 用在疑问句中表推测,表示’’可能性‘’==

对过去猜测(+have done)(can系列 may系列 must)

​ (1) 全能(may系列)

​ (2) 否定疑问(can系列)

​ (3) 肯定(must) 踢出should
解题:有have非猜则虚;无have非实则猜

主谓一致

特点:答案既有动词的单数又有复数
解题方法:找动词前面的信号词
信号词:

① 就近原则 [后面]

就近原则是指谓语动词的单、复数形式与离它最近主语的单、复数形式保持一致

  • ==由here、there、where等引导的倒装句中==,(有时主语不止一个时)谓语动词的单、复数形式与靠近它的主语一致
    There is ==a pen== and several books on the desk.
    Here is ==Mr. Brown== and his children

  • ==由or、either…or、neither…nor、not only…but also、there be…==等连接的并列成分作主语时【谓语动词的单复数形式应与or后面的主语保持一致】
    Either you or ==she== is to go.
    Neither the students nor the ==teacher== knows anything about it. 学生和老师都不知道这件事

② 就远原则 [前面]

  • 当主语后面带有==with、along with、together with、like(像)、but(除了)、except、besides、as well as、no less than、rather than(而不是)including、in addition to==等连接的短语时,【谓语动词的单、复数形式依然与前面的主语(远一点的主语)保持一致】

    The teacher together with his students is taking lessons. [is是看together with 前面的主语 而不是 his students]

    Nobody but two students is in the classroom.

    Everybody except you is down on me.

    John, rather than his roommates, is to blame.

    Jim, together with his classmates, has[找Jim] seen the film.

    The funds rather than mental support are what the organization needs most now.

③ 其他

more than one + 名词(单数)
many a + 名词 (单数)
动词不定式,动词-ing形式短语作主语 (单数)
以-ics结尾的学科名词,以-s结尾的名词news, works, plastics (单数)
And连接,但为同一物 (单数)
the following作主语时 (表语定)
what引导的主语从句 (表语定)
冠词+多个职业名词 (冠词个数定) The math teacher and English teacher is…
each, every, no和代词名词 (前面定)
half of, the rest of, most of, all of及百分数 或 分数+of 等后接名词 (名词定)
The + 形容词表示一类人 (复数)
and 连接两个形容词去修饰一个名词,指两种不同的事物 (复数)
police, faculty (全体教员) personnel (人员,职员) 是集体名词 (复数)
One or two more + 复数名词 (复数)
family, team, committee, party, enemy, audience表集体 (单),集体里的人 (复)
All (人复物单)
双双对对 (复数) 加上 “一副” (单数)
one of + 复数名词 + 定语从句 + (复数);the one of + 复数名词 + 定语从句 + (单数)
“度量衡” 表达整体概念时 (单),数量 (复)

强调句

特点:

① 原文和答案中可凑齐一组强调格式 it is/was…that/who
② 谓语动词位置多个do/does/did

解题:

金三角抠洞洞
(完整强调:不完整、非强调)
It is not until… that…

倒装句

特点:答案中有明显的倒装答案
解题:找倒装句信号词

① 部分倒装 (助动词提前, 谓语不动)

  • a/n/v + as/though…开头
  • No sooner had done… than…
    Hardly had done… when…
    Not until…did S+V…
  • 否定词或否定短语开头
  • Only开头,主句倒装
  • Not only…(倒)…but also…(不倒)…
  • So/Neither/Either开头(你好我也好)(我思故我在)
  • So/Such…(倒)…that…
  • 虚拟省略if, Had/Were/Should 开头的陈述句

② 全部倒装

  • **here/there/介词 **+ 地点/副词 开头,表语前置

    By his side stood his son.

  • wish系列
    as if/ as though/ if only

  • 要是没公主系列
    otherwise, or, but for, without

  • 宁愿做丫鬟系列
    would rather/ would sooner/ would prefer/ had rather/ would as soon

虚拟语气 (If 主将从现主句一定要有will、虚拟语气)

公主丫鬟系列

丫鬟-随从-从句-衣服-if (从句) 要穿之前的绿色衣服->时态往前推一档
公主-主子-主句–公主服—should/w/c/m (公主服)

==现在== (穿衣服的是丫鬟 丫鬟往前推一档)
从句if:do/does——>did, is/am/are——>were
主句:should/would/could/might + do/be
@@ (现在虚拟) 如果我是你,我会接受他的建议
If I were you,I would accept his advice.

==**过去 **==[时态往前推一档] earlier
从句if:did(将军府红衣服) ——> had done(农家绿衣服) 主句:should/would/could/might + have done丫鬟过去式的原型[先穿公主服+丫鬟过去本体原型]
@@ (过去虚拟) 如果你以前好好学习,你会通过考试的
If you had studied hard before, you would have passed the exam.

==将来==
从句if:should do; were to do; did; were [将来->过去将来]
主句:should/would/could/might + do
@@ (将来虚拟) 如果明天下雨,我会待在家里
If it rained (should rain/ were to rain) tomorrow, I would stay at home.

  • if从句中,省略if,were,had,should可以放在句首(用于倒装结构),否定词not不能放在前面

If I were you, I would accept his advice.
Were I you,… [最后是句号] [看屁股后面]

@@ Should it rain tomorrow将来, we would have to put off the visit to the YangPu Bridge.
看句式 是”Were I you,… [最后是句号]”, 是将来时,should do / were to do 因为没有to 果断should

  • 错综时间虚拟语气(答案有现在、有过去)

当从句与主句时间不一致时,动词的形式要根据它所表示的时间作出相应的调整
If you had followed(follow) my advice(穿衣服的往前推,过去的过去)==just now==, you would be(be) ==better now==,
If you had studied(study) hard ==before==, you would be (be) a college student ==now==.

你认不认识我系列 + 1234词

should+do/do, should 可以省略
① ==It’s necessary/strange/natural/important/impossible…that 主语 + (should) + 动词原形==
It is strange that he (should) say so. 他居然会这样说,真是奇怪
It is necessary that he be sent被动 to Beijing.

② ==It is a pity /a shame/ a surprise/ no wonder that 主句 + (should) + V原形==
It is a great pity that he (should) think so. 他居然会这样想,真是一件遗憾的事

③ ==It is/ was suggested/ ordered/ demanded/ proposed/ (1234) + that + 主语 + (should)+ V原形==

一坚持 二命令 三建议 四要求
insited
ordered, commanded
advised, suggested, proposed
demanded, required, requested, desired

It’s suggested that the plan should be carried out.
The doctor suggested that he (should) try to lose his weight.

注意:遇到suggest,insisted

  • suggest:表明,暗示(陈述语句该用啥用啥);建议(虚拟语气)
    Her pale face suggested表明 that she was ill
    Her friends suggested that she (should) be sent to hospitial immediately.
  • insist:坚持说坚持过去的事(陈述语气);坚持要求将来还未发生的事(虚拟语气)
    He insisted that he didn’t stolen the money.
    He insisted that he (should) be set被放 free.

以防万一三大词
for fear that, in case, lest引导的从句中, 若用虚拟语气时,从句谓语为:(should)+do
并且should能省略(for fear that, lest) in case不能省略

Wish系列(寻秦记) [项少龙]
现在 大多数时候穿着丫鬟服
过去 (还未见面)穿丫鬟装只有裤子could的公主装did -> have done
将来 见到项少龙 将来联姻 就要一直穿公主装(一套)了(将来)

与现在事实相反 过去时(were)
过去事实相反 过去完成时, had+done, could, have done
将来事实相反 Should/would/could/might + 动词原形

将来要嫁给项少龙 要穿公主装
I wish(that) Should/would/could/might + 动词原形 he (visit) us tomorrow.
现在见到公主穿着丫鬟装
I wish(that) he visited(visit) us today.
过去穿丫鬟装和只有裤子的公主装
I wish he had visited(visit) us yesterday.

其他用法(赵倩公主做的一切都是如她所希望的)
as if / as though(好像) + 从句 (wish一样)
if only(要是…就好了)

@@ If only I were(be) a bird [对现在的虚拟 如果我是一只鸟就好了] 现在是丫鬟装
@@ If only you had listened(listen) to our advice! [对过去虚拟,公主装/只有裤子的丫鬟装]
@@ If only I wcms be(be) a superstar some day.
@@ He looks有s一般现在时 as if he were an artist.
@@ He talked过去 about the accident as if he had seen.

公主宁愿作宫女而没有将来系列
一眼看不到头没有将来 将来与现在一样
宁愿{would rather, would prefer, would sooner, had rather, would as soon} + 宾语从句中

==与现在将来事实相反 过去时 动词过去时**/were**
过去事实相反 过去完成时 had done==

I would rather you came(come) here now.
I would rather you came(come) here tomorrow.
I would rather you hadn’t told(not tell)me the truth yesterday.

含蓄虚拟语气
在虚拟语气中,并不总是出现if引导的条件句,而通过其他手段来代替条件句
常用的有otherwise, or, without, but for (倘没有, 要不是)等 => 给的是==公主系列==

① I was过去 ill that day. Otherwise, I would have taken part in the sports meeting.
=If I hadn’t been ill that day, I would have taken part in the sports meeting.
Without / But for your help, we couldn’t have finished the work ahead of time.
=If it had not been for your help,…

@@ Without electricity, human being life would be quite different today现在=>公主+be
@@ But for his help, I should not have succeeded
@@ He hesitated for a moment before kicking the ball, otherwise he would have scored a goal.

丫鬟该要干某事了系列
该要干活了后面是丫鬟,丫鬟 从句格式 时态往前推一档
要干 证明是 将来 丫鬟
干某事了 对应的动作

  • 将来 => 从句if:should do; were to do; did; were [将来->过去将来]
    should 不能省
  • It is (high / about) time + (that)从句
    动词过去式、should(不可省略) + do
    该是某人做某事的时候了
    It is high time that you got up/should get up

@@ I’m getting tired;it’s time we wentshould+do没有should就选went home.
@@ You look so tired tonight. It is time you went to bed

==but 后面是真实的句子 该怎样就怎样==
@@ I would have told过去 him the answer had it been possible, but I was该用什么用什么 so busy then.

反义疑问句

特点:答案都是反义疑问句
解题:前肯后否,前否后肯,你主我主,你助我助
考点:

① 否定前缀不算否定句 important imporsible
② Little, few, seldom, hardly, ralely等否定词算否定句
③ Let us…,祈使句 will you? Let’s…,祈使句 shall we?
④ Do…祈使句, will you?
⑤ 否定前置,以后面内容为准
⑥ 名词性从句,动词不定式,动名词做主语,主语代词为it
⑦ 对话回答,保持一致。以后为准,变yes,no

定语从句

特点:答案均为帽帽
解题:

① **一强二定三名词 **(其前位名词或句子逗号)
② **”连”**字绝

考点:

① 直接可连上:who, whom, which, that
② 加介词能连上

介词 which;介词 whom
地点 + 介词 + which 可用 where
时间 + 介词 + which 可用 when
reason for which 可用 why
方法 + 介词 + which 可用 that

③ 前名词后名词,中间变”的”可连 => whose

用于 “…的” 作定语从句的定语
前名 后名 中变的 能够连上为whose
I know the girl whose mother is a teacher

④ 有逗号没连词必带帽,该咋戴帽咋戴帽
⑤ 帽前介词连前后,只有连上才对头
⑥ 只用that,不用which

<1 当先行词是all, any, few, little, none, anything, everything, nothing, everybody, nobody, everyone, no one 或被它们修饰时
<2 当先行词被形容词最高级序数词修饰时
<3 当先行词有the very, the only, the same等修饰时
<4 当主句以who或which开头时,定语从句的先行词用that
<5 当先行词同时包括人和物时,关系词用that
<6 当主句是There be 句型时

⑦ 只用which,不用that

<1 关系代词前有介词
<2 非限制性定语从句
<3 先行词本身是that

名词性从句

特点:答案均为帽帽
解题:1.一强二定三名词;2.缺不缺,疑不疑(主谓宾缺不缺) (主句有没有疑惑不确定)
考点:

① 不缺不疑用that
② 缺选what, who/whom, which(需选项)
③ 不缺有疑,缺啥給啥
④ ==whether== 与 if 均为 “是否” 的意思。除了直接跟在动词后引导宾语从句可以用if,其余一律用whether
⑤ doubt用与肯定句表疑惑;用于否定句,疑问句,表不疑
⑥ The reason why + 句子 + is that…;The reason for + 名词 + is that… [注意有坑]


阅读理解

检索类题目(送分题)

特点:有黑体标题,有表格,有列表
解题:
1.阅读第一段、大标题、黑体标题、确定主题
2.直接看问题,找到定位词,看一个找一个答案
3.要求速度和正确率

记叙文(读全文,需理解)

特点:文章中有明显的时间、地点、人物、对话、故事情节等
解题:
1.一次性读完后面的题目,并画出相应关键词
2.从头到尾读全文,遇到题目关键词或相关内容时,仔细选择答案。(会出现乱序题目)
3.仔细理解内容后作答,小心深层含义的主题

细节题不要过度解读, 东拉西扯,原文的答案就是答案!!

说明议论类(定位及答案)一一对应

特点:文章是大段大段的说明议论内容
解题:
1.段落比较大时,先读各段首句。段落比较小时,先读第一段的第一句,并浏览第一段是否有转折词,若有,则其后为重点内容,若无,则,第一句多为主题
2.浏览每小题找到定位词。这回原文顺序找该定位词,找到即可定位答案。一 一对应选择答案
3.题目多为顺序出题。遇到全文综合考察,放最后一题再做。

题型答案位置

1.细节题(60%)(答案在原文) 【边上写个 细!!】

定位词定位答案 → 和原文 一 一 对应(同义转换)

2.词汇题(有序号)

答案不可自己猜,原文答案已告知。本句,上句,再下句

3.主旨题

文章的第一句,或第一段里的转折部分

4.作者观点态度题

文章的第一句,或第一段里转折部分;文章末尾总结句;建议或观点句子

5.推理题

答案一般为同义句转换。推的越远越错,附加条件越多越多 [可先跳过此题]

6.判断题

四个答案分别定为原文,和原文句子进行一一比对即可。耗时,不难

7.实验,数据,故事类

答案一般为道理。答案在该内容前后的总结句或段落总结句中里

阅读错误总结

==1.题目想当然,看错题目,题目必须仔细看
2.一一对应错误,看到个别单词就当做答案,务必读全文,做到答案信息均有出处
3.忽略绝对肯定或绝对否定句
看到比较绝对的肯定词或否定词,务必原文当中找到出处。否则一般不选
4.根据…的观点,想当然认为是坐着或某人的这类题目,一旦出现”根据”字样,务必确认对应答案
5.想象力丰富,脑部很多内容,按照脑补选答案。忠于原文,不要把你当成作者,你只是读者==

翻译

1.中翻英 [拆句子 ==翻译主谓宾 (定语)一个词前, 两个词放后面;(状语)全放后面==]

①确定所给词的用法/拆句子
②找句子主干,并翻译
③定语状语等,按照规则放好
④微调单复数,冠词,时态语态,顺序

感谢信—申请信—邀请信【热点考点】 建议—投诉—道歉信【概率小】

==准备景点介绍材料== 这两周热点是旅游 介绍南昌旅游景点邀请他来
南昌位于… 南昌历史 八一广场 滕王阁 秋水广场

作文

1.申请、推荐、祝贺信
字不够,优点凑
2.邀请信
字不够,活动内容、细节凑
3.感谢信
字不够,虚拟假设凑
4.建议信
先抑后扬,虚拟用法
5.投诉信
字不够,危害凑
6.通知海报
字不够,活动内容、细节凑
7.发言稿
寒暄介绍少不了

阅读全文

语法单选模块

2023/2/25

2023.2.25

语法类板块[虚拟/主将从现/翻译]

祈使句表将来
时间、条件、让步状语 => 主将从现
Ask her to come and see me when she finishes her work.
考时态语态时候看有没有 虚拟 或 By[没有的话看句子]后面如果是过去时间或过去,句子主句过去完成时。后面是将来完成时,将来或一般现在时 the time we got home, I had forgotten all …
The teacher demanded that her students be on time for every class. [were/be/to be/must be80%考虚拟:(should)+do]
Since既然 you are free tonight, why not drop in and play chess with me? [for/unless/since/though]全是连词的,两句一连,带着连词的那句话就是状语从句

连词一般会考句意
连词是While(80%) [对比关系=>用while] [尽管->引导让步状语从句]
连词是Unless 除非...才能
连词是until 主句中出现not 大概率出现until => not...until
连词是Since语气比as强 意为:'既然'
unless until若同时出现在答案中;
unless(除非...才)[强调"条件"][前面一般是情态动词]
didn't until(直到...才)[强调"时间/状态"][前面一般是时态]
as[90%答案]倒装/正如.../固定搭配
I am afraid 标志着转折含义

所有考介词的点 80%以上都是固定搭配
One of the techniques in reading a passage is to read it as a whole.[as/for/to/in] (as a whole作为整体 in a whole总的来说)
The company would need another ten thousand dollars 1 they could buy some new equipment.[(in/at/by/with)which]

in 用语言、原材料[link]
by 抽象概念的方法[交通工具/手]
with[80%]具体的有东西、有实物

It’s important that he (should)+pass the final examination next week.
any[包含关系=>选不包含关系] 后面有单数/复数/other
时态语态=>找by过去->过去完成时,将来->将来完成时if[作主语,时态往前推一档次] =>虚拟
if后有没有时间[找两种时态、but]错综复杂时态
who引导的定语从句在从句里面做主语时候,此时中间一般不能用逗号隔开[只有ing和ed才用逗号隔开脱帽变太监]
I went to the tailor’s to have a suit made the other day.[make a suit/have a suit be made/have made a suit/have a suit made两个动词一定有一个非谓语]have sth done
It is a long time since It has been along time before


介词:形容词/特定/范围 用on[节日day] at[节日不带day] On a clod afternoon in December
by+年月日星期几点几分 [最后期限] by+小数分数数字 增加多少减少多少上升多少
as[80%的正确选项]…as
can对应的是能力 不是 能够
doing done[同时进行]
The reason why is that
only to do
前肯后否,前否后肯,你主我主,你助我助 [have to/had to 疑问部分常用don’t/didn’t]
many of them


It is high time (that) should do/did
倍数+比较级的顺序关系 倍数和数字+完整的比较级关系 倍数+as…as
比较级考点:同级比较、倍数与比较级;同类不同一个时 用that[much higher than that of a professor ]
被动变主动 疑问变祈使
感官类动词后面是sth 直接接ed
should do 不是真实的就是推测的
have done 不是真实的就是虚拟的
be likely to do 可能做…
The reason for…
even if 尽管
as if 好像
since 自从,既然,因为
非谓语动词做状语时,直接选ed
can’t help doing 禁不住做…


so 与 such 顺序 直接跟在so后面的是形容词 so形容词冠词名词;such冠词形容词名词
more and more; the more, the more
it is time that should todo
by the time + 一般现在时 主语+will have done
could you do some +n for me? 你可以帮我做点…么
It might be days before 离…可能还有一段日子
All those 后面可以省略that
but 后面加动词 but to do[正常情况下] 若but前面出现do时,省略to变do
=> have nothing to do but do 除了…别无其他事可做;前do后没to
such/same/so/as + as…

while……主句pia while后面进行 主句结束pia/一般动作
when….结束pia 主句延续性….balabalabal
as….balabala as…balabalabla

情态动词表猜测,对于过去的事情的猜测
none of 三个或以上,都不


宾语从句的主句是过去的 从句所有的时态要往直前推一档[53]
he told me
I am preparing for the exam.
=> he told me he was preparing for the exam.
be disappointed at sth 对…很失望
there be 结构的非限定形式 there being
有2有3看肯定 看否定;both 复数 any 肯定句 neither 和 every用于单数[看似表达每个,其实指整体]
sb is said…to do 据说某人…
it no use/good/waste of doing sth…
will do /shall do /be about to do表将来
very/副词/副词排序 频率类副词排序 要跟在所有助动词或实义动词之前 如果各种格式都有要放在第一个助动词之后

The professor was so popular that he was alsways listened to with enthusiasm.

suggested 是表明还是建议[虚拟 1234+should do]
it is said/reported to do
miss 人失踪是主动[be doing] 东西丢失是被动 => 被动行为[be done]
regret 遗憾todo 后悔doing
情态动词后是原形 不是真实的就是推测的
must100% should75% maymight30%~
考代词 others、the others后面不加名词;another前单后单,前复后复 other复数后面加名词;


2023.3.4 班级英语模拟考

1.有’s的有生命的人或动物
Jim was late for two classes this morning. He said that he forgot both of the room numbers

2.冠词特指&&泛指 satisfaction 抽象概念 不可数
Sarah looked at the finished painting with 不填 satisfaction.

3.讲英语这件事 不可数 => 不可数 as much as
We should speak English as much as possible

5.时态是现在,先哭;因为丢了玩具
The little girl cried her heart out because she had lost her toy bear and believed she wasn’t ever going to find it.

6.否定放在非谓语最前面
Please tell him not to draw on the wall

7.考非谓语格式 两个主动一个被动(跟了by)
No one can aovid being influenced by advertisements

8.考非谓语动词,逻辑主语是he 被动表主动
Dressed in a white uniform, he looks more like a cook than a doctor.

be to blame意为”应负责任,应受责备”中文意义是被动 ,不可用[to be blamed]
连系动词:smell / taste / sound / prove
一些英文中是被动,但中文中是主动意义的动词==be related to / be absorbed in / be dressed in==

9.前后给了修饰限定的词表特指,介词用On
He decided to visit the family on Friday night.

10.形容词倍数关系 倍数+完整的比较级
Peter’s jacket looked just the same as Jack’s, but it cost twice as much as his.

11.主谓一致 一被二单三时态 图书馆被給了… 被动 be done[A C] 主谓一致 主语找信号词with、like、except、but,就远原则 library
A library with five thousand books is offered to the nation as a gift.

就远一致 当主语后面带有==with、along with、together withlike(像)but(除了)except、besides、as well as、no less than、rather than(而不是)including、in addition to==等连接的短语时,【谓语动词的单、复数形式依然与前面的主语(远一点的主语)保持一致】

就近一致 ==由here、there、where等引导的倒装句中;由or、either…or、neither…nor、not only…but also==

12.倒装Not until 后面 did sb do
Not until the early years of the 19th century did man know what heat is.

完全倒装[ There be + 时间、地点、方位副词 ]==当here、there、out、in、up、then、away、back==等副词置于句首,谓语动词为be、come、go、lie、run等表示动作的动词时,需完全倒装。

部分倒装 ==助动词或情态动词 + 主语 + 实义动词、hardly、seldom、neither、nor、little、no sooner置于句首==;==so、neither、nor + 助动词或情态动词 +主语==;==”only + 副词/介词短语/状语从句” 位于句首时的倒装==;==so + 助动词 + 主语表示前面提出的情况也适用于后者== ;==as引导的让步状语从句的倒装==

反意疑问句:==[前肯后否,前否后肯]==

13.一强二定三名词 时间前是介词肯定连得上 就是强调句
It was on the National Day that she met with her separated sister.

14.时态语态的边 看有没有by[By后面的句子完整,考主句 by后为一般现在时 主句+将来完成时 ;过去的话 主句是过去完成时] 特殊的虚拟 => ==It is important that + (should) do==
It is important that I speak with Mr.Williams immediately.

It’s time句型(当It’s time后用that从句时应该为“主语+ should +动词原形” 或 “主语+过去时”)
==It is important that + (should) do==
It’s required that all sales reports be finished no later than Jane 10

would rather, as if(though)引导的句子也需使用虚拟,表示过去的情况用过去完成时,表示现在与将来的情况用过去时

15.come into effect 生效
The new law will come into effect on the day it is passed.

16.时态语态 would 小心虚拟语气 => 有If => 看时间 前句last…
答案(had been; would have preventedhad been, would prevent) 错综虚拟语气
If my lawyer had been here last Saturday, he would have prevented me from going.

if从句 主句
现在事实相反 did/were [wcms 厕所没水]
would/should/could/might + do(动词原形)
过去事实相反 had done would/should/could/might + have done
将来事实相反 did/were
to do
should do
would/should/could/might + do(动词原形)

18.rewarded 回报的钱或物质 awarded 奖赏的是某种荣誉
Such noble deeds of the doctors and nurses can’t be only rewarded (rewarded/awarded) with money.

19.take make get have 固定搭配 appointment任命
His new appointment takes effect from the beginning of next month.


时态 若是事实 则一般现在时
John frequently attempts to escape being fined whenever he breaks traffic regulations.
考非谓语动词 没逗号,就先找动词。==keep get leave ?? 宾语补足语== 和宾语发生关系
考倒装 倒装助动词 can there be
考帽帽 一强二定三名词 前面to考虑一下定语从句 give sth to sb 如果句子里有 你觉得,我们觉得删掉[you think] please give your vote to whoever you think you can trust[缺宾语].[特指那个人]
The task was tough, but 1 we managed to fulfill it satisfactorily.

anyhow不管用什么样的方法,强调方法 无论如何 不管怎样
anyway即便如此,强调已有条件 无论如何 不管怎样
somehow 以某种方式 somewhat 有点,稍微

注意:==suggest== 表明该咋用咋用 / 建议虚拟语气; ==insisted 已经发生过的用真实 没发生的用虚拟(should)+do==;==regret 遗憾to do 后悔doing [regret to do/doing 遗憾没做todo 后悔做了doing]==
==情态动词==:看事情有没有过去 事情过去了 不是b就 是d,情态动词+had done 不是虚拟就是推测
The hot weather the milk, because it has an unpleasant taste and smell. D
A. must spoil B. must have spoiled C.can spoiled D. should have spoiled

==enjoyed== 后面 作宾语 123456789 + ing 剩下的to do

enjoy doing sth.喜欢做某事 miss doing sth.错过做某事
人+spend…doing sth.花费…做某事 suggest doing sth.建议做某事
be busy doing sth.忙于做某事 mind doing sth.介意做某事
imagine sb. doing sth.想象…做某事 be worth doing sth值得做某事
can’t help doing sth.忍不住做某 consider doing sth.考虑做某事
feel like doing sth.想要做某事 **practice doing sth.**练习做某事
finish doing sth.完成做某事 keep (on) doing sth.继续做某事

有逗号 有连词 就看句意 It may snow, but anyhow I have decided to go out. instead = but
动名词复合结构 I really very much appreciatedoing D me that great favour yeasterday.
B. you doing D. your doing
时态语态 ==insisted 已经发生过的用真实 没发生的用虚拟(should)+do==
考非谓语 先找到自己的句子中有没有谓语 有谓语的 后面只能用非谓语 feel like doing sth… be busy doing sth
考介词80%考固定搭配 be responsible for 对…负责
考连词[没有特殊的1234]
考帽帽 一强二定三名词 so…that、such…that、so…as
考连词 出as 首先考虑 since 有自从的意思
get accustomed to 习惯于 to 是介词,如果原句里面to就给了 那么to一般就是介词
I find it rather difficult to get accustomed to speaking before such a large audience.
It is essential that (should) + do
在主谓宾补结构中,宾语过长,要用it作形式宾语,真正的宾语放在后面
==regret 遗憾to do 后悔doing==


whose 后面没有the的 用whose

whose用于 “…的” 作定语从句的定语
前名 后名 中变的 能够连上为whose
I know the girl名词 whose mother名词 is a teacher

前面有if不考虑to do If kept in the refrigerator, … 不是人 就被动
考时态语态 找by或虚拟 没有的要看句意 主动/被动
==No sooner…than
Hardly…when
It is time (should) do==

can not help doing sth fell like doing sth be worth doing sth

有逗号 有连词,doing done

have 表示有事要做… 主动表被动have sth to do;表示让/使 have sth done…
Susan, I’d like to have these materials printed by ten o’clock
regret to do/doing 遗憾没做todo 后悔做了doing
光秃秃放在前面 首先考虑todo To became a club member

感官类+实义类 4个词 todo剩下 doing正在进行 ed被动 都可

==感官类/使役类动词 后面是sth 直接接done==
感官/使役类动词 可以省略to
目的状语放在后面的时候 中间不能有逗号
作宾语补足语 ==find leave get keep 三个都可以跟,但跟to do后面的to不能省略==
see/watch/notice/observe/look at/listen to/feel/==make/let/have==使役类 后面均可其余的都是 to do

The president promised to keep all the board members informed of how the negotiations were going on.

While尽管 => 1.对比两者之间while 2.让步状语从句 尽管既然while 3.跟when和as区别 表转折
though 表转折 虽然,尽管
Now that 既然,由于

有逗号 无连词 有主语 用非谓语
遗憾通知你(to do) regret to do 没做、regret doing 做过 inform sb of sth 其余全是通知的意思
形式宾语 Our online service宾语 makes it

but for 虚拟 公主 wcms+do

before介词 后面接 动名词
You have to work in this position for two years before being promoted

consider doing 考虑做… 直接跟在动词后面的 直接选doing
Have you ever considered joining

want to do + 被动
want the construction of the classroom building to be completed by the time school starts in August.

前面有名词 先考虑定语从句
One kind of vacation that many American enjoy is camping

unless 时间 until 条件 两个连词首选


found是建立的原型 find是找到的原型
3.wish虚拟 wcms + do
I certainly wish he would teach me how.

4.后面不完整 介词+whom
there wasn’t a single person to whom she could turn for help.

7.不缺 关系副词 when where why 和 可省;缺的话直接连; 前面没宾语
Our school is no longer what it was 10 years ago, when it was not well equipped

8.idea 后面没主语 idea和后面的连不上 语法和句意连不上
Do you have any idea what is actually going on in the classroom?

10.定语前置 ing修饰物 ed修饰人 Mr Smith,tired of the boring speech
Mr Smith, tired of the boring speech, started to read a novel.

12.salary 月/工资
Some famous singers live on the income from their record sales.

13.affair事件 make sense 有意义
In my opinion, what he told us just now about the affair simply doesn’t take any sense

14.lost(被动)没找到不知道在哪 forgotte完全忘了这件事 miss失踪(主动) left知道落下哪
I can’t find my watch. I must have left it in the hotel.

15.街道有两个边 后面谓语动词是is所以Both不对 Neither不对(不是没边)
Either side of the street is lied with different shops, all of which sell electronic products.

17.有比较关系
There were two boys in the lab, the cleverer of whom did the experiment successfully.

18.找虚拟 建议 should do prevent sb from doing forbid sb to do sth
It is strongly suggested that measures be taken to forbid students to cheat in the exams.

19.几个冠词几个人 is 一个 前面有the冠词 就不填
The poet and 不填 pianist is going to give us a talk this afternoon.

20.insisted 过去用真实 没发生用虚拟
He denied having stolen these watches and insisted that he be set free at once.


1.The watch and chain is made of gold
黄油面包 战争和平 手表和表链 都是一件事

4.Taking exercise early in the morning has become part of his retired life
动词不定式/动名词 作主语

6.Where do you live? I live at 105, Stone Street
not unless/until => didn’t -> until 其余是 unless
街道on 城市区域in 门牌号at

7.Tom does speak Chinese well, but his reading and writing skills leave much to be improved
大多是done 有待提高(提高,改变,增加,解决,促销,解决 好的趋势走的) 这件事亟待解决 => 小心将来时,只要没告诉正在做… 就ed

8.The crazy fans had been waiting patiently for two hours, and they would wait to the movie star arrived.
将来完成,完成进行,将来正在进行成为答案的几率较大

9.In any case无论如何,After all毕竟,As a result结果是,In this way用这种方式

10.If we work with a strong will, we can overcome any difficulty, howerer great形容词 it is.
what how不在定语从句里,都是名词性从句。定语从句/状语从句 有逗号在前面 ==however + adj==

12.She likes to eat vegetables and she has a nice vegetable garden in front of her house.
零食+蔬菜+面条 单独使用用复数

13.Hardly had they got to the bus stop when the bus suddenly pulled away.
Hardly had sb do

17.What the doctor is uncertain about is whether my mother will recover from the serious disease soon.
缺不缺 疑不疑,=> 有疑 remind sb of sth

  1. Excuse me!
    Yes ?
    How can I get to the nearest post office?
    情景对话 考中英文差距
    pardon 再说一遍 Yes?咋啦 What’s wrong?

一般中文翻译对的 英文的含义就是错的,中文翻译出来的不选


  1. The man (being) used to getting up early had前面非谓语 his leg broken.
      A. getting B. get C. got D. have got
                used to do/ used to doing 带入句意

  2. The TV programme has ______on the children as ______ as the old.
      A. a good effect; good(实际性)  B. a positive effect; well (习惯性搭配)
                C. a good affect; well  D. affect; well

  3. –He____ to the meeting. Have you informed him of it?  
    –Sorry. I to. [忘了]
      A. hasn’t come; am going  B. didn’t come; have forgotten
      C. hasn’t come; forgot   D. doesn’t come; will have

  4. –Did you look up the time of trains to Shanghai?
     –Yes, the earliest train is ___ to leave at 5:30 am.
            A. likely B. about [be about to立刻马上出现 后面不跟时间]  
           C. possible[不可能成为答案] D. due
          be likely to有可能 如果考对话 要看完 完整对话

  5. Everyone was on time for the meeting ___Chris, who’s usually ten minutes late for everything.
      A. but B. only  C. even甚至 D. yet副词修饰整个句子,放在前面表强调,或者放在屁股后面

  > 介词查缺补漏:
  > 你是我不是except:你们是学生,我是老师
  > except(全能)[鹤立鸡群]:你跟我们是不一样的 我们都是30岁以下 你36;别人都不了解你,只有我了解你
  > except for:except for由于有介词for,故后面只能接名词/动名词或名词性短语
  > except that:except that 只可惜 帽帽词,后面跟句子;除了(句子)他不会生气,他都会
  > beside:是介词,有两个意思,“在…旁边”常用来表示位置和方向。和“与…相比”
  > besides:我是你也是 除了我以外,还有别人喜欢你 表示除……外(还有),是包括在内的意思【加法的概念】
  > but:后面动词 to do;前面有动词 去掉to;but前面必须是(不定/人称/不定)代词 I have nothing but
  
6. She was afraid ___ the dog in case it became dangerous.
A. of exciting B. to excite C. that she excited D. to be exciting

be afraid to do sth 害怕做某事
be afraid of/that 表担忧,担心、语气较弱

  1. Collecting主语 stamps as a hobby___________ increasingly popular during the past fifty years.
    A. becomes B. became C. has become D. had become
    单数 复数 时态
    一被二单三时态
    动词做主语 => 用单数
    recently / the past… / since 现在完成时

  2. I would have虚拟,后面是真实的 come to see you earlier, but I ___ too busy.
    A. had been B. were C. was D. would be
    if 小心错综 虚拟后面小心but,but后面是真实的…

  3. –Hi, Tracy, you look tired.
    –I am tired. I ___ the living-room all day.
    A.painted B. have been painting完成进行 C. had painted现在完成 D. have painted
    对话要看完整

  4. My uncle___________ until he was forty-five.
    A.married B. didn’t marry C.was not marrying D. would marry
    直到…才做了一件什么什么事 => did

  5. I know nothing about the young lady ___________ she is from Beijing.
    A. except B. except for C. except that D. besides

    你是我不是except:你们是学生,我是老师
    except(全能)[鹤立鸡群]:你跟我们是不一样的 我们都是30岁以下 你36;别人都不了解你,只有我了解你
    except for:except for由于有介词for,故后面只能接名词/动名词或名词性短语
    except that:except that 只可惜 帽帽词,后面跟句子;除了(句子)他不会生气,他都会

  6. .She suggested暗示 to the police in the police station that ___.
    A. Mr. Smith stole the necklace B. Mr. Smith should stealshould可省 the necklace
    C. Mr. Smith had stolen the necklace D. Mr. Smith steal the necklace
    ==1234== suggest暗示 过去完成 insist

suggestion、advice、request、order等意为“建议,命令,要求”的名词后,同位语从句中的谓语动词通常用”should+动词原形”的虚拟语气结构,句中的should可以省略。==demand、proposal、advice、suggestion唯独定语从句不能用虚拟该用啥用啥连字诀;若词后的是同位语从句,则 should + do,==

==insist== 表示坚持,有两种情况,一是”坚决要求虚拟“;二是”坚持说“
==suggest== 一是”建议虚拟“;二是”陈述事实”

  1. _________good, the food was soon sold out.
    A. Tasted B. Being tasted C. Tasting D. Having tasted
    ==感官动词== => 连系动词相当于(be)无被动
    能做连系动词的 感官类动词 => 前面加be 带入查看。
    连系动词没有被动,只有主动格式
  2. –I’d like to have some lunch but I have to stay here doing my work.
    –______what you want and I can get it for you.
    A. To tell me B. Telling me C. If you tell me D. Tell me
  3. The wrong you’ve done to him is terrible, for___ you should make an apology to him, I think.
    A. this B. which C. what D. that
  4. –Excuse me, I want to have my watch fixed, but I can’t find a repair shop.
    –I know_____nearby. Come on, I’ll show you.
    A. one特指前面那家 B. it C. some D. that
  5. –The two pairs of shoes are _ the same colour.–But they are different ___ colour.
    A. of; from B. of; in C. in; from D. in; in
    在哪方面不同in
    be of + 名词 = 形容词
  6. The reason __ their failure you know is ___ they didn’t get fully prepared for the experiment.
    A. why.., that B. that.., why C. for.., that D. why.., because
    不是句子不能选A
  7. All the preparations______, we’re ready to start.
    A. made B. being made C. having made D. have been made
    准备被做
  8. The manager has to get all these washing machines ___.
    A. to deliver B. be delivered C. delivered D. delivering
    get sth done

can not be + adj = 最高级

6.He believes that Nigerians are one of the most hard-working people on earth.
one of 后面加最高级

8.CR sent humanitatian aid to those affected by the earthquake in Syria.
谓语动词 sent by被动 不是affected就是to be affected 被动完成ed 选affected

9.It was reported that Jackson turned down a very good job offer.
It is reported…

10.Terry held a great many roles in his lifetime.
修饰名词 名词放在最后 many a意思是许多,后面有a冠词 冠词后面跟单数 => many a role
a great many = a lot of

12.I got shouted at by me neighbor for parking my car in front of her house.
by 被动 => shouted at

13.The bag was snatched by the monkey and taken to a ten-meter-high tree.
数词连字符,形容词,全部用连字符都连起来。

14.If disturbed, the bird may abandon the nest, leaving the baby birds to die.
因为所以 往往都是过去的,里面有may 可能是 是个条件 选A

15.人称代词 你的BD B形容词性物主代词 + 名词

介词 + ing / adj

you have a huge advantage可数名词,不裸奔=>冠词 in life


2.The amount of money needed for the seriously sick boy was soon collected.
非谓语考点 先找谓语 was collected 主语是The amount of money;(which后面少了个be => which was needed)

3.It has become the talk of the town.
变成城市里的热门话题 of后面永远是特指the of前面有可能是定冠词 有可能是不定冠词

5.He has been out of work since a month ago.
since现在完成时 前面要用延续性动词 lost是结束性动词(×)

6.The movie anything but boring; it is, in fact, rather exciting and interesting.
is 后面不能有all

anything but 一点也不 (否定)
nothing but 只有,只不过是… (肯定=>双否表肯)
no more只是 no more than 不再…
all but 几乎,差一点

7.It is only when you nearly lose someone that you fully realize how much you value him.
帽帽第一(50%) 连词第二(30%) 强调句

8.Shall Mary come and play with us? => No, unless she has finished her homework.
情景对话 unless首先考虑

9.The plan broke down just because people were unwilling to cooperate.
broke down失败、坏了 pulled down推倒、拉倒 turned down拒绝 put down放下

10.Travelers are reminded that they should bring their ID cards with them.
时态语态 没有by或虚拟 没有主将从现 硬看 一被二单三时态 => 被提醒 后面should do 前面要一般式 [考时态的时候had been done过去的过去错误的几率大]

11.”Who Moved My Cheese?”, which is a best-selling book, is written by Spencer Johnson.
有逗号 不可能是名词性从句 => 定语从句/状语从句(只能用ever系列) when是时间状语从句 其余的都要ever(however…)
D.what只能引导名词性从句 ==非限== 不能用that~

12.Show me your permit, please. –Oh, it’s not in my pocket. It(must have fallen out)
情态动词 已经掉出去了 过去式(should have fallen out)/(must have fallen out) 掉出去了是==推测== => 推测肯定的

13.If you want to see a doctor, fix the date with him ahead of time. This is a common custom in the USA.
habit个人行为举止的习惯 custom习俗惯例急诊需要预约

14.Peter won’t drive us to the station. He has too small a car to take us all.
too和such的用法 装不下 too/so+形容词+主语+谓语

15.To save time and labor, cartoonists generally draw the hands of their characters with only 3 fingers and a thumb.
非谓语动词 有逗号没连词没主语前面 光秃秃 先考虑To do =>为了节省劳动力
前面有连词的话 就ing

16.Which one can I take? –You can take all of them; I’ll keep none不保留.
三个或三个以上 肯定句不用any any用于否定句和疑问

17.at one’s own expense 花钱

18.I told you肯定 that you shouldn’t waste your time playing the computer games, didn’t I
反义疑问句:前肯后否 前否后肯 你主我主 你助我助 否定前置(我认为,我们认为) I told you肯 told助 => didn’t I

19.I live at 123, King Street
小到门牌号用at

20.Don’t be joking. It’s time to take up your business www.baidu.com
take up开始做某事 set out to do sth; go on to do/doing;go on with+名词;get down to do sth


9.表示时间的多久之后 in/after/later
若句子所在将来时=> In(+段时间)(随时间推移时间事件发生变化) / after(+点时间)(不会随着时间的推移而变化)(4.23日解封)
Three days later After three days

11.修饰人口的 large /

12.不是虚拟的就是推测的 must不对

13.谓语动词跟your brother保持一致 就近原则 以you为准

14.否定词提前的倒装 would -> did D

15.struck match


1.The woman over there is Julia and Shelley’s mother
俩人共有 is 一个 A 几个撇号s 就是几个

2.When he took his gloves off, I noticed that each one had his name written inside.
后面是one goloves手套两个脱下来 个体 两个 A

3.It was a difficult job for him. He had tried everthing but it made little difference
固定搭配 made little difference没有进展 make get take have 固定搭配

4.competition 竞争/竞赛

5.The teacher praised me for the progress I’d made, which was a great encouragement to me.
冠词 特指取得的进步 一种鼓励 抽象概念具体化 B

6.In a word总而言之 In general总体来讲 in particular特别的

7.I wonder why Jenny hasn’t written us recently. We should have heared from her by now.
I wonder 现在 现在完成时

8.I have no objection to spending the evening with them.
object to doing

12.五分之一 one in five,

13.I told your friend how to get to the hotel, but perhaps I should have driven her there.
情态动词 不是虚拟的 就是推测的 没送 虚拟 B, 我应该送他去 D

14.But for your timely warning, we would have been unaware of the danger.
would have done 虚拟 => but for虚拟

16.Seldom did I make any mistakes during my past few years of working here.
seldom 倒装 past few years 过去式 B

18.He’s working hard for fear that he may fall behind
for fear that 虚拟 should + 动词原形,should可以省略

19.Although Anne is happy with her success she wonders what will happen to her private life
先后顺序it 帽帽 连词 其他;前动(what)后动


2.Who are those with the flags? // A group calling it self the League for Peace 【对话基本都有坑】

4.I’d like Jane, rather than Joan, to go to the farewell party on behalf of our class
would like A rather than B else to do sth 我宁愿A而不是B去做这件事
perfer A to B

11.lose heart 失去信心 lose one’s heart 爱上某人

13.for fear that虚拟后面跟公主

14.过去式/过去完成时(90%) go out 出去/熄灭

15.We will take whoerer wants to go there for a sight-seeing 泛指带那些想要去观光的人去观光 不是特指


need doing 主动表被动
5.农场farm 用on
6.other 后名词复数 one and the others
7.commit犯罪 comprehension理解 cooperation合作 compromise许诺
affair公共事件 variety种类 matter事件 a matter of …的角度
by the time 考点
less 是 little的比较级
few 修饰可数名词
little 修饰不可数
any 否定 疑问
some 肯定
whether or
国家名称不加冠词
doubt 否疑 that
单独用 后面 whether


对话里出现的同一个事物时 第一个出现时用a 再次出现时用the
lesson one / the first lesson
…at Gate 21 /21St Gate / the Gate 21 / 21 Gate
I beg your pardon 再说一遍?
gave in 屈服 give up 放弃

…times + more + 名词 + than
倍数关系+完整的比较级

独立主格结构

With + 名词 + ed(被动)/ing/to do(要做没做)/adj/adv/pron
有逗号没连词 句子开头是with 有主语

答案里出现do的原型 90%是错的 (used to do)
若前面的动词是do 后面也是do (5%概率题目)


1….. the news came as a shock to us
news 前面特指 抽象概念具体化

3.refused to accept any of the three suggestions…
代词 three

4.Do you mind if I smoke here? No, Go ahead 不介意,随意
Go ahead随意 No,I mind 不介意,我介意

5.What he has done is far from satisfactory
far from一点也不副词短语(修饰形容词 动词)

8.Do you regret paying 500 dollars for your necklace? No,I would gladly pay twice as much for it
不管多少钱都是不可数 用钱 much 永远是

12.look! Here. Oh, yes, here comes the bus…it comes 名到 代不倒

15.on purpose aim at by chance by design故意的、有意的

16.帽帽 后面缺不缺? 缺了事和人 that which 由于先行词 是人和物 that

17.情态动词动词原形 不是真实的就是推测的 够我们喝了 推测 所以A×陪葬 BD捆绑 C√ 两个人喝20瓶 够喝了

18.人mean todo 物mean doing 一旦答案里出to do 和 doing,do一般带那个词原意才可以除非动词就是do 不能轻易出现

19.but todo 但后面有动词原形to 则to要省略

20.do不能随便选啊!!!


1.主谓一致 there be !! 不会有there have 一双鞋以双为准 单数 Here is a new pair of shoes

2.Your coffee smells great! would you like itsome 指的是同一个!

3.生活开销 living expenses; at my own expense cover bills charge 多少钱 for sth

4.具体专业不加冠词 my major medicine she became a writer

6.your husband who is to blame

主动表被动 谓语动词主动表被动 开始结束 只要不强调被谁开始被谁结束 都是主动表被动 不及物动词没有被动格式
主动表被动
不定式 to blame,to let
be+形容词+to do
in charge of 负责
in the charge of 由…负责
“on”
“under+名词”

9.It all depends 看情况吧

13.如果有两顶帽帽 that先行词 which后置定语

14.出现我们认为 we think 先砍掉 看别的

16.house whose windows 前名后名中变的

17.view 从高处往地处 往远处看的风景

20.charge for收费 change for交换 claim宣称


1.Both sides have accused the other of breaking the contract 只有两方时候 指对方用the other

2.At the end of the cross-talk, the audience特指 present in the hall burst into a laughter

15.强调搭配 后面句子完整(主谓宾完整) 前面有介词

16,.主句是过去 时态全部往前推一档 主句 had hoped that John (would spend) a year in….he stayede

17.定语从句 能连上

19.一枪二定三名次 定!! he gave the reason 能连上 不是which 就是 that;Do you think the reason which he gave is believable ?

20.主语从句 缺不缺疑不疑 AD It is because / that is because


时态语态 一单二被三时态
6.Now then .children, It’s time过去式 you (were washed and dressed) … 不及物动词没有被动 如果是及物动词必须跟宾语没有宾语必须被动

9.remain不及物动词 + 介词 + 名词 若是及物动词后面一定是名词

10.not so as 和…不一样 such后一般加名词 so形容词as as形容词as

13.sb run out of sth 正在进行表将来

15.Did you enjoy yourself yesterday? Yes. As you saw, the party went on in a most一种 pleasant atmosphere 最高级95%以上全部都是定冠词the

17.情景对话的回答者 一般不用强调 ….. —It was in the hotel where he stayed.

19.Neither双方 of them knew what the other was doing 指的双方—>对方

阅读全文

信息安全、新一代信息技术

2023/1/19
信息安全的含义

==信息安全是指信息网络的硬件、软件及其系统中的数据受到保护,不受偶然的或者恶意的原因而遭到破坏、更改和泄露,系统连续、可靠、正常地运行,信息服务不中断。==

信息安全的基本属性:其中机密性,完整性,可用性成为信息安全的金三角CIA
信息有:依附性、普遍性、价值性、时效性、共享性
信息的采集、存储、发布、处理

机密性、完整性、不可抵赖性、可用性、可控性

机密性:防侦收、防辐射、信息加密、物理保密、信息隐形
完整性
主要因素:设备故障、误码、人为攻击、计算机病毒等
主要保护方法:协议、纠错编码方法、密码校验和方法、数字签名 、公让竺

不可抵赖性(Non-repudiation) : 也称抗抵赖性,在网络环境中,信息交换的双方不能否认其在交换过程中发送信息或接收信息的行为。
可用性:信息的可用性涉及面广硬件可用性软件 可用性人员 可用性环境 可用性:主要是自然环境和电磁环境

鉴于信息安全的上述特点,信息安全的目标可以归纳为下列几点。
①**真实性:对信息的来源进行判断,能够对伪造来源的信息进行鉴别。
保密性:保证机密信息不被窃听,或窃听者不能了解信息的真实含义。
完整性:保证数据的一致性,防止数据被非法用户篡改。
可用性:保证合法用户对信息和资源的使用不会被不正当地拒绝。
不可抵赖性:建立有效的责任机制,防止用户否认其行为,这一点在电子商务中是极其重要的。
可控制性:对信息的传播及内容具有控制能力。
可审查性:**对出现的网络安全问题提供调查的依据和手段。

信息威胁产生的原因:
系统漏洞、协议的开放性、人为因素(最主要的)
1.人为的偶然失误 2.计算机犯罪 3.黑客攻击

信息安全策略
信息安全策略是指为保证提供一定级别的安全保护所必须遵守的规则。要实现信息安就要针对信息安全的主要威胁和信息安全所涉及的主要问题,不但从技术上,更应该靠严的安全管理、法律约束安全教育及信息安全服务。信息安全服务是指做好对信息系统的咨询、集成、监理、测评、认证、运维、审计、培训和风险评估、容灾备份和应急响应工作。风险评估有定量定性评估

@@ Internet设计之初,并未充分考虑到网络安全问题。SSH,FTPS,HTTPS,VPN采用加密

网络具有虚拟性匿名性
计算机及网络在给人类带来极大便利的同时,也不可避免地引发了一系列新的社会问题。
因此,**有必要建立和调整相应的社会行为礼仪,道德规范和相应的法律制度,从伦理和法制两方面约束人们在计算机使用中的行为。**

底线原则:无害原则、公正原则、平等原则、互利原则
自律原则:自尊原则、自主原则、慎独原则、诚信原则

计算机理论

信息伦理学与计算机伦理学、网络伦理学虽具有密切的关系,但信息伦理学不完全等同于计算机伦理学或网络伦理学,信息伦理学有着更广阔的研究范围,涵盖了后两者的研究范围。
计算机伦理与网络伦理的研究范围既有相似、重合的地方,又有不同之处。
计算机理论:隐私保护,计算机犯罪,知识产权,软件盗版,病毒,黑客,行业行为规范
网络理论:虚信息的散布、信息安全问题[国家安全问题,隐私侵犯]、不良信息的充斥、网络知识产权的侵犯、网络游戏挑战理论极限
计算机软件分为商品软件、共享软件、自由软件和公有软件

计算机病毒定义

计算机病毒是隐藏在计算机系统的数据资源中,利用系统资源进行繁殖并生存,能够影响计算机系统正常运行并通过系统数据资源共享的途径进行传染的程序。

“计算机病毒”不是天然存在的,而是人故意编制的一种特殊的计算机程序。特征:==感染性、流行性、繁殖性、变种性、潜伏性、针对性、表现性。==

==计算机病毒代码的结构一般来说包括3大功能模块:==
引导模块: 引导模块将病毒由外存引入内存,使后两个模块处于活动状态。
传染模块: 传染模块显然用来将病毒传染到其它对象上去
破坏模块: 破坏模块实施病毒的破坏作用,如删除文件,格式化磁盘等,由于有些病毒的该模块并没有 明显的恶意破坏作用,而只是进行一些视屏或声方面的自我表现作用,故该模块有时又称表现模块

根据病毒存在的媒体,病毒可以划分为: 网络病毒、文件病毒和引导型病毒
网络病毒通过计算机网络传播感染网络中的可执行文件
文件病毒感染计算机中的文件(如:com, exe, doc等)
引导型病毒感染启动扇区(Boot)和硬盘的系统引导扇区(MBR)

还有这三种情况的混合型病毒,例如:多型病毒(文件和引导型)感染文件和引导扇区两种目标,这样的病毒通常都具有复杂的算法,它们使用非常规的办法侵入系统,同时使用了加密和变形算法
宏病毒:破坏Excel、PPT、word

计算机病毒传染的过程是这样的:病毒从带毒载体进入内存,一般==利用操作系统的加载机制或引导机制==。当系统运行一个带毒文件或用一带毒系统盘启动时,病毒就进入内存。而从RAM侵入无毒介质则利用了操作系统的读写磁盘中断或加载机制。

破坏程度:

良性病毒[占用大量CPU资源]
恶性病毒[破坏资源]

驻留内存:

驻留型病毒[启动后,一直占用内存资源]
非驻留型病毒[运行时,才会进入内存]

计算机病毒传播途径
通过软盘: 通过使用外界被感染的软盘进行传播。
通过光盘: 一些软件在写入光盘前就已经被病毒感染,这种带毒的光盘也是病毒传播的一种途径。
通过硬盘: 通过使用带有病毒的计算机,将干净的软盘或光盘感染再进一步扩散。
通过网络: 通过网络传播病毒是目前病毒传播的一种主要途径。其传播速度快,传播范围广,给防范计算机病毒带来严峻的挑战

病毒的防治:

安装防,杀,反病毒工具;养成良好的习惯;自觉培养信息安全意识;注意自己的操作系统;将受病毒侵害的计算机尽快隔离;安装个人版防火墙;注意在没有防病毒软件的计算机上,尽量不使用移动存储设备

@@ 设置较复杂的口令(包括系统、应用)可以大大减少计算机病毒带来的风险

密码技术

下面是密码学中一些常用的术语:
1)明文(Plaintext/Message):指待加密的信息,用P或M表示。日文可以是文本文件、图形、数字化存储的语音流或数字化的视频图像的比特流等。
(2)密文(Cipertext):指明文经过加密处理后的形式,用C表示。
(3)加密(Encryption):指用某种方法伪装消息以隐藏它的内容的过程。
(4)加密算法(Encryption Algorithm) :指将明文变换为密文的变函数,通常用E表示。
(5)解密(Decryption) :指把密支转换成明文的过程。
(6)解密算法(Decryption Algorithm):指将密文变换为明文的变换函数,通常用D表示。
(7)
(密钞(Key
) :变换函数所用的一个控制参数。加密和解密算法的操作通常是在一组密钥控制下进行的,分别称为加密密钥和解密密钥,通常用K表示。
(8)密码分析(Cryptanalysis) :指截获密文者试图通过分析截获的密文从而推断出原来的明文或密钥的过程。

古典密码:替代[全加n]、置换

==对称密码体制:==
对称密码体制(Symmetric Encryption),如果一个密码算法的加密密钥和解密密钥相同,或由其中一个很容易推导出另一个,该算法就是对称密码算法。
(1)优点:加密、解密处理速度快,保密度高等。
(2)缺点:
①密钥是保密通信安全的关键,发信方必须安全、
妥善地把密钥护送
到收信方,不能泄露其内容。如何才能把密钥安全地送到收信方,是对称密码算法的突出问题。对称密码算法的密钥分发过程复杂,所花代价高;
②多人通信时密钥组合的数量会出现爆炸性膨胀,使密钥分发更加复杂化,若有个用户进行两两通信,总共需要的密钥数为个;
③通信双方必须统一密钥,才能发送保密的信息。如果发信者与收信人素不相识,这就无法向对方发送秘密信息了。
④除了密钥管理与分发问题,对称密码算法还存在数字签名困难问题(通信双方拥有同样的消息,接收方可以伪造签名,发送方也可以否
认发送过某消息)。

==非对称密码体制:==

加密密钥是公开的,故称为公钥(public key)
解密密钥是秘密的,故称为私钥(private key)
具有鉴别功能

优点:网络中的每一个用户只需要保存自己的私钥个用户仅需产生对密钥。密钥少,便于管理;密钥分配简单,不需要秘密的通道和复杂的协议来传送密钥。公钥可基于公开的渠道(如密钥分发中心)分发给其他用户,而私钥则由用户自己保管;可以实现数字签名。

缺点:与对称密码体制相比,非对称密码体制的加密、解密处理速度较慢同等安全强度下密钥位数要求多一些。

非对称秘钥应用场景
==数字签名:[防伪造、防抵赖]==
数宇签名是为了表明信息没有受到伪造,确实是信息拥有者发出来的,附在信息原文的后面。就像手写的签名一样,具有不可抵赖性简洁性数字签名是掌握非对称加密,并非纸质签名!!

非对称秘钥应用场景
信息加密:
消息发送方用公钥加密消息,接收方用私钥解密。

对称密码体制加密解密使用相同的密钥;非对称密码体制的加密解密使用不相同的密钥,而且加密密钥和解密密钥要求不能相互推算。

密钥(Key) :变换函数所用的一个控制参数。加密和解密算法的操作通常是在一组密钥控制下进行的
@@ ==数字签名可以做到防止发送方抵赖和接收方伪造==;数字签名(公钥数字签名、电子签章)是一种类似卸载至上的普通的物理签名,但使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算。数字签名的作用:保证信息传输的完整性、发送者的身份认证、防止交易中的抵赖发生

防火墙技术

防火墙(Firewall)是设置在不同网络(如可信任的企业内部网和不可信的公共网)或网络安全域之间的一系列部件的组合。
==防火墙可以记录Internet活动并防止内部信息泄露[日志可以记录操作]==

(1)“隔离”:在不同信任级别的网络之间砌”墙”;
(2)“访问控制”:在墙上开“门”并派驻守卫,按照安全策略来进行检杳信息安全、新和放通

基础组网和防护功能
防火墙可以限制非法用户进入内部网络,比如黑客、网络破坏者等,禁止存在安全脆弱性的服务和未授权的通信数据包进出网络,并对抗各种攻击。

记录监控网络存取与访问
防火墙可以收集关于系统和网络使用和误用的信息并做出日志记录。通过防火墙可以很方便地监视网络的安全性,并在异常时给出报警提示。

限定内部用户访问特殊站点
防火墙通过用户身份认证来确定合法用户,并通过事先确定的完全检查策略,来决定内部用户可以使用的服务,以及可以访问的网站。

限制暴露用户点
利用防火墙对内部网络的划分,可实现网络中网段的隔离,防止影响一个网段的问题通过整个网络传播,从而限制了局部重点或敏感网络安全回题对全局网络造成的影响。

网络地址转换
防火墙可以作为部署NAT的逻辑地址,来缓解地址空间短缺的问题,并消除在变换ISP时带来的重新编址的麻烦。

虚拟专用网
防火墙支持具有Internet服务特性的企业内部网络技术体系虚拟专用网络(Virtual Private Network, VPN)。

防火墙的不足是:
①不能防范不经过防火墙的攻击。
②不能防止来自网络内部的攻击和安全问题。
③由于防火墙性能上的限制,因此它通常不具备实时监控入侵的能力。
④不能防止策略配置不当或错误配置引起的安全威胁。
⑤不能防止受病毒感染的文件的传输。
⑥不能防止利用服务器系统和网络协议漏洞所进行的攻击。
⑦不能防止数据驱动式的攻击。
⑧不能防止内部的泄密行为。
⑨不能防止本身的安全漏洞的威胁。
⑩无法准确做到按内容过滤,无法检测加密的流量。

@@ 防火墙是一种保护计算机网络安全的技术性措施,是一个用以控制进/出两个方向通信的门槛

@@ 防火墙对数据包进行状态监测包过滤,==不可以进行过滤的是数据包中的内容,可以过滤源和目的IP地址、源和目的端口、IP协议号==

@@ 防火墙中地址翻译的主要作用是隐藏内部网络地址

@@ 防火墙包括硬件+软件的技术

反病毒技术

反病毒概念:
反病毒是一种安全机制,它可以通过识别和处理病毒文件来保证网络安全,避免由病毒文件而引起的数据破坏、权限更改和系统崩溃等情况的发生。

计算机病毒的防治技术分成三个方面:==病毒预防技术、病毒检测技术、病毒消除技术==

计算机病毒的预防技术就是通过一定的技术手段防止计算机病毒对系统的传染和破坏。

防火墙:个人防火墙


新一代信息技术[0-5分] [非重要题型]

新在哪里?[云计算、大数据、物联网、人工智能、区块链、人工智能 ]

信息与物质、能量是客观世界的三大构成要素
信息是事物运动状态和特征的反映

信息技术(Information Technology, 简称IT) 指的是用来扩展人们信息器官功能、协助人们更有效地进行信息处理的一门技术。

[扩展感觉器官功能的感测(获取)技术、扩展神经系统功能的通信技术、扩展大脑功能的计算与存储技术、扩展神经系统功能的通信技术、扩展效应器官功能的控制与显示技术]

云计算定义 [是==分布式计算、并行计算、网格计算==的一种]

==云计算是一种按使用量付费的模式,这种模式提供可用的便捷的按需的网络访问,进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供,只需投入很少的管理工作,或与服务供应商进行很少的交互。==

云服务已经不单单是一种分布式计算,而是==分布式计算、效用计算、负载均衡、并行计算、网络存储、热备份荣誉和虚拟化==等计算机技术混合演进并跃升的结果

云计算是一种通过Internet以服务的方式提供动态可伸缩的虚拟化资源的计算模式,使人们像用电一样享用信息的应用和服务。
大型机时代 → PC时代 → 云计算时代

==云计算解决方案带来简单化平台化有服务化==

特征:自助式服务、随时随地使用、可度量的服务、快速资源扩缩、资源池化

云计算技术原理

使用特定的软件、按照指定的优先级调度算法、将数据计算和数据存储分配到云计算集群中的各个节点计算机上,节点计算机并行运算,处理存储在本节点上的数据,结果回收合并。

云计算服务模式
==IaaS核心技术 基础设施即服务==[存储服务]

虚拟化技术、分布式存储技术、高速网络技术、超大规模资源管理技术、云服务计费技术
[一台电脑当做N台来用] [虚拟化技术服务器整合]
虚拟化就是将多台低利用率的服务器上的负载整合到一台服务器上,使服务器硬件资源的利用率尽可能提高利用率

亚马逊公司是著名的跨境电商,也是当前世界上最成功的IaaS服务提供者,拥有非常成功AWS云计算服务平台,为全世界范围内的客户提供云解决方案。

AWS面向用户提供包括弹性计算(存储、数据库、应用程序在内的一整套云计算服务,帮助企业降低投入成本和维护成本。
国内类似的有阿里云、腾讯云、华为云等。

==PaaS技术 平台即服务==[向用户提供虚拟数据的操作系统,数据库管理系统,Web应用系统等服务]

REST技术、多租户技术、并行计算技术、应用服务器技术、分布式缓存技术
无处不在的网络接入
类似的PaaS云平台还有谷歌的GAE (GoogleAppEngine),支持Python语言、JAVA语言、Go语言和PHP语言等。
国内的还有云端软件开发协作平台(码云Gitee)、JEPaaS等。
@@ 在云计算服务中,PaaS向用户提供虚拟数据的操作系统,数据库管理系统,Web应用系统等服务

==SaaS核心技术 软件即服务==

① 大规模多租户支持,运行在应用提供商SaaS上的应用能够同时为多个组织和用户使用,能保证用户之间的相互隔离。没有多租户技术的支持,SaaS就不可能实现
② 认证和安全,认证和安全是多租户的必要条件。当接收到用户发出的操作请求时,其发出请求的用户身份需要被认证,且操作的安全性需要被监控。
③ 定价和计费,定价和计费是SaaS模式的客观要求。提供合理、灵活、具体而便于用户选择的定价策略是SaaS成功的关键之一
④ 服务整合,它是SaaS长期发展的动力。SaaS应用提供商需要通过与其他产品的整合来提供整套产品的解决方案。
⑤ 开发和定制,开发和定制是服务整合的内在需要。一般来讲,每个SaaS应用都提供了完备的软件功能,但是为了能够与其他软件产品进行整合,SaaS应用最好具有一定的二次开发功能,包括公开API,提供沙盒以及脚本运行环境等。

@@ 简答:小亮创业开办公司不久,业务数据量激增,他想购买存储设备,但公司人手少、场地小、资金也不充裕。
小亮目前最迫切购买的是哪种服务,并简述理由。
小亮迫切购实的是云计算服务
【解析】云计算可以提供laaS,PaaS, SaaS服务


大数据[不可能处理完的数据]

==大数据数据价值密度低,价值低,冗余大==

大数据指的是所涉及的资料量规模巨大到无法通过主流软件工具,在合理时间内达到管理、处理并整理成为帮助企业经营决策更积极目的的资讯。

随着大数据时代的到来,“大数据” 已经成为互联网信息技术行业的流行词汇。
关于“什么是大数据”这个问题,大家比较认可关于大数据的”5V”说法。大数据的5个”V”,或者说是大数据的4个特点,包含5个层面:
==数据量大(Volume)、数据类型繁多(Variety) 、真实性(Veracity)、处理速度快(Velocity)、价值密度低(Value)==。[大多数都是真的]

1.数据量大
根据IDC作出的估测,数据一直都在以每年50%的速度增长,也就是说每两年就增长一倍(大数据摩尔定律)
人类在最近两年产生的数据量相当于之前产生的全部数据量。预计到2020年,全球将总共拥有35ZB的数据量,相较于2010年,数据量将增长近30倍

2.数据类型繁多[结构不单一 ]
大数据是由结构化和非结构化数据组成的;10%的结构化数据,存储在数据库中;90%的非结构化数据,它们与人类信息密切相关

3.处理速度快
从数据的生成到消耗,时间窗口非常小,可用于生成决策的时间非常少1秒定律:这一点也是和传统的数据挖掘技术有着本质的不同

4.价值密度低
价值密度低人商业价值高以视频为例,连续不间断监控过程中,可能有用的数据仅仅有一两秒,但具目有很高的商业价值

==大 数据的影响==

一、==全样而非抽样==
过去由于数据存储和处理能力的限制,通常采用抽样的方法,来推断全集数据的总体特征。现在、大数据可以对全集数据进行处理。

二、==效率而非精确==
过去、采用抽样的方法,必须保证样本分析的精确性,以避免微小误差放大到全集数据。因此,传统的数据分析,先确保数据的精确性,其次才是提高算法的效率。
现在、大数据具有“秒极响应”特征,要求迅速给出分析结果,否则就会丧失数据的价值。

三、==相关而非因果==
过去、数据分析的目的,一方面是解释事物背后发展的机理;另一方面,用于预测未来可能发生的事情。
在大数据时代,人们追求“相关性”,而非“因果性

大数据 = 数据 + 大数据技术
大数据技术伴随着大数据的采集、存储、分析和应用的相关技术

需要指出的是,大数据技术是许多技术的一个集合体,这些技术也并非全部都是新生事物,诸如关系数据库、数据仓库、数据采集、ETL、OLAP、数据挖掘、数据隐私和安全、数据可视化等技术是已经发展多年的技术,在大数据时代得到不断补充、完善、提高后又有了新的升华,也可以视为大数据技术的一个组成部分。

从数据分析全流程的角度,大数据技术主要包括:

数据采集与预处理、数据存储和管理、数据处理与分析、数据安全和隐私保护等几个层面的内容。

(1)数据采集与预处理
采集到的数据,通常无法直接用于后续的数据分析。[数据的抽取]
因为对于来源众多、类型多样数据而言,数据缺失和语译模糊等问题是不可避免,因而必须采取相应的措施来解决这些问题,这个过程就叫”数据预处理”。

(2)数据存储与管理
利用分布式文件系统、数据仓库、关系数据库、NoSQL数据库、云数据等,实现对结构化、半结构化和非结构化海量数据的存储和管理。

(3)数据处理与分析
利用分布式并行编程模式和计算框架,结合机器学习和数据挖掘算法,实现对海量数据的处理和分析; 对分析结果进行可视化呈现,帮助人们更好地理解数据、分析数据。

(4)数据安全与隐私保护
在从大数据中挖掘潜在的巨大商业价值学术价值的同时,构建隐私数据保护体系和数据安全体系,有效保护个人隐私和数据安全。

(5)大数据计算模式
大数据计算模式,即依据大数据的不同数据特征和计算特征,从多样性的大数据计算问题和需求中提炼并树立的各种高层笼统或模型。依据大数据处置多样性的需求和以上不同的特征维度,目前呈现了多种典型和重要的大数据计算形式。

批处理计算:Spark是一个针对超大数据集合的低延迟的集群分布式计算系统,比MapReduce快许多。Spark启用了内存分布数据集,除了能够提供交互式查询外,还可以优化迭代工作负载。

==批量处理==

==流计算==
流数据也是大数据分析中的重要数据类型。流数据(或数据流)是指在时间分布和数量上无限的一系列动态数据集合体,数据的价值随着时间的流逝而降低,因此必须采用实时计算的方式给出秒级响应。

目前业内已涌现出许多的流计算框架与平台:
第一类是商业级的流计算平台,包括IBM InfoSphere Streams等;
第二类是开源流计算框架,包括Twitter Storm、Yahoo! S4、Spark Streaming等;
第三类是公司为支持自身业务开发的流计算框架,如Facebook使用Puma和HBase相结合来处理实时数据,百度开发了通用实时流数据计算系统DStream,淘宝开发了通用流数据实时计算系统——银河流数据处理平台。

==图计算==
在大数据时代,许多大数据都是以大规模图网络的形式呈现,如社交网络、传染病传播途径、交通事故对路网的影响等,此外,许多非图结构的大数据也常常会被转换为图模型后再进行处理分析。Pregel主要用于图遍历、最短路径、PageRank计算等。其他代表性的图计算产品还包括:
Facebook针对 Pregel的开源实现 Giraph;Spark下的 GraphX; 图数据处理系统PowerGraph等。

大数据应用
大数据产业是指一切与支撑大数据组织管理和价值发现相关的企业经济活动的集合。大数据产业包括IT基础设施层、数据源层、数据管理层、数据分析层、数据平台层和数据应用层。

数据源层:大数据生态圈里的数据提供者,是生物(生物信息学领域的各类研究机构)大数据、交通(交通主管部门)大数据、医疗(各大医院、体检机构)大数据、政府(政府部门)大数据、电商(淘宝、天猫、苏宁云商、京东等电商)大数据、社交网络(微博、微信、人人网等)大数据、搜索引擎(百度、谷歌等)大数据等各种数据的来源。

数据管理层:包括数据抽取、转换、存储和管理等服务的各类企业或产品,如分布式文件系统(如Hadoop的HDFS和谷歌的GFS)、ETL工具(lnformatica、Datastage、Kettle等)、数据库和数据仓库(Oracle、MySQL、SQL Server、HBASE、GreenPlum等)

数据分析层:包括提供分布式计算、数据挖掘、统计分析等服务的各类企业或者产品,如分布式计算框架MapReduce、统计分析软件SPSS和SAS、数据挖掘工具Weka、数据可视化。

数据应用层:提供智能交通、智慧医疗、智慧物流、智能电网等行业应用的企业、机构或政府部门,如交通主管部门、各大医疗机构、菜鸟网络、国家电网等。

大数据(Big Data)、人工智能(Artificial Intelligence)、物联网(lnternet of Things)和区块链(Blockchain)
等新兴技术的融合突破了传统健康医疗应用在数据分析、数据安全和数据采集方面的局限,协同开启了多元化的健康医疗应用市场。


物联网

简单理解:物物相连的互联网,即物联网。
物联网在国际上又称为传感网,万事万物,小到手表、钥匙,大到汽车、楼房,只要嵌入个微型感应芯片,把它变得智能化,这个物体就可以”自动开口说话”。再借助无线网络技术,人们就可以和物体“对话”,物体和物体之间也能”交流”,这就是物联网。
互联网,RFID技术,EPC标准,在计算机互联网的基础上,利用射频识别技术,无线数据通信技术等,构造了一个实现全球物品信息实时共享的实物互联网。

==三大特征全面感知、可靠传递、智能处理==


公认的物联网定义

1.通过射频识别装备,红外传感器,全球定位系统GPS,激光扫描器等信息传感设备,按约定的协议,把任何物品与互联网相连接,进行信息交换和通信,以实现智能化==识别,定位,跟踪,控制和管理==的一种网络。

2.当每个而不是每种物品能够被唯一标识后,利用识别、通信和计算等技术,在互联网基础上,构建的连接各种物品的网络,就是人们常说的物联网

==物联网中的“物”的涵义要满足以下条件才能够被纳入“物联网”的范围:==

要有相应信息的接收器
要有数据传输通路
要有一定的存储功能
要有CPU
要有操作系统
要有专门的应用程序要有数据发送器
遵循物联网的通信协议
在世界网络中有可被识别的唯一编号

物联网的形成与发展

智能家居:
家庭自动化、智能路由、安全监控、智能厨房、家庭机器人、传感检测、智能宠物、智能花园、跟踪设备;
智能交通:
车联网、智能自行车/摩托车(头盔设备)、无人驾驶、无人机、太空探索;
企业应用:
医疗保健.零售、克付/信用卡、智能办公室、现代农业、建筑施工;
产业互联网:
现代制造、能源工业、供应链、工业机器人、工业可穿戴设备(智能安全帽等)

物联网的发展跟互联网是分不开的,主要两个层面的意思:
第一,物联网的核心和基础依然是互联网,他是在互联网的基础上的延伸和扩展;
第二,物联网是比互联网更加庞大的网络,其网络连接延伸到了任何物品与物品之间,这些物品可以通过各种信息传感设备与互联网连接在一起,进行更为复杂的信息交换和通信

物联网有三大特征:全面感知[感知方式(部分)、感知信息(二维码、湿度、温度)];可靠传递智能处理

物联网是典型的交叉学科,它所涉及的核心技术包括IPv6技术、云计算技术、传感技术、RFID智能识别技术、无线通信技术等。
因此,从技术角度讲,物联网专业主要涉及的专业有:计算机科学与工程、电子与电气工程、电子信息与通讯、自动控制、遥感与遥测、精密仪器、电子商务等等。

物联网的层次结构

==应用层==
物联网应用:环境监测、智能电力、智能交通、工业控制 物联网业务中间件
**应用层**类似于人类社会的”分工”包括应用基础设施/中间件和各种物联网应用,应用基础设施/中间件为物联网应用提供信息处理、计算等通用基础服务设施、能力及资源调用接口,以此为基础实现物联网在众多领域的各种应用
应用层的关键技术:云计算技术、软件和算法、信息和隐私安全技术、标识和解析技术

1、云计算
在这里,云计算和物联网进行深度融合。

2、软件和算法
软件和算法在物联网的信忌处理和应用木A十A工云向昵名的物联网智慧性的集中体现。这其中的关键技术主要包括面向服务的
体系架构(SOA)和中间件技术,重点包括各种物联网计算系统的感知信息处理、交互与优化软件与算法、物联网计算系统体系结构与软件平台研发等。
**面向服务的体系架构(**Service-oriented Architecture,SOA)是一种松耦合的软件组件技术,它将应用程序的不同功能模块化,并通过标准化的接口和调用方式联系起来,实现快速可重用的系统开发和部署。中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。

3、信息和隐私安全技术
安全和隐私技术包括安全体系架构、网络安全技术“智能物体”的广泛部署对社会生活带来的安全威胁
隐私保护技术、安全管理机制和保证措施等。
为实现对物联网广泛部署的”智能物体”的管理,需要进行网络功能和适用性分析,开发适合的管理协议。

4、标识和解析技术
是对物理实体、通信实体和应用实体赋予的或其本身固有的一个或-组属性,并能实现正确解析的技术。
物联网的标示主要包括物体标示和通信标示,物联网标识和解析技术涉及不同的标识体系、不同体系的互操作、全球解析或区域解析、标识管理等。


==网络层==
移动通信网、互联网和其他网:下一代承载网、互联网、专网、异构网融合
网络层主要实现信息的传递、路由和控制,包括延伸网、接入网和核心网,网络层可依托公众电信网和互璇网.也可以依托行业专用通信网络。
网络层关键技术:ZigBee、WIFI无线网络、蓝牙技术、GPS技术

1.ZigBee【仿生学
ZigBee技术是一种近距离、低复杂度、低功耗、低速率、低成本的双向无线通讯技术。这一名称来源于蜜蜂的八字舞由于蜜蜂(bee)是靠飞翔和”嗡嗡”(zig)地抖动翅膀的”舞蹈”来与同伴传递花粉所在方位信息,也就是说蜜蜂依靠这样的方式构成了群体中的通信网络。
ZigBee网络主要特点是低功耗、低成本、时延短、网络容量大、可靠、安全。主要适合用于自动控制和远程控制领域可以嵌入各种设备。
ZigBee协调器Coordinator )
ZigBee路由器)( Router)
ZigBee终端设备(End-device)
一个Zigbee网络由一个协调器节点、多个路由器和多个终端设备节点组成。
【家庭、楼宇自动化以及监控类应用】

2.WIFI无线网络
Wi-Fi是一种可以将个人电脑、手持设备(如PDA、手机)等终端以无线方式互相连接的技术。
WIFI突出优势:
其一,无线电波的覆盖范围广
其二,传输速度非常快
其三,厂商进入该领域的门槛比较低

3.蓝牙技术
蓝牙,是一种支持设备短距离通信(-般10m内)的无线电技术。能在包括移动电话、PDA、无线耳机、笔记本电脑、相关外设等众多设备之间进行无线信息交换。

蓝牙应用—蓝牙耳机
① 手机中的解码芯片对MP3等音乐文件进行解码,产生数字信号并通过蓝牙发送给蓝牙耳机;
② 蓝牙耳机接收数字信号,并通过蓝牙耳机内部的数模转换芯片,把它转换成人耳能听懂的模拟信号
③ 将模拟信号进行放大,需要用到耳机内部的信号放大芯片
④ 耳机单元接收放大后的信号并出声音,此时耳朵便听到了音乐声
替代有线:遥感勘测、移动电子商务、数字电子设备、工业控制、智能化建筑、家庭和办公自动化、电子商务、无线公文包、军事

4.GPS技术
GPS (Global Positioning System,全球定位系统)定利用P定位卫星,在全球范围内实时进行定位、导航的系统。全球四大卫星导航系统:美国全球定位系统(GPS)、俄罗斯”格洛纳斯”系统、欧洲”伽利略”系统、中国”北斗”系统

==感知层==
数据处理:地距离和中高速短距离传输技术、自组织组网技术、协同信息处理技术、传感网中间件技术
数据采集:传感器、二维码/条码、RFID、多媒体信息
感知层实现对物理世界的智能感知识别、信息采集处理和自动控制,并通过通信模块将物理实体连接到网络层和应用层
感知层的关键技术:RFID技术、条形码传感器技术、无线传感器网络技术、产品电子码EPC

1.RFID技术【信息的采集识别】
RFID(Radio Frequency ldentification),即射频识别,俗称电子标签。RFID射频识别是一种**非接触式的自动识别技术**,可识别高速运动物体并可同时识别多个标签,操作快捷方便。通过射频信号自动识别对象并获取相关数据完成信息的自动采集工作,RFID是物联网最关键的一个技术,它为物体贴上电子标签,实现高效灵活的管理。

1)标签(Tag):由耦合元件及芯片组成,每个标签具有唯一的电子编码,附着在物体上标识目标对象;
2)阅读器(Reader)或读写器:读取(有时还可以写入)标签信息的设备,可设计为手持式或固定式;
3)天线(Antenna):在标签和读取器间传递射频信号

RFID手持机[快递标签打印机]。读写感应器(冲水卡)。

RFID工作原理
标签进入磁场古、接收解a读器发出的射频信号,凭借感应电流所获得的能量发送出存储在芯片中的产品信息(Passive Tag,无源标签或被动标签),或者主动发送某一频率的信号(Active Tag,有源标签或主动标签);解读器读取信息并解码后,送至中央信息系统进行有关数据处理。

2.条码技术
条形码是一种信息的图形化处理方法,可以把信息复制成条形码,然后用相应的扫描设备将其中信息输入到计算机中

条形码分为一维条码和二维条码:
一维条形码将宽度不等的多个黑条和空白,按一定的编码规则排列,用以表达一组信息的图形标识符。
二维条形码是在二维空间水平和竖直方向存储信息的条形码。它的优点是信息容量大,译码可靠性高,纠错能力强,制作成本低,保密与防伪性能好。

3.传感器技术
传感器是指能感知预定的被测指标并按照一定规律转换成可用信号的器件和装置,通常由敏感元件和转换元件组成。

4.无线传感器技术
无线传感器网络(WSN, wireless sensor network)
多个功能节点之间通过无线通信形成一个连接的网络,这个网经我们称为无线传感器网络。
它是集分布式信息采集、信息传输和信息处理技术于一体的网络信息系统,以其低成本、微型化、低功耗和灵活的组网方式、铺设方式以及适合移动目标等特点受到广泛重视,是关系国民经济发展和国家安全的重要技术。

无线传感器网络中主要包含两类节点:

传感器节点: 具有感知和通信功能的节点,在传感器网络中负责监控标区域并获取数据,以及完成与其他传感器节点的通信,能够对数据进行简单的处理。
Sink节点:又称为基站节点,负责汇总由传感器节点发送过来的数据,并作进一步数据融合以及其他操作,最终把处理好的数据上传至互联网。

无线传感器网络三种常见拓扑结构:

星型拓扑:具有组网简单、成本低; 但网络覆盖范围小,一旦sink节点发生故障,所有与sink节点连接的传感器节点与网络中心的通信都将中断。星形拓扑结构组网时,电池的使用寿命较长。
网状拓扑:具有组网可靠性高、覆盖范围大的优点但电池使用寿命短、管理复杂。
树状拓扑:具有星形和网状拓扑的一些特点,既保证了网络覆盖范围大,同时又不至于电池使用寿命过短,更加灵活、高效。

无线传感器网络的应用领域
1、军事领域的应用
在军事领域,由于WSN具有密集型、随机分布的特点,使其非常适合应用于恶劣的战场环境。利用WSN能够实现监测敌军区域内的兵力和装备、实时监视战场状况、定位目标、监测核攻击或者生物化学
2、辅助农业生产
WSN特别适用于以下方面的生产和科学研究。
例如,大棚种植室内及土壤的温度、湿度、光照监测、珍贵经济作物生长规律分析、葡萄优质育种和生产等,可为农村发展与农民增收带来极大的帮助。采用WSN建设农业环境自动监测系统,用一套网络设备完成风、光、水、电、热和农药等的数据采集和环境控制,可有效提高农业集约化生产程度,提高农业生产种植的科学性。
3、在生态环境监测和预报中的应用
在环境监测和预报方面,无线传感器网络可用于监视农作物灌溉情况、土壤空气情况、家畜和家禽的环境和迁移状况、无线土壤生态学、大面积的地表监测等,可用于行星探测、气象和地理研究、洪水监测等。基于无线传感器网络,可以通过数种传感器来监测降雨量、河水水位和土壤水分,并依此预测山洪爆发描述生态多样性,从而进行动物栖息地生态监测。还可以通过跟踪鸟类、小型动物和昆虫进行种群复杂度的研究等。

4、在医疗系统和健康护理中的应用
无线传感网技术通过连续监测提供丰富的背景货料并做预警响应,不仅有望解决这一问题还可大大提高医疗的质量和效率。无线传感网集合了微电子技术、嵌入式计算技术、现代网络及无线通信和分布式信息处理等技术,能够通过各类集成化的微型传感器协同完成对各种环境或监测对象的信息的实时监测、感知和采集。

5、产品电子代码EPC
EPC系统(物联网)是在计算机互联网和射频技术RFID的基础上,利用**全球统标识系统编码技术给每一个实体对象个唯一的代码**,构造了一个实现全球物品信息实时共享的实物互联网”Internetof things”。

==@@ 物联网技术作为智慧城市建设的重要技术,其架构一般可分为感知层,其中网络层和应用层负责信息采集和物物之间的信息传输。==

物联网应用

  • 智慧物流[存储、运输、快递监测]
  • 智能交通[共享单车、车联网、智能红绿灯、充电桩监测]
  • 智能安防[智能安防系统:门禁、报警、监控]
  • 智慧能源环保[只能井盖监测水位状态、智能水电表实时远程抄表]
  • 智能医疗[医疗设备、用品可视化]
  • 智慧建筑[用电照明、消防监测、智慧电梯、楼宇检测、古建筑领域白蚁监测]
  • 智能制造[工厂机械设备监控、(化工)工厂环境监控 (厂房的环境主要是采集温湿度,烟感)]
  • 智能家居[智能家居系统平台发展]
  • 智能零售[无人售货机、无人便利店]
  • 智慧农业[农业种植、畜牧养殖]

物联网中传感器节点是在传感器基础上增加了协同计算、通信功能构成了具有感知能力、计算能力和通信能力的传感器节点。智能化是传感器的重要特点,嵌入式智能技术是实现传感器智能化的重要手段。

EPC系统主要由如下六方面组成:

(1) EPC编码标准
(2) EPC标签
(3) 识读器
(4) Savant(神经网络软件)
(5) 对象名解析服务(Object Naming Service: ONS)
(6) 实体标记语言(Physical Markup Language PML)

@@ 在物联网的关键技术中,射频识别(RFID)是一种信息采集技术

公共支撑技术

标识解析、安全技术、信息安全、网络管理


人工智能[智能化]

AlphaGo、ChatGPT 阿尔法狗

==人工智能>机器学习>深度学习>神经网络;
主要应用在:智能处理; 图像处理; 机器视觉; 新闻、电影、音乐、购物推荐,人脸检测,机器人,自动驾驶等等。==
人工智能之父图灵

@@ 推动人工智能发展的三要素==数据、算力、算法==推动了AI的发展
@@ 人工智能是一门综合性交叉学科和边缘学科

机器人三定律

第一定律:机器人不得伤害人类个体,或者目睹人类个体将遭受危险而袖手不管
第二定律:机器人必须服从人给予它的命令,当该命令与第一定律冲突时例外
第三定律:机器人在不违反第一、第二定律的情况下要尽可能保护自己的生存

人工智能的概念

人工智能的定义描述包括以下5种:
1、人工智能是不可思议的计算机程序,是机器可以完成人们认为机器不能胜任的事。
2、人工智能是与人类思考方式相似的计算机程序,能够遵照思维里的逻辑规律进行思考。
3、人工智能是与人类行为相似的计算机程序,只要计算机程序的功能表现与人类在类似环境下行为相似则可以认为该程序是该领域的人工智能程序。
4、工智能是会学习的计算机程序,这一定义也符合人类认知的特点,人类的智慧离不开不断的学习。
5、人工智能是根据对环境的感知,做出合理的行动,并获得最大收益的计算机程序。

人工智能分为:“弱人工智能、强人工智能、超人工智能

概念的表示:知识由概念组成,概念是构成人类知识世界的基本单元

机器学习可以分为五个大类:
  • 监督学习(SupervisedLearning):一从给定的训练数据集中学习出一个函数,当新的数据到来时,可以根据这个函数预测结果,动物识别为监督学习。
  • 无监督学习(Unsupervisedlearning) :无监督学习与监督学习相比,训练集没有人为标注的结果
  • 半监督学习(Semi-SupervisedLearning) :这是——种介于监督学习与无监督学习之间的方法。
  • 迁移学习(TransferLearning):将已经训练好的模型参数迁移到新的模型来帮助新模型训练数据集。
  • 增强学习(Reinforcementlearning) :通过观察周围环境来学习。

深度学习(deep learning)中的重要分支一神经网络,或称人工神经网络(artificial neural network,ANN)。
1943年,心理学家McCulloch和数学家Pitts参考了生物神经元的结构,发表了抽象的神经元模型MP

人的大脑细胞约有$10^{14}$个

深度学习所涉及的技术主要有:

线性代数、概率和信息论、欠拟合、过拟合、正则化、最大似然估计和贝叶斯统计、随机梯度下降、监督学习和无监督学习、深度前馈网络、代价函数和反向传播、正则化、稀疏编码和dropout、自适应学习算法、卷积神经网络、循环神经网络、递归神经网络。深度神经网络和深度堆叠网络、LSTM长短时记忆、主成分分析、上自动编码器、表征学习、蒙特卡洛、受限波兹曼机、深度置信网络、softmax回归、决策树和聚类算法、KNN和SVM、生成对抗网络和有向生成网络、机器视觉和图像识别、自然语言处理、语音识别和机器翻译、有限马尔科夫、动态规划、梯度策略算法和增强学习(Q-learning)等等。

卷积神经网络(Convolutional Neural Networks, CNN)是一类包含卷积计算且具有深度结构的前馈神经网络,是深度学习的代表算法之一。
卷积神经网络依次为:输入层–>卷积层-→>最大池化层->卷积层->最大池化层->全连接层-→>输出层
【可以在人脸领域大规模应用】

应用深度学习的神经网络进行人脸识别技术原理主要是三大步骤:

一是建立一个包含大批量人脸图像的数据库
二是通过各种方式来获得当前要进行识别的目标人脸图像
三是将目标人脸图像与数据库中既有的人脸图像进行比对和筛选

一个完整的视频流人脸识别系统

通过OpenCV抓取摄像头的视频流
通过MTCNN对每帧图片进行人脸检测和对齐,设置每n个间隔帧进行一次检测
通过facenet预训练模型对第二步得到的人脸进行512未的特征值提取
收集目标数据集来训练自己的分类模型
将第三步得到的512维的特征值作为第四步的输入然后输出,即为人脸识别结果

人工智能的应用

智能助理、图像处理、机器视觉、客服服务、安全防护、AI艺术、新一代搜索引擎、机器翻译、自动驾驶、机器人

区块链概述[非对称加密 数字签名]

==区块链[起源于比特币]==是一个信息技术领域的属于。从本质上讲,它是一个共享数据库,存储于其中的数据或信息。具有==去中心化服务器分布在各地、不可伪造、全程留痕、可以追溯、公开透明、集体维护、合作信任==

价值转移
将某一部分价值从A地址转移到B地址,需要A地址精确地减少了这部分价值,而B地址精确地增加了这部分价值。这就是区别于信息转移的价值转移。目前的互联网协议是不支持价值转移功能的。
所以,目前的价值往往不是直接传输,而是由一个中心化的第三方来做背书

==区块链是一种按照时间顺序将数据区块以顺序相连的方式组合成的一种链式数据结构,并以密码学方式保证的不可篡改不可伪造分布式账本。主要解决交易的信任和安全问题==

区块链技术优势[比特币是区块链的应用]

==分布式、可共享、隐私性、防篡改==

区块链技术的出现[一个社区的人互相交易记账金额]
为快速完成信用建设,实现低成本且安全的价值转移,区块链技术就这样应运而生了。
==区块链是一个去中心化的分布式帐本==,每个参与者都是一个节点,每个节点都保存着一份相同的帐本,一旦对帐本进行修改,就需要对所有节点的帐本都进行修改。
区块链可以在没有第三方信用背书的情况下,在一个开放式的平台上进行远距离的安全支付。
==区块链技术的优势是:去中心、去国界、透明、能够重构一种新的征信体系==
==区块链的密码技术主要是:数字签名算法、哈希算法==
==区块链的技术分类主要是:公有链、联盟链、私有链==
==分布式账本的特征点对点、抗毁坏、防篡改、全透明==

区块链技术的出现区块链信用建设特点
1)区块链是分布式的,区块链公信力在网络上会有许多独立的节点,每一节点都有一份备份信息。每个有授权的人都可以从任意一个节点下载全部的信息,同时,区块链公信力网络也是不可篡改的
2)在区块链公信力模型中,区块链不制定政策,它使用算法证明机制来保证他的公证人的角色,它实际上是用基于共识的数学方法,在机器之间建立信任并完成信用创造。

区块链系统用户为何记账
每一个区块链系统中的用户,都可以去记账,记账的奖励则有两个来源:手续费打包奖励
每个区块都只能由一个人打包

区块链系统如何解决防伪问题
1、如何保证区块链上的交易记录是真实的?(身份认证问题)
我们必须保证每一条记录都是由”货币”持有者所发出的,而不是由其他人伪造的。
==非对称加密:[数字签名]公钥和私钥 要使用特定私钥去对应打开公钥才可看到信息==
[加密用公钥、解密用私钥]在实际运用中,私钥可以对一串字符进行加密,而公钥可以把私钥加密后的内容解密。
私钥必须由用户个人保存好,不能告诉他人。而公钥和地址都是公开的,如果用户想让别人给他钱,只需给对方一个地址即可,如果用户想给别人钱,用户需要将自己的公钥和地址一起发送过去。
私钥不能通过公钥推导出来 私钥 → 公钥 → 公钥has → 地址

3、如何避免双重支付?

举例来说,假设A只有10元钱,但是他几乎同时广播了两条消息,第一条消息是”A付10元钱给B”,第二条消息是”A付10元钱给C
此时网络中的用户由于一定的延迟效应,有一部分会先接收到第一条消息,而另一部分会先接收到第二条消息。先接收到第一条消息的用户会对A进行余额检查,所以再接收到第二条消息时,这部分用户就会拒绝第二条消息。同理,先接收到第二条消息的用户也会拒绝后接收到的第一条消息。

4、如何防止篡改已存在的记录?

是否存在一种可能,A在之后将已经存在的记录删掉或篡改呢?
下面将介绍一个解决此问题的原则—-最长链原则
所有用户都只承认最长的那根链条,并默认在最长链后继续挖矿和接块。

A希望抹掉”A付10元钱给B”这个已经存在于链上的信息。举例来说,A的方法是重新打包一个不包含”A付10元钱给B”信息,反而包含”B付10元钱给A”信息的包,并且以这个信息包去重新计算数学题,再重新打一个包,造出一个支链,这个支链上,原信息”A付10元钱给B”就被篡改了。
包含篡改信息的区块虽然也被连到了链上,但由于最长链原则,这个支链并不被世界承认,除非A的计算能力超过了世界上其余所有的人算力之和,那A通过不断努力去延长新的支链,使其最终超过主链长度,才能够完成已有信息的篡改,但现实中这种算力集中情况不可能出现。

区块链应用

  1. 区块链电子发票
    [1.写入开票规则,核准和管控;2.链上申领发票,写入交易订单和身份识别;链上认领发票,更新身份标识;4.验收发票,审核入账,支付报销款]

其次,实现了无纸化报销,因为发票全流程的信息都在链上,报销时只要链上更新发票状态即可,无须再打印为纸质的文件存档;
再次,解决了一票多报、虚抵虚报的问题,利用区块链技术,可以确保发票的唯一性和信息记录的不可篡改性;
最后,其可以帮助政府部门提升监管力度。

  1. 身份认证
  2. 股票系统[Follow My Vote公司裂力于利用区块链技术打造种开源的、可审计的、安全高效的端对端投票系统,防止投票过程中出现安全漏洞。
  3. 供应链
    沃尔玛与IBM以及清华大学展开合作,政府协助下启动了两个独立推进的区块链试点项目,旨在提高供应链数据的准确性,保障食品安全。沃尔玛将区块链技术应用于全球供应链,成本将减少1万亿美元。
  4. 金融行业

==@@ 区块链采用非对称加密==

虚拟现实VR

虚拟现实技术应该具备的三个特征:沉浸感、交互性、想象性

它是以计算机技术为核心的现代高科技手段,模拟生成逼真的视、听、触、嗅、味觉等一体化的虚拟环境,用户借助一些特殊的输入与输出设备,通过自然的方式与虚拟世界中的对象进行交互,从而产生身临其境的感受和体验。

虚拟现实基本特征【3I1M】

==沉浸感(lmmersion):用户感到作为主角存在于模拟环境中的真实程度。
交互性(Interaction):参与者对虚拟环境内物体的可操作程度和从环境中得到反馈的自然程度。
构想性(lmagination):又称自主性,指用户沉静在多维信息空间中,依靠自己的感知和认知能力全方位获取知识,发挥主观能动性,需求解答,形成新的概念。
多感知性(Multi-Sensory):表示计算机技术应该拥有很多感知方式,比如听觉,触觉、嗅觉等等。==

自主性:虚拟环境中物体依据物理定律的移动

理想的虚拟现实技术应该具有一切人所具有的感知功能。由于相关技术,特别是传感技术的限制,目前大多数虚拟现实技术所具有的感知功能仅限于视觉、听觉、触觉、运动等几种。·

虚拟现实是多种技术的综合,其关键技术和研究内容包括以下几个方面:;

动态环境建模技术

对真实的环境建立计算机模型的技术,包括基于图像的建模技术、三维扫描建模技术等;
工具软件有3DS max,AutoCAD,MAYA,Sketch up等。实时三维计算机图形技术实等现实三维图像等方面的技术。

虚拟现实关键技术

交互技术: 用户与计算机交互的技术。键盘和鼠标是目前最常用的工具,但对于三维空间来说,键盘和鼠标不太合适。
**显示技术: ** 在VR系统中,双目立体视觉起了很大的作用。当用户戴上特殊的眼镜后,一只眼睛智能看到奇数帧图像,另一只眼睛只能看到偶数帧图像,奇、偶帧之间的不同即视差,就产生了立体感。
立体声技术: 常见的立体声效果是靠左右耳听到的不同位置录制的不同声音来实现的,所以会有一种方向感。
感觉反馈技术: 在VR系统中,用户可以看到一个虚拟的杯子。你可以设法抓住它,但是你的手没有真正接触杯子的的感觉。解决这一问题的常用装置是在手套内层安装一些可以振动的触点来模拟触觉
语音输入输出技术: 人的语音输入VR系统中识别、处理以及反馈等技术
系统集成技术: 通过结构化的综合布线系统和计算机网络技术,将各个分离的设备、功能和信息等集成到相互关联的、统一和协调的系统之中,使资源达到充分共享,实现集中、高效、便利的管理。

==虚拟现实技术分类==

桌面虚拟现实系统[DesktopVR]:
桌面虚拟现实系统基本上是一套基于普通PC平台的小型桌面虚拟现实系统。桌面虚拟现实的参与者是不完全沉浸的,有时要求参与者使用标准的显示器和立体现实设备、数据手套和六个自由度的三维空间鼠标器,戴上立体眼镜坐在监视器前,在一些专业软件的帮助下,可以通过计算机屏幕观察虚拟境界。

==增强式虚拟现实系统[AR]==:增强式虚拟现实系统允许用户对现实世界进行观察的同时,将虚拟图像叠加在真实物理对象上,为用户提供与所看到的真实环境有关的、存储的计算机中的信息,从而增强用户对真实环境的感受,因此又被称为叠加式或补充现实式虚拟现实系统。

沉浸式虚拟现实系统(lmmersive VR):沉浸式虚拟现实系统使用户沉浸在虚拟世界里。沉浸式虚拟现实系统是一种高级的虚拟现实系统,它提供了一个完全沉浸的体验,使用户有一种置身于虚拟境界之中的感觉。

分布式虚拟现实系统(Distributed VR)
分布式虚拟现实系统式虚拟现实技术和网络技术结合的产物;以沉浸式虚拟现实为基础,多个用户或虚拟世界通过网络相连接;多个用户同时加入统一虚拟空间,共享信息,协同工作达到一个更高的境界。

@@ 增强现实比虚拟现实**更注重虚拟场合、更注重临场感**

虚拟现实技术应用

(1) VR在智慧城市的使用
智慧城市就是运用信息和通信技术手段感测、分析、整合城市运行核心系统的各项关键信息,全景智慧城市时空建设上运用虚拟现实技术虚拟真实环境,是一项综合性城市设计的方法,在与用户、工程单位通过观看设计作品,在网上实时相互沟通交流,从而减少办公成本投资,缩短设计周期,提高规划设计质量。
虚拟现实技术充分利用计算机辅助设备和虚拟现实技术,虚拟现实景观,实现视觉、听觉模拟,可以使城市景观设计更具创造性、灵活性,可减轻设计人员的劳动强度,提高设计质量,节省投资。

(2) VR在其它领域的应用
在医学、娱乐、军事航空航天、房产开发与室内设计、工业仿真、文物古迹、游戏、Web3D、道路桥梁、地理、教育等领域有广泛的应用。如在教育领域,虚拟现实技术在中小学教育、职业教育、高等教育等方面均有应用。在中小学教育方面,结合虚拟现实沉浸式教学的体验,解决课堂教学中的抽象、困难的知识点,实现由传统的“以教促学”的学习方式向学习者通过自身与信息环境的相互作用来得到知识,让学生对课程更感兴趣。

@@ 虚拟化是大数据处理的特点
@@ 全球定位系统、移动电话技术、有限网络属于物联网关键技术
@@ 物联网的核心和基础是互联网
@@ 人工智能应用研究的两个最重要、最广泛的领域是专家系统、机器学习


铭升教育

信息安全

基本知识

1.为什么需要信息安全:因为Internet中存在丰富的信息资源
2.什么是信息安全
答:信息安全是指 放置任何对数据进行 未授权访问没有账户 的 安全措施,或者 防止造成信息 有意无意的泄露、破坏、丢失等问题的发生,让数据处于 远离危险、避免威胁 的状态或特征
3.【填空】信息安全的 六个要素 p37
==**保密性、完整性防伪造、可靠性防伪造**、可用性、可控性、不可否认性防抵赖,采用”数字签名”==

计算机病毒 概念【背】

是指 编制或者在计算机程序中插入的破坏计算机功能和数据,影响计算机使用 并且 能够自我复制的一组计算机指令或程序代码
【说明】
1.计算机病毒 是 程序代码 或 指令
2.【填空】计算机病毒的特征:==传染性、破坏性、潜伏性、隐蔽性、可触发性、不可预见性、变异性、表现性== 等
【说明】
传染性 是病毒 最主要的特征;
破坏性 表现在 破坏程序功能、破坏计算机的性能(占用CPU和内存资源)、破坏数据、盗用信息
表现性 病毒表现出它的破坏能力
3.计算机病毒传播主要途径:计算机网络(Internet和局域网)、移动存储介质(U盘)
4.【多选】计算机病毒防治措施
答:对计算机病毒采用”预防为主“的方针。
① 操作系统软件要及时升级、打补丁 【最主要的】
② 安装杀毒软件、防火墙软件,并及时升级
③ 定期查、杀病毒;
④ 不适用来历不明的移动存储介质,不访问不良站点、不打开来历不明的电子邮件、短信等
⑤ 控制访问权限,设置访问口令,加强数据加密
⑥ 定期制作 系统备份
⑦ 使用正版软件、抵制盗版行为
⑧ 制订规章制度、加强宣传教育,加强安全防范意识

防火墙

概念或功能:处于 ==内网外网== 之间的一道安全屏障。
作用:用于过滤 进、出 内网的数据。通过制订安全策略,设置 允许 或 禁止 通过防火墙的数据包,记录通过防火墙的信息和行为,对网络中出现的攻击行为 进行 检测和告警;
【考点】
① 由于 内网和外网 的安全级别要求不同,需要防火墙过滤
② 防火墙 采用 被动防御策略
防火墙 是 抵御外部攻击,没有杀毒功能反病毒软件 是用于监测和清除 病毒,没有抵御黑客攻击功能(杀毒软件)
④【选择】防火墙 不能 抵御所有攻击;杀毒软件 不能 清除所有病毒
⑤ 记录日志

密码技术

1.密码的 六个要素
明文、加密算法、加密密钥、密文、解密算法、解密密钥
2.【重点】加密技术分为:对称加密 和 非对称加密 两类
3.对称加密:
加密算法 = 解密算法
加密密钥 = 解密密钥
4.==非对称加密(公开密钥)==
加密算法 与 解密算法 不同
加密密钥 与 解密密钥 不同
(1)【考题】网络中传输数据:采用 公开密钥 机制
如,发送电子邮件、金融服务、身份验证、”数字签名”等
(2) 非对称加密有一对密钥
① 公开密钥:用于加密 向 密钥所有者 发送的明文
② 私密密钥:用于 破解公开密钥的加密;用于 **”数字签名”**,防抵赖

@@ ORC(optical character recognition)是文字识别

@@ MD5数据加密算法 DES报文摘要 公钥基础设施:产生 公开密钥和私密密钥
RSA最著名的公钥密码算法、MD5、AES非对称加密算法 PKI属于对称性加密

@@ 用于建立超链接的HTML标记是 < a href… /a >

@@ 快捷方式是扩展名为LNKlink的文件


==计算机病毒代码的结构一般来说包括3大功能模块:==
引导模块: 引导模块将病毒由外存引入内存,使后两个模块处于活动状态。
传染模块: 传染模块显然用来将病毒传染到其它对象上去
破坏模块: 破坏模块实施病毒的破坏作用,如删除文件,格式化磁盘等,由于有些病毒的该模块并没有 明显的恶意破坏作用,而只是进行一些视屏或声方面的自我表现作用,故该模块有时又称表现模块

根据病毒存在的媒体,病毒可以划分为: 网络病毒、文件病毒和引导型病毒
==网络病毒通过计算机网络传播感染网络中的可执行文件
文件病毒感染计算机中的文件(如:com, exe, doc等)
引导型病毒感染启动扇区(Boot)和硬盘的系统引导扇区(MBR)==

还有这三种情况的混合型病毒,例如:多型病毒(文件和引导型)感染文件和引导扇区两种目标,这样的病毒通常都具有复杂的算法,它们使用非常规的办法侵入系统,同时使用了加密和变形算法
宏病毒:破坏Excel、PPT、word

计算机病毒传染的过程是这样的:病毒从带毒载体进入内存,一般利用操作系统的加载机制或引导机制。当系统运行一个带毒文件或用一带毒系统盘启动时,病毒就进入内存。而从RAM侵入无毒介质则利用了操作系统的读写磁盘中断或加载机制。

@@ 解调=> 模拟 ~ 数字 解调=> 数字 ~ 模拟
@@ 经典的Internet数据传输速率可以从10Mbps - 10Gbps
@@ 木马 => 植入威胁(本身PC成为服务器)
@@ BBS贴吧、论坛、电子公告板
@@ 调制拨号接入网 调制解调机器;网卡是通过 局域网接入
@@ C类地址属于 小型规模地址网络 多目的地址传送是:广播地址
@@ Windows操作系统中受限用户在默认情况下可以访问操作自己的文件,这种权限策略称为最小权限原则
@@ 防火墙的安全策略 ①除了允许的,其他的都禁止Windows7 ②除了禁止的,其他都允许


@@ 计算机病毒可以通过计算机网络进行传播,可以通过不移动的计算机硬件设备进行传播,可以通过移动存储设备进行传播,还可以通过点对点通信系统和无线通道传播
@@ 威胁信息安全的因素有很多:”黑客入侵、计算机病毒、自然灾害(来自自然灾害、恶劣的场地环境、电磁辐射和电磁干扰、网络设备自然老化)
@@ 被动攻击是指在不干扰网络信息系统正常工作的情况下,进行监听、截获、窃取、破译和业务流量分析等。被动攻击不会使系统瘫痪,但更加难以检测
@@ 电子商务安全技术有:加密技术、数字签名、认证中心(CA)、Internet电子邮箱的安全协议不包括防火墙技术
@@ 虚拟专用网(VPN)即虚拟私有网络,是一种利用公用网络来构建的私有专用网络;被定义为通过一个公用网络建立一个临时的、安全的链接,是一条穿过混乱的公用网络的安全、稳定的隧道,是对企业内部网的扩展
@@ 使用大量垃圾,占用宽带的攻击破坏的是数据的可用性 [发送大量垃圾信息可占用带宽(造成堵塞), 导致实体计算机用户无法再需要的时候访问网络资源和得到服务,破坏了数据的可用性]
@@ 信息安全包括四大要素:技术、制度、流程、人
@@ 网络连接设备的安全漏洞属于结构隐患。结构隐患一般指网络拓扑结构的隐患和网络硬件的安全隐患
@@ 目前信息安全技术主要有: 密码技术、防火墙技术、虚拟专用网(VPN)技术、数字证书技术、其他安全保密技术
@@ 虚拟专用网(VPN)是将物理分布在不同地点的网络通过公用骨干网(Internet)连接而成的逻辑上的虚拟子网,为了保障信息的安全,VPN技术采用了鉴别、访问控制、保密性和完整性等措施,以防信息被泄露、篡改和复制
@@ 对称密钥加密又称单钥加密,加密密钥和解密密钥相同,或从一个可以推出另一个
@@ 公钥密码体制中,公钥用来加密(可公开),私钥用来解密(不可公开);公钥和私钥是不同的
@@ 加强网络安全的最重要的基础措施是设计有效的网络安全策略
@@ 云计算是对并行计算、网格计算、分布式计算、存储技术的发展与运用
@@ 智能客服系统、人脸识别属于人工智能


@@ 程序与软件程序文档的集合
@@ 计算机网络按使用性质公用网+私用网
@@ 蠕虫在网络中传染 占用CPU内存 主要造成拒绝式服务
@@ ETC是物联网 没有采用人工智能
@@ 货物搬运属于物联网但也有人工智能在里面
@@ 输入设备:光驱、数码相机 刻录机是输出设备
@@ 每段首行首字据页左边界的距离称为左缩进,第二行,相对第一行左侧的偏移量是首行缩进
@@ 链接网络并选择一个网络来传输数据包的计算机,被称为路由器
@@ 多媒体技术和超文本技术的结合,形成了超媒体
@@ 数据窃取是指敏感数据拷贝监听
@@ 人工智能的关键技术包括计算机视觉、生物特征识别、机器学习、语音识别、自然语言识别、机器人技术/专家系统
@@ CPU的内外位置不同分为内部总线外部总线 其中内部总线称为cip[内部总线是CPU内部数据传输通道]
@@ 主板上的是系统总线 主板之外和外设相连的实外部总线
@@ 多媒体具有多样性、继承性…
@@ 计算机病毒的基本构造有引导部分、传染部分、表现部分
@@ 机器学习是使计算机有人工智能
@@ Aero效果 半透明主题
@@ 区位码是数字编码。将所有字符分为
@@ 汉字在计算机内部的编码是机内码
@@ 汉字打印输出有点阵方式矢量图
@@ MIS是管理信息系统 是应用软件
@@ 汉字字库中存放着汉字的 字体字形字号
@@ 比特币的特点:不可伪造、匿名性、不可篡改、可回溯
@@ VR中使用的输入设备包括三维扫描仪、声学跟踪器、光学跟踪器、数据手套 数据手套是虚拟现实中的典型设备
@@ 公钥密码常用于数据加密、数字签名
@@ 在人工智能的关键技术中,生物特征识别是通过指纹、面部对人的身份进行验证
@@ Excel中X轴是分类轴
@@ 秘钥分配中心技术中,常用于对称密码体制中的加密密钥的分配;密钥认证中心技术,更多地用于公开密码体制中的公开密钥
@@ 内存储器和外存储器的最大区别是否被CPU直接访问、断电后是否会丢失信息
@@ 硬盘 U盘 光盘 按照存储速度的快到慢
@@ 智能终端的操作系统:Android、Harmony、IOS
@@ 不同工作簿的单元格引用,工作表改名或移动后 数据自动更新不受影响
@@ 表示层 提供数据格式转换服务 应用层 给用户的应用提供网络服务
@@ 压缩率最大的是JPEG
@@ 区块链的链是整个账本状态变化的日志记录
@@ 人脸识别是人工智能
@@ 计算机系统中,数据、程序、有关的文档的集合称为软件
@@ 资源管理器按大小、修改日期、类型方式排列
@@ 输入了不正确的拼音码可以退格键、ESC
@@ 硬分页符是自带的,不是人为插入的,不可删除
@@ 顶级域名类型包括国家、通用域名、基础结构域(ARPA)
@@ CMYK 青色,洋红色(品红),黄色,黑色
@@ 数字签名应具有可公开检验、唯一性、不可抵赖性、不可伪造性
@@ 百度视图体现了人工智能中的机器视觉
@@ 物联网已经广泛应用于智能交通、智慧医疗、智能安防、智能物流
@@ 动画是用一定的技术手段,将若干静态画面连续呈现形成的,这些静态画面一般称为
@@

@@ CGA VGA TVGA EGA 显示卡的总线接口类型

阅读全文
头像
Asuna
You are the one who can always get to me even with screen between us.