面试专题之Java web编程

Java架构师全路线总结+高频面试题(2024年最新版) 一次偶然,从朋友那里得到一份“java高分面试指南”,里面涵盖了25个分类的面试题以及详细的解析:JavaOOP、Java集合/泛型、Java中的IO与NIO、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、Memcached、MongoDB、Spring、Spring Boot、Spring Cloud、RabbitMQ、Dubbo 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。 阅读详情

题目

  1. JAVA应用服务器有那些?
  2. 启动项目时如何实现不在链接里输入项目名就能启动?
  3. 1分钟之内只能处理1000个请求,你怎么实现,手撕代码?
  4. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?(JSP)
  5. 说一说四种会话跟踪技术
  6. 讲讲Request对象的主要方法
  7. 过滤器有哪些作用和用法?
  8. 请谈谈你对Javaweb开发中的监听器的理解?
  9. 说说web.xml文件中可以配置哪些内容?
  10. get和post的区别
  11. cookie和session的区别?cookie和sessionStorage、localStroage之间的区别?
  12. session怎么保证多次访问是同一个ID?关闭了浏览器怎么办?
  13. 谈谈Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?
  14. 如何设置请求的编码以及响应内容的类型?
  15. Servlet相关接口 , Servlet里面有哪些方法 ? Servlet执行时一般实现哪几个方法?
  16. Servlet 3中的异步处理指的是什么?
  17. servlet生命周期?servlet是单例模式么?为什么是单例?Servlet和cgi有什么区别?
  18. 服务器收到用户提交的表单数据,到底是调用Servlet的doGet()还是doPost()方法?
  19. servlet写过么,底层是怎么处理前台传来的请求的
  20. Servlet中如何获取用户提交的查询参数或表单数据?
  21. Servlet中如何获取用户配置的初始化参数以及服务器上下文参数?
  22. 登录功能如何保证安全性。(MD5+盐)为什么用MD5,讲一下机制。还有什么保证安全的加密方式?
  23. 设计一个怎么使得验证码有效期,过了一段时间就过期了。
  24. 如何实现单点登录

答案

1. JAVA应用服务器有那些?
应用服务器主要为应用程序提供运行环境,为组件提供服务。Java 的应用服务器很多,从功能上分为两大类,JSP 服务器和 Java EE 服务器,也可分其他小类。
相对来说 Java EE 服务器的功能更加强大。相对来说 Java EE 服务器的功能更加强大。我平时就用Tomcat。
JSP 服务器有 Tomcat 、Bejy TIger 、Geronimo 、 Jetty 、Jonas 、Jrun 、Orion 、Resin。
Java EE 服务器有TongWeb 、BES ApplicaTIon Server 、 Apusic ApplicaTIon Server 、 IBM Websphere 、Sun ApplicaTIon Server 、Oracle 的 Oracle9i/AS 、Sun Java System Application Server 、Bea Weblogic 、JBoss、开源GlassFish。
2. 启动项目时如何实现不在链接里输入项目名就能启动?
修改tomcat的配置文件文件:server.xml,将path中配置的项目名称去掉

<Context docBase="userManager" path="/userManage" reloadable="true" source="org.eclipse.jst.jee.server:userManager"/>

修改成:

<Context docBase="userManager" path="/" reloadable="true" source="org.eclipse.jst.jee.server:userManager"/>

这样项目启动的时候就用不用输入项目名称了。
如: localhost:8000

3. 1分钟之内只能处理1000个请求,你怎么实现,手撕代码?

我知道的有两种方式可以实现:
(1) Application 对所有用户访问的次数计数。同时定义一个计时器,单位为一分钟。如果Application 中的用户在单位时间内超出请求次数,就拒绝处理该请求。一分钟再刷新application的值为0.
使用一个Map 维护变量:

// 泛型 String 表示用户标识,List中存放用户不同请求的时间戳。
private Map<String, List<Long>> map = new ConcurrentHashMap<>();

我们只需要在单位计数中判断 List中数量是否超出限制即可。

(2) 使用 aop 实现请求的限制,在需要限制的请求方法上加上 aop 逻辑。即可实现,思路如下:
    在这里插入图片描述
自定义注解类实现请求限制的拦截逻辑,在需要限制的方法上使用注解,超出限制后拒绝处理请求。

4. JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?(JSP)
相同点:
它们本质上都是java类
不同点:
(1)JSP经编译后变成Servlet
(2)jsp更擅长表现于页面显示,Servlet更擅长于逻辑控制
(3)Servlet的应用逻辑是在java文件中,并且完全从表现层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
联系:
JSP是Servlet技术的扩展,本质上是Servlet的简易方式。

5. 说一说四种会话跟踪技术

(1)表单隐藏域

<input type="hidden" id="xxx" value="xxx">

在表单的提交中,加入这样一段数据提交的隐藏域,用户在输入时是看不到的,但是可以通过查看网页源代码来看到,在服务端只能通过post的方式获取参数。这种方式在禁用Cookie的情况下也能工作,但是关闭浏览器后就会失效。

(2)URL重写
http://www.XXX.com/news?id=xxxx
在提交请求的URL后面加上请求需要的参数,常见的GET的请求。安全性能不高,参数可以在地址栏中看到,而且对于参数的长度有限制(1024字节),禁用Cookie的情况下也能使用,但是关闭浏览器后就会失效。

(3)Cookie

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    super.doGet(req, resp);
    
    // 新建一个Cookie
    Cookie cookie = new Cookie("name", "pwd");
    // 设置Cookie的生命周期
    cookie.setMaxAge(Integer.MAX_VALUE);
    // 将Cookie发送到客户端
    resp.addCookie(cookie);
    
    // Cookie的修改,创建一个和已有的cookie同名,然后再添加到客户端,自动修改
    Cookie secCookie = new Cookie("name", "newPwd");
     resp.addCookie(secCookie); // cookie修改
     
     // cookie的删除, 创建一个和已有的cookie同名,并设置maxAge为0,添加到客户端,就会自动删除
     Cookie thirCookie = new Cookie("name", "thirPwd");
     thirCookie.setMaxAge(0); // 修改生命周期为0
     resp.addCookie(thirCookie);  // 删除客户端cookie
}

Cookie实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。 客户端浏览器会把Cookie保存起来,并由浏览器通过域名管理不同网站的Cookie。

除了以上的Cookie属性之外,还写一些常用的属性,可以根据实际的需求设置。
在这里插入图片描述
(4)Session
Session代表服务器与浏览器的一次会话过程,这个过程是连续的,也可以时断时续的。在Servlet中,session指的是HttpSession类的对象。
Session创建时间的误解:一般的误解是Session在客户端访问的时候就会被创建,但其实 Session 的真正创建时机是服务端程序调用 HttpServletRequest.getSession(true) . 我们知道JSP中有个内置对象是session,其实那是因为在我们编写的JSP代码中,默认有这么一句:HttpSession session = HttpServletRequest.getSession(true);在将JSP编译为Servlet代码的时候,Session就会被创建。但是Session会占用服务器的内存,因此我们不需要的时候,最好将该属性置false。 <% @page session=”false”%>。
但客户端访问.html 等静态资源时,不涉及将JSP编译为Servlet,因此Session不会创建。当客户端第一次访问JSP页面时,服务器端将创建一个Session并保存在内存中,同时为客户端返回一个sessionID,以供客户端下次请求应用的时候带着sessionID来表明自己的身份。一次会话一个Session,Session通过sessionID来唯一标识。同时Session默认是在服务器的内存中维护的(虽然我们可以通过技术将session持久化),因此Session的长度不宜太大(虽然本身没有长度限制)。

Session原理:因为http是无状态的连接,Session也不能根据连接来识别是否是同一次会话,因此客户端第一次请求JSP/Servlet之后,服务端会自动生成一个名叫JSESSIONID的Cookie,该Cookie的值就是sessionID,发送到客户端,客户端再次连接的时候,会自动带上这个JSESSIONID,找到相应的session。如果客户端端禁用了Cookie,那么这个JSESSIONID会通过URL重写发送到服务端。

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Cookie: JSESSIONID=819B75C412FB029EDEF52B11484A642B
Host: localhost:8080
Referer: http://localhost:8080/market/
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36

Session的生命周期:
它的生命周期不累加的,而Cookie的生命周期是累加的,不累加的意思就是Session的计时是从最近一次访问开始的,而Cookie是从开始到结束,无论你期间是否使用。

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // TODO Auto-generated method stub
    super.doGet(req, resp);
    
    // 真正的创建一个Session
    HttpSession session = req.getSession(true);
    // 设置Session的生命周期
    session.setMaxInactiveInterval(Integer.MAX_VALUE);
  }

Session相关的属性:
在这里插入图片描述

6. 讲讲Request对象的主要方法

序号方法简述
1Cookie[] getCookies()返回一个数组,包含客户端发送该请求的所有的 Cookie 对象。
2HttpSession getSession(boolean create)返回与该请求关联的当前 HttpSession,或者如果没有当前会话,且创建是真的,则返回一个新的 session 会话。
3Enumeration getParameterNames()返回一个 String 对象的枚举,包含在该请求中包含的参数的名称。
4Object getAttribute(String name)以对象形式返回已命名属性的值,如果没有给定名称的属性存在,则返回 null。
5String getCharacterEncoding()返回请求主体中使用的字符编码的名称。
6String getMethod()返回请求的 HTTP 方法的名称,例如,GET、POST 或 PUT。
7String getParameter(String name)以字符串形式返回请求参数的值,或者如果参数不存在则返回 null。
8String getRemoteHost()返回发送请求的客户端的完全限定名称
9String getRemoteAddr()返回发送请求的客户端的互联网协议(IP)地址
10String getRemoteHost()返回发送请求的客户端的完全限定名称。

7. 过滤器有哪些作用和用法?
过滤器的常见用途:
(1)对用户的请求进行统一的认证、对访问的请求进行记录和审核
(2)对用户传输的数据过滤和替换,转换图像格式,对响应内容进行压缩,减少网络传输
(3)对用户的请求和响应进行加密处理
(4)触发资源访问事件

Filter工作流程:
在这里插入图片描述
和 Filter相关的接口定义:

// 编写一个Filter需要 实现这个接口
public interface Filter {
    
    // 初始化Filter
    public default void init(FilterConfig filterConfig) throws ServletException {}

    // Filter的逻辑实现,如果有多个Filter,必须在末尾执行 chain.doFilter(req, resp);
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException;

    // 过滤器的销毁过程,可用于关闭该过滤器中打开的资源
    public default void destroy() {}
}

// 当有多个过滤器连成一串的时候,知道真正的请求资源之前的一系列过滤
public interface FilterChain {

    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException;

}

// 过滤器的配置相关
// 做Filter初始化的时候,作为参数传入,初始化关于过滤器的配置
public interface FilterConfig {

    public String getFilterName();

    public ServletContext getServletContext();

    public String getInitParameter(String name);

    public Enumeration<String> getInitParameterNames();

}

注意事项: Filter 的执行顺序是在 web.xml 中定义的顺序,在多个Filter中的chain 中,一定在处理完当前Filter的逻辑之后,调用chain.doFilter(request, response) 转到下一个Filter

简单例子:

/**
 * 实现字符编码过滤的过滤器
 * @author ytuan
 */
public class FilterTest implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // TODO Auto-generated method stub

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");

        chain.doFilter(req, resp);
    }

}

8. 请谈谈你对Javaweb开发中的监听器的理解?

监听器示意图:
在这里插入图片描述
JavaWeb中的监听器按照事件源分为三大类:作用范围由大而小
(1)ServletContext (针对整个应用程序上下文)
(2)HttpSession (针对单个用户会话)
(3)ServletRequest (针对某一次ServletRequest )

针对这以上这三种不同作用域的对象,又把对象本身的事件类型分为三种:  
(1)域对象自身的创建和销毁事件监听
(2)域对象中属性的增删事件监听
(3)监听绑定到HttpSession域中某个对象的状态事件监听

这里的对象创建和销毁包括三个,域对象中属性的增删包括ServletContext 和 ServletRequest 两个对象。需要重点注意一下HttpSession绑定的对象,它不同于前两者。

保存在Session域中的对象可以有多种状态:
绑定(session.setAttribute(“bean”,Object))到Session中;
从 Session域中解除绑定(session.removeAttribute(“bean”));
随Session对象持久化到一个存储设备中;
随Session对象从一个存储设备中恢复;

Servlet 规范中定义了两个特殊的监听器接口"HttpSessionBindingListener和HttpSessionActivationListener"来帮助JavaBean 对象了解自己在Session域中的这些状态: ,实现这两个接口的类不需要 web.xml 文件中进行注册。
(1)HttpSessionBindingListener接口
实现了HttpSessionBindingListener接口的JavaBean对象可以感知自己被绑定到Session中和 Session中删除的事件
当对象被绑定到HttpSession对象中时,web服务器调用该对象的
void valueBound(HttpSessionBindingEvent event)方法
当对象从HttpSession对象中解除绑定时,web服务器调用该对象的void valueUnbound(HttpSessionBindingEvent event)方法
(2)HttpSessionActivationListener接口
实现了HttpSessionActivationListener接口的JavaBean对象可以感知自己被活化(反序列化)和钝化(序列化)的事件
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被钝化(序列化)之前,web服务器调用该javabean对象的
void sessionWillPassivate(HttpSessionEvent event) 方法。这样javabean对象就可以知道自己将要和HttpSession对象一起被序列化(钝化)到硬盘中.
当绑定到HttpSession对象中的javabean对象将要随HttpSession对象被活化(反序列化)之后,web服务器调用该javabean对象的
void sessionDidActive(HttpSessionEvent event)方法。这样javabean对象就可以知道自己将要和 HttpSession对象一起被反序列化(活化)回到内存中

9. 说说web.xml文件中可以配置哪些内容?

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <!-- 配置web引用程序中Servlet上下文的初始化参数 -->
    <context-param>
        <param-name>name</param-name>
        <param-value>ytuan996</param-value>
    </context-param>

    <!-- 当程序中发生错误时,跳转到哪一个页面。这里的错误可以是异常,也可以是错误码,但是只能是其中之一 -->
    <error-page>
        <exception-type></exception-type>
        <error-code>500</error-code>
        <location>error.jsp</location>
    </error-page>
    <!-- 配置自己定义的过滤器 -->
    <filter>
        <!-- 还可以在这里定义过滤器的自定义参数 -->
        <init-param>
            <param-name></param-name>
            <param-value></param-value>
        </init-param>
        <filter-name></filter-name>
        <filter-class></filter-class>
    </filter>

    <!-- 过滤器的规则 -->
    <filter-mapping>
        <filter-name></filter-name>
        <url-pattern></url-pattern>
    </filter-mapping>

    <!-- 监听器 -->
    <listener>
        <listener-class></listener-class>
    </listener>

    <!-- 配置Servlet -->
    <servlet>
        <servlet-name></servlet-name>
        <servlet-class></servlet-class>
    </servlet>

    <!-- 配置Servlet的请求路径 -->
    <servlet-mapping>
        <servlet-name></servlet-name>
        <url-pattern></url-pattern>
    </servlet-mapping>

    <!-- 配置访问应用的欢迎页面,一般是首页面 -->
    <welcome-file-list>
        <welcome-file></welcome-file>
    </welcome-file-list>
</web-app>

11. get和post的区别
本质上是无区别的,
在浏览器端,get一般由url调用,顺带一提url的限制也是浏览器的原因,事实上http标准协议对url的长度没有限制,而post一般由表单调用
在restful规范中,get被认为是幂等的,用来请求数据,而post不幂等,用来实现资源的创建
推荐阅读GET 和 POST 到底有什么区别?

12. cookie和session的区别?cookie和sessionStorage、localStroage之间的区别?
第一种回答:
图例:
在这里插入图片描述
(1)session 在服务器端,cookie 在客户端(浏览器)
(2)session 默认被存在在服务器的一个文件里(不是内存)
(3)session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是(4)如果浏览器禁用了 cookie ,同时 session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 session_id)
(5)session 可以放在 文件、数据库、或内存中都可以。

用户验证这种场合一般会用 session 因此,维持一个会话的核心就是客户端的唯一标识,即 session id

第二种回答:
类似这种面试题,实际上都属于“开放性”问题,你扯到哪里都可以。不过如果我是面试官的话,我还是希望对方能做到一点——不要混淆 session 和 session 实现。
本来 session 是一个抽象概念,开发者为了实现中断和继续等操作,将 user agent 和 server 之间一对一的交互,抽象为“会话”,进而衍生出“会话状态”,也就是 session 的概念。
而 cookie 是一个实际存在的东西,http 协议中定义在 header 中的字段。可以认为是 session 的一种后端无状态实现。而我们今天常说的 “session”,是为了绕开 cookie 的各种限制,通常借助 cookie 本身和后端存储实现的,一种更高级的会话状态实现。
所以 cookie 和 session,你可以认为是同一层次的概念,也可以认为是不同层次的概念。具体到实现,session 因为 session id 的存在,通常要借助 cookie 实现,但这并非必要,只能说是通用性较好的一种实现方案。

两种回答个人认为可以结合着来说,从代表会话的session(概念层面)到代表利用cookie实现的session(应用层面),并且结合着原理和实现来讲。

三者区别:
在这里插入图片描述

13. session怎么保证多次访问是同一个ID?关闭了浏览器怎么办?
发送http请求时,消息头中会自动携带cookie信息,这其中就会包括SESSIONID信息,所以只要我们没有关闭浏览器,消息头中都会自动携带这个信息,以供服务器访问相应的session。 但是一旦关闭浏览器cookie中的JSESSIONID信息没有了,不会自动携带这个session信息了,服务器找不到对应的session,就会自动添加新的session,这个新添加的session显然不是我们需要的。这时就需要手动添加在cookie中添加JSESSIONID信息来防止关闭失效。

//在servletA中设置cookie
public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        HttpSession session = request.getSession();
        session.setAttribute("name", "张三");
        out.println("创建一个session并放入姓名属性");
        //手动添加cookie,保存JSESSIONID信息
        Cookie cookie = new Cookie("JSESSIONID", session.getId());
        cookie.setMaxAge(60*30);//设置cookie的生命周期为30min
    相应 给浏览器一个 session id   
        response.addCookie(cookie);
    }


//在servletB中就可以正常访问session了
        public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        HttpSession httpSession = request.getSession();
        String name = (String) httpSession.getAttribute("name");
        out.println("name = "+name);
    }

14. 谈谈Session的save()、update()、merge()、lock()、saveOrUpdate()和persist()方法分别是做什么的?有什么区别?
Hibernate的对象有三种状态:瞬时态(transient)、持久态(persistent)和游离态(detached),瞬时态的实例可以通过调用save()、persist()或者saveOrUpdate()方法变成持久态;游离态的实例可以通过调用 update()、saveOrUpdate()、lock()或者replicate()变成持久态。save()和persist()将会引发SQL的INSERT语句,而update()或merge()会引发UPDATE语句。save()和update()的区别在于一个是将瞬时态对象变成持久态,一个是将游离态对象变为持久态。merge()方法可以完成save()和update()方法的功能,它的意图是将新的状态合并到已有的持久化对象上或创建新的持久化对象。对于persist()方法,按照官方文档的说明:① persist()方法把一个瞬时态的实例持久化,但是并不保证标识符被立刻填入到持久化实例中,标识符的填入可能被推迟到flush的时间;② persist()方法保证当它在一个事务外部被调用的时候并不触发一个INSERT语句,当需要封装一个长会话流程的时候,persist()方法是很有必要的;③ save()方法不保证第②条,它要返回标识符,所以它会立即执行INSERT语句,不管是在事务内部还是外部。至于lock()方法和update()方法的区别,update()方法是把一个已经更改过的脱管状态的对象变成持久状态;lock()方法是把一个没有更改过的脱管状态的对象变成持久状态。

15. 如何设置请求的编码以及响应内容的类型?
设置请求的编码:request.setCharacterEncoding(String)
例如:request.setCharacterEncoding(“utf-8”)
设置响应内容的类型:response.setContentType(String) or response.setHeader(String, String)
例如:response.setContentType(“text/html; charset=utf-8”)

16. Servlet相关接口 , Servlet里面有哪些方法 ? Servlet执行时一般实现哪几个方法?
(1)init() 方法
在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。
缺省的 init() 方法通常是符合要求的,但也可以用定制 init() 方法来覆盖它,典型的是管理服务器端资源。
(2)service() 方法
service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个"请求"(ServletRequest)对象和一个"响应"(ServletResponse)对象作为参数。 在 HttpServlet 中已存在 service() 方法。缺省的服务功能是调用与 HTTP 请求的方法相应的 do 功能。
(3)doGet() 方法
当一个客户通过 HTML 表单发出一个 HTTP GET 请求或直接请求一个 URL 时,doGet() 方法被调用。与 GET 请求相关的参数添加到 URL 的后面,并与这个请求一起发送。当不会修改服务器端的数据时,应该使用 doGet() 方法。
(4)doPost() 方法
当一个客户通过 HTML 表单发出一个 HTTP POST 请求时,doPost() 方法被调用。与 POST 请求相关的参数作为一个单独的 HTTP 请求从浏览器发送到服务器。当需要修改服务器端的数据时,应该使用 doPost() 方法。
(5)destroy() 方法
destroy() 方法仅执行一次,即在服务器停止且卸装 Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。
(6)getServletConfig() 方法
getServletConfig() 方法返回一个 ServletConfig 对象,该对象用来返回初始化参数和 ServletContext。ServletContext 接口提供有关 servlet 的环境信息。
(7)getServletInfo() 方法
getServletInfo() 方法是一个可选的方法,它提供有关 servlet 的信息,如作者、版本、版权。

当服务器调用 sevlet 的 service()、doGet() 和 doPost() 这三个方法时,均需要 “请求”和“响应”对象作为参数。“请求”对象提供有关请求的信息,而“响应”对象提供了一个将响应信息返回给浏览器的一个通信途径。

执行过程中调用的方法:

public void init(ServletConfig config) 
public ServletConfig getServletConfig() 
public String getServletInfo() 
public void service(ServletRequest request,ServletResponse response) 
public void destroy()

17. Servlet 3中的异步处理指的是什么?

在Servlet 3中引入了一项新的技术可以让Servlet异步处理请求。有人可能会质疑,既然都有多线程了,还需要异步处理请求吗?答案是肯定的,因为如果一个任务处理时间相当长,那么Servlet或Filter会一直占用着请求处理线程直到任务结束,随着并发用户的增加,容器将会遭遇线程超出的风险,这种情况下很多的请求将会被堆积起来而后续的请求可能会遭遇拒绝服务,直到有资源可以处理请求为止。异步特性可以帮助应用节省容器中的线程,特别适合执行时间长而且用户需要得到结果的任务,如果用户不需要得到结果则直接将一个Runnable对象交给Executor并立即返回即可。

补充:多线程在Java诞生初期无疑是一个亮点,而Servlet单实例多线程的工作方式也曾为其赢得美名,然而技术的发展往往会颠覆我们很多的认知,就如同当年爱因斯坦的相对论颠覆了牛顿的经典力学一般。事实上,异步处理绝不是Serlvet 3首创,如果你了解Node.js的话,对Servlet 3的这个重要改进就不以为奇了。

下面是一个支持异步处理请求的Servlet的例子。

package chimomo.learning.java.code.jsp;
 
import javax.servlet.AsyncContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
/**
 * @author Created by Chimomo
 */
@WebServlet(urlPatterns = {"/async"}, asyncSupported = true)
public class AsyncServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
 
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
        // 开启Tomcat异步Servlet支持
        req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
 
        // 启动异步处理的上下文
        final AsyncContext ctx = req.startAsync();
 
        // ctx.setTimeout(30000);
        ctx.start(() -> {
            // 在此处添加异步处理的代码
            ctx.complete();
        });
    }
}

此题答案来自 Java - Servlet 3中的异步处理指的是什么?

18. servlet生命周期?servlet是单例模式么?为什么是单例?Servlet和cgi有什么区别?

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
(1)Servlet 通过调用 init () 方法进行初始化。
(2)Servlet 调用 service() 方法来处理客户端的请求。
(3)Servlet 通过调用 destroy() 方法终止(结束)。
(4)最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
在这里插入图片描述
生命周期的方法详细解释可以参考 菜鸟教程部分

毫无疑问Servlet是单例的(默认情况下),具体来讲是收到以下几个因素的影响:
(1)是否在分布式环境中部署
(2)是否实现SingleThreadModel,如果实现则最多会创建20个实例
(3)在web.xml中声明了几次,即使同一个Servlet,如果声明多次,也会生成多个实例。

而它之所以是单例是因为tomcat代码中(StandardWrapper类)定义是单例的
具体分析过程参见Servlet到底是单例还是多例你了解吗?

还有一个问题关于Cgi,本人从来没听说过cgi故暂时不比较了。

19. 服务器收到用户提交的表单数据,到底是调用Servlet的doGet()还是doPost()方法?
HTML的<form>元素有一个method属性,用来指定提交表单的方式,其值可以是get或post。我们自定义的Servlet一般情况下会重写doGet()或doPost()两个方法之一或全部,如果是GET请求就调用doGet()方法,如果是POST请求就调用doPost()方法,那为什么这样呢?我们自定义的Servlet通常继承自HttpServlet,HttpServlet继承自GenericServlet并重写了其中的service()方法,这个方法是Servlet接口中定义的。HttpServlet重写的service()方法会先获取用户请求的方法,然后根据请求方法调用doGet()、doPost()、doPut()、doDelete()等方法,如果在自定义Servlet中重写了这些方法,那么显然会调用重写过的(自定义的)方法,这显然是对模板方法模式的应用。当然,自定义Servlet中也可以直接重写service()方法,那么不管是哪种方式的请求,都可以通过自己的代码进行处理,这对于不区分请求方法的场景比较合适。

20. servlet写过么,底层是怎么处理前台传来的请求的

(1)浏览器向服务器发出GET请求(请求服务器ServletA)
(2)服务器上的容器逻辑接收到该url,根据该url判断为Servlet请求,此时容器逻辑将产生两个对象:请求对象(HttpServletRequest)和响应对象(HttpServletResponce)
(3)容器逻辑根据url找到目标Servlet(本示例目标Servlet为ServletA),且创建一个线程A
(4)容器逻辑将刚才创建的请求对象和响应对象传递给线程A
(5)容器逻辑调用Servlet的service()方法
(6)service()方法根据请求类型(本示例为GET请求)调用doGet()(本示例调用doGet())或doPost()方法
(7)doGet()执行完后,将结果返回给容器逻辑
(8)线程A被销毁或被放在线程池中

其中需要注意的是:
(1)在容器中的每个Servlet原则上只有一个实例
(2)每个请求对应一个线程
(3)多个线程可作用于同一个Servlet(这是造成Servlet线程不安全的根本原因)
(4)每个线程一旦执行完任务,就被销毁或放在线程池中等待回收

不过上面都是浅析,要深究就要从tomcat原理层面说起,推荐阅读:Servlet工作原理

21. Servlet中如何获取用户提交的查询参数或表单数据?

三种方法:
(1)String str=request.getParameter("参数名称")方法可以获得参数值。
(2)String[] str=request.getParameterValues("参数名称")可以获得复选框类的数据
(3)Map map = request.getParameterMap(); 可以获得提交的全部数据。其中map的key是字符串类型,value是字符串数组类型!

22. Servlet中如何获取用户配置的初始化参数以及服务器上下文参数?

获取Servlet初始化参数:
(1)在Servlet中直接调用getinitparaneterr()方法来初始化参数
(2)通过ServletConfig借口的gerInitParameter()方法.
getServletConfig该方法定义在Servlet接口中,返回ServletConfig接口的引用,所有Servlet都继承了该方法.当容器实例化Servlet之前,会从web.xml中读取这个Servlet的初始化参数,并交给ServletConfig,然后再调用init方法时,容器会传送这个ServletConfig的引用到Servlet. 每个Servlet都会有一个ServletConfig引用.一旦有了ServletConfig的引用就可以调用getInitParameter方法来取得Servlet中设置的初始化参数

取得上下文初始化参数?
(1)servlet的ServletConfig对象拥有该servlet的ServletContext的一个引用,所以可这样取得上下文初始化参数:
getServletConfig().getServletContext().getInitParameter()
(2)也可以在Servlet中直接调用getServletContext().getInitParameter()

23. 登录功能如何保证安全性。(MD5+盐)为什么用MD5,讲一下机制。还有什么保证安全的加密方式?
信息摘要是把明文内容按照某种规则生成一段哈希值,即使明文信息改变了一点生成的结果也会完全不同,md5就是一种信息摘要的实现,可以把任意长度的明文生成长度为128位的哈希值
摘要哈希生成分三步:
(1)收集相关业务参数,在这里是金额和目标账户。当然,实际应用中的参数肯定比这多得多,这里只是做了简化。
(2)按照规则,把参数名和参数值拼接成一个字符串,同时把给定的密钥也拼接起来。之所以需要密钥,是因为攻击者也可能获知拼接规则。
(3)利用 MD5 算法,从原文生成哈希值。MD5 生成的哈希值是 128 位的二进制数,也就是 32 位的十六进制数。

除此之外还有Base64位加密(可加密解密)、sha1加密(加密不可逆)、AES加密(需要密钥才能解密)、RSA加密(公钥加密,私钥解密)

推荐阅读:漫画趣解MD5算法

24. 设计一个怎么使得验证码有效期,过了一段时间就过期了。
看存储在哪里,如果是cookie那就设置cookie的过期时间,如果是redis那就设置redis中值的过期时间。

25. 如何实现单点登录
这个要不会看大佬写的文章就完事了 单点登录(SSO)看这一篇就够了

我在这里只总结一下:
在同样一个顶域名下:
客户端将cookie的域设置为顶域,比如默认情况下cookie是属于a.baidu.com或者b.baidu.com的,改成baidu.com。 后端则可以使用Spring-Session共享会话。
在不同域下的登陆:
假设有登陆服务 SSO,用户访问另一个app服务,没有登陆,然后就跳转到SSO,SSO假设也没有登陆,然后输入账密开始登陆,成功后SSO记录好已经登陆的状态,并跳转到app服务(还携带了一个ST(Service Ticket)参数),app拿到st之后就去找sso服务进行验证,验证成功后就保存已登陆状态。

【学习系列】SAP RAP 8:行为定义-Authorization Control 本文介绍了SAP RAP中的授权控制实现方法,主要分为读取操作和修改操作两种场景。读取操作通过ABAP CDS的数据控制语言(DCL)实现,使用访问控制对象限制数据可见范围;修改操作则通过行为定义中的权限检查,包括全局授权、实例授权和预检查三种方式。文章详细说明了如何使用DCL创建访问控制规则,并通过实例演示了全局授权和实例授权的代码实现方法,以及预检查功能在数据更新前的权限验证。这些授权控制机制共同确保用户只能访问和操作其权限范围内的数据,有效保障系统安全性。 阅读详情

相关推荐

Vienna整流器SVPWM调制:7扇区划分与中点电位平衡的MATLAB/Simulink实现解析

本文深入解析Vienna整流器的SVPWM调制技术,重点介绍7扇区划分与中点电位平衡的MATLAB/Simulink实现方法。通过详细的算法推导和仿真模型构建,帮助工程师掌握整流器闭环控制策略,优化功率转换效率。内容涵盖扇区判断逻辑、矢量作用时间计算及中点电位平衡的PID调节等核心技术要点。

weixin_33937499的博客 387

JavaWeb详讲

ServletContext 是一个接口,它表示 Servlet 上下文对象一个 web 工程,只有一个 ServletContext 对象实例。ServletContext 对象是一个域对象。(是可以像 Map 一样存取数据的对象,叫域对象。ServletContext 是在 web 工程部署启动的时候创建。在web 工程停止的时候销毁< %!声明 Java ⽅法 % >同时配置多个 Filter,Filter 的调⽤顺序是由 web.xml 中的配置顺序来决定的,写在上⾯的配置先调。

qq_42925518的博客 2766

Ubuntu22.04 上ROS1 Noetic的安装与常见问题解决

本文详细介绍了在Ubuntu 22.04系统上安装ROS1 Noetic的两种主要方法:推荐使用社区维护的适配仓库进行便捷安装,并提供了从源码编译的备选方案。文章重点解决了安装后编译ROS包时极易遇到的“shared_mutex”等C++标准库冲突问题,并给出了其他常见错误的排查与解决指南,帮助开发者顺利搭建稳定的ROS开发环境。

weixin_29295541的博客 363

【Java Web编程 一】Java Web编程概述

其实可以这么理解,Web开发就是计算机网络应用层里开发的一个专供Http协议使用的应用程序。在应用层 这篇Blog里我们提到过应用层的定义 应用层是为了解决某一类应用问题,而问题的解决又是通过位于不同主机中的多个应用进程之间的通信和协同工作来完成的。这些服务通常使用对应的协议,常见的几种服务协议有(HTTP,FTP/TFTP, TELENT)无论是哪种服务,都需要域名服务器(DNS)来解析ip地址才能通信以提供服务 所以其实Web请求实际上是客户使用浏览器充当客户端,通过http协议与部署了Web应用程

MaoLin Tian's Blog 979

LabVIEW波形图表时间轴设置:三种方法实现横坐标显示日期时间

在数据采集与设备监控场景中,波形图表的横坐标默认显示采样点序号而非日期时间,这给工程师分析历史数据带来不便。理解时间标识(Timestamp)的本质——一个自1904年起算的双精度秒数,是解决问题的关键。时间轴设置既是显示格式问题,更是数据层与显示层的协同设计。通过属性节点动态生成刻度标签、将时间标识数组直接作为X轴数据、或使用波形数据(Waveform)类型绑定t0与dt,分别适用于固定点数显示、非等间隔事件记录和等间隔连续采样等不同需求。合理选择方案并注意缓冲区预分配与时间连续性处理,可让波形图表准确呈

weixin_33416697的博客 253

智能车竞赛走马观碑组:赛道元素与目标板识别策略解析

本文深入解析了智能车竞赛走马观碑组别的核心挑战与策略。重点探讨了如何应对PVC赛道的光照、磨损及无边界线等复杂环境,并详细阐述了基于红色引导区的目标板高效识别流程。通过平衡轻量化模型选择、实时决策与精准控制,实现赛道元素适应性与目标板识别策略的优化,为参赛者提供了一套完整的系统集成与实战调参思路。

x8y9z0的博客 459

javaweb-jsp编程例题

jsp编程题

m0_58467275的博客 1688

YOLO26 无损剪枝:稀疏训练 + 结构化通道裁剪

YOLO26 无损剪枝链路:YOLO26s (7.2M) → 稀疏训练 30 epoch (L1 on BN gamma, lam=0.01) → 阈值剪枝 50% (gamma<quantile(0.5)) → 结构化重建 Conv+BN → 微调 10 epoch (lr=1e-4) → YOLO26n (1.9M, mAP50 0.808, 仅降 0.6%)。核心贡献:稀疏训练让 BN gamma 趋向二元分布 (近 0 或近 1), 剪枝阈值可直接取 median 自动 50% 压缩。

m0_64661770的博客 263

TYUT太原理工大学2022javaweb编程大题

编程题就是给你一个表,几个jsp页面的显示图,让你根据题意写出MVC编码其中,MVC包含了3部分:1、视图(V)——编写jsp页面(HTML、CSS代码)2、模型(M)——编写javabean.java(字符串处理、数据库操作)3、控制器(C)——编写servlet.java(封装对HTTP请求的处理)主要出的题型就这么几类:用户注册、用户登录、用户留言、图书管理题目简单的话,只会要求我们补充MVC,难一点的话会让我们写全部的MVC,这次大题30分,估计难一点大家能理解的理解,理解不了就背下来(都是手打的代

m0_55298718的博客 3万+

在 PFCG 中完成 SAP Fiori 对象分配:把 Business Catalog、Space、Page 与授权一次讲透

摘要:SAP Fiori权限配置的关键在于理解Business Catalog与Space的分层授权逻辑。传统SAP GUI中直接分配事务码的方式不适用于Fiori环境,必须通过PFCG为角色分配Catalog(提供应用访问权限)和Space(控制首页展示结构)。Catalog会自动解析所需OData服务授权,而Space则决定用户在Launchpad中看到的导航布局。常见错误包括直接分配Page而非其所属Space,或仅配置Space而忽略Catalog。最佳实践是优先使用Catalog驱动授权提议,再在

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 327

机器人外接 USB 音响完整配置指南

摘要:本文介绍了为机器人配置外接音响的方法。由于内置音响音量小且有散热噪音,建议使用Type-C接口外接音响。配置步骤包括:1)通过aplay -l命令查看音响端口信息;2)使用aplay命令测试音频播放;3)通过amixer命令设置音量。关键点在于正确识别音响的卡号(如card 2)和设备号(如device 0),并在命令中准确配置这些参数即可实现外接音响的正常使用。

m0_47999117的博客 356

JavaWeb测试题

JavaWeb以及MySql的复习题

m0_56527651的博客 885

YOLOv8交通监控全方案:精准车辆计数+违章(闯红灯/压线)识别,拿来就落地!

作为深耕计算机视觉落地的开发者,我见过太多交通监控的“伪智能”——传统系统要么车辆计数不准(漏计/重复计数),要么违章识别滞后(靠人工回看录像),甚至连“闯红灯、压线”这类常见违章都无法实时判定,完全满足不了实际交通管理需求。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 646

2025年金九银十 Java高级面试指南(1 到 5 年 Java 面试者必备)

对于这些问题我都整理了答案,记录在这个 Java 学习笔记里,这份笔记包括了 Spring,JVM,java 基础,Java 集合,Java 并发编程,微服务,网络,Kafka,分布式,Redis,大厂面试解决方案,分布式事务,设计模式,算法,数据结构,MySQL 等。

m0_69632475的博客 976

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java面试题合集,Java高级进阶学习资料

分布式技术专题+面试解析+相关的手写和学习的笔记pdf还有更多Java笔记分享如下:最后分布式技术专题+面试解析+相关的手写和学习的笔记pdf还有更多Java笔记分享如下:[外链图片转存中…(img-WmxpYjpC-1709296319945)]本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录。

m0_61041374的博客 410

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

吐血整理!java跑批任务防止重复

第1大技能:程序设计和开发 数据结构和算法:常用数据结构,排序,检索等 面向对象编程、设计模式,掌握建模语言和建模工具:UML、MVC编程思想 高质量编码能力:重用性,低耦合,可扩展性,高性能,可维护性,安全性高 集成开发,版本控制,构建等工具:eclipse, svn, maven 等 脚本语言:Perl,PHP, Ruby, Python, Groovy等 第2大技能:Java开发 Java语言基础:异常处理,泛型,reflection,annotation; Java基本

m0_57624876的博客 360

Java面试高频题:Spring Boot+Sentinel+Nacos高并发已撸完

2021都说工作不好找,也是对开发人员的要求变高。前段时间自己有整理了一些Java后端开发面试常问的高频考点问题做成一份PDF文档(1000道高频题),同时也整理一些图文解析及笔记,今天在...

区块链大本营 4824

javaweb从入门到实战,附超全教程文档

01 并发宝典:面试专题 面试专题分为四个部分,分别如下 Synchronized 相关问题 可重入锁 ReentrantLock 及其他显式锁相关问题 Java 线程池相关问题 Java 内存模型相关问题 1.1 Synchronized 相关问题(这里整理了八问) 问题一:Synchronized用过吗?其原理是什么? 问题二:你刚才提到获取对象的锁 ,这个“ 锁 ”到底是什么?如何确定对象的锁? 问题 三:什么是可重入性 , 为什么说Synchronized是可重入锁?

碧海朝天素 248

2021 上半年 1000 道大厂高频面试题汇总(Java 岗)

金九银十”面试求职高峰期,在金三银四时也参与过不少面试,2021 都说工作不好找,也是对开发人员的要求变高。前段时间自己有整理了一些 Java 后端开发面试常问的高频考点问题做成一份 PDF 文档(1000 道高频题),同时也整理一些图文解析及笔记,今天在这免费分享给大家,希望大家在即将的十月面试做好复习,长期的积累和短期的突击让自己能找到一个满意的工作!

Park33的博客 84

java绝对值怎么取

前言 疫情过去,真正的春暖花开又回来了,时不时的可以和朋友约个饭,感慨今年的工作竞争压力很大,工作很不好找。作为一个开发人员,你是否面上了理想的公司,拿到了理想中的薪资? 作为程序员,跳槽就是最好的涨薪方式。前提当然是你有足够实力,而不是只会纸上谈兵。面试准备不充分,就是浪费时间,更是对自己不负责任。 今天给大家分享一份由粉丝投稿,我精心整理出来的一份1200页Java架构面试专题(文末见面试答案),绝大部分都是一线大厂的面试真题,可以根据这份面试专题查漏补缺,希望能够帮助你尽快找到工作! Java架构师成

m0_57473261的博客 492

三流面试聊技术,二流面试聊框架,一流面试…

2021都说工作不好找,其实也是对开发人员的要求变高了。正好前段时间自己有整理了一些Java后端开发面试常问的高频考点问题做成一份PDF文档(1000道高频题),同时也整理一些图文解析及笔...

架构师小秘圈 260

2026 精选 Java 八股文及答案,全面覆盖,直击面试

伴随着互联网行业寒冬的到来,各大企业都不敢大肆招聘,都是选择收缩发展,加上程序员内部越来越卷,要求越来越高了,要想在 IT 行业继续发展进阶拿高薪的话,提升自己是必不可少的。尤其是经历了前不久的裁员事件,以后找工作只会更难,所以,咱们程序员要做的就是进行一个面试+技术的一个储备,才能在面试时,好好拼一把。

sjkflw121150的博客 176

金三银四高频 Java 面试题及答案整理 (建议收藏)

伴随着互联网行业寒冬的到来,各大企业都不敢大肆招聘,都是选择收缩发展,加上程序员内部越来越卷,要求越来越高了,要想在 IT 行业继续发展进阶拿高薪的话,提升自己是必不可少的。尤其是经历了前不久的裁员事件,以后找工作只会更难,所以,咱们程序员要做的就是进行一个面试+技术的一个储备,才能在面试时,好好拼一把。

m0_69632475的博客 434

Java面试备战利器:通义千问1.5-1.8B-Chat-GPTQ-Int4模拟面试与八股文解析

本文介绍了如何利用星图GPU平台,一键自动化部署通义千问1.5-1.8B-Chat-GPTQ-Int4镜像,快速搭建一个专业的Java面试模拟环境。该AI助手可作为私人面试陪练,通过智能问答与深度解析,帮助开发者高效复习Java核心知识点与八股文,提升面试准备效率。

weixin_35916518的博客 66

Java平均工资再次上涨!这份宝藏资料限量免费送!

一年一度的金三银四跳槽季,随着市场对Java 开发人员的需求量逐年递增,对开发人员的技能要求也在提高。近日,笔者把Java 后端开发中常见的面试题、高频考点进行了整理,总共收集...

程序人生的博客 286

Java 面试题及答案整理 (金九银十最新版),大厂高频1000题

伴随着互联网行业寒冬的到来,各大企业都不敢大肆招聘,都是选择收缩发展,加上程序员内部越来越卷,要求越来越高了,要想在 IT 行业继续发展进阶拿高薪的话,提升自己是必不可少的。尤其是经历了前不久的,以后找工作只会更难,所以,咱们程序员要做的就是进行一个面试+技术的一个储备,才能在面试时,好好拼一把。

sjkflw121150的博客 230

Java面试高频题:Spring Boot+JVM+Nacos高并发+高可用已撸完​

2021都说工作不好找,也是对开发人员的要求变高。前段时间自己有整理了一些Java后端开发面试常问的高频考点问题做成一份PDF文档(1000道高频题),同时也整理一些图文解析及笔记,今天在...

优快云云计算 260
上一篇: SpringCloud思维导图与学习SpringCloud系列教程推荐
下一篇: 实例演示:为什么需要重写hashcode和equals
金心靖晨
博客等级 码龄9年 460粉丝 211原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值