主頁 > 後端開發 > B/S結構系統的會話機制(session)

B/S結構系統的會話機制(session)

2023-04-30 07:22:05 後端開發

B/S結構系統的會話機制(session)

在這里插入圖片描述

目錄
  • B/S結構系統的會話機制(session)
    • 每博一文案
    • 1. session 會話機制的概述
    • 2. 什么是 session 的會話
    • 3. session 的作用
    • 4. session 的實作原理解釋
    • 5. 補充: Cookie禁用了,session還能找到嗎 ?
    • 6. 總結一下到目前位置我們所了解的域物件:
    • 7. oa 專案的優化體驗:使用上 session 會話機制:
    • 8. 總結:
    • 9. 最后:


每博一文案

你跑得快,22歲有個家,身邊全是贊嘆,你跑得慢,30歲還在路上追求夢想,有的人為了車,房拼了一輩子,
有的人買輛摩托車走遍了大好江山,你想成為怎樣的人,過怎樣的生活,只要你不后悔就行,
并不是所有人都能在早上七點鐘起床的,也別拿一碗飯來衡量一個人的胃口的大小,
有的人喜歡狼吞虎咽,有的人喜歡細嚼慢咽,允許別人做,別人允許自己做自己,
一歲有一歲的味道,跟著自己的心就好,不是所有選擇都要做正確的選項的,只要你想,你可以選擇
你喜歡的選項,沿途的花會一直開,以后的路也是,祝你祝我,

1. session 會話機制的概述

在Web應用程式中,我們經常要跟蹤用戶身份,當一個用戶登錄成功后,如果他繼續訪問其他頁面,Web程式如何才能識別出該用戶身份?

因為HTTP協議是一個無狀態協議,即Web應用程式無法區分收到的兩個HTTP請求是否是同一個瀏覽器發出的,為了跟蹤用戶狀態,服務器可以向瀏覽器分配一個唯一ID,并以Cookie的形式發送到瀏覽器,瀏覽器在后續訪問時總是附帶此Cookie,這樣,服務器就可以識別用戶身份,

我們把這種基于唯一ID識別用戶身份的機制稱為Session,每個用戶第一次訪問服務器后,會自動獲得一個Session ID,如果用戶在一段時間內沒有訪問服務器,那么Session會自動失效,下次即使帶著上次分配的Session ID訪問,服務器也認為這是一個新用戶,會分配新的Session ID,

2. 什么是 session 的會話

會話對應的英語單詞:session

當用戶打開瀏覽器,進行一系列操作,然后最終將瀏覽器關閉,這個整個程序叫做:一次會話,會話在服務器端也有一個對應的java物件,這個java物件叫做:session

什么是一次請求:用戶在瀏覽器上點擊了一下,然后到頁面停下來,可以粗略認為是一次請求,請求對應的服務器端的java物件是:request, 這里提前透露一點后面的內容: session 物件是用服務器端生成的,所以這里是通過 request 請求的方式向服務器獲取到一個 session 會話物件

  • 一個會話當中包含多次請求(一次會話對應N次請求,)

這里我們可以列印顯示我們的 session 地址資訊

package com.RainbowSea.session;

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.HttpSession;

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


@WebServlet("/session")
public class TestSessionServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        HttpSession session = request.getSession();

        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter out = response.getWriter();

        out.println(" session物件:  " + session);

    }
}

在這里插入圖片描述

從 session 物件當中存在于: org.apache.catalina.session.StandardSession 的位置,

在Java的servlet 的規范當中,session 對應的類名為: HttpSession(jarkata.servlett.http.HttpSession)
在這里插入圖片描述

注意:

sessioin 機制屬于 B/S結構的一部分,如果使用php語言開發WEB專案,同樣也是有session這種機制的,session機制實際上是一個規范,然后不同的語言對這種會話機制都有實作,

獲取 sessoin 的物件方法:

// 注意: sessio 是存盤在服務器端的,所以我們這里使用的是 request 請求的方式,向服務器請求獲取到 session 物件 

// 該訪問獲取到 session 物件,如果服務器端沒有 session 物件會自動創建出  session 物件
 HttpSession session = request.getSession();
        
// 獲取到 session 物件,(引數為 false )表示:如果服務器當中沒有 session 是不會自動創建的,
HttpSession session1 = request.getSession(false);

3. session 的作用

session物件最主要的作用是:保存會話狀態,(用戶登錄成功了,這是一種登錄成功的狀態,你怎么把登錄成功的狀態一直保存下來呢?使用session物件可以保留會話狀態,)

那我們為什么需要session 物件來保存會話狀態呢?

因為HTTP協議是一種無狀態協議,

什么是無狀態:請求的時候,B和S是連接的,但是請求結束之后,連接就斷了,為什么要這么做?HTTP協議為什么要設計成這樣?因為這樣的無狀態協議,可以降低服務器的壓力,請求的瞬間是連接的,請求結束之后,連接斷開,這樣服務器壓力小,

只要B和S斷開了,那么關閉瀏覽器這個動作,服務器知道嗎?

因為 HTTP 協議是無狀態的連接的,所以當我們關閉了 瀏覽器的時候,我們的服務器端是無法接收到瀏覽器被關閉的一個資訊的,所以:我們的服務器自然也就無法知道瀏覽器關閉了,

一個會話對應一個 sessoin 物件,一個 session 對應上一個 ID也就是 (JSESSIONID)

比如:張三打開一個瀏覽器 A,李四打開一個瀏覽器B,訪問服務器之后,在服務端會生成:

  • 張三專屬的session物件,同時會標記上一個 對應的 ID 資訊
  • 李四專屬的session物件 ,同時會標記上一個對應的 ID 資訊,
  • 注意了:這兩者之間的 ID資訊是不一樣的,

代碼舉例:

package com.RainbowSea.serssion;

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.HttpSession;

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

@WebServlet("/test/session")
public class TestSessionServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // request 和 session 都是在服務端的java物件,都在JVM當中
        // request物件代表一次請求,(一次請求對應一個request物件,再次請求就會對應兩個不同的request物件)
        // session物件代表一次會話,(一次會話對應一個session 物件)
        // 獲取session,如何服務器當中沒有 session 物件就會自動創建一個,
        HttpSession session = request.getSession();
        // 獲取到服務器端的 session ,如果沒有不會自動創建 session 物件
        //HttpSession session1 = request.getSession(false);

        //session.setAttribute(); 將資料存盤到 session 會話當中,
        //session.getAttribute() 將資料從 session 會話當中取出




        // 將session 物件回應到瀏覽器端
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println("會話物件:" + session);



    }
}

結果:

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

什么表示一個會話

粗略的可以認為一個:當我們打開一個瀏覽器訪問一個A服務器的時候,如果這個瀏覽器不關閉的情況下,該瀏覽器發送的請求都是向 A服務器,那么哪個瀏覽器發送對于這個A服務器發送的所有的請求都可以理解為是一個 session 會話,

我們也可以再精細一點的再說一下:就是我們在京東網站,A用戶登錄以后,在京東網站當中的,進行查詢商品,購買商品,添加商品購物車,等等,都是屬于該 A用戶專屬的一個 session 的會話,當我們再在京東網站當中,B用戶登錄以后,在京東網站當中的,進行查詢商品,購買商品,添加商品購物車,等等這些是 B用戶請求操作的都是專屬于一個 session 會話,

為什么不使用request物件保存會話狀態?為什么不使用ServletContext物件保存會話狀態?

  • request.setAttribute()存資料,request.getAttribute()取資料,ServletContext也有這個方法,request是請求域,ServletContext是應用域,
  • request是一次請求一個物件,
  • ServletContext物件是服務器啟動的時候創建,服務器關閉的時候銷毀,這個ServletContext物件只有一個,
  • ServletContext物件的域太大,
  • request請求域(HttpServletRequest)、session會話域(HttpSession)、application應用域(ServletContext)
  • 三個域之間的作用域的大小關系:request (請求域)< session(會話域) < application(應用域) ,

4. session 的實作原理解釋

HttpSession session = request.getSession();

這行代碼很神奇,張三訪問的時候獲取的 session 物件就是張三專屬的,李四訪問的時候獲取的 session 物件就是李四專屬的,

這是如何做到的呢?我們可以舉一個有關于我們實際生活當中的一個例子:

比如: 我們張三,李四都是在同一個大學的班級當中,張三和李四上的都是同一個籃球課(體育課),當他們上課的時候

,他們的體育老師帶來了(一筐籃球)(就是 session ),讓同學們自行挑選好自己的籃球,用于上籃球課,這時候我們的張三認真的挑選到了一個籃球,并且試了試手感,感覺十分的不錯,心里就有了一點小心思:就是想要,自己每次上籃球課的時候,都可以找到,并拿到這個手感不錯的籃球,怎么實作這個想法呢?于是,張三同學就在,這個他手中的(手感不錯)籃球上做了一個標記(SESSIONID=xxxxxx),這個標記只有張三自己知道是干什么的,其他同學都不知道,這樣當下次以后的每一節籃球課,張三都可以根據自己所作的這個標記,從眾多籃球當中,找到這個,自己標記到的籃球了,

這個例子當中的: 一筐籃球就可以比作是 : 服務器的當中的 session 會話物件,而其中的 張三自己在籃球上作的標記就可以比作是: SESSIONID=xxxxxx 是 session 物件的 ID 了,

在這里插入圖片描述

session 生成的程序:

一個 session 會話物件 對應一個 JSESSIONID=xxxxxx (就是一個標記 session 會話物件的 ID (類似于一個人的身份證資訊)是唯一的),

服務器當中是有一個類似于 Map 的一個 session 串列,該 session 串列當中存在兩樣東西: key 對應的是 JSESSIONID=xxxxxx (也就是 session 的ID的標記) ,而 value 對應的則是 session 物件,

key (session 的 ID) value ( session 物件)
JSESSIONID=123 session1
JSESSIONID=456 session2

當用戶第一次請求服務器的時候,服務器會為該用戶生成一個 session 會話物件,同時服務器會將該 session 對應 JSESSIONID=123,也就是: sessionID 發送給客戶端,客戶端會接收到服務器發送過來的 JSESSIONID ,并存盤到 客戶端的快取(Cookie) 當中,同時需要注意的是: JSESSIONID=xxxxxx 這個是以Cookie的形式保存在瀏覽器的記憶體中的,瀏覽器只要關閉,這個cookie就沒有了,(當然這是默認的情況下,你是可以自定義設定的,關于 Cookie 的內容這里就不會說明了,)

舉例:具體代碼詳細如下:

package com.RainbowSea.session;

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.HttpSession;

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


@WebServlet("/session")
public class TestSessionServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        HttpSession session = request.getSession();

        // 
        response.setContentType("text/html;charSet=UTF-8");
        PrintWriter out = response.getWriter();


        //
        out.println(" session物件:  " + session);

    }
}

如下是我們的瀏覽器(客戶端) 向服務器 第一次 發送請求(response) 的效果圖:如下:

在這里插入圖片描述

第二次,我們的瀏覽器(客戶端)向服務器發送 第二次請求(response) ,因為我們瀏覽器(客戶端)第一次請求的時候,已經將服務器回應過來的 JSESSIONID 存盤到了,自己客戶端的 Cookie 當中去了,所以,當我們的客戶端再次向上一個服務器發送請求的時候,這是同屬于同一個會話的,所以我們的客戶端將第一次請求的時候,獲取到的 JSESSIONID 發送給 服務器,服務器根據 JSESSIONID 查找session物件, 回傳給客戶端,所以兩者之間的 session 物件的地址是一樣的,因為是同屬于同一個會話的,測驗效果如下:

在這里插入圖片描述

注意:我們的瀏覽器是遵循 HTTP 協議的,而 HTTP 協議是 無狀態的,導致我們的服務器無法知道瀏覽器關閉了,所以我們的 會話銷毀存在一種(延遲銷毀的機制:簡單的說就是,當一個 session 會話,在一定的時間段內沒有,任何的請求發發送了,服務器就會認為該 sessoin 沒有用了,會自動銷毀該 session 會話物件了),當我們關閉瀏覽器,記憶體消失,Cookie 消失,Cookie 消失了,那存在其中的 JSESSIONID (也就是 sessionID ) 自然也就消失了,而 JSESSIONID 消失了,我們的客戶端也就無法根據該 JSESSIONID 獲取到,訪問到 對應的 session 物件了,當到達一定的時間段后,還是沒有任何客戶端訪問該 Session 會話,服務器就會自動銷毀該 session 會話物件了,

關閉瀏覽器,重新發送請求,測驗效果如下圖所示:

在這里插入圖片描述

session物件的銷毀:

session 物件是什么時候銷毀:

瀏覽器關閉的時候,服務器是不知道的,服務器無法監測到瀏覽器關閉了(HTTP協議是無狀態協議),所以 session 的銷毀要依靠 session 超時機制,

但也有一種可能,系統提供了 “安全退出”,用戶可以點擊這個按鈕,這樣服務器就知道你退出了,然后服務器會自動銷毀 session 物件,

  • 第一種: 手動銷毀
// 銷毀 session 物件的
session.invalidate(); 
  • 第二種:自動銷毀(超時銷毀)

為什么關閉瀏覽器,會話結束?

關閉瀏覽器之后,瀏覽器中保存的 JSESSIONID (也就是 session 的ID)消失,下次重新打開瀏覽器之后,

瀏覽器快取中沒有這個 session的ID,自然找不到 服務器中對應的 session 物件,session 物件找不到,等同于會話結束,(超時銷毀,當一個 session 一段時間內沒有,被訪問了,就會自動被服務器銷毀,這里我們的 JSESSIONID 都沒有了,我們就無法找到對應 session 的物件,無法找到 session 物件,就更無法訪問了,)

session 超時銷毀機制的設定的時間點,默認是 Tomcat apache-tomcat-10.0.12\conf\web.xml的 web.xml 配置當中,默認配置為了 30 分鐘

在這里插入圖片描述

在這里插入圖片描述

<!-- ==================== Default Session Configuration ================= -->
<!-- You can set the default session timeout (in minutes) for all newly   -->
<!-- created sessions by modifying the value below.                       -->
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sJQQVVaF-1682775113687)(E:\博客\javaWed博客庫\image-20230424221802452.png)]

當然,這個 session 超時銷毀的時間點,我們也是可以設定的,

我們可以根據自己的需要設定,比如:如果是一個銀行的安全資訊的話,可以設定為 1~5 分鐘,如果是一個長久使用的話可以設定為 24 小時,7天等等,根據實際業務需要靈活的設定,

重點:如下是 session 的生成,銷毀的原理圖示:

在這里插入圖片描述

5. 補充: Cookie禁用了,session還能找到嗎 ?

cookie禁用是什么意思?服務器正常發送cookie給瀏覽器,但是瀏覽器不要了,拒收了,并不是服務器不發了,

如下是: Google Chrome 瀏覽器禁用 Cookie 的設定:
在這里插入圖片描述

在這里插入圖片描述

當我們禁用了瀏覽器的 Cookie 設定,再次訪問我們的 Servlet 服務器的效果如下:

在這里插入圖片描述

下面這個是 Firefox火狐瀏覽器的禁用 Cookie 的設定,

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

結論:當瀏覽器禁用了Cookie 快取功能,服務器正常發送cookie資訊(包括了 JSESSIONID 資訊)給瀏覽器,但是瀏覽器不要了,拒收了,并不是服務器不發了,所以導致的結果就是:客戶端不會發送給服務器 JSESSIONID資訊了,找不到了,每一次請求都會獲取到新的session物件,

問題:cookie禁用了,session機制還能實作嗎?

可以,需要使用 URL 重寫機制,

如下:演示:當我們訪問服務器時,通過瀏覽器的 檢查功能中的 ——> 網路(NetWork) 當中的第一次請求服務器,服務器回應給客戶端的 JSESSIONID 的資訊會顯示在其中的:response headers (請求頭當中 ),

在這里插入圖片描述

將其中的 jsessionid=19D1C99560DCBF84839FA43D58F56E16 拼接到我們訪問的 URL當中,中間使用 ; 分號隔開,如下:需要注意的是,將其中的 JSESSIONID 寫成小寫的: jsessionid

http://127.0.0.1:8080/servlet14/session;jsessionid=F247C2C5CBE489F45383D116224F071B

在這里插入圖片描述

在這里插入圖片描述

原理:是雖然我們瀏覽器沒有保存住服務器回應過來的JSESSIONID資訊,但是我們手動將其中的SESSIOND給記住了,并通過地址欄的方式,get的方式發送給了服務器,服務器就會幫我們去session串列當中找到該對過的JSESSIONID的
session物件,而不是新建esssion物件了,

URL重寫機制會提高開發者的成本,開發人員在撰寫任何請求路徑的時候,后面都要添加一個sessionid,給開發帶來了很大的難度,很大的成本,所以大部分的網站都是這樣設計的:你要是禁用cookie,你就別用了,

怎么理解這個: 你要是禁用了 Cookie 快取機制,你就別用了,就是說,如果你把 Cookie 禁用了一些網站你可能打不開來,或者說無法顯示全部內容資訊,當你開始這個設定 禁用Cookie 都會有一些提示的資訊給到你的,比如:

在這里插入圖片描述

,如下當我們把 Firefox火狐瀏覽器的禁用 Cookie 打開,訪問

  • 京東網站:https://www.jd.com/

在這里插入圖片描述

  • 訪問淘寶:https://www.taobao.com/

在這里插入圖片描述

  • 訪問唯品會:https://www.vip.com/

在這里插入圖片描述

  • 訪問12306 網站:https://www.12306.cn/index/

在這里插入圖片描述

6. 總結一下到目前位置我們所了解的域物件:

  • request(對應的類名:HttpServletRequest)請求域(請求級別的)
  • session(對應的類名:HttpSession)會話域(用戶級別的)
  • application(對應的類名:ServletContext)應用域(專案級別的,所有用戶共享的,)
  • 這三個域物件的大小關系:request < session < application
  • 他們三個域物件都有以下三個公共的方法:
    • setAttribute(向域當中系結資料)
    • getAttribute(從域當中獲取資料)
    • removeAttribute(洗掉域當中的資料)
  • 使用原則:盡量使用小的域,

7. oa 專案的優化體驗:使用上 session 會話機制:

閱讀如下內容,大家可以先移步至: Servlet注解的使用,簡化配置 以及,使用模板方法設計模式優化oa專案_ChinaRainbowSea的博客-CSDN博客看看有助于閱讀理解,

session掌握之后,我們怎么解決oa專案中的登錄問題:就是我們的登錄頁面是一個擺設,當用戶沒有登錄的情況下,可以直接通過在地址欄上輸入 URL 可以訪問到對應的資源資訊,

這里我們可以使用: session 會話機制,讓登錄起作用:就是如果用戶直接通過在地址欄上輸入 URL 可以訪問到對應的資源資訊的時候,判斷用戶是否登錄過,如果登錄過,則可以直接訪問,如果沒有登錄過就跳轉到登錄頁面,進行一個正確的登錄成功的操作,才可以訪問,同時設定一個安全退出系統,銷毀 session 物件的按鈕設定,

登錄成功之后,可以將用戶的登錄資訊存盤到session當中,也就是說session中如果有用戶的資訊就代表用戶登錄成功了,session中沒有用戶資訊,表示用戶沒有登錄過,則跳轉到登錄頁面,

優化原始碼如下:

首先是登錄頁面的優化:當用戶登錄成功,將用戶的登錄資訊存盤到session當中(這里我們存盤到用戶的用戶名資訊,)

核心優化代碼:

// 登錄成功與否
if (success) {

   // 成功,跳轉到用戶串列頁面
   // 這里使用重定向(沒有資源的共享):重定向需要加/專案名 +

   // 獲取session 物件(這里的要求是: 必須獲取到 session ,沒有session 也要新建一個 session 物件)
   // 注意:我們下面的這個會話是不能洗掉的,因為上面我們雖然通過 welcome Servlet 進行了一個會話
   // 但是 welcome 當中是當我們cookie 當中存在并且用戶名和密碼正確的時候才會進行一個 session 的
   HttpSession session = request.getSession();  // 服務器當中沒有 session 會話域自動創建
   session.setAttribute("username", username);  // 將用戶名存盤到 session 會話域當中


   response.sendRedirect(request.getContextPath() + "/dept/list");
   } else {
     // 失敗,跳轉到失敗頁面
     response.sendRedirect(request.getContextPath() + "/error.jsp");
   }

全部的代碼:

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
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.HttpSession;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


@WebServlet({"/user/login", "/user/exit"})
public class UserServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // 獲取到瀏覽器地址欄上的URL路徑
        String servletPath = request.getServletPath();

        if ("/user/login".equals(servletPath)) {
            doLogin(request, response);
        } else if ("/user/exit".equals(servletPath)) {
            doExit(request, response);
        }


    }

    private void doExit(HttpServletRequest request, HttpServletResponse response) throws IOException {
    }

    protected void doLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        // 一個用戶登錄驗證的方式:驗證用戶名和密碼是否正確
        // 獲取用戶名和密碼
        // 前端提交是資料是:username=111&password=fads
        // 注意:post 提交的資料是在請求體當中,而get提交的資料是在請求行當中

        boolean success = false;  // 標識登錄成功

        String username = request.getParameter("username");
        String password = request.getParameter("password");

        String exempt = request.getParameter("exempt");

        // 連接資料庫驗證用戶名和密碼
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        try {
            // 1. 獲取連接,注冊驅動
            connection = DBUtil.getConnection();

            // 2. 獲取操作資料物件,預編譯sql陳述句, ? 占位符不要加,“”,'' 單雙引號,成了字串了,無法識別成占位符了,
            String sql = "select username,password from t_user where username = ? and password = ?";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正執行sql陳述句
            preparedStatement.setString(1, username);
            preparedStatement.setString(2, password);

            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            // 只有一條結果集
            if (resultSet.next()) {
                // 登錄成功
                success = true;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5. 關閉資源,最后使用的最先關閉,
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        // 登錄成功與否
        if (success) {
            
            // 成功,跳轉到用戶串列頁面
            // 這里使用重定向(沒有資源的共享):重定向需要加/專案名 +

            // 獲取session 物件(這里的要求是: 必須獲取到 session ,沒有session 也要新建一個 session 物件)
            // 注意:我們下面的這個會話是不能洗掉的,因為上面我們雖然通過 welcome Servlet 進行了一個會話
            // 但是 welcome 當中是當我們cookie 當中存在并且用戶名和密碼正確的時候才會進行一個 session 的
            HttpSession session = request.getSession();  // 服務器當中沒有 session 會話域自動創建
            session.setAttribute("username", username);  // 將用戶名存盤到 session 會話域當中


            response.sendRedirect(request.getContextPath() + "/dept/list");
        } else {
            // 失敗,跳轉到失敗頁面
            response.sendRedirect(request.getContextPath() + "/error.jsp");
        }


    }
}

其次是當用戶想要直接通過 URL訪問的時候,判斷用戶是否登錄成功過,登錄成功過可以訪問,沒有登錄成功過無法訪問:

思路是:

我們通過 session 會話機制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問 到其資訊,不可以,因為我們這里判斷了一次是否登錄過,只有登錄入過了,才會將中登錄到用戶名為 “username” 的資訊存盤到 session 會話當中,如果沒有的話是查詢不到的,回傳的是 null,需要注意的一點就是,我們的jsp 當中的內置物件,是會自動創建一個 session 會話物件的(所以就會導致,就算我們沒有登錄成功 ,session 物件也是不為空的,因為JSP創建了 session 物件,我們可以通過JSP 指令禁止 JSP 生成 session 內置物件 <%@page session = false %>,需要所有會被訪問,生成的 Jsp 檔案都需要設定該指令,這里 所謂的禁用了就是,對應的訪問生成的 xxx_jsp.java) 當中不會翻譯生成其中內置的 session 物件),但是因為這里我們進行了一個 雙重的判斷機制,

if(session != null && session.getAttribute("username") != null)
// 雙重的判斷,一個是 session 會話域要存在,其次是 會話域當中存盤了名為 "username" 的資訊,可以用戶登錄的資訊可以從 session 找到,如果找不到 ,回傳 null ,找到不為 null ,這樣就解決了 JSP 內置session 物件的沒有登錄 session 不為 null 的影響了,

需要注意一點的就是:這里我們要使用HttpSession session = request.getSession(false)

HttpSession session = request.getSession(false);  // 獲取到服務器當中的session ,沒有不會創建的,
// session 是用戶登錄成功才創建的,其他情況不要創建 session 會話物件,

核心代碼:


// 可以使用模糊查詢 @WebServlet("/dept/*")
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        String servletPath = request.getServletPath();  // 獲取到瀏覽器當中的uri

        // 獲取session 這個 session 是不不需要新建的
        // 只是獲取當前session ,獲取不到這回傳null,
        HttpSession session = request.getSession(false);  // 獲取到服務器當中的session ,沒有不會創建的


        /**
         * 說明這里我們通過 session 會話機制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問
         * 到其資訊,不可以,因為我們這里判斷了一次是否登錄過,只有登錄入過了,才會將中登錄到
         * 用戶名為 “username” 的資訊存盤到 session 會話當中,如果沒有的話是查詢不到的,回傳的是 null
         * 需要注意的一點就是,我們的jsp 當中的內置物件,是會自動創建一個 session 會話物件的,但是
         * 因為這里我們進行了一個 雙重的判斷機制,注意:需要先將對應的 xx_jsp.java 生成才行,同時
         * 使用 <%@page session = false %> 指令的話,需要所有會被訪問,生成的 Jsp 檔案都需要設定,
         *
         *   jakarta.servlet.http.HttpSession session = null;
         *   session = pageContext.getSession();
         */
        if(session != null && session.getAttribute("username") != null) {
            // 雙重的判斷,一個是 session 會話域要存在,其次是 會話域當中存盤了名為 "username" 的資訊
            if ("/dept/list".equals(servletPath)) {
                doList(request, response);
            } else if ("/dept/detail".equals(servletPath)) {
                doDetail(request, response);
            } else if ("/dept/delete".equals(servletPath)) {
                doElete(request,response);
            } else if("/dept/save".equals(servletPath)) {
                doSave(request,response);
            } else if("/dept/modify".equals(servletPath)) {
                doModify(request,response);
            }
        } else {
            response.sendRedirect(request.getContextPath());  // 訪問的web 站點的根即可,自動找到的是名為 index.jsp
        }

    }
}

最后就是:用戶點擊安全退出系統,銷毀 session 物件的實作了,

當我們點擊 安全退出,手動將 session 會話物件銷毀了,就需要重新登錄了,只有重新登錄,建立新的登錄成功的 session 會話資訊,才能再次通過URL訪問,

核心代碼如下:

session.invalidate();  // 銷毀 session 物件,
/**
* 用戶手動點擊安全退出,銷毀 session 物件
* @param request
 * @param response
* @throws IOException
*/
private void doExit(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 獲取到客戶端發送過來的 sessoin
        HttpSession session = request.getSession();

        if (session != null) {
            // 手動銷毀 session 物件
            // 注意:會話銷毀的了,自然需要重寫登錄了,沒有登錄過,無法進行一個路徑的訪問的
            session.invalidate();
            
            // 跳轉會登錄的頁面
            response.sendRedirect(request.getContextPath());  // 專案名路徑默認就是訪問的index.html 的歡迎頁面
        }
    }

全部具體代碼:

package com.RainbowSea.servlet;

import com.RainbowSea.DBUtil.DBUtil;
import com.RainbowSea.bean.Dept;
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.HttpSession;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

// 可以使用模糊查詢 @WebServlet("/dept/*")
@WebServlet({"/dept/list", "/dept/detail", "/dept/delete", "/dept/save", "/dept/modify"})
public class DeptServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
            IOException {

        String servletPath = request.getServletPath();  // 獲取到瀏覽器當中的uri

        // 獲取session 這個 session 是不不需要新建的
        // 只是獲取當前session ,獲取不到這回傳null,
        HttpSession session = request.getSession(false);  // 獲取到服務器當中的session ,沒有不會創建的


        /**
         * 說明這里我們通過 session 會話機制,判斷用戶是否登錄過,如果用戶沒有登錄就想要訪問
         * 到其資訊,不可以,因為我們這里判斷了一次是否登錄過,只有登錄入過了,才會將中登錄到
         * 用戶名為 “username” 的資訊存盤到 session 會話當中,如果沒有的話是查詢不到的,回傳的是 null
         * 需要注意的一點就是,我們的jsp 當中的內置物件,是會自動創建一個 session 會話物件的,但是
         * 因為這里我們進行了一個 雙重的判斷機制,注意:需要先將對應的 xx_jsp.java 生成才行,同時
         * 使用 <%@page session = false %> 指令的話,需要所有會被訪問,生成的 Jsp 檔案都需要設定,
         *
         *   jakarta.servlet.http.HttpSession session = null;
         *   session = pageContext.getSession();
         */
        if(session != null && session.getAttribute("username") != null) {
            // 雙重的判斷,一個是 session 會話域要存在,其次是 會話域當中存盤了名為 "username" 的資訊
            if ("/dept/list".equals(servletPath)) {
                doList(request, response);
            } else if ("/dept/detail".equals(servletPath)) {
                doDetail(request, response);
            } else if ("/dept/delete".equals(servletPath)) {
                doElete(request,response);
            } else if("/dept/save".equals(servletPath)) {
                doSave(request,response);
            } else if("/dept/modify".equals(servletPath)) {
                doModify(request,response);
            }
        } else {
            response.sendRedirect(request.getContextPath());  // 訪問的web 站點的根即可,自動找到的是名為 index.jsp
        }

    }


    /**
     * 修改部門資訊
     *
     * @param request
     * @param response
     */
    private void doModify(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影響資料庫的行數
        int count = 0;


        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");


        try {
            // 1. 注冊驅動,連接資料庫
            connection = DBUtil.getConnection();

            // 2. 獲取到操作資料庫的物件,預編譯sql陳述句,sql測驗
            String sql = "update dept set dname = ?,loc = ? where depton = ?";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正執行sql陳述句
            // 從下標 1開始
            preparedStatement.setString(1, dname);
            preparedStatement.setString(2, loc);
            preparedStatement.setString(3, deptno);

            count = preparedStatement.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 4. 釋放資源,最后使用的優先被釋放
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 更新成功
            // 跳轉到部門串列頁面(部門串列表面是通過java程式動態生成的,所以還需要再次執行另一個Servlet)
            // 轉發是服務器內部的操作,“/” 不要加專案名
            // request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 優化使用重定向,自發前端(需要指明專案名)
            response.sendRedirect(request.getContextPath() + "/dept/list");

        }
    }


    /**
     * 保存部門資訊
     *
     * @param request
     * @param response
     */
    private void doSave(HttpServletRequest request, HttpServletResponse response) throws IOException {

        request.setCharacterEncoding("UTF-8");

        // 獲取到前端的資料,建議 name 使用復制
        String deptno = request.getParameter("deptno");
        String dname = request.getParameter("dname");
        String loc = request.getParameter("loc");

        // 連接資料庫,添加資料
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        // 影響資料庫的行數
        int count = 0;

        try {
            // 1. 注冊驅動,連接資料庫
            connection = DBUtil.getConnection();

            // 2. 獲取操作資料庫物件,預編譯sql陳述句,Sql測驗
            String sql = "insert into dept(depton,dname,loc) values(?,?,?)";
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符, 真正執行sql陳述句,
            // 注意: 占位符的填充是從 1 開始的,基本上資料庫相關的起始下標索引都是從 1下標開始的
            preparedStatement.setString(1, deptno);
            preparedStatement.setString(2, dname);
            preparedStatement.setString(3, loc);

            // 回傳影響資料庫的行數
            count = preparedStatement.executeUpdate();

            // 5.釋放資源
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.close(connection, preparedStatement, null);
        }

        // 保存成功,回傳部門串列頁面
        if (count == 1) {
            // 這里應該使用,重定向
            // 這里用的轉發,是服務器內部的,不要加專案名
            //request.getRequestDispatcher("/dept/list/").forward(request, response);

            // 重定向
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }


    /**
     * 通過部門洗掉部門
     *
     * @param request
     * @param response
     */
    private void doElete(HttpServletRequest request, HttpServletResponse response) throws IOException {

        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        // 獲取到發送資料
        String deptno = request.getParameter("deptno");

         /*
        根據部門編號洗掉資訊,
        洗掉成功,跳轉回原來的部門串列頁面
        洗掉失敗,跳轉洗掉失敗的頁面
         */

        Connection connection = null;
        PreparedStatement preparedStatement = null;

        // 記錄洗掉資料庫的行數
        int count = 0;


        // 連接資料庫進行洗掉操作

        try {
            // 1.注冊驅動,連接資料庫
            connection = DBUtil.getConnection();

            // 開啟事務(取消自動提交機制),實作可回滾
            connection.setAutoCommit(false);

            // 2. 預編譯sql陳述句,sql測驗
            String sql = "delete from dept where depton = ?"; // ? 占位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正的執行sql陳述句
            preparedStatement.setString(1, deptno);
            // 回傳影響資料庫的行數
            count = preparedStatement.executeUpdate();
            connection.commit();  // 手動提交資料
        } catch (SQLException e) {
            // 遇到例外回滾
            if (connection != null) {
                try {
                    // 事務的回滾
                    connection.rollback();
                } catch (SQLException ex) {
                    throw new RuntimeException(ex);
                }
            }
            throw new RuntimeException(e);
        } finally {
            // 4. 釋放資源
            // 因為這里是洗掉資料,沒有查詢操作,所以 沒有 ResultSet 可以傳null
            DBUtil.close(connection, preparedStatement, null);
        }

        if (count == 1) {
            // 洗掉成功
            // 仍然跳轉到部門串列頁面
            // 部門串列頁面的顯示需要執行另外一個Servlet,怎么辦,可以使用跳轉,不過這里最后是使用重定向
            // 注意:轉發是在服務器間的,所以不要加“專案名” 而是 / + web.xml 映射的路徑即可
            //request.getRequestDispatcher("/dept/list/").forward(request,response);

            // 優化:使用重定向機制 注意: 重定向是自發到前端的地址欄上的,前端所以需要指明專案名
            // 注意: request.getContextPath() 回傳的根路徑是,包含了 "/" 的
            response.sendRedirect(request.getContextPath() + "/dept/list");
        }
    }


    /**
     * 通過部門編號,查詢部門的詳情
     *
     * @param request
     * @param response
     */
    private void doDetail(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 獲取到部門編號
        String dno = request.getParameter("dno");
        Dept dept = new Dept();

        // 獲取到部門編號,獲取部門資訊,將部門資訊收集好,然后跳轉到JSP做頁面展示


        try {
            // 2. 連接資料庫,根據部門編號查詢資料庫
            // 1.注冊驅動,連接資料庫
            connection = DBUtil.getConnection();

            // 2. 預編譯SQL陳述句,sql要測驗
            String sql = "select dname,loc from dept where depton = ?";  // ? 占位符
            preparedStatement = connection.prepareStatement(sql);

            // 3. 填充占位符,真正執行sql陳述句
            preparedStatement.setString(1, dno);
            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            while (resultSet.next()) {
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                // 封裝物件(建議使用咖啡豆,因為只有一個物件)
                dept.setDeptno(dno);
                dept.setDname(dname);
                dept.setLoc(loc);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // 5. 釋放資源
            DBUtil.close(connection, preparedStatement, resultSet);
        }

        // 這個咖啡豆只有一個,所以不需要袋子,只需要將這個咖啡豆放到request請求域當中,
        // 用于對應的 jsp顯示
        request.setAttribute("dept", dept);
        //String sign = request.getParameter("f");

        /*if("m".equals(sign)) {

            // 轉發:多個請求為一個請求(地址欄不會發生改變)
            // 注意: 該路徑默認是從 web 開始找的 / 表示 web
            // 轉發到修改頁面
            request.getRequestDispatcher("/edit.jsp").forward(request,response);
        } else if("d".equals(sign)) {
            // 跳轉到詳情頁面
            request.getRequestDispatcher("/detail.jsp").forward(request,response);
        }*/

        // 或者優化
        // 注意 無論是轉發還是重定向都是從 “/” 開始的
        // request.getParameter()拿到的是 f=edit,還是f=detail 就是跳轉到的哪個頁面
        //<a href="https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/04/29//dept/detail?f=edit&dno=">修改</a>
        //<a href="https://www.cnblogs.com/TheMagicalRainbowSea/archive/2023/04/29//dept/detail?f=detail&dno=">詳情</a>
        String forward = "/" + request.getParameter("f") + ".jsp";
        request.getRequestDispatcher(forward).forward(request, response);
    }


    /**
     * 連接資料庫,查詢所有的部門資訊,將部門資訊收集好,然后跳轉到JSP頁面展示
     *
     * @param request
     * @param response
     */
    private void doList(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");  // 設定獲取的的資訊的編碼集
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;

        // 創建一個集合List 存盤查詢到的資訊
        List<Dept> depts = new ArrayList<Dept>();


        try {
            // 連接資料庫,查詢所有部門:
            // 1. 注冊驅動,獲取連接
            connection = DBUtil.getConnection();
            // 2. 獲取操作資料庫物件,預編譯sql陳述句
            String sql = "select depton as det,dname,loc from dept"; // 在mysql中測驗一下是否正確
            preparedStatement = connection.prepareStatement(sql);

            // 3. 執行sql陳述句
            resultSet = preparedStatement.executeQuery();

            // 4. 處理查詢結果集
            while (resultSet.next()) {
                String det = resultSet.getString("det");  // 有別名要使用別名
                String dname = resultSet.getString("dname");
                String loc = resultSet.getString("loc");

                Dept dept = new Dept(det, dname, loc);

                // 將部門物件放到List集合當中
                depts.add(dept);

            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {

            // 5. 關閉資源
            DBUtil.close(connection, preparedStatement, resultSet);
        }


        // 查詢到資料,將資料提交給 list.jsp 顯示資料
        // 將集合存盤的資料放到請求域當中,用于其他Servlet 使用 jsp 也是Servelt
        request.setAttribute("depList", depts);

        // 轉發(注意不要重定向),重定向無法共用 request 請求域當中的資料
        // 轉發路徑,/ 默認是從 web 目錄開始找的
        request.getRequestDispatcher("/list.jsp").forward(request, response);
    }
}

用戶界面的優化:顯示 登錄的用戶名:(該用戶名資訊,從 存盤到 session 會話物件當中,獲取到的,)

核心代碼如下:

需要注意的點就是:這里我們使用的是 JSP 內置的 session 物件,所以在這個 JSP頁面當,你不可以把 session 禁用了,

不要設定這個禁用 session 的指令: <%@page session = false %>

在這里插入圖片描述

優化演示:

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

8. 總結:

  1. session 會話用戶場景:在Web應用程式中,我們經常要跟蹤用戶身份,當一個用戶登錄成功后,如果他繼續訪問其他頁面,Web程式如何才能識別出該用戶身份?
  2. session物件最主要的作用是:保存會話狀態,
  3. 為什么要保存會話狀態:因為HTTP協議是一種無狀態協議,
    • 無狀態:
      • 優點:這樣服務器壓力小,
      • 缺點:服務器無法知道客戶端的狀態(是關閉的狀態,還是開啟的狀態)
  4. 一個 session 會話當中包含多次請求(一次會話對應N次請求,)
  5. session 物件是用服務器端生成的,所以這里是通過 request 請求的方式向服務器獲取到一個 session 會話物件
  6. session 的生成,銷毀,傳遞的原理機制:

在這里插入圖片描述

  1. 簡單的來說吧 ,session 就是一個標記,通過標記 JSESSIONID 獲取到同一個 session 物件,保證你對應的操作是同一個用戶,
  2. Cookie禁用了,session還能找到嗎 ? 可以,使用 URL重寫機制,
  3. 實作用戶登錄,通過 session 會話機制(保存用戶登錄資訊),實作用戶登錄成功,可以通過 URL 直接訪問資源,沒有登錄/登錄失敗,則無法直接通過 URL 訪問資源,

9. 最后:

限于自身水平,其中存在的錯誤,希望大家給予指教,韓信點兵——多多益善,謝謝大家,江湖再見,后悔有期

在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/551493.html

標籤:其他

上一篇:Django筆記三十三之快取操作

下一篇:返回列表

標籤雲
其他(158316) Python(38110) JavaScript(25398) Java(18011) C(15221) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7152) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5870) 数组(5741) R(5409) Linux(5334) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4565) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2432) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1964) Web開發(1951) HtmlCss(1929) python-3.x(1918) 弹簧靴(1913) C++(1912) xml(1889) PostgreSQL(1874) .NETCore(1857) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • B/S結構系統的會話機制(session)

    B/S結構系統的會話機制(session) 每博一文案 你跑得快,22歲有個家,身邊全是贊嘆,你跑得慢,30歲還在路上追求夢想。有的人為了車,房拼了一輩子, 有的人買輛摩托車走遍了大好江山。你想成為怎樣的人,過怎樣的生活,只要你不后悔就行。 并不是所有人都能在早上七點鐘起床的,也別拿一碗飯來衡量一個 ......

    uj5u.com 2023-04-30 07:22:05 more
  • Django筆記三十三之快取操作

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記三十三之快取操作 這一節介紹一下如何在 Django 中使用 redis 做快取操作。 在 Django 中可以有很多種方式做快取,比如資料庫,比如服務器檔案,或者記憶體,這里介紹用的比較多的使用 redis 作為快取。 這篇筆記主要內容如 ......

    uj5u.com 2023-04-30 07:21:30 more
  • Markdown基本語法

    Markdown 基本語法介紹 Markdown 是一種輕量級的標記語言,常用于撰寫檔案和博客文章。它簡單易學,具有清晰的結構和格式化效果,是非常適合寫作和發布內容的工具。下面我們來介紹一些 Markdown 基本語法。 1. 標題 在 Markdown 中,可以使用 # 符號表示標題,一級標題使用 ......

    uj5u.com 2023-04-30 07:21:26 more
  • 希望所有計算機學生能看到這篇c語言教程

    大部分程式員走入編程世界第一個學習的語言就是C語言。 作為一門古老的編程語言,c語言擁有48年的發展歷程。 為什么要學習 C語言? C語言是學習計算機程式設計語言的入門語言。最全面的編程面試網站 C語言是一門偏底層的語言,學好它,可以讓你更好的了解計算機。 學會了C語言,你就能學習現在任何的高級編程 ......

    uj5u.com 2023-04-30 07:21:13 more
  • 驅動開發:通過MDL映射實作多次通信

    在前幾篇文章中`LyShark`通過多種方式實作了驅動程式與應用層之間的通信,這其中就包括了通過運用`SystemBuf`緩沖區通信,運用`ReadFile`讀寫通信,運用`PIPE`管道通信,以及運用`ASYNC`反向通信,這些通信方式在應對`一收一發`模式的時候效率極高,但往往我們需要實作一次性... ......

    uj5u.com 2023-04-30 07:21:04 more
  • P6818 [PA2013]Dzia?ka 題解

    P6818 [PA2013]Dzia?ka 前言 我太菜了。。。。 對著 jiangly 大佬的題解研究了一下午研究了一下午才搞出來(淚目。 作為一個蒟蒻,我就詳細的講一下我對與本題的理解。 題意 本題的的題意描述的還是比較明了。 在二維坐標系中,輸入 $n$ 個點 $m$ 次詢問, 每次詢問,給出 ......

    uj5u.com 2023-04-30 07:15:44 more
  • Python 基于win32com客戶端實作Excel操作

    測驗環境 Python 3.6.2 代碼實作 非多執行緒場景下使用 新建并保存EXCEL import win32com.client from win32api import RGB def save_something_to_excel(result_file_path): excel_app = ......

    uj5u.com 2023-04-30 07:10:32 more
  • FFmpeg開發筆記(二)搭建Windows系統的開發環境

    由于Linux系統比較專業,個人電腦很少安裝Linux,反而大都安裝Windows系統,因此提高了FFmpeg的學習門檻,畢竟在Windows系統搭建FFmpeg的開發環境還是比較麻煩的。不過若有已經編譯好的Windows版本FFmpeg開發包,那就免去了繁瑣的Windows編譯程序,所以直接安裝已 ......

    uj5u.com 2023-04-30 07:10:24 more
  • XMake學習筆記(1):Windows(MSYS2)下MinGW-w64環境搭建和XMake安裝

    以前寫的C++基本都是C with STL,大多是面向程序的演算法題,或者比較小的專案,然后經常報各種編譯錯誤(對編譯原理不熟),經常把人搞到崩潰,搞不懂構建、鏈接之類的東西。 現在開始記錄一下XMake的學習筆記,記錄一些學習程序中踩的坑,在這篇文章,你將學習到Windows下利用MSYS2進行Mi ......

    uj5u.com 2023-04-30 07:10:13 more
  • 安裝Python

    轉載請注明 來源:http://www.eword.name/ Author:eword Email:[email protected] 安裝Python 一、查詢是否安裝了Python及安裝路徑 #查看當前Python版本 python --version Python 2.7.16 #查看當前所有 ......

    uj5u.com 2023-04-30 07:09:28 more