Jsp

Java Server Pages:java服务端页面,同 Servlet 一样用于动态Web技术

写Jsp就像写HTML,可以嵌入Java代码以提供动态服务

Jsp本质依然是Servlet,Tomcat等Servlet容器会在这些jsp文件首次被访问时自动转换为java文件(借助Jasper)并编译为class文件

访问 jsp 页面时注意添加 .jsp 后缀名

模块创建

新建模块

参见前文

载入依赖

        <!-- servlet 依赖库 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- jsp 依赖库 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.3</version>
        </dependency>
        <!-- jstl 标签库 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

新建Java目录

参见前文

Tomcat配置

参见前文

Jsp语法及源码解析

demo01.jsp

<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>JSP</h1>
    <!-- 这是 HTML 注释,会出现在 HTML 页面 -->
    <%-- 这是 JSP 注释,和 HTML 注释不同,不会出现在 HTML 页面中 --%>

    <%-- jsp 表达式 --%>
    <%= new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) %>
    <%-- 等价于 --%>
    <%-- <% --%>
    <%-- String date = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()); --%>
    <%-- out.write(date); --%>
    <%-- %> --%>

    <%-- jsp 脚本片段1 --%>
    <%
        int sum = 0;
        for (int i = 1; i <= 5; i++) {
            out.print("<p>" + "当前循环数值为: " + i + "</p>");
            sum += i;
        }
        out.print("<p>" + "求和结果为: " + sum + "</p>");
    %>

    <%-- jsp 脚本片段2, 嵌套 HTML --%>
    <% int n = 6; %>
    <% if (n > 5) { %>
        <p>数字大于5</p>
    <% } else { %>
        <p>数字小于等于5</p>
    <% } %>

    <%-- jsp 声明, 在 jsp 编译后会作为 Servlet 类的静态内部块、成员属性、成员方法 --%>
    <%!
        static {
            System.out.println("Loading Servlet!");
        }
        private int globalvar = 10;
        public void meth1() {
            System.out.println("这是自定义方法");
        }
    %>
    <%-- 直接使用 --%>
    <%= globalvar %>
    <%-- 直接使用 --%>
    <%
        meth1();
    %>

</body>
</html>

demo01_jsp.java

该文件取自idea_cache\system\tomcat\randomdirname\work\Catalina\localhost\projectname_war\org\apache\jsp目录

idea_cache是Idea缓存所在的路径,视idea版本和个人配置而定

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Date;
import java.text.SimpleDateFormat;
// HttpJspBase 继承自 HttpServlet
public final class demo01_jsp extends org.apache.jasper.runtime.HttpJspBase
        implements org.apache.jasper.runtime.JspSourceDependent,
        org.apache.jasper.runtime.JspSourceImports {

    // jsp 声明 编译后在这里
    static {
        System.out.println("Loading Servlet!");
    }
    private int globalvar = 10;
    public void meth1() {
        System.out.println("这是自定义方法");
    }

    // 省略

    public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
            throws java.io.IOException, javax.servlet.ServletException {

        // 省略

        try {
            // jsp 编译后会默认设置响应的内容类型为该值
            response.setContentType("text/html;charset=UTF-8");
            pageContext = _jspxFactory.getPageContext(this, request, response,
                    null, true, 8192, true);
            // 页面上下文对象, jsp 特有对象
            _jspx_page_context = pageContext;
            // 本质是容器级上下文对象
            application = pageContext.getServletContext();
            // 本质是Servlet级配置对象
            config = pageContext.getServletConfig();
            // 这里调用了 getSession(), 意味着当访问该页面时如果不存在 session 会自动创建
            session = pageContext.getSession();
            // 本质是 getWriter() 获得的 PrintWriter 对象
            out = pageContext.getOut();
            _jspx_out = out;

            out.write("\r\n");
            out.write("\r\n");
            out.write("\r\n");
            out.write("<html>\r\n");
            out.write("<head>\r\n");
            out.write("    <title>Title</title>\r\n");
            out.write("</head>\r\n");
            out.write("<body>\r\n");
            out.write("    <h1>JSP</h1>\r\n");
            // html 注释 编译后在这里
            out.write("    <!-- 这是 HTML 注释,会出现在 HTML 页面 -->\r\n");
            // jsp 注释 编译后被去除
            out.write("    ");
            out.write("\r\n");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");
            // jsp 表达式 编译后在这里
            out.print( new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()) );
            out.write("\r\n");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");

            // jsp 脚本片段1 编译后在这里
            int sum = 0;
            for (int i = 1; i <= 5; i++) {
                out.print("<p>" + "当前循环数值为: " + i + "</p>");
                sum += i;
            }
            out.print("<p>" + "求和结果为: " + sum + "</p>");

            out.write("\r\n");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");
            // jsp 脚本片段2, 嵌套 HTML 编译后在这里
            int n = 6;
            out.write("\r\n");
            out.write("    ");
            if (n > 5) {
                out.write("\r\n");
                out.write("        <p>数字大于5</p>\r\n");
                out.write("    ");
            } else {
                out.write("\r\n");
                out.write("        <p>数字小于等于5</p>\r\n");
                out.write("    ");
            }
            out.write("\r\n");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");
            // 通过 jsp 表达式 使用 jsp 声明 的变量
            out.print( globalvar );
            out.write("\r\n");
            out.write("    ");
            out.write("\r\n");
            out.write("    ");

            // 通过 jsp 代码片段 使用 jsp 声明 的方法
            meth1();

            out.write("\r\n");
            out.write("\r\n");
            out.write("</body>\r\n");
            out.write("</html>\r\n");
        } catch (java.lang.Throwable t) {
            // 省略
        } finally {
            // 省略
        }
    }
}

Jsp指令

概览

指令 语法 作用
page <%@ page ... %> 控制整个 JSP 页面的行为,比如编码方式、内容类型、是否启用 session 等。
include <%@ include file="..." %> 在 JSP 编译阶段静态包含另一个文件的内容(如 HTML、JSP 片段)。
taglib <%@ taglib uri="..." prefix="..." %> 声明并引入一个自定义标签库或标准标签库(如 JSTL)。

<%@ page ...args %>

args

属性名 默认值 说明
language java 指定 JSP 页面使用的脚本语言(目前只支持 Java)
extends - 指定生成的 Servlet 所继承的父类(一般不建议修改)
import - 导入 Java 包或类,多个用逗号分隔,如:import="java.util.*, java.sql.*
session true 是否允许使用 session 对象,设为 false 表示禁用 Session
isThreadSafe true 控制生成的 Servlet 是否实现 SingleThreadModel 接口,false 时容器会确保线程安全处理(已过时)
errorPage - 指定当前页面出错后跳转的错误处理页面,例如:errorPage="error.jsp"
isErrorPage false 表示该页面是否是一个错误处理页面(可访问 exception 内置对象)
contentType text/html 设置响应的 MIME 类型和字符编码,常用:contentType="text/html;charset=UTF-8"
pageEncoding 服务器默认编码(通常是 ISO-8859-1) 设置当前 JSP 文件的字符编码(推荐设置为 UTF-8)
isELIgnored false 是否忽略 EL 表达式(${...},设为 true 后 EL 不会被解析
buffer 8kb 设置 JSP 输出流(out)的缓存大小,如:buffer="16kb"
autoFlush true 缓冲区满时是否自动刷新输出,若为 false,缓冲区满时抛出异常
trimDirectiveWhitespaces false 是否移除 JSP 标签之间的空白字符(优化 HTML 输出)

demo02.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 指定当前页面出错后显示的错误页面 --%>
<%@ page errorPage="error/500.jsp" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%-- 例如出现了0除错误 --%>
    <%
        int x = 1 / 0;
    %>
</body>
</html>

500.jsp

在webapp/error目录下创建

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
  <h1>自定义的错误页面500</h1>
</body>
</html>

<%@ include file="path/to/your_jsp.jsp" %>

静态包含,不推荐使用

demo03.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%@ include file="commons/header.jsp" %>

    <%
        int x = 20;
    %>
    <h1>DEMO03</h1>

    <%-- 载入公共的脚本HTML片段 --%>
    <%-- 编译为Servlet时会直接把该片段编译进当前页面 --%>
    <%-- 由于当前页面和载入的 footer 片段页面均定义了x,会产生错误(变量重复声明定义) --%>
    <%--<%@ include file="commons/footer.jsp"%>--%>
    <%-- 解决办法是使用jsp标签(推荐用法),这样不会直接编译进当前页面,而是作为外部引用 --%>
    <jsp:include page="commons/footer.jsp" />
</body>
</html>

header.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>这是HTML头部片段</h1>

footer.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<h1>这是脚部HTML片段</h1>
<%
int x = 10;
%>

<%@ taglib ...args %>

用于引用外部标签库,例如:jstl标准标签库,见后文

内置对象

九大对象

内置对象名 类型 描述
pageContext javax.servlet.jsp.PageContext 提供对页面所有其他内置对象的访问,支持属性管理(page/request/session/application)和包含/转发操作
request javax.servlet.http.HttpServletRequest 表示客户端的 HTTP 请求,用于获取请求参数、请求头、设置/获取属性等
response javax.servlet.http.HttpServletResponse 用于向客户端发送响应数据,如设置状态码、响应头、重定向等
session javax.servlet.http.HttpSession 用于在多个请求之间保存用户信息,比如登录状态、用户偏好等
application javax.servlet.ServletContext 表示整个 Web 应用上下文,用于共享全局数据、获取配置参数等
config javax.servlet.ServletConfig 表示当前 JSP 页面的配置信息,用于获取初始化参数
out javax.servlet.jsp.JspWriter 用于向浏览器输出内容(相当于 PrintWriter),常用于 <%= %> 输出
page java.lang.Object(实际是 this 表示当前 JSP 页面本身(即生成的 Servlet 实例),类似 Java 中的 this
exception java.lang.Throwable 表示运行时异常或错误,仅在设置了 isErrorPage="true" 的页面中可用

四大对象作用域

对象 作用域 生命周期 范围 使用场景 特点
pageContext 页面作用域 当前页面创建到处理完成 仅当前页面有效 存储页面级别的临时数据,如页面内共享变量 数据只在当前页面有效,离开即失效
request 请求作用域 请求开始到响应结束 同一个请求链(包括转发) 在多个 Servlet/JSP 之间传递数据 支持请求转发时共享,重定向后数据丢失
session 会话作用域 用户首次访问到会话超时或销毁 整个用户会话期间 存储用户登录状态、购物车等需要跨请求保持的数据 可跨多个页面和请求,适合跟踪用户行为
application 应用作用域 Web应用启动到停止或重新部署 整个Web应用的所有用户 存放全局配置、公共资源(如数据库连接池、全局计数器等) 所有用户共享,修改需谨慎,避免并发问题

demo04

<%@ page import="java.io.IOException" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 放在html标签开始之前,请求转发 --%>
<%--<% pageContext.forward("index.jsp"); %>--%>
<%-- 等价于 --%>
<%--<% request.getRequestDispatcher("hello.jsp").forward(request, response); %>--%>
<html>
<head>
    <title>Title</title>
</head>
<body>

<%-- 动态包含 --%>
<%--<% pageContext.include("commons/header.jsp"); %>--%>

<%
    pageContext.setAttribute("name1", "ssydxp_1");

    request.setAttribute("name1", "ssydxr_1");
    request.setAttribute("name2", "ssydxr_2");

    session.setAttribute("name1", "ssydxs_1");
    session.setAttribute("name2", "ssydxs_2");
    session.setAttribute("name3", "ssydxs_3");

    application.setAttribute("name1", "ssydxa_1");
    application.setAttribute("name2", "ssydxa_2");
    application.setAttribute("name3", "ssydxa_3");
    application.setAttribute("name4", "ssydxa_4");
%>

<%-- 按照 pageContext > request > session > application 顺序依次查找指定属性名,如果最终不存在则返回 null --%>
<%-- 此过程就类似类加载的双亲委派机制 --%>
<h1><%= pageContext.findAttribute("name1")%></h1>
<h1><%= pageContext.findAttribute("name2")%></h1>
<h1><%= pageContext.findAttribute("name3")%></h1>
<h1><%= pageContext.findAttribute("name4")%></h1>
<h1><%= pageContext.findAttribute("name5")%></h1>
<%-- 同上, 但查找不到返回空(底层就是调用了 pageContext.findAttribute, 但对 null 进行了处理)--%>
<%-- 即 ${} (EL表达式) 取的是四大对象内的属性而不是变量 --%>
<h1>${name1}</h1>
<h1>${name2}</h1>
<h1>${name3}</h1>
<h1>${name4}</h1>
<h1>${name5}</h1>

</body>
</html>
特性 ${name}<%= pageContext.findAttribute("name") %>
查找机制 完全相同 完全相同
返回 null 时的行为 不输出任何内容(视觉上“消失”) 输出字符串 "null"
HTML 转义 自动进行 不自动进行
推荐使用场景 JSP 页面展示数据 需要 Java 控制逻辑时
可读性 更好 较差
是否适合 MVC 推荐 不推荐用于视图展示

拓展

双亲委派机制:一个类加载器收到加载请求时优先委托给其父类加载器(此过程递归向上到顶级加载器),如果父类加载器无法完成加载请求(即其搜索范围内没有所需的类)则通知子加载器(递归向下到发出委托的子加载器为止)由子加载器进行加载

此机制有效避免了同名类导致的问题,即自己定义的类覆盖系统类

jsp标签

标签一览表

标签作用适用场景
<jsp:include> 包含另一个页面的内容到当前页面 动态包含其他 JSP 页面或资源,适用于需要在运行时合并多个页面的情况
<jsp:forward> 将请求转发到另一个页面 当前页面处理完后将请求转发给另一个页面,常用于页面导航
<jsp:param> 作为子元素传递参数给 <jsp:include><jsp:forward> 需要向被包含或转发的页面传递参数时使用
<jsp:useBean> 在指定的作用域中查找或创建一个 JavaBean 实例 使用 JavaBean 组件管理数据状态
<jsp:setProperty> 设置 JavaBean 的属性值 初始化或更新 JavaBean 的属性
<jsp:getProperty> 获取 JavaBean 的属性值并输出 显示 JavaBean 中的数据
<jsp:text> 输出纯文本内容 当你需要确保某些文本不被解释为HTML或XML时
<jsp:element> 动态定义XML元素 在JSP页面中动态生成XML内容
<jsp:body> 定义自定义标签体的内容 自定义标签开发时定义标签体
<jsp:attribute> 定义自定义标签库中的标签的属性 自定义标签开发时定义标签属性
<jsp:plugin> 生成客户端浏览器中使用的插件(如 Java Applet)所需的 HTML 标记 已很少使用,主要用于早期支持嵌入式Java应用程序

demo05

<%@ page import="xyz.ssydx.entity.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--<jsp:forward page="hello.jsp" />--%>
<%--<jsp:forward page="hello.jsp">--%>
<%--    <jsp:param name="name" value="value"/>--%>
<%--</jsp:forward>--%>
<html>
<head>
    <title>Title</title>
</head>
<body>

    <%--<jsp:include page="commons/footer.jsp">--%>
    <%--    <jsp:param name="name" value="value"/>--%>
    <%--</jsp:include>--%>

    <%--<%--%>
    <%--    People people = new People();--%>
    <%--    people.setName("zhangsan");--%>
    <%--    people.setAge(24);--%>
    <%--    pageContext.setAttribute("people", people);--%>
    <%--%>--%>
    <%--${people.name}--%>
    <%--${people.age}--%>
    <%-- 等价 --%>
    <jsp:useBean id="people" class="xyz.ssydx.entity.People" scope="page" />
    <jsp:setProperty name="people" property="name" value="zhangsan" />
    <jsp:setProperty name="people" property="age" value="24" />
    <jsp:getProperty name="people" property="name"/>
    <jsp:getProperty name="people" property="age"/>

</body>
</html>

People.java

package xyz.ssydx.entity;

public class People {

    private Integer id;
    private String name;
    private Integer age;

    public People(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public People() {}

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

}

EL表达式

隐式对象

隐式对象描述
pageScope page 作用域
requestScope request 作用域
sessionScope session 作用域
applicationScope application 作用域
param Request 对象的参数,字符串
paramValues Request对象的参数,字符串集合
header HTTP 信息头,字符串
headerValues HTTP 信息头,字符串集合
initParam 上下文初始化参数
cookie Cookie值
pageContext 当前页面的pageContext

demo06

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page import="xyz.ssydx.entity.People" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入标准函数标签库 --%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <%
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        request.setAttribute("list", list);

        String[] strings = {"zs", "ls", "ww"};
        request.setAttribute("strings", strings);

        People people = new People(1, "ssydx", 35);
        request.setAttribute("people", people);

        session.setAttribute("name", "ssydx");
        session.setAttribute("age", 25);
    %>

    <%-- EL表达式应该专注于获取数据而不是执行写入操作(很多涉及写入的操作(put方法, set方法等)都是非法的) --%>

    <h1>支持大多数的Java运算符</h1>
    <p>计算3+5:${3+5}</p>
    <p>判断3!=5:${3 != 5}</p>
    <p>判断列表是否为空:${empty list}</p>
    <p>获取列表第一个值:${list.get(1)}</p>
    <p>获取字符串数组第二个元素:${strings[2]}</p>
    <p>获取年龄:${people.age}</p>
    <p>获取姓名的长度:${fn:length(people.name)}</p>

    <h1>登录表单</h1>
    <%-- 提交到当前页 --%>
    <form action="demo06.jsp" method="get">
        <input type="text" name="username" value="user" /> <br />
        <input type="checkbox" name="hobbies" value="小说" checked>小说
        <input type="checkbox" name="hobbies" value="电影">电影
        <input type="checkbox" name="hobbies" value="manga" checked>漫画
        <input type="checkbox" name="hobbies" value="music">音乐 <br />
        <input type="submit" value="确定">
    </form>

    <%-- 请求中的请求参数 --%>
    <p>用户姓名:${param.username}</p>
    <p>用户爱好:${paramValues.hobbies[0]}</p>

    <%-- 请求中的cookie --%>
    <p>获取JSESSIONID的值:${cookie.get("JSESSIONID").value}</p>

    <%-- 作用域对象, 类同四大对象作用域 --%>
    <p>获取会话作用域的属性name的值:${sessionScope["name"]}</p>
    <p>获取会话作用域的属性age的值:${sessionScope.age}</p>

    <%-- tomcat9可以生效, 但这是非标准写法, 不推荐 --%>
    ${pageContext.include("commons/footer.jsp")}

</body>
</html>

jstl标签

核心标签

核心标签一览表

标签描述
<c:out> 用于在 JSP 中显示数据,类似于 <%= ... %>
<c:if> 条件判断标签,与一般程序中使用的 if 类似
<c:choose> 作为 <c:when><c:otherwise> 的父标签,用于组合多条件判断
<c:when> <c:choose> 的子标签,用于判断某个条件是否成立
<c:otherwise> <c:choose> 的子标签,当所有 <c:when> 都为 false 时执行
<c:forEach> 基础迭代标签,支持多种集合类型(如 List、Map、数组等)
<c:set> 用于保存数据(设置变量)
<c:remove> 用于删除某个作用域中的变量
<c:catch> 用来处理产生错误的异常状况,并且将错误信息储存起来
<c:import> 检索一个绝对或相对 URL,并将其内容暴露给当前页面
<c:forTokens> 根据指定的分隔符分割字符串并进行迭代输出
<c:param> 用于给包含(include)或重定向(redirect)的页面传递参数
<c:redirect> 将用户请求重定向到一个新的 URL
<c:url> 创建一个带有可选查询参数的 URL(可用于防止 URL 重写问题)

demo07.jsp

<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.List" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%-- 引入标准核心标签库 --%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

    <h1>登录表单</h1>
    <%-- 提交到当前页 --%>
    <form action="demo07.jsp" method="get">
        <input type="text" name="username" value="user" /> <br />
        <input type="number" name="score" value="5" max="10" min="0" /> <br />
        <input type="submit" value="确定">
    </form>

    <h1>条件语句if</h1>
    <%-- var存储判断结果 --%>
    <c:if test="${param.username == 'admin'}" var="isAdmin">
        <p><c:out value="欢迎登录" /></p>
    </c:if>
    <p>是否为admin? <c:out value="${isAdmin}" /></p>

    <h1>条件语句choose</h1>
    <p>
        <c:choose>
            <c:when test="${param.score >= 5}">
                好
            </c:when>
            <c:when test="${param.score >= 3}">
                中
            </c:when>
            <c:otherwise>
                坏
            </c:otherwise>
        </c:choose>
    </p>

    <h1>循环语句forEach1</h1>
    <c:forEach var="i" begin="1" end="5" step="1">
        <p><c:out value="${i}" /></p>
    </c:forEach>

    <h1>循环语句forEach2</h1>
    <%
    List<String> list = new ArrayList<>();
    list.add("zhangsan");
    list.add("lisi");
    list.add("wangwu");
    request.setAttribute("list", list);
    %>
    <c:forEach var="str" items="${list}">
        <p><c:out value="${str}" /></p>
    </c:forEach>

</body>
</html>

web.xml其他配置

  <jsp-config>
    <jsp-property-group>
      <url-pattern>*.jsp</url-pattern>
      <trim-directive-whitespaces>true</trim-directive-whitespaces>
    </jsp-property-group>
  </jsp-config>
#java##javaweb#
JavaWeb 文章被收录于专栏

此专栏由于更新观看不便,不会保持及时更新,最新更新见计算机合集专栏https://www.nowcoder.com/creation/manager/columnDetail/04yp33

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务