Python進階——詳解元類,metaclass的原理和用法

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

今天是Python專題第18篇文章,我們來繼續聊聊Python當中的元類。

在上上篇文章當中我們介紹了type元類的用法,在上一篇文章當中我們介紹了__new__函數與__init__函數的區別,以及它在一些設計模式當中的運用。這篇文章我們來看看metacalss與元類,以及__new__函數在元類當中的使用。

上一篇文章非常重要,是這一篇的基礎,如果錯過了上篇文章,推薦回顧一下:

Python面試常見問題,__init__是構造函數嗎?

metaclass

metaclass的英文直譯過來就是元類,這既是一個概念也可以認為是Python當中的一個關鍵字,不管怎麼理解,對它的內核含義並沒有什麼影響。我們可以不必糾結,就認為它是類的類的意思即可。在這個用法當中,支持我們自己定義一個類,使得它是後面某一個類的元類。

之前使用type動態創建類的時候,我們傳入了類名,和父類的tuple以及屬性的dict。在metaclass用法當中,其實核心相差不大,只是表現形式有所區別。我們來看一個例子即可:

class AddInfo(type):
    def __new__(cls, name, bases, attr):
        attr['info'] = 'add by metaclass'
        return super().__new__(cls, name, bases, attr)
        
        
class Test(metaclass=AddInfo):
    pass

在這個例子當中,我們首先創建了一個類叫做AddInfo,這是我們定義的一個元類。由於我們希望通過它來實現元類的功能,所以我們需要它繼承type類。我們在之前的文章當中說過,在Python面向對象當中,所有的類的根本來源就是type。也就是說Python當中的每一個類都是type的實例。

我們在這個類當中重載了__new__方法,我們在__new__方法當中傳入了四個參數。眼尖一點的小夥伴一定已經看出來了,這個函數的四個參數,正是我們調用type創建類的時候傳入的參數。其實我們調用type的方法來創建類的時候,就是調用的__new__這個函數完成的,這兩種寫法對應的邏輯是完全一樣的。

我們之後又創建了一個新的類叫做Test,這個當中沒有任何邏輯,直接pass。但是我們在創建類的時候指定了一個參數metaclass=AddInfo,這裏這個參數其實就是指定的這個類的元類,也就是指定這個類的創建邏輯。雖然我們用代碼寫了類的定義,但是在實際執行的時候,這個類是以metaclass為元類創建的。

根據上面的邏輯,我們可以知道,Test類在創建的時候就被賦予了類屬性info。我們可以驗證一下:

拓展類功能

上面這段就是元類的基本用法了,其實本質上和我們之前介紹的type的動態類創建是一樣的,只不過展現的形式不同。那麼我們就有一個問題要問了,我們使用元類究竟能夠做什麼呢?

這裡有一個經典的例子,我們都知道Python原生的list是沒有’add’這個方法的。假設我們習慣了Java當中list的使用,習慣用add來為它添加元素。我們希望創建一個新的類,在這個新的類當中,我們可以通過add來添加函數。通過元類可以很方便地使用這一點。

class ListMeta(type):
    def __new__(cls, name, bases, attrs):
        # 在類屬性當中添加了add函數
        # 通過匿名函數映射到append函數上
        attrs['add'] = lambda self, value: self.append(value)
        return super().__new__(cls, name, bases, attrs)
    
    
class MyList(list, metaclass=ListMeta):
    pass

我們首先是定義了一個叫做ListMeta的元類,在這個元類當中我們給類添加了一個屬性叫做add。它只是包裝了一下而已,底層是通過append方法實現的。我們來實驗一下:

從結果來看也沒什麼問題,我們成功通過調用add方法往list當中插入了元素。這裏藏着一個小細節,我們在ListMeta當中為attrs添加了一個名叫’add’的屬性。這個屬性是添加給類的,而不是類初始化出來的實例的。所以如果我們print出MyList這個類當中的所有屬性,也能看到add的存在。

如果我們直接去通過MyList去訪問add方法的話會引起報錯,因為我們實現add這個方法邏輯的匿名函數限制了需要傳入兩個參數。第一個參數是實例的對象self,第二個參數才是添加的元素value。如果我們通過MyList的類屬性去訪問它的話會觸發一個錯誤,因為缺少了一個參數。因為類當中的屬性實例也是可以調用的,並且Python會在參數前面自動添加self這個參數,就剛好滿足了要求。

搞明白了這些我們只是解決了可能性問題,我們明白了元類可以實現這樣的操作,但沒有解決我們為什麼必須要使用元類呢?就拿剛才的例子來說,我們完全可以繼承list這個類,然後在其中再開發我們想要的方法,為什麼一定要使用元類呢?

就剛才這個場景來說,的確,我們是找不出任何理由的。完全沒有理由不使用繼承,而非要用元類。但是在有些場景和有些問題當中,我們必須要使用元類不可。就是涉及類屬性變更和類創建的時候,我們來看下面這個例子。

控制實例的創建

還記得我們上篇文章介紹的工廠設計模式的例子嗎?就是我們可以通過參數來得到不同類的實例。

我們創建了三種遊戲的類和一個工廠類,我們重載了工廠類的__new__函數。使得我們可以根據實例化時傳入的參數返回不同類型的實例。

class Last_of_us:
    def play(self):
        print('the Last Of Us is really funny')
        
        
class Uncharted:
    def play(self):
        print('the Uncharted is really funny')
        

class PSGame:
    def play(self):
        print('PS has many games')
        
        
class GameFactory:
    games = {'last_of_us': Last_of_us, 'uncharted': Uncharted}
    def __new__(cls, name):
        if name in cls.games:
            return cls.games[name]()
        else:
            return PSGame()
        

uncharted = GameFactory('uncharted')
last_of_us = GameFactory('last_of_us')

假設這個需求完成得很好順利上線了,但是運行了一段時間之後我們發現下游有的時候為了偷懶會不通過工廠類來創建實例,而是直接對需要的類做實例化。原本這沒有問題,但是現在產品想要在工廠類當中加上一些埋點,統計出訪問我們工廠的訪問量。所以我們需要限制這些遊戲類不能直接實例化,必須要通過工廠返回實例

那麼這個功能我們怎麼實現呢?

我們分析一下問題就會發現,這一次不是需要我們在創建實例的時候做動態的添加,而是直接限制一些類不允許直接調用進行創建。限制的方法比較常用的一種就是拋出異常,所以我們希望可以給這些類加上一個邏輯,實例化類的時候傳入一個參數,表明是否是通過工廠類進行的,如果不是,則拋出異常

這裏,我們需要用到另外一個默認函數,叫做__call__,它是允許將類實例當做函數調用。我們通過類名來實例化,其實也是一個調用邏輯。這個__call__的邏輯並不難寫,我們隨手就來:

def __call__(self, *args, **kwargs):
    if len(args) == 0 or args[0] != 'factory':
        raise TypeError("Can't instantiate directly")

但問題是這個__call__函數並不能直接加在類當中,因為它的應用範圍是實例,而不是類。而我們希望的是在創建實例的時候進行限制,而不是對調用實例的時候進行限制,所以這段邏輯只能通過元類實現

我們直接創建類的時候就會觸發異常,因為不是通過工廠創建的。我們這裏判斷是否是工廠創建的邏輯簡化掉了,只是通過一個簡單的字符串來進行的判斷,實際上會用一些更加複雜的邏輯,這不是本文的重點,我們了解即可。

整體運行的邏輯和我們設想的一樣,說明這樣實現是正確的。

總結

我們日常開發當中用到元類的情況非常罕見,一般都是在一些高端開發的場景當中。比如說開發一些框架或者是中間件,為了方便下游的使用,需要創建一些關於類屬性的動態邏輯,才會用到元類。對於普通開發者而言,如果你無法理解元類的含義以及應用,也沒有關係,使用頻率非常低。

另外,元類的概念和動態類、動態語言的概念有關,Python語言的動態特性很多正是通過這一點體現的。所以隨着我們對於Python動態特性理解的加深,理解元類也會變得越來越容易,同樣也會理解越來越深刻。如果我們把Python的元類和裝飾器做一個類比的話,會發現兩者的核心邏輯是很類似的。本質上都是在原有的邏輯之外封裝新的邏輯,只不過裝飾器針對的是一段邏輯,而元類針對的是類的屬性和創建過程。

仔細思考,我相信一定會有靈光乍現的感覺。

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

本文使用 mdnice 排版

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

【其他文章推薦】

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

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

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

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

.NET Framework、.NET Core 和 .NET 5+ 的產品生命周期

.NET Framework、.NET Core 和 .NET 5+ 的產品生命周期

本文整理記錄了 .NET Framework、.NET Core 和 .NET 各個版本的產品支持周期和操作系統兼容性。

早於 .NET Framework 2.0 和 .NET Core 2.1 的 .NET 版本以及 .NET Core 2.2、.NET Core 3.0 已經停止了任何形式的支持,因此本文不做討論。

相關文章:

  • .NET Framework、.NET Core 和 .NET 5+ 的產品生命周期(本文)
  • Visual Studio 的產品生命周期
  • DevExpress 各個版本與 .NET、Visual Studio 的版本兼容性

.NET 版本說明

  • .NET Framework 4.5 是可替換計算機上的 .NET Framework 4 的就地更新,同樣,.NET Framework 4.5.1、4.5.2、4.6、4.6.1、4.6.2、4.7、4.7.1、4.7.2 和 4.8 是對 .NET Framework 4.5 的就地更新。 就地更新意味着它們使用相同的運行時版本,但是程序集版本會更新,並且包括新類型和成員。 安裝其中一個更新后,.NET Framework 4、.NET Framework 4.5、.NET Framework 4.6 或 .NET Framework 4.7 應用應繼續運行,而無需重新編譯。 但是,反過來則不行。 建議不要在較早版本的 .NET Framework 上運行面向更高版本的 .NET Framework 的應用。 例如,建議在 .NET Framework 4.5 上運行面向 .NET Framework 4.6 的應用。(參見 版本 4.5 及更高版本的備註)

  • .NET Core 為一個全新的跨平台框架,支持 Windows、Linux 和 macOS 等多種平台,以 MIT 協議完全開源。.NET Core 最早於 2014 年公布,首個版本 .NET Core 1.0 發佈於 2016 年。隨着版本的演進,.NET Core 不斷加入 .NET Framework 原有的功能,直到 .NET Core 3.1,完成這一進程。剩餘少量 .NET Core 不支持的 .NET Framework 技術 不再向 .NET Core 移植。

  • .NET Core 3.1 之後,隨着技術移植進程的結束,.NET Core 的後續版本將作為 .NET Framework 和 .NET Core 的繼任者,移除 “Core” 字樣直接稱作 “.NET”,版本號自 “.NET 5” 開始。因此,.NET Framework 4.8 將會是 .NET Framework 的最後一個版本。

.NET 支持政策

.NET Core 和 .NET 的版本類型

本節整理自 .NET Core 支持政策。

.NET Core/.NET 分為長期支持 (LTS) 版本和最新 (Current) 版本。自 .NET Core 2.2 起,兩者的支持周期如下:

  • LTS (Long Term Support): 在初始發布后的三年內受支持。
  • Current:在初始發布后,直到下一個 Current 或 LTS 版本發布后的三個月內受支持。

.NET Core 和 .NET 發布節奏

本節整理自 .NET 5 簡介

  • 自 2020 年起,每年 11 月為 .NET 發布新的主要版本,版本號自 5.0 起遞增。即 2020 年 11 月發布 .NET 5.0,2021 年 11 月發布 .NET 6.0,以此類推。

  • 自 2021 年起,將隔年發布的主要版本標記為 LTS 版本(即偶數的主要版本為 LTS 版本,奇數的為 Current 版本)。

.NET Core 和 .NET 生命周期詳情

本節整理自 .NET 5 簡介 以及 .NET Core 支持政策,僅列出了生命周期尚未結束的版本。

版本 初始發布時間 支持級別 結束支持時間
.NET 8 2023 年 11 月(預計) LTS 2025 年 11 月(預計)
.NET 7 2022 年 11 月(預計) Current 2023 年 2 月(預計)
.NET 6 2021 年 11 月(預計) LTS 2024 年 11 月(預計)
.NET 5 2020 年 11 月(預計) Current 2022 年 2 月(預計)
.NET Core 3.1 2019 年 12 月 3 日 LTS 2022 年 12 月 3 日
.NET Core 2.1 2018 年 5 月 30 日 LTS 2021 年 8 月 21 日

.NET Framework 各版本支持政策

本節內容整理自 .NET Framework 生命周期常見問題。

  • .NET Framework 4.5.2 及以上版本被定義為 Windows 操作系統的一個組件,與其父產品獲得相同的生命周期,詳見下節內容。

  • .NET Framework 4、4.5 和 4.5.1 已於 2016 年 1 月 12 日停止支持,客戶和開發人員必須就地更新到 .NET Framework 4.5.2 及以上版本,才能技術獲得技術支持和安全更新。

  • .NET Framework 3.5 SP1 在 Windows 10 v1809 和 Windows Server 2019 及以上版本中作為獨立的產品存在,自 2018 年 10 月 2 日起獲得 5 年主流支持和 5 年擴展支持。

  • .NET Framework 3.5 SP1 在 Windows 10 v1809 和 Windows Server 2019 以前的操作系統中,作為操作系統的組件存在,其生命周期與其父產品相同。

  • .NET Framework 2.0、3.0 和 3.5 已先後停止支持,客戶和開發人員可就地更新到 .NET Framework 3.5 SP1,以便在後者的生命周期內獲得技術支持和安全更新。由於 .NET Framework 3.5 依賴於 2.0 以及 3.0,因此 .NET Framework 2.0 SP2 和 .NET Framework 3.0 SP2 組件在安裝了 .NET Framework 3.5 SP1 環境當中得到支持。

.NET Framework 版本和操作系統版本的關係

本節內容整理自 .NET Framework 版本和依賴關係 以及 .NET Framework 系統需求。

  • .NET Framework 4.5 預裝在了 Windows 8 和 Windows Server 2012 中。此後,每個版本的 Windows 操作系統都包含了特定版本的 .NET Framework 4.x。

  • .NET Framework 4.8 預裝在了 Windows 10 v1903 (build 18362) 及以上版本中。

  • .NET Framework 4.6.1 及以上版本可以安裝在 Windows 7 和 Windows Server 2008 R2 SP1 及以上版本的 Windows 操作系統中,但不可以安裝低於預裝於操作系統中的版本。

  • .NET Framework 4.6 為 Windows Vista 和 Windows 2008 SP2 支持的最高版本。

  • .NET Framework 4.0.3 為 Windows XP 和 Windows Server 2003 支持的最高版本。(參見 在 Windows XP 和 Windows Server 2003 上安裝 .NET Framework)

    注意:.NET Framework 4、.NET Framework 4.0.3 和 Windows XP、Windows Server 2003 已經停止支持,不會再接收任何形式的安全更新。

  • .NET Framework 3.5 SP1 在 Windows XP 和 Windows Server 2013 及以上版本的 Windows 操作系統中,可以使用 安裝程序 進行安裝。

  • .NET Framework 3.5 SP1 在 Windows 10,Window Server 2016 和 Windows Server 2019 中可以通過控制面板直接啟用。

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

聚甘新

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/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

聚甘新

新能源車夯,比亞迪前三季獲利有望跳增九成

中國電動汽車商比亞迪(BYD)周日公布,2016年前九個月盈餘最高有望年增逾九成(91%),主要受惠於中國政策大力推動節能汽車。

比亞迪有股神巴菲特光環加持,上個月又獲得三星入股,可謂左右逢源。據比亞迪表示,上半年獲利來到23億人民幣,較去年同期跳增384%,若連同本季獲利,今年前三季預估將成長83-91%。

比亞迪以製造電池起家,2003年併購秦川汽車後跨足汽車市場,並為日後進軍電動車鋪路。憑藉著電池技術領先同業,比亞迪在中國新能源車領域已先立於不敗之地,近年隨著環保法規轉嚴,比亞迪生產之油電混合車與全電動車業績更是蒸蒸日上。

三星七月下旬以約30億人民幣參與比亞迪新一輪募資,亦是著眼於中國新能源車的商機。不過在三星正式入股後,巴菲特掌舵的柏克夏海瑟威公司(Berkshire Hathaway)對比亞迪的持股比例,則從原先的9.1%稀釋至8.25%。

(本文內容由授權提供)

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

【其他文章推薦】

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

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

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

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

IEA:今之電動車有如10年前的太陽能

英國金融時報31日報導,瑞銀(UBS)預估2021年歐洲未經補貼的純電動車整體持有成本將與傳統內燃機汽車相當、中國也可望在2025年達到這項里程碑,美國則因油價相對低廉、在可預見的未來都無法看到。國際能源署(IEA)首席經濟學家Laszlo Varro指出,電動車目前對商品(原油)市場的影響力大約就像是10年前的太陽能一樣。他說,太陽能現在已是一個規模達數百億美元的市場、擁有龐大的影響力。Varro提到,電動車需達5千萬至1億台的規模、才能取代相當於100萬桶的石油日消費量。

IEA數據顯示,2009年全球40個國家合計僅有不到6千台的電動車、去年已升至120萬台。麥格理集團全球能源策略師Vikas Dwivedi指出,沙烏地阿拉伯對電動車的長期發展存有戒心,這可能就是它為何宣布將讓沙烏地阿拉伯國家石油公司(Saudi Aramco)初次公開發行(IPO)的原因之一。

CNNMoney去年底報導,石油輸出國組織(OPEC)發表的年度「世界石油展望(World Oil Outlook)」報告顯示,2040年高達94%的使用中車輛仍將是依靠石油燃料。OPEC報告顯示,除非出現重大技術性突破,否則在可預見的未來電動車將難以大幅取得市佔率。油國組織預估2040年僅有1%的車輛銷售量是來自純電動車款。

華爾街日報6月報導,根據能源咨詢公司Wood Mackenzie發布的報告,未來20年電動車的普及可能會導致美國汽油需求縮減5-20%。美國目前平均每天的汽油使用量超過900萬桶。報告顯示,如果電動車2035年市占比重因特斯拉(Tesla)等廠商開始推出較低價車款以及續航力和便利性(註:目前大約每100英里就得充電一次)顯著突破而站上35%,美國日需求量可能會減少200萬桶。

即便電動車現在還沒有茁壯到可以侵蝕汽油需求的地步,美國依舊面臨油品供給過剩的問題。

美國石油協會(API)8月18日公布,2016年7月衡量消費者汽油需求的指標(汽油交運量)創下史上最高紀錄、日均量接近970萬桶。美國7月汽油日均產量同樣創下歷史新高、年增1.9%(月增1.3%)至1,020萬桶!

能源情報署(EIA)公布,截至2016年8月19日為止當週美國商用汽油庫存報2.327億桶、較去年同期高出8.5%。EIA公布的數據顯示,美國一般汽油每加侖零售均價8月22日報2.193美元,較一年前短少0.444美元。

(本文內容由授權提供)

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

【其他文章推薦】

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

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

※超省錢租車方案

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

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

聚甘新

中國大陸推排放標準,控管油電混和車排放

大陸環保部30日發布涉及船舶發動機、摩托車、輕便摩托車、輕型混合動力電動汽車和燒鹼、聚氯乙烯(PVC)工業排放的五項國家污染物排放標準,分別為《船舶發動機排氣污染物排放限值及測量方法(中國第1、2階段)》、《摩托車污染物排放限值及測量方法(中國第4階段)》、《輕便摩托車污染物排放限值及測量方法(中國第4階段)》、《輕型混合動力電動汽車污染物排放控制要求及測量方法》和《燒鹼、聚氯乙烯工業污染物排放標準》。上述標準旨在落實《大氣污染防治行動計畫》和《水污染防治行動計畫》,透過制定、修訂重點行業的排放標準力促產業轉型升級。

大陸環保部科技標準司司長鄒首民表示,實施這五項標準將可望大幅削減顆粒物(PM)、氮氧化物(NOx)、二氧化硫(SO2)的污染,有效促進行業技術進步和環境品質的改善。他指出,新標準適用於具有中國船籍在大陸水域航行或作業的船舶裝用的額定淨功率大於37千瓦、新生產船用發動機的環境管理,不適用於遠洋船舶,遠洋運輸船舶執行國際公約的相關規定。

鄒首民指出,上述新標準實施後,按照每年新增船機1,000萬千瓦,壽命期為25年計算,實施第一階段標準3年,所製造投入使用的船機在全壽命期內將減排NOX約140萬噸,PM約40萬噸;若實施第二階段標準3年,也將進一步減排NOX約115萬噸,PM約6萬噸。

另外,對於產量增長迅速的摩托車污染問題也正在引起重視。鄒首民說,為能有效控制摩托車污染,環保部制定了摩托車和輕便摩托車國四標準,自2019年7月1日起,所有新銷售和註冊登記的摩托車和輕便摩托車,應滿足新的標準;以國四標準實施3年估算,這期間,新生產的全部摩托車在其整個使用壽命內,將比實施國三標準減少CO排放約650萬噸、HC排放約200萬噸、NOx排放約30萬噸。

他也指出,近年來大陸國家積極鼓勵發展包括混合動力電動汽車在內的節能與新能源汽車,但由於有電能的輔助,傳統汽車的測量方法無法準確評判混合動力電動汽車的污染物排放狀況;新標準是對《輕型混合動力電動汽車污染物排放測量方法》的修訂,規定了輕型混合動力電動汽車的污染控制要求和測量方法,新標準適用於國四、國五階段的輕型混合動力汽車的環保管理,該測量方法標準的實施,將不會帶來額外的車輛技術升級成本。

(本文內容由授權提供)

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

聚甘新

.Net微服務實戰之DevOps篇,.Net微服務實戰之技術選型篇,.Net微服務實戰之技術架構分層篇

技術只是基礎

  該系列的兩篇文章《.Net微服務實戰之技術選型篇》和《.Net微服務實戰之技術架構分層篇》都是以技術角度出發描述微服務架構的實施。

  如果技術選型篇敘述的是工具,那麼架構分層篇講的就是技巧,而本篇要討論的就是原則。一直以來我會給身邊向我探討問題的人灌輸一種理念,沒有什麼技術銀彈,因為我們做的是軟件工程,提供的是問題相應的解決方案,不同類型問題的解決方案是存在着本質上的差異。

  繼續提供之前的源碼:https://github.com/SkyChenSky/Sikiro

PS:該篇文章與.Net無關,其實主要是沿用前面兩篇文章的命名,此外我認為DevOps不是簡單的工具使用,應從軟件工程角度進行出發。

什麼才是優秀的架構設計?

  曾經有好幾個同行問過我同一個問題:什麼才是優秀的架構設計?我一直信奉着兩句話一個定律

  • 架構服務於業務,技術服務於架構 
  • 康威定律(簡單理解成組織架構的設計等同於系統架構的設計)

  架構設計其實就是一種方案取捨,在有限資源里(包括但不限人力、時間)能讓團隊順利的實施技術,同時滿足業務規模的需要,我認為可以稱之為優秀的架構設計,簡單來說兩個字合適

 

架構核心要素

  核心的主要5大:性能、可用性、伸縮性、擴展性、安全性。 

  而我們所討論的微服務,選擇了擴展性,犧牲了可用性、性能,擴展性的目的就是為了快速響應需求變化降低系統耦合度提高系統模塊的復用度。而微服務的調用是通過跨進程的網絡通信的,跟進程內方法調用比無疑是慢了一個單位;原本單服務99.99%高可用,假如現在三個服務就是99.99%*99.99%*99.99%=99.97%。

  當然我們可以在基於微服務的通過引入其他技術提高可用性、伸縮性和安全,但是確保無疑是犧牲了性能,除了性能還會在團隊開發效率與運維複雜度上會受到影響。由此可見,沒有萬能技術手段,而架構其實在取捨。

  引入一種技術必定帶新的技術問題這是個必然結果,剛提到團隊開發效率運維複雜度會受到影響,那是否有辦法緩解甚至解決並提高呢?既然涉及到團隊、流程這些關鍵字那麼就應該向軟件工程方向尋找方案,合適架構實施還需要合適的開發模式進行支撐的,而風靡全球的DevOps就是不二之選。

軟件工程

   在行業盛傳的一條公式:軟件 = 軟件工程 + 程序,可想而知軟件工程的佔據多麼重要的比重。那麼什麼是軟件工程?百度是這麼解釋的:

  軟件工程是研究和應用如何以系統性的、規範化的、可定量的過程化方法去開發和維護軟件,以及如何把經過時間考驗而證明正確的管理技術和當前能夠得到的最好的技術方法結合起來的學科。它涉及到程序設計語言、數據庫、軟件開發工具、系統平台、標準、設計模式等方面。

  我自己重新總結了一個軟件工程的通俗描述,通過多人協作、有目標、有步驟、有計劃的並使用科學方法論指導開發與維護程序的這個過程。也可以用一條公式表達:軟件工程 = 工具 + 流程 + 模式。

 

軟件危機

  軟件工程的出現目的是為了解決軟件危機的。軟件危機其實是當時落後的軟件生產方式無法滿足迅速增長的計算機軟件需求,從而導致軟件開發與維護過程中出現一列的嚴重問題的現象。那麼三次軟件危機是什麼呢?我整理了個表格(詳細可以自行百度閱讀)

名稱 時間 原因 解決方案
第一次軟件危機

20世紀60年代—70年代

使用機器語言或者彙編語言在特定的機器上進行軟件的設計與編寫,引出的“抽象性”和“可移植性”的問題 高級的編程語言+瀑布開發模式
第二次軟件危機

20世紀80年代—90年代

軟件複雜性進一步升級,需要更好更好的“可組合性”(Composability)、“可延展性”(Malleability)以及“可維護性”(Maintainability) 面向對象編程語言+設計模式
第三次軟件危機 2005年至今 軟件的發展速度已經遠超於硬件的發展,體現於需求複雜度、技術複雜度、團隊協作 更好的工具、開發模式、與協作流程

 

   由上可見,軟件的快速發展直接促使了軟件工程上的進步,新的工具、新的開發與設計模式,新的協作流程也隨之而生。

開發模式的發展

  我工作多年經歷了多家公司,所經歷的有三種開發模式,瀑布、敏捷、DevOps。那麼這三種主流的開發模式也對應着三個發展階段:

瀑布開發模式

  瀑布開發模式是在第一次軟件危機1970時Winston Royce博士提出來。其思想是把項目過程劃分為主要的六個階段:需求收集、需求分析、軟件設計、程序編碼、軟件測試、運行維護。團隊劃分也通過崗位職責進行劃分:產品團隊、開發團隊、測試團隊、運維團隊。到目前為止該開發模式仍然用到做項目制的開發團隊。

  那麼其優點與劣勢也很明顯,優點是計劃明確,職責清晰,按部就班的完成就好。缺點是周期容易拖得太長,不容易調整變更,每個人只為自己職責範圍內的負責,跨部門溝通成本大(這就是為什麼我在圖裡畫了兩堵牆的原因)。我自己呆過一個瀑布模式的團隊,在項目立項后就會被項目經理調動資源成為團隊,而開發人員只會在這一次批次負責編碼與修改測試反饋的問題,基本上上線后的問題跟你無關(除非緊急嚴重的),其他的BUG也許是下一個批次的另外一個開發人員幫你填。

 敏捷開發模式

  準確的說敏捷開發是一種價值觀和原則的體現,2001年17位IT大佬想把瀑布發模式這種重量級的開發過程替換成一種更加輕量級,可惜大家都沒有達成統一意見因此把各自都認同的觀念整理出來成為敏捷宣言。

  敏捷開發其實把產品、開發、測試三種崗位職責的人緊密的聯繫了起來,由原來長周期的大目標拆解成了一個個短周期的小目標。他之所以快,不是因為寫代碼快了,而是節省了很多不必要的前置條件與返工,同時小步快跑的交付也可以提高團隊的士氣,一個長周期項目那枯燥、乏味、痛苦的過程,誰試誰知道。

  舉個例子,大家都是為公司的同個產品努力,沒有什麼合同談判可言,只要需求要求相互了解清楚並且可行就可以開幹了。寫詳細設計文檔的時間,還不如花時間多溝通下需求的核心點,想辦法設計得更容易滿足需求。短周期的交付后,產品與客戶就可以及時的查看交付效果並相應的優化與調整。(快速響應並不代表隨時隨地接受變更響應,可以統一歸到下一個迭代周期,我不贊同拍拍腦袋的變更,自己都沒清楚的功能怎麼說服客戶使用?

  

  敏捷開發的最大好處之一就是短周期的持續交付,這樣方式能在現階段的互聯網行業得到更快速的響應與市場的搶佔,同時能很好的進行技術改進與試錯。但是這種”野蠻的“方式會讓開發團隊與運維團隊形成一條鴻溝,而鴻溝的形成主要原因是運維團隊希望軟件的運作是可靠的,所以他們對資源的變動、新技術的使用尤為的小心、謹慎。

  我曾經呆過一個敏捷開發團隊,生產出了問題運維團隊會自行去修改配置,當然會越改越錯了,而且一天發布次數多了,就會起爭執。

DevOps

  DevOps可以看過是敏捷的擴展與延申,它的出現就是為了解決開發團隊與運維團隊的那條鴻溝,只要存在人工處理的方式擔心的問題總會出現,同一段程序無論執行多少次相同輸入的輸出總是一致的,但是人的處理卻不能保證,那麼使用自動化改善協作的過程,鴻溝自然就跨越了,。那麼開發團隊與運維團隊就可以為相同的目標與方向而努力。而組織架構也將演變成如下:

  

  從上圖也與開頭的康威定律做了一個很好的呼應。

 我是如何實施DevOps的?

 

技術

  這個角度是大家最樂意去關注的,在我們團隊主要使用了以下技術,腳本什麼的我就不花時間貼出來了,在我看來工具的使用,只要花點時間就能解決。

類型 名稱
持續集成/持續交付 Jenkins
源代碼管理 Gitlab
雲平台 阿里雲
軟件包管理器 私有Nuget
代碼檢查 Reshaper
容器化 Docker
分佈式鏈路跟蹤 SkyWalking
日誌系統 ES+Filebeat+kibana
系統監控 Prometheus

  原本代碼檢查想引入SonarQube代替人工檢查+Reshaper,可惜於服務器資源不足。

  對於一般的團隊,我建議優先從Gitlab+Jenkins搭建好完成CI/CD,其次把日誌系統給完善起來,這兩者完成得越早,給團隊帶來的收益就越高,後續才會有更多的時間來完善整套技術體系,這是一個良性的循環

  人延申出的就是團隊與文化,經過上面的講解大家都意識到軟件工程就是一樣多人協作的工作,只有團隊目標一致,共同負責承擔團隊的項目,願意一同與項目成長才能很好的實施DevOps。就像多匹馬拉車一樣,只有它們都有共同的目標的時候才能快速拉車到目的,如果他們一匹向東一匹西,只會讓馬車無法前行甚至四分五裂。

  在我的團隊,因為在招聘人員的時候已經進行過了篩選,所以在合作上非常的順利,當然我也經常在例會和業餘的時候都會給大家傳達思想,讓團隊成員真正的從實際意義上去理解現在的做法。

  對於已經成型的團隊來說如何去落地呢?無非三種,激勵、考核和逐步試行。如果有條件的公司可以設置獎金激勵,如果有績效考核的可以將DevOps實施納入考核目標,如果兩者都沒的,那就選取團隊里願意改變的同事進行試行,使用過後都說好的那麼更會有說服力。 

流程 

   為了落實了文化的改進與技術的使用的這個過程,我們需要科學的、有步驟、有計劃的方式完成這項工作,並且可以讓這套標準化的方式可以重複使用到其他項目上。

  在我的團隊是有產品、前端開發,後端開發、測試、運維組成的。我採用了原型模式+DevOps模式:

  •   產品人員會優先使用Axure RP工具把需求整理產出原型並與需求方確認。
  •   產品確認好的原型就是我們技術的輸入,技術拿到需求後會做一次需求評審,主要是排查需求疑惑和確認需求目標。
  •   需求明確后,由我使用Visual Project任務拆解與排期,任務會建立在我們的項目管理系統Redmine上,如果任務周期過程,我會拆分成多個可交付的短周期,一般會控制在2個星期內。
  •   接到任務后,大家就跟根據自己的任務使用PowerDesigner數據庫設計(早期是由我獨裁設計,後期團隊發展壯大了,就由業務負責人各自設計),在這個階段,如果有新的服務與新的工具庫需要部署,我就會正面與運維溝通讓他把自動化給完成。
  •   因為我們是前後端分離的,所以我們使用了Swagger減少了寫接口文檔的時間,所有任務是否完成以前端是否對接好接口為主導,前端對接好后,就會在Redmine修改自己的任務狀態並新建一個測試任務給到測試。
  •   測試會根據自己寫好的測試用例,進行對完成的任務進行場景測試,如果有BUG會在Redmine提給相應的人進行修改。一般會先由前端人員排查是否是他的交互上的BUG,如果確認是數據問題那麼就會轉給後端開發,開發人員定位BUG時,可以通過我們的SkyWalking和Kibana聯合定位問題,定位問題時間一般都在2-10分鐘。
  •   代碼合併到測試分支后就會通過Jenkins發布到測試環境,生產環境的發布是合併到生產環境後手動確認發布的。

  除此之外,每周一會有一個例會內容不限工作,也可以分享周末去哪裡娛樂了。在該迭代周期快到結束的2-3天會開一個進度會議,看看大家完成情況。因為公司沒有下午茶,所以我們自己通過玩搶紅包領到最大的兩個的請吃下午茶,最少一星期一次。

 結束

  該篇到這裏就分享結束了,也是該系列的最後一篇,我曾經認為技術與管理必須二選一,自從我成為了一個技術與團隊的負責人後,終於讓我認識到,一個優秀的技術思想還是需要一些管理手段才能很好的實施,而我們的技術管理無非就是軟件工程

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

【其他文章推薦】

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

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

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

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

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

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

聚甘新

cute-cnblogs 自定義博客園樣式美化二期來啦~

cute-cnblogs 自定義博客園樣式美化二期來啦~

說明

cute-cnblogs 可愛的博客園樣式美化、自定義博客園樣式 二期樣式已經編寫完畢了,如果說 一期樣式 給人的感覺是簡潔清爽的小嬰兒的話,那麼 二期樣式 就是一個有自己小個性(花樣)的小朋友了~

與一期一樣,需要文件的可以來 github ,喜歡我寫的樣式可以幫我點個 star 喔 ღゝ◡╹)ノ

(PS:有什麼問題也可以留言到 github issues 中喔~)

好了,讓我們擼起袖子開始更換二期樣式吧~

博客示例

ღゝ◡╹)ノ 麋鹿魯喲的博客園

介紹

可愛的博客園樣式美化、自定義博客園樣式 ღゝ◡╹)ノ

  • 本樣式以簡約可愛為核心,美化博客園的显示效果,增加自定義導航;
  • 基於博客園主題“Custom”進行的樣式修改;
  • 閱讀目錄導航;
  • 支持響應式;

功能

一期功能

  • 頂部背景點點動效隨鼠標而動
  • 導航欄自定義
  • 頁面詩意詩句模塊
  • 看板娘-貓(ฅ´ω`ฅ) (自行決定是否添加,因為這個有些影響加載速度)
  • 音樂-網易雲 (自行決定是否添加)
  • 上弔的貓(PS:回到頂部)(已用其餘按鈕代替此功能)
  • 底部跳動的魚<・)))><<
  • 點擊頁面跳動的小豆子及可愛的文字(自行決定是否添加可愛文字的點擊)
  • 評論增加OωO聊天表情
  • 頁面不同的導航背景及人物背景

二期增加功能

  • 側邊欄显示
  • 側邊欄公告欄及個人信息显示
  • 閱讀目錄(標題 h1 > h2 > h3 寫了三級目錄)
  • ️ 主題皮膚切換(淺白、暗黑、閱讀)
  • 仿主播點贊功能點擊推薦
  • ️ 讚賞模塊功能

模版選定

博客皮膚選定: 博客園 Custom 標準模板(與一期不同喔)

使用方法

基本操作

請按照順序進行操作喔~

  • 首先記得申請JS權限

  • 其次博客皮膚選擇 Custom

  • 在此需要獲取數據(不然點擊頭像的關注會失敗)
    找一個沒有登陸的瀏覽器訪問自己的博客園,F12彈出窗口,找到 +加關注,複製follow括號里的內容,暫且先存在一個地方

  • 勾選禁用模板默認CSS

  • 創建一個新隨筆(這裏使用選項中的TinyMCE(推薦)來編寫的) —— “友鏈”;(注意,此處已與一期不同)
  • 點擊 “編輯 HTML 源代碼” 插入以下代碼,修改自己的文本內容后,點擊更新;
  • 只勾選 高級選項中的 “發布”、“允許評論”;
<p style="text-align: center;">歡迎來到我的友鏈小屋</p>
<div class="friendsbox">
<div id="app">
<h6 style="text-align: center; color: #2daebf;">展示本站所有友情站點,排列不分先後,均勻打亂算法隨機渲染的喔!</h6>
<div class="unionbox-box" style="display: flex; flex-wrap: wrap; justify-content: space-between; margin-bottom: 1.5rem; margin-top: 1.5rem;">&nbsp;</div>
<hr style="position: relative; margin: 1rem 0; border: 1px dashed #9eabb3; width: calc(100%); height: 0;" />
<h2 style="text-align: center;">友鏈信息</h2>
<h5 style="text-align: left; line-height: 30px;">博客名稱:<a href="javascript:void(0)">麋鹿魯喲</a><br />博客網址:<a href="javascript:void(0)">https://www.cnblogs.com/miluluyo/</a><br />博客頭像:<a href="javascript:void(0)">https://pic.cnblogs.com/avatar/1273193/20190806180831.png</a><br />博客介紹:<a href="javascript:void(0)">大道至簡,知易行難。</a></h5>
<h2 style="text-align: center;">join us</h2>
<h5 style="text-align: center; color: #2daebf;">如需友鏈,請添加微信(s978761)告知,格式如下</h5>
<table class="table friendstable" style="margin: 0 auto;">
<tbody>
<tr><th><strong>字段</strong></th><th><strong>字義</strong></th></tr>

</tbody>

</table>

</div>

</div>

  • 最後分別複製以下區域代碼,並根據參數更改數據(PS:路徑可進行更改也可不更改,我覺得更改后速度會快一點,自行down文件上傳到博客園文件中,並更改引入路徑,文件都在 github 中,喜歡記得給我個star喔~)

頁面定製CSS代碼

複製 https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.css 的文件內容放到 頁面定製CSS代碼 區域

博客側邊欄公告

<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://blog-static.cnblogs.com/files/miluluyo/jquery2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/milusidebar.js"></script>

<script>
milusidebar({
	'names' : '麋鹿魯喲',/*你的博客園名吶*/
	'notice' : '<b>溫馨提示</b><span><a href="https://github.com/miluluyo/cute-cnblogs" target="_black">cute-cnblogs</a> &nbsp;樣式已開源</span><b style="margin-top: 3px;"><a style="font-size:10px" href="https://www.cnblogs.com/IsAlpaca/" target="_black">查看一期樣式</a></b>',/*裏面文字自己可以更改喔*/
	'headerUrl' : 'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png',/*這個是公告欄的背景圖啦,我覺得這個可愛,如果你有更好看的可以自行更改喔*/
	'follow' : 'a1e76459-101d-47af-a8b6-08d523685c8c', /*還記的開始讓你複製follow括號里的內容嗎,對,就放到這裏就好啦*/
	'sidebarInfo' : [[
	      {'icon':'#icon-github1','url':'https://github.com/miluluyo','title':'github'},
	      {'icon':'#icon-weixin','url':'','title':'微信','classname':'popper_weixin','click':false},
	      {'icon':'#icon-QQ','url':'http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes','title':'QQ'},
	      {'icon':'#icon-juejin','url':'https://juejin.im/user/5d18adce5188256e98090e33','title':'掘金'}
	  ],[
	      {'icon':'#icon-weibobangding','url':'https://www.weibo.com/6001406082/profile?topnav=1&wvr=6','title':'微博'},
	      {'icon':'#icon-csdn','url':'https://blog.csdn.net/qq_39394518','title':'CSDN'},
	      {'icon':'#icon-bilibili','url':'https://space.bilibili.com/100007925','title':'bilibili'},
	      {'icon':'#icon-yuquemianlogo','url':'https://www.yuque.com/miluluyo','title':'語雀'}
	]],/*這個模塊是個人信息內那些小圖標們,別忘記更改喔,具體參數,可以參考下面的表格喔*/
	'signature':'靡不有初  鮮克有終',/*來一句你自己喜歡的句子吧*/
	'popper_weixin':'<div class="popper_box"><p><b>很高興認識你鴨~  (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg" alt="">公眾號:麋鹿魯喲</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg" alt="">微信號:s978761</div></div><p>(加我記得備註 博客園 喔)</div>',/*這裡是微信圖標的彈窗內容,可以自行更改內容喔*/
	'portrait':'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg'
})/*這個是頭像圖片喔,你可以上傳到相冊里,然後F12獲取,或者使用博客園的那個鏈接也可以的撒~*/
</script>

參數說明

名稱 類型 默認值/實例 描述
names 字符串 ‘麋鹿魯喲’ 博客園名稱
notice 字符串 ‘<b>溫馨提示</b><span><a href=”https://github.com/miluluyo/cute-cnblogs” target=”_black”>cute-cnblogs</a>  樣式已開源</span><b style=”margin-top: 3px;”><a style=”font-size:10px” href=”https://www.cnblogs.com/IsAlpaca/” target=”_black”>查看一期樣式</a></b>’ 公告內容
headerUrl 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519075219notice5.png’ 公告欄的背景圖
follow 字符串 ‘a1e76459-101d-47af-a8b6-08d523685c8c’ 複製follow括號里的內容,這是關注的那個碼
sidebarInfo 數組 [[ {‘icon’:’#icon-github1′,’url’:’https://github.com/miluluyo’,’title’:’github’}, {‘icon’:’#icon-weixin’,’url’:”,’title’:’微信’,’classname’:’popper_weixin’,’click’:false}, {‘icon’:’#icon-QQ’,’url’:’http://wpa.qq.com/msgrd?v=3&uin=978761587&site=qq&menu=yes’,’title’:’QQ’}, {‘icon’:’#icon-juejin’,’url’:’https://juejin.im/user/5d18adce5188256e98090e33′,’title’:’掘金’} ],[ {‘icon’:’#icon-weibobangding’,’url’:’https://www.weibo.com/6001406082/profile?topnav=1&wvr=6′,’title’:’微博’}, {‘icon’:’#icon-csdn’,’url’:’https://blog.csdn.net/qq_39394518′,’title’:’CSDN’}, {‘icon’:’#icon-bilibili’,’url’:’https://space.bilibili.com/100007925′,’title’:’bilibili’}, {‘icon’:’#icon-yuquemianlogo’,’url’:’https://www.yuque.com/miluluyo’,’title’:’語雀’} ]] 個人信息內那些小圖標們
icon 圖標
url 跳轉鏈接
title 提示名字
classname 要添加的class名
click 是否允許點擊跳轉
本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看
signature 字符串 ‘靡不有初 鮮克有終’ 個人信息簽名 (寫一句喜歡的話吧)
popper_weixin 字符串 ‘< div class=”popper_box”>< p>< b>很高興認識你鴨~ (づ。◕ᴗᴗ◕。)づ< /b> < /p>< div class=”popper_box_con”>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200614064005qrcode.jpg” alt=””>公眾號:麋鹿魯喲< /div>< div class=”popper_box_con_li”>< img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1493340/t_wxh.jpg” alt=””>微信號:s978761< /div>< /div>< p>(加我記得備註 博客園 喔)< /div>’ 微信焦點彈窗,內容可自行更改,可以放一些公眾號啊啥的~
portrait 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200515061851tx.jpg’ 頭像圖片路徑

頁首Html代碼

  <div id="set_btn_box">
    <div class="set_btn fly_top fadeIn animated">
        <svg class="icon" aria-hidden="true"><use xlink:href="#icon-zhiding"></use></svg>
    </div>
    <div class="set_btn article_icon_btn catalogue_btn">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dagang"></use></svg>
    </div>
    <div class="set_btn article_icon_btn comment">
        <a href="#comment_form_container"><svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-linedesign-01"></use></svg></a>
    </div>
    <div class="set_btn skin_btn">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-pifu"></use></svg>
    </div>
    <div class="set_btn gratuity">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-dashang"></use></svg>
    </div>
    <div class="set_btn article_icon_btn artice_recommend">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-tuijian2"></use></svg>
    </div>
     <canvas id="thumsCanvas" width="200" height="400" style="width:100px;height:200px"></canvas>
    <div class="set_btn catalogue">
        <svg class="icon" aria-hidden="true" style="color:#97A1A7"><use xlink:href="#icon-cebianlan-"></use></svg>
    </div>
</div>
<script src='https://blog-static.cnblogs.com/files/miluluyo/canvas2.js'></script>
<!--
<link href="//files.cnblogs.com/files/linianhui/lnh.cnblogs.css" rel="stylesheet"/>-->

頁腳Html代碼

  <style id="ceshicss">
@media (max-width: 767px){
#set_btn_box {width: 100vw;left: 0;right: 0;bottom: 0;background: hsla(0,0%,100%,.6);height: 49px;display: flex;justify-content: space-between;align-items: center;padding: 12px 40px;border-top: 1px solid #e8e8e8;box-sizing: border-box;}
.set_btn {margin-top: 0;}
.set_btn.fly_top.fadeIn.animated {position: absolute;right: 10px;bottom: 60px;}
.container{bottom:50px}}
#mainContent{width:90%}
</style>
<link href="https://blog-static.cnblogs.com/files/miluluyo/tippy.min.css" rel="stylesheet">
<script src="https://unpkg.com/@popperjs/core@2.4.2/dist/umd/popper.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/tippy.js"></script>
<link rel='stylesheet' href='https://cdn.bootcss.com/animate.css/3.7.2/animate.min.css'>
<script src="https://at.alicdn.com/t/font_1825850_klax1ao4o6.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/three.min.js"></script>
<script src='https://blog-static.cnblogs.com/files/miluluyo/star.js'></script>
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/OwO.min.css" />
<script src="https://blog-static.cnblogs.com/files/miluluyo/OwO2.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/cute-cnblogs2.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/monitoring2.js"></script>

<script>

miluframe({
  Youself:'https://www.cnblogs.com/miluluyo/', /*個人的博客園鏈接*/
  /*博客園導航信息*/
    custom:[{
      name:'首頁',
      link:'https://www.cnblogs.com/miluluyo/',
      istarget:false
    },{
      name:'聯繫',
      link:'https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F',
      istarget:true
    },{
      name:'技能樹',
      link:'https://miluluyo.github.io/',
      istarget:true
    },{
      name:'留言板',
      link:'https://www.cnblogs.com/miluluyo/p/11578505.html',
      istarget:false
    },{
      name:'相冊',
      link:'https://www.cnblogs.com/miluluyo/gallery.html',
      istarget:false
    },{
      name:'友鏈',
      link:'https://www.cnblogs.com/miluluyo/p/11633791.html',
      istarget:false
    },{
      name:'維護',
      link:'https://www.cnblogs.com/miluluyo/p/12092009.html',
      istarget:false
    },{
      name:'投喂',
      link:'https://www.cnblogs.com/miluluyo/p/gratuity.html',
      istarget:false
    },{
      name:'管理',
      link:'https://i.cnblogs.com/',
      istarget:true
    }],
    /*向別人展示自己的友鏈信息*/
    resume:{
        "name":"麋鹿魯喲",
        "link":"https://www.cnblogs.com/miluluyo/",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg",
        "introduction":"大道至簡,知易行難。"
    },
    /*友鏈信息*/
    unionbox:[{
        "name":"麋鹿魯喲",
        "introduction":"生活是沒有標準答案的。",
        "url":"https://www.cnblogs.com/miluluyo",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
      },{
        "name":"麋鹿魯喲的技能樹",
        "introduction":"大道至簡,知易行難。",
        "url":"https://miluluyo.github.io/",
        "headurl":"https://images.cnblogs.com/cnblogs_com/elkyo/1558759/o_o_my.jpg"
      }],
    /*友鏈表格頭信息,這個可以忽略*/
    details:[{
        field: 'name',
        literal: '昵稱',
      },{
        field: 'introduction',
        literal: '標語',
      },{
        field: 'url',
        literal: '鏈接地址',
      },{
        field: 'headurl',
        literal: '頭像地址',
      }],
    /*瀏覽器頂部小圖標*/
    logoimg:'https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png',
    /*文章頁面標題前的圖標,此處圖標有擴展,下面會提到圖標*/
    cuteicon:['icon-caomei','icon-boluo','icon-huolongguo','icon-chengzi','icon-hamigua','icon-lizhi','icon-mangguo','icon-liulian','icon-lizi','icon-lanmei','icon-longyan','icon-shanzhu','icon-pingguo','icon-mihoutao','icon-niuyouguo','icon-xigua','icon-putao','icon-xiangjiao','icon-ningmeng','icon-yingtao','icon-taozi','icon-shiliu','icon-ximei','icon-shizi'],
    /*讚賞,若true則显示此按鈕,false則不显示*/
    isGratuity:true,
    /*讚賞按鈕焦點显示讚賞內容,內容可自行更改*/
    gratuity:'<div class="popper_box"><p><b>要請我喝奶茶嗎  (づ。◕ᴗᴗ◕。)づ</b> </p><div class="popper_box_con"><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png" alt="">微信掃碼</div><div class="popper_box_con_li"><img src="https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png" >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b>&nbsp;&nbsp;<b><a href="https://www.cnblogs.com/miluluyo/p/12930946.html">GO</a></b></div>'
})
</script>
<!-- 點贊 -->
<canvas width="1777" height="841" style="position: fixed; left: 0px; top: 0px; z-index: 2147483647; pointer-events: none;"></canvas><script src="https://blog-static.cnblogs.com/files/miluluyo/mouse-click.js"></script>

<!-- 以下內容是否添加你隨意 -->

<script>
  /*在文章頁面添加古詩詞*/
  $("#navigator").after('<div class="poem-wrap"><div class="poem-border poem-left"></div><div class="poem-border poem-right"></div><h1>念兩句詩</h1><div id="poem_sentence"></div><div id="poem_info"></div></div>')
</script>
<script src="https://sdk.jinrishici.com/v2/browser/jinrishici.js" charset="utf-8"></script>
<script type="text/javascript">
  jinrishici.load(function(result) {
    var sentence = document.querySelector("#poem_sentence")
    var info = document.querySelector("#poem_info")
    sentence.innerHTML = result.data.content
    info.innerHTML = '【' + result.data.origin.dynasty + '】' + result.data.origin.author + '《' + result.data.origin.title + '》'
  });
</script>

<script type="text/javascript">
/* 鼠標特效,我覺得太花哨了就註釋了,喜歡的自己打開註釋就可以 */
/*var a_idx = 0;
jQuery(document).ready(function($) {
    $("body").click(function(e) {
        var a = new Array("去活出你自己。","今天的好計劃勝過明天的完美計劃。","不要輕言放棄,否則對不起自己。","緊要關頭不放棄,絕望就會變成希望。","如果不能改變結果,那就完善過程。","好好活就是干有意義的事,有意義的事就是好好活!","你真正是誰並不重要,重要的是你的所做所為。","你不想為你的信仰冒一下險嗎?難道想等你老了,再後悔莫及嗎?","有些鳥兒是關不住的,它的每一根羽毛都閃耀着自由的光輝。","決定我們成為什麼樣人的,不是我們的能力,而是我們的選擇。","掉在水裡你不會淹死,呆在水裡你才會淹死,你只有游,不停的往前游。","有些路,只能一個人走。","希望你眼眸有星辰,心中有山海。","從此以夢為馬,不負韶華。","人的成就和差異決定於其業餘時間。","佛不要你皈依,佛要你歡喜。","ダーリンのこと 大好きだよ","小貓在午睡時,地球在轉。","我,混世大魔王,申請做你的小熊軟糖。","決定好啦,要暗暗努力。","吶,做人呢最緊要開心。","好想邀請你一起去雲朵上打呼嚕呀。","永遠年輕,永遠熱淚盈眶。","我生來平庸,也生來驕傲。","我走得很慢,但我從不後退。","人間不正經生活手冊。","我是可愛的小姑娘,你是可愛。","數學里,有個溫柔霸道的詞,有且僅有。","吧唧一口,吃掉難過。","你頭髮亂了哦。","健康可愛,沒有眼袋。","日月星辰之外,你是第四種難得。","你是否成為了了不起的成年人?","大家都是第一次做人。","何事喧嘩?!","人間有味是清歡。","你笑起來真像好天氣。","風填詞半句,雪斟酒一壺。","除了自渡,他人愛莫能助。","昨日種種,皆成今我。","一夢入混沌 明月撞星辰","保持獨立 適當擁有","謝謝你出現 這一生我很喜歡","做自己就好了 我會喜歡你的","太嚴肅的話,是沒辦法在人間尋歡作樂的","願你餘生可隨遇而安,步步慢。","黃瓜在於拍,人生在於嗨","奇變偶不變,符號看象限。","從來如此,便對么?","今天我這兒的太陽,正好適合曬鈣 你呢","未來可期,萬事勝意。","星光不問趕路人 時光不負有心人","我當然不會試圖摘月,我要月亮奔我而來","女生要修鍊成的五樣東西: 揚在臉上的自信,長在心底的善良, 融進血里的骨氣,刻進命里的堅強,深到骨子里的教養","燕去燕歸,滄海桑田。縱此生不見,平安惟願","我想認識你 趁風不注意","我一直想從你的窗子里看月亮","長大應該是變溫柔,對全世界都溫柔。","別在深夜做任何決定","山中何事,松花釀酒,春水煎茶。","桃李春風一杯酒,江湖夜雨十年燈。","欲買桂花同載酒,終不似,少年游。");
        var le = Math.ceil(Math.random()*a.length); 
        var $i = $("<span></span>").text(a[le]);/*a[a_idx]*/
        /*a_idx = (a_idx + 1) % a.length;
        var x = e.pageX,
        y = e.pageY;
        $i.css({
            "z-index": 999999999999999999999999999999999999999999999999999999999999999999999,
            "top": y - 20,
            "left": x,
            "position": "absolute",
            "font-weight": "bold",
            "color": "rgb("+~~(255*Math.random())+","+~~(255*Math.random())+","+~~(255*Math.random())+")"
        });
        $("body").append($i);
        $i.animate({
            "top": y - 180,
            "opacity": 0
        },
        2000,
        function() {
            $i.remove();
        });
    });
});*/
</script>


<!--音樂,只在PC端寬度>1000px時显示-->
<link rel="stylesheet" href="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.css">
<div id="player" class="aplayer aplayer-withlist aplayer-fixed" data-id="3116636104" data-server="netease" data-type="playlist" data-order="random" data-fixed="true" data-listfolded="true" data-theme="#2D8CF0"></div>
<script src="https://blog-static.cnblogs.com/files/miluluyo/APlayer.min.js"></script>
<script src="https://blog-static.cnblogs.com/files/miluluyo/Meting.min.js"></script>

<!--貓,只在PC端显示,移動端不加載了,因為會卡頓頁面-->
<script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidget.min.js"></script>
<script>
  var mobile_flag = isMobile();
  if(mobile_flag){
    //console.info("移動端")
  }else{
    //console.info("PC端")
    L2Dwidget.init({
        "model": {
            "jsonPath": "https://unpkg.com/live2d-widget-model-hijiki/assets/hijiki.model.json",
            "scale": 1
        },
        "display": {
            "position": "left",
            "width": 100,
            "height": 200,
            "hOffset": 70,
            "vOffset": 0
        },
        "mobile": {
            "show": true,
            "scale": 0.5
        },
        "react": {
            "opacityDefault": 0.7,
            "opacityOnHover": 0.2
        }
    });
    window.onload = function(){
      $("#live2dcanvas").attr("style","position: fixed; opacity: 0.7; left: 70px; bottom: 0px; z-index: 1; pointer-events: none;")
    }
  }

</script>

<script>

/*記錄訪問數據,我用了兩個,一個是這個在 https://clustrmaps.com/ 網站,另一個是 https://www.51.la/ 這個網站*/
/*
	第一種:https://clustrmaps.com/
	註冊賬號,添加自己博客園的鏈接,選擇自定義Customize,
	選擇 Image based (basic version for websites that don't support javascript),調整到你喜歡的樣式,然後複製
	:這個我插入在了側邊欄的最底部,把生成的代碼粘貼到append內,這就完事了
*/
$('#sideBarMain').append('')

/*
	第二種:https://www.51.la/
	註冊賬號,點控制台,添加統計ID,統計圖標显示我是不显示的
	這個目前插入的js好像報錯,我的是很早之前生成的,還能用,因此還是推薦用第一種吧

	別的地方也有這種很多統計訪問數據的,可以自己找找看呢

*/
</script>

參數說明

名稱 類型 默認值/實例 描述
Youself 字符串 https://www.cnblogs.com/miluluyo/ 個人博客園首鏈接
custom 數組 [{ name:’首頁’, link:’https://www.cnblogs.com/miluluyo/’, istarget:false },{ name:’聯繫’, link:’https://msg.cnblogs.com/send/%E9%BA%8B%E9%B9%BF%E9%B2%81%E5%93%9F’, istarget:true },{ name:’技能樹’, link:’https://miluluyo.github.io/’, istarget:true },{ name:’留言板’, link:’https://www.cnblogs.com/miluluyo/p/11578505.html’, istarget:false },{ name:’相冊’, link:’https://www.cnblogs.com/miluluyo/gallery.html’, istarget:false },{ name:’友鏈’, link:’https://www.cnblogs.com/miluluyo/p/11633791.html’, istarget:false },{ name:’維護’, link:’https://www.cnblogs.com/miluluyo/p/12092009.html’, istarget:false },{ name:’投喂’, link:’https://www.cnblogs.com/miluluyo/p/gratuity.html’, istarget:false },{ name:’管理’, link:’https://i.cnblogs.com/’, istarget:true }] 導航信息
name 導航名
link 導航鏈接
istarget true跳轉到新頁面上,false當前頁面打開
resume 對象 {
“name”:”麋鹿魯喲”,
“link”:”https://www.cnblogs.com/miluluyo/”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”,
“introduction”:”大道至簡,知易行難。”
}
自己的友鏈信息
name 導航名
link 導航鏈接
headurl 頭像
introduction 語錄
unionbox 數組 [{
“name”:”麋鹿魯喲”,
“introduction”:”生活是沒有標準答案的。”,
“url”:”https://www.cnblogs.com/miluluyo”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”
},{
“name”:”麋鹿魯喲的技能樹”,
“introduction”:”大道至簡,知易行難。”,
“url”:”https://miluluyo.github.io/”,
“headurl”:”https://images.cnblogs.com/cnblogs_com/
elkyo/1558759/o_o_my.jpg”
}]
友鏈數組
name 昵稱
introduction 標語
url 鏈接地址
headurl 頭像地址
clicktext 數組 [{ field: ‘name’, literal: ‘昵稱’, },{ field: ‘introduction’, literal: ‘標語’, },{ field: ‘url’, literal: ‘鏈接地址’, },{ field: ‘headurl’, literal: ‘頭像地址’, }] 友鏈表格頭信息,這項可以忽略掉
logoimg 字符串 ‘https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200519070633f12.png’ 瀏覽器頂部小圖標
cuteicon 數組 [‘icon-caomei’,’icon-boluo’,’icon-huolongguo’,’icon-chengzi’,’icon-hamigua’,’icon-lizhi’,’icon-mangguo’,’icon-liulian’,’icon-lizi’,’icon-lanmei’,’icon-longyan’,’icon-shanzhu’,’icon-pingguo’,’icon-mihoutao’,’icon-niuyouguo’,’icon-xigua’,’icon-putao’,’icon-xiangjiao’,’icon-ningmeng’,’icon-yingtao’,’icon-taozi’,’icon-shiliu’,’icon-ximei’,’icon-shizi’] 文章頁面標題前的圖標,此處圖標我只放入了一些水果的icon,不過可以自己引入文件進行修改名字添加自己想加的,本框架有擴展的icon,文件在 github 中的 icon 文件夾內,可以下載去查看
gratuity 字符串 ‘<div class=”popper_box”><p><b>要請我喝奶茶嗎 (づ。◕ᴗᴗ◕。)づ</b> </p><div class=”popper_box_con”><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053817wx.png” alt=””>微信掃碼</div><div class=”popper_box_con_li”><img src=”https://images.cnblogs.com/cnblogs_com/miluluyo/1765646/o_200521053827zfb.png” >支付寶掃碼</div></div><p><b>留下一句你覺得很勵志與美的話給我吧~</b>  <b><a href=”https://www.cnblogs.com/miluluyo/p/12930946.html”>GO</a></b></div>’ 讚賞按鈕焦點显示讚賞內容,內容可自行更改
isGratuity 布爾值 true 默認true,若true則显示此按鈕,false則不显示

更換頂部背景圖

當前框架使用了一張圖片,也可以自己進行更換成隨機圖片API

在css樣式中

 #blogTitle{background:url(https://images.cnblogs.com/cnblogs_com/miluluyo/1764887/o_20051406472117.jpg) center center / cover no-repeat #222;overflow:hidden;width:100%;height:40vh;max-height:40vh;box-shadow:0 1px 2px rgba(150,150,150,.7);       /*搜索這個 更換 background: url() 里的鏈接 即可*/

最後

更多內容請查看 cute-cnblogs 自定義番外篇
(PS:可以使用番外篇里的隨機圖片API喔~)

請吃糖

如果您喜歡這裏,感覺對你有幫助,並且有多餘的軟妹幣的話,不妨投喂一顆糖喔~

<(▰˘◡˘▰)> 謝謝老闆~

微信掃碼

支付寶掃碼

讚賞的時候,留下一句你覺得很勵志與美的話給我吧~

(也可以加一個博客園給我喔,會添加在名字的旁邊喔~點擊可以跳轉~ 例如:去瞧瞧都有誰讚賞了

為了響應大家的號召,方便大家技術交流,之前建立了一個微信群,如果大家有需要可以掃碼(或者搜我微信號:s978761)加我好友,我邀請你加入~本群是一個純交流學習工作的群,不準發布廣告、營銷相關的信息!對了,加我記得備註上:博客園+名稱 加群 喔~

微信號:s978761

公眾號:麋鹿魯喲

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

【其他文章推薦】

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

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

※台北網頁設計公司全省服務真心推薦

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

※推薦評價好的iphone維修中心

聚甘新

過來人告訴你,去工作前最好還是學學Git

前言

只有光頭才能變強。

文本已收錄至我的GitHub精選文章,歡迎Star:https://github.com/ZhongFuCheng3y/3y

之前遇到過很多同學私信問我:「三歪,我馬上要實習了,我要在實習前學些什麼做準備啊?」

三歪在實習之前也同樣問過自己當時的部門老大。

如果再給我一次機會,我會先去花點時間去學學Git

Git我相信大家對它應該不陌生吧?但凡用過GitHub的同學應該多多少少都會了解一下Git

不知道當時大家學Git的時候是看哪個教程的,我看的是廖雪峰老師的Git系列的。

(別看到廖雪峰就以為是廣告了啊,哈哈哈哈,這篇純原創分享)

分享一下三歪的經歷

剛實習的時候,一直都忙着看各種東西。有一天,我學長說:我看你也學了一些基礎了,我們來看看公司的代碼吧,看看我們生產環境是怎麼做的。

於是我學長丟了一個Git鏈接給三歪

https://github.com/ZhongFuCheng3y/3y.git

那三歪做了什麼?三歪去IDEA下把這個Git給Clone下來:

我用Clone完了以後,我學長又補了一句:這個項目不是用master分支的哦,你切換一下分支

三歪:啥?切換分支?咋整?我忘了。

我學長看了下我,貌似不咋會切換分支,就說:“我來吧”。

於是在命令行終端一頓操作后,對三歪說:“好了”

三歪:“我對Git不是很熟悉,之前一直都是在IDEA上操作的。你們一般用命令行多還是圖形界面的多呀?”

我學長:“這沒什麼,反正工具這東西,學學就行,不是什麼大問題。也沒必要說很仔細去學它,就工具嘛”

三歪:“嗯”

時間飛逝,又過了一段時間…

三歪被分配了一個需求,於是就需要新建分支去做這個需求了。所有的標準應用線上走的是master分支,公司通過一個發布系統來控制發布版本、以及整套上下線的流程。

於是我要先在發布系統裡邊新建Git分支:

完了以後,我就在IDEA界面上選擇那個被我新建完的分支

但發現我死活找不到…於是我就問我學長:我在發布系統裡邊新建了分支,為什麼在IDEA上找不到啊?

學長:“怎麼會呢,我看看”。

找了一會,他問我:“你fetch 過了嗎?”

三歪:“啥?”

於是他拿着我的電腦,打開了終端,又以是命令行的方式敲了一頓,問我:“這是不是你新建的分支?“

三歪點了點頭,於是我學長說:”好了,你再看看“。

後來發現,新建完遠程分支,如果在IDEA上要能感知到,可以在pull界面上刷新一下,那就能找到了。

也不是說命令行一定會就比界面牛逼,其實IDEA的Git功能也做得挺好的。現在我都是混合使用,一些操作用命令行,一些操作用IDEA快捷鍵。

commitpush的時候就喜歡用快捷鍵。command+k和command +shift+k我就感覺比敲命令要快不少。

這些都是個人習慣的問題,也無對錯之分,怎麼方便怎麼來。

其實也不是所有的系統都會走發布系統的(有標準應用,非標準應用)。如果要自己寫一個啟動的腳本,一般我們會做些什麼?無非就是用Git拉最新的代碼,然後用maven打個包,然後啟動。

理解Git

如果你看過上一篇《三歪給女朋友講解什麼是Git》應該能大概了解什麼是Git了。

其實我覺得學Git主要理解工作區 -> 暫存區->倉庫 這幾個概念。

我們使用Git其實絕大部分的操作都是在本地上完成的,比如說add 和commit

只有我們push的時候,才會把本地完成好的內容推到遠程倉庫

通過上一篇文章我們知道在每個人的本地都有完整的歷史版本,所以我們可以在本地就能穿梭到不同的版本,然後將修改之後的代碼再重新提交到遠程倉庫上。

所謂的工作區實際上就是我們真正的的本地目錄

我們在本地添加文件后,需要add到暫存區,文件一旦被add到了暫存區,意味着Git能追蹤到這個文件。

當我們修改到一定程度之後,我們會執行一次提交commit,在提交的時候我們會”備註“自己這次的提交修改了什麼內容。

一次commit在Git就是一個版本,Git是版本控制的軟件,我們可以隨意穿梭到任何的版本中,修改代碼。

暫存區是這麼一個概念呢?

暫存區就像購物車,沒到付款的時候你都不確定購物車裡的東西全部都是要的。每拿一件商品就付一次款,那麻煩可大了。

從宏觀上看,Git其實有本地和遠程的概念,只是本地又分了工作區、暫存區、本地倉庫。再次強調:我們操作幾乎都是在本地完成,每個人的本地都會有所有歷史版本信息

我們一般會新建分支去支持每一次的修改。

其實分支這個概念也挺好理解的:我們需要并行開發,同時我們又不關心對方改的是什麼內容,改的是什麼文件。因此我們需要在自己的專屬環境下去修改內容,只要把最終修改完后的內容合併到一個主分支就OK了。

假設三歪做完了,經過校驗通過後,把自己的代碼merge(合併)到origin/master分支后,然後就發布上線啦。

隨後,雞蛋也做完了,自己的分支校驗完了以後,他此時也想把自己的代碼合併到origin/master。不料,他改的代碼跟三歪改的代碼有衝突了(Git不知道選擇誰的的代碼),那雞蛋只能手動merge了。

綜合來看,我們使用Git大多數的場景就是各自分支開發,然後各自在本地commit(提交),最後匯總到master分支。

所以,我們學Git大多數就學怎麼實現分支的增刪改、切換以及版本的穿梭

學習Git的小tips:

Unix/Linux 命令中,- 后一般跟短命令選項(通常是單字母,也有一些命令是例外的),-- 后一般跟長命令選項。如果只有一個單獨的--,後面不緊跟任何選項,則表示命令選項結束,後續的都作為命令的參數而不是選項。

例如:git checkout -- filename filename作為git checkout 的參數,而不是選項。

日常Git使用場景

、如果這個項目的代碼我們在本地還沒有,我們先去GitLab裡邊找對應的Git地址,然後Clone到本地:

git clone https://github.com/ZhongFuCheng3y/3y.git

、接到了新的需求,我們要新建一個分支,然後基於這個分支去開發:

git checkout -b feature/sanwaiAddLog

在開發的時候,我們肯定會有兩個操作:

  • 在原來的基礎上添加新的文件
  • 在原有的文件上修改

、不管怎麼樣,等我們做到一定程度了,我們都會提交代碼。如果我們添加了新的文件,我們需要先add,然後再commit

git add .
git commit  -m "try to commit files to GitHub, i am java3y"

、假設我們一切順利,在沒人打擾的情況下已經寫好了代碼了,然後我們會把自己的分支push到遠程倉庫

git push

、假設我們寫到一半,其他小夥伴已經把他的代碼merge到主分支了,我們也需要把他最新的 代碼給pull拉取下來(可以 git fetch + git merge 替代)。

git pull

如果沒有衝突,那git就會把他的代碼給merge到我當前的分支上。如果有衝突,Git會提醒我去手動解決一下衝突。

、假設我們寫到一半了,現在工作區的代碼都已經commit了。此時同事說要不幫忙一起排查一個問題,同事一般用的是自己分支,於是就得問他:你用的哪個分支啊?於是得把他的分支給拉下來,看看他的代碼哪兒有問題

git fecth -- 手動拉取遠程倉庫更新的信息
git checkout  分支名   -- 切換到他的分支

現在切換到他的分支,相當於你的環境跟他的環境是一模一樣的,於是就可以愉快地一起看Bug了。

、假設我們寫到一半了,現在工作區的代碼還沒commit。現在有同事說要排查問題或者一個新的Bug被發現了,要緊急切換到其他的分支。現在我又不想commit(我就寫了一半,編譯還報着錯誤,沒理由讓我commit吧)。

這時,我會把工作區的代碼先stash到暫存區給保存起來,然後就可以愉快地切換其他的分支了。

git stash

等我解決完另一個bug或者幫別人看完問題了,我再把剛剛保存在暫存區的代碼給撈出來,繼續幹活

git stash pop

、我一直在修Bug,現在的分支已經被我搞得人摸鬼樣了,我非常難受,甚至不知道自己在這個過程中改了多少東西了。

思路已經完全被打亂了,我想回到一個穩定的commit重新出發,重來吧(通過下面的命令,把工作區的代碼都改成對應commit的代碼了)。

git reset --hard  版本號

那我怎麼找到版本號呢?Git也是有日誌的:

git log --pretty=oneline

常用的Git命令

查看Git工作區、暫存區的變更情況(可以知道哪些沒有commit、哪些沒有被Git追蹤):git status

拉取遠程最新的變更到本地:git fetch

切換分支:git checkout 分支名

將代碼還原到某個版本(包括工作目錄):git reset --hard 版本號

查看Git的提交(commit)記錄:git log

將代碼還原到某個版本后,後悔了,想重新回去,但在提交記錄已經找不到了。git reset --hard reset 之後的 commit都給抹殺掉了。找到最近的執行Git命令:git reflog

還原到某個版本了,現在我為了穩健,不想再原來的分支上修改了,再新建一個分支吧(-b 參數把當前分支切換到了要創建的分支上):git checkout -b 分支名

我們把上一次還是”相對穩健“的分支合併到我新建的分支上:git merge 分支

突然想看看現在有多少個分支:git branch -a

新增幾個文件了,隨手git add一下吧

改得差不多了,隨手git commit -m 一下吧,最好還是寫好備註,不然以後等改多了,你都不知道你改了什麼啦。

改完了,提交到遠程吧:git push

想把遠程分支最新的代碼給拉下來,然後合併到本地上。我們可以用git fetchgit merge 來實現,也可以通過git pull來實現。一般我用的都是git fetch+git merge ,這樣會更加可控一些

有的時候,本地分支在master分支,然後忘了切其他的分支去修改,直接在master改了,然後也push到遠程了。等你發現的時候,你會真的想罵自己。

咋辦?最簡單的辦法其實我們還是可以git reset --hard到對應的版本,然後將其修改或者復原,再強制提交到master分支:git push -u origin/master -f

三歪瞎扯

在這篇文章中,我列出的Git常用的命令其實並不多吧。

像很多博客講的difftagconfig之類的命令我都沒有講,我這邊現實開發時這些命令也沒怎麼用過…

如果覺得我說漏的,可以在評論區補充,一起學習。

其實現在IDEA也很強大,很多時候都可以配合IDEA給我們提供的Git去做很多事。有的場景敲命令會比較方便,有的時候就直接圖形化界面就比較方便。

diff 這個功能而言, 肯定還是圖形界面好用一些吧(至少我是這樣認為的

IDEA配合一些快捷鍵,使用Git也能爽得飛起。Git始終也只是一個工具,如果你有興趣可以了解它的實現(我覺得大部分人可能不知道它是怎麼實現的);

如果沒興趣看它的實現,了解它是怎麼使用的,也足夠應付日常的開發場景了。

總的來說,現在的互聯網公司大多數還是用Git的,Git本身使用上其實不難,只要理解了Git是幹嘛的,它有個本地倉庫的概念,它可以來回穿梭各種版本,然後將本地的信息提交到遠程,跟着教程把常用的命令敲敲也差不多了。

如果實在是不懂,也別慌(我都給你們打了個樣了);主動認慫,虛心求教,同事們都不會嫌棄你的。

如果實習之前不知道要準備什麼去公司,要是對Git不了解,我覺得Git可以有佔一席之位。

更多Git命令和參考資料:

  • https://github.com/xjh22222228/git-manual
  • https://juejin.im/post/5edcf3a36fb9a047fa04fbc3
  • https://www.liaoxuefeng.com/wiki/896043488029600

各類知識點總結

下面的文章都有對應的原創精美PDF,在持續更新中,可以來找我催更~

  • 92頁的Mybatis
  • 129頁的多線程
  • 141頁的Servlet
  • 158頁的JSP
  • 76頁的集合
  • 64頁的JDBC
  • 105頁的數據結構和算法
  • 142頁的Spring
  • 58頁的過濾器和監聽器
  • 30頁的HTTP
  • 42頁的SpringMVC
  • Hibernate
  • AJAX
  • Redis
  • ……

涵蓋Java後端所有知識點的開源項目(已有8K+ star):

  • GitHub
  • Gitee訪問更快

我是三歪,一個想要變強的男人,感謝大家的點贊收藏和轉發,下期見。給三歪點個贊,對三歪真的非常重要!

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

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

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

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

※回頭車貨運收費標準

聚甘新