標籤: 南投搬家費用

  • 深入理解java繼承從“我爸是李剛”講起

    深入理解java繼承從“我爸是李剛”講起

    目錄

    前言
    本文主要多方面講解java繼承,旨在讓初學者通俗易懂,至於“我爸是李剛”,反正樓主也不知道誰爸是李剛。
    @

    1、繼承的概述

    1.1、繼承的由來

    至於由來簡單一句話:多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為。

    繼承描述的是事物之間的所屬關係,這種關係是 is-a 的關係。

    1.2、繼承的定義

    繼承:就是子類繼承父類的屬性行為,使得子類對象具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。

    這裏再聲明一點,父類又稱為超類或者基類。而子類又稱為派生類這點很基礎!

    1.3、繼承的優點

    1. 提高代碼的復用性
    2. 類與類之間產生關係,為多態做了完美的鋪墊(不理解沒關係,之後我會再寫一篇多態的文章)

    雖然繼承的優點很多但是Java只支持單繼承,不支持多繼承

    1.4、繼承的格式

    通過 extends 關鍵字,可以聲明一個子類繼承另外一個父類,定義格式如下:

      class 父類 {
       ... 
       }
       class 子類 extends 父類 { 
       ... 
       } 

    2、關於繼承之後的成員變量

    當類之間產生了關係后,其中各類中的成員變量,產生了哪些影響呢? 關於繼承之後的成員變量要從兩方面下手,一是成員變量不重名方面,二是成員變量重名方面。

    2.1、成員變量不重名

    如果子類父類中出現不重名的成員變量,這時的訪問是沒有影響的。代碼如下:

      class liGang {
            // 父類中的成員變量。
           String name ="李剛";//------------------------------父類成員變量是name
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name2 ="李小剛";//--------------------------子類成員變量是name2
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+name2);
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    2.2、 成員變量重名

    如果子類父類中出現重名的成員變量,這時的訪問是有影響的。代碼如下:

    class liGang {
            // 父類中的成員變量。
           String name ="李剛";//------------------------------父類成員變量是name
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";//---------------------------子類成員變量也是name
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+name);
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李小剛   我是李小剛
    
    

    子父類中出現了同名的成員變量時,在子類中需要訪問父類中非私有成員變量時,需要使用 super 關鍵字,至於修飾父類成員變量,類似於之前學過的 this 。 使用格式 super.父類成員變量名

    this表示當前對象,super則表示父類對象,用法類似!

    class liGang {
            // 父類中的成員變量。
           String name ="李剛";
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.name);
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    2.3、關於繼承中成員變量值得思考的一個問題

    同學你有沒有想過這樣一個問題。如果父類中的成員變量
    非私有:子類中可以直接訪問。
    私有:子類是不能直接訪問的。如下:

    當然,同學你要自己體驗體驗編譯報錯過程,看圖沒體驗感不得勁,~嘔,你這無處安放的魅力,無理的要求,我佛了,行吧~

      class liGang2 {
            // 父類中的成員變量。
            private String name ="李剛";
    
        }
        class LiXiaoGang2 extends liGang2 {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.name);//------編譯失敗不能直接訪問父類私有屬性(成員變量)
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class PrivateVariable {
            public static void main(String[] args) {
                // 創建子類對象
                ExtendDemo.LiXiaoGang z = new ExtendDemo.LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }

    通常開發中編碼時,我們遵循封裝的原則,使用private修飾成員變量,那麼如何訪問父類的私有成員變量呢?其實這個時候在父類中提供公共的getXxx方法和setXxx方法就可以了。代碼如下:

    class liGang {
            // 父類中的成員變量。
          private String name ="李剛";
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
        class LiXiaoGang extends liGang {
            // 子類中的成員變量
            String name ="李小剛";
            // 子類中的成員方法
            public void show() {
                // 訪問父類中的name,
                System.out.println("我爸是"+super.getName());
                // 繼承而來,所以直接訪問。
                // 訪問子類中的name2
                System.out.println("我是"+this.name);  //當然this可省略
            }
        }
    public class Demo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang z = new LiXiaoGang();
                // 調用子類中的show方法
                z.show();
            }
        }
        //演示結果: 我爸是李剛   我是李小剛

    分析如下:

    3、關於繼承之後的成員方法

    分析完了成員變量,現在我們一起來分析分析成員方法。
    想一想,當類之間產生了關係,其中各類中的成員方法,又產生了哪些影響呢? 同樣我們依舊從兩方面分析。
    #### 3.1、成員方法不重名
    如果子類父類中出現不重名的成員方法,這時的調用是沒有影響的。對象調用方法時,會先在子類中查找有沒有對 應的方法,若子類中存在就會執行子類中的方法,若子類中不存在就會執行父類中相應的方法。代碼如下:

     class liGang3 {
            // 父類中的成員方法。
           public void zhuangRen1(){//--------------------------父類方法名zhuangRen1
               System.out.println("我叫李剛,人不是我撞的,別抓我,我不認識李小剛");
           }
        }
        class LiXiaoGang3 extends liGang3 {
    
            // 子類中的成員方法
            public void zhuangRen() {//--------------------------子類方法名zhuangRen
                System.out.println("有本事你們告去,我爸是李剛");  
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的show方法
                liXiaoGang.zhuangRen();
                liXiaoGang.zhuangRen1();
            }
        }
        
    打印結果:有本事你們告去,我爸是李剛
            我叫李剛,人不是我撞的,別抓我,我不認識李小剛
    

    #### 3.2、成員方法重名 【方法重寫】
    成員方法重名大體也可以分兩種情況:

    1、方法名相同返回值類型、參數列表卻不相同(優先在子類查找,沒找到就去父類)
    2、方法名、返回值類型、參數列表都相同,沒錯這就是重寫(Override)

    這裏主要講方法重寫 :子類中出現與父類一模一樣的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱為重寫或者複寫。聲明不變,重新實現。 代碼如下:

        class liGang3 {
            // 父類中的成員方法。
           public void zhuangRen(int a){
               System.out.println("我叫李剛,人不是我撞的,別抓我");
           }
        }
        class LiXiaoGang3 extends liGang3 {
    
            // 子類中的成員方法
            public void zhuangRen(int a) {
                System.out.println("有本事你們告去,我爸是李剛");
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的zhuangRen方法
                liXiaoGang.zhuangRen(1);
    
            }
        }
        結果打印:有本事你們告去,我爸是李剛

    #### 3.3、繼承中重寫方法的意義
    子類可以根據需要,定義特定於自己的行為。既沿襲了父類的功能名稱,又根據子類的需要重新實現父類方法,從而進行擴展增強。比如李剛會開車,李小剛就牛了,在父類中進行擴展增強還會開車撞人,代碼如下:

     class liGang3 {
            // 父類中的成員方法。
           public void kaiChe(){
               System.out.println("我會開車");
           }
        }
        class LiXiaoGang3 extends liGang3 {
            // 子類中的成員方法
            public void kaiChe(){
                super.kaiChe();
                System.out.println("我還會撞人");
                System.out.println("我還能一撞撞倆婆娘");
            }
        }
        public class MemberMethod {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang3 liXiaoGang = new LiXiaoGang3();
                // 調用子類中的zhuangRen方法
                liXiaoGang.kaiChe();
    
    打印結果:   我會開車
               我還會撞人
               我還能一撞撞倆婆娘
            }
        }
    

    不知道同學們發現了沒有,以上代碼中在子類中使用了 super.kaiChe();super.父類成員方法,表示調用父類的成員方法。

    最後重寫必須注意這幾點:

    1、方法重寫時, 方法名與形參列表必須一致。
    2、子類方法覆蓋父類方法時,必須要保證子類權限 >= 父類權限。
    3、方法重寫時,子類的返回值類型必須要 <= 父類的返回值類型。
    4、方法重寫時,子類拋出的異常類型要 <= 父類拋出的異常類型。

    粗心的同學看黑板,look 這裏【注意:只有訪問權限是>=,返回值、異常類型都是<=

    下面以修飾權限為例,如下:

    4、關於繼承之後的構造方法

    為了讓你更好的體會,首先我先編寫一個程序

       class liGang4 {
            // 父類的無參構造方法。
            public liGang4(){
                System.out.println("父類構造方法執行了。。。");
            }
        }
        class LiXiaoGang4 extends liGang4 {
            // 子類的無參構造方法。
           public LiXiaoGang4(){
               System.out.println("子類構造方法執行了====");
           }
        }
        public class ConstructionDemo {
            public static void main(String[] args) {
                // 創建子類對象
                LiXiaoGang4 z = new LiXiaoGang4();
    
            }
        }

    用一分鐘猜想一下結果是什麼,猜好了再看下面結果:

    父類構造方法執行了。。。
    子類構造方法執行了====

    好了,看了結果之後,你可能有疑惑。父類構造器方法怎麼執行了?我們先來分析分析,首先在main方法中實例化了子類對象,接着會去執行子類的默認構造器初始化,這個時候在構造方法中默認會在第一句代碼中添加super();沒錯,他就是開掛般的存在,不寫也存在的!有的調~讀四聲“跳”~皮的同學就會說,你說存在就存在啊,無憑無據 ~呀,你這個該死的靚仔~ 如下:

    構造方法的名字是與類名一致的,所以子類是無法繼承父類構造方法的。 構造方法的作用是初始化成員變量的。所以子類的初始化過程中,必須先執行父類的初始化動作。子類的構造方法中默認會在第一句代碼中添加super(),表示調用父類的構造方法,父類成員變量初始化后,才可以給子類使用。

    當然我已經強調很多遍了 super() 不寫也默認存在,而且只能是在第一句代碼中,不在第一句代碼中行不行,答案是當然不行,這樣會編譯失敗,如下:

    5、關於繼承的多態性支持的例子

    直接上代碼了喔

    class A{
        public String show(C obj) {
            return ("A and C");
        }
    
        public String show(A obj) {
            return ("A and A");
        }
    
    }
    class B extends A{
        public String show(B obj) {
            return ("B and B");
        }
    }
    class C extends B{
        public String show(A obj) {
            return ("A and B");
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            A a=new A();
            B b=new B();
            C c=new C();
            System.out.println("第一題 " + a.show(a));
            System.out.println("第二題 " + a.show(b));
            System.out.println("第三題 " + a.show(c));
        }
    }
    運行結果:
            第一題 A and A
            第二題 A and A
            第三題 A and C

    其實吧,第一題和第三題都好理解,第二題就有點意思了,會發現A類中沒有B類型這個參數,這個時候,你就應該知道子類繼承就是父類,換句話說就是子類天然就是父類,比如中國人肯定是人,但是人不一定是中國人(可能是火星人也可能是非洲人),所以父類做為參數類型,直接傳子類的參數進去是可以的,反過來,子類做為參數類型,傳父類的參數進去,就需要強制類型轉換。

    6、super與this的用法

    了解他們的用法之前必須明確一點的是父類空間優先於子類對象產生

    在每次創建子類對象時,先初始化父類空間,再創建其子類對象本身。目的在於子類對象中包含了其對應的父類空間,便可以包含其父類的成員,如果父類成員非private修飾,則子類可以隨意使用父類成員。代碼體現在子類的構 造方法調用時,一定先調用父類的構造方法。理解圖解如下:

    #### 5.1、 super和this的含義:

    super :代表父類的存儲空間標識(可以理解為父親的引用)。

     

    this :代表當前對象的引用(誰調用就代表誰)。

    #### 5.2、 super和this訪問成員

    this.成員變量 ‐‐ 本類的
    super.成員變量 ‐‐ 父類的
    this.成員方法名() ‐‐ 本類的
    super.成員方法名() ‐‐ 父類的

    #### 5.3、super和this訪問構造方法

    this(...) ‐‐ 本類的構造方法
    super(...) ‐‐ 父類的構造方法

    #### 5.4、super()和this()能不能同時使用?

    不能同時使用,thissuper不能同時出現在一個構造函數裏面,因為this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,所以在同一個構造函數裏面有相同的語句,就失去了語句的意義,編譯器也不會通過。
    #### 5.5、總結一下super與this

    子類的每個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()super()this() 都必須是在構造方法的第一行,所以不能同時出現

    到這裏,java繼承你get到了咩,get到了請咩一聲,隨便隨手~點個讚唄~

    推薦閱讀本專欄的下一篇java文章

    歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術…

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 北京公車採購案 八成為電動車

    北京公交集團持續落實首都空氣清淨規劃,2016年計畫採購2700輛新的公車,其中有81%將採購電動車款,以減少大眾運輸系統的碳排放量。

    北京霧霾問題嚴重,改用電動車來取代汽油車是減輕空氣汙染的方法之一。2015年間,北京公交集團共置換了2306輛汽油公車為新式環保公車,其中有一半以上是新能源電動車。同時,2015年改造超過8000輛柴油公車,減少60%的氮氧化合物排放量。

    在純電動車基礎設施方面,北京公交集團陸續興建吳癸電動車線路網、變電站、充電站網絡等,共有21個公車站可供純電動車充電。

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 010.Kubernetes二進制部署kube-controller-manager

    010.Kubernetes二進制部署kube-controller-manager

    一 部署高可用kube-controller-manager

    1.1 高可用kube-controller-manager介紹


    本實驗部署一個三實例 kube-controller-manager 的集群,啟動后將通過競爭選舉機制產生一個 leader 節點,其它節點為阻塞狀態。當 leader 節點不可用時,阻塞的節點將再次進行選舉產生新的 leader 節點,從而保證服務的可用性。

    為保證通信安全,本文檔先生成 x509 證書和私鑰,kube-controller-manager 在如下兩種情況下使用該證書:

    • 與 kube-apiserver 的安全端口通信;
    • 在安全端口(https,10252) 輸出 prometheus 格式的 metrics。

    1.2 創建kube-controller-manager證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cat > kube-controller-manager-csr.json <<EOF
      3 {
      4   "CN": "system:kube-controller-manager",
      5   "hosts": [
      6     "127.0.0.1",
      7     "172.24.8.71",
      8     "172.24.8.72",
      9     "172.24.8.73"
     10   ],
     11   "key": {
     12     "algo": "rsa",
     13     "size": 2048
     14   },
     15   "names": [
     16     {
     17       "C": "CN",
     18       "ST": "Shanghai",
     19       "L": "Shanghai",
     20       "O": "system:kube-controller-manager",
     21       "OU": "System"
     22     }
     23   ]
     24 }
     25 EOF
     26 #創建kube-controller-manager的CA證書請求文件



    解釋:

    hosts 列表包含所有 kube-controller-manager 節點 IP;

    CN 和 O 均為 system:kube-controller-manager,kubernetes 內置的 ClusterRoleBindings system:kube-controller-manager 賦予 kube-controller-manager 工作所需的權限。



      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# cfssl gencert -ca=/opt/k8s/work/ca.pem \
      3 -ca-key=/opt/k8s/work/ca-key.pem -config=/opt/k8s/work/ca-config.json \
      4 -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager	#生成CA密鑰(ca-key.pem)和證書(ca.pem)


    1.3 分發證書和私鑰

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     scp kube-controller-manager*.pem root@${master_ip}:/etc/kubernetes/cert/
      7   done


    1.4 創建和分發kubeconfig


    kube-controller-manager 使用 kubeconfig 文件訪問 apiserver,該文件提供了 apiserver 地址、嵌入的 CA 證書和 kube-controller-manager 證書:

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# kubectl config set-cluster kubernetes \
      4   --certificate-authority=/opt/k8s/work/ca.pem \
      5   --embed-certs=true \
      6   --server=${KUBE_APISERVER} \
      7   --kubeconfig=kube-controller-manager.kubeconfig
      8 
      9 [root@k8smaster01 work]# kubectl config set-credentials system:kube-controller-manager \
     10   --client-certificate=kube-controller-manager.pem \
     11   --client-key=kube-controller-manager-key.pem \
     12   --embed-certs=true \
     13   --kubeconfig=kube-controller-manager.kubeconfig
     14 
     15 [root@k8smaster01 work]# kubectl config set-context system:kube-controller-manager \
     16   --cluster=kubernetes \
     17   --user=system:kube-controller-manager \
     18   --kubeconfig=kube-controller-manager.kubeconfig
     19 
     20 [root@k8smaster01 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
     21 
     22 [root@k8smaster01 ~]# cd /opt/k8s/work
     23 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     24 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     25   do
     26     echo ">>> ${master_ip}"
     27     scp kube-controller-manager.kubeconfig root@${master_ip}:/etc/kubernetes/
     28   done


    1.5 創建kube-controller-manager的systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# cat > kube-controller-manager.service.template <<EOF
      4 [Unit]
      5 Description=Kubernetes Controller Manager
      6 Documentation=https://github.com/GoogleCloudPlatform/kubernetes
      7 
      8 [Service]
      9 WorkingDirectory=${K8S_DIR}/kube-controller-manager
     10 ExecStart=/opt/k8s/bin/kube-controller-manager \\
     11   --profiling \\
     12   --cluster-name=kubernetes \\
     13   --controllers=*,bootstrapsigner,tokencleaner \\
     14   --kube-api-qps=1000 \\
     15   --kube-api-burst=2000 \\
     16   --leader-elect \\
     17   --use-service-account-credentials\\
     18   --concurrent-service-syncs=2 \\
     19   --bind-address=##MASTER_IP## \\
     20   --secure-port=10252 \\
     21   --tls-cert-file=/etc/kubernetes/cert/kube-controller-manager.pem \\
     22   --tls-private-key-file=/etc/kubernetes/cert/kube-controller-manager-key.pem \\
     23   --port=0 \\
     24   --authentication-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     25   --client-ca-file=/etc/kubernetes/cert/ca.pem \\
     26   --requestheader-allowed-names="" \\
     27   --requestheader-client-ca-file=/etc/kubernetes/cert/ca.pem \\
     28   --requestheader-extra-headers-prefix="X-Remote-Extra-" \\
     29   --requestheader-group-headers=X-Remote-Group \\
     30   --requestheader-username-headers=X-Remote-User \\
     31   --authorization-kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     32   --cluster-signing-cert-file=/etc/kubernetes/cert/ca.pem \\
     33   --cluster-signing-key-file=/etc/kubernetes/cert/ca-key.pem \\
     34   --experimental-cluster-signing-duration=8760h \\
     35   --horizontal-pod-autoscaler-sync-period=10s \\
     36   --concurrent-deployment-syncs=10 \\
     37   --concurrent-gc-syncs=30 \\
     38   --node-cidr-mask-size=24 \\
     39   --service-cluster-ip-range=${SERVICE_CIDR} \\
     40   --pod-eviction-timeout=6m \\
     41   --terminated-pod-gc-threshold=10000 \\
     42   --root-ca-file=/etc/kubernetes/cert/ca.pem \\
     43   --service-account-private-key-file=/etc/kubernetes/cert/ca-key.pem \\
     44   --kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \\
     45   --logtostderr=true \\
     46   --v=2
     47 Restart=on-failure
     48 RestartSec=5
     49 
     50 [Install]
     51 WantedBy=multi-user.target
     52 EOF


    1.6 分發systemd

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for (( i=0; i < 3; i++ ))
      4   do
      5     sed -e "s/##MASTER_NAME##/${MASTER_NAMES[i]}/" -e "s/##MASTER_IP##/${MASTER_IPS[i]}/" kube-controller-manager.service.template > kube-controller-manager-${MASTER_IPS[i]}.service
      6   done						#修正相應IP
      7 [root@k8smaster01 work]# ls kube-controller-manager*.service
      8 [root@k8smaster01 ~]# cd /opt/k8s/work
      9 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
     10 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
     11   do
     12     echo ">>> ${master_ip}"
     13     scp kube-controller-manager-${master_ip}.service root@${master_ip}:/etc/systemd/system/kube-controller-manager.service
     14   done						#分發system


    二 啟動並驗證

    2.1 啟動kube-controller-manager 服務

      1 [root@k8smaster01 ~]# cd /opt/k8s/work
      2 [root@k8smaster01 work]# source /opt/k8s/bin/environment.sh
      3 [root@k8smaster01 work]# for master_ip in ${MASTER_IPS[@]}
      4   do
      5     echo ">>> ${master_ip}"
      6     ssh root@${master_ip} "mkdir -p ${K8S_DIR}/kube-controller-manager"
      7     ssh root@${master_ip} "systemctl daemon-reload && systemctl enable kube-controller-manager && systemctl restart kube-controller-manager"
      8   done


    2.2 檢查kube-controller-manager 服務

      1 [root@k8smaster01 ~]# source /opt/k8s/bin/environment.sh
      2 [root@k8smaster01 ~]# for master_ip in ${MASTER_IPS[@]}
      3   do
      4     echo ">>> ${master_ip}"
      5     ssh root@${master_ip} "systemctl status kube-controller-manager|grep Active"
      6   done



    2.3 查看輸出的 metrics

      1 [root@k8smaster01 ~]# curl -s --cacert /opt/k8s/work/ca.pem --cert /opt/k8s/work/admin.pem --key /opt/k8s/work/admin-key.pem https://172.24.8.71:10252/metrics |head


    注意:以上命令在 kube-controller-manager 節點上執行。

    2.4 查看權限

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:kube-controller-manager



    ClusteRole system:kube-controller-manager 的權限很小,只能創建 secret、serviceaccount 等資源對象,各 controller 的權限分散到 ClusterRole system:controller:XXX 中。

    當在 kube-controller-manager 的啟動參數中添加 –use-service-account-credentials=true 參數,這樣 main controller 會為各 controller 創建對應的 ServiceAccount XXX-controller。內置的 ClusterRoleBinding system:controller:XXX 將賦予各 XXX-controller ServiceAccount 對應的 ClusterRole system:controller:XXX 權限。

      1 [root@k8smaster01 ~]# kubectl get clusterrole|grep controller



    如deployment controller:

      1 [root@k8smaster01 ~]# kubectl describe clusterrole system:controller:deployment-controller


    2.5 查看當前leader

      1 [root@k8smaster01 ~]# kubectl get endpoints kube-controller-manager --namespace=kube-system  -o yaml



    kubelet 認證和授權:https://kubernetes.io/docs/admin/kubelet-authentication-authorization/#kubelet-authorization
    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • Spring 框架常用語法進行總結

     

    Spring 框架常用語法進行總結:

    spring框架的二大主要的功能就是IOC和AOP。

    IOC: 控制反轉(依賴注入)

    AOP: 面向切面編程

    學習spring最好的方法就是去看官網,裏面有詳細的說明及使用原則

    介紹spring 中的註解的使用,xml配置等目前在市面上面較少。

     

    首先介紹Java自帶的元註解 (元註解就是 能註解到註解上的註解,能用在其他註解上的註解 )

    Java5.0定義了4個標準的meta-annotation類型

    @Target :

    用於描述註解的範圍,即註解在哪用。它說明了Annotation所修飾的對象範圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)等。取值類型(ElementType)

       CONSTRUCTOR:用於描述構造器
      FIELD:用於描述域即類成員變量
      LOCAL_VARIABLE:用於描述局部變量
      METHOD:用於描述方法
      PACKAGE:用於描述包
      PARAMETER:用於描述參數
      TYPE:用於描述類、接口(包括註解類型) 或enum聲明
      TYPE_PARAMETER:1.8版本開始,描述類、接口或enum參數的聲明
      TYPE_USE:1.8版本開始,描述一種類、接口或enum的使用聲明
      
    eg :

    public @interface Log {
      ......
    }

     

    @Retention :

    用於描述註解的生命周期,表示需要在什麼級別保存該註解,即保留的時間長短。取值類型RetentionPolicy)

       SOURCE:在源文件中有效(即源文件保留)
      CLASS:在class文件中有效(即class保留)
      RUNTIME:在運行時有效(即運行時保留)  
    eg:

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
      ......
    }

     

    @Documented :

    用於描述其它類型的annotation應該被作為被標註的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。它是一個標記註解,沒有成員。

    eg :

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Log {
      ......
    }
    @Inherited :

    用於表示某個被標註的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。

     

     

    Spring 常用的註解

    在註解配置中常用的啟動方法就是:

    <--在XML中啟用方法-->
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
    Person bean = (Person) applicationContext.getBean("person");
    System.out.println(bean);

    --------------------------------------------------------------------------
       <--在註解中啟用方法-->
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
    Person bean = applicationContext.getBean(Person.class);
    System.out.println(bean);


    getBeanNamesForType:得到當前IOC容器加載進來的bean的名稱
    String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
    for (String name : namesForType) {
    System.out.println(name);
    }
    @Component

    組件,沒有明確的角色

    @Service

    在業務邏輯層使用(service層)

    @Repository

    在數據訪問層使用(dao層)

    @Controller

    在展現層使用,控制器的聲明(Controller)

    @Bean

    注入ioc容器中,默認是以方法的名稱作為注入容器裏面的名稱,需注意@bean可以不在配置類裏面使用,不過經過@Bean註解使用過的方法所在的類也會被加載到ioc容器裏面。

    //配置類==配置文件

    @Configuration

    告訴Spring這是一個配置類,用在一個類的上面,配置類

    @Configuration == <bean id=”person” class=”com.opendev.entity.Person”></bean>

    @ComponentScan

    value:指定要掃描的包,用在配置類上面,告訴程序在spring中的掃包範圍

    @ComponentScans

    掃描多個包還有提供掃包的自定義掃包規則

     

    package com.atguigu.config;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.context.annotation.ComponentScan.Filter;
    import org.springframework.context.annotation.ComponentScans;

    import com.atguigu.bean.Person;

    //配置類==配置文件
    @Configuration  //告訴Spring這是一個配置類

    @ComponentScans(
    value = {
    @ComponentScan(value="com.atguigu",includeFilters = {
    /*@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
    @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),*/
    @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
    },useDefaultFilters = false)
    }
    )
    //@ComponentScan value:指定要掃描的包
    //excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件
    //includeFilters = Filter[] :指定掃描的時候只需要包含哪些組件
    //FilterType.ANNOTATION:按照註解
    //FilterType.ASSIGNABLE_TYPE:按照給定的類型;
    //FilterType.ASPECTJ:使用ASPECTJ表達式
    //FilterType.REGEX:使用正則指定
    //FilterType.CUSTOM:使用自定義規則
    public class MainConfig {

    //給容器中註冊一個Bean;類型為返回值的類型,id默認是用方法名作為id
    @Bean("person")//聲明了注入ioc容器裏面的對象為person,默認都是以方法名作為id
    public Person person01(){
    return new Person("lisi", 20);
    }
    }

     

    //類中組件統一設置。滿足當前條件,這個類中配置的所有bean註冊才能生效;

    @Conditional

    裏面需要寫上相應接口的實現類

    @Import

    導入組件,id默認是組件的全類名

    spring中bean的作用域

    默認是單實例的

    //默認是單實例的
    /**
    * ConfigurableBeanFactory#SCOPE_PROTOTYPE    
    * @see ConfigurableBeanFactory#SCOPE_SINGLETON  
    * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST request
    * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION sesssion
    * @return\
    * @Scope:調整作用域
    * prototype:多實例的:ioc容器啟動並不會去調用方法創建對象放在容器中。
    * 每次獲取的時候才會調用方法創建對象;
    * singleton:單實例的(默認值):ioc容器啟動會調用方法創建對象放到ioc容器中。
    * 以後每次獲取就是直接從容器(map.get())中拿,
    * request:同一次請求創建一個實例
    * session:同一個session創建一個實例
    *
    * 懶加載:
    * 單實例bean:默認在容器啟動的時候創建對象;
    * 懶加載:容器啟動不創建對象。第一次使用(獲取)Bean創建對象,並初始化;
    *
    */
    //@Scope("prototype")
    @Lazy
    @Bean("person")
    public Person person(){
    System.out.println("給容器中添加Person....");
    return new Person("張三", 25);
    }
    spring中bean 的生命周期
    /**
    * bean的生命周期:
    * bean創建---初始化----銷毀的過程
    * 容器管理bean的生命周期;
    * 我們可以自定義初始化和銷毀方法;容器在bean進行到當前生命周期的時候來調用我們自定義的初始化和銷毀方法
    *
    * 構造(對象創建)
    * 單實例:在容器啟動的時候創建對象
    * 多實例:在每次獲取的時候創建對象\
    *
    * BeanPostProcessor.postProcessBeforeInitialization
    * 初始化:
    * 對象創建完成,並賦值好,調用初始化方法。。。
    * BeanPostProcessor.postProcessAfterInitialization
    * 銷毀:
    * 單實例:容器關閉的時候
    * 多實例:容器不會管理這個bean;容器不會調用銷毀方法;
    *
    *
    * 遍歷得到容器中所有的BeanPostProcessor;挨個執行beforeInitialization,
    * 一但返回null,跳出for循環,不會執行後面的BeanPostProcessor.postProcessorsBeforeInitialization
    *
    * BeanPostProcessor原理
    * populateBean(beanName, mbd, instanceWrapper);給bean進行屬性賦值
    * initializeBean
    * {
    * applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    * invokeInitMethods(beanName, wrappedBean, mbd);執行自定義初始化
    * applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    *}
    *
    *
    *
    * 1)、指定初始化和銷毀方法;
    * 通過@Bean指定init-method和destroy-method;
    * 2)、通過讓Bean實現InitializingBean(定義初始化邏輯),
    * DisposableBean(定義銷毀邏輯);
    * 3)、可以使用JSR250;
    * @PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法
    * @PreDestroy:在容器銷毀bean之前通知我們進行清理工作
    * 4)、BeanPostProcessor【interface】:bean的後置處理器;
    * 在bean初始化前後進行一些處理工作;
    * postProcessBeforeInitialization:在初始化之前工作
    * postProcessAfterInitialization:在初始化之後工作
    *
    * Spring底層對 BeanPostProcessor 的使用;
    * bean賦值,注入其他組件,@Autowired,生命周期註解功能,@Async,xxx BeanPostProcessor;
    *
    *
    *
    */
    spring中的自動裝配
    /**
    * 自動裝配;
    * Spring利用依賴注入(DI),完成對IOC容器中中各個組件的依賴關係賦值;
    *
    * 1)、@Autowired:自動注入:
    * 1)、默認優先按照類型去容器中找對應的組件:applicationContext.getBean(BookDao.class);找到就賦值
    * 2)、如果找到多個相同類型的組件,再將屬性的名稱作為組件的id去容器中查找
    * applicationContext.getBean("bookDao")
    * 3)、@Qualifier("bookDao"):使用@Qualifier指定需要裝配的組件的id,而不是使用屬性名
    * 4)、自動裝配默認一定要將屬性賦值好,沒有就會報錯;
    * 可以使用@Autowired(required=false);
    * 5)、@Primary:讓Spring進行自動裝配的時候,默認使用首選的bean;
    * 也可以繼續使用@Qualifier指定需要裝配的bean的名字
    * BookService{
    * @Autowired
    * BookDao bookDao;
    * }
    *
    * 2)、Spring還支持使用@Resource(JSR250)和@Inject(JSR330)[java規範的註解]
    * @Resource:
    * 可以和@Autowired一樣實現自動裝配功能;默認是按照組件名稱進行裝配的;
    * 沒有能支持@Primary功能沒有支持@Autowired(reqiured=false);
    * @Inject:
    * 需要導入javax.inject的包,和Autowired的功能一樣。沒有required=false的功能;
    * @Autowired:Spring定義的; @Resource、@Inject都是java規範
    *
    * AutowiredAnnotationBeanPostProcessor:解析完成自動裝配功能;
    *
    * 3)、 @Autowired:構造器,參數,方法,屬性;都是從容器中獲取參數組件的值
    * 1)、[標註在方法位置]:@Bean+方法參數;參數從容器中獲取;默認不寫@Autowired效果是一樣的;都能自動裝配
    * 2)、[標在構造器上]:如果組件只有一個有參構造器,這個有參構造器的@Autowired可以省略,參數位置的組件還是可以自動從容器中獲取
    * 3)、放在參數位置:
    *
    * 4)、自定義組件想要使用Spring容器底層的一些組件(ApplicationContext,BeanFactory,xxx);
    * 自定義組件實現xxxAware;在創建對象的時候,會調用接口規定的方法注入相關組件;Aware;
    * 把Spring底層一些組件注入到自定義的Bean中;
    * xxxAware:功能使用xxxProcessor;
    * ApplicationContextAware==》ApplicationContextAwareProcessor;
    *
    *
    *
    *
    */

     

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 工信部擬規範新能源汽車廢舊動力電池綜合利用

    工業和資訊化部近日就《新能源汽車廢舊動力蓄電池綜合利用行業規範條件》向社會公開徵求意見,擬要求已在禁止建設區域投產運營的廢舊動力蓄電池綜合利用企業,要在一定期限內通過“依法搬遷、轉產”等方式逐步退出。

    禁止建設區域包括:國家法律、法規、規章及規劃確定或縣級以上人民政府批准的自然保護區、生態功能保護區、風景名勝區、飲用水水源保護區、基本農田保護區和其他需要特別保護的區域等。

    意見稿還對廢舊動力電池綜合利用作出規範。廢舊動力蓄電池綜合利用企業應依據相關國家、行業標準,參考新能源汽車和動力蓄電池生產企業提供的拆卸、拆解技術資訊,嚴格遵循先梯級利用後再生利用的原則,提高綜合利用水準。

    根據意見稿,濕法冶煉條件下,鎳、鈷、錳的綜合回收率應不低於98%;火法冶煉條件下,鎳、稀土的綜合回收率應不低於97%;不得擅自丟棄、傾倒、焚燒與填埋廢舊動力電池中的有色金屬、石墨、塑膠、橡膠、隔膜、電解液等零部件和材料。

    在能源消耗方面,意見稿規定,企業應加強對拆卸、儲存、拆解、檢測和再生利用等環節的能耗管控,努力降低綜合能耗,提高能源利用效率,鼓勵企業採用先進適用的節能技術工藝及裝備。

    此外,工信部同日發佈《新能源汽車廢舊動力蓄電池綜合利用行業規範公告管理暫行辦法(徵求意見稿)》,擬對新能源汽車廢舊動力蓄電池綜合利用企業實行動態管理,委託相關專業機構負責協助做好公告管理相關工作。

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 【algo&ds】2.線性表

    【algo&ds】2.線性表

    1.線性表

    線性表(英語:Linear List)是由n(n≥0)個元素()a[0],a[1],a[2]…,a[n-1]組成的。

    其中:

    • 數據元素的個數n定義為表的長度 = “list”.length() (”list”.length() = 0(表裡沒有一個元素)時稱為空表)
    • 將非空的線性表(n>=1)記作:(a[0],a[1],a[2],…,a[n-1])
    • 數據元素a[i](0≤i≤n-1)只是個抽象符號,其具體含義在不同情況下可以不同

    一個數據元素可以由若干個數據項組成。數據元素稱為記錄,含有大量記錄的線性表又稱為文件。這種結構具有下列特點:存在一個唯一的沒有前驅的(頭)數據元素;存在一個唯一的沒有後繼的(尾)數據元素;此外,每一個數據元素均有一個直接前驅和一個直接後繼數據元素。

    2.線性表的存儲結構

    • 鏈表
      • 單鏈表
        • 動態單鏈表
        • 靜態單鏈表
      • 循環鏈表
        • 單循環鏈表
        • 雙循環鏈表
      • 靜態鏈表

    3.順序表

    利用數組的連續存儲空間順序存放線性表的各元素

    3.1結構體定義

    如果需要使用自定義的結構體來維護一個順序表,通常來講結構體的元素一般是一個固定大小的數組(可用長度足夠大),以及當前數組存放的元素個數,也即數組的長度

    typedef struct LNode *List;
    struct LNode {
        ElementType Data[MAXSIZE];
        int Last;//記錄順序表的最後一個元素的下標
    } ;
    struct LNode L;
    List PtrL;

    訪問結構體的成員

    • 訪問下標為 i 的元素:L.Data[i] 或 PtrL->Data[i]
    • 線性表的長度:L.Last+1 或 PtrL->Last+1
    • 指針變量PtrL還可以這樣訪問兩個屬性(*PtrL).Data[i](*PtrL).Last,不過這種訪問方式並不常用

    而一般來講,為了簡單,不會去維護這樣一個結構體,(因為一旦維護了這個結構體,就需要去封裝相應的函數,比如說常見的插入、刪除、查找等操作),而是直接類似下面這樣

    ElementType data[MaxSize];
    int length;

    定義一個足夠大的數組,然後定義一個對應關聯的變量來時刻維護數組的長度。

    這兩種定義方式沒有什麼區別,一種是把常用操作封裝好,方便調用,另一種則是需要時刻自己維護對應的屬性。因為順序表的結構足夠簡單,所以不定義結構體也是可以的。

    3.2順序表的常見操作

    為了方便,這一節內容記錄的都是在定義的結構體基礎上,封裝的常見操作。

    1.初始化

    List MakeEmpty( ) {
        List PtrL;
        PtrL = (List )malloc( sizeof(struct LNode) );
        PtrL->Last = -1;
        return PtrL;
    }

    初始化的順序表,長度為0,所以Last為-1

    2.查找

    int Find( ElementType X, List PtrL ) {
        int i = 0;
        while( i <= PtrL->Last && PtrL->Data[i]!= X )
            i++;
        if (i > PtrL->Last) return -1; /* 如果沒找到,返回-1 */
        else return i; /* 找到后返回的是存儲位置 */
    }

    查找操作比較簡單,從順序表的第一個元素(下標為0開始)開始遍歷。

    還有一種更加巧妙一點的實現方式,就是引入哨兵思想。

    int Find( ElementType X, List PtrL ) {
        PtrL->Data[0] = x;//順序表第一個元素就是哨兵,賦值為x
        int i = PtrL->Last;//從最後一個元素開始遍歷
        while( PtrL->Data[i]!= X )
            i--;
        return i;
    }

    這樣做的好處很明顯,少了邊界的判斷,可以優化時間複雜度,編碼也更加簡單。

    注意:這裏把下標為0的元素設置為哨兵,則要求順序表從下標為1開始存儲。而且,函數如果沒有找到,則一定返回i=0

    3.插入

    看圖示應該要注意,移動的方向是從后往前移,如果從前往後移,則Data[i]=Data[i+1]=…=Data[n],因為後面的元素都被前面移過來的元素給覆蓋了。

    void Insert( ElementType X, int i, List PtrL ) {
        int j;
        if ( PtrL->Last == MAXSIZE-1 ) { /* 表空間已滿,不能插入*/
            printf("表滿");
            return;
        }
        if ( i < 1 || i > PtrL->Last+2) { /*檢查插入位置的合法性*/
            printf("位置不合法");
            return;
        }
        for ( j = PtrL->Last; j >= i-1; j-- )
            PtrL->Data[j+1] = PtrL->Data[j]; /*將 ai~ an倒序向後移動*/
        PtrL->Data[i-1] = X; /*新元素插入*/
        PtrL->Last++; /*Last仍指向最後元素*/
        return;
    }

    為什麼這裏需要判斷順序表的空間是否已滿?

    因為這個數組,是在初始化之後就固定了數組可容納的元素個數MaxSize,一旦超出,則程序下標就會越界。C++提供了動態數組vector,可以很方便的支持動態擴展數組長度,而且基本的插入刪除等操作都封裝好了,可以很方便的使用。

    4.刪除

    同樣的,需要注意元素移動的方向,如果從后往前移,則最後面的元素會一直覆蓋到Data[i]。

    void Delete( int i, List PtrL ) {
        int j;
        if( i < 1 || i > PtrL->Last+1 ) { /*檢查空表及刪除位置的合法性*/
            printf (“不存在第%d個元素”, i );
            return ;
        }
        for ( j = i; j <= PtrL->Last; j++ )
            PtrL->Data[j-1] = PtrL->Data[j]; /*將 ai+1~ an順序向前移動*/
        PtrL->Last--; /*Last仍指向最後元素*/
        return;
    }

    5.排序

    因為排序算法比較多,本文不展開講解,可以參考以下博文,內容包括了常見的十大排序算法的算法分析,時間複雜度和空間複雜度分析以及c實現和動圖圖解。

    4.鏈表

    不要求邏輯上相鄰的兩個元素物理上也相鄰;通過“鏈”建立起數據元素之間的邏輯關係。插入、刪除不需要移動數據元素,只需要修改“鏈”。

    4.1單鏈表

    typedef struct LNode *List;
    struct LNode {
        ElementType Data;
        List Next;
    };
    struct Lnode L;
    List PtrL;
    1.求表長
    int Length ( List PtrL ) {
        List p = PtrL; /* p指向表的第一個結點*/
        int j = 0;
        while ( p ) {
            p = p->Next;
            j++; /* 當前p指向的是第 j 個結點*/
        }
        return j;
    }

    時間複雜度O(n)

    2.查找

    按序查找

    List FindKth( int K, List PtrL ) {
        List p = PtrL;
        int i = 1;
        while (p !=NULL && i < K ) {
            p = p->Next;
            i++;
        }
        if ( i == K ) return p;
        /* 找到第K個,返回指針 */
        else return NULL;
        /* 否則返回空 */
    }

    時間複雜度O(n)

    按值查找

    List Find( ElementType X, List PtrL ) {
        List p = PtrL;
        while ( p!=NULL && p->Data != X )
            p = p->Next;
        return p;
    }

    時間複雜度O(n)

    3.插入

    (1)先構造一個新結點,用s指向;
    (2)再找到鏈表的第 i-1個結點,用p指向;
    (3)然後修改指針,插入結點 ( p之後插入新結點是 s)

    List Insert( ElementType X, int i, List PtrL ) {
        List p, s;
        if ( i == 1 ) { /* 新結點插入在表頭 */
            s = (List)malloc(sizeof(struct LNode)); /*申請、填裝結點*/
            s->Data = X;
            s->Next = PtrL;
            return s; /*返回新表頭指針*/
        }
        p = FindKth( i-1, PtrL ); /* 查找第i-1個結點 */
        if ( p == NULL ) { /* 第i-1個不存在,不能插入 */
            printf("參數i錯");
            return NULL;
        } else {
            s = (List)malloc(sizeof(struct LNode)); /*申請、填裝結點*/
            s->Data = X;
            s->Next = p->Next; /*新結點插入在第i-1個結點的後面*/
            p->Next = s;
            return PtrL;
        }
    }
    4.刪除

    (1)先找到鏈表的第 i-1個結點,用p指向;
    (2)再用指針s指向要被刪除的結點(p的下一個結點);
    (3)然後修改指針,刪除s所指結點;
    (4)最後釋放s所指結點的空間。

    List Delete( int i, List PtrL ) {
        List p, s;
        if ( i == 1 ) { /* 若要刪除的是表的第一個結點 */
            s = PtrL; /*s指向第1個結點*/
            if (PtrL!=NULL) PtrL = PtrL->Next; /*從鏈表中刪除*/
            else return NULL;
            free(s); /*釋放被刪除結點 */
            return PtrL;
        }
        p = FindKth( i-1, PtrL ); /*查找第i-1個結點*/
        if ( p == NULL ) {
            printf("第%d個結點不存在", i-1);
            return NULL;
        } else if ( p->Next == NULL ) {
            printf("第%d個結點不存在", i);
            return NULL;
        } else {
            s = p->Next; /*s指向第i個結點*/
            p->Next = s->Next; /*從鏈表中刪除*/
            free(s); /*釋放被刪除結點 */
            return PtrL;
        }
    }

    4.2雙鏈表

    雙向鏈表,又稱為雙鏈表,是的一種,它的每個數據結點中都有兩個,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。

    typedef struct DuLNode {
        ElemType data;
        struct DuLNode *prior, *next;
    } DuLNode, *DuLinkList;

    4.3循環鏈表

    4.3.1單循環鏈表

    存儲結構和單鏈表相同。

    typedef struct LNode {
        ElemType data;
        struct LNode *next;
    } LNode, *LinkList;
    
    // 設立尾指針的單循環鏈表的12個基本操作
    void InitList(LinkList *L) { // 操作結果:構造一個空的線性表L
        *L = (LinkList)malloc(sizeof(struct LNode)); // 產生頭結點,並使L指向此頭結點
        if (!*L) // 存儲分配失敗
            exit(OVERFLOW);
        (*L)->next = *L; // 指針域指向頭結點
    }
    
    void DestroyList(LinkList *L) { // 操作結果:銷毀線性表L
        LinkList q, p = (*L)->next; // p指向頭結點
        while (p != *L) { // 沒到表尾
            q = p->next;
            free(p);
            p = q;
        }
        free(*L);
        *L = NULL;
    }
    
    void ClearList(LinkList *L) /* 改變L */ { // 初始條件:線性表L已存在。操作結果:將L重置為空表
        LinkList p, q;
        *L = (*L)->next; // L指向頭結點
        p = (*L)->next; // p指向第一個結點
        while (p != *L) { // 沒到表尾
            q = p->next;
            free(p);
            p = q;
        }
        (*L)->next = *L; // 頭結點指針域指向自身
    }
    
    Status ListEmpty(LinkList L) { // 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE
        if (L->next == L) // 空
            return TRUE;
        else
            return FALSE;
    }
    
    int ListLength(LinkList L) { // 初始條件:L已存在。操作結果:返回L中數據元素個數
        int i = 0;
        LinkList p = L->next; // p指向頭結點
        while (p != L) { // 沒到表尾
            i++;
            p = p->next;
        }
        return i;
    }
    
    Status GetElem(LinkList L, int i, ElemType *e) { // 當第i個元素存在時,其值賦給e並返回OK,否則返回ERROR
        int j = 1; // 初始化,j為計數器
        LinkList p = L->next->next; // p指向第一個結點
        if (i <= 0 || i > ListLength(L)) // 第i個元素不存在
            return ERROR;
        while (j < i) { // 順指針向後查找,直到p指向第i個元素
            p = p->next;
            j++;
        }
        *e = p->data; // 取第i個元素
        return OK;
    }
    
    int LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) { // 初始條件:線性表L已存在,compare()是數據元素判定函數
        // 操作結果:返回L中第1個與e滿足關係compare()的數據元素的位序。
        //           若這樣的數據元素不存在,則返回值為0
        int i = 0;
        LinkList p = L->next->next; // p指向第一個結點
        while (p != L->next) {
            i++;
            if (compare(p->data, e)) // 滿足關係
                return i;
            p = p->next;
        }
        return 0;
    }
    
    Status PriorElem(LinkList L, ElemType cur_e, ElemType *pre_e) { // 初始條件:線性表L已存在
        // 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅,
        //           否則操作失敗,pre_e無定義
        LinkList q, p = L->next->next; // p指向第一個結點
        q = p->next;
        while (q != L->next) { // p沒到表尾
            if (q->data == cur_e) {
                *pre_e = p->data;
                return TRUE;
            }
            p = q;
            q = q->next;
        }
        return FALSE; // 操作失敗
    }
    
    Status NextElem(LinkList L, ElemType cur_e, ElemType *next_e) { // 初始條件:線性表L已存在
        // 操作結果:若cur_e是L的數據元素,且不是最後一個,則用next_e返回它的後繼,
        //           否則操作失敗,next_e無定義
        LinkList p = L->next->next; // p指向第一個結點
        while (p != L) { // p沒到表尾
            if (p->data == cur_e) {
                *next_e = p->next->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE; // 操作失敗
    }
    
    Status ListInsert(LinkList *L, int i, ElemType e) /* 改變L */ { // 在L的第i個位置之前插入元素e
        LinkList p = (*L)->next, s; // p指向頭結點
        int j = 0;
        if (i <= 0 || i > ListLength(*L) + 1) // 無法在第i個元素之前插入
            return ERROR;
        while (j < i - 1) { // 尋找第i-1個結點
            p = p->next;
            j++;
        }
        s = (LinkList)malloc(sizeof(struct LNode)); // 生成新結點
        s->data = e; // 插入L中
        s->next = p->next;
        p->next = s;
        if (p == *L) // 改變尾結點
            *L = s;
        return OK;
    }
    
    Status ListDelete(LinkList *L, int i, ElemType *e) /* 改變L */ { // 刪除L的第i個元素,並由e返回其值
        LinkList p = (*L)->next, q; // p指向頭結點
        int j = 0;
        if (i <= 0 || i > ListLength(*L)) // 第i個元素不存在
            return ERROR;
        while (j < i - 1) { // 尋找第i-1個結點
            p = p->next;
            j++;
        }
        q = p->next; // q指向待刪除結點
        p->next = q->next;
        *e = q->data;
        if (*L == q) // 刪除的是表尾元素
            *L = p;
        free(q); // 釋放待刪除結點
        return OK;
    }
    
    void ListTraverse(LinkList L, void(*vi)(ElemType)) { // 初始條件:L已存在。操作結果:依次對L的每個數據元素調用函數vi()
        LinkList p = L->next->next; // p指向首元結點
        while (p != L->next) { // p不指向頭結點
            vi(p->data);
            p = p->next;
        }
        printf("\n");
    }

    4.3.2雙循環鏈表

    // 線性表的雙向鏈表存儲結構
    typedef struct DuLNode {
        ElemType data;
        struct DuLNode *prior, *next;
    } DuLNode, *DuLinkList;
    
    // 帶頭結點的雙向循環鏈表的基本操作(14個)
    void InitList(DuLinkList *L) {
        // 產生空的雙向循環鏈表L
        *L = (DuLinkList)malloc(sizeof(DuLNode));
        if (*L)
            (*L)->next = (*L)->prior = *L;
        else
            exit(OVERFLOW);
    }
    
    void DestroyList(DuLinkList *L) {
        // 操作結果:銷毀雙向循環鏈表L
        DuLinkList q, p = (*L)->next; // p指向第一個結點
        while (p != *L) { // p沒到表頭
            q = p->next;
            free(p);
            p = q;
        }
        free(*L);
        *L = NULL;
    }
    
    void ClearList(DuLinkList L) { // 不改變L
        // 初始條件:L已存在。操作結果:將L重置為空表
        DuLinkList q, p = L->next; // p指向第一個結點
        while (p != L) { // p沒到表頭
            q = p->next;
            free(p);
            p = q;
        }
        L->next = L->prior = L; // 頭結點的兩個指針域均指向自身
    }
    
    Status ListEmpty(DuLinkList L) {
        // 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE
        if (L->next == L && L->prior == L)
            return TRUE;
        else
            return FALSE;
    }
    
    int ListLength(DuLinkList L) {
        // 初始條件:L已存在。操作結果:返回L中數據元素個數
        int i = 0;
        DuLinkList p = L->next; // p指向第一個結點
        while (p != L) { // p沒到表頭
            i++;
            p = p->next;
        }
        return i;
    }
    
    Status GetElem(DuLinkList L, int i, ElemType *e) {
        // 當第i個元素存在時,其值賦給e並返回OK,否則返回ERROR
        int j = 1; // j為計數器
        DuLinkList p = L->next; // p指向第一個結點
        while (p != L && j < i) { // 順指針向後查找,直到p指向第i個元素或p指向頭結點
            p = p->next;
            j++;
        }
        if (p == L || j > i) // 第i個元素不存在
            return ERROR;
        *e = p->data; // 取第i個元素
        return OK;
    }
    
    int LocateElem(DuLinkList L, ElemType e, Status(*compare)(ElemType, ElemType)) {
        // 初始條件:L已存在,compare()是數據元素判定函數
        // 操作結果:返回L中第1個與e滿足關係compare()的數據元素的位序。
        // 若這樣的數據元素不存在,則返回值為0
        int i = 0;
        DuLinkList p = L->next; // p指向第1個元素
        while (p != L) {
            i++;
            if (compare(p->data, e)) // 找到這樣的數據元素
                return i;
            p = p->next;
        }
        return 0;
    }
    
    Status PriorElem(DuLinkList L, ElemType cur_e, ElemType *pre_e) {
        // 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅,
        // 否則操作失敗,pre_e無定義
        DuLinkList p = L->next->next; // p指向第2個元素
        while (p != L) { // p沒到表頭
            if (p->data == cur_e) {
                *pre_e = p->prior->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE;
    }
    
    Status NextElem(DuLinkList L, ElemType cur_e, ElemType *next_e) {
        // 操作結果:若cur_e是L的數據元素,且不是最後一個,則用next_e返回它的後繼,
        // 否則操作失敗,next_e無定義
        DuLinkList p = L->next->next; // p指向第2個元素
        while (p != L) { // p沒到表頭
            if (p->prior->data == cur_e) {
                *next_e = p->data;
                return TRUE;
            }
            p = p->next;
        }
        return FALSE;
    }
    
    DuLinkList GetElemP(DuLinkList L, int i) { // 另加
        // 在雙向鏈表L中返回第i個元素的地址。i為0,返回頭結點的地址。若第i個元素不存在,
        // 返回NULL
        int j;
        DuLinkList p = L; // p指向頭結點
        if (i < 0 || i > ListLength(L)) // i值不合法
            return NULL;
        for (j = 1; j <= i; j++)
            p = p->next;
        return p;
    }
    
    Status ListInsert(DuLinkList L, int i, ElemType e) {
        // 在帶頭結點的雙鏈循環線性表L中第i個位置之前插入元素e,i的合法值為1≤i≤表長+1
        // 改進算法2.18,否則無法在第表長+1個結點之前插入元素
        DuLinkList p, s;
        if (i < 1 || i > ListLength(L) + 1) // i值不合法
            return ERROR;
        p = GetElemP(L, i - 1); // 在L中確定第i個元素前驅的位置指針p
        if (!p) // p=NULL,即第i個元素的前驅不存在(設頭結點為第1個元素的前驅)
            return ERROR;
        s = (DuLinkList)malloc(sizeof(DuLNode));
        if (!s)
            return OVERFLOW;
        s->data = e;
        s->prior = p; // 在第i-1個元素之後插入
        s->next = p->next;
        p->next->prior = s;
        p->next = s;
        return OK;
    }
    
    Status ListDelete(DuLinkList L, int i, ElemType *e) {
        // 刪除帶頭結點的雙鏈循環線性表L的第i個元素,i的合法值為1≤i≤表長
        DuLinkList p;
        if (i < 1) // i值不合法
            return ERROR;
        p = GetElemP(L, i); // 在L中確定第i個元素的位置指針p
        if (!p) // p = NULL,即第i個元素不存在
            return ERROR;
        *e = p->data;
        p->prior->next = p->next; // 此處並沒有考慮鏈表頭,鏈表尾
        p->next->prior = p->prior;
        free(p);
        return OK;
    }
    
    void ListTraverse(DuLinkList L, void(*visit)(ElemType)) {
        // 由雙鏈循環線性表L的頭結點出發,正序對每個數據元素調用函數visit()
        DuLinkList p = L->next; // p指向頭結點
        while (p != L) {
            visit(p->data);
            p = p->next;
        }
        printf("\n");
    }
    
    void ListTraverseBack(DuLinkList L, void(*visit)(ElemType)) {
        // 由雙鏈循環線性表L的頭結點出發,逆序對每個數據元素調用函數visit()
        DuLinkList p = L->prior; // p指向尾結點
        while (p != L) {
            visit(p->data);
            p = p->prior;
        }
        printf("\n");
    }

    4.4靜態鏈表

    前面講解的都是動態鏈表,即需要指針來建立結點之間的連接關係。而對有些問題來說結點的地址是比較小的整數(例如5位數的地址),這樣就沒有必要去建立動態鏈表,而應使用方便得多的靜態鏈表。
    靜態鏈表的實現原理是hash,即通過建立一個結構體數組,並令數組的下標直接表示結點的地址,來達到直接訪問數組中的元素就能訪問結點的效果。另外,由於結點的訪問非常方便,因此靜態鏈表是不需要頭結點的。靜態鏈表結點定義的方法如下:

    struct Node{
        typename data;//數據域
        int next;//指針域
    }node[size];

    參考資料:

    • 《算法筆記》
    • 《數據結構和算法》-極客時間專欄

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 【集合系列】- 紅黑樹實現分析

    【集合系列】- 紅黑樹實現分析

    一、故事的起因

    JDK1.8最重要的就是引入了紅黑樹的設計(當衝突的鏈表長度超過8個的時候),為什麼要這樣設計呢?好處就是避免在最極端的情況下衝突鏈表變得很長很長,在查詢的時候,效率會非常慢。

    • 紅黑樹查詢:其訪問性能近似於折半查找,時間複雜度O(logn);
    • 鏈表查詢:這種情況下,需要遍歷全部元素才行,時間複雜度O(n);

    本文主要是講解紅黑樹的實現,只有充分理解了紅黑樹,對於後面的分析才會更加順利。

    簡單的說,紅黑樹是一種近似平衡的二叉查找樹,其主要的優點就是“平衡“,即左右子樹高度幾乎一致,以此來防止樹退化為鏈表,通過這種方式來保障查找的時間複雜度為log(n)。

    關於紅黑樹的內容,網上給出的內容非常多,主要有以下幾個特性:

    • 1、每個節點要麼是紅色,要麼是黑色,但根節點永遠是黑色的;
    • 2、每個紅色節點的兩個子節點一定都是黑色;
    • 3、紅色節點不能連續(也即是,紅色節點的孩子和父親都不能是紅色);
    • 4、從任一節點到其子樹中每個恭弘=叶 恭弘子節點的路徑都包含相同數量的黑色節點;
    • 5、所有的恭弘=叶 恭弘節點都是是黑色的(注意這裏說恭弘=叶 恭弘子節點其實是上圖中的 NIL 節點);

    在樹的結構發生改變時(插入或者刪除操作),往往會破壞上述條件3或條件4,需要通過調整使得查找樹重新滿足紅黑樹的條件。

    二、調整方式

    上面已經說到當樹的結構發生改變時,紅黑樹的條件可能被破壞,需要通過調整使得查找樹重新滿足紅黑樹的條件。

    調整可以分為兩類:一類是顏色調整,即改變某個節點的顏色,這種比較簡單,直接將節點顏色進行轉換即可;另一類是結構調整,改變檢索樹的結構關係。結構調整主要包含兩個基本操作:左旋(Rotate Left)右旋(RotateRight)

    2.1、左旋

    左旋的過程是將p的右子樹繞p逆時針旋轉,使得p的右子樹成為p的父親,同時修改相關節點的引用,使左子樹的深度加1,右子樹的深度減1,通過這種做法來調整樹的穩定性。過程如下:

    以jdk1.8為例,打開HashMap的源碼部分,紅黑樹內部類TreeNode屬性分析:

    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
            //指向父節點的指針
            TreeNode<K,V> parent;
            //指向左孩子的指針
            TreeNode<K,V> left;
            //指向右孩子的指針
            TreeNode<K,V> right;
            //前驅指針,跟next屬性相反的指向
            TreeNode<K,V> prev;
            //是否為紅色節點
            boolean red;
            ......
    }

    左旋方法rotateLeft如下:

    /*
     * 左旋邏輯
     */
    static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                                  TreeNode<K,V> p) {
                //root:表示根節點
                //p:表示要調整的節點
                //r:表示p的右節點
                //pp:表示p的parent節點
                //rl:表示p的右孩子的左孩子節點
                TreeNode<K,V> r, pp, rl;
                //r判斷,如果r為空則旋轉沒有意義
                if (p != null && (r = p.right) != null) {
                    //多個等號的連接操作從右往左看,設置rl的父親為p
                    if ((rl = p.right = r.left) != null)
                        rl.parent = p;
                    //判斷p的父親,為空,為根節點,根節點的話就設置為黑色
                    if ((pp = r.parent = p.parent) == null)
                        (root = r).red = false;
                    //判斷p節點是左兒子還是右兒子
                    else if (pp.left == p)
                        pp.left = r;
                    else
                        pp.right = r;
                    r.left = p;
                    p.parent = r;
                }
                return root;
    }

    2.2、右旋

    了解了左旋轉之後,相應的就會有右旋,邏輯基本也是一樣,只是方向變了。右旋的過程是將p的左子樹繞p順時針旋轉,使得p的左子樹成為p的父親,同時修改相關節點的引用,使右子樹的深度加1,左子樹的深度減1,通過這種做法來調整樹的穩定性。實現過程如下:

    同樣的,右旋方法rotateRight如下:

    /*
     * 右旋邏輯
     */
    static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
                                                   TreeNode<K,V> p) {
                //root:表示根節點
                //p:表示要調整的節點
                //l:表示p的左節點
                //pp:表示p的parent節點
                //lr:表示p的左孩子的右孩子節點
                TreeNode<K,V> l, pp, lr;
                //l判斷,如果l為空則旋轉沒有意義
                if (p != null && (l = p.left) != null) {
                    //多個等號的連接操作從右往左看,設置lr的父親為p
                    if ((lr = p.left = l.right) != null)
                        lr.parent = p;
                    //判斷p的父親,為空,為根節點,根節點的話就設置為黑色
                    if ((pp = l.parent = p.parent) == null)
                        (root = l).red = false;
                    //判斷p節點是右兒子還是左兒子
                    else if (pp.right == p)
                        pp.right = l;
                    else
                        pp.left = l;
                    l.right = p;
                    p.parent = l;
                }
                return root;
    }

    三、操作示例介紹

    3.1、插入調整過程圖解

    3.2、刪除調整過程圖解

    3.3、查詢過程圖解

    四、總結

    至此,紅黑樹的實現就基本完成了,關於紅黑樹的結構,有很多種情況,情況也比較複雜,但是整體調整流程,基本都是先調整結構然後調整顏色,直到最後滿足紅黑樹特性要求為止。整篇文章,如果有理解不當之處,歡迎指正!

    五、參考

    1、
    2、

    作者:炸雞可樂
    出處:

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 上海推出新能源汽車充電樁綜合保險

    近期,上海保險業與國家電網積極合作,推出充電樁綜合保險產品。該產品包括充電樁財產保險和充電樁用電安全責任保險。充電樁財產保險保額最高1萬元,充電樁用電安全責任保險保額3萬元。

    合作初期,上海相關險企採取贈送方式,建立專項資金池,向新能源車主贈送推廣充電樁保險。同時也在國家電網營業廳設點,向預報樁客戶宣傳介紹充電樁綜合保險的相關產品和服務。

    據瞭解,2015年7月1日起,上海正式實施《上海市電動汽車充電基礎設施建設管理暫行規定》。7月1日後,上海市民必須提供政府備案的充電服務機構出具的“充電設施已裝證明”,才能在銷售企業辦理購買新能源汽車手續,必須安裝好充電樁後才能獲得國家和地方政府補貼、並申請到新能源車免費牌照。此外,《規定》也明確充電設施所有權人應當承擔充電設施維修更新養護及侵害第三者權益責任。

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 日問周刊 | 全棧面試匯總 | 第二期

    日問周刊 | 全棧面試匯總 | 第二期

    勤學如春起之苗,不見其增,日有所長;輟學如磨刀之石,不見其損,日有所虧。

    我在 github 上新建了一個倉庫 ,每天至少一個問題。有關全棧,graphql,devops,微服務以及軟技能,促進職業成長,歡迎交流。

    以諸葛武侯的誡子書與君共勉

    夫君子之行,靜以修身,儉以養德。非澹泊無以明志,非寧靜無以致遠。夫學須靜也,才須學也,非學無以廣才,非志無以成學。淫慢則不能勵精,險躁則不能治性。年與時馳,意與日去,遂成枯落,多不接世,悲守窮廬,將復何及!

    【Q037】linux 有哪些發行版,你最喜歡哪一個

    原文鏈接,歡迎討論:

    開放問題,不過你至少得知道一個發行版…

    【Q036】http 狀態碼中 301,302和307有什麼區別

    原文鏈接,歡迎討論:

    • 301,Moved Permanently。永久重定向,該操作比較危險,需要謹慎操作:如果設置了301,但是一段時間后又想取消,但是瀏覽器中已經有了緩存,還是會重定向。
    • 302,Fount。臨時重定向,但是會在重定向的時候改變 method: 把 POST 改成 GET,於是有了 307
    • 307,Temporary Redirect。臨時重定向,在重定向時不會改變 method

    【Q035】http 常見的狀態碼有哪些

    原文鏈接,歡迎討論:

    【Q034】如何實現一個 loading 動畫

    原文鏈接,歡迎討論:

    【Q033】如何對接口進行限流]

    原文鏈接,歡迎討論:

    一般採用漏桶算法:

    1. 漏桶初始為空
    2. API 調用是在往漏桶里注水
    3. 漏桶會以一定速率出水
    4. 水滿時 API 拒絕調用

    可以使用 redis 的計數器實現

    1. 計數器初始為空
    2. API 調用計數器增加
    3. 給計數器設置過期時間,隔段時間清零,視為一定速率出水
    4. 計數器達到上限時,拒絕調用

    當然,這隻是大致思路,這時會有兩個問題要注意

    1. 最壞情況下的限流是額定限流速率的2倍
    2. 條件競爭問題

    不過實際實現時注意以下就好了(話說一般也是調用現成的三方庫做限流…),可以參考我以前的文章

    【Q032】js 中什麼是 softbind,如何實現

    原文鏈接,歡迎討論:

    【Q031】js 中如何實現 bind

    原文鏈接,歡迎討論:

    最簡單的 bind 一行就可以實現,而在實際面試過程中也不會考察你太多的邊界條件

    Function.prototype.fakeBind = function(obj) {
      return (...args) => this.apply(obj, args)
    }

    測試一下

    function f (arg) {
      console.log(this.a, arg)
    }
    
    // output: 3, 4
    f.bind({ a: 3 })(4)
    
    // output: 3, 4
    f.fakeBind({ a: 3 })(4)

    【Q030】linux 中如何打印所有網絡接口

    原文鏈接,歡迎討論:

    ifconfig

    ifconfig 是最簡單最常用,但是打印信息太多了

    $ ifconfig

    netstat

    netstatip 也挺好用,特別是它們還可以打印路由表

    $ netstat -i

    ip

    $ ip link
    
    $ ip addr

    【Q029】websocket 如何向特定的用戶組推送消息

    redis 處維護一個對象,記錄每個 group 所對應的 connections/sockets

    {
      'Class:201901': [student1Socket, student2Socket]
    }

    當 client 剛連入 server 時,便加入某個特定的組,或者叫 room,比如 student01,剛開始連入 server,可能要加入 room:Student:01Class:201901Group:10086

    $ who
    
    $ last

    一圖勝千言

    使用 jsonb_pretty 函數,示例如下

    > select jsonb_pretty('{"a": {"b": 4}}'::jsonb)
    +----------------+
    | jsonb_pretty   |
    |----------------|
    | {              |
    |     "a": {     |
    |         "b": 4 |
    |     }          |
    | }              |
    +----------------+
    SELECT 1
    Time: 0.018s

    一個簡單的 Promise 的粗糙實現,關鍵點在於

    1. pending 時, thenable 函數由一個隊列維護
    2. 當狀態變為 resolved(fulfilled) 時,隊列中所有 thenable 函數執行
    3. resolved 時, thenable 函數直接執行

    rejected 狀態同理

    class Prom {
      static resolve (value) {
        if (value && value.then) {
          return value 
        }
        return new Prom(resolve => resolve(value))
      }
    
      constructor (fn) {
        this.value = undefined
        this.reason = undefined
        this.status = 'PENDING'
    
        // 維護一個 resolve/pending 的函數隊列
        this.resolveFns = []
        this.rejectFns = []
    
        const resolve = (value) => {
          // 注意此處的 setTimeout
          setTimeout(() => {
            this.status = 'RESOLVED'
            this.value = value
            this.resolveFns.forEach(({ fn, resolve: res, reject: rej }) => res(fn(value)))
          })
        }
    
        const reject = (e) => {
          setTimeout(() => {
            this.status = 'REJECTED'
            this.reason = e
            this.rejectFns.forEach(({ fn, resolve: res, reject: rej }) => rej(fn(e)))
          })
        }
    
        fn(resolve, reject)
      }
    
    
      then (fn) {
        if (this.status === 'RESOLVED') {
          const result = fn(this.value)
          // 需要返回一個 Promise
          // 如果狀態為 resolved,直接執行
          return Prom.resolve(result)
        }
        if (this.status === 'PENDING') {
          // 也是返回一個 Promise
          return new Prom((resolve, reject) => {
            // 推進隊列中,resolved 后統一執行
            this.resolveFns.push({ fn, resolve, reject }) 
          })
        }
      }
    
      catch (fn) {
        if (this.status === 'REJECTED') {
          const result = fn(this.value)
          return Prom.resolve(result)
        }
        if (this.status === 'PENDING') {
          return new Prom((resolve, reject) => {
            this.rejectFns.push({ fn, resolve, reject }) 
          })
        }
      }
    }
    
    Prom.resolve(10).then(o => o * 10).then(o => o + 10).then(o => {
      console.log(o)
    })
    
    return new Prom((resolve, reject) => reject('Error')).catch(e => {
      console.log('Error', e)
    })

    首參不一樣,直接上 API

    React.cloneElement(
      element,
      [props],
      [...children]
    )
    
    React.createElement(
      type,
      [props],
      [...children]
    )

    它一般可以使用第三方庫 來實現,源碼很簡單,可以讀一讀

    主要有兩個要點

    1. 選中
    2. 複製

    選中

    選中主要利用了

    選中的代碼如下

    const selection = window.getSelection();
    const range = document.createRange();
    
    range.selectNodeContents(element);
    selection.removeAllRanges();
    selection.addRange(range);
    
    selectedText = selection.toString();

    取消選中的代碼如下

    window.getSelection().removeAllRanges();

    它有現成的第三方庫可以使用:

    複製

    複製就比較簡單了,execCommand

    document.exec('copy')

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!

  • 科學警訊:熱浪使熊蜂瀕臨滅絕 連帶影響農糧產量

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

    台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家費用,距離,噸數怎麼算?達人教你簡易估價知識!