Jvm內部編碼採用的是Unicode編碼.javascript
常見的字符編碼集:ASCII編碼,GBK編碼,Unicode編碼html
UTF-8只是unicode的實現方式之一;java
UTF-8最大的一個特色,就是它是一種變長的編碼方式。它可使用1~4個字節表示一個符號,根據不一樣的符號而變化字節長度。 程序員
UTF-8的編碼規則很簡單,只有兩條:
1)對於單字節的符號,字節的第一位設爲0,後面7位爲這個符號的unicode碼。所以對於英語字母,UTF-8編碼和ASCII碼是相同的。
2)對於n字節的符號(n>1),第一個字節的前n位都設爲1,第n+1位設爲0,後面字節的前兩位一概設爲10。剩下的沒有說起的二進制位,所有爲這個符號的unicode碼。 web
UNICODE編碼,它採用雙字節編碼,兼容英文字符和其餘國家民族的雙字節字符編碼。 數據庫
亂碼產生的緣由: apache
每一個國家爲了統一編碼都會規定該國家/地區計算機信息交換用的字符集編碼,爲了解決本地字符信息的計算機處理,因而出現了各類本地化版本,引進LANG, Codepage 等概念。如今大部分具備國際化特徵的軟件核心字符處理都是以 Unicode 爲基礎的,在軟件運行時根據當時的 Locale/Lang/Codepage 設置肯定相應的本地字符編碼設置,並依此處理本地字符。在處理過程當中須要實現 Unicode 和本地字符集的相互轉換。 瀏覽器
同然,java內部採用的就是Unicode編碼,因此在java運行的過程當中就必然存在從Unicode編碼與相應的計算機操做系統或者瀏覽器支持的編碼格式相互轉化的過程,這個轉換的過程有一系列的步驟,若是某個步驟出現錯誤,則輸出的文字就會是亂碼。 tomcat
因此產生java亂碼的問題就在於JVM與對應的操做系統/瀏覽器進行編碼格式轉換時出現了錯誤。 服務器
其實要解決java亂碼問題的方法仍是比較簡單的,可是要究其緣由,理解背後的原理仍是須要了解
其實解決 JAVA 程序中的漢字編碼問題的方法每每很簡單,但理解其背後的緣由,定位問題,還須要瞭解現有的漢字編碼和編碼轉換。
基礎知識:
編碼:編碼是信息從一種形式或者格式轉換爲另一種形式的過程,通俗的講例 ‘陳’在計算機中怎麼表達,這個過程就是編碼;
解碼:是編碼的逆過程,將計算機中的二進制轉換爲咱們可以看懂的文字或者圖片信息;
Java編碼轉換過程:
生命週期:
1.程序員在操做系統上經過編輯器編寫程序代碼而且以.java的格式保存操做系統中,這些文件咱們稱之爲源文件。
2.經過JDK中的javac.exe編譯這些源文件造成.class類。
三、直接運行這些類或者部署在WEB容器中運行,獲得輸出結果。
主要的過程:
第一步:當咱們用編輯器編寫java源文件,程序文件在保存時會採用操做系統默認的編碼格式(通常咱們中文的操做系統採用的是GBK編碼格式)造成一個.java文件。java源文件是採用操做系統默認支持的file.encoding編碼格式保存的。下面代碼能夠查看系統的file.encoding參數值。
System.out.print(System.getProperty(「file.encoding」));
第二步:當咱們使用javac.exe編譯咱們的java文件時,JDK首先會確認它的編譯參數encoding來肯定源代碼字符集,若是咱們不指定該編譯參數,JDK首先會獲取操做系統默認的file.encoding參數,而後JDK就會把咱們編寫的java源程序從file.encoding編碼格式轉化爲JAVA內部默認的UNICODE格式放入內存中。
第三步:JDK將上面編譯好的且保存在內存中信息寫入class文件中,造成.class文件。此時.class文件是Unicode編碼的,也就是說咱們常見的.class文件中的內容不管是中文字符仍是英文字符,他們都已經轉換爲Unicode編碼格式了。
在這一步中對對JSP源文件的處理方式有點兒不一樣:WEB容器調用JSP編譯器,JSP編譯器首先會查看JSP文件是否設置了文件編碼格式,若是沒有設置則JSP編譯器會調用調用JDK採用默認的編碼方式將JSP文件轉化爲臨時的servlet類,而後再編譯爲.class文件並保持到臨時文件夾中。
第四步:運行編譯的類:在這裏會存在一下幾種狀況(亂碼發生常見的位置)
一、直接在console上運行。
二、JSP/Servlet類。
三、java類與數據庫之間。
這三種狀況每種狀況的方式都會不一樣,
這種狀況下,JVM首先會把保存在操做系統中的class文件讀入到內存中,這個時候內存中class文件編碼格式爲Unicode,而後JVM運行它。若是須要用戶輸入信息,則會採用file.encoding編碼格式對用戶輸入的信息進行編碼同時轉換爲Unicode編碼格式保存到內存中。程序運行後,將產生的結果再轉化爲file.encoding格式返回給操做系統並輸出到界面去。整個流程以下:
因爲JSP文件最終也會轉換爲servlet文件(只不過存儲的位置不一樣而已),因此這裏咱們也將JSP文件歸入其中。
當用戶請求Servlet時,WEB容器會調用它的JVM來運行Servlet。首先JVM會把servlet的class加載到內存中去,內存中的servlet代碼是Unicode編碼格式的。而後JVM在內存中運行該Servlet,在運行過程當中若是須要接受從客戶端傳遞過來的數據(如表單和URL傳遞的數據),則WEB容器會接受傳入的數據,在接收過程當中若是程序設定了傳入參數的的編碼則採用設定的編碼格式,若是沒有設置則採用默認的ISO-8859-1編碼格式,接收的數據後JVM會將這些數據進行編碼格式轉換爲Unicode而且存入到內存中。運行Servlet後產生輸出結果,同時這些輸出結果的編碼格式仍然爲Unicode。緊接着WEB容器會將產生的Unicode編碼格式的字符串直接發送置客戶端,若是程序指定了輸出時的編碼格式,則按照指定的編碼格式輸出到瀏覽器,不然採用默認的ISO-8859-1編碼格式。整個過程流程圖以下:
咱們知道java程序與數據庫的鏈接都是經過JDBC驅動程序來鏈接的,而JDBC驅動程序默認的是ISO-8859-1編碼格式的,也就是說咱們經過java程序向數據庫傳遞數據時,JDBC首先會將Unicode編碼格式的數據轉換爲ISO-8859-1的編碼格式,而後在存儲在數據庫中,即在數據庫保存數據時,默認格式爲ISO-8859-1。
常見的編碼&解碼的場景:
在java中主要有四個場景須要進行編碼解碼操做:
1:I/O操做
2:內存
3:數據庫
4:JavaWeb(網絡傳輸)
1.在前面提過亂碼問題無非就是轉碼過程當中編碼格式的不統一產生的,好比編碼時採用UTF-8,解碼採用GBK,但最根本的緣由是字符到字節或者字節到字符的轉換出問題了,而這中狀況的轉換最主要的場景就是I/O操做的時候。固然I/O操做主要包括網絡I/O(也就是javaWeb)和磁盤I/O。網絡I/O下節介紹。
網絡傳輸:
用戶想服務器發送一個HTTP請求,須要編碼的地方有url、cookie、parameter,通過編碼後服務器接受HTTP請求,解析HTTP請求,而後對url、cookie、parameter進行解碼。在服務器進行業務邏輯處理過程當中可能須要讀取數據庫、本地文件或者網絡中的其餘文件等等,這些過程都須要進行編碼解碼。當處理完成後,服務器將數據進行編碼後發送給客戶端,瀏覽器通過解碼後顯示給用戶。在這個整個過程當中涉及的編碼解碼的地方較多,其中最容易出現亂碼的位置就在於服務器與客戶端進行交互的過程。
上面整個過程能夠歸納成這樣,頁面編碼數據傳遞給服務器,服務器對得到的數據進行解碼操做,通過一番業務邏輯處理後將最終結果編碼處理後傳遞給客戶端,客戶端解碼展現給用戶。因此下面我就請求對javaweb的編碼&解碼進行闡述。
客戶端想服務器發送請求無非就經過四中狀況:
一、URL方式直接訪問。
二、頁面連接。
三、表單get提交
四、表單post提交
服務器的解碼勢必會形成很大的困擾,下面咱們將已tomcat,看tomcat是如何對URL進行解碼操做的。
解析請求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,這個方法把傳過來的 URL 的 byte[] 設置到 org.apache.coyote.Request 的相應的屬性中。這裏的 URL 仍然是 byte 格式,轉成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:
從上面的代碼可知,對URI的解碼操做是首先獲取Connector的解碼集,該配置在server.xml中
<Connector URIEncoding="utf-8" />
若是沒有定義則會採用默認編碼ISO-8859-1來解析。
對於Query String部分,咱們知道不管咱們是經過get方式仍是POST方式提交,全部的參數都是保存在Parameters,而後咱們經過request.getParameter,解碼工做就是在第一次調用getParameter方法時進行的。在getParameter方法內部它調用org.apache.catalina.connector.Request 的 parseParameters 方法,這個方法將會對傳遞的參數進行解碼。下面代碼只是parseParameters方法的一部分:
從上面代碼能夠看出對query String的解碼格式要麼採用設置的ChartSet要麼採用默認的解碼格式ISO-8859-1。注意這個設置的ChartSet是在 http Header中定義的ContentType,同時若是咱們須要改指定屬性生效,還須要進行以下配置:
咱們知道經過URL方式提交數據是很容易產生亂碼問題的,因此咱們更加傾向於經過表單形式。當用戶點擊submit提交表單時,瀏覽器會更加設定的編碼來編碼數據傳遞給服務器。經過GET方式提交的數據都是拼接在URL後面(能夠當作query String??)來提交的,因此tomcat服務器在進行解碼過程當中URIEncoding就起到做用了。tomcat服務器會根據設置的URIEncoding來進行解碼,若是沒有設置則會使用默認的ISO-8859-1來解碼。假如咱們在頁面將編碼設置爲UTF-8,而URIEncoding設置的不是或者沒有設置,那麼服務器進行解碼時就會產生亂碼。這個時候咱們通常能夠經過new String(request.getParameter("name").getBytes("iso-8859-1"),"utf-8") 的形式來獲取正確數據。
對於POST方式,它採用的編碼也是由頁面來決定的即contentType。當我經過點擊頁面的submit按鈕來提交表單時,瀏覽器首先會根據ontentType的charset編碼格式來對POST表單的參數進行編碼而後提交給服務器,在服務器端一樣也是用contentType中設置的字符集來進行解碼(這裏與get方式就不一樣了),這就是經過POST表單提交的參數通常而言都不會出現亂碼問題。固然這個字符集編碼咱們是能夠本身設定的:request.setCharacterEncoding(charset) 。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="GBK" %>
在上面代碼中有兩個地方存在編碼:pageEncoding、contentType的charset。其中pageEncoding是jsp文件自己的編碼,而contentType的charset是指服務器發送給客戶端時的內容編碼。
第一次:轉換爲.java文件;
第二次:轉換爲.class文件;
第三次:業務邏輯處理後輸出。
第一步:JVM將JSP編譯爲.jsp文件。在這個過程當中pageEncoding就起到做用了,JVM首先會獲取pageEncoding的值,若是該值存在則採用它設定的編碼來編譯,不然則採用file.encoding編碼來編譯。
第二步:JVM將.java文件轉換爲.class文件。在這個過程就與任何編碼的設置都沒有關係了,無論JSP採用了什麼樣的編碼格式都將無效。通過這個階段後.jsp文件就轉換成了統一的Unicode格式的.class文件了。
後臺通過業務邏輯處理後將產生的結果輸出到客戶端。在這個過程當中contentType的charset就發揮了功效。若是設置了charset則瀏覽器就會使用指定的編碼格式進行解碼,不然採用默認的ISO-8859-1編碼格式進行解碼處理。
流程如以下:
出現亂碼的位置:
Javascript
使用javascript編碼不給瀏覽器插手的機會,編碼以後再向服務器發送請求,而後在服務器中解碼。在掌握該方法的時候,咱們須要料及javascript編碼的三個方法:escape()、encodeURI()、encodeURIComponent()。
把URI字符串採用UTF-8編碼格式轉化成escape格式的字符串。相對於encodeURI,encodeURIComponent會更增強大,它會對那些在encodeURI()中不被編碼的符號(; / ? : @ & = + $ , #)通通會被編碼。可是encodeURIComponent只會對URL的組成部分進行個別編碼,而不用於對整個URL進行編碼。對應解碼函數方法decodeURIComponent。