Request和Response相關介紹

Request?和?Response?是什么?

  • Request(請求對象):用來接收瀏覽器發過來的數據。
  • Response(響應對象):用來把服務器處理后的結果返回給瀏覽器。


1. request 的作用(獲取請求數據)

  • 當你在瀏覽器訪問網頁或提交表單時,瀏覽器會把你的請求(比如你輸入的賬號、密碼等)通過HTTP協議發給服務器(Tomcat)。
  • 這些數據會被Tomcat解析,然后封裝到request對象里
  • 在Servlet代碼里,你可以通過request對象獲取到這些數據,比如獲取表單里的用戶名、密碼等。
  • 拿到數據后,服務器就可以根據這些信息做后續的業務處理,比如登錄驗證。


2. response 的作用(設置響應數據)

  • 服務器處理完業務后,需要把結果返回給瀏覽器,比如返回一個網頁、提示信息等。
  • 這些返回的數據會被封裝到response對象里
  • Tomcat會把response對象里的內容解析出來,按照HTTP協議的格式發回給瀏覽器。
  • 瀏覽器收到后,就會把內容展示出來,比如顯示網頁、彈出提示等。


通過一個案例來初步體驗下request和response對象的使用。

寫一個表單,請求方式為GET,當我們輸入不同的username,并點擊提交時,界面上就會出現username,歡迎訪問

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="http://localhost:8080/web_demo_war_exploded/demo" method="get">用戶名:<input type="text" name="username"><input type="submit" value="提交">
</form>
</body>
</html>

啟動成功后就可以通過瀏覽器來訪問,并且根據傳入參數的不同就可以在頁面上展示不同的內容:?

import javax.servlet.*;
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(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String name = req.getParameter("username");//避免中文亂碼問題resp.setHeader("content-type","text/html;charset=utf-8");resp.getWriter().write("<h1>"+name+",歡迎訪問<h1>");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");}
}

總結一句話

  • request:用來接收瀏覽器發來的數據。
  • response:用來把服務器的處理結果返回給瀏覽器。

Request繼承體系

一、Request繼承體系是什么?

在Java?Web開發中,Request對象用來封裝瀏覽器發來的請求數據。

但這個Request對象其實有一套“繼承體系”,也就是有父接口、子接口和實現類。

1. ServletRequest(接口)

  • Java官方提供的最基礎的請求對象接口,定義了所有請求對象都應該有的方法。
  • 就像“交通工具”這個大類,規定了所有交通工具都應該有“啟動、停止”等功能。

2. HttpServletRequest(接口)

  • 繼承自ServletRequest,專門針對HTTP協議的請求對象接口,增加了很多和HTTP相關的方法(如獲取請求頭、參數等)。
  • 就像“汽車”是“交通工具”的一種,除了啟動、停止,還能“開空調、開車窗”等。

3. RequestFacade(實現類)

  • 這是Tomcat服務器自己寫的一個實現類,真正實現了HttpServletRequest接口的所有方法。
  • 就像“寶馬汽車”是“汽車”的一種,寶馬公司造出來的具體汽車。

二、Tomcat處理請求的過程

  1. 瀏覽器發請求(比如你在Chrome里訪問一個網站)。
  2. Tomcat服務器接收到請求,會先解析請求數據。
  3. Tomcat用RequestFacade類,把請求數據封裝成一個Request對象。
  4. Tomcat把Request對象傳給Servlet的service方法,讓你的Java代碼可以方便地獲取請求里的各種信息。

三、生活中的實際例子

例子1:快遞包裹

  • ServletRequest:快遞包裹的“標準接口”,規定所有快遞包裹都要有“收件人、寄件人、內容物”等信息。
  • HttpServletRequest:專門針對“順豐快遞”的包裹,除了基本信息,還多了“順豐單號、保價服務”等特殊功能。
  • RequestFacade:順豐公司實際生產出來的某個快遞包裹,里面裝著你的快遞。

例子2:公司員工

  • ServletRequest:公司規定的“員工”標準(必須有姓名、工號等)。
  • HttpServletRequest:技術部員工,除了基本信息,還要有“技術等級、編程語言”等。
  • RequestFacade:張三,技術部的一個具體員工。

ServletRequest和HttpServletRequest是繼承關系,并且兩個都是接口,接口是無法創建對象,那么方法里的HttpServletRequest參數是從哪兒來的呢?

//接口無法創建對象,那么這個參數是哪兒來的呢
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String name = req.getParameter("username");resp.setHeader("content-type","text/html;charset=utf-8");resp.getWriter().write("<h1>"+name+",歡迎訪問<h1>");
}@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");
}

一、接口不能創建對象,參數對象從哪來的?

  • ServletRequest?和?HttpServletRequest?都是接口,接口是不能直接用?new?創建對象的。
  • 但是在?Servlet 的?doGet、doPost?方法里,參數卻是?HttpServletRequest req,這個對象是誰創建的呢?

二、Request對象的創建過程

  1. Tomcat?服務器收到瀏覽器請求后,會先解析請求數據。
  2. Tomcat 會用?RequestFacade?這個類,來實現?HttpServletRequest?接口,把請求數據封裝成一個對象。
  3. Tomcat 再把這個對象傳給你的?Servlet 的?doGet/doPost 方法,你就能直接用?req.getParameter("username")?這樣的方法獲取請求參數了。

簡單說:你用的?req?對象,其實是?Tomcat 幫你創建好的?RequestFacade 對象,只不過它的類型是 HttpServletRequest。

三、生活中的實際例子

例子1:快遞公司和快遞單

  • 接口(HttpServletRequest):就像快遞公司規定的“快遞單標準”,規定了快遞單必須有收件人、寄件人、地址等信息。
  • RequestFacade:就像順豐公司實際印出來的快遞單,完全符合標準,但是順豐自己造的。
  • 你(Servlet):只管用快遞單(req對象)填寫和查信息,不用關心快遞單是誰印的、怎么印的。

例子2:公司員工和崗位說明書

  • 接口:公司規定的“崗位說明書”,比如“程序員”要會寫代碼、會調試。
  • 實現類:張三是公司招聘的程序員,完全符合崗位說明書的要求。
  • 你(項目經理):只管讓張三干活(調用方法),不用管張三是怎么被招聘進來的

四、代碼中的體現

protected void doGet(HttpServletRequest req, HttpServletResponse resp) {// req其實是Tomcat創建的RequestFacade對象String name = req.getParameter("username");// ...
}
import javax.servlet.*;
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(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println(req);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");}
}

啟動服務器訪問頁面,控制臺輸出org.apache.catalina.connector.RequestFacade@36bf693c?

五、總結

  • ServletRequest:最基礎的請求接口,規定了所有請求對象的基本功能。
  • HttpServletRequest:專門針對HTTP協議的請求接口,功能更豐富。
  • RequestFacade:Tomcat實現的具體類,真正用來封裝和傳遞請求數據。

Tomcat會用RequestFacade把瀏覽器的請求數據封裝好,然后傳給你的Servlet代碼,讓你可以方便地獲取和處理請求信息。

Request獲取請求數據

請求行包含三塊內容,分別是請求方式請求資源路徑HTTP協議及版本,例如
GET /tomcat_demo_war/index.html?username=suger1201&password=dsaasd HTTP/1.1

對于這三部分內容,request對象都提供了對應的API方法來獲取,具體如下:

  • 獲取請求方式:?GET
    String getMethod()
  • 獲取虛擬目錄(項目訪問路徑):?/request-demo
    String getContextPath()
  • 獲取URI(統一資源標識符):?/request-demo/req1
    String getRequestURI()

介紹完上述方法后,咱們通過代碼把上述方法都使用下:

import javax.servlet.*;
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(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();System.out.println("請求方式:" + method);String contextPath = req.getContextPath();System.out.println("項目訪問路徑:" + contextPath);StringBuffer requestURL = req.getRequestURL();System.out.println("URL:" + requestURL);String requestURI = req.getRequestURI();System.out.println("URI:" + requestURI);String queryString = req.getQueryString();System.out.println("請求參數:" + queryString);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");}
}

這里的請求方式是GET

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="/web_demo_war_exploded/demo" method="get">用戶名:<input type="text" name="username"><input type="submit" value="提交">
</form>
</body>
</html>

啟動服務器,并向表單中隨便輸入一個username,然后點擊提交,控制臺輸出如下
請求方式:GET
項目訪問路徑:/web_demo_war_exploded
URL:http://localhost:8080/web_demo_war_exploded/demo
URI:/web_demo_war_exploded/demo
請求參數:username=Cyderpunk2077%40gmail.com

獲取請求頭數據

對于請求頭的數據,格式為key: value
所以根據請求頭名稱獲取對應值的方法為

String getHeader(String name) 

接下來,在代碼中如果想要獲取客戶端瀏覽器的版本信息,則可以使用

import javax.servlet.*;
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(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String agent = req.getHeader("user-agent");System.out.println(agent);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("post...");}
}

啟動服務器,控制臺輸出如下
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36

獲取請求體數據?

瀏覽器在發送GET請求的時候是沒有請求體的,所以需要把請求方式變更為POST

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="/web_demo_war_exploded/demo" method="post">用戶名:<input type="text" name="username"><input type="submit" value="提交">
</form>
</body>
</html>

對于請求體中的數據,Request對象提供了如下兩種方式來獲取其中的數據,分別是:

  • 獲取字節輸入流,如果前端發送的是字節數據,比如傳遞的是文件數據,則使用該方法
    ServletInputStream getInputStream()
  • 獲取字符輸入流,如果前端發送的是純文本數據,則使用該方法
    BufferedReader getReader()

下面我們在Servlet的doPost方法中獲取數據

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;@WebServlet(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println("get??");}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//由于獲取的是純文本數據,所以這里用的getReader()BufferedReader bufferedReader = req.getReader();String line = bufferedReader.readLine();System.out.println(line);}
}

?form表單提交數據

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body>
<form action="http://localhost:8080/request-demo/demo" method="post">用戶名:<input type="text" name="username"><input type="submit" value="提交"></form>
</body>
</html>
BufferedReader流是通過request對象來獲取的,當請求完成后request對象就會被銷毀,request對象被銷毀后,BufferedReader流就會自動關閉,所以此處就不需要手動關閉流了。

就像你收到一封信(POST請求),你用?getReader()?就是把信封拆開,直接讀里面的信紙內容(請求體),而不是看信封上的地址(請求頭或參數)。?

啟動服務器,訪問?http://localhost:8080/requset-demo/index.html?,填入username并提交,在控制臺就可以看到前端發送的請求數據了

小結

HTTP請求數據中包含了請求行請求頭請求體,針對這三部分內容,Request對象都提供了對應的API方法來獲取對應的值:

  • 請求行
    • getMethod()獲取請求方式
    • getContextPath()獲取項目訪問路徑
    • getRequestURL()獲取請求URL
    • getRequestURI()獲取請求URI
    • getQueryString()獲取GET請求方式的請求參數
  • 請求頭
    • getHeader(String name)根據請求頭名稱獲取其對應的值
  • 請求體
    • 注意: 瀏覽器發送的POST請求才有請求體
    • 如果是純文本數據:getReader()
    • 如果是字節數據如文件數據:getInputStream()

獲取請求參數的通用方式

請求參數獲取方式

  • GET方式:
    String getQueryString()
  • POST方式:
    BufferedReader getReader();

思考:
GET請求方式和POST請求方式區別主要在于獲取請求參數的方式不一樣,是否可以提供一種統一獲取請求參數的方式,從而統一doGet和doPost方法內的代碼??

解決方式一:
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;@WebServlet(urlPatterns = "/demo")
public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//獲取請求方式String method = req.getMethod();String param = "";//根據請求方式來獲取請求參數if ("GET".equals(method)) {param = req.getQueryString();} else if ("POST".equals(method)) {BufferedReader bufferedReader = req.getReader();param = bufferedReader.readLine();}//將請求參數進行打印控制臺System.out.println(param);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

代碼中的一些疑問:

1.?"GET".equals(method)?是什么意思?
  • method?是一個字符串變量,保存了請求方式,比如?"GET"?或?"POST"。
  • "GET".equals(method)?的意思是:判斷 method 變量的值是不是 "GET"。

2. 為什么不寫成?method.equals("GET")?
  • 其實?method.equals("GET")?也可以用,功能是一樣的。
  • 但如果?method?變量是?null,method.equals("GET")?會報空指針異常(NullPointerException)。
  • 而?"GET".equals(method)?就算?method?是?null,也不會報錯,只會返回?false。

所以這種寫法更安全!


3. 還能怎么用?

這種寫法適用于任何字符串比較,比如:

if ("POST".equals(method)) { ... }
if ("admin".equals(role)) { ... }
if ("zhangsan".equals(username)) { ... }
4.?生活中的例子

就像你問別人“你是不是張三?”

  • 你直接問“張三.equals(你)”,不管對方是誰(哪怕沒人),你都不會出錯。
  • 但如果你問“你.equals(張三)”,對方要是沒人(null),你就會出錯。
5. 總結
  • "GET".equals(method)?是安全的字符串比較寫法,防止空指針異常。
  • 這種寫法在Java開發中非常常見,推薦你以后都這樣寫!

使用request的getMethod()來獲取請求方式,根據請求方式的不同分別獲取請求參數值,這樣就可以解決上述問題,但是以后每個Servlet都需要這樣寫代碼,實現起來比較麻煩,這種方案我們不采用

解決方式二:

request對象已經將上述獲取請求參數的方法進行了封裝,并且request提供的方法實現的功能更強大,以后只需要調用request提供的方法即可,在request的方法中都實現了哪些操作呢?

  1. 根據不同的請求方式獲取請求參數,例如:username=zhangsan&password=asd123&hobby=1&hobby=2
  2. 把獲取到的內容進行分割,username=zhangsan&password=asd123&hobby=1?->?username=zhangsan password=asd123 hobby=1 hobby=2?->?username zhangsan password asd123 hobby 1 hobby 2
  3. 把分割后端數據,存入到一個Map集合中,其中Map集合的泛型為<String,String[]>,因為參數的值可能是一個,也可能有多個,所以value的值的類型為String數組。

基于上述理論,request對象為我們提供了如下方法:

  • 獲取所有參數Map集合
    Map<String,String[]> getParameterMap()
  • 根據名稱(Key)獲取參數值(Value)(數組)
    String[] getParameterValues(String name)
  • 根據名稱(Key)獲取參數值(Value)(單個值)
    String getParameter(String name)

接下來,我們通過案例來把上述的三個方法進行實例演示:

  1. 隨便寫一個表單,加上一個復選框,愛好可以多選,所以到時候的參數值就不止一個了
    <!DOCTYPE html>
    <html lang="en">
    <head><meta charset="UTF-8"><title>Title</title>
    </head>
    <body>
    <form action="/web_demo_war_exploded/demo" method="post">用戶名:<input type="text" name="username"><br>密碼:<input type="password" name="password"><br>愛好:<input type="checkbox" name="hobby" value="1">Apex<input type="checkbox" name="hobby" value="2">Terraria<br><input type="submit" value="提交">
    </form>
    </body>
    </html>

    getParameterMap()

    import javax.servlet.*;
    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.Map;@WebServlet(urlPatterns = "/demo")
    public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//獲取所有參數的Map集合Map<String, String[]> parameterMap = req.getParameterMap();//遍歷Mapfor (String key : parameterMap.keySet()) {System.out.print(key+":");String[] values = parameterMap.get(key);for (String v : values) {System.out.print(v+" ");}System.out.println();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
    }
    /*
    獲取的結果如下
    username:Cyderpunk2077@gmail.com 
    password:dsaasd 
    hobby:1 2 
    */

    getParameterValues(String name)

    @WebServlet(urlPatterns = "/demo")
    public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String[] hobbies = req.getParameterValues("hobby");for (String s : hobbies) {System.out.println(s);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
    }
    /*
    將兩個復選框都勾上,得到結果如下
    1
    2
    */

    getParameter(String name)

    @WebServlet(urlPatterns = "/demo")
    public class ServletDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String username = req.getParameter("username");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
    }
    //輸出結果就是你輸入的username

    如果你在post請求方式下輸入了中文username并提交,控制臺會輸出亂碼
    解決方案:

??

package com.itheima.request;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("/req")
public class RequestDemo extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String username = req.getParameter("username");
// 先用ISO-8859-1解碼,再用UTF-8編碼username = new String(username.getBytes("ISO-8859-1"), "UTF-8");System.out.println(username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req,resp);}
}

遇到的問題

new String(username.getBytes("ISO-8859-1"), "UTF-8")是什么作用?

它的作用就是:把已經亂碼的字符串“修正”回來,變成正常的中文。

?詳細分解

  • username.getBytes("ISO-8859-1")

把“亂碼”的字符串,按照ISO-8859-1編碼方式轉成字節數組。

這一步的意思是:把你看到的亂碼,變回原始的字節數據。

  • new?String(...,?"UTF-8")

用UTF-8編碼方式,把上一步得到的字節數組重新解碼成字符串。

這一步的意思是:用正確的方式重新“翻譯”這些字節,得到正常的中文。

這行代碼就是“先把亂碼還原成字節,再用UTF-8重新解碼,得到正常中文”。

方法作用缺點
req.getParameterMap()獲取所有參數的 Map(參數名→值數組 )需遍歷處理
req.getParameter("username")獲取單個參數的第一個值無法直接獲取多值參數(如?hobby?)
req.getParameterValues("hobby")獲取單個參數的所有值(返回數組 )需逐個參數調用

Request請求轉發

請求轉發(forward):一種在服務器內部的資源跳轉方式。

  1. 瀏覽器發送請求給服務器,服務器中對應的資源A接收到請求
  2. 資源A處理完請求后將請求發給資源B
  3. 資源B處理完后將結果響應給瀏覽器
  4. 請求從資源A到資源B的過程就叫請求轉發

?測試步驟

  1. 創建一個RequestDemo1類,接收/req5的請求,在doGet方法中打印這里是RequestDemo1
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;@WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是RequestDemo1");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }

  2. 創建一個RequestDemo2類,接收/req6的請求,在doGet方法中打印這里是RequestDemo2
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;@WebServlet("/RequestDemo2")
    public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是RequestDemo2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
  3. 在RequestDemo1的方法中使用req.getRequestDispatcher("/RequestDemo2").forward(req,resp)進行請求轉發
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.IOException;@WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是RequestDemo1");//加上這行request.getRequestDispatcher("/RequestDemo2").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }

  4. 啟動測試
    當我們訪問RequestDemo1時,控制臺會得到如下輸出

????????這里是RequestDemo1
????????這里是RequestDemo2

????????說明請求已經轉發到了/RequestDemo2??

在轉發的同時我們還可以傳遞數據給/RequestDemo2


request對象提供了三個方法:
  • 存儲數據到request域[范圍,數據是存儲在request對象]中
    void setAttribute(String name,Object o);
  • 根據key獲取值
    Object getAttribute(String name);
  • 根據key刪除該鍵值對
    void removeAttribute(String name);

接著上個需求來:

  1. 在RequestDemo1的doGet方法中轉發請求之前,將數據存入request域對象中.
    @WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是RequestDemo1");//存儲數據request.setAttribute("msg","HELLO~");//請求轉發request.getRequestDispatcher("/RequestDemo2").forward(request, response);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
  2. 在RequestDemo2的doGet方法從request域對象中獲取數據,并將數據打印到控制臺.
    @WebServlet("/RequestDemo2")
    public class RequestDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是RequestDemo2");Object msg = request.getAttribute("msg");System.out.println(msg);}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
  3. 啟動訪問測試,得到的輸出結果如下

????????這里是RequestDemo1
????????這里是RequestDemo2
????????HELLO~

請求轉發的特點
  • 瀏覽器地址欄路徑不發生變化
    雖然后臺從/這里是RequestDemo1轉發到/這里是RequestDemo2,但是瀏覽器的地址一直是/這里是RequestDemo1,未發生變化
  • 只能轉發到當前服務器的內部資源
    不能從一個服務器通過轉發訪問另一臺服務器
  • 一次請求,可以在轉發資源間使用request共享數據
    雖然后臺從/RequestDemo1轉發到/RequestDemo2,但是這個只有一次請求
遇到的問題
1.?request.setAttribute?是什么?
  • 這是?HttpServletRequest 提供的方法,用來在一次請求的范圍內存儲數據。
  • 你可以把它理解為“在本次請求的背包里放東西”,后續在請求轉發(forward)到其他 Servlet 或 JSP?時,可以隨時取出來用。
2.?語法和用法
request.setAttribute("鍵", 值);
  • "msg"?是你給數據起的名字(key)。
  • "HELLO~"?是你要存的數據(value),可以是任意對象。
3. 作用和場景
  • 作用:在一次請求內,多個 Servlet/JSP?之間共享數據。
  • 常見場景:請求轉發(forward)時傳遞數據,比如表單校驗、頁面跳轉時傳遞提示信息等。
4.?取出數據的方法

在被轉發的 Servlet 或?JSP?中,可以這樣取出數據:

Object msg = request.getAttribute("msg");
System.out.println(msg); // 輸出:HELLO~
5. 注意事項
  • request.setAttribute?存的數據只在本次請求有效,請求結束就沒了。
  • 如果用的是重定向(redirect),數據不會帶過去,只有請求轉發(forward)才可以。
6. 總結
  • request.setAttribute("msg", "HELLO~");?就是把數據存到本次請求的“背包”里,方便后續在同一次請求的其他地方取用。
  • 常用于Servlet/JSP 之間的數據傳遞。

Response對象

Reponse的繼承體系和Request的繼承體系也非常相似:

Response設置響應數據功能介紹

HTTP響應數據總共分為三部分內容,分別是==響應行、響應頭、響應體==,對于這三部分內容的數據,respone對象都提供了哪些方法來進行設置?

  1. 響應行
    響應行包含三塊內容,分別是 HTTP/1.1[HTTP協議及版本] 200[響應狀態碼] ok[狀態碼的描述]
    對于響應頭,比較常用的就是設置響應狀態碼:
    void setStatus(int sc);
  2. 響應頭
    響應頭的格式為key:value形式
    設置響應頭鍵值對:
    void setHeader(String name,String value);
  3. 響應體
    對于響應體,是通過字符、字節輸出流的方式往瀏覽器寫,
    獲取字符輸出流:
    PrintWriter getWriter();

    獲取字節輸出流

    ServletOutputStream getOutputStream();

    ?

Response請求重定向?

  • 重定向的實現方式
    response.setStatus(302);
    response.setHeader("location","資源B的訪問路徑");
    //或
    resposne.sendRedirect("資源B的訪問路徑");
  • 創建一個ResponseDemo1類,接收/ResponseDemo1的請求,在doGet方法中打印這里是ResponseDemo1
    @WebServlet("/ResponseDemo1")
    public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是ResponseDemo1");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
  • 創建一個ResponseDemo2類,接收/ResponseDemo1的請求,在doGet方法中打印這里是ResponseDemo2
    @WebServlet("/ResponseDemo2")
    public class ResponseDemo2 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是ResponseDemo2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
  • 在ResponseDemo1的方法中使用response.sendRedirect("/web_demo_war_exploded/RequestDemo2");
    @WebServlet("/ResponseDemo1")
    public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是ResponseDemo1");//重定向response.sendRedirect("/web_demo_war_exploded/RequestDemo2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }
    
  • 啟動測試,訪問/ResponseDemo1,控制臺得到如下輸出,同時地址欄的地址也變更為/RequestDemo2

這里是RequestDemo1
這里是RequestDemo2

重定向的特點
  • 瀏覽器地址欄路徑發送變化
    當進行重定向訪問的時候,由于是由瀏覽器發送的兩次請求,所以地址會發生變化
  • 可以重定向到任何位置的資源(服務內容、外部均可)
    因為第一次響應結果中包含了瀏覽器下次要跳轉的路徑,所以這個路徑是可以任意位置資源。
  • 兩次請求,不能在多個資源使用request共享數據
    因為瀏覽器發送了兩次請求,是兩個不同的request對象,就無法通過request對象進行共享數據

?

簡單解釋重定向和請求轉發的區別

?

一、重定向(Redirect)
1. 什么是重定向?

重定向就是服務器告訴瀏覽器“你去別的地方找”,然后瀏覽器自己重新發起請求。

2.?生活中的例子

場景:你去銀行辦業務

  1. 你走進銀行A,問“我要辦信用卡”。
  1. 銀行A的工作人員說:“我們這里不辦信用卡,請您去銀行B,地址是XX路XX號。”
  1. 你走出銀行A,自己走到銀行B。
  1. 在銀行B辦完信用卡。
3.?重定向的特點
  • 瀏覽器地址欄會變:因為你去了新的地方,地址欄顯示銀行B的地址。
  • 可以跳轉到任何地方:銀行A可以讓你去銀行B,也可以讓你去郵局、超市等。
  • 兩次請求:你去了銀行A一次,又去了銀行B一次。
  • 不能帶信息:銀行A不能直接把你的資料傳給銀行B,你得重新說明。
4.?代碼示例?
// 重定向到另一個頁面
resp.sendRedirect("/login.html");
二、請求轉發(Forward)
1. 什么是請求轉發?

請求轉發就是服務器內部幫你轉接,瀏覽器不知道發生了跳轉。

2. 生活中的例子

場景:你打電話給客服

  1. 你打電話給客服A,問“我要投訴”。
  1. 客服A說:“這個問題我幫你轉接給投訴部門,請稍等。”
  1. 客服A在內部把你的電話轉給了投訴部門。
  1. 投訴部門直接和你通話,解決問題。
3. 請求轉發的特點
  • 瀏覽器地址欄不變:因為你一直以為在和客服A通話,地址欄還是顯示客服A的號碼。
  • 只能轉接到內部:客服A只能轉給公司內部的部門,不能轉給其他公司。
  • 一次請求:你只打了一次電話,只是內部轉接了。
  • 可以帶信息:客服A可以把你的問題、會員號等信息直接轉給投訴部門。
4. 代碼示例
// 請求轉發到另一個Servlet
req.getRequestDispatcher("/demo2").forward(req, resp);

三、對比總結

| 方面 | 重定向 | 請求轉發?|

|------|--------|----------|

|?地址欄?| 會變 |?不變 |

|?請求次數?| 兩次 | 一次 |

|?跳轉范圍?|?任何地方 | 只能內部?|

|?數據傳遞?|?不能 | 可以 |

|?效率?| 較低 | 較高?|

四、什么時候用哪個?

一、什么時候用重定向?

1.?登錄成功后跳轉到首頁

場景:?用戶登錄成功,需要跳轉到網站首頁。

為什么用重定向?

  • 登錄成功后,如果用戶刷新頁面,不會重復提交登錄信息。
  • 地址欄會變成首頁地址,用戶可以直接收藏或分享首頁鏈接。

代碼示例:

// 登錄驗證成功后
if (loginSuccess) {resp.sendRedirect("/index.html"); // 重定向到首頁
}

2. 表單重復提交避免

場景:?用戶提交訂單后,如果刷新頁面,不會重復提交訂單。

為什么用重定向?

  • 用戶提交訂單后,重定向到訂單成功頁面。
  • 如果用戶刷新頁面,只會刷新訂單成功頁面,不會重復提交訂單。

代碼示例:

// 訂單提交成功后
if (orderSuccess) {resp.sendRedirect("/order-success.html"); // 重定向到成功頁面
}
二、什么時候用請求轉發?

1. 頁面內部模塊跳轉

場景:?在一個頁面內部,根據不同的條件顯示不同的內容。

為什么用請求轉發?

  • 保持同一個URL,用戶不知道內部發生了跳轉。
  • 可以在不同模塊間傳遞數據。

代碼示例:

// // 用戶訪問 /dashboard
if (userRole.equals("admin")) {// 管理員看到管理面板req.getRequestDispatcher("/admin-dashboard.jsp").forward(req, resp);
} else if (userRole.equals("user")) {// 普通用戶看到用戶面板req.getRequestDispatcher("/user-dashboard.jsp").forward(req, resp);
} else {// 游客看到歡迎頁面req.getRequestDispatcher("/guest-dashboard.jsp").forward(req, resp);
}

場景:根據商品類型顯示不同的詳情模板

// 用戶訪問 /product/detail?id=123
String productType = product.getType();
if (productType.equals("phone")) {// 手機顯示手機專用模板req.getRequestDispatcher("/phone-detail.jsp").forward(req, resp);
} else if (productType.equals("laptop")) {// 筆記本顯示筆記本專用模板req.getRequestDispatcher("/laptop-detail.jsp").forward(req, resp);
} else if (productType.equals("book")) {// 書籍顯示書籍專用模板req.getRequestDispatcher("/book-detail.jsp").forward(req, resp);
}

效果:?用戶訪問的都是?/product/detail,但根據商品類型顯示不同模板。

場景:根據訂單狀態顯示不同的處理頁面

// 用戶訪問 /order/status?id=456
String orderStatus = order.getStatus();
switch (orderStatus) {case "pending":// 待付款狀態req.getRequestDispatcher("/order-pending.jsp").forward(req, resp);break;case "paid":// 已付款狀態req.getRequestDispatcher("/order-paid.jsp").forward(req, resp);break;case "shipped":// 已發貨狀態req.getRequestDispatcher("/order-shipped.jsp").forward(req, resp);break;case "delivered":// 已送達狀態req.getRequestDispatcher("/order-delivered.jsp").forward(req, resp);break;
}

效果:?用戶訪問的都是?/order/status,但根據訂單狀態顯示不同內容。

路徑問題

  • 問題1:轉發的時候路徑上沒有加虛擬目錄web_demo_war_exploded,而重定向加了,那么到底什么時候需要加,什么時候不需要加呢?

  • 其實判斷的依據很簡單,只需要記住下面的規則即可:

    • 瀏覽器使用:需要加虛擬目錄(項目訪問路徑)
    • 服務端使用:不需要加虛擬目錄

解釋為什么瀏覽器要加虛擬目錄,服務端不加。

一、什么是虛擬目錄?

虛擬目錄就是項目在服務器上的訪問路徑,比如:

  • 你的項目叫?web_demo
  • 訪問路徑是?http://localhost:8080/web_demo/
  • 這里的?/web_demo/?就是虛擬目錄

二、為什么瀏覽器要加虛擬目錄?
// 重定向
resp.sendRedirect("/web_demo/login.html");

原因:

  • 重定向是服務器告訴瀏覽器“你去訪問這個地址”
  • 瀏覽器收到指令后,自己重新發起請求
  • 瀏覽器是外部客戶端,不知道你的項目在服務器上的具體位置
  • 所以必須告訴瀏覽器完整的訪問路徑(包含虛擬目錄)

生活中的例子

  • 你問路,別人告訴你“去銀行”,但沒說具體地址
  • 你必須知道銀行的完整地址(包含街道、門牌號),才能找到
三、為什么服務端不加虛擬目錄?

請求轉發的情況

// 請求轉發
req.getRequestDispatcher("/login.html").forward(req, resp);

原因:

  • 請求轉發是服務器內部操作
  • 服務器知道你的項目在文件系統中的真實位置
  • 不需要通過外部的虛擬目錄來定位資源
  • 直接使用內部路徑即可

生活中的例子

  • 你在公司內部,同事說“去找張三”
  • 你知道張三在哪個辦公室,不需要知道公司的門牌號
問題2:在重定向的代碼中,``web_demo_war_exploded`是固定編碼的,如果后期通過Tomcat插件配置了項目的訪問路徑,那么所有需要重定向的地方都需要重新修改,該如何優化?

在代碼中動態去獲取項目訪問的虛擬目錄,request對象中提供了getContextPath()方法

@WebServlet("/ResponseDemo1")
public class ResponseDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("這里是ResponseDemo1");//獲取虛擬目錄String contextPath = request.getContextPath();//把虛擬目錄拼在前面response.sendRedirect(contextPath + "/RequestDemo2");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
}

Response響應字符數據

要想將字符數據寫回到瀏覽器,我們需要兩個步驟:

  • 通過Response對象獲取字符輸出流: PrintWriter writer = resp.getWriter();

  • 通過字符輸出流寫數據: writer.write(“aaa”);

接下來,我們實現通過些案例把響應字符數據給實際應用下:

  1. 返回一個簡單的字符串hello world
    @WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {PrintWriter printWriter = response.getWriter();printWriter.write("hello world");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }

    返回一串html字符串,并且能被瀏覽器解析

  2. 返返回一串html字符串,并且能被瀏覽器解析,需要注意設置響應數據的編碼為utf-8
    @WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//設置響應的數據格式及數據的編碼response.setContentType("text/html;charset=utf-8");PrintWriter printWriter = response.getWriter();printWriter.write("<h1>你好<h1>");}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }

Response響應字節數據

要想將字節數據寫回到瀏覽器,我們需要兩個步驟:

  • 通過Response對象獲取字節輸出流:ServletOutputStream outputStream = resp.getOutputStream();

  • 通過字節輸出流寫數據: outputStream.write(字節數據);

接下來,我們實現通過些案例把響應字符數據給實際應用下:

  1. 返回一個圖片文件到瀏覽器
    import javax.servlet.*;
    import javax.servlet.http.*;
    import javax.servlet.annotation.*;
    import java.io.FileInputStream;
    import java.io.IOException;@WebServlet("/RequestDemo1")
    public class RequestDemo1 extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {FileInputStream fis = new FileInputStream("D:\\background.jpg");ServletOutputStream os = response.getOutputStream();byte[] buffer = new byte[1024];int len = 0;while ((len = fis.read(buffer)) != -1){os.write(buffer,0,len);}fis.close();//不需要關閉ServletOutputStream,response會幫我們關閉}@Overrideprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doGet(request, response);}
    }

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915568.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915568.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915568.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Springboot 實現熱部署

spring為開發者提供了一個名為spring-boot-devtools的模塊來使Spring Boot應用支持熱部署&#xff0c;提高開發者的開發效率&#xff0c;無需手動重啟Spring Boot應用。引入依賴<dependency><groupId>org.springframework.boot</groupId><artifactId>s…

虛擬機擴展磁盤容量后擴展分區大小

1. 首先檢查磁盤剩余空間sudo fdisk -l /dev/sda2. 如果有未分配空間&#xff0c;直接擴展分區sudo fdisk /dev/sda在fdisk交互界面中&#xff0c;依次寫入d # 刪除分區 3 # 例如選擇分區3&#xff08;/dev/sda3&#xff09; n # 新建分區 p # 主分區 3 # 分區號3 # 起始扇…

元宇宙與游戲:虛實交融的數字文明新紀元

引言&#xff1a;當游戲遇見元宇宙在紐約現代藝術博物館&#xff08;MoMA&#xff09;的"虛擬世界"特展中&#xff0c;一幅數字藝術作品《元宇宙誕生》引發觀展熱潮。這幅由AI與人類藝術家共同創作的作品&#xff0c;描繪了游戲《堡壘之夜》的虛擬演唱會與現實世界交…

音視頻學習(四十二):H264幀間壓縮技術

必要性與優勢 原始數字視頻數據量龐大&#xff0c;未經壓縮的視頻難以有效傳輸和存儲。例如&#xff0c;一個 1080p、30fps 的無壓縮視頻&#xff0c;每秒數據量可達數百兆比特。視頻壓縮的目標是在保證視覺質量的前提下&#xff0c;最大限度地減少數據冗余。視頻數據中存在多種…

微服務雪崩防護最佳實踐之sentinel

思考1、當服務訪問量達到一定程度&#xff0c;流量扛不住的時候&#xff0c;該如何處理&#xff1f;2、服務之間相互依賴&#xff0c;當服務A出現響應時間過長&#xff0c;影響到服務B的響應&#xff0c;進而產生連鎖反應&#xff0c;直至影響整個依賴鏈上的所有服務&#xff0…

阿里云監控及運維常見問題

云監控介紹&#xff1a;阿里云的云監控服務&#xff08;CloudMonitor&#xff09;是一款簡單易用、功能強大的監控工具&#xff0c;主要用來幫助用戶實時監控阿里 云上的各種資源&#xff08;比如服務器、數據庫、網絡等&#xff09;&#xff0c;并在出現問題時及時發出警報&am…

深入解析 vLLM 分布式推理與部署策略

在當今人工智能快速發展的時代&#xff0c;大型語言模型&#xff08;LLM&#xff09;的推理和部署面臨著諸多挑戰&#xff0c;尤其是當模型規模日益龐大時&#xff0c;如何高效地利用硬件資源成為關鍵問題。vLLM 作為一種強大的工具&#xff0c;為分布式推理和部署提供了多種策…

PostgreSQL 終端命令詳解及實際應用案例

PostgreSQL 作為一款功能強大的開源關系型數據庫&#xff0c;其終端命令是數據庫管理員、開發人員日常操作的核心工具。這些命令覆蓋數據庫連接、對象管理、數據操作、備份恢復、權限控制等多個維度&#xff0c;掌握其實際應用場景能顯著提升工作效率。本文結合不同行業的實操案…

步進電機基礎

1、ENABLE ̄\overline{ENABLE}ENABLE 的意思&#xff1a; ENABLE上面的橫線表示這是一個低電平有效的信號&#xff1a;當ENABLE信號為低電平&#xff08;0V或邏輯0&#xff09;時&#xff0c;芯片被使能&#xff08;激活&#xff09;當ENABLE信號為高電平&#xff08;VDD或邏輯…

Redis進階--緩存

目錄 一、引言 二、介紹 1.為什么Mysql的速度慢呢&#xff1f; 2.緩存更新策略 1.定期生成 2.實時生成 3.redis的內存淘汰機制 4.注意事項 1.緩存預熱 2.緩存穿透 3.緩存擊穿 4.緩存雪崩 三、總結 一、引言 本篇文章將介紹Redis其中一個重要的作用&#xff0c;作為…

微軟原版系統下載的幾個好用網站

“不要因為走得太遠&#xff0c;而忘記為什么出發。” 愿每一位Windows用戶&#xff0c;都能在這份情懷中找到屬于自己的小確幸&#xff01;滿滿的情懷&#xff01; 微軟官方渠道 微軟官網提供純凈的ISO鏡像下載&#xff0c;適用于Windows 10/11等系統。訪問Microsoft官網下…

kotlin Flow快速學習2025

其實&#xff0c;第一章節&#xff0c;只是讓你了解下Flow的基本情況。我們開發中&#xff0c;基本很少使用這種模式。所以來講&#xff0c;我們甚至可以直接使用StateFlow和SharedFlow才是正途。這是很多教程沒有說明的點。所以第一章隨便瀏覽下即可。日后再補充理解都是可以的…

【人工智能99問】什么是教師強制?(16/99)

文章目錄什么是教師強制&#xff1f;教師強制&#xff08;Teacher Forcing&#xff09;的定義原比例&#xff08;Original Proportion&#xff09;教師強制的舉例說明&#xff08;一&#xff09;教師強制的舉例說明&#xff08;二&#xff09;優點和缺點解決曝光偏差的方法什么…

【WPF】WPF 自定義控件之依賴屬性

&#x1f4e6; WPF 自定義控件之依賴屬性 在開發 WPF 應用時&#xff0c;自定義控件能幫助我們復用邏輯和樣式&#xff0c;但我很快會遇到一個問題&#xff1a;在控件內部如何支持數據綁定和屬性變更通知&#xff1f;特別是我們繼承自 Control 的時候&#xff0c;已經不能再繼承…

DOM型XSS破壞

目錄 首先 然后 第一種 第二種&#xff08;DOM&#xff09; HTMLCollection HTML Relationships Custom 解 首先 <script>//urlencode解碼 //location接口的hash屬性是一個字符串&#xff0c;包含一個“#”后跟位置URL的片段標識符。如果URL沒有片段標識符&#…

Linux C 多線程基本操作

我們已經了解進程的基本概念&#xff1a;進程是正在執行的程序&#xff0c;并且是系統資源分配的基本單位。當用戶需要在一臺計算機上去完成多個獨立的工作任務時&#xff0c;可以使用多進程的方式&#xff0c;為每個獨立的工作任務分配一個進程。多進程的管理則由操作系統負責…

C語言基礎:二維數組練習題

1. 一個二維數組賦了初值&#xff0c;用戶輸入一個數&#xff0c;在該二維數組中查找。找到則返回行列位置&#xff0c;沒找到則提示。#include <stdio.h>int main() {int arr[3][3] {{1, 2, 3},{4, 5, 6},{7, 8, 9}};int t;printf("要查找的數&#xff1a;")…

Java面試題034:一文深入了解MySQL(6)

Java面試題029&#xff1a;一文深入了解MySQL&#xff08;1&#xff09; Java面試題030&#xff1a;一文深入了解MySQL&#xff08;2&#xff09; Java面試題031&#xff1a;一文深入了解MySQL&#xff08;3&#xff09; Java面試題032&#xff1a;一文深入了解MySQL&#x…

Java基礎教程(011):面向對象中的構造方法

10-面向對象-構造方法 構造方法也叫做構造器、構造函數。 作用&#xff1a;在創建對象的時候給成員變量進行初始化的。 ? 一、構造方法的特點特點說明與類同名構造方法的名稱必須與類名相同沒有返回類型構造方法沒有返回值&#xff0c;甚至不能寫 void自動調用使用 new 創建對…

Adobe Photoshop:數字圖像處理的終極工具指南

Hi&#xff0c;我是布蘭妮甜 &#xff01;Adobe Photoshop自1990年問世以來&#xff0c;已經成為數字圖像處理領域的標桿和代名詞。這款強大的軟件不僅徹底改變了攝影、設計和藝術創作的方式&#xff0c;還深刻影響了我們消費和感知視覺內容的文化方式。從專業攝影師到社交媒體…