一文帶你了解js數據儲存及深複製(深拷貝)與淺複製(淺拷貝)_租車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

背景

在日常開發中,偶爾會遇到需要複製對象的情況,需要進行對象的複製。

由於現在流行標題黨,所以,一文帶你了解js數據儲存及深複製(深拷貝)與淺複製(淺拷貝)

理解

首先就需要理解 js 中的數據類型了
js 數據類型包含

  1. 基礎類型:StringNumbernullundefinedBoolean以及ES6引入的Symboles10中的BigInt
  2. 引用類型:Object

由於 js 對變量的儲存是棧內存堆內存完成的。

  • 基礎類型將數據保存在棧內存
  • 引用類型將數據保存在堆內存

由於 js 在數據讀取和寫入的時候,對基礎類型是直接讀寫棧內存中的數據,引用類型是將一個內存地址保存在棧內存中,讀寫都是修改棧內存中指向堆內存的地址

以如下代碼為例

let obj = {
  a:1,
  arr:[1,3,5,7,9],
  b:2,
  c:{
    num:100
  }
}
let num = 10

在內存中的表現為

我們聲明個obj1

let obj1 = obj;
console.log(obj1 == obj);//true

因為這個賦值,把內存變成了這樣

然後,內存中只是給js棧內存新增了一個指向堆內存的地址而已,這種就叫做淺複製。因為如圖可以看到,如果我們修改obj.a的話,實際修改的是堆內存0x88888888中的變量a,由於obj1也指向這個地址,所以obj1.a也被修改了

深複製是指,不單單複製引用地址,連堆內存都複製一遍,使objobj1不指向同一個地址。

代碼

分開來看深複製淺複製

淺複製

由上述圖可知,淺複製只是複製了堆內存的引用地址,通常在業務需求中出現的淺複製是指複製引用對象的第一層,也就是,基本類型複製新值,引用類型複製引用地址

淺複製可以使用的方案有循環賦值擴展運算符object.assign(),

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

let obj = {
  a:1,
  arr:[1,3,5,7,9],
  b:2,
  c:{
    num:100
  }
}
function clone1(obj){ // 使用循環賦值
  let b = {};
  for(let key in obj){
    b[key] = obj[key]
  }
  return b
}
function clone2(obj){ // 使用擴展運算符
  let b = {
    ...obj
  };
  return b
}
function clone3(obj){ // 使用object.assign()
  let b = {};
  Object.assign(b,obj)
  return b
}
let obj1 = clone1(obj);
let obj2 = clone2(obj);
let obj3 = clone3(obj);

console.log(obj1 === obj); //false 代表複製成功了
console.log(obj2 === obj); //false 代表複製成功了
console.log(obj3 === obj); //false 代表複製成功了
console.log('obj0.c.num修改前',obj.c.num); //100
console.log('obj1.c.num修改前',obj1.c.num); //100
console.log('obj2.c.num修改前',obj2.c.num); //100
console.log('obj3.c.num修改前',obj3.c.num); //100
obj0.c.num = 555;
console.log('obj0.c.num修改后',obj.c.num); //555
console.log('obj1.c.num修改后',obj1.c.num); //555
console.log('obj2.c.num修改后',obj2.c.num); //555
console.log('obj3.c.num修改后',obj3.c.num); //555

由於是淺複製,所以引用類型只是複製了內存地址,修改其中一個對象的子屬性后,引用這個地址的值都會被修改。

淺克隆圖解如下

深複製

由於淺複製只是複製第一層,為了解決引用類型的複製,需要使用深複製來完成對象的複製,基本類型複製新值,引用類型開闢新的堆內存

深複製可以使用的方案有JSON.parse(JSON.stringify(obj))循環賦值

JSON.parse(JSON.stringify(obj))

let obj = {
  a:1,
  arr:[1,3,5,7,9],
  c:{
    num:100
  },
  fn:function(){
     console.log(1)
  },
  date:new Date(),
  reg:/\.*/g
}
function clone1(obj){ // 使用JSON.parse(JSON.stringify(obj))
  return JSON.parse(JSON.stringify(obj))
}
let obj1 = clone1(obj);
console.log(obj === obj1); //false 代表複製成功了
obj.c.num = 555;

console.log(obj.c.num,obj1.c.num) // 555,100

看起來是複製成功了!!~地址也變了,修改obj,obj1的引用地址不會跟着變化。

但是我們來console一下obj以及obj1

console.log(obj)
console.log(obj1)

似乎發現了離奇的事情,只有obj.a以及obj.c正確的複製了,日期類型方法正則表達式均沒有複製成功,發生了一些奇怪的事情

循環賦值 deepClone

那麼為了解決這種事情,就需要寫一個deepClone方法來完成深複製了,參考了許多開源庫的寫法,將所有的複製項單獨拆出,方便未來對特殊類型進行擴展,也防止不同功能間的變量互相干擾

 //既然是深複製,一定要傳入一個object,再return 一個新的 Object
function deepClone(obj){
    let newObj;
    if(obj instanceof Array){ // 數組的話,要new一個數組
      newObj = []
    }else if(obj instanceof Object){  // 對象的話,要new一個對象
      newObj = {}
    }
    if(obj === null) {
      return cloneNull(obj)
    }
    if(typeof obj=='function'){
        return cloneFunction(obj)
    }
    if(typeof obj!='object') {
        return cloneOther(obj)
    }
    if(obj instanceof RegExp) {
        return cloneRegExp(obj)
    }
    if(obj instanceof Date){
        return cloneDate(obj)
    }
    if(obj instanceof Array){
        for(let index in obj){
            newObj[index] = deepClone(obj[index]); // 對數組子項進行複製
        }
    }
    if(obj instanceof Object){
        for(let key in obj){
            newObj[key] = deepClone(obj[key]); // 對對象子項進行複製
        }
    }
    return newObj;
}
function cloneNull(obj){ // 複製NULL
  return obj
}
function cloneFunction(obj){ // 複製方法,
  // 複製一個新方法,將原方法轉成字符串,並new一個新的function
  return new Function('return '+obj.toString())()
}
function cloneOther(obj){ // 複製非對象的數據
  return obj
}
function cloneRegExp(obj){ // 複製正則對象
  return new RegExp(obj)
}
function cloneDate(obj){ // 複製日期對象
  return new Date(obj)
}

這樣一個基本上滿足功能的深複製就完成了。先測試一下

let obj = {
  a:1,
  arr:[1,3,5,7,9],
  c:{
    num:100
  },
  fn:function(){
     console.log(1)
  },
  date:new Date(),
  reg:/\.*/g
}

let obj1 = deepClone(obj);
console.log(obj.c === obj1.c); // false  代表複製成功
console.log(obj.fn === obj1.fn);// false  代表複製成功 
console.log(obj.date === obj1.date);// false  代表複製成功
console.log(obj.reg === obj1.reg);// false  代表複製成功

console一下

console.log(obj)
console.log(obj1)

這樣,就完成了deepClone深複製方法

經過深複製后,圖解如下

優化 deepClone

上述代碼還有優化空間,參考了lodash庫,在進行 new 對象時,可以使用 constructor構造函數 來進行創建新的實例,這樣

  1. 可以不用判斷遞歸中,是數組還是對象
  2. 如果深複製的某一項是某個原型的實例,深複製完成后,依然是該原型的實例
function deepClone(obj){
    let newObj = new obj.constructor;
    if(obj === null) {
      return cloneNull(obj)
    }
    if(typeof obj=='function'){
        return cloneFunction(obj)
    }
    if(typeof obj!='object') {
        return cloneOther(obj)
    }
    if(obj instanceof RegExp) {
        return cloneRegExp(obj)
    }
    if(obj instanceof Date){
        return cloneDate(obj)
    }
    if(obj instanceof Array){
        for(let index in obj){
            newObj[index] = deepClone(obj[index]); // 對數組子項進行複製
        }
    }
    if(obj instanceof Object){
        for(let key in obj){
            newObj[key] = deepClone(obj[key]); // 對對象子項進行複製
        }
    }
    return newObj;
}
function cloneNull(obj){ // 複製NULL
  return obj
}
function cloneFunction(obj){ // 複製方法,
  // 複製一個新方法,將原方法轉成字符串,並new一個新的function
  return new Function('return '+obj.toString())()
}
function cloneOther(obj){ // 複製非對象的數據
  return obj
}
function cloneRegExp(obj){ // 複製正則對象
  return new RegExp(obj)
}
function cloneDate(obj){ // 複製日期對象
  return new Date(obj)
}

最終版本 deepClone

然後可以有一個合併版本的,比較節省代碼,將下方區分開的複製方法,合併到deepClone中,可以極大地減少代碼體積

function deepClone(obj){ //
    let newObj = new obj.constructor;
    if(obj === null) return obj
    if(typeof obj=='function') return new Function('return '+obj.toString())()
    if(typeof obj!='object') return obj
    if(obj instanceof RegExp) return new RegExp(obj)
    if(obj instanceof Date) return new Date(obj)
    // 運行到這裏,基本上只存在數組和對象兩種類型了
    for(let index in obj){
        newObj[index] = deepClone(obj[index]); // 對子項進行遞歸複製
    }
    return newObj;
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

“操控之王”大7座SUV國產後要死磕漢蘭達?_租車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

5T渦輪增壓發動機,最大馬力253匹、峰值扭矩高達420牛米。傳動系統搭載的是一款六速手自一體變速箱。CX-9是馬自達率先針對北美市場研發的7座SUV車型,所以在北美已經率先上市,據悉售價為31000美金,約合人民幣20萬元左右,隨着全球性的SUV熱潮不斷升溫,作為汽車消費大國的中國,不出意外也會分到一杯羹。

眾所周知馬自達旗下的車型幾乎都以優秀的操控性見長,旗下昂克賽拉、阿特茲兩台轎車在同級橫比當中操控性都是非常出眾的典型,而今年所推出的轎跑型SUV——CX-4更是將SUV的操控性提高了一個層次。而根據有關消息,今年早些時候在北美率先上市的全新一代馬自達7座SUV CX-9,將在明年正式由一汽馬自達進行國產。

馬自達近年來的“魂動”設計理念已經深入人心,無論是昂克賽拉、阿特茲、還是CX-4,在很多人眼裡都是“騷的無與倫比”。

全新一代馬自達CX-9依然延續了“魂動”的設計,從已經曝光的圖片我們可以看到,CX-9的外觀將家族化的靈動感進行了一定程度上的柔化,讓視覺侵略性極強的外觀效果和SUV的威武感融合得比較恰當。

全新CX-9採用了七座的作為布局,軸距達到了2930mm,小編猜測不出意外的話,

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

國產後的馬自達CX-9軸距會將進一步加長,預計屆時國產後的CX-9車內乘坐空間會更加充裕。

值得注意的是,隨着國際大流下的渦輪增壓發動機大肆普及,在豐田、本田這兩個日系品牌紛紛推出渦輪增壓車型,在動力系統方面頗有一套造詣的馬自達也坐不住了。全新馬自達CX-9搭載了一款創馳藍天的2.5T渦輪增壓發動機,最大馬力253匹、峰值扭矩高達420牛米。傳動系統搭載的是一款六速手自一體變速箱。

CX-9是馬自達率先針對北美市場研發的7座SUV車型,所以在北美已經率先上市,據悉售價為31000美金,約合人民幣20萬元左右,隨着全球性的SUV熱潮不斷升溫,作為汽車消費大國的中國,不出意外也會分到一杯羹。

全文總結:根據小編的猜測,作為馬自達旗下定位高端化的SUV車型,進入國產以後售價應該會向現在的漢蘭達和福特銳界看齊,而2.5T的馬自達CX-9將會是以頂配車型進行銷售,較低的配置應該還會搭載現在阿特茲的2.0L自然吸氣發動機,而2.5L的自然吸氣發動機或許將不會在CX-9車型中搭載。

由於目前官方對於CX-9國產的消息透露的不多,具體投產和上市時間暫時不得而知,作為一款較為值得關注的7座SUV車型,我們也會持續對其保持高度關注並進行消息更新。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

YouTube 的 PWA 應用終於在 Windows 與 ChromeOS 上推出了(內含一秒安裝方式)_潭子電動車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

一直以來,Windows 平台上都沒有可以直接安裝到電腦上的 YouTube 應用程式,所以每次當你想要觀賞影片時,都必須先打開瀏覽器,然後再前往 YouTube 網站上,不像在手機上那麼直接點擊就能前往。不過,現在 YouTube 的 PWA 應用終於登陸 Windows 10 與 ChromeOS 上,以後不用那麼麻煩囉!

YouTube 的 PWA 應用終於在 Windows 與 ChromeOS 上推出了

明明 YouTube 在全球擁有那麼多用戶,但卻遲遲沒有在電腦版本上推出像手機那般一點就開的應用程式,大家只能從瀏覽器前往網頁版本,隨著 PWA(漸進式網頁應用程式)的興起,這件小事日前已經迎刃而解。只要你是 Windows 10 或 ChromeOS 作業系統,現在都能安裝 YouTube 的 PWA。

怎麼安裝呢?首先打開你的 Chrome 或 Microsoft Edge 瀏覽器,然後前往 YouTube 網站。接著點選瀏覽器網址列旁邊的「+」號即可進行安裝。由於 Chrome 與 Edge 瀏覽器上會看到的圖示不太一樣,下面筆者將兩者都截圖下來,這樣大家比較好辨認。接著同意安裝後就馬上完成,完全不需等候。

安裝好後,你可以在電腦桌面上看到開啟 YouTube PWA 應用的捷徑,同時在開始功能表中也可以看到 YouTube PWA 的身影,未來想要看 YouTube 影片,你可以直接點擊圖示就能快速喚出。

YouTube PWA 應用的好處是它會變成獨立應用般運作(如下圖),不需要另外開瀏覽器,特別是當你瀏覽器分頁或視窗開了很多的時候,從一大堆網站裡面撈出 YouTube 也並不是那麼容易的事情,非常難找。其他包含功能、介面,其實就與網頁版差不多,沒有什麼需要重新適應的問題。

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

如果某日你想要移除它,就像移除其他 Windows 系統中的應用程式與軟體一樣,從設定功能中去執行就好,不會有一安裝就像被咬住不放的感覺。目前 YouTube PWA 需在網路連線狀態下運作,Google 一直在鼓勵各家 PWA 的開發者於應用中增加更多離線功能,這表示未來我們還可以在 YouTube PWA 中看到一些新的離線驚喜,筆者個人是希望可以有影片下載,這樣就算在網路不方便連接的地方也可以輕鬆看啦!

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

我終於搞清了啥是 HTTPS 了_台中搬家

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

引言

最近上海連續下了一周雨,溫度一夜之間回到解放前,穿夏裝的我被凍得瑟瑟發抖,躲在家裡哪也不想去。

在家百無聊賴的刷着網頁,看到公眾號後台的留言,有同學問我 HTTP 和 HTTPS 有啥區別?

這還用問,當然是 HTTPS 要比 HTTP 更加的安全啊,沒看到後面帶着個 S 呢么,帶着 S 就這麼 NB 。

然後同學的下一個問題把我問懵逼了,為啥帶 S 的更安全呢?能詳細的講講么。

我跟你講嗷,不是我吹,我這麼多年。。。。。。

就沒見過你這麼刨根究底的同學,老問這種我也不是很清楚的問題。

雖然這個問題問的我老臉一紅,但是我有一種不要臉的精神 「我不會,但是我可以學」 。

HTTP

首先先來了解下 HTTP :

HTTP 協議全稱為:Hyper Text Transfer Protocol ,翻譯過來就是超文本傳輸協議,請不要質疑這個翻譯,我專門用百度翻譯翻了一下。

TCP/IP 四層模型應該都知道的,有數據鏈路層,網絡層,傳輸層和應用層:

而 HTTP 協議就是位於 TCP/IP 四層模型的應用層上。

這裏很多人都會混淆 TCP 和 HTTP ,實際上 HTTP 是基於 TCP 連接基礎上的。

簡單的說, TCP 就是單純建立連接,不涉及任何我們需要請求的實際數據,簡單的傳輸。而 HTTP 是用來收發數據,即實際應用上來的。

HTTP 協議通過請求和響應在客戶端和服務端之間收發數據,進行通信:

HTTPS

HTTP 協議看起來好像沒啥問題,唯一的問題就是不夠安全,因為 HTTP 協議的傳輸方式完全是由明文傳輸的,不做任何加密,這就讓一些不懷好意的人有了可乘之機。

這種傳輸方式誘發了一種經典的攻擊方式:中間人攻擊。

對於這種情況,最簡單的我們可以使用加密方案,比如使用 AES 加密,服務端和客戶端先約定一個隨機生成的密鑰 key ,後續的通信中,所有的信息都使用這個密鑰進行 AES 加密:

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

這樣雖然後面的通信過程安全了,但是我們在第一發送 AES 密鑰的時候還是存在被中間人攔截的風險,一旦中間人攔截到我們的密鑰,可用對密鑰進行更換或者直接解密請求內容:

這時我們可以使用不對稱加密,來專門對密鑰的傳輸做一次額外的保護。

不對稱加密會有兩個密鑰,一個是公鑰,一個是私鑰。明文可以使用公鑰加密私鑰解密,也可以使用私鑰加密公鑰解密。

現在比較通用的非對稱加密算法有 RSA 。

看到這裏的同學一定在奇怪,既然都使用了不對稱加密,為啥只對 AES 的密鑰做不對稱加密,好像有多此一舉,完全可以對後續所有的通信信息全都使用不對稱加密。

因為不對稱加密相比較對稱加密性能上存在明顯的劣勢,可能你覺得在一個請求中多消耗幾 ms 或者幾 ns 無所謂,但是請求到達服務端是要進行解密,每個請求都多消耗幾 ms 累計起來還是非常可觀的。

上面這個方案看起來已經很安全了,中間人即使攔截到我們的公鑰,由於不知道我們的私鑰貌似也沒辦法解密。

實際上中間人完全不需要解密我們的信息,他可以生成一對新的公私鑰發送給客戶端進行攻擊,後續客戶端的通信中間人使用自己創造的私鑰進行解密,然後通過服務端生成的公鑰進行加密返回給服務端:

CA 證書

上面的問題我們僅通過客戶端和服務端已經沒辦法了,這時候需要引入新的第三方機構,一個頒發 CA 證書的機構。

常見的第三方 CA 機構有:Symantec(賽門鐵克),Comodo(科莫多),GeoTrust(環度網信),GoDaddy,Thawte,daoRapidSSL 等等。

在中間人攻擊中,我們遇到的問題不是加密算法不夠神奇,不是密鑰方式不夠嚴謹,而是我們沒有辦法向我們的客戶端表明我們給他的公鑰是我們的,是不是很像我沒辦法證明我是我的問題。

所以第三方機構應運而生,第三方機構只做一件事情,將服務端的公鑰刻上了我們的名字(CA 證書),客戶端接收到公鑰之後,只需要來第三方機構這裏查詢,就能知道這個公鑰是不是真的服務器,然後再將自己生成的 AES 密鑰使用 CA 證書中解密得到的公鑰進行加密后發送給服務端。

最後服務端使用私鑰解密得到 AES 密鑰,就可以愉快的和客戶端進行通信了。

最後的最後,CA 機構驗證不是每次都要去 CA 機構查詢。這樣做太傻了而且太耗時,尤其是很多 CA 機構的服務都在海外,這樣一來一去消耗的時間太多了。

CA 機構高明的地方就在於,我們去找它註冊公鑰,它會使用另一個來註冊的公司的私鑰對我們的公鑰加密,得到一個我們的公鑰的指紋(全球唯一),然後將這家公司的公鑰信息(其實也是證書)和我們的公鑰以及我們公鑰的指紋打包成一個證書。

當我們使用 HTTPS 將證書下發給客戶端校驗時,客戶端(比如瀏覽器)從證書中看到了上級證書的信息,恰巧這個證書就在瀏覽器(或者本機)中,已經被驗證過是合法的,瀏覽器只要使用這個證書中的公鑰將我們的公鑰指紋進行解密,然後比對我們的公鑰信息就知道我們也是的合法的。因為假證書中的公鑰簽名不可能被合法的上級證書中公鑰解密 。

這段稍微有點繞,慢慢看多看幾次就理解了。

參考

https://www.jianshu.com/p/691b8ba3a70f

https://blog.csdn.net/u010144805/article/details/80803059

https://blog.csdn.net/caofengtao1314/article/details/87912078

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

游泳池誤倒不明液體冒白煙 19名幼童急送醫_貨運

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

中和景新國小委外由游泳池晚間發生誤混液體,造成多名幼童不適,環保署人員趕抵檢查現地。(吳家詮翻攝)

新北景新國小外包游泳池,晚間發生誤倒入不明液體,早成多名在場幼童身體不適。(圖摘自Google Map)

新北市中和區景新國小旁一處私人游泳池,晚間現場疑似救生員誤將不明液體倒入泳池產生白煙,造成現場場19名小朋友噁心嘔吐,送新北雙和醫院急救。

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

 

消防局初步了解,現場為伊藤萬游泳晚上6時50分至8時為學校的游泳課程,因工作人員誤將強酸(氯)及強鹼(消毒水)加在一起,故產生化學作用冒煙,引起上課學生身體不適,小朋友分屬不同學校參加游泳課程,目前送醫計有18名,有1名為家長自行送往醫院,消防局救護車載運10女8男(18名),均送往雙和醫院。

 

雙和醫院稍晚表示,另有5名泳池的教練也因身體不適,陸續到院檢查治療。

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

XStream學習手冊_租車

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

一、前言

1、XStream官網

    http://x-stream.github.io

2、XStream是什麼

    XStream是一個簡單的基於Java的類庫,用來將Java對象序列化成XML(JSON)或反序列化為對象(即:可以輕易的將Java對象和XML文檔相互轉換)

3、XSteam能幹什麼

    XStream在運行時使用Java反射機制對要進行序列化的對象樹的結構進行探索,並不需要對對象作出修改。XStream可以序列化內部字段,包括私private和final字段,並且支持非公開類以及內部類。

    在缺省情況下,XStream不需要配置映射關係,對象和字段將映射為同名XML元素。但是當對象和字段名與XML中的元素名不同時,XStream支持指定別名。XStream支持以方法調用的方式,或是Java 標註的方式指定別名。

    XStream在進行數據類型轉換時,使用系統缺省的類型轉換器。同時,也支持用戶自定義的類型轉換器。

4、XStream特點

  • 使用方便 – XStream的API提供了一個高層次外觀,以簡化常用的用例

  • 無需創建映射 – XStream的API提供了默認的映射大部分對象序列化

  • 性能  – XStream快速和低內存佔用,適合於大對象圖或系統

  • 乾淨的XML  – XStream創建一個乾淨和緊湊XML結果,這很容易閱讀

  • 不需要修改對象 – XStream可序列化的內部字段,如private和final字段,支持非公開類和內部類。默認構造函數不是強制性的要求

  • 完整對象圖支持 – XStream允許保持在對象模型中遇到的重複引用,並支持循環引用

  • 可自定義的轉換策略 – 定製策略可以允許特定類型的定製被表示為XML的註冊

  • 安全框架 – XStream提供了一個公平控制有關解組的類型,以防止操縱輸入安全問題

  • 錯誤消息 – 出現異常是由於格式不正確的XML時,XStream拋出一個統一的例外,提供了詳細的診斷,以解決這個問題

  • 另一種輸出格式 – XStream支持其它的輸出格式,如JSON

5、XStream常見的用途

    傳輸、持久化、配置、單元測試

二、XStream入門

1、添加XSteam依賴

<dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.12</version> </dependency> <dependency> <groupId>org.codehaus.jettison</groupId> <artifactId>jettison</artifactId> <version>1.4.1</version> </dependency>

2、XStream基本使用

package io.github.xstream.test01; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import lombok.AllArgsConstructor; import lombok.ToString; ​ public class XStreamTest01 { public static void main(String[] args) { Student student = new Student("張三", 20); XStream xStream = new XStream();//需要XPP3庫 //XStream xStream = new XStream(new DomDriver());//不需要XPP3庫 //XStream xStream = new XStream(new StaxDriver());//不需要XPP3庫開始使用Java 6 //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); //XML反序列化 student = (Student) xStream.fromXML(xml); System.out.println(student); ​ xStream = new XStream(new JettisonMappedXmlDriver()); xStream.setMode(XStream.NO_REFERENCES); //Json序列化 String json = xStream.toXML(student); System.out.println(json); //Json反序列 student = (Student) xStream.fromXML(json); System.out.println(student); } } ​ @AllArgsConstructor @ToString class Student { private String name; private int age; }

3、程序運行結果

<io.github.xstream.test01.Student>
  <name>張三</name>
  <age>20</age>
</io.github.xstream.test01.Student>
Security framework of XStream not initialized, XStream is probably vulnerable.
Student(name=張三, age=20)
{"io.github.xstream.test01.Student":{"name":"張三","age":20}}
Student(name=張三, age=20)
Security framework of XStream not initialized, XStream is probably vulnerable.

注意:文中使用到的Lombok註解,Lombok依賴自行添加;XStream序列化XML時需要引用的jar包:xstream-[version].jar、xpp3-[version].jar、xmlpull-[version].jar,當引入xstream依賴後會自動依賴xpp3、xmlpull依賴。XStream序列化JSON需要引用的jar包:jettison-[version].jar。

    使用XStream序列化時,對JavaBean沒有任何限制。JavaBean的字段可以是私有的,也可以沒有getter或setter方法,還可以沒有默認的構造函數。

    XStream序列化XML時可以允許用戶使用不同的XML解析器,用戶可以使用一個標準的JAXP DOM解析器或自Java 6集成STAX解析器。這樣用戶就不需要依賴xpp3-[version].jar。

三、XStream混疊

1、混疊是一種技術來定製生成XML或者使用XStream特定的格式化XML。假設,一個下面的XML格式是用於序列化/反序列化Student對象。

<student name="張三"> <phone> <brand>小米</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果</brand> <description>蘋果手機的描述</description> </phone> </student>

2、根椐上面的XML格式,我們創建實體類

@AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行代碼

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

4、驗證輸出

<io.github.xstream.test02.Student> <studentName>張三</studentName> <phones> <io.github.xstream.test02.Phone> <brand>小米手機</brand> <description>小米手機的描述</description> </io.github.xstream.test02.Phone> <io.github.xstream.test02.Phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </io.github.xstream.test02.Phone> </phones> </io.github.xstream.test02.Student>

    在上面的結果,我們已經看到了Student對象名稱是完全合格的。要替換它作為學生的標籤,按照四、XStream類混疊的步驟

    另外,在上述結果中可以看出,所需studentName要重命名來命名。要替換它,按照五、XStream字段混疊的步驟

    在上面的結果,我們可以看到手機標記被添加成為手機列表。替換它,按照六、XStream隱式集合混疊的步驟

    在上面的結果,我們可以看到這個名字來作為一個子節點,需要將它作為根節點的屬性。替換它,按照七、XStream屬性混疊的步驟

四、XStream類混疊

1、類混疊是用來創建一個類的XML完全限定名稱的別名。讓我們修改XStreamTest02例子,將下面的代碼添加到XStreamTest02例子裏面

xStream.alias("student", Person02.class); xStream.alias("phone", Phone.class);

2、執行代碼

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 xStream.alias("student", Student.class); xStream.alias("phone", Phone.class); ​ //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行結果

<student> <studentName>張三</studentName> <phones> <phone> <brand>小米手機</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </phone> </phones> </student>

可以看到<io.github.xstream.test02.Student>和<io.github.xstream.test02.Phone>分別被修改為了<student>和<phone>

五、XStream字段混疊

1、字段混疊用於創建以XML字段的別名。讓我們再次修改原來的XStreamTest02例子,將下面的代碼添加到XStreamTest02例子裏面

xStream.aliasField("name", Student.class, "studentName");

2、執行代碼

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 xStream.alias("student", Student.class); xStream.alias("phone", Phone.class); xStream.aliasField("name", Student.class, "studentName"); ​ //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行結果

<student> <name>張三</name> <phones> <phone> <brand>小米手機</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </phone> </phones> </student>

可以看到<studentName>被修改為了<name>

六、XStream隱式集合混疊

1、隱式集合混疊時使用的集合是表示在XML無需显示根。例如,在我們的例子中,我們需要一個接一個,但不是在根節點來显示每一個節點。讓我們再次修改原來的XStreamTest02例子,將下面的代碼添加到XStreamTest02例子裏面

xStream.addImplicitCollection(Student.class, "phones");

2、執行代碼

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 xStream.alias("student", Student.class); xStream.alias("phone", Phone.class); xStream.aliasField("name", Student.class, "studentName"); xStream.addImplicitCollection(Student.class, "phones"); ​ //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行結果

<student> <name>張三</name> <phone> <brand>小米手機</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </phone> </student>

可以看到<phones>被隱藏了

七、XStream屬性混疊

1、屬性混疊用於創建一個成員變量作為XML屬性序列化。讓我們再次修改原來的XStreamTest02例子,將下面的代碼添加到XStreamTest02例子裏面

xStream.useAttributeFor(Student.class, "studentName");

2、執行代碼

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 xStream.alias("student", Student.class); xStream.alias("phone", Phone.class); xStream.aliasField("name", Student.class, "studentName"); xStream.addImplicitCollection(Student.class, "phones"); xStream.useAttributeFor(Student.class, "studentName"); ​ //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行結果

<student name="張三"> <phone> <brand>小米手機</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </phone> </student>

可以看到<name>被作為了<student>的屬性

八、XStream包混疊

1、包混疊用於創建一個類XML的完全限定名稱的別名到一個新的限定名稱。讓我們再次修改原來的XStreamTest02例子,將下面代碼

xStream.alias("student", Student.class); xStream.alias("phone", Phone.class);

修改為

xStream.aliasPackage("xx.xx.xx.xx", "io.github.xstream.test02");

2、執行代碼

package io.github.xstream.test02; ​ import com.thoughtworks.xstream.XStream; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest02 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones); ​ XStream xStream = new XStream();//需要XPP3庫 // xStream.alias("student", Student.class); // xStream.alias("phone", Phone.class); xStream.aliasPackage("xx.xx.xx.xx", "io.github.xstream.test02"); xStream.aliasField("name", Student.class, "studentName"); xStream.addImplicitCollection(Student.class, "phones"); xStream.useAttributeFor(Student.class, "studentName"); ​ //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString class Student { private String studentName; private List<Phone> phones; } ​ @AllArgsConstructor @ToString class Phone { private String brand; private String description; }

3、執行結果

<xx.xx.xx.xx.Student name="張三"> <xx.xx.xx.xx.Phone> <brand>小米手機</brand> <description>小米手機的描述</description> </xx.xx.xx.xx.Phone> <xx.xx.xx.xx.Phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </xx.xx.xx.xx.Phone> </xx.xx.xx.xx.Student>

可以看到包名由io.github.xstream.test02替換為了xx.xx.xx.xx

九、XStream註解

1、前面的四、五、六、七、八步驟都是通過代碼操作的

//xStream.alias("student", Student.class);
//xStream.alias("phone", Phone.class);
xStream.aliasPackage("xx.xx.xx.xx", "io.github.xstream.test02");
xStream.aliasField("name", Student.class, "studentName");
xStream.addImplicitCollection(Student.class, "phones");
xStream.useAttributeFor(Student.class, "studentName");

2、XStream同時也支持註解,使用註解會變得簡單也會達到相同的效果

package io.github.xstream.test03; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.*; import com.thoughtworks.xstream.converters.basic.BooleanConverter; import lombok.AllArgsConstructor; import lombok.Data; import lombok.ToString; ​ import java.util.ArrayList; import java.util.List; ​ public class XStreamTest03 { public static void main(String[] args) { List<Phone> phones = new ArrayList<>(); phones.add(new Phone("小米手機", "小米手機的描述")); phones.add(new Phone("蘋果手機", "蘋果手機的描述")); Student student = new Student("張三", phones, 20, true); XStream xStream = new XStream();//需要XPP3庫 //xStream.processAnnotations(new Class[]{Student.class}); xStream.autodetectAnnotations(true); //XML序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @AllArgsConstructor @ToString //別名註解 @XStreamAlias("student") class Student { @XStreamAlias("name") //把字段節點設置成屬性 @XStreamAsAttribute private String studentName; //省略集合根節點 @XStreamImplicit private List<Phone> phones; //隱藏字段 @XStreamOmitField private int age; //設置轉換器 @XStreamConverter(value = BooleanConverter.class, booleans = {false}, strings = {"男", "女"}) private boolean sex; } ​ @AllArgsConstructor @ToString @XStreamAlias("phone") class Phone { private String brand; private String description; }

3、使用註解的話,需要XML序列化之前添加如下代碼

xStream.autodetectAnnotations(true);

或者

xStream.processAnnotations(new Class[]{Student.class});

4、執行結果

<student name="張三"> <phone> <brand>小米手機</brand> <description>小米手機的描述</description> </phone> <phone> <brand>蘋果手機</brand> <description>蘋果手機的描述</description> </phone> <sex>男</sex> </student>

使用註解我們也可以看到也能達到相同的效果

注意:當使用XStream對象處理一個被註解的類型時,XStream對象也會處理所有與其相關的類型的註解信息,即該類型的父類、父接口、所有子類的註解。

十、XStream自定義轉換器

1、XStream自帶的轉換器

    XStream內部有許多轉換器,用於JavaBean對象到XML或JSON之間的轉換。這些轉換器的詳細信息網址:http://x-stream.github.io/converters.html

2、使用自定義轉換器

package io.github.xstream.test04; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; ​ public class XStreamTest04 { public static void main(String[] args) { Student student =new Student("張三",19); XStream xStream = new XStream(); //註冊轉換器 xStream.registerConverter(new StudentConverter()); //序列化 String xml = xStream.toXML(student); System.out.println(xml); //反序列化 student=(Student)xStream.fromXML(xml); System.out.println(student); } } ​ @Getter @Setter @ToString @AllArgsConstructor class Student { private String name; private int age; }

自定義轉換器

package io.github.xstream.test04; ​ import com.thoughtworks.xstream.converters.Converter; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.UnmarshallingContext; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; ​ public class StudentConverter implements Converter { //定義轉換器能轉換的JavaBean類型 @Override public boolean canConvert(Class type) { return type.equals(Student.class); } ​ //把對象序列化成XML或JSON @Override public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) { Student student = (Student) value; writer.startNode("姓名"); writer.setValue(student.getName()); writer.endNode(); writer.startNode("年齡"); writer.setValue(student.getAge() + ""); writer.endNode(); writer.startNode("轉換器"); writer.setValue("自定義的轉換器"); writer.endNode(); } ​ //把XML或JSON反序列化成對象 @Override public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Student student = new Student("", -1); reader.moveDown(); student.setName(reader.getValue()); reader.moveUp(); reader.moveDown(); student.setAge(Integer.parseInt(reader.getValue())); reader.moveUp(); return student; } }

3、執行結果

<student>
  <姓名>張三</姓名>
  <年齡>19</年齡>
  <轉換器>自定義的轉換器</轉換器>
</student>
Security framework of XStream not initialized, XStream is probably vulnerable.
Student(name=張三, age=19)

4、常用的轉換器接口與抽象類

SingleValueConverter:單值轉換接口
AbstractSingleValueConverter:單值轉換抽象類
Converter:常規轉換器接口

十一、XStream對象流

1、對象輸出流

package io.github.xstream.test05; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.io.*; ​ public class XStreamTest05 { public static void main(String[] args) throws IOException, ClassNotFoundException { XStreamTest05 xStreamTest04 = new XStreamTest05(); String path = "F:\\test.txt"; XStream xStream = new XStream();//需要XPP3庫 xStream.processAnnotations(Student.class); xStream.autodetectAnnotations(true); xStreamTest04.writeObject(xStream, path); } ​ //對象輸出流方法 public void writeObject(XStream xStream, String path) throws IOException { Student zs = new Student("張三", 20); Student ls = new Student("李四", 21); Student ww = new Student("王五", 22); ObjectOutputStream objectOutputStream = xStream.createObjectOutputStream(new FileOutputStream(path)); objectOutputStream.writeObject(zs); objectOutputStream.writeObject(ls); objectOutputStream.writeObject(ww); objectOutputStream.writeObject("totalStudent"); objectOutputStream.writeInt(3); objectOutputStream.close(); } } ​ @AllArgsConstructor @ToString //別名註解 @XStreamAlias("student") class Student { @XStreamAlias("name") //把字段節點設置成屬性 @XStreamAsAttribute private String studentName; private int age; }

2、在指定路徑中打開test.txt文件,查看執行結果

<object-stream> <student name="張三"> <age>20</age> </student> <student name="李四"> <age>21</age> </student> <student name="王五"> <age>22</age> </student> <string>totalStudent</string> <int>3</int> </object-stream>

注意:XStream對象流是通過標準java.io.ObjectOutputStream和java.io.ObjectInputStream對象。因為XML文檔只能有一個根節點,必須包裝在一個序列化的所有元素額外的根節點。這個根節點默認為<object-stream>上面的例子所示。 

3、對象輸入流

package io.github.xstream.test05; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamAsAttribute; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.io.*; ​ public class XStreamTest05 { public static void main(String[] args) throws IOException, ClassNotFoundException { XStreamTest05 xStreamTest04 = new XStreamTest05(); String path = "F:\\test.txt"; XStream xStream = new XStream();//需要XPP3庫 xStream.processAnnotations(Student.class); xStream.autodetectAnnotations(true); xStreamTest04.readObject(xStream, path); } ​ //對象輸入流方法 public void readObject(XStream xStream, String path) throws IOException, ClassNotFoundException { ObjectInputStream objectInputStream = xStream.createObjectInputStream(new FileInputStream(path)); System.out.println((Student) objectInputStream.readObject()); System.out.println((Student) objectInputStream.readObject()); System.out.println((Student) objectInputStream.readObject()); System.out.println(objectInputStream.readObject()); System.out.println(objectInputStream.readInt()); } } ​ @AllArgsConstructor @ToString //別名註解 @XStreamAlias("student") class Student { @XStreamAlias("name") //把字段節點設置成屬性 @XStreamAsAttribute private String studentName; private int age; }

4、執行結果

Student(studentName=張三, age=20)
Student(studentName=李四, age=21)
Student(studentName=王五, age=22)
totalStudent
3

十二、XStream持久化API

1、保存Java對象

package io.github.xstream.test06; ​ import com.thoughtworks.xstream.persistence.FilePersistenceStrategy; import com.thoughtworks.xstream.persistence.PersistenceStrategy; import com.thoughtworks.xstream.persistence.XmlArrayList; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.io.File; import java.util.List; ​ public class XStreamTest06 { public static void main(String[] args) { XStreamTest06 xStreamTest06=new XStreamTest06(); xStreamTest06.saveObject(); } ​ //保存Java對象 public void saveObject(){ PersistenceStrategy strategy = new FilePersistenceStrategy(new File("F:\\")); List list = new XmlArrayList(strategy); list.add(new Student("張三",13)); list.add(new Student("李四",21)); list.add(new Student("王五",17)); } } ​ @ToString @AllArgsConstructor class Student { private String name; private int age; }

2、運行程序結果,在F磁盤的根路徑可以看到有三個文件:int@0.xml、int@1.xml、int@2.xml,每個對象都被序列化到XML文件里

3、讀取並刪除JavaBean對象

package io.github.xstream.test06; ​ import com.thoughtworks.xstream.persistence.FilePersistenceStrategy; import com.thoughtworks.xstream.persistence.PersistenceStrategy; import com.thoughtworks.xstream.persistence.XmlArrayList; import lombok.AllArgsConstructor; import lombok.ToString; ​ import java.io.File; import java.util.Iterator; import java.util.List; ​ public class XStreamTest06 { public static void main(String[] args) { XStreamTest06 xStreamTest06 = new XStreamTest06(); xStreamTest06.deleteObject(); } //讀取並刪除Java對象 public void deleteObject() { PersistenceStrategy strategy = new FilePersistenceStrategy(new File("F:\\")); List list = new XmlArrayList(strategy); for (Iterator it = list.iterator(); it.hasNext(); ) { System.out.println((Student) it.next()); //刪除對象序列化文件 it.remove(); } } } ​ @ToString @AllArgsConstructor class Student { private String name; private int age; }

4、運行程序結果,可以看到把F磁盤的根路徑int@0.xml、int@1.xml、int@2.xml文件刪除了

Security framework of XStream not initialized, XStream is probably vulnerable.
Student(name=張三, age=13)
Student(name=李四, age=21)
Student(name=王五, age=17)

十三、XStream操作JSON

1、XStream序列化JSON的重命名

package io.github.xstream.test07; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import io.github.xstream.test04.StudentConverter; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; ​ public class XStreamTest07 { public static void main(String[] args) { XStreamTest07 xStreamTest07 = new XStreamTest07(); xStreamTest07.serializeJson(); } ​ public void serializeJson() { Student student = new Student("張三", 19); XStream xStream = new XStream(new JettisonMappedXmlDriver());//設置Json解析器 xStream.autodetectAnnotations(true); //JSON序列化 String xml = xStream.toXML(student); System.out.println(xml); //JSON反序列化 student = (Student) xStream.fromXML(xml); System.out.println(student); } } ​ @ToString @AllArgsConstructor @XStreamAlias("人") class Student { @XStreamAlias("姓名") private String name; @XStreamAlias("年齡") private int age; }

2、運行結果

{"人":{"姓名":"張三","年齡":19}}
Student(name=張三, age=19)
Security framework of XStream not initialized, XStream is probably vulnerable.

注意:XStream序列化JSON的重命名的方式與其序列化成XML的方式一樣!

3、去掉序列化JSON的根節點

package io.github.xstream.test07; ​ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver; import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver; import com.thoughtworks.xstream.io.json.JsonWriter; import io.github.xstream.test04.StudentConverter; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; ​ import java.io.Writer; ​ public class XStreamTest07 { public static void main(String[] args) { XStreamTest07 xStreamTest07 = new XStreamTest07(); xStreamTest07.removeRootNode(); } ​ public void removeRootNode() { Student student = new Student("張三", 19); XStream xStream = new XStream(new JsonHierarchicalStreamDriver() { public HierarchicalStreamWriter createWriter(Writer writer) { return new JsonWriter(writer, JsonWriter.DROP_ROOT_MODE); } }); //Json序列化 String xml = xStream.toXML(student); System.out.println(xml); } } ​ @ToString @AllArgsConstructor @XStreamAlias("人") class Student { @XStreamAlias("姓名") private String name; @XStreamAlias("年齡") private int age; }

4、運行結果

{
  "name": "張三", "age": 19 }

注意:去掉根節點后的JSON串是不能反序列化的,因為XStream不知道它的類型。

5、JSON的解析器區別

前面兩個例子使用了不同的JSON解析器,這裏說明他們的不同之處:

  1. JettisonMappedXmlDriver:是支持序列化和反序列化Json的。

  2. JsonHierarchicalStreamDriver:只支持序列化,不支持反序列化。

 

參考:

    http://x-stream.github.io

    https://www.yiibai.com/xstream

    https://www.cnblogs.com/LiZhiW/p/4313493.html

● 別在 Java 代碼里亂打日誌了,這才是正確的打日誌姿勢!

● 高可用Redis服務架構分析與搭建

● 8 種方案,幫你解決重複提交問題!請拿走

● IDEA 解決 Maven 依賴衝突的高能神器,這一篇夠不夠?

● 你連微服務的網關都說不清楚,還天天鼓搗着要把項目拆分微服務?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

1.3T動力強?比速新SUV/MPV預售價僅6.19-8.6萬_租車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

總結:外觀上也算是比較高顏值,原創度方面值得表揚,豐富的配置和良好的空間表現,綜合實力還是比較強的,未來的路充滿挑戰啊。比速M3預售價:6。19-8。39萬比速M3主要針對的是嚮往舒適安逸生活的家庭消費群體而打造的,外觀上更有精緻感,車身線條簡潔舒暢,誇張的X造型中網採用大面積鍍鉻裝飾,給人很霸氣的感覺,辨識度很高,B、C、D柱也是採用黑色設計,懸浮車頂非常時尚。

比速T3

預售價:7.49-8.69萬元

比速T3的整體外觀設計走的是時尚風格,進氣格柵與大燈採用整體式設計,視覺效果出色,再加上鍍鉻飾條的點綴與中央“BISU”車標巧妙的融合,營造了飛翼式的造型特點,黑色運動包圍與車頂行李架很有SUV的范。

而側面A柱、B柱和C柱都採用了黑色的設計,營造出懸浮式車頂的炫酷效果,雙五輻式輪圈,硬朗的尾燈線條,雙邊雙出的排氣管,運動感十足。

內飾部分依然透露着很潮流的氣息,黑棕雙色搭配的內飾很顯檔次,多處採用銀色飾條飾板,凸顯年輕的風格,懸浮式的中控屏設計是一大亮點,據說,比速T3還全系標配車身穩定系統、四輪碟剎、倒車雷達、中控大屏、倒車影像等使用功能。

比速T3的長寬高分別為4350/1825/1685mm,軸距為2565mm,與哈弗H2、瑞風S3等競爭對手相比稍微佔優,雙縫線皮質座椅質感出色,前後排空間表現還算良好,

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

儲物格空間比較實用。

動力方面,比速T3全系標配1.3T發動機,最大功率133馬力,與之匹配的5擋手動變速器,相信不會讓我們失望。

總結:外觀上也算是比較高顏值,原創度方面值得表揚,豐富的配置和良好的空間表現,綜合實力還是比較強的,未來的路充滿挑戰啊。

比速M3

預售價:6.19-8.39萬

比速M3主要針對的是嚮往舒適安逸生活的家庭消費群體而打造的,外觀上更有精緻感,車身線條簡潔舒暢,誇張的X造型中網採用大面積鍍鉻裝飾,給人很霸氣的感覺,辨識度很高,B、C、D柱也是採用黑色設計,懸浮車頂非常時尚。

內飾整體造型設計還是挺跟得上潮流的,仿木飾板的加入提升了不少豪華感,雖說這個價位來說塑料面板在所難免,但是邊緣仿縫線的設計增添了很多精緻感,頂配車型一塊10英寸的中控屏是一大亮點,支持手機互聯、導航等主流功能。

其長*寬*高為4760*1810*1751mm,軸距達到2800mm,和寶駿730等競品相比很有優勢,門板還有擋把兩側都包裹了皮革、絨布等軟性材質,觸感很好,即使是第三排空間也很舒適。

在動力系統部分,將提供了1.5L自然吸氣發動機,最大功率114馬力,後期還會有1.5T渦輪增壓發動機和CVT變速器,而傳動部分,將搭載5擋手動變速器。

總結:在高手如雲的自主品牌MpV市場里,會與有寶駿730、風行S500等車型廝殺,相信在車內空間、配置方面有優勢的比速M3會得到用戶的肯定。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

CR-V、RAV4要哭?全新美繫緊湊型SUV酷似大切諾基!_租車

※超省錢租車方案

商務出差、學生出遊、旅遊渡假、臨時用車!GO 神州租賃有限公司!合法經營、合法連鎖、合法租賃小客車!

4T有所不同,指南者所搭載的1。4T發動機最大馬力達到165匹,而自由俠的1。4T馬力值為150匹,相信未來正式上市以後動力表現還是值得期待。此外作為配置分級,不出意外的話全新指南者還將會搭載一台與現款自由光一樣的2。4L自然吸氣發動機,最大馬力值175匹。

Jeep在華國產以後,先後推出了自由光和自由俠兩款SUV車型,但自由光作為中型SUV售價偏高,自由俠作為小型SUV尺寸偏小,使得很大一部分意向車型在緊湊型SUV的潛在買家有些感到捉急..不過不需要糾結,廣汽菲克第三款SUV——指南者,將在不久后發布,並有望在年內正式上市。

指南者的名聲來頭並不小,作為Jeep品牌全系尚以進口方式在國內進行銷售之時,指南者可以說是Jeep品牌銷量非常高的車型,車身緊湊,外觀也是足夠的霸氣,

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

日本、大陸,發現這些先進的國家已經早就讓電動車優先上路,而且先進國家空氣品質相當好,電動車節能減碳可以減少空污

而且進口指南者最低售價僅在20萬出頭,這也讓很大一部分車主有了購買進口車型的理由。

(圖為現款在售2014款指南者)

全新指南者的外觀依舊向自家旗艦大切諾基看齊,標誌性的7孔豎狀進氣格柵彰顯的是Jeep家族的血統,整體性極強的前臉設計風格讓指南者的車頭看上去更寬,視覺效果也更加威武霸氣。

全新指南者的內飾與自由光的相似程度比較高,從官圖上看用深淺配色的撞色設計的確有助於提高視覺衝擊力,至於實車的內飾表現如何,還需要在正式上市以後看到實車再進行評判。

動力總成方面,全新指南者搭載了號稱Jeep史上最強的小排量渦輪增壓發動機1.4T FIRE Multi-Air2,與自由俠身上那台1.4T有所不同,指南者所搭載的1.4T發動機最大馬力達到165匹,而自由俠的1.4T馬力值為150匹,相信未來正式上市以後動力表現還是值得期待。

此外作為配置分級,不出意外的話全新指南者還將會搭載一台與現款自由光一樣的2.4L自然吸氣發動機,最大馬力值175匹。

市場分析:Jeep指南者作為進口車型在華銷售的價格並不高(22.19-28.09萬),進行國產後售價有望進一步壓低,屆時可能會成為合資緊湊型SUV市場中的有力競爭选手。據悉Jeep指南者將在11月16日正式發布,至於具體的配置,我們也將在新車上市後進行詳細分析。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

有別於一般網頁架設公司,除了模組化的架站軟體,我們的營業主軸還包含:資料庫程式開發、網站建置、網頁設計、電子商務專案開發、系統整合、APP設計建置、專業網路行銷。

【JVM故事】一個Java字節碼文件的誕生記

萬字長文,完全虛構。

 

 

組裡來了個實習生,李大胖面完之後,覺得水平一般,但還是留了下來,為什麼呢?各自猜去吧。

李大胖也在心裏開導自己,學生嘛,不能要求太高,只要肯上進,慢慢來。就稱呼為小白吧。

小白每天來的很早,走的很晚,都在用功學習,時不時也向別人請教。只是好像天資差了點。

都快一周了,才能寫些“簡單”的代碼,一個註解,一個接口,一個類,都來看看吧:

public @interface Health {

    String name() default "";
}


public interface Fruit {

    String getName();

    void setName(String name);

    int getColor();

    void setColor(int color);
}


@Health(name = "健康水果")
public class Apple implements Fruit {

    private String name;
    private int color;
    private double weight = 0.5;

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int getColor() {
        return color;
    }

    @Override
    public void setColor(int color) {
        this.color = color;
    }

    public double weight() {
        return weight;
    }

    public void weight(double weight) {
        this.weight = weight;
    }
}

與周圍人比起來,小白進步很慢,也許是自己不夠聰明,也許是自己不適合干這個,小白好像有點動搖了。

這幾天,小白明顯沒有一開始那麼上進了,似乎有點想放棄,這不,趴在桌子上竟然睡着了。

 

(二)

 

在夢中,小白來到一個奇怪又略顯陰森的地方,眼前有一個破舊的小房子,從殘缺不全的門縫裡折射出幾束光線。

小白有些害怕,但還是鎮定了下,深呼吸幾口,徑直朝着小房子走去。

小白推開門,屋裡沒有人。只有一個“機器”在桌子旁大口大口“吃着”東西,背後也不時的“拉出”一些東西。

小白很好奇,就湊了上去,準備仔細打量一番。

“你要幹嘛,別影響我工作”。突然冒出一句話,把小白嚇了一大跳,慌忙後退三步,媽呀,心都快蹦出來了。

“你是誰呀?”,驚慌中小白說了句話。

“我是編譯器”,哦,原來這個機器還會說話,小白這才緩了過來。

“編譯器”,小白好像聽說過,但一時又想不起,於是猜測到。

“網上評論留言里說的小編是不是就是你啊”?

“你才是呢”,編譯器白了一眼,沒好聲氣的說到。

要不是看在長得還行的份上,早就把你趕走了,編譯器心想。

“哦,我想起來了,編譯器嘛,就是編譯代碼的那個東西”,小白恍然大悟到。

“請注意你的言詞,我不是個東西,哦,不對,我是個東西,哦,好像也不對,我。我。”,編譯器自己也快暈了。

編譯器一臉的無奈,遇上這樣的人,今天我認栽了。

小白才不管呢,心想,今天我竟然見到了編譯器,我得好好請教請教他。

那編譯器會幫助她嗎?

 

 

(三)

 

小白再次走上前來,定睛一看,才看清楚,編譯器吃的是Java源碼,拉的是class(字節碼)文件。

咦,為啥這個代碼這麼熟悉呢,不就是我剛剛寫的那些。“停,停,快停下來了”。編譯器被小白叫停了。

“你又要幹嘛啊”?編譯器到。

“嘻嘻,這個代碼是我寫的,我想看看它是怎麼被編譯的”,小白到。

編譯器看了看這個代碼,這麼“簡單”,她絕對是個菜鳥。哎,算了,還是讓她看看吧。

不過編譯器又到,“整個編譯過程是非常複雜的,想要搞清楚裏面的門道是不可能的,今天也就只能看個熱鬧了”。

“編譯后的內容都是二進制數據,再通俗點說,就是一個長長的字節數組(byte[])”,編譯器繼續說,“通常把它寫入文件,就是class文件了”。

“但這不是必須的,也可以通過網絡傳到其它地方,或者保存在內存中,用完之後就丟棄”。

“哇,還可以這樣”,小白有些驚訝。編譯器心想,你是山溝里出來的,沒見過世面,大驚小怪。

繼續到,“從數據結構上講,數組就是一段連續的空間,是‘沒有結構’的,就像一個線段一樣,唯一能做的就是按索引訪問”。

小白到,“編譯后的內容一定很繁多,都放到一個數組裡面,怎麼知道什麼東西都在哪呢?不都亂套了嘛”。

編譯器覺得小白慢慢上道了,心裏有一絲安慰,至少自己的講解不會完全白費。於是繼續到。

“所以JVM的那些大牛們早就設計好了字節碼的格式,而且還把它們放入到了一個字節數組裡面”。

小白很好奇到,“那是怎麼實現的呢”?

“其實也沒有太高深的內容,既然數組是按位置的,那就規定好所有內容的先後順序,一個接一個往數組裡放唄”。

“如果內容的長度是固定(即定長)的,那最簡單,直接放入即可”。

“如果內容長度是不固定(即變長)的,也很簡單,在內容前用一到兩個字節存一下內容的長度不就OK了”。

 

 

(四)

 

“字節碼的前4個字節必須是一個固定的数字,它的十進制是3405691582,大部分人更熟悉的是它的十六進制,0xCAFEBABE”。

“通常稱之為魔術数字(Magic),它主要是用來區分文件類型的”,編譯器到。

“擴展名(俗稱後綴名)不是用來區分文件類型的嗎”?小白說到,“如.java是Java文件,.class是字節碼文件”。

“擴展名確實可以區分,但大部分是給操作系統用的,或給人看到。如我們看到.mp3時知道是音頻、.mp4是知道是視頻、.txt是文本文件”。

“操作系統可以用擴展名來關聯打開它的軟件,比如.docx就會用word來打開,而不會用文本文件”。編譯器繼續到。

“還有一個問題就是擴展名可以很容易被修改,比如把一個.java手動改為.class,此時讓JVM來加載這個假的class文件會怎樣呢”?

“那JVM先讀取開頭4個字節,發現它不是剛剛提到的那個魔數,說明它不是合法的class文件,就直接拋異常唄”,小白說到。

“很好,真是孺子可教”,編譯器說道,“不過還有一個問題,不知你是否注意到?4個字節對應Java的int類型,int類型的最大值是2147483647”。

“但是魔數的值已經超過了int的最大值,那怎麼放得下呢,難道不會溢出嗎”?

“確實啊,我怎麼沒發現呢,那它到底是怎麼放的呢”?小白到。

“其實說穿了不值得一提,JVM是把它當作無符號數對待的。而Java是作為有符號數對待的。無符號數的最大值基本上是有符號數最大值的兩倍”。

“接下來的4個字節是版本號,不同版本的字節碼格式可能會略有差異,其次在運行時會校驗,如JDK8編譯后的字節碼是不能放到JDK7上運行的”。

“這4個字節中的前2個是次(minor)版本,后2個是主(major)版本”。編譯器繼續到,“比如我現在用的JDK版本是1.8.0_211,那次版本就是0,主版本就是52”。

“所以前8個字節的內容是,0xCAFEBABE,0,52,它們並不是源代碼里的內容”。

Magic [getMagic()=0xcafebabe]
MinorVersion [getVersion()=0]
MajorVersion [getVersion()=52]

 

(五)

 

當編譯器讀到源碼中的public class的時候,然後就就去查看一個表格,如下圖:

自顧自的說著,“public對應的是ACC_PUBLIC,值為0x0001,class默認就是,然後又讀ACC_SUPER的值0x0020”。

“最後把它倆合起來(按位或操作),0x0001 | 0x0020 => 0x0021,然後把這個值存起來,這就是這個類的訪問控制標誌”。

小白這次算是開了眼界了,只是還有一事不明,“這個ACC_SUPER是個什麼鬼”?

編譯器解釋到,“這是歷史遺留問題,它原本表達在調用父類方法時會特殊處理,不過現在已經不再管它了,直接忽略”。

接着讀到了Apple,它是類名。編譯器首先要獲取類的全名,org.cnt.java.Apple。

然後對它稍微轉換一下形式,變成了,org/cnt/java/Apple,“這就是類名在字節碼中的表示”。

編譯器發現這個Apple類沒有顯式繼承父類,表明它繼承自Object類,於是也獲取它的全名,java/lang/Object。

接着讀到了implements Fruit,說明該類實現了Fruit接口,也獲取全名,org/cnt/java/Fruit。

小白說到,“這些比較容易理解,全名中把點號(.)替換為正斜線(/)肯定也是歷史原因了。但是這些信息如何存到數組裡呢”?

“把點號替換為正斜線確實是歷史原因”,編譯器繼續到,“這些字符串雖然都是類名或接口名,但本質還是字符串,類名、接口名只是賦予它的意義而已”。

“除此之外,像字段名、方法名也都是字符串,同理,字段名、方法名也是賦予它的意義。所以字符串是一種基本的數據,需要得到支持”。

“除了字符串之外,還有整型数字,浮點数字,這些也是基本的數據,也需要得到支持”。

因此,設計者們就設計出了以下幾種類型,如圖:

“左邊是類型名稱,方便理解,右邊是對應的值,用於存儲”,編譯器繼續到。

“這裏的Integer/Long/Float/Double和Utf8都是具體保存數據用的,表示整型數/浮點數和字符串。其它的類型大都是對字符串的引用,並賦予它一定的意義”。

“所以類名首先被存儲為一個字符串,也就是Utf8,它的值對應的是1”。編譯器接着到,“由於字符串是一個變長的,所以就先用兩個字節存儲字符串的長度,接着跟上具體的字符串內容”。

所以字符串的結構就是這樣,如圖:

“類名字符串的存儲數據為,1、18、org/cnt/java/Apple。第一個字節為1,表明是Utf8類型,第2、3兩個字節存儲18,表示字符串長度是18,接着存儲真正的字符串。所以共用去1 + 2 + 18 => 21個字節”。

“父類名字符串存儲為,1、16、java/lang/Object。共用去19個字節”。

“接口名字符串存儲為,1、18、org/cnt/java/Fruit。共用去21個字節”。

小白聽的不住點頭,編譯器喘口氣,繼續講解。

“字符串存好后,就該賦予它們意義了,在後續的操作中肯定涉及到對這些字符串的引用,所以還要給每個字符串分配一個編號”。

如Apple為#2,即2號,Object為#4,Fruit為#6。

“由於這三個字符串都是類名或接口名,按照設計規定應該使用Class表示,對應的值為7,然後再指定一個字符串的編號即可”。

因此類或接口的表示如下圖:

“先用1個字節指明是類(接口),然後再用2個字節存儲一個字符串的編號。整體意思很直白,就是把這個編號的字符串當作類名或接口名”。

“類就表示為,7、#2。7表示是Class,#2表示類名稱那個字符串的存儲編號。共用去3個字節”。

“父類就表示,7、#4。共用去3個字節。接口就表示為,7、#6。共用去3個字節”。

其實這三個Class也分別給它們一個編號,方便別的地方再引用它們。

 

 

(六)

 

“其實上面這些內容都是常量,它們都位於常量池中,它們的編號就是自己在常量池中的索引”。編譯器說到。

“常量池很多人都知道,起碼至少是聽說過。但絕大多數人對它並不十分熟悉,因為很少有人見過它”。

編譯器繼續到,“今天你可算是來着了”,說著就把小白寫的類編譯後生成的常量池擺到了桌子上。

“這是什麼東西啊,這麼多,又很奇怪”,小白說到,這也是她第一次見。

ConstantPoolCount [getCount()=46]
ConstantPool [
#0 = null
#1 = ConstantClass [getNameIndex()=2, getTag()=7]
#2 = ConstantUtf8 [getLength()=18, getString()=org/cnt/java/Apple, getTag()=1]
#3 = ConstantClass [getNameIndex()=4, getTag()=7]
#4 = ConstantUtf8 [getLength()=16, getString()=java/lang/Object, getTag()=1]
#5 = ConstantClass [getNameIndex()=6, getTag()=7]
#6 = ConstantUtf8 [getLength()=18, getString()=org/cnt/java/Fruit, getTag()=1]
#7 = ConstantUtf8 [getLength()=4, getString()=name, getTag()=1]
#8 = ConstantUtf8 [getLength()=18, getString()=Ljava/lang/String;, getTag()=1]
#9 = ConstantUtf8 [getLength()=5, getString()=color, getTag()=1]
#10 = ConstantUtf8 [getLength()=1, getString()=I, getTag()=1]
#11 = ConstantUtf8 [getLength()=6, getString()=weight, getTag()=1]
#12 = ConstantUtf8 [getLength()=1, getString()=D, getTag()=1]
#13 = ConstantUtf8 [getLength()=6, getString()=<init>, getTag()=1]
#14 = ConstantUtf8 [getLength()=3, getString()=()V, getTag()=1]
#15 = ConstantUtf8 [getLength()=4, getString()=Code, getTag()=1]
#16 = ConstantMethodRef [getClassIndex()=3, getNameAndTypeIndex()=17, getTag()=10]
#17 = ConstantNameAndType [getNameIndex()=13, getDescriptorIndex()=14, getTag()=12]
#18 = ConstantDouble [getDouble()=0.5, getTag()=6]
#19 = null
#20 = ConstantFieldRef [getClassIndex()=1, getNameAndTypeIndex()=21, getTag()=9]
#21 = ConstantNameAndType [getNameIndex()=11, getDescriptorIndex()=12, getTag()=12]
#22 = ConstantUtf8 [getLength()=15, getString()=LineNumberTable, getTag()=1]
#23 = ConstantUtf8 [getLength()=18, getString()=LocalVariableTable, getTag()=1]
#24 = ConstantUtf8 [getLength()=4, getString()=this, getTag()=1]
#25 = ConstantUtf8 [getLength()=20, getString()=Lorg/cnt/java/Apple;, getTag()=1]
#26 = ConstantUtf8 [getLength()=7, getString()=getName, getTag()=1]
#27 = ConstantUtf8 [getLength()=20, getString()=()Ljava/lang/String;, getTag()=1]
#28 = ConstantFieldRef [getClassIndex()=1, getNameAndTypeIndex()=29, getTag()=9]
#29 = ConstantNameAndType [getNameIndex()=7, getDescriptorIndex()=8, getTag()=12]
#30 = ConstantUtf8 [getLength()=7, getString()=setName, getTag()=1]
#31 = ConstantUtf8 [getLength()=21, getString()=(Ljava/lang/String;)V, getTag()=1]
#32 = ConstantUtf8 [getLength()=16, getString()=MethodParameters, getTag()=1]
#33 = ConstantUtf8 [getLength()=8, getString()=getColor, getTag()=1]
#34 = ConstantUtf8 [getLength()=3, getString()=()I, getTag()=1]
#35 = ConstantFieldRef [getClassIndex()=1, getNameAndTypeIndex()=36, getTag()=9]
#36 = ConstantNameAndType [getNameIndex()=9, getDescriptorIndex()=10, getTag()=12]
#37 = ConstantUtf8 [getLength()=8, getString()=setColor, getTag()=1]
#38 = ConstantUtf8 [getLength()=4, getString()=(I)V, getTag()=1]
#39 = ConstantUtf8 [getLength()=3, getString()=()D, getTag()=1]
#40 = ConstantUtf8 [getLength()=4, getString()=(D)V, getTag()=1]
#41 = ConstantUtf8 [getLength()=10, getString()=SourceFile, getTag()=1]
#42 = ConstantUtf8 [getLength()=10, getString()=Apple.java, getTag()=1]
#43 = ConstantUtf8 [getLength()=25, getString()=RuntimeVisibleAnnotations, getTag()=1]
#44 = ConstantUtf8 [getLength()=21, getString()=Lorg/cnt/java/Health;, getTag()=1]
#45 = ConstantUtf8 [getLength()=12, getString()=健康水果, getTag()=1]
]

“在常量池前面會用2個字節來存儲常量池的大小,需要記住的是,這個大小不一定就是池中常量的個數。但它減去1一定是最大的索引”。

“因為,常量池中為0的位置(#0)永遠不使用,還有Long和Double類型一個常量佔2個連續索引(沒錯,又是歷史原因),實際只是用了第1個索引,第2個索引永遠空着(參見#18、#19)”。

編譯器繼續到,“#0是特殊的,用來表示‘沒有’的意思,其它地方如果想表達沒有的話,可以指向它。如Object是沒有父類的,所以它的父類指向#0,即沒有”。

“所以常量都是從#1開始。可以看看#1到#6的內容,就是剛剛上面講的”。編譯器說到。

“真是學到不少知識啊”,小白說到,“關於常量池能不能再多講點”?編譯器只好繼續講。

 

 

(七)

 

“常量池就是一個容器,它裏面放了各種各樣的所有信息,並且為每個信息分配一個編號(即索引),如果想要在其它地方使用這些信息,直接使用這個編號就行了”。

編譯器繼續到,“這個常量池在一些語言中也被稱為‘符號表’,通過編號來使用的這種方式也被稱為‘符號引用’”。

相信很多愛學習的同學對符號表和符號引用這兩個詞都很熟悉,不管之前是不是真懂,至少現在應該是真的搞懂了。因為你已經看到了。

“採用這種常量池和常量引用方式的好處其實很多,就說個最容易想到的,就是重複利用,節省空間,便於管理”。編譯器繼續說。

“比如一個類里有10個方法,每個方法里都定義一個length的局部變量,那麼length這個名字就會出現在常量池裡面,且只會出現一次,那10個方法都是對它的引用而已”。

“如果有一個方法的名字也叫length的話,那也是對同一個常量的引用,因為這個length常量只是個字符串數據而已,本身沒有明確含義,它的含義來自於引用它的常量”。

“哦,原來如此”,小白開悟到,“‘符號表、符號引用’這些‘高大上’的叫法,不過就是根據索引去列表裡獲取元素罷了”,哈哈。

編譯器看到小白這麼開心,就準備拋出一個問題,“打壓”一下她。於是說到。

“常量池看上去和數組/列表非常相似,都是容器且都是基於索引訪問的。為啥常量池只被稱為符號表,而不是符號數組或符號列表呢”?

小白自然回答不上來。編譯器繼續說,“表的英文單詞是Table。它和數組/列表的唯一區別就是,數組/列表裡的元素長度都是固定的。表裡的元素長度是不固定的”。

“常量池中的好幾種常量的長度都是變長的,所以自然是表了”。

小白點了點頭,心裏想,這編譯器就是厲害,我這輩子看來都無法達到他的高度了。

編譯器繼續說到,“字節碼的前8個字節存儲魔數和版本,接着的2個(9和10)字節存儲常量池的大小,後面接着(從11開始)就是整個常量池的內容了”。

“之所以把常量池放這麼靠前,是因為後面的所有內容都要依賴它、引用它”。

緊跟在常量池之後的就是這個類的基本信息,如下:

“首先用2個字節存儲上面已經計算好的訪問控制標誌,即0x0021”。

“然後用2個字節存儲這個類在常量池中的索引,就是#1”。

“然後用2個字節存儲該類的父類在常量池中的索引,就是#3”。

“由於接口可以有多個,所以再用2個字節存儲接口的個數,因為只實現了1個接口,所以就存儲数字1”。

“接着存儲所有接口在常量池中的索引,每個接口用2個字節。因為只實現了1個接口,所以存儲的索引就是#5”。

AccessFlags [getAccessFlags()=0x21, getAccessFlagsString()=[ACC_PUBLIC, ACC_SUPER]]
ThisClass [getClassIndex()=1, getClassName()=org/cnt/java/Apple]
SuperClass [getClassIndex()=3, getClassName()=java/lang/Object]
InterfacesCount [getCount()=1]
Interfaces [getClassIndexes()=[5], getClassNames()=[org/cnt/java/Fruit]]

 

 

 

(八)

 

編譯器繼續到,“接下來該讀取字段信息了”。當讀到private時,就去下面這張表裡找:

找到ACC_PRIVATE,把它的值0x0002保存以下,這就是該字段的訪問控制標誌。

接着讀到的是String,這是字段的類型,然後會把這個String類型存入常量池,對應的索引是#8。

可以看到是一個Utf8,說明是字符串,內容是 Ljava/lang/String; ,以大寫L開頭,已分號;結尾,中間是類型全名,這是在字節碼中表示類(對象)類型的方式。

接着讀到的是name,這是字段名稱,也是個字符串,同樣也把它放入常量池,對應的索引是#7。

編譯器說到,“現在一個字段的信息已經讀取完畢,按照相同的方式把剩餘的兩個字段也讀取完畢”。

“那字段的信息又該怎麼存儲呢”?小白問到。“不要着急嘛”,編譯器說著就拿出了字段的存儲格式:

首先2個字節是訪問控制標誌,接着2個字節是字段名稱在常量池中的索引,接着2個字節是字段描述(即類型)在常量池中的索引。

接着2個字節就是屬性個數,然後就是具體的屬性信息了。例如字段上標有註解的話,這個註解信息就會放入屬性信息里。

編譯器繼續說到,“屬性信息是字節碼中比較複雜的內容,這裏就不說太多了”。接着就可以按格式整理數據了。

因為一個類的字段可以有多個,所以先用2個字節存儲一下字段數目,本類有3個字段,所以就存儲個3。

第一個字段,0x0002、#7、#8、0。共用去8個字節,因為自動沒有屬性內容。

第二個字段,0x0002、#9、#10、0。共用去8個字節。

第二個字段,0x0002、#11、#12、0。共用去8個字節。

編譯器接着說,“所以存儲這3個字段信息共用去2 + 8 + 8 + 8 => 26個字節”。

小白說到,“我現在基本已經搞明白套路了。其實有些東西沒有想象中的那麼複雜啊”。

“複雜的東西還是有的,我們現在先不考慮”,編譯器說到,“還有一個問題,不知你發現了沒有”。

字段color的類型是int,但是在常量池中卻變為大寫字母I,同樣weight的類型是double,常量池中卻是大寫字母D。

小白說到,“我來猜測一下吧,int、double是Java中的數據類型,I、D是與之對應的在JVM中的表示形式。對吧”?

“算你聰明”,編譯器說到,“其實Java和JVM之間關於類型這塊有一個映射表”,如下:

有兩個需要注意。“第一點上面已經說過了,就是類都會映射成LClassName;這種形式,如Object映射為Ljava/lang/Object;”。

第二點是數組,“數組在Java中用一對中括號([])表示,在JVM中只用左中括號([)表示。也就是[]映射為[”。

“多維數組也一樣,[][][]映射為[[[”。然後還有類型,“Java是把類型放到前面,JVM是把類型放到後面”。如double[]映射為[D。

“double[][][]映射為[[[D”。同理,“String[]映射為[Ljava/lang/String;,Object[][]映射為[[Ljava/lang/Object;”。

“我似乎又明白了一些,Java有自己的規範,字節碼也有自己的規範,它們之間的映射關係早都已經定義好了”。小白繼續到。

“只要按照這種映射關係,就能把Java源碼給轉換為字節碼。是吧”?

“粗略來說,可以這麼理解,其實這就是編譯了,但一定要清楚,真正的編譯是非常複雜的一個事情”,編譯器到。

小白說到,“字段完了之後,肯定該方法了,就交給我吧,讓我也試試”。

“年輕人啊,就是生猛,你來試試吧”。編譯器說到。

FieldsCount [getCount()=3]
Fields [
#0 = FieldInfo [getAccessFlags()=FieldAccessFlags [getAccessFlags()=0x2, getAccessFlagsString()=[ACC_PRIVATE]], getNameIndex()=7, getName()=name, getDescriptorIndex()=8, getDescriptor()=Ljava/lang/String;, getAttributesCount()=0, getAttributes()=[]]
#1 = FieldInfo [getAccessFlags()=FieldAccessFlags [getAccessFlags()=0x2, getAccessFlagsString()=[ACC_PRIVATE]], getNameIndex()=9, getName()=color, getDescriptorIndex()=10, getDescriptor()=I, getAttributesCount()=0, getAttributes()=[]]
#2 = FieldInfo [getAccessFlags()=FieldAccessFlags [getAccessFlags()=0x2, getAccessFlagsString()=[ACC_PRIVATE]], getNameIndex()=11, getName()=weight, getDescriptorIndex()=12, getDescriptor()=D, getAttributesCount()=0, getAttributes()=[]]
]

 

 

 

(九)

 

小白說,“方法呢肯定也有自己的格式,你把它找出來我看看”。

“好好,我這就找”,編譯器苦笑到。我堂堂一個編譯器,今天竟然成了小白的助手,慚愧啊。

說著編譯器就找到了,於是放到了桌子上:

“咦,怎麼和字段的一模一樣”,小白到。那這就更簡單了。

先是訪問控制標誌,接着是方法名稱索引,然後是方法描述索引,最後是和方法關聯的屬性。於是照貓畫虎,小白就開始了。

先讀到public關鍵字,這是個訪問控制修飾符,肯定也有一張表和它對應,可以找到這個關鍵字對應的數值。

還沒等小白開口,編譯器就趕緊把表找出來了:

小白繼續,ACC_PUBLIC對應的值是0x0001,就把這個值先保存起來。

然後是方法的名字,getName,是一個字符串,照例把它存入常量池,並且有一個索引,就是#26。

接着該方法的描述了,小白認為方法和字段是不同的,除了有返回類型之外,還有參數呢,這該咋整呢?

於是就問編譯器,“方法的描述應該也有格式吧”?

“你越來越聰明了”,編譯器說到,“其實也很簡單,我來簡單說下吧”。

“在Java中如果把訪問控制符、方法名、參數名、方法體都去掉,其實就剩下‘方法簽名’了”。

例如,沒有入參沒有返回值的,就是這個樣子,void()。

返回值為String,入參為int,double,String的,其實就是這樣個子,String(int, double, String)。

“這個方法簽名其實就是在Java中對方法的描述,在字節碼中和它差不多,就是把返回類型放到後面,把參數間的逗號去掉”。

因此void()映射為()V,這裏要注意的是void對應的是大寫字母V。

String(int, double, String)映射為(IDLjava/lang/String;)Ljava/lang/String;

“不難,不難”,小白說到,於是又繼續開始了。

小白按照這種格式,把剛剛的那個方法描述也存入了常量池,得到的索引就是#27。

小白按這個套路把6個方法都整理好了,接下來該按格式把數據寫入字節數組了。

編程新說注:方法的代碼對應的是JVM的指令,這裏就忽略不談了,後續可能會單獨再說。

編譯器提醒小白說,“你是不是還漏掉了一個方法啊”?

小白又看了一遍Java源碼,仔細數了數,是6個呀,沒錯啊。

編譯器說到,你在學習時有沒有見過這樣一句話,“當類沒有定義構造函數時,編譯器會為它生成一個默認的無參構造函數”。

小白連忙點頭,“嗯嗯嗯,見過的”。

“這就是了”,編譯器說道,“不過需要注意的是,在字節碼中構造方法的名字都是<init>,返回類型都是V”。

“這也是規定的吧”,小白說到,編譯器點了點頭。

編譯器又說到,“其實還有方法的參數信息,如參數位置,參數類型,參數名稱,參數的訪問控制標誌等”。

“這些信息都是放在方法格式里最後的屬性信息中的,咱們也暫時不說它們了”。

編程新說注

在JDK7及以前,字節碼中不包含方法的參數名。因為JVM執行指令時,參數是按位置傳入的,所以參數名對代碼的執行沒有用處。

由於越來越多的框架採用按方法參數名進行數值綁定,Java也只好在JDK8時加入了對參數名的支持。

不過需要設置一下編譯器的–parameters參數,這樣才能把方法參數名也放入字節碼中。

可以看看常量池中的#32是“MethodParameters”字符串,說明字節碼中已經包含參數名了。

常量池中#7、#9、#11三個字符串就是參數名,同時也是字段名,這就是復用的好處。

編程新說注方法的格式和字段的格式完全一樣,就不再演示寫入過程了。

因此這個類共有7個方法。

MethodsCount [getCount()=7]
Methods [
#0 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=13, getName()=<init>, getDescriptorIndex()=14, getDescriptor()=()V, getAttributesCount()=1, getAttributes()=[Code [getMaxStack()=3, getMaxLocals()=1, getCodeLength()=12, getJvmCode()=JvmCode [getCode()=12], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=3, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=8], LineNumTable [getStartPc()=4, getLineNumber()=12], LineNumTable [getStartPc()=11, getLineNumber()=8]]], LocalVariableTable [getLocalVarTableLength()=1, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=12, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0]]]]]]]
#1 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=26, getName()=getName, getDescriptorIndex()=27, getDescriptor()=()Ljava/lang/String;, getAttributesCount()=1, getAttributes()=[Code [getMaxStack()=1, getMaxLocals()=1, getCodeLength()=5, getJvmCode()=JvmCode [getCode()=5], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=1, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=16]]], LocalVariableTable [getLocalVarTableLength()=1, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=5, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0]]]]]]]
#2 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=30, getName()=setName, getDescriptorIndex()=31, getDescriptor()=(Ljava/lang/String;)V, getAttributesCount()=2, getAttributes()=[Code [getMaxStack()=2, getMaxLocals()=2, getCodeLength()=6, getJvmCode()=JvmCode [getCode()=6], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=2, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=21], LineNumTable [getStartPc()=5, getLineNumber()=22]]], LocalVariableTable [getLocalVarTableLength()=2, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0], LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=7, getDescriptorIndex()=8, getIndex()=1]]]]], MethodParameters [getParametersCount()=1, getParameters()=[Parameter [getNameIndex()=7, getAccessFlags()=0x0]]]]]
#3 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=33, getName()=getColor, getDescriptorIndex()=34, getDescriptor()=()I, getAttributesCount()=1, getAttributes()=[Code [getMaxStack()=1, getMaxLocals()=1, getCodeLength()=5, getJvmCode()=JvmCode [getCode()=5], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=1, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=26]]], LocalVariableTable [getLocalVarTableLength()=1, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=5, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0]]]]]]]
#4 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=37, getName()=setColor, getDescriptorIndex()=38, getDescriptor()=(I)V, getAttributesCount()=2, getAttributes()=[Code [getMaxStack()=2, getMaxLocals()=2, getCodeLength()=6, getJvmCode()=JvmCode [getCode()=6], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=2, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=31], LineNumTable [getStartPc()=5, getLineNumber()=32]]], LocalVariableTable [getLocalVarTableLength()=2, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0], LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=9, getDescriptorIndex()=10, getIndex()=1]]]]], MethodParameters [getParametersCount()=1, getParameters()=[Parameter [getNameIndex()=9, getAccessFlags()=0x0]]]]]
#5 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=11, getName()=weight, getDescriptorIndex()=39, getDescriptor()=()D, getAttributesCount()=1, getAttributes()=[Code [getMaxStack()=2, getMaxLocals()=1, getCodeLength()=5, getJvmCode()=JvmCode [getCode()=5], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=1, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=35]]], LocalVariableTable [getLocalVarTableLength()=1, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=5, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0]]]]]]]
#6 = MethodInfo [getAccessFlags()=MethodAccessFlags [getAccessFlags()=0x1, getAccessFlagsString()=[ACC_PUBLIC]], getNameIndex()=11, getName()=weight, getDescriptorIndex()=40, getDescriptor()=(D)V, getAttributesCount()=2, getAttributes()=[Code [getMaxStack()=3, getMaxLocals()=3, getCodeLength()=6, getJvmCode()=JvmCode [getCode()=6], getExceptionTableLength()=0, getExceptionTables()=[], getAttributesCount()=2, getAttributes()=[LineNumberTable [getLineNumTableLength()=2, getLineNumTables()=[LineNumTable [getStartPc()=0, getLineNumber()=39], LineNumTable [getStartPc()=5, getLineNumber()=40]]], LocalVariableTable [getLocalVarTableLength()=2, getLocalVarTables()=[LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=24, getDescriptorIndex()=25, getIndex()=0], LocalVarTable [getStartPc()=0, getLength()=6, getNameIndex()=11, getDescriptorIndex()=12, getIndex()=1]]]]], MethodParameters [getParametersCount()=1, getParameters()=[Parameter [getNameIndex()=11, getAccessFlags()=0x0]]]]]
]

編程新說注方法部分的輸出內容很多,是因為包含了方法體的代碼的信息。

 

 

(十)

 

“真是後生可畏啊”,編譯器感慨到。“小白竟然也能按照套路去在做點事情了”。

不過編譯器並不自危,因為最核心的內容是,可執行代碼如何轉換為JVM指令集中的指令,這可是“壓箱底”的乾貨,可不能隨便告訴別人,長得再好看也不行。哈哈,O(∩_∩)O。

接着編譯器拿出一個完整的字節碼文件格式圖給小白看:

小白看完后說,“和剛剛講的一樣,只是最後也有這個屬性信息啊”。

編譯器說,“屬性信息是字節碼文件中非常複雜的內容,可以暫時不管用了”。

上面已經說了,至少註解的相關內容是放在屬性信息里的。

那就看看你寫的這個類的屬性信息都是什麼吧:

AttributesCount [getCount()=2]
Attributes [
#0 = SourceFile [getSourcefileIndex()=42]
#1 = RuntimeVisibleAnnotations [getNumAnnotations()=1, getAnnotations()=[Annotation [getTypeIndex()=44, getNumElementValuePairs()=1, getElementValuePairs()=[ElementValuePair [getElementNameIndex()=7, getElementValue()=ElementValue [getTag()=ElementValueTag [getTagChar()=s], getUnion()=ElementValueUnion [getConstValueIndex()=45]]]]]]]
]

編譯器繼續說,共有2條屬性信息,第一條是源代碼文件的名字,在常量池中的#42。其實就是Apple.java了。

第二條是運行時可見的註解信息,本類共有1個註解,註解類型是常量池中的#44。其實就是Lorg/cnt/java/Health;了。

該註解共顯式設置了1對屬性值。屬性名稱是常量池中的#7,就是name了,類型是小寫的s,表示String類型,屬性值是#45,也就是“健康水果”了。

下圖中的這些類型,都是可以用於註解屬性的類型:

最後,編譯器打印出一行信息:

—–bytes=1085—–

小白說,“這是什麼意思”?“這是編譯后產生的字節碼的總長度,是1085個字節”,編譯器到。

小白剛想表達對編譯器的感謝,忽然聞到一陣香味,而且是肉香。

PS:最後幾句話就不寫了,請你來補充完整吧,嘻嘻。

 

 

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

【面試】我是如何面試別人List相關知識的,深度有點長文

我是如何在畢業不久只用1年就升為開發組長的

爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

【面試】我是如何在面試別人Spring事務時“套路”對方的

【面試】Spring事務面試考點吐血整理(建議珍藏)

【面試】我是如何在面試別人Redis相關知識時“軟懟”他的

【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)

【面試】如果你這樣回答“什麼是線程安全”,面試官都會對你刮目相看(建議珍藏)

【面試】迄今為止把同步/異步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)

【面試】一篇文章幫你徹底搞清楚“I/O多路復用”和“異步I/O”的前世今生(深度好文,建議珍藏)

【面試】如果把線程當作一個人來對待,所有問題都瞬間明白了

Java多線程通關———基礎知識挑戰

品Spring:帝國的基石

 

 

 

作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號的二維碼,歡迎關注!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

我對互聯網汽車的一些思考

理想非常美好,現實不確定性很多,唯有務實工作,埋頭苦幹,用釘釘子的精神,把一個又一個的問題解決,一個又一個的難關攻克,不斷夯實技術基礎,不斷積累試驗數據,不斷磨鍊人才隊伍,不斷疊代升級,螺旋發展,才有可能到達勝利的彼岸。

11月16日,以“創新驅動 造福人類——攜手共建網絡空間命運共同體”為主題的第三屆世界互聯網大會在烏鎮開幕,作為參与本屆大會的汽車行業代表,浙江吉利控股集團董事長李書福發表了題為《我對互聯網汽車的一些思考》的署名文章,全文如下:

我對互聯網汽車的一些思考

吉利控股集團董事長 李書福

外行人看熱鬧,內行人看門道。我半路出家,談不上內行,但由於在汽車行業工作幾十年,對所謂的互聯網汽車略有所研究。如果我沒有記錯的話,大概在2007年夏天,有一位中國記者問我:“李總,智能手機改變了傳統電話,在通訊領域發起了一場革命,你認為下一場革命是什麼行業?”我當初回答不出來,我真的不知道是哪個行業。她說:“有人講下一場革命會發生在汽車行業。”

我當初確實沒有想到,也不以為然。當然這位記者也沒有搞清楚這場革命將如何起源,如何發展。而今天,基本上已經有了一個方向,比如電動化、智能化、輕量化,尤其是無人駕駛及人們所謂的互聯網汽車,這些方向在汽車行業內已經被廣泛認同。但如何電動化,電動化程度及水平,如何輕量化,輕量化程度及水平,如何智能化,智能化程度及水平等等問題都沒有標準答案。行業內不同的企業尋求不同的解決方案,大家都在大量投入研發,尤其是在一些基礎技術研發方面的投資是巨大的。

有些技術的研發不僅僅是錢的問題,錢當然重要,但完全靠錢不一定達到目的。比如電動化程度及水平的問題,電動車技術不是新鮮技術,一百多年來一直存在。到今天,有些技術雖然取得了突破,但還有許多技術難題依然找不到解決方案。許多人都說自己的技術世界第一,都說自己的商業模式最有競爭力。但無論怎麼說,汽車電動化這條道路上還有許多困難與問題在等待着我們克服與解決,就是成為“世界第一”了也不一定就能取得成功。

我認為這一場產業革命今天才剛剛開始,離真正的成功還有很大的距離。又比如輕量化的問題,表面上看,電動化與輕量化沒有直接聯繫,但事實上二者關聯非常密切。從商業的本質與產品競爭來講,沒有輕量化就不能電動化,二者密不可分。由於電池的能量密度太低,今天的電動化技術還不能幫助整車解決輕量化的難題,二者是矛盾體。如何解決這個矛盾,全球汽車同行都在做各種嘗試,既要在成本上可行,又要在用戶體驗上有競爭力,雖然已經有了一些科學發明,已經有了一些創新成果,已經有了一些解決方案,在世界各地經常傳來一些神奇的重大技術突破的好消息,但還是需要實踐檢驗。這些檢驗不僅僅是技術可行性,同時還必須要有商業可行性。由於汽車這個產品的特殊性,這種檢驗需要一個漫長而反覆的過程。

探明石油儲量的增加以及全球石油價格的下降,對電動汽車的發展不是好消息,對電動車的成本、經濟性及用戶體驗提出更高要求。由於汽車的電動化與輕量化技術還走在路上,因此我們必須了解行業全貌,共同努力,积極探索,必須明白電動化技術路線存在許多不確定性,需要耐心,切勿急於求成。操之過急可能欲速而不達,只有尊重規律,認清現實,一步一個腳印,循序漸進,才能水到渠成。

互聯網與電動化、輕量化照樣有着先天的關係。汽車工業的發展過程就是工業與信息化技術融合的過程。傳統汽車工業是機電一體化工業,今天的汽車工業是人機、機電、电子信息一體化工業,也就是有些人所謂的互聯網汽車。

所謂互聯網,就是信息技術的網絡化發展,既涉及到汽車本身通訊技術的研發,也涉及到汽車與外部通訊網絡的連接。所謂互聯網汽車,就是把汽車與互聯網有機融合,實現人機交互智能化、機電執行自動化、駕駛路線信息化,最終實現完全無人駕駛,讓汽車成為服務於交通運輸領域的超級機器人。

但是,所有這一切美好追求,都是人們的理想藍圖。什麼時候能夠實現?如何實現?成功率有多高?誰能保證?所有這一切的一切,都需要市場檢驗。理想非常美好,現實不確定性很多,唯有務實工作,埋頭苦幹,用釘釘子的精神,把一個又一個的問題解決,一個又一個的難關攻克,不斷夯實技術基礎,不斷積累試驗數據,不斷磨鍊人才隊伍,不斷疊代升級,螺旋發展,才有可能到達勝利的彼岸。

吉利雖然制定了“藍色吉利行動”計劃,但現在看來需要重新審視,滾動修訂,尤其是美國新一任總統的能源戰略將會對巴黎氣候協定產生影響,從而對汽車行業產生影響,對汽車的電動化進程產生影響。

從理論上講,真正的互聯網汽車必須是高度電動化的汽車,沒有電動化就很難實現互聯網化,也就不可能高度智能化。原因很簡單,電動化有利於能源供給自動化、車輛啟停安全性及長時間待機網絡化的在線服務,但是從此岸到彼岸不能簡單跨越發展,也無法跨越發展,必須按規律發展。

沃爾沃新一代XC90是全球第一輛真正意義上的互聯網概念汽車

有想法的人很多,把想法變成現實卻非常艱難。十多年前沃爾沃就開始研究無人駕駛汽車、電動汽車及輕量化技術。去年沃爾沃XC90新一代車型在全球同步投放市場,其受歡迎程度完全出乎人們的想象,尤其在美國、德國、日本等市場更是供不應求,一車難求,一年內在全球拿了一百多個大獎,這麼一輛沃爾沃XC90,為何如此受歡迎呢?原因有以下幾條:

一、全球第一輛真正意義上的互聯網概念汽車,並且具備持續迭代升級的基礎能力;

二、已經實現0-130公里高度輔助駕駛功能,可以讓司機駕車更加輕鬆;

三、車內空氣質量達到兒童級別標準,不僅過濾了pM2.5,而且確保一氧化氮、一氧化碳、碳氧化合物、甲醛、花粉等各種有害物質得到有效控制;

四、車內音響設計達到歌劇院標準,舉世無雙;

五、安全水平全球遙遙領先;

六、2.0的排氣量油耗,相當於4.0功率、扭矩。

基於可疊代升級的SpA基礎架構開發的沃爾沃XC90,雖然走在行業前列,獲得市場的廣泛認可,但追兵與模仿者也不在少數,其競爭對手紛紛解剖、分析研究XC90,同行開發的類似產品也陸續推向市場,參与競爭。因此,如何更快更好升級疊代,始終擺在汽車行業的面前。

CMA模塊架構具有很強的疊代升級屬性

2013年,吉利歐洲研發中心成立,沃爾沃主導與協同吉利研發CMA基礎架構模塊,這種模塊的最大特點就是靈活、軟硬件都可疊代升級,可以搭載純電、油電、插電,也可以搭載傳統能源,电子架構技術領先,人機交互智能,最終可升級為完全無人駕駛。吉利汽車、沃爾沃汽車兩個汽車公司都可以使用CMA基礎架構模塊進行各自汽車產品的研發設計,雙方的汽車產品標準不同,設計不同,配置不同,消費群體不同。簡單地講,CMA架構就是吉利汽車公司與沃爾沃汽車公司聯合打造的互聯網汽車基礎架構,與傳統汽車底盤概念完全不同。吉利汽車基於CMA基礎架構推出的全新品牌LYNK & CO已於上月在歐洲發布,媒體和消費者給與了高度評價,該品牌下首款車型將於明年第四季度面世。

吉利汽車旗下全新品牌LYNK&CO應互聯網而生

我可以毫不誇張地說,沃爾沃汽車公司的自動駕駛技術是當今世界上最先進的技術,沃爾沃公司研發的XC90、S90汽車產品是全球唯一大批量商業化的高度輔助駕駛汽車,實現了時速0-130公里的智能輔助駕駛。自動駕駛的目的首先是為了安全,無論是電動汽車還是傳統汽車,安全是最基本的前提,這一點全世界沒有第二個汽車公司可以與沃爾沃相抗衡。美國優步與沃爾沃合作開發無人車並由沃爾沃提供基礎車型;全球知名汽車安全零部件公司奧特利夫與沃爾沃合作,聯合研發完全無人駕駛技術等等,都足以說明沃爾沃在無人駕駛技術方面的全球地位。即便這樣,我們依然認為前方的道路充滿了挑戰。

S90已經實現時速0-130公里的智能輔助駕駛

變革是需要條件的,革命是需要環境的。汽車行業的變革雖然已經開始,但條件還需要繼續成熟,環境還需要不斷改善。汽車行業的變革是需要時間的,而且可能反覆並充滿不確定性。理想可以吹得天花亂墜,可以充滿無窮想象,但現實依然殘酷無情。商場就是戰場,眼淚不能解決問題。吉利造汽車是從自己投資建設技師學院及工程師學院開始培養人才的,近二十年來在技師、工程師培養上大量投入。沃爾沃汽車公司雖然做了大量基礎性工作,雖然在汽車及交通領域互聯互通技術上取得了大量研究成果,但以後的路還很長,面臨的挑戰依然非常嚴峻。當然,我們充滿信心,我們堅信,世上無難事,只要肯登攀。

最後,我想用一句千年古訓與大家共勉:“不積跬步,無以至千里;不積小流,無以成江海。”本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準