深入理解JVM(③)虛擬機的類加載過程

前言

上一篇我們介紹到一個類的生命周期大概分7個階段:加載、驗證、準備、解析、初始化、使用、卸載。並且也介紹了類的加載時機,下面我們將介紹一下虛擬機中類的加載的全過程。主要是類生命周期的,加載、驗證、準備、解析和初始化這五個階段所執行的具體動作。

加載

類加載過程的第一個階段就是加載,在加載階段,Java虛擬機需要完成以下三件事情:

1. 通過一個類的全限定名來獲取定義此類的二進制字節流。
2. 將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3. 在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
《Java虛擬機規範》對這三點要求其實並不是特別具體,這樣留給虛擬機實現和Java應用的靈活度都是相當大的。僅第一條,獲取二進制字節流,並沒有有指出從哪裡獲取,如何獲取。這樣就已經能被我們的Java開發人員玩出各種花樣了。
例如:

  • 從ZIP包中讀取(JAR、EAR、WAR)。
  • 從網絡中獲取(Web Applet)。
  • 運行時計算生成,最典型的就是動態代理技術,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass()來為特定接口生成形式為“$Proxy”的代理類的二進制字節流。
  • 由其他文件生成(JSP)。
  • 從數據庫中讀取。
  • 從加密文件中獲取(防止被反編譯獲取源碼)。
  • …….. …..

相對於類加載的其他階段,非數組類型的加載階段是開發人員可控性最強的階段。加載階段即可以使用Java虛擬機里內置的引導類加載器完成,也可以由用戶自定義的類加載器去完成。

驗證

驗證這一階段的目的是確保Class文件的字節流中包含的信息符合《Java虛擬機規則》的全部約束要求,保證這些信息被當作代碼運行后不會危害虛擬機自身安全。
驗證階段大致上會完成下面四個階段的檢驗動作:
文件格式驗證、元數據驗證、字節碼驗證和複合引用驗證

文件格式驗證

這是驗證的第一個階段,主要是驗證字節流是否符合Class文件格式的規範,並且能被當前版本的虛擬機處理。
這一階段的驗證點有:

  • 是否以魔數0xCAFEBABE開頭。
  • 主、次版本號是否在當前Java虛擬機接受範圍之內。
  • 常量池的常量中是否有不被支持的常量類型。
  • 指向常量的各種索引值是否有指向不錯在的常量或不符合類的常量。

這個階段的驗證是基於二進制字節流進行的,只有通過了這個階段的驗證之後,這段字節流才被允許進入Java虛擬機內存的方法區中進行存儲,後面的階段都是基於方法區的存儲結構進行的,不會再直接讀取、操作字節流了。

元數據驗證

第二階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合《Java虛擬機規則》的要求,這個階段主要有以下一些驗證點:

  • 當前類是否有父類(除java.lang.Object外,所有類都應當有父類)。
  • 當前類的父類是否繼承了不允許被繼承的類(被final修飾的類)。
  • 如果當前類非抽象類,是否實現了父類或接口要求實現的所有方法。
  • 類中的字段、方法是否與父類產生矛盾。
字節碼驗證

第三階段是整個驗證過程最複雜的一個階段,主要目的是通過數據流分析和孔劉分析,確定程序語義是合法的、符合邏輯的。
為了保證被校驗的方法在運行時不會做出危害虛擬機的安全的行為,主要做了如下一些校驗:

  • 保證任意時刻操作棧的數據類型與指令代碼序列都能配合工作,例如不會出現類似於“在操作放置了一個int類型數據,使用時卻按long類型來加載如本地變量表中”這樣的情況。
  • 保證任何跳轉指令都不會跳轉到方法體以外的字節碼指令上。
  • 保證方法體中的類型轉換總是有效的。例如:一個子類對象賦值給父類數據類型,這是安全的,但是把父類對象賦值為子類數據類型,獲取賦值給另外一個毫無關係的數據類型,則是不合法的。
  • … …

如果一個類型中有方法體的字節碼沒有通過字節碼驗證,那它肯定是有問題的;但如果一個方法體通過了字節碼驗證,也仍然不能保證它一定就是安全的。因為字節碼驗證也是在程序中進行的,即不能通過程序準確地檢查出程序是否能在有限時間之內結束運行。

符號引用驗證

最後一個階段的校驗行為發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在連接的第三個階段——解析階段發生。
本階段通常需要校驗下列內容:

  • 符號引用中通過字符串描述的全限定名是否能找到對應的類。
  • 在指定類中是否存在符合方法的字段描述符及簡單名稱所描述的方法和字段。
  • 符合引用中的類、字段、方法的可訪問性,是否可被當前類訪問。

驗證階段對於虛擬機的類加載機制來說,是一個非常重要的、但卻不是必須要執行的階段,因為如果程序運行的全部代碼都已經被反覆使用和驗證過,在生成環境的實施階段就可以考慮使用-Xverify:none參數來關閉大部分的類驗證措施,來縮短類加載的時間。

準備

準備階段是正式為類中定義的變量分配內存並設置類變量的初始值的階段,這些變量所使用的內存都應當在方法區中進行分配,需要注意的是,這裏所說的方法區只是一個概念上的區域,在JDK7以及之前HotpSpot用永久代實現方法區,這個概念是正確的,但是在JDK8以及之後,類變量會隨着Class對象一起存放在Java堆中,這個時候類變量存在於方法區就僅僅是一個概念了。
在準備階段有兩點需要着重強調
1、在準備階段進行內存分配的僅包括類變量,而不包括實例變量,實例變量將會在對象實例化時隨着對象一起分配在Java堆中。
2、這裏所說的為類變量設置初始值,“通常情況”下是數據類的零值。
例如一個類變量定義為:

public static int value = 666;

那變量在準備階段過後的初始值為0而不為666,因為這個時候還未開始執行任何Java方法,而把value賦值為666的putstatic指令是程序被編譯后,存放於類構造器 ()方法之中,所以把value賦值為666的動作要到類的初始化階段才會被執行。

但是如果類字段的屬性表中存在ConstantValue屬性,那在準備階段變量值就會被初始化為ConstantValue屬性所指定的初始值,例如:

public static final int value = 666;

在編譯時Javac將會為value生成ConstantValue屬性,在準備階段虛擬機就會根據ConstantValue的設置將value賦值為666。

解析

解析階段是Java虛擬機將常量池內的符號引用替換為直接引用的過程,符號引用在Class文件中它以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等類型的常量出現。
先來解釋一下什麼是符號引用和直接引用。

  • 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
  • 直接引用:直接引用是可以直接指向目標的指針、相對偏移量或者是一個能間接定位到目標的句柄。

解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定這7類符號引用進行,分別對應於常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MehodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info
這8種常量類型。

初始化

初始化階段是類加載過程的最後一個步驟,之前介紹的幾個類加載的動作里,出了在加載階段用戶應用程序可以通過自定義類加載器的方式局部參与外,其餘動作都完全由Java虛擬機來主導控制。
簡單的來說,初始化階段就是執行類構造器<clinit>()方法的過程。那麼<clinit>()是如何執行的呢?

  • <clinit>()方法是由編譯器自動收集類中所有變量的複製動作和靜態語句塊(stataic{}塊)中的語句合併產生的,編譯器收集的順序是由語句在源文件中出現的的順序決定的,靜態語句塊中只能訪問到定義在靜態語句塊之前的變量,定義在它之後的變量,在前面的靜態語句塊可以賦值,但是不能訪問。
    例如:
  • <clinit>()方法與類的構造函數不同,它不需要顯式地調用父類構造器,Java虛擬機會保證在子類 ()方法執行前,父類 ()方法以及執行完畢。
  • 由於父類的<clinit>()方法先執行,即父類中定義的靜態語句塊要優先於子類的變量賦值操作。
    如下代碼運行結果會是 4
    父類
public class FatherClass {

    public static int fatherObject = 3;

    static {
        fatherObject = 4;
    }
}

子類

public class SonClass extends FatherClass{

    public static int sonObject = fatherObject;

}

測試

@Test
public void testClassLoad(){
    System.out.println(SonClass.sonObject);
}

運行結果

4
  • <clinit>()方法對於類或接口來說並不是必需的。
  • 接口中不能使用靜態語句塊,但仍然有變量初始化的賦值操作,因此接口與類一樣都會生成<clinit>()方法。
  • Java虛擬機必須保證一個類的<clinit>()方法在多線程環境中被正確地加鎖同步,若同時多個線程區初始化一個類,那麼只會有其中一個線程去執行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執行完畢<clinit>()方法。

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

Jmeter系列(35)- 使用 ServerAgent 監控服務器

如果你想從頭學習Jmeter,可以看看這個系列的文章哦

https://www.cnblogs.com/poloyy/category/1746599.html

 

前言

  • 做性能測試,監控服務器資源指標是必須有的一步
  • 這一篇博客將講解通過 Jmeter 插件來監控服務器,再通過 Jmeter 觀察監控結果

 

下載、安裝、運行插件

  1. JMeterPlugins-Extras.jar
  2. JMeterPlugins-Standard.jar
  3. ServerAgent-2.2.3.zip

可以通過官方下載:https://jmeter-plugins.org/downloads/old/

注意

  • 前兩個插件最新版本在官網已經不提供下載了,只能下載舊版本,官方建議通過 Plugins Manager 下載需要的插件哦
  • 博主安裝了以下插件,已經包含上面講的前兩個插件了

 

這裏也提供百度雲下載鏈接哈

鏈接:https://pan.baidu.com/s/1bAaKqGoyaTVoq5picgBGMw 

提取碼:q92i

 

客戶端(Jmeter 端)

  1. 如果通過官網下載的話,就將 JMeterPlugins-Extras.jar 和 JMeterPlugins-Standard.jar 放到  D:\apache-jmeter-5.2.1\lib\ext 
  2. 然後通過 PerfMon Metrics Collector 監聽器進行服務器性能數據显示

 

服務端(Linux)

將 ServerAgent-2.2.3.zip 放到任意目錄下,解壓

unzip  ServerAgent-2.2.3.zip 

 

進入 ServerAgent 目錄

 

我提供的 ServerAgent 裏面,兩個 start 腳本已經是可執行腳本了,直接運行即可

./startAgent.sh

 

運行成功,則會出現下圖

 

如果無法運行,則需要給腳本賦權

chmod 777 startAgent.sh

 

如果是想監控 Window 機器,則將 ServerAgent 放到 Window 電腦的目錄下,然後直接雙擊運行  startAgent.bat 即可

 

划重點

  • 啟動 ServerAgent 的前提是系統已安裝配置好 Java 環境
  • 默認佔用端口是 4444

 

啟動 ServerAgent 可能會出現的問題

端口被佔用

殺掉佔用 4444 端口的進程

  1. 先查出 4444 端口是什麼進程佔用
  2. 然後查看進程詳情,確認是否能殺掉
  3. 如果可以,則殺掉、

 

ServerAgent 通過其他端口啟動

./startAgent.sh --udp-port 0 --tcp-port 1234

 

0 代表不開啟該端口,一般我們只用 tcp 足以

 

端口未對外開放

  • 如果服務器開啟了防火牆,而且之前沒用過 4444 端口,那麼該端口很可能會被禁用
  • 為了解決防火牆和開放端口的問題,可以參考這篇博文:https://www.cnblogs.com/poloyy/p/12213297.html

 

Jmeter 監控服務器全流程

一個簡單的負載測試線程組結構樹

一般做負載測試,會監控服務器資源、活動線程數、響應時間、TPS等等,所以加了一堆監聽器

 

PerfMon Metrics Collector

IP 自然就是填需要監控的服務器 IP啦,如果是監控本機就填 127.0.0.1 就好了

 

可監控的指標列表

還挺多,不過一般重點關注前面四個就好了

 

運行結果

我的 cpu 真高…因為數據庫出現死鎖了,回頭可以好好性能分析一波了!

  本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

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

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

Spring Bean生命周期的各階段介紹

一.xml方式配置bean

二.Aware接口

  2.1 BeanNameAware

  2.2 BeanFactoryAware

  2.3 ApplicationContextAware

  2.4 Aware各接口的執行順序

  2.4 Aware接口總結

三.BeanPostProcessor接口

四.InitializingBean接口

五.init-method方法

六.DestructionAwareBeanPostProcessor接口

七.DisposableBean接口

八.destory-method方法

九.生命周期大雜燴

  9.1 實現多接口的Student類

  9.2 BeanPostProcessor前後置處理

  9.3 DestructionAwareBeanPostPrecessor接口

  9.4 配置xml文件

  9.5 測試代碼

  9.6 輸出結果

十.總結

 

 

 

 

  Spring Bean的生命周期是一個老生常談的問題了,網上一搜一大把,無非就是畫一幅流程圖(比如下面這幅圖),然後用語言介紹創建bean后執行各Aware接口,然後BeanPostProcessor…..最終Bean創建成功了,就可以使用這個Bean了,然後在容器銷毀的時候,又會執行一些操作。

  其實對於上面的提到的流程圖,注意上面的圖只是Spring Bean的大概流程(省略了一部分),主要涉及到了5個接口,分別是XxxAware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean接口,本文將會對這幾個接口,以及init-method、destroy-method做相關的使用介紹,在明白怎麼使用后,再把他們串起來,這樣的話,對於Spring Bean的生命周期就差不多知道咋回事了,而不用死記硬背。

 

一. xml方式配置Bean

  在說Aware、BeanPostProcessor、InitiailizingBean、Destruction、DisposableBean這些接口前,先簡單回顧一下使用xml配置並獲取一個Student類的bean過程,後面介紹各個接口的使用方式時時,也是按照這個形式;

1.1 創建Student類

  平淡無奇的Student類:

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;
}

  

1.2 創建配置文件

  平淡無奇的applicationContext.xml配置文件,創建一個student bean,利用setter方式設置初始值:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  

1.3 測試

  創建一個Main類,用於測試

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

@Slf4j
public class Test {

    public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        Student student = beanFactory.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  下面是運行程序的輸出,可以看到和預期相符,創建一個Student的bean,id和name默認值為99、張三;

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

   

二.Aware接口

  Aware接口有很多實現類,本文只介紹BeanNameAware、BeanFactoryAware、ApplicationContextAware,關係如下:

  

 

2.1 BeanNameAware

  創建一個Student類,讓該類實現BeanNameAware接口,並且重寫setBeanName方法

@Data
@Slf4j
public class Student implements BeanNameAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("beanName:{}, student bean:{}", s, this);
        this.id = 100;
        log.info("將beanName:{}的id改為100", s);
    }
}

  配置文件和測試程序都不改變,運行測試程序,輸出內容如下:

INFO  [main] cn.ganlixin.entity.Student - beanName:student, student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 將beanName:student的id改為100
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=100, name=張三)

  可以看到,實現BeanNameAware接口后,重寫setBeanName的方法中,獲取到的student bean,是已經初始化的bean(屬性都已經有值了),並且setBeanName方法中可以對當前的bean進行各種操作,包括修改bean的某些屬性,最後獲取到的bean是已經修改后的bean。

  這裏只是簡單介紹了一下BeanNameAware接口的用法,使用BeanNameAware接口,可以對當前Bean進行操作

 

2.2 BeanFactoryAware

  創建Student類,實現BeanFactoryAware接口,並且重寫setBeanFactory方法

@Data
@Slf4j
public class Student implements BeanFactoryAware {

    private Integer id;
    private String name;

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy方法
     *
     * @param beanFactory 創建該bean的beanFactory
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // 可以在setBeanFactory方法中獲取、修改beanFactory中的所有bean
        
        log.info("student this bean:{}", this);
        Student student = beanFactory.getBean("student", Student.class);
        log.info("通過beanFactory獲取student bean:{}", student);

        // 將name設置為李四
        this.name = "李四";
    }
}

  運行輸出如下:

INFO  [main] cn.ganlixin.entity.Student - student this bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - 通過beanFactory獲取student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=李四)

  通過上面的代碼輸出結果可以看出,實現BeanFactoryAware接口后,可以在setBeanFactory方法中操作BeanFactory的所有bean,操作的範圍要比BeanNameAware要大。

 

2.3 ApplicationContextAware

  ApplicationContext,有多種稱呼,比如“應用容器”、“環境”、“上線文”…

  創建Student類,實現ApplicationContextAware接口,並且重寫setApplicationContext接口:

@Data
@Slf4j
public class Student implements ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("Student this:{}", this);

        final Student student = applicationContext.getBean("student", Student.class);
        final Environment environment = applicationContext.getEnvironment();
        log.info("student bean:{}", student);
        log.info("env -> user.dir:{}", environment.getProperty("user.dir"));
    }
}

  需要修改一下測試程序,測試程序中加載配置時使用的XmlBeanFactory,而XmlBeanFactory不會回調ApplicationContextAware接口的setApplicationContext方法,下面使用ClassPathXmlApplicationContext類來加載配置:

@Slf4j
public class Test {

    public static void main(String[] args) {
        //BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        log.info("測試程序獲取到的student bean:{}", student);
    }
}

  運行測試程序:

INFO  [main] cn.ganlixin.entity.Student - Student this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - env -> user.dir:/Users/ganlixin/code/java-code-all/spring
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  實現ApplicationContextAware接口后,在setApplicationContext方法中,入參是當前的applicationContext,也就是說,可以在該方法中對Spring容器進行設置,操作的範圍又要比BeanFactoryAware的setBeanFactory要廣得多。

 

2.4 Aware各接口執行的先後順序

  既然有這幾個Aware接口,如果一個類同時實現了這3個接口,那麼執行順序是怎樣的呢?下面就來測試一下。

  創建Student類,分別實現BeanNameAware、BeanFactoryAware、ApplicationContextAware接口,並重寫其接口的方法:

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  仍舊使用ClassPathXmlApplicationContext類來加載配置,運行輸出結果如下:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

2.4 Aware接口總結

  上面演示了Spring中幾個Aware接口的用法和特點,下面總結一下:

  1.實現BeanNameAware接口后,重寫setBeanName方法,可以對單個Bean進行擴展修改;

  2.實現BeanFactoryAware接口后,重寫setBeanFactory方法,可以對bean工廠中的所有Bean進行擴展修改;

  3.實現ApplicationContextAware接口后,重寫setApplicationContext方法后,可以對整個容器進行擴展修改;

  4.這幾個接口的執行順序分別是BeanNameAware->BeanFactoryAware->ApplicationContextAware;

 

三.BeanPostProcessor接口

  BeanPostProcessor和前面的Aware接口有些區別,通過下面的例子就能看出區別在哪裡!

  下面舉個例子,創建MyBeanPostProcessor類,實現BeanPostProcessor接口,注意,這裏沒有在Student類上實現BeanPostProcessor接口。

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在各種Aware接口執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    /**
     * 實現了BeanPostProcessor接口后,重寫postProcessBeforeInitialization,在initMethod方法執行完畢后執行該方法
     *
     * @param bean     本次處理的bean
     * @param beanName 本次處理的beanName(bean id)
     * @return 返回的是在本方法中處理后的bean
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

  創建兩個類,分別是Student和User類,其中Use類沒有實現Aware接口,Student類實現了前面提到的3個Aware接口

@Data
public class User {
    private Integer id;
    private String name;
}

  

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware {

    private Integer id;
    private String name;

    /**
     * 實現了BeanNameAware接口后,需重寫setBeanName方法,接收的參數就是bean的id
     *
     * @param s bean的id
     */
    @Override
    public void setBeanName(String s) {
        log.info("call BeanNameAware.setBeanName()");
    }

    /**
     * 實現BeanFactoryAware接口后,需重寫setBeanFactroy
     *
     * @param beanFactory 創建該bean的bean工廠
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("call BeanFactoryAware.setBeanFactory()");
    }

    /**
     * 實現ApplicationContextAware接口后,徐重寫setApplicationContext方法
     *
     * @param applicationContext 該bean所在的上下文(applicationContext、容器)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("call ApplicationContextAware.setApplicationContext()");
    }
}

  

  xml配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean -->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

  測試:

INFO  [main] cn.ganlixin.entity.Student - call BeanNameAware.setBeanName()
INFO  [main] cn.ganlixin.entity.Student - call BeanFactoryAware.setBeanFactory()
INFO  [main] cn.ganlixin.entity.Student - call ApplicationContextAware.setApplicationContext()
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student1, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:user, bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  從上面的運行結果可以得出以下結論:

  1.因為只有Student實現了Aware接口,所以創建student bean的時候會調用對應的Aware接口方法,而User類沒有實現Aware接口,所以並沒有調用Aware接口方法;

  2.Student和User類都沒有繼承BeanPostProcessor接口,但是在創建student和user bean的時候,都掉用了MyBeanPostProcessor類中的前置和後置處理(繼承自BeanPostProcessor接口);

  3.BeanPostProcessor接口的前置和後置處理,是在Aware接口之後調用;

  4.很重要的一點,需要將BeanPostProcessor接口實現類聲明為bean,使用<bean>配置或者使用@Component註解,不然BeanPostProcessor不起作用。

 

四.InitializingBean接口

  創建Student類,實現InitializingBean接口,然後重寫afterPropertiesSet方法:

@Data
@Slf4j
public class Student implements InitializingBean {

    private Integer id;
    private String name;

    @Override
    public void afterPropertiesSet() throws Exception {
        // 同樣可以在這裏修改bean的屬性值
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }
}

  修改xml配置文件,創建student bean,測試:

INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  

五.init-method

  創建Student類,增加一個額外的方法display()

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void display() {
        log.info("Student.display call, this:{}", this);
    }
}

  修改配置文件,在<bean>標籤中增加init-method屬性,值為display,也就是Student的display方法名:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>
</beans>

  運行測試:

INFO  [main] cn.ganlixin.entity.Student - Student.display call, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)

  上面,輸出了display中的內容,這是在設置bean的時候調用的。

 

六.DestructionAwareBeanPostProcessor接口

  DestructionAwareBeanPostProcessor接口,從名稱上可以看出來是DestructionAware + BeanPostProcessor的組合,其實也的確是這樣,但是需要注意的就是,spring並沒有提供DestructionAware接口!!

  下面是DestructionAwareBeanPostProcessor接口的定義:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * Destruction執行的操作
     *
     * @param bean     處理的bean
     * @param beanName bean的名稱
     * @throws BeansException
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * 是否需要執行postProcessBeforeDestruction方法
     *
     * @param bean 執行Destruction的bean
     * @return 是否需要執行postProcessBeforeDestruction方法
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }
}

  DestructionAwareBeanPostProceesor繼承自BeanPostProcessor接口,所以也可以重寫前值和後置處理。

  下面介紹使用示例,創建MyDestructionAwareBeanPostProceesor,繼承DestructionAwareBeanPostProceesor接口:

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }
    
    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  修改配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>

    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean> -->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
    }
}

  運行測試程序,輸出如下:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:cn.ganlixin.entity.Student, bean:Student(id=99, name=張三)

  可以看到,在手動調用destroyBean方法來銷毀student bean的時候,調用了MyDestructionAwareBeanPostProcessor中定義的方法。

  需要注意的是,雖然這裏使用destroyBean來銷毀了student bean,如果又通過getBean來獲取student bean,則會重新創建student bean。

 

七.DisposableBean接口 

  前面介紹了DestructionAwareBeanPostProcessor接口,可以對所有的bean設置銷毀(destruction)后的處理操作。

  而這裏介紹的DisposableBean接口,就是對單獨的Bean進行destrction后的處理,也就是說不是應用到所有的bean上。

  簡單介紹一下用法,創建Student類和User類,User類正常(不實現任何接口),Student類實現DisposableBean接口,然後重寫destroy方法:

@Data
@Slf4j
public class Student implements DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destroy, this:{}", this);
    }
}

@Data
public class User {
    private Integer id;
    private String name;
}

  創建配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="cn.ganlixin.entity.Student" id="student">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>

    <bean class="cn.ganlixin.entity.User" id="user">
        <property name="id" value="88"/>
        <property name="name" value="王五"/>
    </bean>
</beans>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);
        User user = context.getBean("user", User.class);

        log.info("測試程序獲取到的student bean:{}", student);
        log.info("測試程序獲取到的user bean:{}",user);

        // 獲取bean工廠,然後調用destroyBean銷毀bean
        AutowireCapableBeanFactory factory = context.getAutowireCapableBeanFactory();
        factory.destroyBean(student);
        factory.destroyBean(user);
    }
}

  運行輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的user bean:User(id=88, name=王五)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destroy, this:Student(id=99, name=張三)

  可以看到,雖然測試代碼中destroy了student和user兩個bean,但是只有student bean在銷毀時觸發了DisposableBean的destory方法。

 

八.destroy-method方法

  和init-method相對應的就是destory-method方法了,創建Student類,增加clean方法(自定義):

@Data
@Slf4j
public class Student {

    private Integer id;
    private String name;

    public void clean() {
        log.info("Student.clean, this:{}", this);
    }
}

  修改配置文件,<bean>標籤中使用destroy-method屬性,值為clean方法

<bean class="cn.ganlixin.entity.Student" id="student" destroy-method="clean">
    <property name="id" value="99"/>
    <property name="name" value="張三"/>
</bean>

  測試程序:

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        registry.removeBeanDefinition("student");
    }
}

  輸出:

INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - Student.clean, this:Student(id=99, name=張三)

  

九.聲明周期大雜燴

  上面對每一種接口都做了介紹,這裏就將所有接口都做一下整合,嘗試在一個測試程序中測試所有接口,這個過程中就會對Bean的生命周期有清晰的認識:

9.1 實現多接口的Student類

  創建Student類,實現Aware、InitializingBean、DisposableBean接口,並且增加display、clean方法,作為init-method和destory-method。

package cn.ganlixin.entity;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

@Data
@Slf4j
public class Student implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    private Integer id;
    private String name;

    @Override
    public void setBeanName(String s) {
        log.info("BeanNameAware.setBeanName, this:{}", this);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        log.info("BeanFactoryAware.setBeanFactory, this:{}", this);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        log.info("ApplicationContextAware.setApplicationContext, this:{}", this);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("InitialingBean.afterPropertiesSet, this:{}", this);
    }

    @Override
    public void destroy() throws Exception {
        log.info("DisposableBean.destory, this:{}", this);
    }

    public void display() {
        log.info("init-method, Student.display, this:{}", this);
    }

    public void clean() {
        log.info("destroy-method, Student.clean, this:{}", this);
    }
}

 

9.2 BeanPostProcessor前後置處理

  創建MyBeanPostProcessor接口實現類,並重寫前置和後置處理方法:

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessBeforeInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("MyBeanPostProcessor.postProcessAfterInitialization, beanName:{}, bean:{}", beanName, bean);
        return bean;
    }
}

 

9.3 DestructionAwareBeanPostPrecessor接口

  創建MyDestructionAwareBeanPostProcessor類,並重寫其中的方法(不重寫BeanPostProcessor的前後置處理方法):

package cn.ganlixin.processor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

@Slf4j
public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {

    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        log.info("DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, \n\tbeanName:{}, bean:{}", beanName, bean);
    }

    @Override
    public boolean requiresDestruction(Object bean) {
        return true; // 返回true,一律執行postProcessBeforeDestruction方法
        // 如果返回false,則不執行postProcessBeforeDestruction方法
    }
}

  

9.4 配置xml文件  

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 創建student bean,指定init-method和destroy-method -->
    <bean class="cn.ganlixin.entity.Student" id="student" init-method="display" destroy-method="clean">
        <property name="id" value="99"/>
        <property name="name" value="張三"/>
    </bean>


    <!-- 將實現了DestructionAwareBeanPostProcessor接口的實現類聲明為bean-->
    <bean class="cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor"/>

    <!-- 將實現了BeanPostProcessor接口的類也聲明為bean-->
    <bean class="cn.ganlixin.processor.MyBeanPostProcessor"/>
</beans>

  

9.5 測試代碼

package cn.ganlixin;

import cn.ganlixin.entity.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@Slf4j
public class Test {

    public static void main(String[] args) {
        // 使用ApplicationContext來加載配置
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = context.getBean("student", Student.class);

        log.info("測試程序獲取到的student bean:{}", student);

        // 刪除bean
        BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getAutowireCapableBeanFactory();
        factory.removeBeanDefinition("student");
    }
}

  

9.6 輸出結果

INFO  [main] cn.ganlixin.entity.Student - BeanNameAware.setBeanName, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - BeanFactoryAware.setBeanFactory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - ApplicationContextAware.setApplicationContext, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessBeforeInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - InitialingBean.afterPropertiesSet, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - init-method, Student.display, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyBeanPostProcessor - MyBeanPostProcessor.postProcessAfterInitialization, beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.Test - 測試程序獲取到的student bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.processor.MyDestructionAwareBeanPostProcessor - DestructionAwareBeanPostProcessor.postProcessBeforeDestruction, 
	beanName:student, bean:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - DisposableBean.destory, this:Student(id=99, name=張三)
INFO  [main] cn.ganlixin.entity.Student - destroy-method, Student.clean, this:Student(id=99, name=張三)

  

十.總結

  看了上面這個輸出結果,再結合下面這個圖,基本就能掌握Bean的大致生命周期了。

  

 

   原文地址:https://www.cnblogs.com/-beyond/p/13188675.html

   

 

  

 

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

【其他文章推薦】

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

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

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

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

線段樹(毒瘤)總結

我們在這篇博客里將具體介紹一種超級毒瘤超級高效的算法
線段樹

概念引入

首先來認識一下線段樹
什麼是線段樹呢:
線段樹是一種二叉樹,也就是對於一個線段,我們會用一個二叉樹來表示。比如說一個長度為6的線段,我們可以表示成這樣

這個圖是什麼意思呢?

  • 將這個做成一個樹的結構 每個根節點存儲左右兩個節點的權值之和
    舉個栗子:最上邊的線段表示1~6的和 而他的左兒子表示1~3的和 右兒子表示4~6的和
  • 然後他左兒子的左兒子又表示1~2的和 左兒子的右兒子表示3的權值
  • 因此 節點i的權值=i左兒子的權值+i右兒子的權值
  • 所以我們可以得到 tree[rt].sum = tree[l].sum + tree[r].sum
  • 根據這個原理 我們就可以進行遞歸建樹了
struct node{
      int l,r,sum;//l表示左兒子 r表示右兒子  sum表示當前節點存儲的權值
}tree[maxn*4];

void build(int i,int l,int r){
	tree[i].l = l;tree[i].r = r;
	if(l == r){
		tree[i].sum = a[l];//a數組存儲給出的數組初始值
		return;
	}
	int mid = (l+r)/2;
	build(i*2,l,mid);
	build(i*2+1,mid+1,r);
	tree[i].sum = tree[i*2].sum+tree[i*2+1].sum;
	return;
}

這就是線段樹的建樹方法 如果你要問為什麼我們要花好幾倍的內存去建樹來完成一個數組就能完成的事情 那就是因為我們需要讓這個超級大的數組去干一些比較困難的事情
那什麼是比較困難的事情呢 讓我們進入下個專題

簡單的操作

單點修改 區間查詢

  • 舉個例子 我們要求出區間1~5的和
  • 顯然可以 for(int i = 1;i<=5;++i){ans+=a[i]};
  • 但是我們仍然要使用線段樹來進行操作
  • 首先看區間的位置
    當前處在根節點 存儲的左邊界是1 右邊界是6
    根節點的左兒子的左邊界是1 右邊界是3 右兒子的左邊界是4 右邊界是6
    左兒子的區間完全被該區間包括 所以我們直接返回左兒子的權值
    右兒子的左邊界在目標區間右邊界的左邊 所以我們繼續遞歸搜索右邊界
    現在最新的左兒子為4~5 完全包括在目標區間之中 直接返回權值 右兒子6與該區間毫無關係 返回0
    現在我們就可以把返回的值加起來了 3+2=5
  • 有人可能會吐槽了 用一個數組能解決的問題 為什麼要搞的這麼複雜
  • 但是有的時候雖然數組很方便 但是他並不能滿足我們的需求 ,O(n)的效率 ,有時候是無法令出題人快樂的, 這個時候就需要用到線段樹了 O(\(log_n\))

因此用代碼怎麼實現呢 也很簡單
先讓我們總結一下線段樹的查詢方式:

  • 如果當前區間完全被包括在目標區間之中,直接返回當前區間的權值
  • 如果當前區間與目標區間毫無關係 直接返回 0
  • 如果當前區間與目標區間有交叉 繼續遞歸搜索左兒子和右兒子
    那我們就可以有這樣的代碼實現形式
int search(int rt,int l,int r){
	if(tree[rt].r < l ||tree[rt].l > r)return 0;
	if(tree[rt].l >= l && tree[rt].r <= r)return tree[rt].sum;
	int ans = 0;
	if(tree[rt*2].r >= l)ans += search(2*rt,l,r);
	if(tree[rt*2+1].l <= r)ans += search(2*rt+1,l,r);
	return ans;
}

 那單點修改呢  這個相對就簡單許多了 * 給出一個位置x 一個值k * 如果我們要修改x位置的數 讓他加上一個數k 我們就讓樹去遞歸尋找這個位置 “`cpp void add(int rt,int x,int k){ if(tree[rt].l == tree[rt].r){//到達恭弘=叶 恭弘子節點 說明找到該位置 tree[rt].sum += k; return; } if(x <= tree[rt*2].r)add(rt*2,x,k); // 遞歸搜索左兒子 else add(rt*2+1,x,k);//遞歸搜索右兒子 tree[rt].sum = tree[rt*2].sum + tree[rt*2+1].sum;//重新將權值加和 return; } “`

區間修改單點查詢

區間修改和單點查詢的方法有很多
為了一會對pushdown的講解 我們這裏說一種比較便於下面理解的方法

區間修改和區間查詢很像

  • 不過區間查詢的 ”如果當前區間完全包括在目標區間就返回當前區間的值“要改為將當前區間打上k標記
  • 舉個例子: 我們要把一個區間所有的數加上k
  • 那就去遞歸搜索線段樹 如果發現某個線段樹的區間完全包括在目標區間中 那就將這個區間打上k標記
  • 但是我們這裏的建樹就要有所不同了
  • 因為我們的所有節點的初始值都會為0(為了便於記錄標記k)
void build(int l,int r,int rt){
    tree[rt].num=0;
    tree[rt].l=l;
    tree[rt].r=r;
    if(l==r)
        return ;
    int mid=(r+l)/2;
    build(l,mid,rt*2);
    build(mid+1,r,rt*2+1);
}

void add(int rt,int l,int r,int k){
    if(tree[rt].l>=l && tree[rt].r<=r){
        tree[rt].num+=k;
        return ;
    }
    if(tree[rt*2].r>=l)
       add(rt*2,l,r,k);
    if(tree[rt*2+1].l<=r)
       add(rt*2+1,l,r,k);
}

單點查詢可以去尋找這個節點 路上遇到的所有標記都要累加起來 最後再加上這個節點的初始值 用代碼實現大概是這個樣子 “`cpp void search(int rt,int dis){ ans+=tree[rt].num; if(tree[rt].l==tree[rt].r) return ; if(dis<=tree[rt*2].r) search(rt*2,dis); if(dis>=tree[rt*2+1].l) search(rt*2+1,dis); } //主函數中 printf(“%d\n”,ans+a[x]);//a[x]為目標位置的初始值 “`

建議將上面的板子打熟再向下繼續觀看

區間修改與區間查詢(pushdown and lazy)

前方高難
看到這樣的題你或許會想 不就是上邊那兩种放在一起嗎
但是如果你真的這樣寫完了代碼你會發現 WA
為什麼呢

先來回想一下剛才的操作:將區間加上標記 最終查詢的時候去從上往下找 將標記累加最後再加上初始值

但是這樣真的可以嗎?

答案是否定的 原因很簡單:如果你要求1~3區間的和 而你剛剛將3~5的區間加上標記 因為1~3並不包含3~5的標記 所以我們計算后的結果並不是加k之後的和 而是初始值的和

那如何解決這個問題呢? 也很簡單:只要將我們的標記k下放到i的兒子不就好了嗎

所以我們的算法雛形就出來了(這也是線段樹最毒瘤而且難調最具有魅力的地方)

  • 首先我們在結構體中多定義一個變量lazy 用於記錄標記 每次有加的操作的時候我們就加到lazy上
  • 然後就是下放操作pushdown 用於將lazy下放到i的兒子節點中
  • 所以通過簡單的推理和歸納我們仍然有以下性質:
      1. 如果當前區間完全被包含在目標區間中 則這個區間的權值 tree[rt].sum += k*(tree[rt].r - tree[rt].l + 1)
      2. 如果當前區間與目標區間有交集但是並沒有被完全覆蓋 就下放懶惰標記
      3. 下放之後分別對左兒子和右兒子進行相同的操作
  • 最後仍然是按照tree[rt].sum = tree[rt2].sum + tree[rt2+1].sum向上更新
    因此代碼實現就是
void pushdown(long long rt){
	if(tree[rt].lazy != 0){//如果當前區間已經被標記
		tree[rt*2].lazy += tree[rt].lazy;//下放到左兒子
		tree[rt*2+1].lazy += tree[rt].lazy;//下放到右兒子
		long long mid = (tree[rt].l + tree[rt].r)/2;
		tree[rt*2].sum += tree[rt].lazy*(mid - tree[rt*2].l + 1);//更新左兒子的值
		tree[rt*2+1].sum += tree[rt].lazy*(tree[rt*2+1].r - mid);//更新右兒子的值
		tree[rt].lazy = 0;//清空當前節點的懶惰標記
	}
	return;
}

void add(long long rt,long long l,long long r,long long k){
	if(tree[rt].l >= l && tree[rt].r <= r){//如果當前區間完全包含在目標區間直接更新並且標記懶惰標記
		tree[rt].sum += k*(tree[rt].r-tree[rt].l+1);//更新當前區間的權值
		tree[rt].lazy += k;//增加懶惰標記
		return;
	}
	pushdown(rt);//下放懶惰標記
	if(tree[rt*2].r >= l)add(rt*2,l,r,k);//遞歸更新左兒子
	if(tree[rt*2+1].l <= r)add(rt*2+1,l,r,k);//遞歸更新右兒子
	tree[rt].sum = tree[rt*2].sum+tree[rt*2+1].sum;//更新當前節點的權值
	return;
}

區間查詢的時候和之前幾乎一樣 不同的是要進行懶惰標記的下放之後在累加

long long search(long long rt,long long l,long long r){
	if(tree[rt].l >= l && tree[rt].r <= r)return tree[rt].sum;//如果當前區間完全包含在目標區間內 直接返回當前區間的權值
	if(tree[rt].r < l || tree[rt].l > r)return 0;//如果當前區間和目標區間完全沒有關係 直接返回0
	pushdown(rt);//下放懶惰標記
	long long s = 0;
	if(tree[rt*2].r >= l)s += search(rt*2,l,r);
	if(tree[rt*2+1].l <= r)s += search(rt*2+1,l,r);
	return s;//最後返回這個區間的和
}

線段樹模型大概就是這個樣子(線段樹還是比較受出題人青睞的難道是因為難調??)
附上練習攻略:
簡單線段樹建議用洛谷P3374【模板】樹狀數組1
        洛谷P3368【模板】樹狀數組2練習板子
如果簡單線段樹沒有問題了
可以去嘗試一下:洛谷P3372【模板】線段樹1
        洛谷P3373【模板】線段樹2
        洛谷P6242【模板】線段樹3

謝謝觀看
點個關注>_<

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

電動車不只轎跑車市場,電動垃圾車可成利基

電動車熱潮可說成也特斯拉(Tesla)、敗也特斯拉,先是因特斯拉成功引起媒體追逐熱潮而一時暴紅,也因為特斯拉表現不如預期遭市場唱空而暫時沉寂,不過,電動車的商機並不只有特斯拉所主打的時尚轎、跑車,其實若論節能減碳與減少空污,每天固定行駛里程數更高,煞停又重新起步更頻繁,而且重量與油耗都遠大於轎車的垃圾車,更是利基市場。  
    電動車固然需要充電,而電力也可能來自於火力發電,但是火力發電廠的能源效率可達 60%,汽油引擎內燃機卻只有 30%,這是電動車節能的理論基礎之一;此外,電動車可應用煞車回電,甚至如輪胎大廠固特異正研發「燒胎回電」,在煞車時可將車體的動能部分回收為電能,內燃機引擎車煞車時所有動能都化為摩擦熱喪失,這是電動車較內燃機引擎車節能的理論基礎之二,因此,車體越重,煞停時消耗的動能越大、行駛時煞車次數越多,電動車較內燃機節省能源的幅度就更高。   以此觀之,沉重的垃圾車,每到定點就要停車,等裝完垃圾再重新啟動,顯然比開在高速公路的輕量跑車來得更有節能空間。   於是,原本也是特斯拉共同創辦人之一的伊恩萊特(Ian Wright),選擇鎖定垃圾車,成立新創事業「萊特速度」(Wrightspeed),伊恩萊特表示,一般標準垃圾車每天都要煞停、再啟動 1,000 次,導致每 3 個月就會磨光煞車皮,每年燒掉 1.4 加侖汽油。這是很有節能潛力的目標。   萊特速度改裝現有垃圾車,將原本內燃機動力總成改裝為電動動力總成,並且加裝煞車回電系統,除了可回收電力以外,也能減輕煞車皮的負擔,不再每三個月就磨光煞車皮,而為了延長行駛距離,動力總成上還加裝了一組汽油發電機,作為備用能源,這與油電混合動力車不同,汽油發電機並不是直接驅動車輛,而是供電給電池,透過電動動力總成來驅動車輛,維持全電動的動力總成架構。    
省成本又解決空污問題   有了汽油發電機,只要有加油站,就不擔心會停擺,也因此,萊特速度的電池容量也不大,標準垃圾車電池容量僅 78 度電,而中型垃圾車電池容量更只有 26 度電,相較之下,特斯拉 Model S 最大擁有 85 度電容量的電池。   但萊特速度在煞車回電上的能力則遠大於特斯拉轎車,煞車回電的發電容量最高達 730 千瓦,以提供強大的煞車力來煞停沉重的垃圾車,並將盡可能多的動能回收為電能。   而煞車回電不僅回收電池電量,減少煞車皮損耗,也減少了煞車時對車軸與整個車身造成的損耗,伊恩萊特表示,一般垃圾車行駛 20 萬英里就要報廢,大概只能開上 5 年,並且需要大量維修工作,轉換為萊特速度電動系統以後,可大為延長使用期限,每年除了減少 3.5 萬美元的燃料費用以外,還能再減少 2 萬美元的維修費用,這讓萊特速度的客戶可在 4 年內回本。   除了省錢以外,改裝為電動車也解決了老舊垃圾車的空污問題,以加州為例,新的空污標準讓老舊垃圾車瀕臨淘汰,使用老舊垃圾車的單位面臨必須重買新車以符合空污排放標準的難題,但如果以萊特速度的技術改裝為電動車,就解決了空污問題,其成本也比重買新車來得低。   而這不僅是空氣污染問題,垃圾車穿越社區時,引擎聲往往擾人清夢,伊恩萊特認為,改為電動車之後,安靜將是一大競爭力。由於美國有 11 萬輛垃圾車,除了垃圾車以外,送貨用車如快遞業者的送貨廂型車等也是可能的客戶,萊特速度的改裝生意大有可為。   伊恩萊特更自信滿滿地表示,雖然特斯拉做得很好,但在保護環境方面,萊特速度能減少的碳排放、空氣污染與噪音一定能超過特斯拉。萊特速度到 2015 年 3 月,募資總額達 3,200 萬美元,目前正在進行最新一輪募資,預期要雇用至 300 名員工,提升最大年產能至 5,000 輛規模。     本文全文授權轉載自《科技新報》─〈〉

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

【其他文章推薦】

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

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

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

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

攜手 Uber 比亞迪進軍美電動車市場

叫車服務 Uber 日前證實已與比亞迪達成合作協議,將在美國推廣電動車服務,此舉被視為比亞迪挑戰美國電動車霸主特斯拉的一項舉動。   Uber 發言人 Lauren Altmin 指出,Uber 與比亞迪合作,將是其聚焦電動汽車服務的第一步。Uber 的測試計畫幾周前已在芝加哥展開,並最終將擴大到更多美國城市。Altmin 表示,Uber 去年年底開始與比亞迪和美國汽車銷售商 Green Wheels USA 展開談判,Green Wheels USA 將銷售比亞迪 e6 電動車,並負責在當地建設充電樁。   報導指出,Uber 正試圖用電動車擴大其低階租車領域的市場規模。比亞迪則透過與 Uber 的合作,實現進軍美國電動汽車市場的目標。

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

【其他文章推薦】

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

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

解讀兩會後新能源汽車盛況——第五屆中國國際新能源汽車論壇5月在滬召開

近些年,環保問題的凸顯,阻礙了傳統能源汽車快速前進的腳步,多地以環境保護的緣由實行限購限行等措施,而在此非常時期,新能源汽車的崛起和發展就越來越受到各方的關注。《穹頂之下》讓業界積極關注解決霧霾的辦法。工業燒煤與汽車尾氣排放是造成城市霧霾現象的主因,新能源替代煤炭與石油是保證中國可持續發展的主要措施,因此新能源汽車需要得到推廣也成為業界共識。

恰逢此時,新能源汽車技術日趨成熟,新能源汽車的崛起和發展已經受到越來越多的人關注,”兩會”期間,新能源汽車的發展成為代表們熱議的話題。新能源汽車的發展能否借助”兩會”的東風,走上快車道。

在連續四屆精彩紛呈的新能源汽車論壇和一屆亞太鋰電池峰會的基礎上,由希邁商務諮詢主辦,上海交大密西根學院、工信部賽迪顧問大力支持,並得到了亞太電動車協會陳清泉院士傾情指導的“第五屆中國國際新能源汽車論壇2015”將於2015年5月18日至19日在上海隆重召開。屆時將有三百位行業領導出席本次論壇。

部分已確認參會的公司如下:

工業和資訊化部中國電子資訊產業發展研究院、上海新能源汽車推進辦、比亞迪戴姆勒、特斯拉、樂視超級汽車(中國)有限公司、富豪汽車、通用汽車中國、上汽集團、中科力帆、江鈴汽車、揚州亞星客車股份有限公司、長城新能源、上海大眾聯合、中國電力企業聯合會、艾爾維、NEC、綠嘀汽車租賃(北京)有限公司、泰國國家石油公司、ATL、飛思卡爾、東莞振華新能源等等。

會議結構

  上午 下午
大會第一天

“智”造時代政策預測、製造趨勢及試點城市推廣情況

工信部、科技部、交通部、發改委及地方政府的政策支持
下一代電動汽車產品及商業模式
國內外整車商新能源新動向

新能源汽車電機及控制系統

新能源汽車開發特點及供應配套要求
動力總成電氣化
變速器和電驅動的開發技術與實踐

大會第二天

基礎設施引領商業新模式

充電基礎設施建設的市場分析
能源互聯網電動汽車項目
實現高效、智慧、規範的網路化建設與運營

新能源汽車市場新常態

樂視——重新定義汽車
谷歌更酷的下一步:無人駕駛
無線充電技術新突破
車身輕量化

微型電動車市場准入

政策准入前景
市場格局變化
電池技術路線選擇

動力電池突破之路

動力電池未來商業模式、技術路線探索
燃料電池及材料研發
智慧電池以及電動車動力管理系統

      整車廠商-電池企業對接洽談會

據悉,中國國際新能源汽車論壇將吸引來自國內外主流參會企業和嘉賓,預計將達到300人左右。相對規模與檔次都名列亞洲前茅,學習促進行業發展,業主相互技藝分享,充分享受資源平臺,創造最大企業價值。

連絡人:Hill ZENG(曾先生)
電話:+86-21-6045 1760
傳真:+86-21-6047 5887
郵箱:
唯一報名官方網址:

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

續航力有解?特斯拉將推軟體更新

特斯拉執行長馬斯克 (Elon Musk) 宣稱 19 日將發布軟體更新,車主不必再憂慮電動車續航力不足的問題。   馬斯克 15 日在推特發文宣布,車主不必再煩惱電動車里程數,特斯拉將透過軟體更新解決問題,Model S 全系列車種皆可受惠。他說,特斯拉將在美西時間 19 日上午 9 點舉辦記者會說明。   目前還不清楚特斯拉是否找出方法,加長電動車續航力;或要更新地圖,方便車主尋找附近充電站。目前 Model S 的 60 kilowatt 車款,充飽電可跑 208 英里;85kwh 車款則可跑 265 英里,續航力超過市面現行的電動車。      

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

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

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

為在中國實現國產 特斯拉將與重慶車企合作電動車?

昨(17)日, 重慶整車製造企業力帆集團在其微信公眾號上稱特斯拉汽車公司的投資及政府關係總監肯•摩根一行於3月16日造訪了力帆集團,並與力帆高層就電動車領域的充電站建設、電池、投入與使用成本等話題進行了深入交流。   據力帆集團高層介紹,肯•摩根對力帆純電動汽車底盤換電池技術十分感興趣。力帆純電動汽車底盤換電池技術已於去年4月獲得了國家發明專利,該技術對換電池過程實現了全智慧控制,換電池時間可控制在3分鐘以內。   此外,肯•摩根還造訪了長安新能源汽車。有知情人士透露,長安也是特斯拉的潛在合作夥伴。長安汽車之前計畫斥資180億元人民幣投入到研發、技術、供應、運營四個領域,2015至2025年將分三個階段推出共計34款新能源產品,其中純電動車佔多數。   特斯拉CEO埃隆•馬斯克此前不止一次稱特斯拉會在2-3年內在中國達到國產。有業內人士表示,此次特斯拉來渝探營,將有可能促進重慶車企與特斯拉聯手,打造純電動汽車。 

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

【其他文章推薦】

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

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

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

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

新北清潔公司,居家、辦公、裝潢細清專業服務

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

PK 中國製!屏科大研發電動車獲訂單

  高雄佛光山因坐落在山區,高低坡起伏大,向中國進口電動車代步,但常常載了人後,遇到坡度大的地方就無法爬坡,屏東科技大學車輛工程系教授陳勇全帶領研究團隊,22 名學生經過 2 年時間,研發出第 3 代電動車,經實地測試,屏科大的電動車載滿 8 個人後爬坡,大勝僅載有司機 1 人的中國製電動車,佛光山立即向屏科大下訂 10 輛電動車,因目前尚未進入量產階段,屏科大預計最快 6 月底交車。   陳勇全指出,目前國內外生產的觀光休閒電動車大多只能在平地行駛,無法長時間行駛於陡坡,或是搭載人數有限,屏科大團隊經過 2 年研發,除了可輕鬆搭載8人,一般30%(約 15 度)以內的坡度行駛無需換檔,且具有電動輔助轉向系統、真空輔助煞車系統、下坡電磁自動煞車等,提高安全性與駕駛操控性。   學生許峰玠表示,價格方面贏不過中國,他們把重點放在更安全、更舒適、效能更好的電動車,研究團隊分成控制、懸吊、結構等三方面共同研發,他負責結構部分,利用高強度鋼板、防鬆脫螺絲製作車體結構,取代一般市面上的焊接鋼板,目前已通過專利申請,畢業後也希望加入學校育成中心計劃,繼續電動車的量產與開發。   屏科大校長戴昌賢指出,許多像屏科大一樣位於山區的學校,都有代步電動車的需求,目前除了佛光山下訂單,法鼓山也在接洽中,由於校方屬研發單位,無法大量接單進行量產,將輔導研發團隊學生技轉創業,達到學以致用的目的。

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

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

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

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

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

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準