chrome 新的session 設置_為什么還是由這么多人搞不懂Cookie、Session、Token?

3dc9372b7efb305c9a947c6c949d3654.gif

作者:不學無數的程序員鏈接:https://urlify.cn/Yfm6Vr

# Cookie

?洛:大爺,樓上322住的是馬冬梅家吧???大爺:馬都什么???夏洛:馬冬梅。? 7大爺:什么都沒啊???夏洛:馬冬梅啊。大爺:馬什么沒?夏洛:行,大爺你先涼快著吧。

在了解這三個概念之前我們先要了解HTTP是無狀態的Web服務器,什么是無狀態呢?就像上面夏洛特煩惱中經典的一幕對話一樣,一次對話完成后下一次對話完全不知道上一次對話發生了什么。如果在Web服務器中只是用來管理靜態文件還好說,對方是誰并不重要,把文件從磁盤中讀取出來發出去即可。但是隨著網絡的不斷發展,比如電商中的購物車只有記住了用戶的身份才能夠執行接下來的一系列動作。所以此時就需要我們無狀態的服務器記住一些事情。

那么Web服務器是如何記住一些事情呢?既然Web服務器記不住東西,那么我們就在外部想辦法記住,相當于服務器給每個客戶端都貼上了一個小紙條。上面記錄了服務器給我們返回的一些信息。然后服務器看到這張小紙條就知道我們是誰了。那么Cookie是誰產生的呢?Cookies是由服務器產生的。接下來我們描述一下Cookie產生的過程

  1. 瀏覽器第一次訪問服務端時,服務器此時肯定不知道他的身份,所以創建一個獨特的身份標識數據,格式為key=value,放入到Set-Cookie字段里,隨著響應報文發給瀏覽器。

  2. 瀏覽器看到有Set-Cookie字段以后就知道這是服務器給的身份標識,于是就保存起來,下次請求時會自動將此key=value值放入到Cookie字段中發給服務端。

  3. 服務端收到請求報文后,發現Cookie字段中有值,就能根據此值識別用戶的身份然后提供個性化的服務。

4acb4d58f77bcafe925bc6f144427f2c.png

接下來我們用代碼演示一下服務器是如何生成,我們自己搭建一個后臺服務器,這里我用的是SpringBoot搭建的,并且寫入SpringMVC的代碼如下。

@RequestMapping("/testCookies")public String cookies(HttpServletResponse response){    response.addCookie(new Cookie("testUser","xxxx"));    return "cookies";}

項目啟動以后我們輸入路徑http://localhost:8005/testCookies,然后查看發的請求。可以看到下面那張圖使我們首次訪問服務器時發送的請求,可以看到服務器返回的響應中有Set-Cookie字段。而里面的key=value值正是我們服務器中設置的值。

f430d23dbafb1b5ac1cacd2adb3314ba.png

接下來我們再次刷新這個頁面可以看到在請求體中已經設置了Cookie字段,并且將我們的值也帶過去了。這樣服務器就能夠根據Cookie中的值記住我們的信息了。

82b223604a7e481d866475fd6a044a94.png

接下來我們換一個請求呢?是不是Cookie也會帶過去呢?接下來我們輸入路徑http://localhost:8005請求。我們可以看到Cookie字段還是被帶過去了。

66ff0258fc838f5cddf1947f11925be4.png

那么瀏覽器的Cookie是存放在哪呢?如果是使用的是Chrome瀏覽器的話,那么可以按照下面步驟。

  1. 在計算機打開Chrome

  2. 在右上角,一次點擊更多圖標->設置

  3. 在底部,點擊高級

  4. 在隱私設置和安全性下方,點擊網站設置

  5. 依次點擊Cookie->查看所有Cookie和網站數據

然后可以根據域名進行搜索所管理的Cookie數據。所以是瀏覽器替你管理了Cookie的數據,如果此時你換成了Firefox等其他的瀏覽器,因為Cookie剛才是存儲在Chrome里面的,所以服務器又蒙圈了,不知道你是誰,就會給Firefox再次貼上小紙條。

ce0ec76eb481bf1aadf4756d08293039.png

# Cookie中的參數設置

說到這里,應該知道了Cookie就是服務器委托瀏覽器存儲在客戶端里的一些數據,而這些數據通常都會記錄用戶的關鍵識別信息。所以Cookie需要用一些其他的手段用來保護,防止外泄或者竊取,這些手段就是Cookie的屬性。

參數名作用后端設置方法
Max-Age設置cookie的過期時間,單位為秒cookie.setMaxAge(10)
Domain指定了Cookie所屬的域名cookie.setDomain("")
Path指定了Cookie所屬的路徑cookie.setPath("");
HttpOnly告訴瀏覽器此Cookie只能靠瀏覽器Http協議傳輸,禁止其他方式訪問cookie.setHttpOnly(true)
Secure告訴瀏覽器此Cookie只能在Https安全協議中傳輸,如果是Http則禁止傳輸cookie.setSecure(true)

下面我就簡單演示一下這幾個參數的用法及現象。

Path

設置為cookie.setPath("/testCookies"),接下來我們訪問http://localhost:8005/testCookies,我們可以看到在左邊和我們指定的路徑是一樣的,所以Cookie才在請求頭中出現,接下來我們訪問http://localhost:8005,我們發現沒有Cookie字段了,這就是Path控制的路徑。

c6318f81410701d01761e8b96c182127.png

Domain

設置為cookie.setDomain("localhost"),接下來我們訪問http://localhost:8005/testCookies我們發現下圖中左邊的是有Cookie的字段的,但是我們訪問http://172.16.42.81:8005/testCookies,看下圖的右邊可以看到沒有Cookie的字段了。這就是Domain控制的域名發送Cookie。

9e9f992dbc6b43a17a423f9c121bea38.png

接下來的幾個參數就不一一演示了,相信到這里大家應該對Cookie有一些了解了。

Session

Cookie是存儲在客戶端方,Session是存儲在服務端方,客戶端只存儲SessionId

在上面我們了解了什么是Cookie,既然瀏覽器已經通過Cookie實現了有狀態這一需求,那么為什么又來了一個Session呢?這里我們想象一下,如果將賬戶的一些信息都存入Cookie中的話,一旦信息被攔截,那么我們所有的賬戶信息都會丟失掉。所以就出現了Session,在一次會話中將重要信息保存在Session中,瀏覽器只記錄SessionId一個SessionId對應一次會話請求。

3abbe79dd6b3e294c6d7b60583932dfc.png
?@RequestMapping("/testSession")?@ResponseBody?public?String?testSession(HttpSession?session){?????session.setAttribute("testSession","this?is?my?session");?? ??return?"testSession";?}???@RequestMapping("/testGetSession")@ResponseBodypublic String testGetSession(HttpSession session){    Object testSession = session.getAttribute("testSession");    return String.valueOf(testSession);}

這里我們寫一個新的方法來測試Session是如何產生的,我們在請求參數中加上HttpSession session,然后再瀏覽器中輸入http://localhost:8005/testSession進行訪問可以看到在服務器的返回頭中在Cookie中生成了一個SessionId。然后瀏覽器記住此SessionId下次訪問時可以帶著此Id,然后就能根據此Id找到存儲在服務端的信息了。

fe8dc1725bb7ac5cf16e10e7c4e101be.png

此時我們訪問路徑http://localhost:8005/testGetSession,發現得到了我們上面存儲在Session中的信息。那么Session什么時候過期呢?

  • 客戶端:和Cookie過期一致,如果沒設置,默認是關了瀏覽器就沒了,即再打開瀏覽器的時候初次請求頭中是沒有SessionId了。

  • 服務端:服務端的過期是真的過期,即服務器端的Session存儲的數據結構多久不可用了,默認是30分鐘。

bd7f20110dbd2103fcf25d6854b522de.png

既然我們知道了Session是在服務端進行管理的,那么或許你們看到這有幾個疑問,Session是在在哪創建的?Session是存儲在什么數據結構中?接下來帶領大家一起看一下Session是如何被管理的。

Session的管理是在容器中被管理的,什么是容器呢?Tomcat、Jetty等都是容器。接下來我們拿最常用的Tomcat為例來看下Tomcat是如何管理Session的。在ManageBase的createSession是用來創建Session的。

?@Override?public?Session?createSession(String?sessionId)?{?????//首先判斷Session數量是不是到了最大值,最大Session數可以通過參數設置?????if?((maxActiveSessions?>=?0)?&&?????????????(getActiveSessions()?>=?maxActiveSessions))?{?????????rejectedSessions++;?????????throw?new?TooManyActiveSessionsException(?????????????????sm.getString("managerBase.createSession.ise"),?????????????????maxActiveSessions);????}????//?重用或者創建一個新的Session對象,請注意在Tomcat中就是StandardSession????//?它是HttpSession的具體實現類,而HttpSession是Servlet規范中定義的接口????Session?session?=?createEmptySession();????//?初始化新Session的值 ???session.setNew(true);????session.setValid(true);????session.setCreationTime(System.currentTimeMillis());????//?設置Session過期時間是30分鐘????session.setMaxInactiveInterval(getContext().getSessionTimeout()?*?60);????String?id?=?sessionId;????if?(id?==?null)?{????????id?=?generateSessionId();????}????session.setId(id);//?這里會將Session添加到ConcurrentHashMap中????sessionCounter++;????//將創建時間添加到LinkedList中,并且把最先添加的時間移除????//主要還是方便清理過期Session????SessionTiming?timing?=?new?SessionTiming(session.getCreationTime(),?0);????synchronized?(sessionCreationTiming)?{        sessionCreationTiming.add(timing);        sessionCreationTiming.poll();    }    return session}

到此我們明白了Session是如何創建出來的,創建出來后Session會被保存到一個ConcurrentHashMap中。可以看StandardSession類。

protected?Map<String,?Session>?sessions?=?new?ConcurrentHashMap<>();

到這里大家應該對Session有簡單的了解了。

Session是存儲在Tomcat的容器中,所以如果后端機器是多臺的話,因此多個機器間是無法共享Session的,此時可以使用Spring提供的分布式Session的解決方案,是將Session放在了Redis中。

Token

Session是將要驗證的信息存儲在服務端,并以SessionId和數據進行對應,SessionId由客戶端存儲,在請求時將SessionId也帶過去,因此實現了狀態的對應。而Token是在服務端將用戶信息經過Base64Url編碼過后傳給在客戶端,每次用戶請求的時候都會帶上這一段信息,因此服務端拿到此信息進行解密后就知道此用戶是誰了,這個方法叫做JWT(Json Web Token)。

dd4969c3b0156889cd4263ae2422f26a.png

Token相比較于Session的優點在于,當后端系統有多臺時,由于是客戶端訪問時直接帶著數據,因此無需做共享數據的操作。

# Token的優點

  1. 簡潔:可以通過URL,POST參數或者是在HTTP頭參數發送,因為數據量小,傳輸速度也很快

  2. 自包含:由于串包含了用戶所需要的信息,避免了多次查詢數據庫

  3. 因為Token是以Json的形式保存在客戶端的,所以JWT是跨語言的

  4. 不需要在服務端保存會話信息,特別適用于分布式微服務

# JWT的結構

實際的JWT大概長下面的這樣,它是一個很長的字符串,中間用.分割成三部分

8db5701ea4c7c370ba6cb7c038328a70.png

JWT是有三部分組成的

Header

是一個Json對象,描述JWT的元數據,通常是下面這樣子的

{  "alg": "HS256",  "typ": "JWT"}

上面代碼中,alg屬性表示簽名的算法(algorithm),默認是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的類型(type),JWT 令牌統一寫為JWT。最后,將上面的 JSON 對象使用 Base64URL 算法轉成字符串。

JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。Base64 有三個字符+、/和=,在 URL 里面有特殊含義,所以要被替換掉:=被省略、+替換成-,/替換成_ 。這就是 Base64URL 算法。

Payload

Payload部分也是一個Json對象,用來存放實際需要傳輸的數據,JWT官方規定了下面幾個官方的字段供選用。

  • iss (issuer):簽發人

  • exp (expiration time):過期時間

  • sub (subject):主題

  • aud (audience):受眾

  • nbf (Not Before):生效時間

  • iat (Issued At):簽發時間

  • jti (JWT ID):編號

當然除了官方提供的這幾個字段我們也能夠自己定義私有字段,下面就是一個例子

{  "name": "xiaoMing",  "age": 14}

默認情況下JWT是不加密的,任何人只要在網上進行Base64解碼就可以讀到信息,所以一般不要將秘密信息放在這個部分。這個Json對象也要用Base64URL算法轉成字符串

Signature

Signature部分是對前面的兩部分的數據進行簽名,防止數據篡改。

首先需要定義一個秘鑰,這個秘鑰只有服務器才知道,不能泄露給用戶,然后使用Header中指定的簽名算法(默認情況是HMAC SHA256),算出簽名以后將Header、Payload、Signature三部分拼成一個字符串,每個部分用.分割開來,就可以返給用戶了。

HS256可以使用單個密鑰為給定的數據樣本創建簽名。當消息與簽名一起傳輸時,接收方可以使用相同的密鑰來驗證簽名是否與消息匹配。

a5a73d77c0b6c4b300cc0ac16c679338.png

# Java中如何使用Token

上面我們介紹了關于JWT的一些概念,接下來如何使用呢?首先在項目中引入Jar包

compile('io.jsonwebtoken:jjwt:0.9.0')

然后編碼如下

 // 簽名算法 ,將對token進行簽名 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 通過秘鑰簽名JWT byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary("SECRET"); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); Map<String,Object> claimsMap = new HashMap<>(); claimsMap.put("name","xiaoMing"); claimsMap.put("age",14); JwtBuilder builderWithSercet = Jwts.builder()        .setSubject("subject")        .setIssuer("issuer")        .addClaims(claimsMap)        .signWith(signatureAlgorithm, signingKey);System.out.printf(builderWithSercet.compact());

發現輸出的Token如下

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJzdWJqZWN0IiwiaXNzIjoiaXNzdWVyIiwibmFtZSI6InhpYW9NaW5nIiwiYWdlIjoxNH0.3KOWQ-oYvBSzslW5vgB1D-JpCwS-HkWGyWdXCP5l3Ko

此時在網上隨便找個Base64解碼的網站就能將信息解碼出來

b35089177a79dac07993e0df5eea6692.png

# 總結

相信大家看到這應該對Cookie、Session、Token有一定的了解了,接下來再回顧一下重要的知識點

  • Cookie是存儲在客戶端的

  • Session是存儲在服務端的,可以理解為一個狀態列表。擁有一個唯一會話標識SessionId。可以根據SessionId在服務端查詢到存儲的信息。

  • Session會引發一個問題,即后端多臺機器時Session共享的問題,解決方案可以使用Spring提供的框架。

  • Token類似一個令牌,無狀態的,服務端所需的信息被Base64編碼后放到Token中,服務器可以直接解碼出其中的數據。

?往期推薦?

?

  • SO面試題07:使用equlas和hashcode方法時,我們需要考慮那些問題?
  • TIOBE 3 月榜單:Delphi也許不屬于這個時代了!!!
  • 別再說自己沒時間,一文講透操作系統

fcdc0ad4b6021d40d85a467b04ddfabc.png

585b190c57cabc1dc497068c9bb3dafc.gif?

點擊

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

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

相關文章

mysql for update用處_for update的作用和用法

一、for update定義for update是一種行級鎖&#xff0c;又叫排它鎖&#xff0c;一旦用戶對某個行施加了行級加鎖&#xff0c;則該用戶可以查詢也可以更新被加鎖的數據行&#xff0c;其它用戶只能查詢但不能更新被加鎖的數據行&#xff0e;如果其它用戶想更新該表中的數據行&…

python大數據零基礎_零基礎學習大數據人工智能,學習路線篇!

大數據處理技術怎么學習呢?首先我們要學習Python語言和Linux操作系統&#xff0c;這兩個是學習大數據的基礎&#xff0c;學習的順序不分前后。Python&#xff1a;Python 的排名從去年開始就借助人工智能持續上升&#xff0c;現在它已經成為了語言排行第一名。從學習難易度來看…

python刪除文件和linux刪除文件區別_使用Python批量刪除文件列表

使用Python批量刪除文件列表環境&#xff1a;已知要刪除的文件列表&#xff0c;即確定哪些文件要刪除。代碼如下&#xff1a;#!/usr/bin/env python#codingutf-8#目的&#xff1a;本程序主要為刪除給定的文件列表import osimport shutil#引入模塊&#xff0c;os為包含普遍的操作…

華為python有必要學嗎_【華為云技術分享】這個 Python 庫有必要好好學學

這里看一個最基本的例子&#xff0c;這里給到一個 User 的 Class 定義&#xff0c;再給到一個 data 數據&#xff0c;像這樣&#xff1a;1 class User(object):2 def __init__(self, name, age):3 self.name name4 self.age age56 data [{7 name: Germey,8 age: 239 }, {10 nam…

python求平行四邊形面積_python 已知平行四邊形三個點,求第四個點的案例

我就廢話不多說了&#xff0c;大家還是直接看代碼吧&#xff01;import numpy as np#已知平行四邊形三個點&#xff0c;求第四個點#計算兩點之間的距離def CalcEuclideanDistance(point1,point2):vec1 np.array(point1)vec2 np.array(point2)distance np.linalg.norm(vec1 -…

eview面板數據之混合回歸模型_【視頻教程】Eviews系列25|面板數據回歸分析之Hausman檢驗及本章常見問題解答...

點擊上方關注我們!本期我們學習Eviews統計建模最后一部分--面板數據回歸分析Hausman檢驗及本章常見問題解答。實操&#xff1a;Hausman檢驗判斷是固定效應模型還是隨機效應模型上期我們講到模型判斷若選擇模型2,需進一步通過Hausman檢驗判斷固定效應還是隨機效應&#xff0c;接…

python打出由邊框包圍的_python opencv 圖像邊框(填充)添加及圖像混合的實現方法(末尾實現類似幻燈片漸變的效果)...

圖像邊框的實現圖像邊框設計的主要函數cv.copyMakeBorder()——實現邊框填充主要參數如下&#xff1a;參數一&#xff1a;源圖像——如&#xff1a;讀取的img參數二——參數五分別是&#xff1a;上下左右邊的寬度——單位&#xff1a;像素參數六&#xff1a;邊框類型&#xff1…

mysql5.7.21備份腳本_Shell腳本使用匯總整理——mysql數據庫5.7.8以后備份腳本

#!/bin/sh#db_backups_conf.txt文件路徑db_backups_conf"/wocloud/shell/db_backups_conf.txt"#判斷文件是否存在if [ -f "${db_backups_conf}" ];thenecho $(date %Y-%m-%d %H:%M:%S)" 數據庫配置信息文件存在&#xff0c;開始進行數據備份"#獲…

python嵌套列表操作_python基礎(list列表的操作,公共方法,列表嵌套,元祖)...

list 列表索引&#xff0c;切片與str相同。列表的操作(增刪改查)1&#xff0c;增加.append()  追加&#xff0c;增加到最后li [alex,wusir,egon,女神,taibai]li.append(ppp)print(li).insert()  中間插入&#xff0c;通過索引li [alex,wusir,egon,女神,taibai]li.insert(…

mybatis mysql selectkey_Mybatis示例之SelectKey的應用

SelectKey在Mybatis中是為了解決Insert數據時不支持主鍵自動生成的問題&#xff0c;他可以很隨意的設置生成主鍵的方式。不管SelectKey有多好&#xff0c;盡量不要遇到這種情況吧&#xff0c;畢竟很麻煩。SelectKey需要注意order屬性&#xff0c;像Mysql一類支持自動增長類型的…

python程序設計上機實踐第三章答案_20192419 實驗三《Python程序設計》實驗報告

學號 2019-2020-2 《Python程序設計》實驗3報告課程&#xff1a;《Python程序設計》班級&#xff1a;1924姓名&#xff1a; 萬騰陽學號&#xff1a;20192419實驗教師&#xff1a;王志強實驗日期&#xff1a;2020年5月16日必修/選修&#xff1a; 公選課1.實驗內容創建服務端和客…

完成數獨的算法 python_python實現數獨算法實例

本文實例講述了python實現數獨算法的方法。分享給大家供大家參考。具體如下&#xff1a;# -*- coding: utf-8 -*-Created on 2012-10-5author: Administratorfrom collections import defaultdictimport itertoolsa [[ 0, 7, 0, 0, 0, 0, 0, 0, 0], #0[ 5, 0, 3, 0, 0, 6, 0, …

python讀取多個文件csv_Python:讀取多個文本文件并寫入相應的csv文件

我在別處找不到這個問題的答案&#xff0c;所以我將繼續把它貼在這里&#xff1a;我有一個Python腳本&#xff0c;它將讀取文本文件的內容&#xff0c;將其內容拆分為單詞&#xff0c;然后輸出一個CSV文件&#xff0c;該文件將文本縮減為單詞頻率列表。(最后&#xff0c;我將插…

java treetable_在Swing中創建TreeTable | 學步園

TreeTable是Tree和Table的一個結合&#xff0d;就是一個即能夠展開和收起行&#xff0c;同時也能夠顯示多個列的組件。在Swing的標準包里沒有一個叫做JtreeTable的組件&#xff0c;但是我們很容易通過把Jtree做成Jtable的渲染器來創建一個這樣的組件。這篇文章就是關于如何使用…

python爬去微博十大流行語_用python重新定義【2019十大網絡流行語】-后臺/架構/數據庫-敏捷大拇指-一個敢保留真話的IT精英社區...

↑關注 置頂 ~ 有趣的不像個技術號52568040f9313098ffa367d9d9d21437.jpg (5.73 KB, 下載次數: 0)2019-12-10 04:43 上傳3f4d5fe0016d011a7a68af763314befd.jpg (1.06 KB, 下載次數: 0)2019-12-10 04:43 上傳“文明互鑒真硬核&#xff0c;融梗檸檬誰覺得。霸凌第一九九六&…

java中怎么獲取配置文件的值_java如何獲取配置文件的值

轉&#xff1a;原創 編碼小王子 發布于2018-10-11 18:07:52 閱讀數 2722 收藏展開java大型項目中都會很多系統常量,比如說數據庫的賬號和密碼,以及各種token值等,都需要統一的管理,如果零落的散布到各個類等具體的代碼中的話,在后期管理上將是一場災難,所有需要對這些變量進行統…

python實現隊列_用Python實現的數據結構與算法:隊列

一、概述隊列(Queue)是一種先進先出(FIFO)的線性數據結構&#xff0c;插入操作在隊尾(rear)進行&#xff0c;刪除操作在隊首(front)進行。二、ADT隊列ADT(抽象數據類型)一般提供以下接口&#xff1a;Queue() 創建隊列enqueue(item) 向隊尾插入項dequeue() 返回隊首的項&#xf…

java 監聽窗口是否改變_JAVA項目監聽文件是否發生變化

一.spring容器都初始化完成之后做操作packagecom.bijian.study.listener;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.ApplicationListener;importorg.springframework.context.event.ContextRefreshedEvent;importorg.s…

笨辦法學python3 pdf 腳本之家_解決python3輸入的坑——input()

如下所示&#xff1a;a,b,c,d input()很簡單的代碼&#xff0c;如果輸入為1 -1 -2 3結果會報錯&#xff0c;原因在于input函數會將你的輸入作為python腳本運行&#xff0c;那么輸入就變成了1-1 -2 3&#xff0c;即0 -2 3結果當然是錯誤的了&#xff0c;解決辦法就是將輸入用引…

java 數組寫法_java書寫、數據類型、數組定義

這里只記錄java與php、javascript不同的地方&#xff0c;相同的地方就不贅述了。1.java文件源碼為以.java為后綴的文件&#xff0c;字節碼文件是以.class為后綴的文件。2.寫好一個java源碼之后&#xff0c;cmd進入源碼文件盤符&#xff0c;用命令 javac helloworld.java將源碼轉…