避免Java中的NullPointerException

我使用x != null来避免#1。有替代方案吗?

if (x != null) {// ...}
1405795 次浏览

而不是空对象模式——它有它的用途——您可能会考虑空对象是bug的情况。

抛出异常时,检查堆栈跟踪并处理bug。

  • 如果你认为一个对象不应该为空(或者它是一个bug),请使用断言。
  • 如果您的方法不接受空参数,请在javadoc中说明并使用断言。

只有当您想处理对象可能为空的情况时,您才必须检查对象!=null…

有人建议在Java7中添加新的注释来帮助处理null/Notnull参数:http://tech.puredanger.com/java7/#jsr308

有时,您有一些方法可以对定义对称操作的参数进行操作:

a.f(b); <-> b.f(a);

如果你知道b永远不会为空,你可以交换它。它对equals最有用:而不是foo.equals("bar");最好做"bar".equals(foo);

根据您检查的对象类型,您可以使用apache共享中的一些类,例如:apacheCommonslangapache Commons集合

示例:

String foo;...if( StringUtils.isBlank( foo ) ) {///do something}

或(取决于您需要检查的内容):

String foo;...if( StringUtils.isEmpty( foo ) ) {///do something}

StringUtils类只是众多类中的一个;公地中有很多很好的类可以进行空安全操作。

下面是一个示例,说明在包含apache库时如何在JAVA中使用null验证(commons-lang-2.4.jar)

public DOCUMENT read(String xml, ValidationEventHandler validationEventHandler) {Validate.notNull(validationEventHandler,"ValidationHandler not Injected");return read(new StringReader(xml), true, validationEventHandler);}

如果您使用的是Spring,Spring在其包中也具有相同的功能,请参阅库(spring-2.4.6.jar)

关于如何从Spring使用这个静态类的示例(org.springframework.util.断言)

Assert.notNull(validationEventHandler,"ValidationHandler not Injected");

如果不允许空值

如果你的方法被外部调用,从这样开始:

public void method(Object object) {if (object == null) {throw new IllegalArgumentException("...");}

然后,在该方法的其余部分,您将知道object不是null。

如果它是一个内部方法(不是API的一部分),只需记录它不能为空,就是这样。

示例:

public String getFirst3Chars(String text) {return text.subString(0, 3);}

但是,如果您的方法只是传递值,而下一个方法传递它等,则可能会出现问题。在这种情况下,您可能需要检查上面的参数。

如果允许null

这真的取决于。如果发现我经常这样做:

if (object == null) {// something} else {// something else}

所以我做了两个完全不同的事情。没有丑陋的代码片段,因为我真的需要根据数据做两件不同的事情。例如,我应该处理输入,还是应该计算一个好的默认值?


我很少使用“if (object != null && ...”这个成语。

如果您展示您通常使用该习语的示例,则可能更容易为您提供示例。

提出这个问题表明您可能对错误处理策略感兴趣。如何以及在哪里处理错误是一个普遍的架构问题。有几种方法可以做到这一点。

我最喜欢的是:允许异常泛滥——在“主循环”或其他具有适当职责的函数中捕获它们。检查错误条件并适当地处理它们可以被视为一项专门的职责。

当然也要看看面向方面的编程——它们有巧妙的方法将if( o == null ) handleNull()插入到您的字节码中。

无论你在哪里传递一个数组或向量,将它们初始化为空,而不是null。-这样你就可以避免大量检查null,一切都很好:)

public class NonNullThing {
Vector vectorField = new Vector();
int[] arrayField = new int[0];
public NonNullThing() {
// etc
}
}

在我看来,这听起来像是初级到中级开发人员在某些时候往往面临的一个相当常见的问题:他们要么不知道或不信任他们参与的合约,并防御性地过度检查空值。此外,在编写自己的代码时,他们倾向于依靠返回空值来指示一些东西,从而要求调用者检查空值。

换句话说,有两个实例会出现null检查:

  1. 其中null是合同条款中的有效响应;和

  2. 这不是一个有效的响应。

(2)很简单。从Java1.7开始,您可以使用#0。(如果您坚持使用以前的版本,那么#1离子可能是一个不错的选择。)

此方法的“正确”用法如下所示。该方法返回传递给它的对象,如果对象为空,则抛出NullPointerException。这意味着返回值始终为非空。该方法主要用于验证参数。

public Foo(Bar bar) {this.bar = Objects.requireNonNull(bar);}

它也可以像assertion一样使用,因为如果对象为空,它会抛出异常。在这两种使用中,都可以添加一条消息,该消息将显示在异常中。下面将它用作断言并提供消息。

Objects.requireNonNull(someobject, "if someobject is null then something is wrong");someobject.doCalc();

一般来说,当一个值为空但不应该为空时,抛出像NullPointerException这样的特定异常有利于抛出像AssertionError这样的更一般的异常。这是Java库采用的方法;当参数不允许为空时,有利于NullPointerException而不是IllegalArgumentException

(1)有点难。如果您无法控制正在调用的代码,那么您就卡住了。如果null是有效的响应,您必须检查它。

但是,如果它是您确实控制的代码(通常是这种情况),那么情况就不同了。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是空值。

对于非集合,这可能更难。将此视为一个示例:如果您有这些接口:

public interface Action {void doSomething();}
public interface Parser {Action findAction(String userInput);}

其中Parser获取原始用户输入并找到要做的事情,也许如果您正在为某事实现命令行界面。现在,如果没有适当的操作,您可能会使它返回的合约为空。这导致了您所说的空值检查。

另一种解决方案是永远不返回null,而是使用空对象模式

public class MyParser implements Parser {private static Action DO_NOTHING = new Action() {public void doSomething() { /* do nothing */ }};
public Action findAction(String userInput) {// ...if ( /* we can't find any actions */ ) {return DO_NOTHING;}}}

比较:

Parser parser = ParserFactory.getParser();if (parser == null) {// now what?// this would be an example of where null isn't (or shouldn't be) a valid response}Action action = parser.findAction(someInput);if (action == null) {// do nothing} else {action.doSomething();}

ParserFactory.getParser().findAction(someInput).doSomething();

这是一个更好的设计,因为它会导致更简洁的代码。

也就是说,也许findAction()方法抛出带有有意义错误消息的Exception是完全合适的——尤其是在您依赖用户输入的情况下。findAction方法抛出Exception比调用方法在没有解释的情况下用简单的NullPointerException爆炸要好得多。

try {ParserFactory.getParser().findAction(someInput).doSomething();} catch(ActionNotFoundException anfe) {userConsole.err(anfe.getMessage());}

或者,如果您认为try/catch机制太丑,而不是什么都不做,您的默认操作应该向用户提供反馈。

public Action findAction(final String userInput) {/* Code to return requested Action if found */return new Action() {public void doSomething() {userConsole.err("Action not found: " + userInput);}}}

只是在这种情况下-

在调用equals方法之前不检查变量是否为空(下面的字符串比较示例):

if ( foo.equals("bar") ) {// ...}

如果foo不存在,将导致NullPointerException

如果你像这样比较你的String,你可以避免这种情况:

if ( "bar".equals(foo) ) {// ...}

我试过NullObjectPattern,但对我来说并不总是最好的方法。有时“不采取行动”是不合适的。

NullPointerException运行时异常,这意味着它是开发人员的错误,并且有足够的经验,它会告诉您错误的确切位置。

现在来回答:

尽量使您的所有属性及其访问器尽可能私有,或者根本避免将它们暴露给客户端。当然,您可以在构造函数中拥有参数值,但通过缩小范围,您不会让客户端类传递无效值。如果您需要修改值,您可以始终创建一个新的object。您只检查构造函数中的值一次,在其余方法中,您几乎可以确定值不为空。

当然,经验是理解和应用这个建议的更好方法。

字节!

Google集合框架提供了一种很好的优雅方式来实现空检查。

库类中有一个方法是这样的:

static <T> T checkNotNull(T e) {if (e == null) {throw new NullPointerException();}return e;}

用法是(import static):

...void foo(int a, Person p) {if (checkNotNull(p).getAge() > a) {...}else {...}}...

或者在你的例子中:

checkNotNull(someobject).doCalc();

哇,当我们有57种不同的方法来推荐NullObject pattern时,我几乎讨厌添加另一个答案,但我认为一些对这个问题感兴趣的人可能会想知道,Java7的桌面上有一个提案,可以添加"零安全处理"——一个用于if-nn-eque-null逻辑的简化语法。

Alex Miller给出的例子是这样的:

public String getPostcode(Person person) {return person?.getAddress()?.getPostcode();}

?.表示只有在左侧标识符不为空时才取消引用,否则将表达式的其余部分评估为null。有些人,如JavaPosse成员Dick Wall和Devoxx的选民真的很喜欢这个提议,但也有反对意见,理由是它实际上会鼓励更多地使用null作为哨兵值。


更新时间:Java7中空安全运算符的正式提案已在项目硬币。下提交语法与上面的示例略有不同,但它是相同的概念。


更新时间:空安全运算符提案没有进入Project Coin。因此,您不会在Java7中看到此语法。

最终,完全解决这个问题的唯一方法是使用不同的编程语言:

  • 在Objective-C中,您可以执行相当于在nil上调用方法的操作,绝对不会发生任何事情。这使得大多数空检查变得不必要,但它会使错误更难诊断。
  • 在Java派生语言不错中,所有类型都有两个版本:潜在空版本和非空版本。您只能调用非空类型的方法。潜在空类型可以通过显式检查null转换为非空类型。这使得更容易知道哪些地方需要空值检查,哪些地方不需要空值检查。

如果不允许未定义的值:

您可以配置您的IDE以警告您潜在的空取消引用。例如。在Eclipse中,请参阅首选项>Java>编译器>错误/警告/空分析

如果允许未定义的值:

如果您想定义一个新的API,其中未定义的值是有意义的,使用选项模式(可能从函数式语言中熟悉)。它有以下优点:

  • 在API中明确说明输入或输出是否存在。
  • 编译器强制您处理“未定义”的情况。
  • 期权是单子,所以不需要冗长的空值检查,只需使用map/foreach/getOrElse或类似的组合器即可安全地使用值(示例)

Java8有一个内置的#0类(推荐);对于早期版本,有库的替代品,例如番石榴#0Java函数类型#2。但是像许多函数式模式一样,在Java(甚至8)中使用Option会导致相当多的样板文件,你可以使用不那么冗长的JVM语言来减少这些样板文件,例如Scala或X的。

如果您必须处理可能返回空值的API,您无法在Java中做太多事情。Xitter和Groovy有埃尔维斯算子?:零安全解引用算子?.,但请注意,这在null引用的情况下返回null,因此它只是“推迟”对null的正确处理。

如果您使用(或计划使用)像JetBrains IntelliJ IDEAEclipseNetBeans这样的JavaIDE或像findbugs这样的工具,那么您可以使用注释来解决这个问题。

基本上,你有@Nullable@NotNull

您可以在方法和参数中使用,如下所示:

@NotNull public static String helloWorld() {return "Hello World";}

@Nullable public static String helloWorld() {return "Hello World";}

第二个示例不会编译(在IntelliJ IDEA中)。

当你在另一段代码中使用第一个helloWorld()函数时:

public static void main(String[] args){String result = helloWorld();if(result != null) {System.out.println(result);}}

现在IntelliJ IDEA编译器会告诉您检查是无用的,因为helloWorld()函数永远不会返回null

使用参数

void someMethod(@NotNull someParameter) { }

如果你写这样的东西:

someMethod(null);

这不会编译。

使用@Nullable的最后一个示例

@Nullable iWantToDestroyEverything() { return null; }

做这个

iWantToDestroyEverything().something();

你可以肯定这不会发生:)

这是一个很好的方法,可以让编译器检查比平时更多的东西,并强制你的契约更强大。不幸的是,并非所有编译器都支持它。

在IntelliJ IDEA 10.5及更高版本中,他们添加了对任何其他@Nullable@NotNull实现的支持。

见博客文章更灵活和可配置@Nullable/@NotNull注释

对于实用程序类,您可以检查参数是否为空。

在所有其他情况下,您可能不必这样做。尽可能使用封装,从而减少您想检查null的地方。

除了使用assert,您还可以使用以下内容:

if (someobject == null) {// Handle null here then move on.}

这比:

if (someobject != null) {..........


.....}
public static <T> T ifNull(T toCheck, T ifNull) {if (toCheck == null) {return ifNull;}return toCheck;}

另一个建议是进行防御性编程-您的类/函数提供已知且安全的默认值,并且为真正的错误/异常保留null。

例如,不要让返回String的函数在出现问题时返回null(例如将数字转换为字符串),而是让它们返回一个空String (""). 在继续之前您仍然需要测试返回值,但不会有异常的特殊情况。这种编程风格的另一个好处是您的程序将能够区分正常操作和异常并做出相应的响应。

您可以使用搜索结果。他们还有一个Eclipse插件),可以帮助您找到重复的空检查(以及其他),但请记住,有时您应该选择防御性编程。还有Java合同可能会有所帮助。

Java的“问题”。

首先,我对此的看法:

我认为当传递NULL时“吃”一些东西是不好的,因为NULL不是一个有效的值。如果你没有带着某种错误退出方法,那么这意味着你的方法没有出错,这不是真的。那么在这种情况下,你可能会返回null,在接收方法中,你再次检查null,它永远不会结束,你最终会得到“if!=null”等。

因此,恕我直言,null必须是阻止进一步执行的关键错误(即null不是有效期)。

我解决这个问题的方法是这样的:

首先,我遵循这个惯例:

  1. 所有公共方法/API始终检查其参数是否为空
  2. 所有私有方法都不检查null,因为它们是受控方法(如果上面没有处理,只需让die带有null指针异常)
  3. 唯一不检查null的其他方法是实用方法。它们是公共的,但如果你出于某种原因调用它们,你知道你传递了什么参数。这就像试图在不提供水的情况下在水壶里烧水…

最后,在代码中,public方法的第一行是这样的:

ValidationUtils.getNullValidator().addParam(plans, "plans").addParam(persons, "persons").validate();

请注意addParam()返回self,以便您可以添加更多参数来检查。

如果任何参数为空,方法validate()将抛出已检查的ValidationException(已检查或未检查更多的是设计/品味问题,但我的ValidationException已检查)。

void validate() throws ValidationException;

例如,如果“计划”为空,则消息将包含以下文本:

"参数[计划]遇到非法参数值null"

如您所见,addParam()方法(string)中的第二个值是用户消息所需要的,因为您无法轻松检测传入的变量名,即使使用反射(无论如何不是本文的主题…)。

是的,我们知道超出这一行,我们将不再遇到空值,因此我们只需安全地调用这些对象的方法。

这样,代码干净、易于维护和可读。

我是“快速失败”代码的粉丝。问问你自己——在参数为空的情况下,你是否做了一些有用的事情?如果你没有明确的答案来回答你的代码在这种情况下应该做什么……即—它一开始就不应该为空,那么忽略它,允许抛出NullPointerException。调用代码将与IllegalArgumentException一样有意义,但如果抛出NPE,开发人员更容易调试和理解哪里出错了,而不是你的代码试图执行其他一些意想不到的应急逻辑——这最终会导致应用程序失败。

我喜欢Nat Pryce的文章。以下是链接:

在文章中还有一个链接到一个Git存储库的Java也许类型,我觉得很有趣,但我不认为它单独可以减少检查代码膨胀。在互联网上做了一些研究后,我认为!=null代码膨胀主要可以通过精心设计来减少。

还有一个选择:

以下简单的函数有助于隐藏null检查(我不知道为什么,但我没有在同一个常见库中找到它):

public static <T> boolean isNull(T argument) {return (argument == null);}

你现在可以写

if (!isNull(someobject)) {someobject.doCalc();}

这是IMO表达!= null的更好方式。

Null不是一个“问题”。它是完成建模工具集中不可或缺的一部分。软件旨在对世界的复杂性进行建模,而null承担了它的负担。Null表示“无数据”或“未知”在Java等。因此,将null用于这些目的是合适的。我不喜欢“Null对象”模式;我认为它会引起“谁将保护监护人的问题。
如果你问我女朋友的名字,我会告诉你我没有女朋友。在Java语言中,我会返回null。另一种选择是抛出有意义的异常来指示一些不能(或不想)在那里解决的问题,并将其委托给堆栈中更高的某个地方以重试或向用户报告数据访问错误。

  1. 对于“未知的问题”,给出“未知的答案”。(从业务角度来看,这是正确的,是空安全的)在使用之前在方法中检查一次空参数可以减轻多个调用者在调用前检查它们。

    public Photo getPhotoOfThePerson(Person person) {if (person == null)return null;// Grabbing some resources or intensive calculation// using person object anyhow.}

    以前导致正常的逻辑流程,从我的照片库中没有不存在的女朋友的照片。

    getPhotoOfThePerson(me.getGirlfriend())

    它符合新的JavaAPI(期待)

    getPhotoByName(me.getGirlfriend()?.getName())

    虽然它是相当“正常的业务流程”没有找到照片存储到数据库中的一些人,我用来对像下面的一些其他情况

    public static MyEnum parseMyEnum(String value); // throws IllegalArgumentExceptionpublic static MyEnum parseMyEnumOrNull(String value);

    不要讨厌键入<alt> + <shift> + <j>(在Eclipse中生成javadoc)并为您的公共API编写三个额外的单词。除了那些不读留档的人之外,这对所有人来说都绰绰有余。

    /*** @return photo or null*/

    /*** @return photo, never null*/
  2. This is rather theoretical case and in most cases you should prefer java null safe API (in case it will be released in another 10 years), but NullPointerException is subclass of an Exception. Thus it is a form of Throwable that indicates conditions that a reasonable application might want to catch (javadoc)! To use the first most advantage of exceptions and separate error-handling code from 'regular' code (according to creators of Java) it is appropriate, as for me, to catch NullPointerException.

    public Photo getGirlfriendPhoto() {try {return appContext.getPhotoDataSource().getPhotoByName(me.getGirlfriend().getName());} catch (NullPointerException e) {return null;}}

    可能会出现以下问题:

    Q.如果getPhotoDataSource()返回null怎么办?
    答:这取决于业务逻辑。如果我找不到相册,我会给你看没有照片。如果appContext未初始化怎么办?这个方法的业务逻辑忍受了这个。如果相同的逻辑应该更严格,然后抛出异常,它是业务逻辑的一部分,应该使用显式检查null(案例3)。新的Java空安全API在这里更适合选择性地指定什么意味着什么,什么不意味着要初始化在程序员错误的情况下是快速失败的。

    Q.可以执行冗余代码并获取不必要的资源。
    A.如果getPhotoByName()尝试打开数据库连接,创建PreparedStatement并最终使用人名作为SQL参数,就可能发生这种情况。方法对于一个未知的问题给出一个未知的答案(案例1)在这里工作。在获取资源之前,该方法应该检查参数并在需要时返回未知结果。

    Q.由于try闭包打开,这种方法有性能损失。
    答:首先,软件应该易于理解和修改。只有在这之后,人们才能考虑性能,并且只有在需要的时候!在需要的地方!(来源)和许多其他)。

    PS。这种方法的使用将与将错误处理代码与“常规”代码分开原则在某些地方的合理使用一样合理。考虑下一个例子:

    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {try {Result1 result1 = performSomeCalculation(predicate);Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());Result3 result3 = performThirdCalculation(result2.getSomeProperty());Result4 result4 = performLastCalculation(result3.getSomeProperty());return result4.getSomeProperty();} catch (NullPointerException e) {return null;}}
    public SomeValue calculateSomeValueUsingSophisticatedLogic(Predicate predicate) {SomeValue result = null;if (predicate != null) {Result1 result1 = performSomeCalculation(predicate);if (result1 != null && result1.getSomeProperty() != null) {Result2 result2 = performSomeOtherCalculation(result1.getSomeProperty());if (result2 != null && result2.getSomeProperty() != null) {Result3 result3 = performThirdCalculation(result2.getSomeProperty());if (result3 != null && result3.getSomeProperty() != null) {Result4 result4 = performLastCalculation(result3.getSomeProperty());if (result4 != null) {result = result4.getSomeProperty();}}}}}return result;}

    PPS。对于那些快速否决(并且不那么快阅读留档)的人,我想说我一生中从未遇到过空指针异常(NPE)。但这种可能性是Java创作者的故意设计,因为NPE是Exception的子类。我们在Java历史上有一个先例,当ThreadDeathError时,不是因为它实际上是一个应用程序错误,而是仅仅因为它不是故意被捕获的!多少NPE适合成为Error而不是ThreadDeath!但事实并非如此。

  3. 仅当业务逻辑暗示“无数据”时才检查它。

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {if (personId == null)return;DataSource dataSource = appContext.getStuffDataSource();Person person = dataSource.getPersonById(personId);if (person != null) {person.setPhoneNumber(phoneNumber);dataSource.updatePerson(person);} else {Person = new Person(personId);person.setPhoneNumber(phoneNumber);dataSource.insertPerson(person);}}

    public void updatePersonPhoneNumber(Long personId, String phoneNumber) {if (personId == null)return;DataSource dataSource = appContext.getStuffDataSource();Person person = dataSource.getPersonById(personId);if (person == null)throw new SomeReasonableUserException("What are you thinking about ???");person.setPhoneNumber(phoneNumber);dataSource.updatePerson(person);}

    如果appContext或dataSource未初始化未处理的运行时NullPointerException将杀死当前线程并由Thread.defaultUncaughtHandler处理(供您定义和使用您喜爱的记录器或其他通知机制)。如果未设置,线程组#un捕获异常将打印堆栈跟踪到系统错误。应该监控应用程序错误日志并为每个未处理的异常打开Jira问题,实际上是应用程序错误。程序员应该在初始化的某个地方修复bug。

Guava,Google的一个非常有用的核心库,有一个很好且有用的API来避免空值。我发现使用和避免Null解释非常有帮助。

正如wiki中解释的那样:

Optional<T>是一种将可为空的T引用替换为非空值。可选可以包含非空T引用(在这种情况下,我们说引用是“存在的”),或者它可能包含没有(在这种情况下,我们说引用是“缺席”)。它永远不会表示“包含null”。

用法:

Optional<Integer> possible = Optional.of(5);possible.isPresent(); // returns truepossible.get(); // returns 5

您还可以使用Checker Framework(与JDK 7及更高版本一起)静态检查空值。这可能会解决很多问题,但需要运行一个额外的工具,该工具目前仅适用于OpenJDK AFAIK。https://checkerframework.org/

好吧,我现在已经在技术上回答了一百万次,但我必须这么说,因为这是与Java程序员的无休止的讨论。

很抱歉,我不同意以上所有的观点。我们必须在Java中测试null的原因是Java程序员不知道如何处理内存。

我这样说是因为我在C++编程方面有很长的经验,我们不这样做。换句话说,你不需要这样做。请注意,在Java,如果你击中一个悬空指针,你会得到一个正常的异常;在C++这个异常通常不会被捕获并终止程序。

不想这样做?然后遵循C/C++的一些简单规则。

不要那么容易地实例化事物,认为,每个“新”都会给你带来很多麻烦,并遵循这些简单的规则。

一个类只能以3种方式访问内存->

  1. 它可以“拥有”类成员,他们将遵循以下规则:

    1. 所有“HAS”成员都在构造函数中创建为“new”。
    2. 您将关闭 /de析构函数中分配或等效的关闭()函数在Java为同一类和没有其他。

这意味着你需要记住(就像Java一样)谁是每个资源的所有者或父级,并尊重这种所有权。一个对象只被创建它的类删除。还有->

  1. 一些成员将被“使用”但不拥有或“拥有”。这是另一个类中的“OWN”,并作为参数传递给构造函数。由于这些成员归另一个类所有,我们永远不会删除或关闭它,只有父类可以。

  2. 类中的方法也可以实例化本地对象以供内部使用,这些对象永远不会传递到类的一侧,或者它们应该是普通的“有”对象。

最后,为了使所有这些工作,您需要有一个纪律严明的设计,其中包含层次结构形式的类,并且没有循环。

在这种设计下,遵循上述规则,层次结构设计中的子类不可能访问被销毁的指针,因为这意味着父类在子类之前被销毁,分层非循环设计不允许它。

最后,还要记住,在启动系统时,您应该从上到下构建层次结构并从下到上销毁。任何地方都不会有空指针,或者有人违反规则。

Java7有一个新的java.util.Objects实用程序类,上面有一个requireNonNull()方法。如果参数为空,则抛出NullPointerException,但它会稍微清理代码。示例:

Objects.requireNonNull(someObject);someObject.doCalc();

该方法在构造函数中赋值之前对检查最有用,每次使用它可以节省三行代码:

Parent(Child child) {if (child == null) {throw new NullPointerException("child");}this.child = child;}

成为

Parent(Child child) {this.child = Objects.requireNonNull(child, "child");}

我更喜欢这个

public void simpleFunc(SomeObject someObject){someObject = someObject != null ? someObject : new SomeObject(null);someObject.doSomething();}

当然,在我的例子中,某些对象优雅地处理一个空参数。例如记录这样的事件,什么也不做。

只是永远不要使用null。不要允许它。

在我的类中,大多数字段和局部变量都有非空的默认值,我在代码中的任何地方都添加了契约语句(总是开启的断言),以确保这是强制执行的(因为它更简洁,比让它作为NPE出现,然后必须解析行号等更具表现力)。

一旦我采用这种做法,我注意到问题似乎可以自行修复。你会在开发过程的更早阶段偶然发现问题,并意识到你有一个弱点…更重要的是…它有助于封装不同模块的关注点,不同模块可以相互“信任”,并且不再用if = null else构造乱扔代码!

这是防御性编程,从长远来看会产生更干净的代码。始终对数据进行消毒,例如在这里通过执行严格的标准,问题就会消失。

class C {private final MyType mustBeSet;public C(MyType mything) {mustBeSet=Contract.notNull(mything);}private String name = "<unknown>";public void setName(String s) {name = Contract.notNull(s);}}

class Contract {public static <T> T notNull(T t) { if (t == null) { throw new ContractException("argument must be non-null"); return t; }}

合同就像总是在运行的迷你单元测试,即使在生产中,当事情失败时,你知道为什么,而不是你必须以某种方式找出的随机NPE。

您可以在方法调用之前使用拦截器。这就是面向方面编程关注的。

假设M1(对象测试)是一个方法,M2是一个方法,我们在方法调用之前应用一个方面,M2(Object test2)。如果test2 != null然后调用M1,否则做另一件事。它适用于所有你想应用方面的方法。如果你想为实例字段和构造函数应用方面,你可以使用应用程序Spring也可以是方法方面的最佳选择。

首先,我们不能真正删除所有空条件。我们可以使用@NotNull@Nullable注释(如前所述)来减少它们。但这需要一些框架的支持。这是OVal可以提供帮助的地方。

基本思想是对象/参数/构造函数应该始终满足先决条件。你可以有很多先决条件,例如NullableNotNull,OVal会注意对象在调用时应该处于一致状态。

我猜OVal在内部使用Airi J来验证先决条件。

@Guardedpublic class BusinessObject{public BusinessObject(@NotNull String name){this.name = name;}
...}

例如,

// Throws a ConstraintsViolatedException because parameter name is nullBusinessObject bo = new BusinessObject(null);

Java8带来了新的java.util.Optional类,可以说它解决了一些问题。至少可以说它提高了代码的易读性,并且在公共API的情况下,使API的合同对客户端开发人员更清晰。

他们是这样工作的:

创建给定类型(Fruit)的可选对象作为方法的返回类型。它可以为空或包含Fruit对象:

public static Optional<Fruit> find(String name, List<Fruit> fruits) {for (Fruit fruit : fruits) {if (fruit.getName().equals(name)) {return Optional.of(fruit);}}return Optional.empty();}

现在看看这段代码,我们在其中为给定的Fruit实例搜索Fruitfruits)的列表:

Optional<Fruit> found = find("lemon", fruits);if (found.isPresent()) {Fruit fruit = found.get();String name = fruit.getName();}

您可以使用map()运算符对可选对象执行计算或从中提取值。orElse()允许您为缺失值提供回退。

String nameOrNull = find("lemon", fruits).map(f -> f.getName()).orElse("empty-name");

当然,空值/空值的检查仍然是必要的,但至少开发人员意识到该值可能为空,忘记检查的风险是有限的。

在使用Optional从头开始构建的API中,只要返回值可能为空,并且仅在不能为null时返回普通对象(约定),客户端代码可能会放弃对简单对象返回值的空检查…

当然,Optional也可以用作方法参数,在某些情况下,可能比5或10个重载方法更好地指示可选参数。

Optional提供了其他方便的方法,例如orElse允许使用默认值,ifPresentlambda表达式一起使用。

我邀请您阅读这篇文章(我写这个答案的主要来源),其中很好地解释了NullPointerException(和一般的空指针)有问题以及Optional带来的(部分)解决方案:Java可选对象

  1. 永远不要将变量初始化为null。
  2. 如果(1)不可能,则将所有集合和数组初始化为空集合/数组。

在您自己的代码中执行此操作,您可以避免!=空检查。

大多数时候,空检查似乎是保护集合或数组上的循环,所以只需将它们初始化为空,您就不需要任何空检查。

// BadArrayList<String> lemmings;String[] names;
void checkLemmings() {if (lemmings != null) for(lemming: lemmings) {// do something}}


// GoodArrayList<String> lemmings = new ArrayList<String>();String[] names = {};
void checkLemmings() {for(lemming: lemmings) {// do something}}

这有一个很小的开销,但对于更干净的代码和更少的NullPointerExceptions来说是值得的。

我们一直在使用Apache库(Apache Commons)来解决这个问题。

ObjectUtils.equals(object, null)

CollectionUtils.isEmpty(myCollection);

StringUtils.isEmpty("string");

我喜欢之前的答案,作为一种实践,为集合提供初始默认值或空集,以最小化需求。

这些可以是简单的用法,可以防止您拥有NullPointerException或使用空集合。这并不能回答如何处理空对象的问题,但这些为对象或集合的基本验证提供了一些检查。

希望这有帮助。

避免不必要的null-checks的方法很简单:

You need to know which variables can be null, and which cannot, and you need to be confident about which category a given variable fall into.

但是,尽管它可以简单地表达,但实现它更难。关键在于confident部分,因为您如何确定变量不能为空?

对此没有快速解决方案,简单的答案,但这里有一些提示:

  1. 干净的代码。想要推理一段代码的行为,最重要的一点是它的编写方式要易于理解。根据变量所代表的内容命名变量,根据方法命名方法,应用Single responsibility principle(2中的Shttp://en.wikipedia.org/wiki/SOLID_(对象-oriented_design)对于凌乱的代码,试图理解一个方法做了什么可能会让你忘记你为什么要阅读这个方法。(提示:阅读Robert C. Martin的“干净的代码”)

  2. 避免返回null值。如果null值会使你的程序无法正常运行,请抛出exception(确保添加适当的错误处理)。返回null值可能是可以接受的情况,例如试图从数据库中获取一个对象。在这些情况下,编写处理null值的代码,并在你的耳朵后面记下,这里我们有一些可能返回null的东西。处理返回的null值尽可能接近返回null的方法的调用者(不要只是盲目地将其传回调用链)。

  3. 永远不要将显式null值作为参数传递(至少不要跨类传递)。如果您曾经处于传递null参数是唯一选择的位置,那么创建一个没有此参数的新方法就是要走的路。

  4. 验证您的输入!确定您的应用程序的“入口点”。它们可以是Web服务、REST服务、远程EJB类、控制器等。对于这些入口点中的每个方法,问问自己:“如果此参数为空,此方法是否会正确执行?”如果答案是否定的,请添加Validate.notNull(someParam, "Can't function when someParam is null!");。如果缺少所需参数,这将抛出IllegalArgumentException。在入口点中进行这种类型的验证的好处是,您可以轻松地在从入口点执行的代码中假设此变量永远不会为空!此外,如果这失败了,在入口点,调试比你在代码深处得到NullPointerException要容易得多,因为这样的失败只能意味着一件事:客户端没有向你发送所有必需的信息。在大多数情况下,你想验证所有输入参数,如果你发现自己处于需要允许大量null值的位置,这可能是设计糟糕的接口的迹象,需要重构/添加来满足客户端的需求。

  5. 使用Collection时,返回一个空而不是null!

  6. 使用数据库时,请使用not null约束。通过这种方式,您将知道从数据库读取的值不能为空,并且您不必检查它。

  7. 构建你的代码并坚持使用它。这样做可以让你对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,那么你可以假设这些值永远不会为空。

  8. 如果你还没有这样做,那就为你的代码编写自动化测试。通过编写测试,你将对你的代码进行推理,你也将变得更加自信,它做了它应该做的事情。此外,自动化测试可以保护你在重构过程中不犯错误,让你立即知道这段代码没有像以前那样做。

当然,你仍然需要空值检查,但它可以减少到最低限度(即知道你可能会得到一个空值的情况,而不是到处都是,只是为了确定。)当谈到空值检查时,我实际上更喜欢使用三元运算符(但要小心使用,当你开始嵌套它们时,它们会变得非常混乱。

public String nullSafeToString(final Object o) {return o != null ? o.toString() : "null";}

我可以更笼统地回答它吗?

当方法以我们意想不到的方式获取参数时,我们通常会遇到这个问题(错误的方法调用是程序员的错)。例如:你希望得到一个对象,而不是一个空。你希望得到一个至少有一个字符的字符串,而不是一个空的字符串…

所以没有区别:

if(object == null){//you called my method badly!

}

if(str.length() == 0){//you called my method badly again!}

在我们执行任何其他函数之前,他们都希望确保我们收到了有效的参数。

正如在其他一些答案中提到的,为了避免上述问题,您可以遵循按合同设计模式。请参阅http://en.wikipedia.org/wiki/Design_by_contract

要在java中实现此模式,您可以使用核心java注释(如javax.annotation.NotNull)或使用更复杂的库(如Hibernate Validator)。

只是一个样本:

getCustomerAccounts(@NotEmpty String customerId,@Size(min = 1) String accountType)

现在您可以安全地开发方法的核心函数,而无需检查输入参数,它们可以保护您的方法免受意外参数的影响。

您可以更进一步,确保在您的应用程序中只能创建有效的pojos。(来自hibernate验证器站点的示例)

public class Car {
@NotNullprivate String manufacturer;
@NotNull@Size(min = 2, max = 14)private String licensePlate;
@Min(2)private int seatCount;
// ...}

我发现Guava先决条件在这种情况下非常有用。我不喜欢将空值留给空指针异常,因为理解NPE的唯一方法是定位行号。生产版本和开发版本的行号可能不同。

使用Guava先决条件,我可以检查空参数并在一行中定义有意义的异常消息。

例如,

Preconditions.checkNotNull(paramVal, "Method foo received null paramVal");

这是大多数开发人员最常见的错误。

我们有很多方法来处理这个问题。

办法1:

org.apache.commons.lang.Validate //using apache framework

NotNull(对象对象,字符串消息)

办法2:

if(someObject!=null){ // simply checking against null}

办法3:

@isNull @Nullable  // using annotation based validation

办法4:

// by writing static method and calling it across whereever we needed to check the validation
static <T> T isNull(someObject e){if(e == null){throw new NullPointerException();}return e;}

我完全无视那些建议在任何情况下都使用空对象的答案。这种模式可能会破坏契约,将问题埋得越来越深,而不是解决它们,更不用说使用不当会创建另一堆需要未来维护的样板代码。

实际上,如果从方法返回的内容可以为null,并且调用代码必须对此做出决定,则应该有一个较早的调用来确保状态。

还要记住,如果不小心使用,空对象模式将消耗内存。为此-NullObject的实例应该在所有者之间共享,而不是每个所有者的唯一实例。

此外,我不建议在类型旨在成为原始类型表示的情况下使用这种模式——就像数学实体一样,不是标量:向量、矩阵、复数和POD(普通旧数据)对象,它们旨在以Java内置类型的形式保存状态。在后一种情况下,你最终会调用具有任意结果的getter方法。例如,NullPerson.getName()方法应该返回什么?

为了避免荒谬的结果,这种情况值得考虑。

这对每个Java开发人员来说都是一个非常常见的问题。因此,Java8中提供了官方支持,可以在没有混乱代码的情况下解决这些问题。

Java8引入了java.util.Optional<T>。它是一个可能包含也可能不包含非空值的容器。Java8给出了一种更安全的方法来处理在某些情况下值可能为空的对象。它的灵感来自haskellscala的想法。

简而言之,可选类包含显式处理值存在或不存在的情况的方法。然而,与空引用相比的优势在于,可选类强制您在值不存在时考虑情况。因此,您可以防止意外的空指针异常。

在上面的例子中,我们有一个家庭服务工厂,它返回家庭中可用的多个设备的句柄。但是这些服务可能可用/不可用;这意味着它可能会导致NullPointerException。不要在使用任何服务之前添加nullif条件,而是让我们将其包装到可选<服务>。

包装到选项

让我们考虑一种从工厂获取服务引用的方法。不是返回服务引用,而是将其包装为可选。它让API用户知道返回的服务可能可用,也可能不可用/正常,防御性地使用

public Optional<Service> getRefrigertorControl() {Service s = new  RefrigeratorService();//...return Optional.ofNullable(s);}

正如您所看到的,Optional.ofNullable()提供了一种简单的方法来包装引用。还有另一种方法可以获得可选的引用,Optional.empty()Optional.of()。一种用于返回空对象而不是重新调整null,另一种用于包装不可为空的对象,分别。

那么它对避免空检查有多大帮助呢?

包装引用对象后,可选提供了许多有用的方法来在没有NPE的包装引用上调用方法。

Optional ref = homeServices.getRefrigertorControl();ref.ifPresent(HomeServices::switchItOn);

Optional.ifPresent调用给定的消费者,如果它是一个非空值。否则,它什么也不做。

@FunctionalInterfacepublic interface Consumer<T>

表示接受单个输入参数且不返回任何结果的操作。与大多数其他功能接口不同,消费者需要通过副作用进行操作。它非常干净且易于理解。在上面的代码示例中,如果可选的保持引用非空,则会调用HomeService.switchOn(Service)

我们经常使用三元运算符来检查空值条件并返回一个替代值或默认值。可选提供了另一种在不检查空值的情况下处理相同条件的方法。Optional.orElse(defaultObj)返回defaultObj,如果可选值为空值。让我们在示例代码中使用它:

public static Optional<HomeServices> get() {service = Optional.of(service.orElse(new HomeServices()));return service;}

现在HomeServices.get()做同样的事情,但方式更好。它检查服务是否已经初始化not。如果是,则返回相同的服务或创建一个新的New服务。可选的. orElse(T)有助于返回默认值。

最后,这是我们的NPE以及null无检查代码:

import java.util.Optional;public class HomeServices {private static final int NOW = 0;private static Optional<HomeServices> service;
public static Optional<HomeServices> get() {service = Optional.of(service.orElse(new HomeServices()));return service;}
public Optional<Service> getRefrigertorControl() {Service s = new  RefrigeratorService();//...return Optional.ofNullable(s);}
public static void main(String[] args) {/* Get Home Services handle */Optional<HomeServices> homeServices = HomeServices.get();if(homeServices != null) {Optional<Service> refrigertorControl = homeServices.get().getRefrigertorControl();refrigertorControl.ifPresent(HomeServices::switchItOn);}}
public static void switchItOn(Service s){//...}}

完整的帖子是NPE以及Null免检代码…真的吗?

您可以使用像JUnit这样的框架将类与单元测试结合起来。这样你的代码将是干净的(没有无用的检查),你将确保你的实例不会为空。

这是使用单元测试的一个很好的理由。

可以用Java8个lambda定义处理嵌套空检查的util方法。

void example() {Entry entry = new Entry();// This is the same as H-MANs solutionPerson person = getNullsafe(entry, e -> e.getPerson());// Get object in several stepsString givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName());// Call void methodsdoNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt());}
/** Return result of call to f1 with o1 if it is non-null, otherwise return null. */public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) {if (o1 != null) return f1.apply(o1);return null;}
public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) {return getNullsafe(getNullsafe(o0, f1), f2);}
public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) {return getNullsafe(getNullsafe(o0, f1, f2), f3);}

/** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) {if (o1 != null) f1.accept(o1);}
public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) {doNullsafe(getNullsafe(o0, f1), f2);}
public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) {doNullsafe(getNullsafe(o0, f1, f2), f3);}

class Entry {Person getPerson() { return null; }}
class Person {Name getName() { return null; }}
class Name {void nameIt() {}String getGivenName() { return null; }}

(这个答案最初发布在这里

Java8现在有一个可选类,它考虑包装对象,如果存在一个值,isPresent()将返回true,get()将返回该值。

我遵循以下指南以避免空检查。

  1. 尽可能避免成员变量的惰性初始化。在声明本身中初始化变量。这将处理NullPointerExceptions。

  2. 在循环的早期确定成员变量的可变性。有效地使用final关键字等语言结构。

  3. 如果您知道方法的增强不会更改,请将它们声明为final

  4. 尽可能限制数据的突变。有些变量可以在构造函数中创建,永远无法更改。删除公共setter方法,除非真的需要

    例如。假设应用程序中的一个类(A.java)正在维护一个像HashMap这样的集合。不要在A.java中提供public getter方法,并允许B.java直接在Map中添加一个元素。相反,在A.java中提供一个API,它将元素添加到集合中。

    // Avoida.getMap().put(key,value)
    //recommended
    public void addElement(Object key, Object value){// Have null checks for both key and value here : single placemap.put(key,value);}
  5. And finally, use try{} catch{} finally{} blocks at right places effectively.

对于Java8或更高版本,最好的选择可能是使用#0类。

Optional stringToUse = Optional.of("optional is there");stringToUse.ifPresent(System.out::println);

这对于可能的空值的长链特别方便。示例:

Optional<Integer> i = Optional.ofNullable(wsObject.getFoo()).map(f -> f.getBar()).map(b -> b.getBaz()).map(b -> b.getInt());

关于如何在null上抛出异常的示例:

Optional optionalCarNull = Optional.ofNullable(someNull);optionalCarNull.orElseThrow(IllegalStateException::new);

Java7介绍了#0方法,当需要检查某些内容是否为空时,该方法会很方便。

String lowerVal = Objects.requireNonNull(someVar, "input cannot be null or empty").toLowerCase();

在Java8中,如果本地变量/字段/方法参数/方法返回类型从未赋值null(并且不检查null),则可以使用类型T;如果可以使用null,则可以使用类型Optional<T>。然后使用方法map处理T ->,使用方法flatMap处理T -> Optional<R>

class SomeService {@Injectprivate CompanyDao companyDao;
// return Optional<String>public Optional<String> selectCeoCityByCompanyId0(int companyId) {return companyDao.selectById(companyId).map(Company::getCeo).flatMap(Person::getHomeAddress).flatMap(Address::getCity);}
// return String + default valuepublic String selectCeoCityByCompanyId1(int companyId) {return companyDao.selectById(companyId).map(Company::getCeo).flatMap(Person::getHomeAddress).flatMap(Address::getCity).orElse("UNKNOWN");}
// return String + exceptionpublic String selectCeoCityByCompanyId2(int companyId) throws NoSuchElementException {return companyDao.selectById(companyId).map(Company::getCeo).flatMap(Person::getHomeAddress).flatMap(Address::getCity).orElseThrow(NoSuchElementException::new);}}
interface CompanyDao {// real situation: no company for such id -> use Optional<Company>Optional<Company> selectById(int id);}
class Company {// company always has ceo -> use PersonPerson ceo;public Person getCeo() {return ceo;}}
class Person {// person always has name -> use StringString firstName;// person can be without address -> use Optional<Address>Optional<Address> homeAddress = Optional.empty();
public String getFirstName() {return firstName;}public Optional<Address> getHomeAddress() {return homeAddress;}}
class Address {//  address always contains country -> use StringString country;//  city field is optional -> use Optional<String>Optional<String> city = Optional.empty();
String getCountry() {return country;}Optional<String> getCity() {return city;}}

空对象模式可以用作这个问题的解决方案。为此,应该修改一些Object的类。

public abstract class SomeObject {public abstract boolean isNil();}
public class NullObject extends SomeObject {@Overridepublic boolean isNil() {return true;}}public class RealObject extends SomeObject {@Overridepublic boolean isNil() {return false;}}

现在不用检查,

 if (someobject != null) {someobject.doCalc();}

我们可以使用,

if (!someObject.isNil()) {someobject.doCalc();}

参考:https://www.tutorialspoint.com/design_pattern/null_object_pattern.htm

因为Java 7java.util.Objects存在。

但是从Java 8开始,您可以使用Objects类的Objects.isNull(var)Objects.nonNull(var)方法来进行空指针检查。

例如,

String var1 = null;Date var2 = null;Long var3 = null;
if(Objects.isNull(var1) && Objects.isNull(var2) && Objects.isNull(var3))System.out.println("All Null");else if (Objects.nonNull(var1) && Objects.nonNull(var2) && Objects.nonNull(var3))System.out.println("All Not Null");

如果您使用的是java8或更高版本,请从java.util.Objects中选择isNull(yourObject)

示例:-

String myObject = null;
Objects.isNull(myObject); //will return true

用法:下面的代码返回一个非空值(如果名称不为空,则将返回该值,否则将返回默认值)。

final String name = "Jobin";String nonNullValue = Optional.ofNullable(name).orElse("DefaultName");

Java8在java.util包中引入了一个新的类可选。

Java8可选:

1.不需要空检查。
2.)运行时不再有NullPointerException。
3.)我们可以开发干净整洁的API。

可选-一个容器对象,可能包含也可能不包含非空值。如果存在值,isPresent()将返回true,get()将返回值。

有关更多详细信息,请在此处找到oracle文档:-https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html

总而言之,避免陈述

if (object != null) {....}
  1. 从java 7开始,您可以使用Objects方法:

    Objects.isNull(对象)

    Objects.nonNull(对象)

    Objects.requireNonNull(对象)

    Objects.equals(对象1,对象2)

  2. 从java 8开始,您可以使用可选类(何时使用

object.ifPresent(obj -> ...); java 8

object.ifPresentOrElse(obj -> ..., () -> ...); java 9

  1. 依赖方法契约(JSR 305)并使用查找错误。用注释@javax.annotation.Nullable@javax.annotation.Nonnnul标记您的代码。还提供先决条件。

    Preconditions.checkNotNull(对象);

  2. 在特殊情况下(例如字符串和集合),您可以使用apache-Commons(或Google guava)实用程序方法:

公有静态布尔值为空(CharSequence cs)

//apache StringUtils//静态布尔值为空

//apache MapUtils//公共静态布尔值为空

public静态boolean isNullOr空(@Nullable String string)//Guava Strings

  1. 当您需要在null时分配默认值时,请使用apache公共资源lang

公共静态对象默认值

你有一个选择

  • 在方法上使用检查器框架's@DeepresNonNull。例如,如果您使用空参数调用带有此类注释的方法,则会得到此结果。它将在编译期间失败,甚至在您的代码运行之前!因为在运行时它将是NullPointerException

    @RequiresNonNull(value = { "#1" })static void check( Boolean x) {if (x) System.out.println("true");else System.out.println("false");}
    public static void main(String[] args) {
    
    check(null);
    }

gets

[ERROR] found   : null[ERROR] required: @Initialized @NonNull Boolean[ERROR] -> [Help 1]

还有其他方法,如使用Java8的可选,番石榴注释,空对象模式等,只要你达到避免的目标就没关系!=null

使用Java8,您可以将供应商传递给如下所示的helper方法,

if(CommonUtil.resolve(()-> a.b().c()).isPresent()) {
}

上面取代了下面的锅炉板代码,

if(a!=null && a.b()!=null && a.b().c()!=null) {
}

//CommonUtil.java

 public static <T> Optional<T> resolve(Supplier<T> resolver) {try {T result = resolver.get();return Optional.ofNullable(result);} catch (NullPointerException var2) {return Optional.empty();}}

你可以通过跟随大多数其他问题的答案来避免NullPointerException,我只想添加一些#1中引入的更多方法来优雅地处理这个场景,并展示一些旧的也可以使用,从而减少你的努力。

  1. public static boolean isNull(Object obj)

    如果提供的引用为空,则返回true,否则返回错误。

    Java1.8

  2. public static boolean nonNull(Object obj)

    如果提供的引用非空,则返回true,否则返回错误。

    Java1.8

  3. public static <T> T requireNonNullElse​(T obj, T defaultObj)

    如果第一个参数非空,则返回第一个参数,否则返回非空的第二个参数。

    Java9

  4. public static <T> T requireNonNullElseGet​(T obj, Supplier<? extends T> supplier)

    如果第一个参数非空,则返回该参数,否则返回supplier.get()的非空值。

    Java9

  5. public static <T> T requireNonNull​(T obj, Supplier<String> messageSupplier)

    检查指定的对象引用是否为空,否则将引发自定义的NullPointerException。

    Java1.8

有关上述函数的更多详细信息,请参阅这里

具有空安全性的静态编程语言是优雅的替代方案,但它意味着更大的更改。

函数式方法可能有助于包装重复的空检查并执行匿名代码,如下面的示例。

    BiConsumer<Object, Consumer<Object>> consumeIfPresent  = (s,f) ->{if(s!=null) {f.accept(s);}};
consumeIfPresent.accept(null, (s)-> System.out.println(s) );consumeIfPresent.accept("test", (s)-> System.out.println(s));
BiFunction<Object, Function<Object,Object>,Object> executeIfPresent  = (a,b) ->{if(a!=null) {return b.apply(a);}return null;};executeIfPresent.apply(null, (s)-> {System.out.println(s);return s;} );executeIfPresent.apply("test", (s)-> {System.out.println(s);return s;} );

另一个替代!=null检查是(如果您无法在设计上摆脱它):

Optional.ofNullable(someobject).ifPresent(someobject -> someobject.doCalc());

Optional.ofNullable(someobject).ifPresent(SomeClass::doCalc);

与某个类是某个对象的类型。

但是,您无法从doCalc()获取返回值,因此仅对false方法有用。

Java8在java.util包中引入了一个新的类可选。它用于表示值存在或不存在。这种新构造的主要优点是不再有太多的空检查和NullPointerException。它避免了任何运行时NullPointerExceptions,并支持我们开发干净整洁的JavaAPI或应用程序。像Collectionsarrays一样,它也是一个最多容纳一个值的容器。

下面是一些有用的链接你可以遵循

https://www.mkyong.com/java8/java-8-optional-in-depth/

https://dzone.com/articles/java-8-optional-avoid-null-and

您可以为对象和字符串创建一个通用方法,以便您可以在应用程序中使用它-这可以帮助您和您的同事:创建一个类,例如。StringU实用程序并添加方法,例如。getNullString

public static String getNullString(Object someobject){if(null==someobject )return null;
else if(someobject.getClass().isInstance("") &&(((String)someobject).trim().equalsIgnoreCase("null")||((String)someobject).trim().equalsIgnoreCase("")))return null;
else if(someobject.getClass().isInstance(""))return (String)someobject;
elsereturn someobject.toString().trim();}

并简单地将此方法称为,

if (StringUtilities.getNullString(someobject) != null){//Do something}

在Java中避免空检查的最佳方法是正确处理和使用异常。根据我的经验,随着您更接近前端,空检查变得更加常见和必需,因为它更接近可能通过UI提供无效信息的用户(例如,没有值,正在提交字段)。

有人可能会争辩说,您应该能够控制UI正在做什么,以免您忘记大多数UI是通过某种第三方库完成的,例如,对于空白输入框,可能会返回NULL或空字符串,具体取决于情况或库。

你可以像这样把两者结合起来:

try{myvar = get_user_supplied_value();if (myvar == null || myvar.length() == 0) { alert_the_user_somehow(); return; };
process_user_input(myvar);} catch (Exception ex) {handle_exception(ex);}

人们采取的另一种方法是说:

if (myvar && myvar.length() > 0)  { };

你也可以抛出一个异常(这是我喜欢的)

if (myvar == null || myvar.length() == 0) {throw new Exception("You must supply a name!");};

但这取决于你。

有一个很好的方法来检查JDK中的空值。Optional.java有大量的方法来解决这些问题。如:

    /*** Returns an {@code Optional} describing the specified value, if non-null,* otherwise returns an empty {@code Optional}.** @param <T> the class of the value* @param value the possibly-null value to describe* @return an {@code Optional} with a present value if the specified value* is non-null, otherwise an empty {@code Optional}*/public static <T> Optional<T> ofNullable(T value) {return value == null ? empty() : of(value);}
    /*** Return {@code true} if there is a value present, otherwise {@code false}.** @return {@code true} if there is a value present, otherwise {@code false}*/public boolean isPresent() {return value != null;}
    /*** If a value is present, invoke the specified consumer with the value,* otherwise do nothing.** @param consumer block to be executed if a value is present* @throws NullPointerException if value is present and {@code consumer} is* null*/public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}

帮助JAVER真的,真的很有用。

public class Null {
public static void main(String[] args) {String str1 = null;String str2 = "";
if(isNullOrEmpty(str1))System.out.println("First string is null or empty.");elseSystem.out.println("First string is not null or empty.");
if(isNullOrEmpty(str2))System.out.println("Second string is null or empty.");elseSystem.out.println("Second string is not null or empty.");}
public static boolean isNullOrEmpty(String str) {if(str != null && !str.isEmpty())return false;return true;}}

产出

str1 is null or empty.str2 is null or empty.

在上面的程序中,我们有两个字符串str1和str2。str1包含空值,str2是一个空字符串。

我们还创建了一个函数isNullOr空ty(),顾名思义,它检查字符串是null还是空。它使用使用!=null和is空()方法的字符串空值检查来检查它。

简单地说,如果字符串不是null并且is空()返回false,则它既不是null也不是空的。否则,它就是。

但是,如果字符串仅包含空格字符(空格),上述程序不会返回空。从技术上讲,is空()看到它包含空格并返回false。对于带有空格的字符串,我们使用字符串方法trim()来修剪所有前导和尾随空格字符。

Objects.isNull(null)如果您使用的是Java8,那么您可以尝试此代码。

如果您不使用Java8,请尝试使用以下代码

Object ob=null;if(ob==null){ **do something}

就个人而言,我会选择吉姆-纳尔逊的回答,或者如果我确实发现空检查对特定上下文很方便,我会将龙目岛合并到我的项目中并使用@陈志立注释。

示例:

import lombok.NonNull;
public class NonNullExample extends Something {private String name;  
public NonNullExample(@NonNull Person person) {super("Hello");this.name = person.getName();}}

甚至@陈志立序言也提到:

  • 或者:我如何学会停止担心并爱上NullPointerException。