台灣特斯拉售價公布,Model S台幣306萬元起跳

特斯拉的台灣櫃點曝光,官網上也更新了台灣車款訊息以及價格。經典車款Model S最低價約為新台幣306.1萬元,而Model X與Model 3的價格則仍待公布。台灣使用者也可購買Powerwall產品。

根據台灣特斯拉所揭露的訊息,Model S車款共分成:60(60kWh電池、後輪驅動)、75(75kWh電池、後輪驅動)、90D(90kWh電池、全時四輪驅動)以及P90D(90kWh Performance電池、全時四輪驅動)等四款,含稅、不含政府補貼的現金售價由新台幣306.1萬元起跳,最昂貴的P90D現金價為502萬元。車主亦可加價選配Autopilot輔助駕駛功能、Ultra Hi-Fi音響等配備。

車款的配備與續航力等訊息也在網站上公開,續航力最佳的90D可行駛557公里。而特斯拉也同步公開油費與牌照稅的節省訊息,Model S每款車輛在五年的行駛當中,平均可節省新台幣6.7萬元。

備受矚目的平價車款Model 3台灣定價預計在2017年公布,有興趣的車主現可支付3.3萬元預購,車輛將在2017年下半年從美國開始陸續交車。若比對Model S 60的美國版定價6.6萬元美金,美國定價3.5萬美金的Model 3,台灣版定價可能會超過新台幣150萬。

台灣客戶可開始在台灣特斯拉官網上預購「能源牆」儲能電池。(來源:Tesla Taiwan)

此外,特斯拉亦將開始在台灣布建超級充電站,主要將設置於主幹道、人口密集區或特定商業場所。台灣使用者也可開始預購儲能系統6.4kWh版的Powerwall(正式定名「能源牆」),以供家庭或搭配電動車使用,亦可整合太陽能板。

(首圖:台灣特斯拉官網 Model S 車款介紹。翻攝台灣特斯拉官網)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

嚴凱泰:特斯拉「來就來吧」

裕隆集團旗下裕隆日產今(30)日召開股東會,由董事長嚴凱泰親自主持,會前媒體問及他對車市、以及特斯拉來台,他表示,雖然國內景氣不佳,但因有汰舊換新政策,預期今年車市仍會微幅成長;至於特斯拉來台設銷售據點,他則表示,電動車是發展趨勢,只是全球的基礎建設尚未完備,油電混合車仍會是主流,但看好長期趨勢,裕隆也是會全力發展。   嚴凱泰今年首度缺席裕隆股東會,引發市場對他健康的臆測,上周他首度出席中華車股東會,今天再親自主席裕日車股東會,精神奕奕回答媒體問題,針對車市,他維持微幅成長的看法。   至於特斯拉將來台設銷售公司,搶攻台灣的汽車市場,嚴凱泰則說:台灣是自由市場,「來就來吧」,他認為在全球基礎建設還未到位下,市場仍以油電車為主,而裕隆集團仍會投入電動車發展。   而針對近期勞資關心的「罷工」、「一例一休」、「加班」等議題,他均以「沒有研究回應」。   (本文內容由 授權轉載;首圖來源: CC BY 2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

特斯拉出貨不順,第二季交車輛不如預期

全球電動車龍頭廠商特斯拉(Tesla)雖然討論度極高,旗下各款電動車產品的訂單量也大,但交車量卻不如預期,已是連續第三季度挑戰交車目標失敗。

相關報導指出,特斯拉今年第二季交車量合計約14,370輛,不只低於原先預期的17,000輛,也比第一季的14,810輛還少了3%左右。這已是特斯拉連續第三季實際交車量低於目標。

特斯拉表示,交車量不如預期的原因是因為產能擴張太快、且電動休旅車Model X生產延誤。特斯拉亦表示,目前仍有約5,150輛車正在運送途中,預計在第三季初陸續交付客戶,並將計入第三季交車量。

為供應大量訂單,特斯拉近期積極擴產,預計今年下半年共可交車5萬輛;但全年預計7.9萬輛的交車量,仍將低於原先預期的8~9萬輛。今年稍早,特斯拉執行長Elon Musk曾表示要在2018年前將年產能提高至50萬輛;但近期的出貨狀況再次引發市場懷疑。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

彰化縣祭出電動機車補助,加速改善空氣品質

彰化縣長年受空污所苦,縣府為改善空氣品質,祭出最高新台幣1.9萬元的電動機車購車補助,希望能加速淘汰縣內2.5萬輛老舊機車改用電動車,以減輕空污排放。

彰化縣環保局統計,縣內目前領牌機車共有81.4萬輛,其中有約24.9萬輛是出廠超過12年的二行程老車,空污影響相當大。環保局為減輕PM2.5排放,推出多項補貼措施,目標在今年汰換2.5萬輛老車改用電動車。若能完成目標,每年可減少13公噸PM2.5排放,對縣內空氣環境改善大有助益。

縣府推出的補貼措施依照汰換產品與新購置的電動機車有所不同。汰換二行程機車補貼新台幣1,500元,汰換舊車後新購機車,小型與輕型再補助16,300元、換購重型電動機車則補助19,100元,相當於新車價格的三分之一。

今年前半年,彰化民眾已汰換12,806輛二行程機車,民眾反映普遍良好。但也有民眾希望政府進一步補助電池更換的費用。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步”網站設計“幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

鴻海電動車團隊成軍,2020年推出新車款

中國騰訊集團、和諧汽車與台灣背景的鴻海集團共同成立電動車商產生Future Mobility,並預計在今年第三季完成首輪融資、陸續增聘人手,計畫在2020年前推出旗下首款智能電動車。

外媒報導,Future Mobility的聯合創始人暨營運長Daniel Kirchert挖角自中國Infiniti。Kirchert打算將公司員工從目前的50人擴增到明年底的600人規模,並表示將在中國大陸生產旗下電動車。公司亦正與中國與香港上市公司商討融資,預計在第三季就會完成首輪融資,未來也會考慮與歐美投資者合作。

Future Mobility是鴻海、騰訊、和諧汽車於2015年共同成立的電動車公司「和諧富騰」旗下之新創公司。和諧富騰由騰訊、鴻海分別持股29.4%,其餘由和諧汽車持股,致力於推動「互聯網+智能電動車」,並已從BMW借將Carsten Breitfeld擔任CEO。

新車價格與Audi A4、BMW i3相等

Kirchert指出,Future Mobility的首輛智能電動車款預計最晚將在2020年亮相,初步規劃定價為30萬元人民幣左右,約與Audi A4、BMW i3同一等級。新車將在中國大陸生產,但目前還未決定要自建廠房或與其他車廠合作。

另一方面,富士康、騰訊、和諧汽車以簽署協議,將在鄭州投資人民幣10億元成立公司,以布局智慧電動車級互聯網相關專案工作。

鴻海除積極入主電動車事業之外,和諧汽車亦於去年底取得河南和諧旗下鋰電池汽車公司綠野汽車之55%股權給和諧富騰,鴻海相當於間接掌握里電池電動車的事業。加上鴻海已在鄭州、杭州、北京布局新能源汽車租賃服務,從鋰電池到電動車事業皆已有策畫。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

騙補者或被取消資質 新能源補貼新政欲出

中國新能源汽車騙補事件結果即將出爐。日前,國務院方已經就新能源汽車推廣應用督查報告作出批示,要求嚴肅懲處市場騙補行為,同時完善相關補貼制度。  
 
部分騙補車企或被取消資質 今年初,部分車企被爆造假資料,騙取中國國家財政補貼,形成了新能源汽車騙補產業鏈,引起外界一片譁然。隨後,工信部、財政部、科技部以及 發改委聯合發佈《關於開展新能源汽車推廣應用核查工作的通知》,通過自查與現場督查的方式,調查全部車輛生產企業以及新能源汽車運營企業(含公交、客運、專用車等)、租賃企業、企事業單位等新能源汽車用戶,全面核查財政資金使用及管理情況、新能源汽車生產與使用情況。   目前,中國界定騙補和違規謀補方式主要有三種,包括車輛未達到推廣標準甚至未生產,違規取得牌照騙取補貼;車輛符合規定,但賣給關聯企業而非終端使用者,未達到補貼條件提前謀取補貼;車輛賣給終端使用者,但在獲取補貼後大量閒置,造成財政資金嚴重浪費。   同時,根據車企的違規情節採取不同的措施,包括取消財政補貼資格,追回補貼資金、罰款、取消汽車生產資質,將問題車型從推薦車型目錄中剔除,等等。  
新一輪補貼政策即將推出 目前,按照中’國的補貼政策,新能源汽車補貼分為國家補貼和地方補貼兩大類,而大部分新能源汽車示範推廣城市的地方補貼標準與國家1:1配比。高額的政策性補貼,不僅成為了新能源生產企業的主要利潤來源,也是新能源汽車推廣的重要動力。   分析人士指出,打擊騙補為未來新能源車發展奠定較好的補貼環境基礎。而出臺更為完善的政策,將是新能源汽車未來可持續性發展的關鍵,才能讓大市場刺激強勢產品和讓優秀企業脫穎而出。   據悉,隨著騙補調查結果的出爐,新一輪的新能源補貼政策也將隨之出爐。  
文章來源:南方日報(中國大陸)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

類加載器 – 類的加載、連接與初始化

類的加載、連接與初始化

概述

在Java代碼中,類型的加載、連接與初始化過程都是在程序運行期間完成的

  • 類型:可以理解為一個class
  • 加載:查找並加載類的二進制數據,最常見的情況是將已經編譯完成的類的class文件從磁盤加載到內存中
  • 連接:確定類型與類型之間的關係,對於字節碼的相關處理
    • 驗證:確保被加載的類的正確性
    • 準備:為類的靜態變量分配內存,並將其初始化為默認值。但是在到達初始化之前,類變量都沒有初始化為真正的初始值
    • 解析:在類型的常量池中尋找類、接口、字段和方法的符號引用,把這些符號引用轉換為直接引用的過程
  • 初始化:為類的靜態變量賦予正確的初始值
  • 使用:比如創建對象,調用類的方法等
  • 卸載:類從內存中銷毀

理解:public static int number = 666;

上面這段代碼,在類加載的連接階段,為對象number分配內存,並初始化為0;然後再初始化階段在賦予正確的初始值:666

類的使用方式

Java程序對類的使用方式可分為兩種

  • 主動使用
    • 創建類的實例
    • 訪問某個類或接口的靜態變量,或者對靜態變量賦值
    • 調用類的靜態方法
    • 反射
    • 初始化類的子類
    • Java虛擬機啟動時被標明為啟動類的類(包含main方法)
    • JDK1.7開始提供的動態語言支持(java.lang.invoke.MethodHandle實例的解析結果REF_getStatic,REF_putStatic,REF_invokeStatic句柄對應的類沒有初始化,則初始化)
  • 被動使用
    • 除了主動使用的七種情況之外,其他使用Java類的方法都被看作是對類的被動使用,都不會導致類的初始化

所有的Java虛擬機實現必須在每個類或接口被Java程序“首次主動使用”時才初始化他們

代碼理解

示例一:類的加載連接和初始化過程

代碼一

public class Test01 {
    public static void main(String[] args) {
        System.out.println(Child01.str);
    }
}

class Father01 {
    public static String str = "做一個好人!";
    static {
        System.out.println("Father01 static block");
    }
}

class Child01 extends Father01 {
    static {
        System.out.println("Child01 static block");
    }
}

運行結果做一個好人!

Father01 static block
做一個好人!

代碼二

public class Test01 {
    public static void main(String[] args) {
        System.out.println(Child01.str2);
    }
}

class Father01 {
    public static String str = "做一個好人!";

    static {
        System.out.println("Father01 static block");
    }
}

class Child01 extends Father01 {
    public static String str2 = "做一個好人!";
    static {
        System.out.println("Child01 static block");
    }
}

運行結果

Father01 static block
Child01 static block
做一個好人!

分析:

  • 代碼一中,我們通過子類調用父類中的str,這個str是在父類被定義的,對Father01主動使用,沒有主動使用Child01,故Child01的靜態代碼塊沒有執行,父類的靜態代碼塊被執行了。 -> 對於靜態字段來說,只有直接定義了該字段的類才會被初始化。
  • 代碼二中,對Child01主動使用;根據主動使用的7種情況,調動類的子類時,其所有的父類都會被先初始化,所以Father01會被初始化。 -> 當一個類初始化時,要求其父類全部都已經初始化完畢了。

以上驗證的是類的初始化情況,那麼如何驗證類的加載情況呢,可以通過在啟動的時候配置虛擬機參數:-XX:+TraceClassLoading查看

運行代碼一,查看輸出結果,可以看見控制台打印了very多的日誌,第一個加載的是java.lang.Object類(不管加載哪個類,他的父類一定是Object類),後面是加載的一系列jdk的類,他們都位於rt包下。往下查看,可以看見Loaded classloader.Child01,說明即使沒有初始化Child01,但是程序依然加載了Child01類。

[Opened /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded java.lang.Object from /usr/local/jdk1.8/jre/lib/rt.jar]
...
[Loaded java.lang.Void from /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded classloader.Father01 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
[Loaded classloader.Child01 from file:/home/fanxuan/Study/java/jvmStudy/out/production/jvmStudy/]
Father01 static block
做一個好人!
[Loaded java.lang.Shutdown from /usr/local/jdk1.8/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /usr/local/jdk1.8/jre/lib/rt.jar]

拓展:JVM參數介紹

因為前一章節使用了JVM參數,所以對其做一下簡單的介紹

  • 所有的JVM參數都是以-XX:開頭的
  • 如果形式是:-XX:+<option>,表示開啟option選項
  • 如果形式是:-XX:-<option>,表示關閉option選項
  • 如果形式是:-XX:<option>=<value>,表示將option選項的值設置為value

示例二:常量的本質含義

public class Test02 {
    public static void main(String[] args) {
        System.out.println(Father02.str);
    }
}

class Father02{
    public static final String str = "做一個好人!";

    static {
        System.out.println("Father02 static block");
    }
}

執行結果

做一個好人!

分析

可以看見,此段代碼並沒有初始化Father02類。這是因為final表示的是一個常量,在編譯階段常量就會被存入到調用這個常量的方法所在的類的常量池當中,本質上,調用類並沒有直接引用到定義常量的類,因此並不會觸發定義常量的類的初始化。在本代碼中,常量str會被存入到Test02的常量池中,之後Test02與Father02沒有任何關係,甚至可以刪除Father02的class文件。

我們反編譯一下Test02類

Compiled from "Test02.java"
public class classloader.Test02 {
  public classloader.Test02();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #4                  // String 做一個好人!
       5: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

第一塊是Test02類的構造方法,第二塊是我們要看的main方法。可以看見3: ldc #4 // String 做一個好人!,此時這個值已經是確定無疑的做一個好人!了,而不是Father02.str,證實了上面說的在編譯階段常量就會被存入到調用這個常量的方法所在的類的常量池當中

拓展:助記符

因前一章節涉及到了助記符,所以介紹下本章節涉及到的助記符及擴展

  • ldc:表示將int、float或String類型的常量值常量池中推送至棧頂
  • bipush:表示將單字節(-128 -至 127)的常量推送至棧頂
  • sipush:表示將一個短整形(-32768 至 32767)的常量推送至棧頂
  • iconst_1:表示將int類型的1推送至棧頂(這類助記符只有iconst_m1 – iconst_5七個)

示例三:編譯期常量與運行期常量的區別

public class Test03 {
    public static void main(String[] args) {
        System.out.println(Father03.str);
    }
}

class Father03 {
    public static final String str = UUID.randomUUID().toString();

    static {
        System.out.println("Father03 static block");
    }
}

運行結果

Father03 static block
a60c5db4-2673-4ffc-a9f0-2dbe53fae583

分析

本代碼與示例二的區別在於str的值是在運行時確認的,而不是編譯時就確定好的,屬於運行期常量,而不是編譯期常量。當一個常量的值並非編譯期間確定的,那麼其值就不會被放到調用類的常量池中,這時在程序運行時,會導致主動使用這個常量所在的類,導致這個類被初始化。

示例四:數組創建本質

代碼一

public class Test04 {
    public static void main(String[] args) {
         Father04 father04_1 = new Father04();
        System.out.println("-----------");
        Father04 father04_2 = new Father04();
    }
}

class Father04 {
    static {
        System.out.println("Father04 static block");
    }
}

運行結果

Father04 static block
-----------

分析

  • 創建類的實例時,會初始化類
  • 所有的Java虛擬機實現必須在每個類或接口被Java程序“首次主動使用”時才初始化他們

代碼二

public class Test04 {
    public static void main(String[] args) {
        Father04[] father04s = new Father04[1];
        System.out.println(father04s.getClass());
    }
}

運行結果

class [Lclassloader.Father04;

分析

  • 創建數組對象不再主動使用的7種情況內,屬於被動使用,故不會初始化Father04
  • 打印father04s的類型為[Lclassloader.Father04,這是虛擬機在運行期生成的。 -> 對於數組示例來說,其類型是有JVM在運行期動態生成的,表示為[Lclassloader.Father04這種形式,動態生成的類型,其父類就是Object。
  • 對於數組來說,JavaDoc經常將構成數組的元素為Component,實際上就是將數組降低一個維度后的類型

反編譯一下:

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: anewarray     #2                  // class classloader/Father04
       4: astore_1
       5: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
       8: aload_1
       9: invokevirtual #4                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
      12: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      15: return
  • anewarray:表示創建一個引用類型(如類、接口、數組)的數組,並將其引用值值壓入棧頂
  • newarray:表示創建一個指定的原始類型(如int、float、char等)的數組,並將其引用值壓入棧頂

示例五:接口的加載與初始化

代碼一

public class Test05 {
    public static void main(String[] args) {
        System.out.println(Child05.j);
    }
}

interface Father05 {
    int i = 5;
}

interface Child05 extends Father05 {
    int j = 6;
}

運行結果

6

分析

  • 接口中定義的常量本身就是public、static、final的
  • 結果顯而易見,這時我們刪除掉Father05.class文件和Child05.class文件,程序依然可以正常運行
    • 接口中的常量本身就是final常量,會被加載到Test05的常量池中
    • 此時,Father05和Child05都不會被加載

代碼二

public class Test05 {
    public static void main(String[] args) {
        System.out.println(Child05.j);
    }
}

interface Father05 {
    int i = 5;
}

interface Child05 extends Father05 {
    int j = new Random().nextInt(8);
}

運行結果

6

將Father05.class文件刪除,運行結果

Exception in thread "main" java.lang.NoClassDefFoundError: classloader/Father05
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at classloader.Test05.main(Test05.java:15)
Caused by: java.lang.ClassNotFoundException: classloader.Father05
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    ... 13 more

分析

  • 只有在真正使用到父接口的時候(如引用接口中所定義的常量時),才會加載初始化

代碼三

public class Test05 {
    public static void main(String[] args) {
        System.out.println(Child05.j);
    }
}

interface Father05 {
    Thread thread = new Thread() {
        {
            System.out.println("Father05 code block");
        }
    };
}

class Child05 implements Father05 {
    public static int j = 8;
}

運行結果

8

分析

  • 在初始化一個類時,並不會先初始化他所實現的接口

代碼四

public class Test05 {
    public static void main(String[] args) {
        System.out.println(Father05.thread);
    }
}

interface GrandFather {
    Thread thread = new Thread() {
        {
            System.out.println("GrandFather code block");
        }
    };
}

interface Father05 extends GrandFather{
    Thread thread = new Thread() {
        {
            System.out.println("Father05 code block");
        }
    };
}

運行結果

Father05 code block
Thread[Thread-0,5,main]

分析

  • 在初始化一個接口時,並不會先初始化他的父接口

示例六:類加載器準備階段和初始化階段

代碼一

public class Test06 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();

        System.out.println("i:" + Singleton.i);
        System.out.println("j:" + Singleton.j);
    }
}

class Singleton {
    public static int i;

    public static int j = 0;

    private static Singleton singleton = new Singleton();

    private Singleton() {
        i ++;
        j ++;
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

運行結果

i:1
j:1

分析

首先Singleton.getInstance();進入SingletongetInstance方法,getInstance會返回Singleton的實例,Singleton的實例是new Singleton();出來的,因此調用了自定義的私有構造方法。在調用構造方法之前,給靜態變量賦值,i默認賦值為0,j顯式的賦值為0,經過構造函數之後,值都為1。

代碼二

public class Test06 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();

        System.out.println("i:" + Singleton.i);
        System.out.println("j:" + Singleton.j);
    }
}

class Singleton {
    public static int i;

    private static Singleton singleton = new Singleton();

    private Singleton() {
        i ++;
        j ++;
    }

    public static int j = 0;

    public static Singleton getInstance() {
        return singleton;
    }
}

運行結果

i:1
j:0

分析

程序主動使用了Singleton類,準備階段對類的靜態變量分配內存,賦予默認值,下面給出類在連接及初始化階段常量的值的變化

  • i : 0
  • singleton:null
  • j : 0
  • getInstance:初始化
    • i:0
    • singleton:調用構造函數
      • i:1
      • j:1
    • j:0【覆蓋了之前的1】

故返回的值i為1,j為0

深入解析類的加載、連接與初始化

類的加載

  • 將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,然後再內存中創建一個java.lang.Class對象(規範並未說明Class對象位於哪裡,HotSpot虛擬機將其放在了方法區中)用來封裝類在方法區的數據結構
  • 加載.class文件的方式
    • 從本地系統直接加載
    • 通過網絡下載.class文件
    • 從zip等歸檔文件中加載.class文件
    • 從專有數據庫中提取.class文件
    • 將Java源代碼動態編譯為.class文件
  • 類的加載的最終產品是位於內存中的Class對象
  • Calss對象封裝了類在方法區內的數據結構,並且向Java程序員提供了訪問方法區內的數據結構的接口
  • 有兩種類型的類加載器
    • Java虛擬機自帶的類加載器
      • 根類加載器(Bootstrap):該加載器沒有父加載器。他負責加載虛擬機的核心類庫,如java.lang.*等。根類加載器從系統屬性sun.boot.class.path所指定的目錄中加載類庫。根類加載器的實現依賴於底層操作系統,呀沒有繼承java.lang.CalssLoader類
      • 擴展類加載器(Extension):父加載器為根加載器。從java.ext.dirs系統屬性所指定的目錄中加載類庫,或者從JDK的安裝目錄的jre\lib\ext子目錄(擴展目錄)下加載類庫,如果用戶創建的JAR文件放在這個目錄下,也會自動由擴展類加載器加載。擴展類加載器是純Java類,是java.lang.ClassLoader類的子類
      • 系統(應用)類加載器(System):父加載器為擴展加載器。從環境變量classpath或者系統屬性java.class.path所指定的目錄中加載類,是用戶自定義類加載器的默認父加載器。系統類加載器是純Java類,是java.lang.ClassLoader類的子類
    • 用戶自定義的類加載器
      • java.lang.ClassLoader的子類
      • 用戶可以定製類的加載方式
  • 類加載器並不需要等到某個類被“首次使用”時再加載他
  • JVM規範允許類加載器在預料某個類將要被使用時就預先加載他,如果在預先加載的過程中遇到了.class文件缺失或存在錯誤,類加載器必須在程序首次主動使用時才報告錯誤(LinkageError錯誤)。如果這個類一直沒有被程序主動使用,那麼類加載器就不會報告錯誤

類的連接

類被加載后,就進入連接階段。連接就是將已經讀入到內存中的類的二進制數據合併到虛擬機的運行時環境中去

類的驗證

類的驗證的內容

  • 類文件的結構檢查
  • 語義檢查
  • 字節碼驗證
  • 二進制兼容性驗證

類的準備

在準備階段,Java虛擬機為類的靜態變量分配內存,並設置默認的初始值。例如對於下面的Sample類,在準備階段,將為int類型的靜態變量i分配4個字節的內存空間,並且賦默認值0;為long類型的靜態變量j分配8個字節的內存空間,並賦予默認值0

public class Sample {
    private static int i = 8;
    private static long j = 8L;
    ......
}

類的初始化

在初始化階段,Java虛擬機執行類的初始化語句,為類的靜態變量賦予初始值。在程序中,靜態變量的初始化有兩種途徑:

  • 在靜態變量的聲明處初始化
  • 在靜態代碼塊中初始化

靜態變量的聲明語句,預計靜態代碼塊都被看作類的初始化語句,Java虛擬機會按照初始化語句在類文件中的先後順序來依次執行他們

類的初始化步驟

  • 假如這個類還沒有被加載和連接誒,需要先進行加載和連接
  • 假如類存在直接父類,並且這個父類還沒有被初始化,需要先初始化直接父類
  • 假如類中存在初始化語句,需要依次執行這些初始化語句

類的初始化時機

  • 當Java虛擬機初始化一個類時,要求他的所有父類都已經被初始化,但是這條規則並不適用於接口

    • 在初始化一個類時,並不會先初始化他所實現的接口
    • 在初始化一個接口時,並不會先初始化他的父接口

    因此,一個父接口並不會因為他的子接口或實現類的初始化而初始化,只有當程序首次使用特定接口的靜態變量時,才會導致該接口的初始化。代碼參照代碼理解-接口的初始化

  • 只有當程序訪問的靜態變量或者靜態方法確實在當前類或者當前接口中定義時,才認為是對類或接口的主動使用

  • 調用ClassLoader類的loadClass方法加載一個類,並不是對類的主動使用,不會導致類的初始化

拓展部分

類實例化

類的生命周期除了前文提到的加載、連接、初始化之外,還有類示例化,垃圾回收和對象終結

  • 為新的對象分配內存
  • 為實例變量賦予默認值
  • 為實例變量賦予正確的初始值
  • Java編譯器為它編譯的每一個類都至少生成一個實例初始化方法,在Java的class文件中,這個實例初始化方法被稱為<init>。針對源代碼中每一個類的構造方法,Java編譯器都產生一個<init>方法

類的卸載

  • 當一個類被加載、連接和初始化后,它的生命周期就開始了。當代表該類的Class對象不再被引用,即不可觸及時,Class對象就會結束生命周期,這個類在方法區內的數據也會被卸載,從而結束自己的生命周期
  • 一個類何時結束生命周期,取決於代表它的Class對象何時結束生命周期
  • 由Java虛擬機自帶的類加載器所加載的類,在虛擬機的生命周期中,始終不會被卸載。Java虛擬機本身會始終引用這些類加載器,而這類加載器則會始終引用它們所加載的類的Class對象,因此這些Class對象始終是可觸及的;由用戶自定義的類加載器所加載的類是可以被卸載的

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步”網站設計“幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

Windows平台LoadLibrary加載動態庫搜索路徑的問題

一、背景

在給Adobe Premiere/After Effects等後期製作軟件開發第三方插件的時候,我們總希望插件依賴的動態庫能夠脫離插件的位置,單獨存儲到另外一個地方。這樣一方面可以與其他程序共享這些動態庫,還能保證插件安裝時非常的清爽。就Adobe Premiere Pro/After Effects來說,插件文件是放到C:\Program Files\Adobe\Common\Plug-ins\7.0\MediaCore(Windows平台)的。這個是PremiereProAfterEffects的公共插件目錄,二者在啟動的時候都會嘗試去這個位置加載插件。與此同時,我們希望自己開發的插件所依賴的動態庫放到另外的位置,另外也希望插件显示鏈接的動態庫能夠盡量少。因為如果是顯式鏈接的話,這些插件依賴的動態庫必須和插件保存在同一個位置。不然插件找不到這些依賴文件就會加載失敗的。當然,我們也可以在環境變量裏面增加一條路徑,但是這容易污染環境變量,或者與其他的程序庫產生衝突。LoadLibrary在這個時候就產生作用了。LoadLibrary通過將指定路徑的動態庫加載到當前的調用進程,然後獲取其導出的函數就可以正常使用了。對於像第三方插件這樣的應用場景,LoadLibrary可以說是個不錯的實現方式。但是正因此也有個弊端,我們無法使用工具得知其的依賴庫。

二、使用實例

我們在給Adobe Premiere Pro開發的一款插件中,正是使用了這種方法:
(1)首先從註冊表中獲取到我們插件依賴的動態庫文件所在的位置:

 1 bool GetInstallationPath(std::string& result) {
 2     DWORD data_type;
 3     CHAR value[1024];
 4     PVOID pv_data = value;
 5     DWORD size = sizeof(value);
 6     auto err = RegGetValue(HKEY_CLASSES_ROOT, "test_app\\plugin", "install_location", RRF_RT_ANY, &data_type, pv_data, &size);
 7     if (err == ERROR_SUCCESS) {
 8         std::string filepath(value); 
 9         std::regex_replace(std::back_inserter(result), filepath.begin(), filepath.end(), std::regex("[\\\\/]+[^\\\\/]+$"), "");
10         return true;
11     }
12     return false;
13 }

(2)通過調用LoadLibrary來加載指定的依賴庫

std::string    dirname;
if (!GetInstallationPath(dirname)) {
    return false;
}  
SetDllDirectory(dirname.c_str());
insmedia_dll.handle = LoadLibrary("core.dll");

如上述代碼所示,我們的插件唯一依賴的動態庫叫core.dll。而core.dll文件存放的位置記錄在註冊表中。程序先從註冊表中獲取core.dll所在的文件夾,然後設置到DLL的搜索路徑中。最後再調用LoadLibrary加載它。在最初開發及發布后,插件運行的很好。然而,在Adobe發布Premiere Pro CC 2020之後,插件就不工作了。這是為啥呢?根據過往的經驗來看,插件加載不上只有一個原因:依賴的動態庫缺失或者是加載錯了版本。那麼,我們就來看看到底是哪個依賴加載錯了導致插件加載失敗呢?通過在WinDBG裏面調試看到了如下的差異:

看上圖很顯然,我們的插件在加載ffmpeg的庫文件時,先找到了PremierePro安裝根目錄裏面的版本了。而PremierePro使用的ffmpeg版本顯然跟我們不一樣。正是因為這兩個庫的版本不對,導致我們的插件加載失敗了。那麼,LoadLibrary這種方法顯然還是存在一些Bug了。我們的core.dll還依賴OpenCV、ffmpeg等第三方庫。看MSDN的解釋是,LoadLibrary會先從調用進程的目錄下搜索動態庫的依賴。這樣的行為顯然不是我們想要的。這個時候,我們還有個選擇:使用LoadLibraryEx。具體的使用方法仍然一樣,只不過傳給LoadLibraryEx的第一個參數是我們要加載的動態庫的絕對路徑:

 1 std::string    dirname;
 2 if (!GetInstallationPath(dirname)) {
 3     return false;
 4 }  
 5  
 6 std::string absolute_path = dirname + "\\InsMedia.dll";
 7 insmedia_dll.handle = LoadLibraryEx(absolute_path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
 8 if (!insmedia_dll.handle) {
 9     return false;
10 }

注意到第三個參數為LOAD_WITH_ALTERED_SEARCH_PATH,通過指定LOAD_WITH_ALTERED_SEARCH_PATH,讓系統DLL搜索順序從DLL所在目錄開始。這樣就能夠保證加載動態庫的時候優先加載我們打包的動態庫。從而避免因為動態庫加載錯誤導致插件失敗。

從上圖可以看到,所有依賴的動態庫都變成了我們自己提供的庫文件了,插件也能正常加載了。完美!

三、參考鏈接

1. https://blog.csdn.net/cuglifangzheng/article/details/50580279
2. https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

【編程題與分析題】Javascript 之繼承的多種實現方式和優缺點總結

[!NOTE]
能熟練掌握每種繼承方式的手寫實現,並知道該繼承實現方式的優缺點。

原型鏈繼承

    function Parent() {
      this.name = 'zhangsan';
      this.children = ['A', 'B', 'C'];
    }
    Parent.prototype.getName = function() {
      console.log(this.name);
    }
    
    function Child() {
      
    }
    Child.prototype = new Parent();
    var child = new Child();
    console.log(child.getName());

[!NOTE]
主要問題:
1. 引用類型的屬性被所有實例共享(this.children.push(‘name’))
2. 在創建Child的實例的時候,不能向Parent傳參

借用構造函數(經典繼承)

    function Parent(age) {
      this.names = ['zhangsan', 'lisi'];
      this.age = age;
      
      this.getName = function() {
        return this.names;
      }
      
      this.getAge = function() {
        return this.age;
      }
    }
    
    function Child(age) {
      Parent.call(this, age);
    }
    var child = new Child(18);
    child.names.push('haha');
    console.log(child.names);
    
    var child2 = new Child(20);
    child2.names.push('yaya');
    console.log(child2.names);

[!NOTE]
優點:
1. 避免了引用類型的屬性被所有實例共享
2. 可以直接在Child中向Parent傳參
缺點:
方法都在構造函數中定義了,每次創建實例都會創建一遍方法

組合繼承(原型鏈繼承和經典繼承雙劍合璧)

    /**
    * 父類構造函數
    * @param name
    * @constructor
    */
    function Parent(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue'];
    }
    
    Parent.prototype.getName = function() {
      console.log(this.name);
    }
    
    // child
    function Child(name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    
    Child.prototype = new Parent();
    // 校正child的構造函數
    Child.prototype.constructor = Child;
    
    // 創建實例
    var child1 = new Child('zhangsan', 18);
    child1.colors.push('orange');
    console.log(child1.name, child1.age, child1.colors);    // zhangsan 18 (4) ["red", "green", "blue", "orange"]
    
    var child2 = new Child('lisi', 28);
    console.log(child2.name, child2.age, child2.colors);    // lisi 28 (3) ["red", "green", "blue"]

[!NOTE]
優點: 融合了原型鏈繼承和構造函數的優點,是Javascript中最常用的繼承模式

—— 高級繼承的實現

原型式繼承

    function createObj(o) {
      function F(){};
      // 關鍵:將傳入的對象作為創建對象的原型
      F.prototype = o;
      return new F();
    }
    
    // test
    var person = {
        name: 'zhangsan',
        friends: ['lisi', 'wangwu']
    }
    var person1 = createObj(person);
    var person2 = createObj(person);
    
    person1.name = 'wangdachui';
    console.log(person1.name, person2.name);  // wangdachui, zhangsan
    
    person1.friends.push('songxiaobao');
    console.log(person2.friends);       // lisi wangwu songxiaobao

[!WARNING]
缺點:
對於引用類型的屬性值始終都會共享相應的值,和原型鏈繼承一樣

寄生式繼承

    // 創建一個用於封裝繼承過程的函數,這個函數在內部以某種形式來增強對象
    function createObj(o) {
      var clone = Object.create(o);
      clone.sayName = function() {
        console.log('say HelloWorld');
      }
      return clone;
    }

[!WARNING]
缺點:與借用構造函數模式一樣,每次創建對象都會創建一遍方法

寄生組合式繼承

基礎版本

    function Parent(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue'];
    }
    
    Parent.prototype.getName = function() {
      console.log(this, name);
    }
    
    function Child(name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    
    // test1:
    // 1. 設置子類實例的時候會調用父類的構造函數
    Child.prototype = new Parent();
    // 2. 創建子類實例的時候也會調用父類的構造函數
    var child1 = new Child('zhangsan', 18);   // Parent.call(this, name);
    
    
    // 思考:如何減少父類構造函數的調用次數呢?
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    
    // 思考:下面的這一句話可以嗎?
    /* 分析:因為此時Child.prototype和Parent.prototype此時指向的是同一個對象,
            因此部分數據相當於此時是共享的(引用)。
            比如此時增加 Child.prototype.testProp = 1; 
            同時會影響 Parent.prototype 的屬性的。
          如果不模擬,直接上 es5 的話應該是下面這樣吧
          Child.prototype = Object.create(Parent.prototype);*/
    Child.prototype = Parent.prototype;
    
    // 上面的三句話可以簡化為下面的一句話
    Child.prototype = Object.create(Parent.prototype);
    
    
    
    // test2:
    var child2 = new Child('lisi', 24);

優化版本

    // 自封裝一個繼承的方法
    function object(o) {
      // 下面的三句話實際上就是類似於:var o = Object.create(o.prototype)
      function F(){};
      F.prototype = o.prototype;
      return new F();
    }
    
    function prototype(child, parent) {
      var prototype = object(parent.prototype);
      // 維護原型對象prototype裏面的constructor屬性
      prototype.constructor = child;
      child.prototype = prototype;
    }
    
    // 調用的時候
    prototype(Child, Parent)

創建對象的方法

  • 字面量創建
  • 構造函數創建
  • Object.create()
var o1 = {name: 'value'};
var o2 = new Object({name: 'value'});

var M = function() {this.name = 'o3'};
var o3 = new M();

var P = {name: 'o4'};
var o4 = Object.create(P)

原型

  • JavaScript 的所有對象中都包含了一個 __proto__ 內部屬性,這個屬性所對應的就是該對象的原型
  • JavaScript 的函數對象,除了原型 __proto__ 之外,還預置了 prototype 屬性
  • 當函數對象作為構造函數創建實例時,該 prototype 屬性值將被作為實例對象的原型 __proto__

原型鏈

任何一個實例對象通過原型鏈可以找到它對應的原型對象,原型對象上面!

的實例和方法都是實例所共享的。

一個對象在查找以一個方法或屬性時,他會先在自己的對象上去找,找不到時,他會沿着原型鏈依次向上查找。

注意: 函數才有prototype,實例對象只有有__proto__, 而函數有的__proto__是因為函數是Function的實例對象

instanceof原理

判斷實例對象的__proto__屬性與構造函數的prototype是不是用一個引用。如果不是,他會沿着對象的__proto__向上查找的,直到頂端Object。

判斷對象是哪個類的直接實例

使用對象.construcor直接可判斷

構造函數,new時發生了什麼?

   var obj  = {}; 
   obj.__proto__ = Base.prototype;
   Base.call(obj);  
  1. 創建一個新的對象 obj;
  2. 將這個空對象的__proto__成員指向了Base函數對象prototype成員對象
  3. Base函數對象的this指針替換成obj, 相當於執行了Base.call(obj);
  4. 如果構造函數显示的返回一個對象,那麼則這個實例為這個返回的對象。 否則返回這個新創建的對象

類的聲明

// 普通寫法
function Animal() {
  this.name = 'name'
}

// ES6
class Animal2 {
  constructor () {
    this.name = 'name';
  }
}

繼承

借用構造函數法

在構造函數中 使用Parent.call(this)的方法繼承父類屬性。

原理: 將子類的this使用父類的構造函數跑一遍

缺點: Parent原型鏈上的屬性和方法並不會被子類繼承

function Parent() {
  this.name = 'parent'
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

原型鏈實現繼承

原理:把子類的prototype(原型對象)直接設置為父類的實例

缺點:因為子類只進行一次原型更改,所以子類的所有實例保存的是同一個父類的值。
當子類對象上進行值修改時,如果是修改的原始類型的值,那麼會在實例上新建這樣一個值;
但如果是引用類型的話,他就會去修改子類上唯一一個父類實例裏面的這個引用類型,這會影響所有子類實例

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  this.type = 'child'
}

Child.prototype = new Parent();
var c1 = new Child();
var c2 = new Child();
c1.__proto__ === c2.__proto__

組合繼承方式

組合構造函數中使用call繼承和原型鏈繼承。

原理: 子類構造函數中使用Parent.call(this);的方式可以繼承寫在父類構造函數中this上綁定的各屬性和方法;
使用Child.prototype = new Parent()的方式可以繼承掛在在父類原型上的各屬性和方法

缺點: 父類構造函數在子類構造函數中執行了一次,在子類綁定原型時又執行了一次

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

Child.prototype = new Parent();

組合繼承方式 優化1:

因為這時父類構造函數的方法已經被執行過了,只需要關心原型鏈上的屬性和方法了

Child.prototype = Parent.prototype;

缺點:

  • 因為原型上有一個屬性為constructor,此時直接使用父類的prototype的話那麼會導致 實例的constructor為Parent,即不能區分這個實例對象是Child的實例還是父類的實例對象。
  • 子類不可直接在prototype上添加屬性和方法,因為會影響父類的原型

注意:這個時候instanseof是可以判斷出實例為Child的實例的,因為instanceof的原理是沿着對象的__proto__判斷是否有一個原型是等於該構造函數的原型的。這裏把Child的原型直接設置為了父類的原型,那麼: 實例.__proto__ === Child.prototype === Child.prototype

組合繼承方式 優化2 – 添加中間對象【最通用版本】:

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

Child.prototype = Object.create(Parent.prototype); //提供__proto__
Child.prototype.constrctor = Child;

Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__

創建JS對象的多種方式總結

工廠模式

 
    /**
    * 工廠模式創建對象
    * @param name
    * @return {Object}
    */
    function createPerson(name){
        var o = new Object();
        o.name = name;
        o.getName = function() {
          console.log(this.name);
        }
        return o;
    }
    var person = createPerson('zhangsan');
    console.log(person.__proto__ === Object.prototype); // true

缺點:無法識別當前的對象,因為創建的所有對象實例都指向的是同一個原型

構造函數模式

構造函數創建對象基礎版本

    /**
    * 使用構造函數的方式來創建對象
    * @param name
    * @constructor
    */
    function Person(name) {
      this.name = name;
      this.getName = function() {
        console.log(this.name)
      }
    }
    var person = new Person('lisi');
    console.log(person.__proto__ === Person.prototype)

優點:實例剋識別偽一個特定的類型
缺點:每次創建實例對象的時候,每個方法都會被創建一次

構造函數模式優化

    function Person(name) {
      this.name = name;
      this.getName = getName;
    }
    
    function getName() {
      console.log(this.name);
    }
    
    var person = new Person('zhangsan');
    console.log(person.__proto__ === Person.prototype);

優點:解決了每個方法都要被重新創建的問題
缺點:不合乎代碼規範……

原型模式

原型模式基礎版

    function Person(name) {
      
    }
    Person.prototype.name = 'lisi';
    Person.prototype.getName = function() {
      console.log(this.name);
    }
    var person = new Person();
    console.log(Person.prototype.constructor)       // Person

優點:方法不會被重新創建
缺點:1. 所有的屬性和方法所有的實例上面都是共享的;2. 不能初始化參數

原型模式優化版本一

    function Person(name) {
      
    }
    Person.prototype = {
        name: 'lisi',
        getName: function() {
          console.log(this.name);
        }
    }
    var person = new Person();
    console.log(Person.prototype.constructor)       // Object
    console.log(person.constructor == person.__proto__.constructor) // true

優點:封裝性好了一些
缺點:重寫了Person的原型prototype屬性,丟失了原始的prototype上的constructor屬性

原型模式優化版本二

    function Person(name) {
      
    }
    Person.prototype = {
        constructor: Person,
        name: 'lisi',
        getName: function() {
          console.log(this.name)
        }
    }
    var person = new Person();

優點:實例可以通過constructor屬性找到所屬的構造函數
缺點:所有的屬性和方法都共享,而且不能初始化參數

組合模式

    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
        constructor: Person,
        getName: function() {
          console.log(this.name)
        }
    }
    var person = new Person('zhangsan');

優點:基本符合預期,屬性私有,方法共享,是目前使用最廣泛的方式
缺點:方法和屬性沒有寫在一起,封裝性不是太好

動態原型模式

    // 第一種創建思路:
    function Person(name) {
       this.name = name;
       if (typeof this.getName !== 'function') {
           Person.prototype.getName = function() {
             console.log(this.name);
           }
       }
    }
    var person = new Person();

    // 第二種創建的思路:使用對象字面量重寫原型上的方法
    function Person(name) {
      this.name = name;
      if (typeof this.getName !== 'function') {
          Person.prototype = {
              constructor: Person,
              getName: function() {
                console.log(this.name)
              }
          }
          return new Person(name);
      }
    }
    
    var person1 = new Person('zhangsan');
    var person2 = new Person('lisi');
    console.log(person1.getName());
    console.log(person2.getName());
    

寄生構造函數模式

    /**
    * 寄生構造函數模式
    * @param name
    * @return {Object}
    * @constructor
    */
   function Person(name){
        var o = new Object();
        o.name = name;
        o.getName = function() {
          console.log(this.name)
        }
        return o;
   }
   var person = new Person('zhangsan');
   console.log(person instanceof Person);   // false
   console.log(person instanceof Object);   // true
   
   
   // 使用寄生-構造函數-模式來創建一個自定義的數組
   /**
    * 特殊數組的構造器
    * @constructor
    */
   function SpecialArray() {
     var values = new Array();
     /*for (var i = 0, len = arguments.length; i < len; i++) {
         values.push(arguments[i]);
     }*/
     // 開始添加數據(可以直接使用apply的方式來優化代碼)
     values.push.apply(values, arguments);
     
     // 新增的方法
     values.toPipedString = function(){
         return this.join('|');
     }
     
     return values;
   }
   
   // 使用new來創建對象
   var colors1 = new SpecialArray('red1', 'green1', 'blue1');
   // 不使用new來創建對象
   var colors2 = SpecialArray('red2', 'green2', 'blue2');
   
   console.log(colors1, colors1.toPipedString());
   console.log(colors2, colors2.toPipedString());

穩妥構造函數模式

    /**
    * 穩妥的創建對象的方式
    * @param name
    * @return {number}
    * @constructor
    */
    function Person(name){
        var o = new Object();
        o.sayName = function() {
           // 這裡有點類似於在一個函數裏面使用外部的變量
           // 這裏直接輸出的是name
          console.log(name);
        }
        return o;
    }
    var person =  Person('lisi');
    person.sayName();
    person.name = 'zhangsan';
    person.sayName();
    console.log(person instanceof Person);      // false
    console.log(person instanceof Object);      // false

[!NOTE]
與寄生的模式的不同點:1. 新創建的實例方法不引用this 2.不使用new操作符調用構造函數
優點:最適合一些安全的環境中使用
缺點:和工廠模式一樣,是無法識別對象的所屬類型的

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

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

前蘋果資深副總 傳接手電動車計畫

日前國外媒體《The Information》才報導,蘋果的電動車計畫可能順延至 2021 年;而《華爾街日報》引述知情人士的說法,曾任蘋果資深副總裁的Bob Mansfield 將接掌電動車計畫。

熟知蘋果電動車計畫Project Titan 的內部人士向《華爾街日報》透露,曾任技術部門資深副總裁的Bob Mansfield 將接手這項計畫。不過,目前蘋果官方與Bob Mansfield 本人都不願對於報導內容發表任何評論。

Bob Mansfield 於1999 年加入蘋果,在已故執行長Steve Jobs 底下出任管理階層;過去他曾主導硬體開發工程,產品包括MacBook Air、iMac、iPad 等。蘋果曾在2012 年宣布Bob Mansfield 將要退休,但後來又被執行長Tim Cook 說服、高薪留任公司;2013 年開始他從經營團隊名單隱身,官方僅表示他負責特別專案項目,並直接向Tim Cook 彙報。

Bob Mansfield 與像是設計長Jonathan Ive 等高層一向較少在公開場合露面,而在產品發表會或精心製作的產品影片當中可見他的身影。不過本月份開始,蘋果員工發現負責電動車計畫的資深經理轉而向Bob Mansfield 彙報。

當重點產品iPhone 銷售成長放緩、影響營收與獲利後,新的發展計畫成了蘋果迫切需要的項目。該公司雖未公開承認正在開發一款電動車,不過國外媒體陸續報導,已有數百名員工加入專案,並且聘請來自汽車產業的電池技術與自動駕駛專家,這項計畫可望成為未來推動蘋果成長的發展項目之一。

(本文授權自《》──〈〉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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