您的位置:首页 > 编程语言 > Java开发

Java中有关Null的9件事

2016-04-15 15:02 369 查看
**

Java中有关Null的9件事

**

对于
Java
程序员来说,
null
是令人头痛的东西。时常会受到空指针异常(
NPE
)的骚扰。连
Java
的发明者都承认这是他的一项巨大失误。
Java
为什么要保留
null
呢?
null
出现有一段时间了,并且我认为
Java
发明者知道
null
与它解决的问题相比带来了更多的麻烦,但是
null
仍然陪伴着
Java


我越发感到惊奇,因为
Java
的设计原理是为了简化事情,那就是为什么没有浪费时间在指针、操作符重载、多继承实现的原因,
null
却与此正好相反。好吧,我真的不知道这个问题的答案,我知道的是不管
null
Java
开发者和开源社区如何批评,我们必须与
null
共同存在。与其为
null
的存在感到后悔,我们倒不如更好的学习
null
,确保正确使用
null


为什么在
Java
中需要学习
null
?因为如果你对
null
不注意,
Java
将使你遭受空指针异常的痛苦,并且你也会得到一个沉痛的教训。精力充沛的编程是一门艺术,你的团队、客户和用户将会更加欣赏你。以我的经验来看,导致空指针异常的一个最主要的原因是对
Java
null
的知识还不够。你们当中的很多已经对
null
很熟悉了,但是对那些不是很熟悉的来说,可以学到一些关于
null
老的和新的知识。让我们一起重新学习
Java
null
的一些重要知识吧。

Java
中的
Null
是什么?


正如我说过的那样,
null
Java
中一个很重要的概念。
null
设计初衷是为了表示一些缺失的东西,例如缺失的用户、资源或其他东西。但是,一年后,令人头疼的空指针异常给
Java
程序员带来不少的骚扰。在这份材料中,我们将学习到
Java
null
关键字的基本细节,并且探索一些技术来尽可能的减少
null
的检查以及如何避免恶心的空指针异常。

1)首先,
null
Java
中的关键字,像
public
static
final
。它是大小写敏感的,你不能将
null
写成
Null
NULL
,编译器将不能识别它们然后报错。

Object obj = NULL; // Not Ok
Object obj1 = null  //Ok


使用其他语言的程序员可能会有这个问题,但是现在IDE的使用已经使得这个问题变得微不足道。现在,当你敲代码的时候,
IDE
Eclipse
Netbeans
Idea
可以纠正这个错误。但是使用其他工具像
Notepad
Vim
Emacs
,这个问题却会浪费你宝贵时间的。

2)就像每种原始类型都有默认值一样,如
int
默认值为
0
boolean
的默认值为
false
null
是任何引用类型的默认值,不严格的说是所有
object
类型的默认值。就像你创建了一个布尔类型的变量,它将
false
作为自己的默认值,
Java
中的任何引用变量都将
null
作为默认值。这对所有变量都是适用的,如成员变量、局部变量、实例变量、静态变量(但当你使用一个没有初始化的局部变量,编译器会警告你)。为了证明这个事实,你可以通过创建一个变量然后打印它的值来观察这个引用变量,如下图代码所示:

private static Object myObj;
public static void main(String args[]){
System.out.println("What is value of myObjc : " + myObj);
}


What is value of myObjc : null


这对静态和非静态的
object
来说都是正确的。就像你在这里看到的这样,我将
myObj
定义为静态引用,所以我可以在主方法里直接使用它。注意主方法是静态方法,不可使用非静态变量。

3)我们要澄清一些误解,
null
既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,你也可以将
null
转化成任何类型,来看下面的代码:

String str = null; // null can be assigned to String
Integer itr = null; // you can assign null to Integer also
Double dbl = null;  // null can also be assigned to Double

String myStr = (String) null; // null can be type cast to String
Integer myItr = (Integer) null; // it can also be type casted to Integer
Double myDbl = (Double) null; // yes it's possible, no error


你可以看到在编译和运行时期,将
null
强制转换成任何引用类型都是可行的,在运行时期都不会抛出空指针异常。

4)
null
可以赋值给引用变量,你不能将
null
赋给基本类型变量,例如
int
double
float
boolean
。如果你那样做了,编译器将会报错,如下所示:

int i = null; // type mismatch : cannot convert from null to int
short s = null; //  type mismatch : cannot convert from null to short
byte b = null: // type mismatch : cannot convert from null to byte
double d = null; //type mismatch : cannot convert from null to double

Integer itr = null; // this is ok
int j = itr; // this is also ok, but NullPointerException at runtime


正如你看到的那样,当你直接将
null
赋值给基本类型,会出现编译错误。但是如果将
null
赋值给包装类
object
,然后将
object
赋给各自的基本类型,编译器不会报,但是你将会在运行时期遇到空指针异常。这是
Java
中的自动拆箱导致的,我们将在下一个要点看到它。

5) 任何含有
null
值的包装类在
Java
拆箱生成基本数据类型时候都会抛出一个空指针异常。一些程序员犯这样的错误,他们认为自动装箱会将
null
转换成各自基本类型的默认值,例如对于
int
转换成
0
,布尔类型转换成
false
,但是那是不正确的,如下面所示:

Integer iAmNull = null;
int i = iAmNull; // Remember - No Compilation Error


但是当你运行上面的代码片段的时候,你会在控制台上看到主线程抛出空指针异常。在使用
HashMap
Integer
键值的时候会发生很多这样的错误。当你运行下面代码的时候就会出现错误。

import java.util.HashMap;
import java.util.Map;

/**
* An example of Autoboxing and NullPointerExcpetion
*
* @author WINDOWS 8
*/
public class Test {
public static void main(String args[]) throws InterruptedException {
Map numberAndCount = new HashMap<>();
int[] numbers = {3, 5, 7,9, 11, 13, 17, 19, 2, 3, 5, 33, 12, 5};

for(int i : numbers){
int count = numberAndCount.get(i);
numberAndCount.put(i, count++); // NullPointerException here
}
}
}


输出:

Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:25)


这段代码看起来非常简单并且没有错误。你所做的一切是找到一个数字在数组中出现了多少次,这是
Java
数组中典型的寻找重复的技术。开发者首先得到以前的数值,然后再加一,最后把值放回
Map
里。程序员可能会以为,调用
put
方法时,自动装箱会自己处理好将
int
装箱成
Interger
,但是他忘记了当一个数字没有计数值的时候,
HashMap
get()
方法将会返回
null
,而不是
0
,因为
Integer
的默认值是
null
而不是
0
。当把
null
值传递给一个
int
型变量的时候自动装箱将会返回空指针异常。设想一下,如果这段代码在一个
if
嵌套里,没有在
QA
环境下运行,但是你一旦放在生产环境里,
BOOM
:

6)如果使用了带有
null
值的引用类型变量,
instanceof
操作将会返回
false


Integer iAmNull = null;
if(iAmNull instanceof Integer){
System.out.println("iAmNull is instance of Integer");

}else{
System.out.println("iAmNull is NOT an instance of Integer");
}


输出:

i
AmNull is NOT an instance of Integer


这是
instanceof
操作一个很重要的特性,使得对类型强制转换检查很有用。

7)你可能知道不能调用非静态方法来使用一个值为
null
的引用类型变量。它将会抛出空指针异常,但是你可能不知道,你可以使用静态方法来使用一个值为
null
的引用类型变量。因为静态方法使用静态绑定,不会抛出空指针异常。下面是一个例子:

public class Testing {
public static void main(String args[]){
Testing myObject = null;
myObject.iAmStaticMethod();
myObject.iAmNonStaticMethod();
}

private static void iAmStaticMethod(){
System.out.println("I am static method, can be called by null reference");
}

private void iAmNonStaticMethod(){
System.out.println("I am NON static method, don't date to call me by null");
}


输出:

I am static method, can be called by null reference
Exception in thread "main" java.lang.NullPointerException
at Testing.main(Testing.java:11)


8)你可以将
null
传递给方法使用,这时方法可以接收任何引用类型,例如
public void print(Object obj)
可以这样调用
print(null)
。从编译角度来看这是可以的,但结果完全取决于方法。
Null
安全的方法,如在这个例子中的
print
方法,不会抛出空指针异常,只是优雅的退出。如果业务逻辑允许的话,推荐使用
null
安全的方法。

9)你可以使用
==
或者
!=
操作来比较
null
值,但是不能使用其他算法或者逻辑操作,例如小于或者大于。跟
SQL
不一样,在
Java
null==null
将返回
true
,如下所示:

public class Test {

public static void main(String args[]) throws InterruptedException {

String abc = null;
String cde = null;

if(abc == cde){
System.out.println("null == null is true in Java");
}

if(null != null){
System.out.println("null != null is false in Java");
}

// classical null check
if(abc == null){
// do something
}

// not ok, compile time error
if(abc > null){

}
}
}


输出:

null == null is true in Java


这是关于
Java
null
的全部。通过
Java
编程的一些经验和使用简单的技巧来避免空指针异常,你可以使你的代码变得
null
安全。因为
null
经常作为空或者未初始化的值,它是困惑的源头。对于方法而言,记录下
null
作为参数时方法有什么样的行为也是非常重要的。总而言之,记住,
null
是任何一个引用类型变量的默认值,在
Java
中你不能使用
null
引用来调用任何的
instance
方法或者
instance
变量。

原文链接: javarevisited 翻译: ImportNew.com - Calarence

译文链接:
http://www.importnew.com/14229.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息