Servlet-学习笔记-上

1. Servlet简介

1.1 动态资源和静态资源

静态资源

  • 无需再程序运行时通过代码生成的资源,再程序运行之前就写好的资源,例如html css js img ,音频文件和视频文件

动态资源

  • 需要在程序运行时通过代码运行生成的资源,在程序运行之前无法确定的数据,运行时动态生成,例如Servlet,Thymeleaf......

  • 动态资源指的不是视图上的动画效果或者是简单的人机交互效果

生活举例

  • 去蛋糕店买蛋糕

    • 直接买柜台上已经做好的:静态资源

    • 和柜员说要求后现场制作:动态资源

1.2 Servlet简介

Servlet(server applet) 是运行在服务端(tomcat)的java小程序,是sun公司提供的一套定义动态资源规范;从代码上来讲Servlet就是一个接口

  • 用来接收从、处理客户端请求、响应给浏览器的动态资源。 在整个Web应用中,Servlet主要负责接收处理请求、协同调度功能以及响应数据。我们可以把Servlet称为Web应用中的控制器

  • 不是所有的JAVA项目都能用于处理客户端请求,能处理客户端请求并做出相应的一套技术标准就是Servlet

  • Servlet是运行在服务端的,所以Servlet必须在WEB应用中开发且在Tomcat这样的服务器容器中进行

请求响应与HttpServletRequest和HttpServletResponce之间的对应关系

package com.mdklea.servlet;
​
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
​
import java.io.IOException;
import java.io.PrintWriter;
​
/**
 * 1 创建javaWEB 项目,同时将tomcat添加为当前项目的依赖
 * 2 重写service方法 service(HeepServletRequest req, HttpServletResponce resp)
 * 3 在service方法中,定义业务处理代码
 * 4 在web.xml中,配置Servlet 对应的请求映射路径
 *
 * 1 servlet-api.jar 导入问题
 *      servlet-api 编码的时候需要,运行的时候,在服务器的环境中,由服务软件(Tomcat)提供
 *      因此,我们的JAVAWEB项目中,在打包/构建的时候,是无需携带servlet-api的jar包的
 * 2 Content-Type 响应头的问题
 *      MIME类型响应头,媒体类型,文件类型,相应的数据类型
 *      MIME类型用于告诉客户端相应的数据是什么类型的数据,客户端以此类型决定用什么方式解析响应体
 */
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1. 从request 对象中获取请求中的任何信息(username参数)
        String username = request.getParameter("username");//根据参数名获取参数值,无论url是在url?后还是在请求体中
        String inFo = "yes";
        //2. 处理业务的代码
        if ("md2".equals(username)) {
            inFo = "no";
        }
        //3. 将要相应的数据放入responce
        //应该设置Content-Type响应头
        //response.setHeader("Content-Type","text/html");
        response.setContentType("text/html");
        PrintWriter writer = response.getWriter(); //该方法是返回一个向响应体中返回字符串的打印流
        writer.write(inFo);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
         version="6.0">
​
<!--   模糊匹配-->
<!--    /   匹配全部,不包含jsp文件-->
<!--    /*  匹配全部,包含jsp文件-->
<!--    /a/*    匹配前缀,后缀模糊-->
<!--    *.action    匹配后缀,前缀模糊-->
    <servlet>
        <servlet-name>userServlet</servlet-name>
        <servlet-class>com.mdklea.servlet.UserServlet</servlet-class>
    </servlet>
​
    <servlet>
        <servlet-name>servlet1</servlet-name>
        <servlet-class>com.mdklea.servlet.Servlet1</servlet-class>
    </servlet>
​
    <servlet-mapping>
        <servlet-name>userServlet</servlet-name>
        <url-pattern>/userServlet</url-pattern>
    </servlet-mapping>
​
    <servlet-mapping>
        <servlet-name>servlet1</servlet-name>
        <url-pattern>/s1</url-pattern>
    </servlet-mapping>
</web-app>

2. Servlet注解方式配置

@webServlet注解源码

value 与 urlPatterns互为别名

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
​
package jakarta.servlet.annotation;
​
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
​
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebServlet {
    String name() default "";
​
    String[] value() default {};
​
    String[] urlPatterns() default {};
​
    int loadOnStartup() default -1;
​
    WebInitParam[] initParams() default {};
​
    boolean asyncSupported() default false;
​
    String smallIcon() default "";
​
    String largeIcon() default "";
​
    String description() default "";
​
    String displayName() default "";
}

3. Servlet生命周期

3.1 声明周期简介

什么是Servlet的生命周期

  • 应用程序中的对象不仅在空间上有层次结构的关系,在时间上也会因为处于程序运行过程中的不同阶段而表现出不同状态和不同行为-----这就是对象的生命周期。

  • 简单的叙述生命周期,就是对象在容器中从开始创建到销毁的过程。

servlet容器

  • Servlet对象是Servlet容器创建的,生命周期方法都是由容器(目前我们使用的是Tomcat)调用的。这一点和我们之前所编写的代码有很大不同。在今后的学习中我们会看到,越来越多的对象交给容器或框架来创建,越来越多的方法由容器或框架来调用,开发人员要尽可能多的将精力放在业务逻辑的实现上。

servlet主要声明周期执行特点

3.2 线性安全问题

3.3 defaultServlet

package com.mdklea.servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.PushBuilder;
​
import java.io.IOException;
​
/**
 * 1 实例化        构造器     第一次请求
 * 2 初始化        init       构造完毕
 * 3 接收请求 服务  service    每次请求
 * 4 销毁          destory    关闭服务
 *
 * Servlet在Tomcat中是单例的
 * Servlet的成员变量在多个线程之中是共享的
 * 不建议在Service方法中修改成员变量,在并发请求时,会引发线程安全问题
 */
public class ServletLifeCicle extends HttpServlet {
    public ServletLifeCicle() {
    }
​
    @Override
    public void init() throws ServletException {
        super.init();
    }
​
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
​
    }
​
    @Override
    public void destroy() {
        super.destroy();
    }
}
<servlet>
    <servlet-name>ServletLifeCycle</servlet-name>
    <servlet-class>com.mdklea.servlet.ServletLifeCicle</servlet-class>
    <!--
        默认值是-1,含义是 tomcat 启动时不会实例化该servlet
        其他正整数 15 含义是 tomcat 在启动时,实例化该servlet的顺序,如果序号冲突了,tomcat会自动协调启动顺序
    -->
    <load-on-startup>-1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>ServletLifeCycle</servlet-name>
    <url-pattern>/ServletLifeCycle</url-pattern>
</servlet-mapping>

4. Servlet继承结构

4.1 Servlet接口

/*
    1. 顶级的Servlet接口
    public interface Servlet {
    // 初始化方法,构造完毕后,由tomcat自动调用完成初始化功能的方法
    void init(ServletConfig var1) throws ServletException;
​
    // 获取ServletConfig 对象的方法
    ServletConfig getServletConfig();
​
    // 接收用户请求,向用户响应信息的方法
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
​
    // 返回Servlet字符串形式描述信息的方法
    String getServletInfo();
​
    // Servlet在回收前,由tomcat调用的销毁方法,往往用于做资源的释放工作
    void destroy();
}
 */

4.2 GenericServlet抽象类

/*
    2. 抽象的类     GenericServlet  侧重于除了service方法以外的其他方法的基础处理
    public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
        private transient ServletConfig config;
​
        public void destroy() {
            // 将抽象方法重写为普通方法,在方法内部没有任何的代码实现
            // 平庸实现
        }
        //tomcat在调用init 方法时,会读取配置信息进入一个ServletConfig 对象并将该对象传入init方法
        public void init(ServletConfig config) throws ServletException {
            // 将config 对象存储为当前的属性
            this.config = config;
            // 调用了重载的无参init
            this.init();
        }
        public void init() throws ServletException {
            // 重载的初始化方法,我们重写初始化方法时对应的方法
        }
        // 返回ServletConfig的方法
        public ServletConfig getServletConfig() {
            return this.config;
        }
        // 再次抽象声明service方法
        public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
​
    }
 */

4.3 HttpServlet 抽象类

/*
    // 抽象的类     HttpServlet
    public abstract class HttpServlet extends GenericServlet {
            //
            public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
​
            // 参数的父转子
            HttpServletRequest request;
            HttpServletResponse response;
            try {
                request = (HttpServletRequest)req;
                response = (HttpServletResponse)res;
            } catch (ClassCastException var6) {
                throw new ServletException(lStrings.getString("http.non_http"));
            }
            // 调用重载的service 方法
            this.service(request, response);
        }
​
    //
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // 获取请求的方式  GET POST PUT DELETE OPTIONS
            String method = req.getMethod();
            long lastModified;
​
            // 根据请求方式调用对应的do。。。方法   故意响应405
            if (method.equals("GET")) {
                lastModified = this.getLastModified(req);
                if (lastModified == -1L) {
                    this.doGet(req, resp);
                } else {
                    long ifModifiedSince;
                    try {
                        ifModifiedSince = req.getDateHeader("If-Modified-Since");
                    } catch (IllegalArgumentException var9) {
                        ifModifiedSince = -1L;
                    }
​
                    if (ifModifiedSince < lastModified / 1000L * 1000L) {
                        this.maybeSetLastModified(resp, lastModified);
                        this.doGet(req, resp);
                    } else {
                        resp.setStatus(304);
                    }
                }
            } else if (method.equals("HEAD")) {
                lastModified = this.getLastModified(req);
                this.maybeSetLastModified(resp, lastModified);
                this.doHead(req, resp);
            } else if (method.equals("POST")) {
                this.doPost(req, resp);
            } else if (method.equals("PUT")) {
                this.doPut(req, resp);
            } else if (method.equals("DELETE")) {
                this.doDelete(req, resp);
            } else if (method.equals("OPTIONS")) {
                this.doOptions(req, resp);
            } else if (method.equals("TRACE")) {
                this.doTrace(req, resp);
            } else {
                String errMsg = lStrings.getString("http.method_not_implemented");
                Object[] errArgs = new Object[]{method};
                errMsg = MessageFormat.format(errMsg, errArgs);
                resp.sendError(501, errMsg);
            }
​
        }
​
        //doGet方法
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String msg = lStrings.getString("http.method_get_not_supported");
            this.sendMethodNotAllowed(req, resp, msg);
        }
        //sendMethodNotAllowed
        private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
            String protocol = req.getProtocol();
            // 故意响应405
            if (protocol.length() != 0 && !protocol.endsWith("0.9") && !protocol.endsWith("1.0")) {
                resp.sendError(405, msg);
            } else {
                resp.sendError(400, msg);
            }
        }
​
        // 自定义Servlet
        Class Servlet1 extends HttpServlet {
            protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                // 接收用户请求信息
​
                // 做出响应
            }
        }
    }
    1 部分程序员推荐在servlet中重写do*** 方法处理请求,理由:service方法中可能做了一些处理,如果我们直接重写service的话,父类中service
      方法中的一些处理可能会失效
    2 目前直接重写service 也没有什么问题
    3 后续使用SpringMVC框架后,我们则无需继承HttpServlet,处理请求的方法也无需是 do*** service
    4 如果doGet 和doPost 方法中,我们定义的代码都一样,可以让一个方法直接调用另一个方法
​
    掌握的技能 继承HttpServlet后,要么重写service 方法,要么重写 doGet/doPost
 */

5. ServletConfig 的使用

ServletConfig 是什么

  • 为Servlet提供初始配置参数的一种对象,每个Servlet都有自己独立唯一的ServletConfig对象

  • 容器会为每个Servlet实例化一个ServletConfig对象,并通过Servlet生命周期的init方法传入给Servlet作为属性

6. ServletContext 的使用

ServletContext 是什么

  • ServletContext 对象有称呼为上下文对象,或者叫应用域对象(后面同意讲解域对象)

  • 容器会为每个app创建一个独立的唯一的ServletContext对象

  • ServletContext对象为所有的Servlet所共享

  • ServletContext可以为所有的Servlet提供初始配置参数

ServletContext怎么用

package com.mdklea.servlet;
​
​
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
​
​
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        ServletConfig servletConfig = getServletConfig();
//        String key1 = servletConfig.getInitParameter("key1");
//        System.out.println("key1: " + key1);
//        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
//        while (initParameterNames.hasMoreElements()) {
//            String s = initParameterNames.nextElement();
//            System.out.println("pname: " + s);
//        }
        System.out.println("--------------------------------------");
        ServletContext servletContext = this.getServletContext();
        Enumeration<String> initParameterNames = servletContext.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            String pname = initParameterNames.nextElement();
            System.out.println("pname:  " + pname +"\n" + "context" + servletContext.getInitParameter(pname));
        }
    }
}

ServletContext APIs

获取资源的真实路径

String realPath = servletContext.getReadPath("资源在web目录中的路径");
  • 例如我们的目标是需要获取项目中某个静态资源的路径,不是工程目录中的路径,而是部署目录中的路径,我们如果直接拷贝其在我们电脑中的完整路径的话其实是有问题的,因为如果该项目以后部署到公司服务器上的话,路径肯定是会发生变化的,所以我们需要使用代码动态获取资源的真实路径,只要使用了servletContext 动态获取资源的真实路径,那么无论项目的部署路径发生什么变化,都会动态获取项目运行时候的实际路径,所以就不会发生由于写死真实路径而导致项目部署位置改变引发的路径错误问题。

获取项目的上下文路径

String contextPath = servletContext.getContextPath();
  • 项目部署名称,也叫项目的上下文路径,在部署进入tomcat时使用的路径,该路径是可能发生变化的,通过API动态获取项目真实的上下文路径,可以帮助我们解决一些后端页面渲染技术或者请求转发和响应重定向中的路径问题

域对象相关的API

  • 域对象:一些用于存储数据和传递数据的对象,传递数据不同的范围,我们称之为不同的域不同的域对象代表不同的域,共享数据的范围也不同

  • ServletContext代表应用,所以ServletContext域也叫应用域,是webapp中最大的域,可以在本应用内实现数据的共享和传递

  • webapp中三大域对象,分别是应用域,会话域,请求域

  • 后面我们会为三大域通一进行讲解和演示

package com.mdklea.servlet;
​
​
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
​
​
public class Servlet01 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        ServletConfig servletConfig = getServletConfig();
//        String key1 = servletConfig.getInitParameter("key1");
//        System.out.println("key1: " + key1);
//        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
//        while (initParameterNames.hasMoreElements()) {
//            String s = initParameterNames.nextElement();
//            System.out.println("pname: " + s);
//        }
        System.out.println("--------------------------------------");
        ServletContext application = this.getServletContext();
        Enumeration<String> initParameterNames = application.getInitParameterNames();
        while (initParameterNames.hasMoreElements()) {
            String pname = initParameterNames.nextElement();
            System.out.println("pname:  " + pname +"\n" + "context" + servletContext.getInitParameter(pname));
        }
        //作为域对象一定会有的API
        // void setAttribute(String key,Object value)   向狱中存储/修改数据
        application.setAttribute("keya","valueA");
        application.setAttribute("keyb","valueb");
        // Object getAttribute(String key)  获取域中的数据
        String keya = (String) application.getAttribute("keya");
        System.out.println(keya);
        // void removeAttribute(String key);    删除域中的数据
        application.removeAttribute("keyb");
​
    }
}
​

7. HttpServletRequest

  • 获取请求行信息相关(方式,请求的url,协议及版本)

package com.mdklea.servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
​
@WebServlet("/Servlet4")
public class Servlet04 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 行相关 GET POST     uri        Http/1.1
        System.out.println(req.getMethod());    //获取请求方式
        System.out.println(req.getScheme());    // 获取请求协议
        System.out.println(req.getProtocol());  // 获取请求协议及版本
        System.out.println(req.getRequestURI());    // 获取请求的uri     项目内的资源路径
        System.out.println(req.getRequestURL());    // 获取请求的url     项目内资源的完成的路径
        /*
        URI :统一资源标识符    /demo/a.html                interface URI()                 资源定位要求和规范       动物类
        URL :统一资源定位符    http://ip:port/demo/a.html  class URL implements UTI{}      一个具体的资源路径       哺乳动物类
         */
        System.out.println(req.getLocalPort());     // 本应用容器的端口号    8080
        System.out.println(req.getServerPort());    // 客户端发请求时使用的端口号
        System.out.println(req.getRemotePort());    // 客户端软件的端口号
        // 头相关 key-value,key-value
        // 根据名字单独获取某个请求头
        String accept = req.getHeader("Accept");
        System.out.println("Accept" + accept);
​
        // 获取本次请求中所有的请求头的名字
        Enumeration<String> headerNames = req.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String hname = headerNames.nextElement();
            System.out.println(hname + req.getHeader(hname));
        }
    }
}

package com.mdklea.servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
​
@WebServlet("/servlet5")
public class Servlet05 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println(req.getParameter("username"));
        System.out.println(req.getParameter("userPwd"));
//        System.out.println(req.getParameter("hobby"));
        //  根据参数名获取多个参数值
        String[] hobbies = req.getParameterValues("hobby");
        for (String hobby : hobbies) {
            System.out.println(hobby);
        }
        // 获取所有的参数名
        Enumeration<String> parameterNames = req.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String pname = parameterNames.nextElement();
            String[] parameterValues = req.getParameterValues(pname);
            if (parameterValues.length > 1) {
                System.out.println(pname + " = " + Arrays.toString(parameterValues));
            }else {
                System.out.println(pname + "=" + parameterValues[0]);
            }
        }
        // 获取map形式的参数名
        Map<String, String[]> parameterMap = req.getParameterMap();
        Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
        for (Map.Entry<String, String[]> entry : entries) {
            String key = entry.getKey();
            String[] value = entry.getValue();
            if (value.length > 1) {
                System.out.println(key + " = " + Arrays.toString(value));
            }else {
                System.out.println(key + " = " + value[0]);
            }
        }
        /*
           以上API专门用于获取key=value形式的参数,无论这些参数是在url后还是在请求体中
           请求
                请求行     方式      uri     http/1.1
                请求头
                请求体
​
                form  表单标签提交GET请求时,参数以键值对形式放在url后,不放在请求体里,GET方式的请求也是可以有请求体的
​
                post
​
                获得请求体中的非键值对数据?
                //  获得一个从请求体中读取字符的字符输入流
                BufferedReader reader = req.getReader();    JSON串
                //  获得一个从请求中读取二进制数据字节的输入流
                ServletInputStream inputStream = req.getInputStream();  文件
         */
        String servletPath = req.getServletPath();
    }
}

8. HttpServletResponce

HttpServletResponce是什么

  • HttpServletResponce 是一个接口,其父接口是ServletResponse

  • HttpServletResponce 是Tomcat预先创建的,在Tomcat调用service方法时传入

  • HttpServletResponce代表对客户端的响应,该对象会被转换成响应的报文发送给客户端,通过该对象我们可以设置响应信息

HttpServletResponce常用API

package com.mdklea.servlet;
​
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
​
import java.io.IOException;
import java.io.PrintWriter;
​
@WebServlet("/servlet6")
public class Servlet06 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置相应行相关的API  HTTP/1.1    200/404/405/500/...
        resp.setStatus(200);
        String info = "<h1>Hello</h1>";
        // 设置响应头相关的API
        resp.setHeader("aaa","valuea");
        resp.setHeader("Content-Type","text/html");
        resp.setHeader("Content-Length","1234");
        resp.setContentType("text/html");
        resp.setContentLength(info.getBytes().length);
​
        // 设置响应体内容API
        // 获得一个向响应体中输入文本字符输出流
        PrintWriter writer = resp.getWriter();
        writer.write(info);
        // 获得一个向响应体中输入二进制信息的字节输入流
        // ServletOutputStream outputStream = resp.getOutputStream();
    }
}

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值