有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java为什么对空引用调用方法会成功编译?

正在尝试执行这段代码

public class Main {
    public static void main(String... args) {
        new Main();
    }

    Main() {
        A a = null;
        a.foo();
    }
}

class A {
    void foo() {}
}

很明显,我将得到一个NullPointerException,因为a已初始化为null。代码编译是因为我(编码者)知道变量a只能在此位置为null,而它(编译器)。。。事实上,它也知道这一点,以至于我得到了警告信息

Null pointer access: The variable a can only be null at this location

我的问题是,既然我和it都知道会抛出异常,为什么允许我的代码编译?有没有可能不出现例外情况

编辑:

换句话说,为什么我会在这样的事情中遇到编译器错误

    try {
        throw new Exception();
        int x = 0;
    } catch (Exception e) {
    }

而不是在这方面

    try {
        if(true)throw new Exception();
        int x = 0;
    } catch (Exception e) {
    }

放得很差:编译器如何区分阻塞与不阻塞“明显错误”


共 (4) 个答案

  1. # 1 楼答案

    Java spec

    4.12.2

    A variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T.

    还有, 例如,考虑:

             while(true)
            {
            //stay blank
            }
    

    这在我的NetBeans中给出了一个错误,即无法访问下面的行

    再一次

           while(true)
            {
               if(false)break;
            }
    

    现在你期待什么?同样的事?不是。它进入无限循环。 同样,您应该能够在代码中捕获NPE。这是允许的。因此,对于不同类型的歧义,可能存在许多组合。这很难选择&;限制

  2. # 3 楼答案

    我可能不完全正确,但应该归咎于解析器。我实现了一个Java parser 1.5,它基本上调用compilationUnit()。首先获取源的流

    流被标记化(创建标记——关键字、变量……所有较小的部分)。然后,它可能会进行多次调用——例如,如果Java程序必须以关键字开头,那么它可能会查找访问说明符。之后,解析器需要有关键字“class”。它会在关键词之后找到其他可能性。我可以写public class{}public int i;。如果得到正确的关键字,解析将继续,否则将抛出一个ParseException

    例如,我在解析器中做了一个小改动:

    private Token jj_consume_token(int kind) throws ParseException {   
            Token oldToken;
            if ((oldToken = token).next != null) {
                token = token.next;
            } else {
                token = token.next = token_source.getNextToken();
            }
            jj_ntk = -1;
            if (token.kind == kind) {
                jj_gen++;
                if (++jj_gc > 100) {
                    jj_gc = 0;
                    for (int i = 0; i < jj_2_rtns.length; i++) {
                        JJCalls c = jj_2_rtns[i];
                        while (c != null) {
                            if (c.gen < jj_gen) {
                                c.first = null;
                            }
                            c = c.next;
                        }
                    }
                }
    
                return token;
            }
    
            jj_kind = kind;
            throw generateParseException();
    
            } 
        }
    

    致:

    private Token jj_consume_token(int kind) throws ParseException {
    
            try{
            Token oldToken;
            if ((oldToken = token).next != null) {
                token = token.next;
            } else {
                token = token.next = token_source.getNextToken();
            }
            jj_ntk = -1;
            if (token.kind == kind) {
                jj_gen++;
                if (++jj_gc > 100) {
                    jj_gc = 0;
                    for (int i = 0; i < jj_2_rtns.length; i++) {
                        JJCalls c = jj_2_rtns[i];
                        while (c != null) {
                            if (c.gen < jj_gen) {
                                c.first = null;
                            }
                            c = c.next;
                        }
                    }
                }
    
                return token;
            }
            token = oldToken;
            jj_kind = kind;
            throw generateParseException();
               } catch (ParseException ex) {
                recover(ex,SEMICOLON);
                return token;
            } 
        }
    

    恢复块看起来像:

    void recover(ParseException ex, int recoveryPoint) {
            syntaxErrors.add(ex);
            Token t;
            do {
                t = getNextToken();
            } while (t.kind != EOF && t.kind != recoveryPoint);
        }
    

    我刚才做了什么?我恢复了解析器抛出的异常,即使没有方法或只有语句。例如

    class A{
    Date d;
    System.out.print("No exception");
    }
    

    我现在没有错误。我做了些改变。因此,如果希望解析器在这种情况下报告异常,可以实现解析器:)希望您现在就得到它

  3. # 4 楼答案

    它可以编译,因为Java语言规范并不禁止它。谁知道呢,也许你想在调用者中捕捉这个NullPointerException。这不是最好的代码设计,但JLS允许这样做

    请注意,Eclipse编译器可以配置为此时显示错误。默认情况下,它会发出警告:

    $ java -jar org.eclipse.jdt.core_3.10.2.v20150120-1634.jar -source 1.7 -cp rt.jar Main.java
    ----------
    1. WARNING in L:\Lan\Projects\JavaTest\EcjTest\src\Main.java (at line 8)
           a.foo();
            ^
    Null pointer access: The variable a can only be null at this location
    ----------
    1 problem (1 warning)
    

    此外,静态代码分析器(如FindBugs)将触发此代码的警告

    为什么不被JLS禁止的问题是基于观点的。但是请注意,现在无法添加此类功能,因为这可能会破坏向后兼容性。我相信,有一个现有的代码实际上可能依赖于这种行为