initialized. 是在Flow階段由AssignAnalyzer檢測出來的,需要的朋友可以參考下" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javac final變量未賦值檢測案例講解

 更新時間:2022年12月20日 10:49:37   作者:一個努力的碼農(nóng)  
這篇文章主要介紹了javac final變量未賦值檢測案例講解,通過本文,我們可以知道Eclipse中報如下錯誤:The blank final field b may not have been
initialized. 是在Flow階段由AssignAnalyzer檢測出來的,需要的朋友可以參考下

前言

我們在前面介紹AssignAnalyzer時,對AssignAnalyzer.letInit(DiagnosticPosition, VarSymbol)方法進行了簡單的介紹.本文就舉一個案例,來深入理解一下.

案例

案例代碼如下:

public class CheckInitError {

	static final int b;
	
	public CheckInitError(){
		
		
	}
	
}

本代碼在IDE環(huán)境(如Eclipse)中報如下錯誤:The blank final field b may not have been
initialized.
那么它是如何檢測出錯誤的,本文就來揭秘.(Eclipse中內(nèi)置了Java編譯器)

解析

還是在javac中的Flow階段,最終來到了AbstractAssignAnalyzer.analyzeTree(Env<?>, JCTree)方法,在該方法中,又調(diào)用了Flow.BaseAnalyzer.scan(JCTree)方法,進而調(diào)用AbstractAssignAnalyzer.visitClassDef(JCClassDecl)方法,同時,由于字段b是可追蹤的,因此會在處理靜態(tài)字段時調(diào)用AbstractAssignAnalyzer.newVar(JCVariableDecl)方法,將b所對應的符號保存在vardecls中,下標位置為0(下標為0的原因是它是第一個變量).這部分的內(nèi)容是在上篇文章中有所介紹的,本文不再展開.

在AbstractAssignAnalyzer#visitClassDef方法中,處理完靜態(tài)字段,靜態(tài)初始塊,實例字段,實例初始塊之后,就會處理方法(包括構造器).

那么由于在CheckInitError中只定義了一個構造器,因此如下代碼只會處理一次:

for (List<JCTree> l = tree.defs; l.nonEmpty(); l = l.tail) {
    if (l.head.hasTag(METHODDEF)) {
        scan(l.head);
    }
}

由于CheckInitError構造器所對應的樹節(jié)點是JCMethodDecl,因此最終會調(diào)用AbstractAssignAnalyzer.visitMethodDef(JCMethodDecl).

在AbstractAssignAnalyzer#visitMethodDef中,一開始,是進行判斷:

// 如果該方法的語句體為null,則意味著該方法是一個抽象方法,不處理.
if (tree.body == null) {
    return;
}

// 忽略處理合成方法,但是合成的lambda方法還是處理的
if ((tree.sym.flags() & (SYNTHETIC | LAMBDA_METHOD)) == SYNTHETIC) {
    return;
}

然后是保存現(xiàn)場:

final Bits initsPrev = new Bits(inits);
final Bits uninitsPrev = new Bits(uninits);
int nextadrPrev = nextadr;
int firstadrPrev = firstadr;
int returnadrPrev = returnadr;

Assert.check(pendingExits.isEmpty());

接下來,判斷當前方法是一個構造函數(shù),其構造函數(shù)中的第一個語句不是this(…)的語句嗎?

 boolean isInitialConstructor =
                    TreeInfo.isInitialConstructor(tree);

思考: 為何會在這里做這樣的判斷? AssignAnalyzer的定位是檢查final變量是否有多次賦值,那么假設我們在一個類中final 字段(未初始化的),那么不管你有多少個構造函數(shù),那么就應該在一個最終調(diào)用的構造器中對這個變量進行初始化.舉例:

public class CheckInitError {
	
	final int b;
	public CheckInitError(){
		//this(3); // 第1種
		b = 3; // 第2種
		// 如果第1行,第2行注釋掉,則報錯,因為b沒有最終初始化
	}
	public CheckInitError(int a ){
		b  = a;
	}
	
} 

那么對于CheckInitError來說, CheckInitError方法構造函數(shù)中的第一個語句不是this(…),而是super();因此isInitialConstructor為true.為啥呢? javac會在構造器的第一行插入super(),至于是在什么條件下插入,如何插入,我們后續(xù)介紹,本文不表.

由于isInitialConstructor等于true,因此,如下代碼是不會執(zhí)行的:

if (!isInitialConstructor) {
    firstadr = nextadr;
}

接下來,是處理方法的參數(shù),那么由于在案例中是沒有參數(shù)的,因此如下代碼是不會執(zhí)行的:

for (List<JCVariableDecl> l = tree.params; l.nonEmpty(); l = l.tail) {
    JCVariableDecl def = l.head;
    scan(def);
    // 參數(shù)應該有PARAMETER的修飾符,否則就是一個錯誤
    Assert.check((def.sym.flags() & PARAMETER) != 0, "Method parameter 	without PARAMETER flag");
	initParam(def);
}

protected void initParam(JCVariableDecl def) {
	inits.incl(def.sym.adr);
	uninits.excl(def.sym.adr);
}

這段代碼的作用是依次處理參數(shù),然后將參數(shù)加入到變量已經(jīng)初始化的位圖中,至于為啥? 原因很簡單:參數(shù)是調(diào)用方傳遞的,當方法執(zhí)行時,形參是肯定有值的(初始化的),否則就是一個錯誤

接下來處理方法體,由于javac默認添加了一個super()語句,那么就會進行實質的處理(副作用).但是這部分與本文關聯(lián)不大,本文就不展開了.

方法體執(zhí)行完之后,如果isInitialConstructor為true,則判斷構造器是否將類中的變量(final變量但是沒有初始賦值的)都初始化了.如下:

if (isInitialConstructor) {
    boolean isSynthesized = (tree.sym.flags() &
                             GENERATEDCONSTR) != 0; // 判斷該構造器是否是合成的
    // 這里判斷的是構造器是否將類中的變量(final變量但是沒有初始賦值的)都初始化了.
    for (int i = firstadr; i < nextadr; i++) {
        JCVariableDecl vardecl = vardecls[i];
        VarSymbol var = vardecl.sym;
        if (var.owner == classDef.sym) {
            // choose the diagnostic position based on whether
            // the ctor is default(synthesized) or not
            if (isSynthesized) {
                checkInit(TreeInfo.diagnosticPositionFor(var, vardecl),
                    var, "var.not.initialized.in.default.constructor");
            } else {
                checkInit(TreeInfo.diagEndPos(tree.body), var);
            }
        }
    }
}

那么對于當前,由于該構造器不是合成的,因此isSynthesized為false.同時,在該類中只定義了一個變量–> b,那么因此循環(huán)只會執(zhí)行一次(firstadr = 0,nextadr = 1,這部分的內(nèi)容在上篇文章有介紹)

在循環(huán)中,通過下標取得b對應的VarSymbol,調(diào)用AssignAnalyzer.checkInit(DiagnosticPosition, VarSymbol, String)方法進行判斷.如下:

void checkInit(DiagnosticPosition pos, VarSymbol sym, String errkey) {
    if ((sym.adr >= firstadr || sym.owner.kind != TYP) &&
        trackable(sym) &&
        !inits.isMember(sym.adr)) {
        log.error(pos, errkey, sym);
        inits.incl(sym.adr);
    }
}

對于當前來說,符號是可跟蹤的,但是在inits(初始化變量位圖)中不存在對應的下標,因此會調(diào)用log#error方法,進行錯誤日志輸出.然后將其加入到inits(這樣做的目的,是為了一次編譯能獲得更多的錯誤信息)

對于當前,是報如下錯誤:

1

然后,是pendingExits 處理和恢復現(xiàn)場,這部分的內(nèi)容,我們后續(xù)文章會舉例子進行講解.

總結

通過本文,我們可以知道Eclipse中報如下錯誤:The blank final field b may not have been
initialized. 是在Flow階段由AssignAnalyzer檢測出來的.

到此這篇關于javac final變量未賦值檢測案例講解的文章就介紹到這了,更多相關javac final變量未賦值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Netty分布式客戶端接入流程初始化源碼分析

    Netty分布式客戶端接入流程初始化源碼分析

    這篇文章主要介紹了Netty分布式客戶端接入流程初始化源碼分析,有關channelConfig有關的初始化過程剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Java防鎖屏小程序代碼實例

    Java防鎖屏小程序代碼實例

    這篇文章主要介紹了Java防鎖屏小程序代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-09-09
  • SpringBoot實現(xiàn)多數(shù)據(jù)源的切換實踐

    SpringBoot實現(xiàn)多數(shù)據(jù)源的切換實踐

    這篇主要介紹了SpringBoot實現(xiàn)多數(shù)據(jù)源的切換,本文基于AOP來實現(xiàn)數(shù)據(jù)源的切換,文中通過示例代碼介紹的非常詳細,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 靈活控制任務執(zhí)行時間的Cron表達式范例

    靈活控制任務執(zhí)行時間的Cron表達式范例

    這篇文章主要為大家介紹了靈活控制任務執(zhí)行時間的Cron表達式范例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • SpringBoot集成RocketMQ的使用示例

    SpringBoot集成RocketMQ的使用示例

    RocketMQ是阿里巴巴開源的一款消息中間件,性能優(yōu)秀,功能齊全,被廣泛應用在各種業(yè)務場景,本文就來介紹一下SpringBoot集成RocketMQ的使用示例,感興趣的可以了解一下
    2023-11-11
  • Java中和隊列相關的基本操作

    Java中和隊列相關的基本操作

    在Java中,隊列是一種常用的數(shù)據(jù)結構,用于存儲和管理元素。Java提供了Queue接口和其實現(xiàn)類,包括LinkedList和ArrayDeque等。隊列的基本操作包括入隊(enqueue)、出隊(dequeue)、獲取隊首元素(peek)和判斷隊列是否為空(isEmpty)。
    2023-09-09
  • Spring+Vue整合UEditor富文本實現(xiàn)圖片附件上傳的方法

    Spring+Vue整合UEditor富文本實現(xiàn)圖片附件上傳的方法

    這篇文章主要介紹了Spring+Vue整合UEditor富文本實現(xiàn)圖片附件上傳的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-07-07
  • 使用idea開發(fā)javaWeb應用程序的思路(實現(xiàn)用戶的增刪改查)

    使用idea開發(fā)javaWeb應用程序的思路(實現(xiàn)用戶的增刪改查)

    這篇文章主要介紹了使用idea開發(fā)javaWeb應用程序的思路(實現(xiàn)用戶的增刪改查),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Java8中List轉Map(Collectors.toMap) 的技巧分享

    Java8中List轉Map(Collectors.toMap) 的技巧分享

    在最近的工作開發(fā)之中,慢慢習慣了很多Java8中的Stream的用法,很方便而且也可以并行的去執(zhí)行這個流,這篇文章主要給大家介紹了關于Java8中List轉Map(Collectors.toMap) 的相關資料,需要的朋友可以參考下
    2021-07-07
  • java自定義類加載器代碼示例

    java自定義類加載器代碼示例

    這篇文章主要介紹了java自定義類加載器代碼示例,具有一定借鑒價值,需要的朋友可以了解下。
    2017-12-12

最新評論