
Почему Вы должны узнать о null в Java?
Потому что, если Вы не обратите внимания на null, будьте уверены, Java заставит страдать от ужасногоjava.lang.NullPointerException
и Вы выучите этот урок, но пойдете более трудным путем. Написание устойчивого к "падениям" кода - это искусство и Ваша команда, заказчики и пользователи оценят это. По моему опыту, одна из основных причин NullPointerException
это недостаток знаний о null в Java. Многие из Вас уже знакомы с null, остальные смогу узнать некоторые старые и новые вещи о ключевом слове null. Давайте повторим или узнаем некоторые важные вещи о null в Java.
Что есть null в Java
Как я говорил, null очень-очень важная концепция в Java. Первоначально он был изобретен для обозначения отсутствия чего-либо, например, отсутствие пользователя, ресурса или чего угодно, но уже в течение года озадачил Java-программистов множествомnull pointer exception
. В этом уроке, мы узнаем основные факты о ключевом слове null в Java, изучим некоторые приемы как избежать неприятностей с null pointer exceptions
и минимизировать проверки на null.
Перво-наперво, null это ключевое слово в Java, так же как
public
,static
илиfinal
. Регистр учитывается, Вы не можете писать null как Null или NULL, компилятор не распознает его и будет выброшена ошибка.Object obj = NULL; // Not Ok Object obj1 = null //Ok
Зачастую, с этим встречаются программисты, перешедшие с других языков программирования, но при использовании современных IDE проблема становится незначительной. В наши дни, IDE вроде Eclipse или NetBeans могут исправлять эту ошибку пока Вы набираете код, но в эпоху Notepad, Vim и Emacs, это была распространенная проблема, которая могла съесть кучу драгоценного времени.
Так же, как каждый примитив имеет значение по умолчанию, например, у
int
это 0, у boolean это false, null это значение по умолчанию любых ссылочных типов, проще говоря, для всех объектов. Так же, как при создании логической переменной ее значение по умолчанию равно 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
Это истинно как для статических, так и для не статических объектов, как Вы можете видеть здесь, я сделал
myObj
статической ссылкой, так что я могу использовать ее непосредственно в методеmain
, который является статическим методом и не позволяет обращаться к не статическим переменным изнутри.Несмотря на распространенное заблуждение, null это не объект (
Object
) и ни тип. Это просто специальное значение, которое может быть назначено любому ссылочному типу, и Вы можете привести 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 к любому ссылочному типу пройдет успешно как во время компиляции, так и во время выполнения программы. В отличии от того, что многие из Вас возможно подумали, это не приведет к выбрасыванию
NullPointerException
.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 объекту класса-обертки, а затем присвоить этот объект соответствующему примитивному типу, компилятор не отреагирует, но мы будем вознаграждены
null pointer exception
во время выполнения. Это происходит из-за авто упаковки (autoboxing
) в Java, и мы еще встретимся с ним в следующем пункте.Любой класс-обертка со значением null будет выбрасывать
java.lang.NullPointerException
когда Java распакует(unbox
) его в примитивную переменную. Некоторые программисты делают ошибку допуская, что авто упаковка(autoboxing
) позаботится о конвертации null в значение по умолчанию для соответствующего примитивного типа, например, 0 дляint
, false дляboolean
и т.д., но это не верно, в чем можно убедиться ниже:Integer iAmNull = null; int i = iAmNull; // Remember - No Compilation Error
Но, когда Вы запустите данный фрагмент кода, в консоли Вы увидите
Exception in thread "main" java.lang.NullPointerException
Это часто происходит при работе с
HashMap
иInteger key
. Выполнение кода, показанного ниже прервется, как только Вы его запустите.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 } } }
Output: Exception in thread "main" java.lang.NullPointerException at Test.main(Test.java:25)
Этот код выглядит очень простым и безобидным. Вы просто подсчитываете сколько раз число встречается в массиве, классическая техника нахождения дубликатов. Разработчик берет предыдущее подсчитанное количество, увеличивает его на единицу и вставляет обратно в
Map
. Он мог бы подумать, что авто-упаковка позаботится о преобразованииInteger
вint
, как это делается в момент вызова методаput()
, но он забывает, что если для числа подсчет еще не проводился, методget()
вернет изHashMap
null, не ноль, потому что значение по умолчанию для Integer это null, а не 0, и авто-упаковка выброситnull pointer exception
при попытке сконвертировать Integer в переменнуюint
.Оператор
instanceof
будет возвращать false если в качестве параметра указать любую ссылочную переменную со значением null или null сам по себе. Пример: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"); }
Output : iAmNull is NOT an instance of Integer
Это важное свойство оператора
instanceof
, которое делает его полезным для проверки приведения типов.Вы знаете, что Вы не можете вызвать нестатический метод у ссылочной переменной со значением null, это вызовет
NullPointerException
, но Вы можете не знать, что Вы можете вызвать статический метода у ссылочной переменной со значением null. Т.к. статические методы используют статическое связывание, они не выбрасываютNullPointerException
. Вот пример: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"); } }
Output: I am static method, can be called by null reference Exception in thread "main" java.lang.NullPointerException at Testing.main(Testing.java:11)
Вы можете послать null в качестве параметра метода, принимающего любой ссылочный тип, к примеру:
public void print(Object obj)
может быть вызван как
print(null)
Это нормально с точки зрения компилятора, но дальнейшее поведение полностью зависит от метода. Null-безопасный метод не выбросит
NullPointerException
, а просто корректно завершится. Если бизнес логика позволяет, рекомендуется писать null-безопасные методы.Вы можете сравнивать null используя операторы
==
(равно) и!=
(не равно), но не можете использовать его с другими арифметическими или логическими операторами, вроде<
(меньше) или>
(больше). В отличии от SQL, в Javanull == 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){ } } }
Output: null == null is true in Java
NullPointerException
, Вы сможете сделать Ваш код безопасным. Т.к. значение null может рассматриваться как пустое или неинициализированное значение, это часто является источником путаницы, вот почему так важно документировать поведение метода при входящем значении null. Всегда помните, null это значение по умолчанию ссылочных переменных, и Вы не можете вызывать методы экземпляра или получать доступ к переменным экземпляра используя null-ссылку в Java.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ