LeetCode 78,面試常用小技巧,通過二進制獲得所有子集

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是LeetCode專題第47篇文章,我們一起來看下LeetCode的第78題Subsets(子集)。

這題的官方難度是Medium,點贊3489,反對79,通過率59.9%。從這個數據我們也可以看得出來,這是一道難度不是很大,但是質量很高的題。的確,在這道題的解法當中,你會學到一種新的技巧。

廢話不多說,我們先來看題意。

題意

這題的題意非常簡單,和上一題有的一拼,基本上從標題就能猜到題目的意思。給定一個沒有重複元素的int型數組,要求返回所有的子集,要求子集當中沒有重複項,每一項當中也沒有重複的元素。

樣例

Input: nums = [1,2,3]
Output:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

照搬上題

剛拿到手可能有點蒙,但是稍微想一下就會發現,這一題和上題非常接近,兩者唯一的不同就是,子集沒有數量的限制,從空集開始,一直到它本身結束,不論多少個元素都可以。而上一題要求的是有數量限制的,也就是說上一題我們求的其實是限定了k個元素的子集。

想明白這點就簡單了,顯然我們可以復用上一題的算法,我們來遍歷這個k,從0到n,就可以獲得所有的子集了。只要你上一題做出來了,那麼這題幾乎沒有任何難度。如果你沒有看過上一題的文章的話,可以通過傳送門回顧一下:

LeetCode 77,組合挑戰,你能想出不用遞歸的解法嗎?

我們直接來看代碼:

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        # 上一題求解k個組合的解法
        def combine(n, k, ret):
            window = list(range(1, k+1)) + [n+1]
            j = 0
            
            while j < k:
                cur = []
                for i in range(k):
                    cur.append(nums[window[i] - 1])
                ret.append(cur[:])
                
                j = 0
                while j < k and window[j+1] == window[j] + 1:
                    window[j] = j + 1
                    j += 1
                window[j] += 1
                
        # 手動添加空集
        ret = [[]]
        n = len(nums)
        # 遍歷k從1到n
        for i in range(1, n+1):
            combine(n, i, ret)
        return ret

二進制組合

照搬上一題的解法固然是可行的,但是這麼做完全沒有必要,也得不到任何收穫。所以我們應該想一下新的解法。

既然這道題讓我們求的是所有的子集,那麼我們可以從子集的特點入手。我們之前學過,一個含有n個元素的子集的數量是。這個很容易想明白,因為n個元素,每個元素都有兩個狀態,選或者不選。並且這n個元素互相獨立,也就是說某個元素選或者不選並不會影響其他的元素,所以我們可以知道一共會有種可能。

我們也可以從組合數入手,我們令所有子集的數量為S,那麼根據上面我們用組合求解的解法,可以得到:

兩者的結果是一樣的,說明這個結論一定是正確的。

不知道大家看到n個元素,每個元素有兩個取值有什麼想法,如果做過的題目數量夠多的話,應該能很快聯想到二進制。因為在二進制當中,每一個二進制位就只有0和1兩種取值。那麼我們就可以用n位的二進制數來表示n個元素集合取捨的狀態。n位二進制數的取值範圍是,所以我們用一重循環去遍歷它,就相當於一重循環遍歷了整個集合所有的狀態。

這種技巧我們也曾經在動態規劃狀態壓縮的文章當中提到過,並且在很多題目當中都會用到。所以建議大家可以了解一下,說不定什麼時候面試就用上了。

根據這個技巧, 我們來實現代碼就非常簡單了。

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        ret = []
        n = len(nums)
        # 遍歷所有的狀態
        # 1左移n位相當於2的n次方
        for s in range(1 << n):
            cur = []
            # 通過位運算找到每一位是0還是1
            for i in range(n):
                # 判斷s狀態在2的i次方上,也就是第i位上是0還是1
                if s & (1 << i):
                    cur.append(nums[i])
            ret.append(cur[:])
            
        return ret

從代碼來看明顯比上面的解法短得多,實際上運行的速度也更快,因為我們去掉了所有多餘的操作,我們遍歷的每一個狀態都是正確的,也不用考慮重複元素的問題。

總結

不知道大家看完文章都有一些什麼感悟,可能第一種感悟就是LeetCode應該按照順序刷吧XD。

的確如此,LeetCode出題人出題都是有套路的,往往出了一道題之後,為了提升題目數量(湊提數),都會在之前題目的基礎上做變形,變成一道新題。所以如果你按照順序刷題的話,會很明顯地發現這一點。如果你從這個角度出發去思考的話,不但能理解題目之間的聯繫,還能揣摩出出題人的用意,這也是一件很有趣的事情。

如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

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

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

.Net Core基礎的健康檢查

前言

健康檢查能查看我們的應用程序當前是否是一個健康的運行狀態。微軟已經給我們提供了健康檢查輪子,只需要簡單的配置就能完成服務的狀態檢查。一起來實現一個最簡單的健康檢查吧。

開始

  • 新建一個空的webApi項目。 並引用Microsoft.Extensions.Diagnostics.HealthChecks 包。並在ConfigureServicesConfigure中加入相關配置
public void ConfigureServices(IServiceCollection services)
{
    //健康檢查服務
    services.AddHealthChecks();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    //加入中間件
    app.UseHealthChecks("/healthChecks");
}

最簡單的檢查就完成了,我們測試一下。

返Healthy,表示服務正常。

自定義拓展

HealthChecks提供了一個IHealthCheck接口,這個接口只有一個CheckHealthAsync方法,我們只需要實現這個接口就可以實現我們需要的各種自定義的檢查項目。CheckHealthAsync返回一個HealthCheckResult的枚舉代表健康檢查的幾種狀態,分別是異常,降級,健康。

public enum HealthStatus
{
    Unhealthy = 0,
    Degraded = 1,
    Healthy = 2,
}

實現接口,返回不健康狀態。

public class SqlHealthChecks : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
    {
        if (1 == 1)
        {
            return Task.FromResult(HealthCheckResult.Unhealthy());
        }
    }
}

ConfigureServices中添加自定義的檢查,AddCheck可以添加你自定的健康檢查服務,

public void ConfigureServices(IServiceCollection services)
{
    //健康檢查服務
    services.AddHealthChecks().AddCheck<SqlHealthChecks>("key");
}

測試可以發現返回的為不健康的應用

自定義返回值

我們可以利用HealthCheckOptions來實現健康檢查的自定義返回內容.

private static Task WriteResponse(HttpContext context, HealthReport healthReport)
{
    context.Response.ContentType = "application/json";
    var result = JsonHelper.SerializeObject(new
    {
        code = context.Response.StatusCode,
        errors = healthReport.Entries.Select(e => new
        {
            key = e.Key,
            value = e.Value.Status.ToString()
        })
    });

    return context.Response.WriteAsync(result);
}


public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime appLifetime)
{

    app.UseHealthChecks("/healthChecks", new HealthCheckOptions{ResponseWriter = WriteResponse});

}

測試返回效果

引入Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore 可以為DbContext進行檢查


public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks().AddCheck<SqlHealthChecks>("key").AddDbContextCheck<DbContext>("DbContext");
}

健康檢查UI

引入AspNetCore.HealthChecks.UI並在ConfigureServicesConfigure中加入相應的配置

public void ConfigureServices(IServiceCollection services)  
{  
    services.AddHealthChecksUI();  
}  

public void Configure(IApplicationBuilder app, IHostingEnvironment env)  
{  
    app.UseHealthChecksUI();  
}  

appsetting,json文件中加入配置

{
  "HealthChecksUI": {
    "HealthChecks": [
      {
        "Name": "HealthCheck",
        "Uri": "https://localhost:5000/healthCheck"
      }
    ],
    "EvaluationTimeinSeconds": 10,
    "MinimumSecondsBetweenFailureNotifications": 60
  }
}

啟動項目並指向/healthchecks-ui。

擴展包

開源社區已經有很多現有的優秀的擴展包我們可以直接引用

AspNetCore.HealthChecks.Npgsql
AspNetCore.HealthChecks.Redis
AspNetCore.HealthChecks.AzureStorage
AspNetCore.HealthChecks.AzureServiceBus
AspNetCore.HealthChecks.MySql
AspNetCore.HealthChecks.DocumentDb
AspNetCore.HealthChecks.SqLite
AspNetCore.HealthChecks.Kafka
AspNetCore.HealthChecks.RabbitMQ
AspNetCore.HealthChecks.IdSvr
AspNetCore.HealthChecks.DynamoDB
AspNetCore.HealthChecks.Oracle
AspNetCore.HealthChecks.Uris
AspNetCore.HealthChecks.System
AspNetCore.HealthChecks.Network
AspNetCore.HealthChecks.SqlServer
AspNetCore.HealthChecks.MongoDb

參考文章

  • 微軟官方文檔
  • 社區
  • 源碼理解HealthCheck

總結

實現了一個最簡單的健康檢查功能,可以在這個基礎上進行自定義的擴展和開發。

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

【其他文章推薦】

※教你寫出一流的銷售文案?

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

麥加朝聖減環境足跡 綠色朝覲漸扎根

摘錄自2018年8月23日中央社報導

200多萬名穆斯林到麥加朝聖接近尾聲,數以千計清潔工忙著在垃圾當中將塑膠分離出來。這是全世界最大型的年度盛會之一,對沙烏地阿拉伯帶來巨大環保挑戰。

聖城麥加附近的米納(Mina)馬穆尼亞營地(Mamuniya)放置幾個不同顏色的大桶子:黑色桶收集有機垃圾,藍色桶回收鋁鐵罐和塑膠,這些都是為了減少朝覲環境足跡的作法。

麥加市公共衛生部門主管薩阿迪(Mohammed al-Saati)指出,到伊斯蘭教第一聖地朝聖期間,製造出的垃圾量超過4萬2000公噸。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

3個月捕鯨177頭!日堅稱出於「研究」目的

摘錄自2018年08月25日蘋果日報日本報導

據日本周三(22日)官方資料,一批捕鯨船3個月內,由西北太平洋捕獲177頭鯨魚。同一時間,日方正準備下個月於巴西舉行的世界捕鯨會議(International Whaling Commission, IWC)中爭取重啟商業捕鯨。

《Standard Digital》報導,據日本水產廳表示,這批捕鯨船共捉到43頭小鬚鯨與134頭北鬚鯨,再度引起國際關注。但外媒擔心,各國對日方的施壓只會使保守分子和政客更堅定地繼續進行捕鯨作業。

此外,日方也強調「這次出航獲得的研究資料都會交給世界捕鯨協會,有助提升大眾對於保育、管理海洋資源的知識。」並指出這次行動屬於12年計劃的一部分,且主要目的出於研究,部分品種也非瀕臨絕種,所以可以捕捉。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

全球首列!中國氫燃料電池有軌電車正式投入營運

  新華社報導,由中國中車唐山公司研製的世界首列商用型氫燃料混合動力100%低地板現代有軌電車,10月26日在河北唐山市唐胥鐵路載客營運,為全球氫燃料電池有軌電車首次商業營運,也是中國在新能源軌道交通領域實現重大突破。   經過4年多時間,中車唐山公司率先在全球首次突破了燃料電池/超級電容混合動力牽引和控制等一系列關鍵技術,研製的有軌電車完全取消受電弓和接觸網,實現污染物「零排放」和全程「無網」運行。   據悉,唐山工業旅遊線路採用氫燃料電池有軌電車,無需架設接觸網,不用沿途安裝第三軌和充電樁,完整保留了百年唐胥鐵路的原貌;列車採用世界最先進的100%低地板技術,車廂地板距軌道面僅35公分,無需月臺;最小轉彎半徑僅19公尺,可沿現有城市道路直接鋪設軌道,在地面行駛和停靠;線路運營全程13.84公里,有軌電車一次快速加氫只需15分鐘,可持續行駛40公里,最高運行時速70公里。   中車唐山公司表示,旗下氫燃料混合動力有軌電車係採用2動1拖3輛編組,設乘客座位66個,最大載客量336人,還可根據運營需求靈活增加編組和載客量;這種有軌電車不僅可最高速度持續運行,且在制動、停站時,由燃料電池和制動能量回收系統為超級電容和蓄電池充電,能量回收率達30%以上。   (本文內容由授權使用。首圖為中車唐山電車示意圖,來源:)  

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

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司"嚨底家"!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

OPEC:2040年OECD美洲市場EV市佔35%、中國逼近29%

  石油輸出國組織(OPEC)11月7日發布「世界石油展望(WOO)」報告指出,假設全球電動車(EV)年度銷售量在2040年達到8千萬輛(相當於每5台車就有3台是電動車),在電動車滲透率高於預期的情況下、全球石油日需求量可能會在2040年較基準預估值減少250萬桶至1.08億桶。據此推斷,全球石油需求將在2030年代下半階段持平於這個水位。   WOO指出,2016年全球上路的電動車據估計已升至200萬輛。目前已有6個國家的電動車市佔率(占整體轎車銷售量比重)突破1%、挪威電動車銷售量佔比更是高達29%。不過,電動車目前僅佔全球整體轎車車隊不到0.2%的比例。   WOO預估(見圖),2040年電動車在經濟合作暨發展組織(OECD)美洲新車市場的銷售佔比將高達35%左右、屆時中國電動車銷售佔比預估也將逼近29%,印度預估將達18%。   根據DNV GL首度發布的「能源轉型展望」報告,受電動車滲透率持續上揚的影響,石油供應將在2020~2028年期間轉趨持平、隨後大幅下降,2034年將遭天然氣超越。這份報告預估電動車、內燃引擎車將在2022年達到「成本平價」,預估到2033年全球半數輕型新車銷售量都將是電動車。   Thomson Reuters報導,嘉能可(Glencore)董事長Tony Hayward 5月受訪時表示,電動車的快速進步意味著石油需求可能會在2040年以前觸頂,深海鑽油、加拿大油砂等高成本原油生產商恐將先被淘汰出局,擁有生產成本優勢的OPEC相對較不受衝擊。Hayward曾任英國石油公司(BP Plc)執行長。   英國金融時報8月報導,瑞銀(UBS)預估2021年歐洲未經補貼的純電動車整體持有成本將與傳統內燃機汽車相當、中國也可望在2025年達到這項里程碑。   (本文內容由授權使用。首圖來源:public domain CC0)

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

【其他文章推薦】

※教你寫出一流的銷售文案?

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

為什麼Web開發人員在2020年不用最新的CSS功能

轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

原文出處:https://dzone.com/articles/why-masses-are-not-using-latest-css-features-in-20

 

儘管CSS每年都會發布全新的特性,但實際上這些新功能很少會被web開發人員實際在生產項目中使用到,甚至去了解它們的動力也不會比去多完成幾個需求更多。那究竟是什麼原因導致的呢? 

1.使用最新特性不是優先事項

在一個新項目的初期階段,它用到的可能只是幾條CSS規則,但隨着項目的持續更新和迭代,項目中使用到的規則就會變得越來越複雜,CSS也會越來越複雜尺寸也會隨之不斷膨脹。因此,作為項目優化的第一要務,作為資源的CSS需要盡可能的精簡和減少複雜度,第一是為了便於更好地理解和維護,第二也是為了加載更為高效。那麼,更實用且可投入生產環境的一些CSS特性會被高頻使用,其他的特性則會被暫時擱置一旁。

並且,在一般情況下,樣式和品牌在一段時間內都會相對固定,完成任務的需求要比使用最新CSS的特性要更緊迫。 

 

 

(圖片來源於網絡)

 

2.預算限制

預算成本是影響了所有項目的主要因素。它在開發階段會高度影響事項的優先級。集成新的CSS功能需要時間,而開發團隊來說,增加的這部分時間成本會影響到項目的整體進度。因此,開發進度會重視在優化其他功能(而不是CSS功能)時花費的時間成本。

另外,引入了最新的CSS特性,還可能會使開發團隊把一部分精力放在解決瀏覽器兼容性問題上。這點和JavaScript不同,JavaScript有Babel來完成編譯,而CSS沒有提供類似功能。

 

 

 (圖片來源於網絡)

 

3.社區發展還未跟上

JavaScript每隔一段時間舉行一次會議。同樣,Vue和React也會為了幫助開發人員跟上社區的步伐而定期舉行會議。但是,對於CSS而言,它們根本沒有這樣的活動!因此,開發人員很難掌握其功能和路線圖。他們應該如何保持對新功能發展趨勢的了解? 沒有版本發布說明,也沒有定期的發布會,這根本不能點燃社區用戶的學習激情。 

對普通用戶而言,既然舊的技術已經滿足了需求,那麼又何必那麼麻煩閱讀文檔學習新的功能呢?

和框架和其他編程語言不同,CSS沒有針對安全問題的補丁程序。他只是一套標準,反正大多數客戶只需要關心網站看起來UI差不多就行了。

4.很難提升簡歷的含金量

即使你在掌握CSS方面付出了很多的努力,對CSS的新特性也了如指掌,但你也很難向你的客戶或老闆證明這一點,因為類似像這樣“熟練掌握CSS3以外的CSS特性”對他人而講是沒有意義的,因為它不是CSS3。在CSS開發領域,CSS3的出現是很有意義的,因為它完成了前端領域的統一:

  • Web開發人員提升了技能
  • 加速瀏覽器廠商統一支持了全新的CSS標準
  • 企業的技術棧更新

巨大的需求帶來了巨大的機會。除了大量的書籍、課程和視頻來幫助人們了解CSS3外,還催生了全新的布局模型,如Flexbox和Grid,儘管它們不是CSS3的一部分。

但這裏我們指的是CSS3外的特性,它們本身除了認可程度很低外,對開發團隊來講也是個相對不熟悉的東西,因此,開發團隊很難會把時間花在對市場沒有意義的事情上,客戶也不會關心你到底用不用新的技術。

5.缺乏時間

編寫CSS的主要目的是使你的網站的表現內容的形式更美觀及易於理解。CSS通過控制兩類事物來幫助開發人員去實現這個目標:布局和設計。布局(Layout)負責元素列和行排布,而設計(design)指顏色、字體、間距、動畫和邊框等基礎外觀。

但目前,舊的特性已經能處理的很好了,為什麼要花更多時間去使用新特性去替代已經很好的形式呢?

總結

CSS發布周期沒有固定的周期和計劃,導致一切都來的很突然 ,另外舊的CSS特性已經能很好的完成日常工作了,這讓很多Web開發人員沒有特別的動力去升級它們。

另外,新的特性知名度也不高,對最終用戶的吸引力也不足,很難從需求層面驅動使用。所以這就是為什麼都2020年了,CSS的新特性仍然使用的人較少的原因。

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

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司“嚨底家”!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

Java 源碼刨析 – 線程的狀態有哪些?它是如何工作的?

線程(Thread)是併發編程的基礎,也是程序執行的最小單元,它依託進程而存在

一個進程中可以包含多個線程,多線程可以共享一塊內存空間和一組系統資源,因此線程之間的切換更加節省資源、更加輕量化,也因此被稱為輕量級的進程。

   

線程的狀態在 JDK 1.5 之後以枚舉的方式被定義在 Thread 的源碼中,它總共包含以下 6 個狀態:

NEW新建狀態,線程被創建出來,但尚未啟動時的線程狀態;

RUNNABLE就緒狀態,表示可以運行的線程狀態,它可能正在運行,或者是在排隊等待操作系統給它分配 CPU 資源;

BLOCKED阻塞等待鎖的線程狀態,表示處於阻塞狀態的線程正在等待監視器鎖,比如等待執行 synchronized 代碼塊或者使用 synchronized 標記的方法;

WAITING等待狀態,一個處於等待狀態的線程正在等待另一個線程執行某個特定的動作,比如,一個線程調用了 Object.wait() 方法,那它就在等待另一個線程調用 Object.notify() Object.notifyAll() 方法;

TIMED_WAITING計時等待狀態,和等待狀態(WAITING)類似,它只是多了超時時間,比如調用了有超時時間設置的方法 Object.wait(long timeout) Thread.join(long timeout) 等這些方法時,它才會進入此狀態;

TERMINATED終止狀態,表示線程已經執行完成。

線程狀態的源代碼如下:

public enum State {

    /**

     * 新建狀態,線程被創建出來,但尚未啟動時的線程狀態

     */

    NEW,

   

    /**

     * 就緒狀態,表示可以運行的線程狀態,但它在排隊等待來自操作系統的 CPU 資源

     */

    RUNNABLE,

   

    /**

     * 阻塞等待鎖的線程狀態,表示正在處於阻塞狀態的線程

     * 正在等待監視器鎖,比如等待執行 synchronized 代碼塊或者

     * 使用 synchronized 標記的方法

     */

    BLOCKED,

   

    /**

     * 等待狀態,一個處於等待狀態的線程正在等待另一個線程執行某個特定的動作。

     * 例如,一個線程調用了 Object.wait() 它在等待另一個線程調用

     * Object.notify() 或 Object.notifyAll()

     */

    WAITING,

   

    /**

     * 計時等待狀態,和等待狀態 (WAITING) 類似,只是多了超時時間,比如

     * 調用了有超時時間設置的方法 Object.wait(long timeout) 和 

     * Thread.join(long timeout) 就會進入此狀態

     */

    TIMED_WAITING,

   

    /**

     * 終止狀態,表示線程已經執行完成

     */

}

   

線程的工作模式是,首先先要創建線程並指定線程需要執行的業務方法,然後再調用線程的 start() 方法,此時線程就從 NEW(新建)狀態變成了 RUNNABLE(就緒)狀態;

然後線程會判斷要執行的方法中有沒有 synchronized 同步代碼塊,如果有並且其他線程也在使用此鎖,那麼線程就會變為 BLOCKED(阻塞等待)狀態,當其他線程使用完此鎖之後,線程會繼續執行剩餘的方法。

   

當遇到 Object.wait() Thread.join() 方法時,線程會變為 WAITING(等待狀態)狀態;

如果是帶了超時時間的等待方法,那麼線程會進入 TIMED_WAITING(計時等待)狀態;

當有其他線程執行了 notify() notifyAll() 方法之後,線程被喚醒繼續執行剩餘的業務方法,直到方法執行完成為止,此時整個線程的流程就執行完了,執行流程如下圖所示:

BLOCKED WAITING 的區別】

雖然 BLOCKED WAITING 都有等待的含義,但二者有着本質的區別。

首先它們狀態形成的調用方法不同

其次 BLOCKED 可以理解為當前線程還處於活躍狀態,只是在阻塞等待其他線程使用完某個鎖資源

WAITING 則是因為自身調用 Object.wait() 或着是 Thread.join() 又或者是 LockSupport.park() 而進入等待狀態,只能等待其他線程執行某個特定的動作才能被繼續喚醒。

比如當線程因為調用了 Object.wait() 而進入 WAITING 狀態之後,則需要等待另一個線程執行 Object.notify() Object.notifyAll() 才能被喚醒。

   

start() run() 的區別】

首先從 Thread 源碼來看,start() 方法屬於 Thread 自身的方法,並且使用了 synchronized 來保證線程安全,源碼如下:

public synchronized void start() {

    // 狀態驗證,不等於 NEW 的狀態會拋出異常

    if (threadStatus != 0)

        throw new IllegalThreadStateException();

    // 通知線程組,此線程即將啟動

   

    group.add(this);

    boolean started = false;

    try {

        start0();

        started = true;

    } finally {

        try {

            if (!started) {

                group.threadStartFailed(this);

            }

        } catch (Throwable ignore) {

            // 不處理任何異常,如果 start0 拋出異常,則它將被傳遞到調用堆棧上

        }

    }

}

   

run() 方法為 Runnable 的抽象方法,必須由調用類重寫此方法,重寫的 run() 方法其實就是此線程要執行的業務方法,源碼如下:

public class Thread implements Runnable {

 // 忽略其他方法……

  private Runnable target;

  @Override

  public void run() {

      if (target != null) {

          target.run();

      }

  }

}

@FunctionalInterface

public interface Runnable {

    public abstract void run();

}

   

從執行的效果來說,start() 方法可以開啟多線程,讓線程從 NEW 狀態轉換成 RUNNABLE 狀態,而 run() 方法只是一個普通的方法。

   

其次,它們可調用的次數不同,start() 方法不能被多次調用,否則會拋出 java.lang.IllegalStateException;而 run() 方法可以進行多次調用,因為它只是一個普通的方法而已。

   

【線程優先級】

Thread 源碼中和線程優先級相關的屬性有 3 個:

// 線程可以擁有的最小優先級

public final static int MIN_PRIORITY = 1;

   

// 線程默認優先級

public final static int NORM_PRIORITY = 5;

   

// 線程可以擁有的最大優先級

public final static int MAX_PRIORITY = 10

   

線程的優先級可以理解為線程搶佔 CPU 時間片的概率,優先級越高的線程優先執行的概率就越大,但並不能保證優先級高的線程一定先執行。

   

在程序中我們可以通過 Thread.setPriority() 來設置優先級,setPriority() 源碼如下:

public final void setPriority(int newPriority) {

    ThreadGroup g;

    checkAccess();

    // 先驗證優先級的合理性

    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {

        throw new IllegalArgumentException();

    }

    if((g = getThreadGroup()) != null) {

        // 優先級如果超過線程組的最高優先級,則把優先級設置為線程組的最高優先級

        if (newPriority > g.getMaxPriority()) {

            newPriority = g.getMaxPriority();

        }

        setPriority0(priority = newPriority);

    }

}

   

【線程的常用方法】

線程的常用方法有以下幾個。

   

join()

一個線程中調用 other.join() ,這時候當前線程會讓出執行權給 other 線程,直到 other 線程執行完或者過了超時時間之後再繼續執行當前線程,join() 源碼如下:

public final synchronized void join(long millis)

throws InterruptedException {

    long base = System.currentTimeMillis();

    long now = 0;

    // 超時時間不能小於 0

    if (millis < 0) {

        throw new IllegalArgumentException(“timeout value is negative”);

    }

    // 等於 0 表示無限等待,直到線程執行完為之

    if (millis == 0) {

        // 判斷子線程 (其他線程) 為活躍線程,則一直等待

        while (isAlive()) {

            wait(0);

        }

    } else {

        // 循環判斷

        while (isAlive()) {

            long delay = millis  now;

            if (delay <= 0) {

                break;

            }

            wait(delay);

            now = System.currentTimeMillis()  base;

        }

    }

}

   

從源碼中可以看出 join() 方法底層還是通過 wait() 方法來實現的。

   

例如,在未使用 join() 時,代碼如下:

public class ThreadExample {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            for (int i = 1; i < 6; i++) {

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                System.out.println(子線程睡眠: + i + 秒。);

            }

        });

        thread.start(); // 開啟線程

        // 主線程執行

        for (int i = 1; i < 4; i++) {

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println(主線程睡眠: + i + 秒。);

        }

    }

}

程序執行結果為:

複製主線程睡眠:1秒。

子線程睡眠:1秒。

主線程睡眠:2秒。

子線程睡眠:2秒。

主線程睡眠:3秒。

子線程睡眠:3秒。

子線程睡眠:4秒。

子線程睡眠:5秒。

   

從結果可以看出,在未使用 join() 時主子線程會交替執行。

   

然後我們再把 join() 方法加入到代碼中,代碼如下:

public class ThreadExample {

    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread(() -> {

            for (int i = 1; i < 6; i++) {

                try {

                    Thread.sleep(1000);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

                System.out.println(子線程睡眠: + i + 秒。);

            }

        });

        thread.start(); // 開啟線程

        thread.join(2000); // 等待子線程先執行 2 秒鐘

        // 主線程執行

        for (int i = 1; i < 4; i++) {

            try {

                Thread.sleep(1000);

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            System.out.println(主線程睡眠: + i + 秒。);

        }

    }

}

程序執行結果為:

   

複製子線程睡眠:1秒。

子線程睡眠:2秒。

主線程睡眠:1秒。 

// thread.join(2000); 等待 2 秒之後,主線程和子線程再交替執行

子線程睡眠:3秒。

主線程睡眠:2秒。

子線程睡眠:4秒。

子線程睡眠:5秒。

主線程睡眠:3秒。

從執行結果可以看出,添加 join() 方法之後,主線程會先等子線程執行 2 秒之後才繼續執行。

   

yield()

Thread 的源碼可以知道 yield() 為本地方法,也就是說 yield() 是由 C C++ 實現的,源碼如下:

public static native void yield();

   

yield() 方法表示給線程調度器一個當前線程願意出讓 CPU 使用權的暗示,但是線程調度器可能會忽略這個暗示。

   

比如我們執行這段包含了 yield() 方法的代碼,如下所示:

public static void main(String[] args) throws InterruptedException {

    Runnable runnable = new Runnable() {

        @Override

        public void run() {

            for (int i = 0; i < 10; i++) {

                System.out.println(線程: +

                        Thread.currentThread().getName() +  I + i);

                if (i == 5) {

                    Thread.yield();

                }

            }

        }

    };

    Thread t1 = new Thread(runnable, “T1”);

    Thread t2 = new Thread(runnable, “T2”);

    t1.start();

    t2.start();

}

   

當我們把這段代碼執行多次之後會發現,每次執行的結果都不相同,這是因為 yield() 執行非常不穩定,線程調度器不一定會採納 yield() 出讓 CPU 使用權的建議,從而導致了這樣的結果。

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

【其他文章推薦】

※教你寫出一流的銷售文案?

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

※回頭車貨運收費標準

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

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

福斯汽車、SMA Solar傳聯手發展電動車

福斯汽車(Volkswagen)深陷柴油車排氣造假案使得品牌形象受重創,為了早點擺脫「柴油門」陰影,轉移消費者的眼光,福斯近來大力推動電動車,雄心勃勃的發展電動車的同時,傳出找來德國逆變器大廠SMA Solar 結盟。

SMA Solar 成立於1981 年,公司名來自「系統、測量、系統工程」(System, Mess and Anlagentechnik)德文簡寫,為全球最大逆變器廠,逆變器應用於直流電交流電轉換,不僅應用於太陽能,也應用於電池能源儲存系統,SMA Solar 市場地位穩固,特斯拉(Tesla)於2016 年5 月宣布Powerwall 將進行小規模規格提升,其重點之一就是改為可支援SMA Solar 產品。

福斯在「柴油門」事件後,積極將事業目標轉向電動車,以便洗刷受到作假事件影響的聲譽,投注數十億歐元資金,大力發展零碳排放的純電動車以及隨選共乘服務,計劃在2025 年以前推出30 款電動車,攻佔四分之一的全球電動車市場。

但是分析師指出,福斯在2015 年只不過出貨6.7 萬輛電動車與油電混合車,以這樣的市佔與進度,要在2025 年攻佔四分之一電動車市場的目標可能很難達成,尤其是競爭對手早已起步,為了追上豐田(Toyota)與雷諾(Renault)日產(Nissan)聯盟,福斯需要找尋強大的合作夥伴。

德國地方日報《黑森下薩克森大眾報》(Hessische Niedersaechsische Allgemeine)於2016 年8 月初報導,福斯尋求能源儲存與綠能大廠SMA Solar 的合作。

福斯的電動車廠表示,SMA Solar 在靜態能源儲存領域相當強勢,對福斯來說,尋求正確的合作夥伴,將是發展電動車的重要關鍵。不過,SMA Solar 目前已經與賓士製造商戴姆勒(Daimler)以及特斯拉在能源儲存方面合作,目前對於福斯的可能合作消息不表示意見。

(本文授權自《》──〈〉。照片來源:)

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

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司“嚨底家”!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新

超詳細Maven技術應用指南

該文章,GitHub已收錄,歡迎老闆們前來Star!

GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual

搜索關注微信公眾號“碼出Offer”,送你學習福利資源!

一、前言

在我們的項目資源中,你會發現需要導入的jar包越來越多,讓jar包的管理越來越沉重。它會表現為以下幾個缺點:

  • 每個項目都需要手動搜集和導入所需要的jar包
  • 項目中用到的jar包有版本更新,我們需要重新搜集並導入到項目中
  • 相同的jar包導入到不同的項目中,jar包會在本地存儲多份

針對上述問題,我們就需要使用統一的管理工具:Maven

二、了解Maven

2.1 什麼是Maven

Maven是一個基於項目對象模型(POM)的概念的純Java開發的開源的項目管理工具。主要用來管理Java項目,進行依賴管理(jar包依賴管理)和項目構建(項目編譯、打包、測試、部署)。此外還能分模塊開發,提高開發效率。

2.2 Maven的下載安裝

關於Maven的下載,我們需要下載它的解壓包。

Maven下載地址: https://us.mirrors.quenda.co/apache/maven/maven-3/3.6.3/binaries/

image-20200616171323409

下載后將Maven解壓到目錄中就可以了!

注意: 解壓的目錄與tomact服務器的形式是一樣的,不要有中文及特殊符號!

image-20200616171637526

2.3 Maven目錄結構解析

目錄名稱 描述
bin 存儲mvn的各種可執行文件
boot 含有plexus-classworlds類加載器框架,Maven 使用該框架加載自己的類庫
conf 存放settings.xml等配置文件
lib 存儲Maven運行時所需要的Java類庫
LICENSE/NOTICE/README.txt 針對Maven的版本、第三方軟件等簡要介紹

2.4 配置環境變量

Maven依賴Java環境的配置環境,所以要確保jdk版本在1.7以上,maven版本在3.3以上。

  • 配置環境變量與jdk環境變量配置是一樣的,在本機中創建MAVEN_HOME環境變量,並將maven的解壓路徑設置進去,點擊確定(路徑參考上圖解壓后的結果圖路徑)
  • 修改path環境變量,添加%MAVEN_HOME%\bin后,一路點擊確定即可!

2.5 測試

下載解壓、配置環境變量后,我們打開DOS命令窗口,鍵入mvn -v查看maven版本信息

  • 如果看到如下圖片maven的版本信息,證明maven安裝配置成功!
  • 在Maven的版本信息你就可以得知它依賴於jdk環境!

image-20200616172931556

2.6 Maven項目模型圖

三、Maven的配置

3.1 配置本地倉庫

本地倉庫簡單來說,就是在本地的maven中存儲管理所需jar包

  1. 首先,打開maven目錄conf文件夾中的settings.xml配置文件

  2. 其次,找到標號1的那一行配置信息,並複製此配置信息放在其下面

  3. 然後,在磁盤中創建一個目錄,作為存儲jar文件的本地倉庫

  4. 最後,將複製的此配置信息路徑替換成自己創建的本地倉庫目錄路徑,參考標號2的操作

image-20200616174928266

3.2 配置jdk

3.2.1 全局配置

由於Maven依賴於jdk環境,所以我們也需要在maven中配置jdk(我使用的jdk是主流的1.8版本)

  1. 打開settings.xml配置文件,找到<profiles>標籤,你會發現標籤內都是註釋的內容,我們需要在標籤內,寫入自己的jdk的配置信息。配置如下:
  2. 在maven中添加好jdk的配置信息后,我們需要在</profiles>結束標籤后添加<activeProfiles>標籤內容,讓配置好的<profiles>標籤中內容生效

注意: profile標籤中的id是此配置信息的名稱,在後面使用activeProfile標籤讓其配置生效的時候,需要保證id與activeProfile的名稱一致!(貼圖供大家參考!)

    <!-- 配置jdk -->
    <profile>
        <id>jdk1.8</id>
        <activation>
            <activeByDefault>true</activeByDefault>    
            <jdk>1.8</jdk>    
        </activation>    
        <properties>    
            <maven.compiler.source>1.8</maven.compiler.source>    
            <maven.compiler.target>1.8</maven.compiler.target>
            <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> 
        </properties>    
    </profile>
<!-- 使配置好的profiles標籤中內容生效 -->
<activeProfiles>
    <activeProfile>jdk1.8</activeProfile>
</activeProfiles>

image-20200616181057029

3.2.2 單個項目修改

後面我們會了解到maven項目是通過pom.xml進行構建信息配置和依賴信息配置。其中就包括配置編譯需要的jdk版本。所以我們直接修改pom文件就可以實現單個項目修改,但是我們並不推薦此種方式,因為這個方式需要每個項目都要修改,不具有可重用性!

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
</build>

四、倉庫

4.1 倉庫概念

  • 存儲依賴的地方,體現形式就是本地的一個目錄。
  • 倉庫中不僅存放依賴,而且管理着每個依賴的唯一標識(坐標),Java項目憑坐標獲取依賴。

4.2 倉庫分類

Maven倉庫可以分為本地倉庫和遠程倉庫,其中遠程倉庫又分為中央倉庫、公共倉庫和私服

  • 本地倉庫: 本地倉庫存放着項目中所需jar文件
  • 中央倉庫: Maven的中央倉庫是由Maven社區提供存儲jar文件的倉庫
  • 公共倉庫: 國內廠家提供的存儲jar文件的倉庫,比如:aliyun倉庫
  • 私服: 由公司創建的存儲jar文件的倉庫,可在公司範圍內共享,不對外開放

當項目中需要jar文件依賴時,會從倉庫中查找獲取,如果我們把所有倉庫都配置好。maven在查找獲取依賴的時候遵循一個依賴查找順序,如下:(如果本地倉庫找不到依賴就去私服下載,以此類推……)

依賴查找順序: 本地倉庫 – > 私服 – > 公共倉庫 – > 中央倉庫

image-20200616184606968

4.3 本地倉庫

本地倉庫: 本地目錄中存放所需jar包,需修改settings配置文件來配置本地倉庫

  • 使用過的依賴都會存儲在本地倉庫中,實現復用

4.4 遠程倉庫

4.4.1 中央倉庫

中央倉庫: Maven中央倉庫是由Maven社區提供的倉庫,不用任何配置,maven中內置了中央倉庫地址。其中就包含絕大多數流行的開源Java構件

  • https://mvnrepository.com/可以搜索需要的依賴的相關信息(倉庫搜索服務)
  • http://repo.maven.apache.org/maven2/為中央倉庫地址
4.4.2 公共倉庫

公共倉庫: 第三方維護的jar文件倉庫,比如阿里雲提供的倉庫。但是jar文件可能不如官方的中央倉庫全,有時候也會找不到,所以如果項目構建不成功,可以更改鏡像為官方的,下載完jar包再去改回來

  • aliyun倉庫地址:http://maven.aliyun.com/nexus/content/groups/public/

  • 因為Maven社區提供的中央倉庫在國外,國內使用下載依賴速度過慢,所以一般我們都配置國內的公共倉庫來代替中央倉庫

  • 使用時,需要在settings.xml配置文件中添加配置信息。打開settings.xml配置文件,找到<mirrors>標籤,你也會發現這是一個空標籤,最後標籤內填寫如下配置就OK!

    <!--setting.xml中添加如下配置-->
    <mirror>
        <id>aliyun</id>  
        <!-- 中心倉庫的 mirror(鏡像) -->
        <mirrorOf>central</mirrorOf>    
        <name>Nexus aliyun</name>
        <!-- aliyun倉庫地址 以後所有要指向中心倉庫的請求,都會指向aliyun倉庫-->
        <url>http://maven.aliyun.com/nexus/content/groups/public</url>  
    </mirror>

image-20200616223651045

4.4.3 私服

私服: 公司創建存儲jar文件的倉庫,只對公司範圍共享,不對外開放。可以通過Nexus來創建、管理一個私服。

4.4.3.1 私服概念
  • 私服是架設在局域網的一種特殊的遠程倉庫,目的是代理遠程倉庫及部署第三方構件。

  • 有了私服之後,當 Maven 需要下載依賴時,直接請求私服,私服上存在則下載到本地倉庫;否則,私服請求外部的遠程倉庫,將構件下載到私服,再提供給本地倉庫下載。

  • 私服可以解決在企業做開發時每次需要的jar包都要在中心倉庫下載,且每次下載完只能被自己使用,不能被其他開發人員使用

  • 所謂私服就是一個服務器,但是不是本地層面的,是公司層面的,公司中所有的開發人員都在使用同一個私服

4.4.3.2 私服架構

我們可以使用專門的 Maven 倉庫管理軟件來搭建私服,比如:Apache Archiva,Artifactory,Sonatype Nexus。這裏我們使用Sonatype Nexus

  • 我們可以在圖中得到在無私服的情況下,我們都需要從遠程倉庫去獲取jar文件並存儲在本地倉庫,由於中央倉庫是國外的,所以下載速度比較慢等等原因,並存在很多缺點,比如我們公司內使用
  • 我們在圖中可清晰的看到,如果有私服的話,我們需要從私服中,查找jar文件,如果私服沒有該jar文件,就需要去中央倉庫或公共倉庫去下載,然後傳到私服中,最後傳入到自己的本地倉庫進行使用。假設公司員工再一次使用到該jar文件,它會先從私服中找有沒有這個jar文件,由於我們之前的員工已經將該jar文件存儲到了私服中,所以就省去了其他員工調用遠程倉庫的步驟。並且私服是公司內部局域網類型的,下載速度會比遠程倉庫快出很多倍
無私服 有私服
私服1 私服2
4.4.3.3 Nexus安裝(了解即可)
  • Nexus官網地址:https://blog.sonatype.com/

  • 下載地址:https://help.sonatype.com/repomanager2/download/download-archives—repository-manager-oss

下載我們需要下載Zip解壓包即可,將解壓包解壓到本地盤符中即可!

解壓后,你會看到nexus目錄為私服目錄,sonatype-work目錄中包含存儲私服下載的依賴。

4.4.3.4 Nexus登錄

訪問私服:http://localhost:8081/nexus/

登錄私服的賬號為admin,密碼為admin123

4.4.3.5 倉庫列表
倉庫類型 描述
group 包含多個倉庫,通過group庫的地址可以從包含的多個倉庫中查找構件
hosted 私服 服務器本地的倉庫,其中存儲諸多構件
proxy 代理倉庫,其會關聯一個遠程倉庫, 比如中央倉庫,aliyun倉庫,向該倉庫查找構件時,如果沒有會從其關聯的倉庫中下載
倉庫名 描述
Releases 存放項目的穩定發布版本,一個模塊做完后如果需要共享給他人,可以上傳到私服的該庫
Snapshots 對應不穩定的發布版本
3rd party 存放中央倉庫沒有的 ,如ojdbc.jar,可以上傳到私服的該庫中
倉庫列表
私服_list
4.4.3.6 倉庫組

而此時就有問題,私服中有很多倉庫,每個倉庫都有自己的url,則項目中配置哪個倉庫呢 ?

私服中有一個倉庫組,組中包含多個倉庫,可以指定倉庫組的url,即可從多個倉庫中獲取構件

關於倉庫的設置: 由於我們在使用私服的時候,本地倉庫沒有的jar文件,需要去私服找,私服沒有的話,就去中央倉庫找。所以我們需要把私服內的中央倉庫換為阿里雲倉庫,這樣可以保證我們國內的下載速度。

倉庫組 注意:proxy的倉庫排序在最後
私服_deploy2
4.4.3.7 手動上傳倉庫
4.4.3.8 Maven關聯私服

配置settings.xml,設置私服地址、認證等信息(關聯私服需要添加配置文件信息如下,找到父標籤,添加子標籤內容即可)

<servers>
    <server> 
        <id>nexus-public</id> <!-- nexus的認證id -->
        <username>admin</username> <!--nexus中的用戶名密碼-->
        <password>admin123</password> 
    </server>
</servers>
<profiles>
    <profile> 
        <id>nexus</id> 
        <repositories> 
            <repository> 
                <id>nexus-public</id> <!--nexus認證id 【此處的repository的id要和 <server>的id保持一致】-->
                <!--name隨便-->
                <name>Nexus Release Snapshot Repository</name> 
                <!--地址是nexus中倉庫組對應的地址-->
                <url>http://localhost:8081/nexus/content/groups/public/</url>
                <releases><enabled>true</enabled></releases> 
                <snapshots><enabled>true</enabled></snapshots> 
            </repository>
        </repositories> 
        <pluginRepositories> <!--插件倉庫地址,各節點的含義和上面是一樣的-->
            <pluginRepository> 
                <id>nexus-public</id> <!--nexus認證id 【此處的repository的id要和 <server>的id保持一致】-->
                <!--地址是nexus中倉庫組對應的地址-->
                <url>http://localhost:8081/nexus/content/groups/public/</url>
                <releases><enabled>true</enabled></releases> 
                <snapshots><enabled>true</enabled></snapshots> 
            </pluginRepository> 
        </pluginRepositories> 
    </profile>
</profiles>
<activeProfiles>
    <activeProfile>yourjdk</activeProfile>
    <!-- 使私服配置生效 -->
    <activeProfile>nexus</activeProfile>
</activeProfiles>
4.4.3.9 Meven項目部署到私服
  • 執行mvn deploy指令即可將項目部署到私服對應的倉庫中,此時項目中的打包方式多為jar
  • 但需要提前在項目的pom.xml中配置部署私服倉庫位置,如下:

注意: 如上的 repository的 id 依然是要和settings.xml中配置的server中的id 一致,才能通過私服的認證

<project>
        ...
    <dependencies>
        .....
    </dependencies>

    <!-- 在項目的pom.xml中 配置私服的倉庫地址,可以將項目打jar包部署到私服 -->
    <distributionManagement>
        <repository>
            <id>nexus-public</id> <!-- nexus認證id -->
            <url>http://localhost:8081/nexus/content/repositories/releases</url>
        </repository>
        <snapshotRepository>
            <id>nexus-public</id> <!-- nexus認證id -->
            <url>http://localhost:8081/nexus/content/repositories/snapshots</url>
        </snapshotRepository>
    </distributionManagement>
</project>

五、IDEA中的Maven操作

5.1 創建Maven項目

創建Maven項目
image-20200616215847109
指定項目名稱和項目位置
image-20200616220455702
5.1.1 節點配置解析
節點 詳細描述
groupId 這是項目組的編號,這在組織或項目中通常是獨一無二的。 例如,一家銀行集團 com.company.bank擁有所有銀行相關項目。
artifactId 這是項目的 ID。這通常是項目的名稱。 例如,consumer-banking。 除了 groupId 之外,artifactId 還定義了 artifact 在存儲庫中的位置。
version 這是項目的版本。與 groupId 一起使用,artifact 在存儲庫中用於將版本彼此分離。 例如:com.company.bank:consumer-banking:1.0com.company.bank:consumer-banking:1.1
5.1.2 語義化版本號
  • 規則:正式穩定版本從v0.1.0開始,配套軟件公共API
  • 注意:正式版發布后不可修改,只能在下一個版本中發布新內容
版本類型 詳細描述
主要版本 當你做了不兼容的API 修改(正式版發布、架構升級)
次要版本 當你做了向下兼容的功能性新增(功能增減)
修訂版本 當你做了向下兼容的問題修正(BUG修復、查缺補漏)
5.1.3 擴展(SNAPSHOT)

在使用maven過程中,我們在開發階段經常性的會有很多公共庫處於不穩定狀態,隨時需要修改併發布,可能一天就要發布一次,遇到bug時,甚至一天要發布N次。我們知道,maven的依賴管理是基於版本管理的,對於發布狀態的artifact,如果版本號相同,即使我們內部的鏡像服務器上的組件比本地新,maven也不會主動下載的。如果我們在開發階段都是基於正式發布版本來做依賴管理,那麼遇到這個問題,就需要升級組件的版本號,可這樣就明顯不符合要求和實際情況了。但是,如果是基於快照版本,那麼問題就自熱而然的解決了,而maven已經為我們準備好了這一切。

maven中的倉庫分為兩種,snapshot快照倉庫和release發布倉庫。snapshot快照倉庫用於保存開發過程中的不穩定版本,release正式倉庫則是用來保存穩定的發行版本。定義一個組件/模塊為快照版本,只需要在pom文件中在該模塊的版本號后加上-SNAPSHOT即可(注意這裏必須是大寫),如下:

<groupId>cc.mzone</groupId>
<artifactId>m1</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>

maven會根據模塊的版本號(pom文件中的version)中是否帶有-SNAPSHOT來判斷是快照版本還是正式版本。如果是快照版本,那麼在mvn deploy時會自動發布到快照版本庫中,會覆蓋老的快照版本,而在使用快照版本的模塊,在不更改版本號的情況下,直接編譯打包時,maven會自動從鏡像服務器上下載最新的快照版本。如果是正式發布版本,那麼在mvn deploy時會自動發布到正式版本庫中,而使用正式版本的模塊,在不更改版本號的情況下,編譯打包時如果本地已經存在該版本的模塊則不會主動去鏡像服務器上下載

在maven的約定中,依賴的版本分為兩類——SNAPSHOT和RELEASE。SNAPSHOT依賴泛指以-SNAPSHOT為結尾的版本號,例如1.0.1-SNAPSHOT。除此之外,所有非-SNAPSHOT結尾的版本號則都被認定為RELEASE版本,即正式版,雖然會有beta、rc之類說法,但是這些只是軟件工程角度的測試版,對於maven而言,這些都是RELEASE版本。所以一般我們需要上傳到發布倉庫的時候可以在<version>標籤內直接寫版本即可,不需要再添加任何標籤!

5.2 IDEA關聯Maven

在IDEA中關聯本地安裝的maven,後續就可以通過idea來使用maven管理項目(我使用的aliyun倉庫)

在全局設置中關聯Maven
image-20200616222756521
Maven項目展示 (缺少test包下resources文件夾)
image-20200616224937892

5.3 IDEA創建測試包下resources文件夾

我們在使用IDEA創建Maven項目時,IDEA是沒有幫我們創建test包下的resources文件夾。但是Maven規範中是包含這個文件夾的,所以我們需要手動創建並聲明該文件夾

創建存放測試配置的文件夾
image-20200616225355373
指定文件夾名稱 (下拉框選擇resources文件夾創建即可)
image-20200616225859663
文件目錄結構展示 (完整Maven規範目錄結構)
image-20200616230042781

5.4 Maven項目目錄結構解析

注意: 項目中的創建包、創建類、執行,都與普通項目無異

目錄名稱 描述
src/main/java 用於創建包,存放編寫的源代碼(.java文件)
src/main/resources 存放項目中所需配置文件,比如:c3p0.properties
src/test/java 用於創建包,存放編寫的測試代碼(.java文件)
src/test/resources 存放項目中測試代碼所需配置文件
根目錄/pom.xml 項目對象模型(project object model),maven項目核心文件,其中定義項目構建方式,聲明依賴等

5.5 Maven項目類型

根據項目類型,在pom.xml文件中添加相應配置。

  • 項目類型分為Java項目和JavaWeb項目

  • 如果項目為Java項目需要在<project>標籤內添加 jar

  • 如果項目為JavaWeb項目需要在<project>標籤內添加 war

注意: Maven可以根據項目類型來確定打包方式,比如Java項目打包成jar包JavaWeb項目打包成war包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.mylifes1110</groupId>
    <artifactId>firstmaven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--
        設置項目類型,打包方式:
        如果為Java項目則使用jar
        如果為JavaWeb項目使用war
    -->
    <packaging>jar</packaging>
<!--    <packaging>war</packaging>-->
</project>

5.6 IDEA中導入依賴

5.6.1 導入依賴須知

建好項目后,需要導入需要的jar包,要通過坐標來在倉庫中查找導入

  • 每個構件都有自己的坐標,其坐標分為groupId、artifactId、version 三種,翻譯後為項目標識、項目名稱、版本號
  • 在maven項目中只需要配置坐標,maven便會自動加載對應依賴。刪除坐標則會移除依賴
節點 描述
project 工程的根標籤。
modelVersion 模型版本需要設置為 4.0。
groupId 這是工程組的標識。它在一個組織或者項目中通常是唯一的。例如,一個銀行組織 com.companyname.project-group 擁有所有的和銀行相關的項目。
artifactId 這是工程的標識。它通常是工程的名稱。例如,消費者銀行。groupId 和 artifactId 一起定義了 artifact 在倉庫中的位置。
version 這是工程的版本號。在 artifact 的倉庫中,它用來區分不同的版本。例如:com.company.bank:consumer-banking:1.0 com.company.bank:consumer-banking:1.1
5.6.2 查找依賴

依賴查找服務需要在如下網址中查找依賴,獲取依賴坐標后,在maven項目中的pom.xml文件中導入

  • 依賴查找服務地址: https://mvnrepository.com/
查找jar文件
image-20200616111418839
jar文件的選擇
image-20200616112444879
Copy依賴坐標
image-20200617003002005
5.6.3 導入依賴

在項目的pom.xml文件中添加依賴

  • 首先添加<dependencies>標籤
  • 最後添加複製好的依賴坐標

注意: 導入依賴可以導入多個所需jar文件的依賴,依次在<dependencies>標籤內添加依賴坐標即可

導入依賴
image-20200617003853705
5.6.4 同步依賴

導入依賴后,你會發現pom.xml文件中的依賴坐標是紅色報錯的,而在IDEA的右下角有那麼一個框。這時候需要點擊框內提示信息在maven倉庫中同步下載依賴到項目中

同步下載依賴到項目中
image-20200617004515463
5.6.5 查看依賴

導入並同步下載好的依賴,可以通過項目和IDEA中Maven控制面板查看

查看依賴
image-20200617005635304

5.7 基於Maven創建Web項目

5.7.1 修改web項目打包方式

pom.xml文件中設置 <packaging>war/packaging>修改打包方式為web項目

5.7.2 導入web依賴

基於Maven項目,我們導入了maven所需依賴,如果創建web項目的話,還需要導入JSPServletJSTL依賴,使Maven項目具有web編譯環境。(在pom.xml文件中添加以下依賴)

        <dependency>
            <!-- jstl 支持 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <!-- servlet編譯環境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <!-- jsp編譯環境 -->
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>

image-20200617123210482

5.7.3 創建web項目目錄結構

web項目在IDEA中有web規範目錄文件夾的,比如webapp、WEB-INF、web.xml、index.jsp。而由於Maven項目中沒有該規範目錄,這就需要我們自己創建目錄結構了!

創建web項目目錄結構有以下注意點:

  • webapp文件夾必須是基於main目錄下創建的,與java文件夾同級
  • webapp文件夾IDEA自動識別此文件夾,給與了特殊藍色圓點標識
  • 在WEB-INF目錄下創建的的web.xml是一個空的xml文件,我們需要在該文件中鍵入如下web.xml空白模板信息,只需要將下面的模板複製到項目中的web.xml文件中即可!
  • 創建的文件夾以及文件名稱千萬不要寫錯!

注意: 在創建項目的時候,其實我們是可以指定使用IDEA來創建來創建web項目的目錄結構的,創建項目時需要勾選Create from archetype。只是IDEA為我們構建的項目架構是有版本差異的,而且還附加了很多對我們無用的註解等等,所以我們一般都手動創建,IDEA自動構建作為了解就好!

xml空白模板

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
</web-app>
IDEA自動構建項目結構
image-20200617164915737
手動構建web項目結構
基於main目錄下創建webapp文件夾
image-20200617124249878
image-20200617124319926
基於webapp目錄創建WEB-INF文件夾
image-20200617124624648
基於WEB-INF目錄創建web.xml文件
image-20200617125038803
image-20200617125055216
xml文件內容展示
image-20200617125729115
基於webapp目錄創建index.jsp文件
image-20200617125613861
目錄展示 (完整的web項目目錄結構)
image-20200617125808227
5.7.4 tomact的引入

關於Tomact服務的引入,需要我們手動添加tomact服務

添加tomact服務后,如果對tomact服務器在IDEA中的開發流程不熟悉的小夥伴,不要灰心。請參考tomact服務器基礎和開發步驟即可,此文章中詳細講到了關於tomact的各種知識點!

添加tomact服務
image-20200617131801103

5.8 pom文件的其他標籤操作

5.8.1 build標籤修改默認打包名

默認的打包名稱:artifactid+verson.打包方式

我們可以通過build中finalName修改,如下操作:

<build>
  <finalName>maven_name</finalName>
</build>  
5.8.2 引入插件

dependencies引入開發需要的jar包,有時候我們還需要引入一些常用的插件,比如:jdk、tomact、分頁插件等等

插件配置位置也是在<build>標籤中,如下:

<build>
<plugins>
    <!-- java編譯插件,配jdk的編譯版本 -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
    <!-- tomcat插件 -->
    <plugin>
      <groupId>org.apache.tomcat.maven</groupId>
      <artifactId>tomcat7-maven-plugin</artifactId>
      <configuration>
        <port>8080</port>
        <path>/</path>
        <uriEncoding>UTF-8</uriEncoding>
        <server>tomcat7</server>
      </configuration>
    </plugin>
  </plugins>
</build>
5.8.3 控制打包資源

如果在java文件夾中添加java類,會自動打包編譯到classes文件夾下。但是xml文件默認不會被打包,需要我們手動指定打包。可以使用<resources>標籤來指定要打包資源的文件夾要把哪些靜態資源打包到classes根目錄下

<!--打包指定靜態資源-->
<build>
<resources>
    <resource>
      <!-- 指定要打包資源的文件夾 要把哪些靜態資源打包到 classes根目錄下-->
      <directory>src/main/resources</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/
*.properties</include>
      </includes>
    </resource>
    <resource>
      <directory>src/main/resources</directory>
      <excludes>
        <exclude>spring/*</exclude>
      </excludes>
      <includes>
        <include>*.xml</include>
        <!--<include>*/
*.properties</include>-->
      </includes>
    </resource>
  </resources>
</build>

六、依賴的生命周期

6.1 什麼是依賴的生命周期

jar文件的生效時間段可以成為依賴的生命周期

6.2 依賴的生命周期分類與詳解

前三個是常用的依賴生命周期設置,而後兩個製作了解即可,幾乎用不到!

標識 周期
compile 缺省值(默認依賴生命周期),適用於所有階段(測試運行,編譯,運行,打包)
provided 類似compile,期望JDK、容器或使用者會提供這個依賴。如servlet-api.jar;適用於(測試運行,編譯)階段
test 只在測試時使用,適用於(編譯,測試運行)階段,如 junit.jar
runtime 依賴在編譯器不使用,只在運行時使用,如 mysql的驅動jar,適用於(運行,測試運行)階段
system Maven不會在倉庫中查找對應依賴,在本地磁盤目錄中查找;適用於(編譯,測試運行,運行)階段

6.3 依賴生命周期的使用

關於依賴生命周期的使用,需要在期望指定生命周期的依賴內添加<scope>標籤,在此標籤內添加所需依賴標識。

complie

jstl依賴默認沒有compile標識的生命周期。因為在依賴中不指定生命周期就是默認指定適用於所有階段的生命周期,其默認標識為compile。

image-20200617135847510

provided

servlet和jsp依賴默認指定provided標識的生命周期。因為我們在servlet或jsp代碼時是需要這兩個依賴的,但是我們將項目部署到tomact中,本地tomact目錄的lib文件夾下也會有一些jar文件,所以這造成了一種依賴衝突。為了避免這種依賴衝突我們需要指定依賴的生命周期為編譯和測試運行階段。這樣我們在書寫代碼時,編譯期也有有依賴可以使用,不會飄紅,而在過了編譯期後項目部署到了tomact中,該依賴聲生命就會被結束掉了,不會影響tomact服務器內置依賴的使用!

image-20200617135818392

test

在maven項目中,項目結構是區分main主文件和test測試文件的。如果我們在使用Junit單元測試時,指定依賴的生命周期為test,那該依賴只適用於test測試文件內,在其他文件的階段默認沒有單元測試的依賴。

簡單來說,在test文件夾內創建的測試類,使用@Test註解不會有任何問題。如果換做在main文件夾和其他文件夾中創建測試類,使用@Test註解就會因沒有依賴注入而報錯。

image-20200617140515452

runtime

我們在添加mysql驅動的依賴時,你會發現它並沒有指定生命周期為runtime。這是因為我們在書寫jdbc工具類的操作時,如果在編譯期沒有mysql驅動的依賴,它並不會飄紅報錯。如果沒有依賴只有在我們運行的時候才會發生報錯,並告知mysql驅動依賴未找到。所以,這就顯得runtime這個依賴生命周期十分的雞肋。因此,可以不指定該生命周期。

image-20200617141145812

system

當依賴的生命周期設置為system時,表示該依賴項是我們自己提供的,不需要Maven到倉庫裏面去找。
指定scope為system需要與另一個屬性元素systemPath一起使用,它表示該依賴項在當前系統的位置,使用的是絕對路徑。由於此類依賴不是通過 Maven 倉庫解析的,而且往往與本機系統綁定,可能造成構建的不可移植,因此應該慎用。systemPath 元素可以引用環境變量。

image-20200617142301138

七、maven的構建命令

7.1 項目的構建過程

7.2 常用構建命令

一般命令的鍵入在IDEA中的框中鍵入命令就可以!

image-20200617170717904

命令 描述
mvn compile(常用) 編譯項目,生成target文件
mvn package(常用) 打包項目,生成war或jar文件
mvn clean(常用) 清理編譯或打包后的項目結構
mvn install(常用) 打包後上傳到Maven本地倉庫
mvn source:jar 打包項目,生成jar包
mvn deploy 只打包,不測試
mvn site 生成站點
mvn test 執行測試源碼

7.3 maven項目的生命周期

maven項目的生命周期分為了三個階段,而這三個階段相互獨立、互不影響

  1. 清理生命周期(Clean Lifecycle): 該生命周期負責清理項目中多餘信息,保持資源和代碼的整潔性。一般用來清空目錄下的文件
  2. 默認構建生命周期(Default Lifeclyle): 該生命周期表示項目的構建過程,其中定義了一個項目構建要經歷的不同階段
  3. 站點管理生命周期(Site Lifecycle): site生命周期的目的是建立和發布項目站點,Maven能夠基於POM所包含的信息,自動生成一個友好的站點

clean

該生命周期主要是對項目編譯生成的文件進行清理,清理的話主要是清理編譯後項目中的target文件夾,清理后還可以通過編譯指令重新生成。

  • 命令: mvn clean

default

該生命周期的主要目的是項目的編譯 -> 測試 -> 打包 -> 發布

  • 命令: mvn deploy
  • 其中default生命周期分為23個階段,如下列舉比較重要的幾個階段
  • validate:驗證工程是否正確,所有需要的資源是否可用。
  • compile:編譯項目的源代碼。
  • test:使用合適的單元測試框架來測試已編譯的源代碼。這些測試不需要已打包和布署。
  • Package:把已編譯的代碼打包成可發布的格式,比如jar。
  • integration-test:如有需要,將包處理和發布到一個能夠進行集成測試的環境。
  • verify:運行所有檢查,驗證包是否有效且達到質量標準。
  • install:把包安裝到maven本地倉庫,可以被其他工程作為依賴來使用。
  • Deploy:在集成或者發布環境下執行,將最終版本的包拷貝到遠程的repository,使得其他的開發者或者工程可以共享。

site

該生命周期的主要是建立和發布項目站點,Maven能夠基於POM所包含的信息,自動生成一個友好的站點

  • 命令: mvn site

注意: 低版本的site插件可能引發失敗現象!升級高版本site插件即可!

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-site-plugin</artifactId>
  <version>3.7.1</version>
</plugin>

7.4 maven命令與插件的關係

maven命令是操作maven項目的重要方式,但是我們要知道maven命令只是支配maven插件工作的一個方式,其工作核心主要還是maven插件。maven內嵌了項目操作的插件,我們只需要通過執行命令來調用插件來完成項目的編譯、測試、發布等工作

注意: 執行一次命令可能會觸發多個插件工作

八、傳遞依賴和衝突解決

8.1 什麼是傳遞依賴

假如有Maven項目A,項目B依賴A,項目C依賴B。那麼我們可以說 C依賴A。也就是說,依賴的關係為:C – > B – > A, 那麼我們執行項目C時,會自動把B、A都下載導入到C項目的jar包文件夾中,這就是依賴的傳遞性。

8.2 什麼是依賴衝突

依賴衝突我在上文中也提到過,依賴衝突是當直接引用或者間接引用出現了相同的jar包擁有不同版本的時候。

舉個例子:A依賴於B,B依賴於C,此時C的版本為V1.0;如果此時引入的C依賴還有一個V2.0,那麼我們的A傳遞依賴於C,此時C的版本為V2.0。這時候就是一個衝突,直接或間接的都引用了C,而C版本有兩個!

8.3 手動解決依賴衝突

如果我不想在C依賴中出現B,那麼就可以主動的使用依賴排除技術,排除B依賴的引用,如下操作需要添加排除依賴的配置信息(<exclusions>標籤和其中信息,其中兩個id標籤是需要排除依賴的id):

     <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.12.RELEASE</version>

            <!--手動排除依賴-->
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>

        </dependency>
     </dependencies>

8.4 自動解決依賴衝突

8.4.1 自動解決依賴衝突的途徑

自動解決依賴通途問題的兩個途徑分為:短路優先原則先聲明優先原則

8.4.2 短路優先原則

首先,先看如下依賴關係

  1. A – > B – > C – > D – > E(V1.0)
  2. A – > F – > E(V2.0)

解釋: 1中,A依賴於B,B依賴於C,C依賴於D,D依賴於E,此時E的版本為V1.0版本(2中解釋基本相似)

所謂短路優先,就是路徑段的依賴有限被加載使用,這裏我們可以看到2中的路徑是最短的路徑,所以maven在加載依賴的時候,是使用2中的這一條依賴和版本

8.4.3 先聲明優先原則

針對依賴路徑長度相同的情況,則使用先聲明優先原則,看如下依賴關係

  1. A – > B – > C(V1.0)
  2. A – > D – > C(V2.0)

此時路徑優先原則不能解決問題時,maven需要判斷在A項目的<depencies>標籤內,B和D的哪個依賴聲明在前面,如果B依賴聲明早於D依賴,那麼就使用1中的這一條依賴和版本

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

【其他文章推薦】

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

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

※想知道最厲害的網頁設計公司“嚨底家”!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

聚甘新