10、Servlet

servlet

注意: 现在默认是在类上写注解@WebServlet("/Aservlet")来代替web.xml的配置

servlet就是在服务器端接受请求并完成响应的java类。

第一个例子 -- Hello World

使用到了MyEclipse与Tomcat7。新建一个Web Project。

新建一个Java Class,如下

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class Aservlet implements Servlet {

    @Override
    public void destroy() {
        System.out.println("servelet调用了destroy()");
    }

    @Override
    public ServletConfig getServletConfig() {
        return null;
    }

    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void init(ServletConfig arg0) throws ServletException {
        System.out.println("servelet服务器init");
        
    }

    @Override
    public void service(ServletRequest req, ServletResponse resp)
            throws ServletException, IOException {
        
        System.out.println("servelet服务器接收到请求");
        resp.getWriter().write("Hello World");
        
    }
    
}

在web.xml里面注册。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
    xmlns="http://java.sun.com/xml/ns/javaee" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
  <display-name></display-name> 
  <!--  注册servlet到服务器 -->
  <servlet> 
  <!-- 配置servlet的名字,保证唯一,一般用类名 -->
    <servlet-name>Aservlet</servlet-name>
    <!-- 完整路径,若没有package,就是如下写法 -->
    <servlet-class>Aservlet</servlet-class>
  </servlet>
  <!-- 配置servlet的访问路径 -->
  <servlet-mapping>
  <!-- 这个名字和上面的name一致 -->
    <servlet-name>Aservlet</servlet-name>
    <url-pattern>/Aservlet</url-pattern>
  </servlet-mapping>
  <!-- 默认使用index.jsp作为欢迎文件 -->
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

在浏览器输入http://localhost:8080/hello/Aservlet就可以看到显示Hello World.

servlet生命周期

  • Servlet对象创建时机? 第一次访问servlet时。
  • Servlet对象创建的特点? 只在第一次访问时调用init一个servlet实例在服务器中只有一个。
  • 当请求访问servlet时,service方法会处理请求.
  • 当服务器将要关闭,服务器会销毁服务器中的Servlet对象,在真正销毁之前调用destory方法。

其他方法

  • getServletInfo => 基本用不到,可以返回servlet作者信息、版权等。
  • getServletConfig => 获得启动信息对象,如下面的例子
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }
    // 这里获得的config是从init里面得到的,需要用一个成员变量存储。扩大其生命周期
    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

ServletConfig详解

  • String getInitParameter(String name) 获得配置信息 根据键获得值
  • Enumeration getInitParameterNames()获得配置信息 获得所有键
  • String getServletName() 获得servlet的名称 <servlet-name>AServlet</servlet-name>
  • ServletContext getServletContext() 该方法返回ServletContext对象.

在目录下新建一个Bservlet

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public class Bservlet implements Servlet {
    
    private ServletConfig config;
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        this.config = config;
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }
    
    @Override
    public void destroy() {
        System.out.println("servelet调用了destroy()");
    }
    
    @Override
    public String getServletInfo() {
        return null;
    }

    @Override
    public void service(ServletRequest req, ServletResponse resp)
            throws ServletException, IOException {
        // 获得所有参数建
        Enumeration<String> en = getServletConfig().getInitParameterNames();
        while (en.hasMoreElements()) {
            // 获得键
            String key = en.nextElement();
            // 获得值
            String value = getServletConfig().getInitParameter(key);
            resp.getWriter().write(key + " -> " + value + "\n");
        }
        // 获得servlet名字
        String servletName = getServletConfig().getServletName();
        resp.getWriter().write(servletName+ "\n");
    }
}

web.xml里新增

<!-- 其他内容 -->
<servlet> 
  <!-- 配置servlet的名字,保证唯一,一般用类名 -->
    <servlet-name>Bservlet</servlet-name>
    <!-- 完整路径,若没有package,就是如下写法 -->
    <servlet-class>Bservlet</servlet-class>
    <!-- 配置初始化参数,键值对的形式 
    getInitParameterNames()获取的就是这里所有的参数键值对-->
    <init-param>
        <param-name>name</param-name>
        <param-value>Tom</param-value>
    </init-param>
    <init-param>
        <param-name>age</param-name>
        <param-value>18</param-value>
    </init-param>
  </servlet>
  
  <!-- 配置servlet的访问路径 -->
  <servlet-mapping>
  <!-- 这个名字和上面的name一致 -->
    <servlet-name>Bservlet</servlet-name>
    <url-pattern>/Bservlet</url-pattern>
  </servlet-mapping>
<!-- 其他内容 -->

在浏览器输入http://localhost:8080/hello/Bsevlet,显示如下数据

name -> Tom
age -> 18
Bservlet

GenericServlet

自己写一个MyGenericServlet

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;


public abstract class MyGenericServlet implements Servlet, ServletConfig {
    private ServletConfig config;
    
    @Override
    public void destroy() {
        
    }

    @Override
    public ServletConfig getServletConfig() {
        return config;
    }

    @Override
    public String getServletInfo() {
        return "";
    }
    // 在连接服务器的时候,init(config)肯定会被调用,如果想在初始化动作里面完成其他初始化,再写一个空参init被有参的调用
    // 子类重写空参init即可
    // 这么做把默认初始化和用户自定义初始化绑定起来了
    @Override
    public void init(ServletConfig config) throws ServletException {
      // init方法 妥善的保存config对象
        this.config = config;
        System.out.println("有参init");
        this.init();
    }
    // 自定义初始化
    // 空参init方法,为了防止开发人员重写 原生init方法
    public void init() throws ServletException {
        
    }
    // 将service函数设为抽象的,强迫用户去实现。因为没有service函数的servlet是没有意义的
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException;

    @Override
    public String getServletName() {
        return config.getServletName();
    }

    @Override
    public ServletContext getServletContext() {
        return config.getServletContext();
    }

    @Override
    public String getInitParameter(String key) {
        return config.getInitParameter(key);
    }

    @Override
    public Enumeration<String> getInitParameterNames() {
        return config.getInitParameterNames();
    }
}

新建一个类继承刚写的MyGenericServlet

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// 只是重写了init()和service方法
public class Cservlet extends MyGenericServlet {
    // 连接到服务器会自动调用有参的init,打印有参init和无参init
    @Override
    public void init() {
        System.out.println("无参init");
    }

    @Override
    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        String value = getInitParameter("name");
        String servletName = getServletName();
        res.getWriter().write(value + "\n");
        res.getWriter().write(servletName + "\n");
    }
}

GenericServlet有如下好处

1. init方法 妥善的保存config对象
2. 空参init方法,为了防止开发人员重写 原生init方法
3. service方法空实现=> 声明为抽象
4. destory方法空实现 
5. 实现getServletInfo,getServletConfig
6. 实现了servletConfig接口. 接口中的方法直接调用config实现类实现.

HttpServlet

自己实现一个简单的MyHttpservlet

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

// 这里声明为abstract主要是为了防止用户直接使用该类(new一个实例)。而必须通过继承
public abstract class MyHttpServlet extends MyGenericServlet {

    @Override
    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        // 帮用户强转
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        // 函数重载, 用户自定义的
        service(req, res);
    }
    
    public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {  
        // 获得请求方式,根据请求方式的不同调用不同的doXXX()方法
        String method = request.getMethod();
        if ("GET".equals(method)) {
            doGet(request, response);
        } else if ("POST".equals(method)) {
            doPost(request, response);
        }
    }
    
    // 处理GET请求
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 具体的处理逻辑
    }
    
    // 处理POST请求
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       // 具体的处理逻辑
    }
}

子类继承MyHttpServlet后,可以选择实现doGet()或者doPost() 。也可以重写自定义的service(HttpServletRequest req, HttpServletResponse res),这样不会太注重到底是什么请求方法。

servlet细节

servlet线程安全

Servlet的实例在服务器运行期间只有一个实例存在,所以线程不安全。

线程不安全: 如果使用成员变量来接受线程参数,如果发生并发,那么会出现线程问题(覆盖,实例只有一个,成员变量也只有一个,后面来的会把前面的覆盖)

解决办法: 将装载线程参数的变量放置到方法中,写成局部变量。

servlet的创建实例时机

默认情况: 第一次访问该servlet时候。

让servlet实例随着服务器的启动而创建,在servlert标签中 添加一个配置即可:<load-on-startup></load-on-startup>在该配置中填入一个整数可实现。

数字的数值,在有多个servlet需要随着服务器启动而启动时,决定启动顺序。数字越小优先级越高。最小就是0。一般0~5。取3就行。

如果数字一样,谁先配置谁先创建。

servlet的路径配置

<url-pattern></url-pattern>该配置,配置方式有两种:

  1. 路径匹配: 一定以"/"开头

/AServlet -> http://localhost:8080/project-name/Aservlet
/ABC/AServlet -> http://localhost:8080/project-name/ABC/Aservlet
/ABC/* -> http://localhost:8080/project-name/ABC/any-word*匹配任意内容太

  1. 后缀名匹配: 以开头*

*.do
*.action
*.html

后缀名匹配和路径匹配不能同一配置中混合使用. 例如: /.do
一个servlet可以配置多个路径. 直接在<servlet-mapping>元素中添加多个<url-pattern>配置即可。优先级: /AServlet > /abc/
> *.do > /*

注意:匹配范围越大,优先级越低。

ServletContext

我们一个Web项目有且只有一个ServletContext .

  • 创建:随着项目的启动而创建
  • 销毁:随着项目的关闭而销毁
  • 获得:通过ServletConfig对象的 getServletContext方法获得。

功能

  1. 可以获得项目参数
  2. 是Servlet技术中的3个域对象之一
  3. 获得项目内的资源
<!-- 项目级的启动参数 -->
<context-param>
    <param-name>name</param-name>
    <param-value>Rose</param-value>
 </context-param>
   <context-param>
    <param-name>age</param-name>
    <param-value>15</param-value>
 </context-param>
  <context-param>
    <param-name>gender</param-name>
    <param-value>female</param-value>
 </context-param>
import java.io.IOException;
import java.util.Enumeration;

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


public class Fservlet extends HttpServlet {
    // 获得并输出所有项目启动参数
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 获得ServletContext对象
        ServletContext servletContext = getServletContext();
        Enumeration<String> en = servletContext.getInitParameterNames();
        
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            String value = servletContext.getInitParameter(key);
            
            response.getWriter().write("<h2>"+key + " -> " + value+"</h2>");
            
        }
    }

}

servlet之间通讯

先写一个简单的例子,用另外一个类作为中间媒介,通过static对象在类之间共享,实现通讯。

import java.util.HashMap;
import java.util.Map;


public class Constant {
    public static String word;
    public static Map<String, Object> map = new HashMap<>(); 
}

Gservlet向Hservlet发送消息

import java.io.IOException;
import java.io.PrintWriter;

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


public class Gservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 通过static对象在类之间共享
        Constant.word = "hahaha";
        Constant.map.put("money", "1000");
        Constant.map.put("iphone", "Apple"); 
    }
}

Hservlet接收消息

import java.io.IOException;
import java.io.PrintWriter;

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


public class Hservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String word = Constant.word;
        String money = Constant.map.get("money");
        String phone = Constant.map.get("iphone");
        
        System.out.println(word);
        System.out.println(money);
        System.out.println(phone);
    }
}

其实ServletContext已经帮我们想到了这一点

使用servletContext.setAttribute(key, value)设置属性键值对。

import java.io.IOException;
import java.io.PrintWriter;

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


public class Gservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        
        ServletContext sc = getServletContext();
        
        sc.setAttribute("money", "100");
        sc.setAttribute("iphone", "Apple");
        System.out.println("已发送");
        
//      Constant.word = "hahaha";
//      Constant.map.put("money", "1000");
//      Constant.map.put("iphone", "Apple"); 
    }
}

使用servletContext.setAttribute(key, value)设置属性键值对。

import java.io.IOException;

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


public class Hservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
//      String word = Constant.word;
//      String money = Constant.map.get("money");
//      String phone = Constant.map.get("iphone");
        
        ServletContext sc = getServletContext();
        
        String money = (String) sc.getAttribute("money");
        String phone = (String) sc.getAttribute("iphone");
        System.out.println(money);
        System.out.println(phone);
    }
}

其他方法

// 删除键值对
        sc.removeAttribute("money");
        // 遍历所有属性
        Enumeration<String> en = sc.getAttributeNames();
        while (en.hasMoreElements()) {
            String key = en.nextElement();
            System.out.println(key);
        }

上面的ServletContext介绍的是application域。

Servlet三大域

  • application
  • request
  • session

域用于服务器组件之间的通讯(例如:两个servlet之间通讯)。域的实质就是map。application域 就是在整个项目内共享数据的map,所以两个servlet之间通信,其他servlet也会知道。且application域不是线程安全的,不能知道接受到的信息是否就是来自某一个具体的servlet,例如上例中Hservlet不知道它接收到信息就是Gservlet传来的。

操作域的方法:

  • void setAttribute(String key,Object value);
  • Object getAttribute(String key);
  • Enumeration<String> getAttributeNames();
  • void removeAttribute(String key);

获得内部资源

package servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.URL;

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

public class Iservlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
         ServletContext sc = getServletContext();
            // 1、 获得WebRoot下的user.xml这样写就行
            InputStream is =  sc.getResourceAsStream("/user.xml");
            System.out.println(is); 
            String realPath = sc.getRealPath("/user.xml");
            System.out.println(realPath); // //\webapps\cookie_session\user.xml
            // 2、Class也有获得资源的方法,/表示根目录是src或者说classes目录,ServletContext的根目录是项目的根目录(WebRoot)
            // 这样写是获取classes目录下的xml,WEB-INF/classes/user.xml
            InputStream is3 = Fservlet.class.getResourceAsStream("/user.xml");
            System.out.println(is3);
            // 这样写是获取工程包(package)下的目录下的xml,注意不加"/"
            InputStream is4 = Fservlet.class.getResourceAsStream("user.xml");
            System.out.println(is4);
            // 获得classes下具体某个包下xml的绝对路径
            URL url = Fservlet.class.getResource("user.xml");
            System.out.println(url.getPath()); // /webapps/cookie_session/WEB-INF/classes/cookie/user.xml
           // 获得src(或者说classes)下xml的绝对路径
            URL url2 = Fservlet.class.getResource("/user.xml");
            System.out.println(url2.getPath()); // /webapps/cookie_session/WEB-INF/classes/user.xml
        }
}

总结

Servlet Server Applet -> 运行在服务器端的小程序

功能:接收请求并完成响应。

本质:就是一个Java类

规则

  • 实现Servlet
  • 继承GenericServlet
  • 继承HttpServlet

生命周期

  1. 第一次访问时,服务器会创建servlet的实例,创建完后才调用init ()方法进行初始化
  2. 讲请求交给service(req, res)处理。
  3. 服务器关闭时调用destroy()方法。

servlet具体过程

小任务--统计访问人数

package servlet;

import java.io.IOException;

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

public class Lservlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
            //1 获得Application域中存放的统计数字
            Integer count = (Integer) getServletContext().getAttribute("count");
            //2 判断是否获得到统计数字
            if(count == null){
                //没获得到=> 将数字初始化为1
                count = 1;
            }else{
                //获得到了=> 将数字加1
                count += 1;
            }
            //3 输出,放回到Application域中
            response.getWriter().write("you are the "+ count+" vistors");
            getServletContext().setAttribute("count",count);
    }

}

by @sunhaiyu

2017.3.25

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,216评论 11 349
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,778评论 6 342
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 《做最好的自己》一书为青年学生们提供了一个可供参考和借鉴的,包含了价值观、态度和行为等三个层面的理论模型——“成功...
    失落的美好阅读 869评论 0 10