Ссылочные типы. Null

Если передать значение примитивного типа в метод, оно будет скопировано:

public static void main(String[] args) {
    // объявляем переменную примитивного типа
    int voltage = 4100;
    
    // передаём
    add100mV(voltage);
    
    // 4100 — без изменений
    System.out.println(voltage);
}
private static void add100mV(int lowVoltage) {
    lowVoltage += 100;
    // Абсолютно бесполезное действие.
    // Переменная lowVoltage существует 
    // только в пределах метода и уничтожается,
    // когда метод отработал.
}

Если передать объект в метод, будет передана ссылка на этот объект.

public static void main(String[] args) {
    int[] voltages = {4100};
    // массивы в Java — это объекты.
    add100mVToFirstElement(voltages); // передаём
    System.out.println(voltages[0]); // 4200
}
private static void add100mVToFirstElement(int[] lowVoltages) {
    lowVoltages[0] += 100;
    // Локальная переменная lowVoltages
    // указывает на массив,
    // мы прибавляем 100 мВ
    // к значению нулевого элемента
    // этого массива.
}

Это называется «передача по значению» и «передача по указателю» соответственно.

Кроме того, что можно указывать на объекты (например, int[] voltages = {4100}; или Phone myPhone = new Nexus5();), также можно указывать на пустое значение.

public static void main(String[] args) {
    int[] voltages = null;
    // пустой указатель
    add100mVToFirstElement(voltages); // передаём
}
private static void add100mVToFirstElement(int[] lowVoltages) {
    lowVoltages[0] += 100;
}

Результат:

Exception in thread "main" java.lang.NullPointerException
	at JavaneseNull.add100mVToFirstElement(JavaneseNull.java:11)
	at JavaneseNull.main(JavaneseNull.java:8)

Выполнение программы прервалось, т. к. произошла попытка чтения элемента из null. Ещё один пример безнадёжного кода:

public static void main(String[] args) {
    MobilePhone mp = null;
    mp.printSerialNumber();
}

null существует, чтобы показать отсутствие значения. Любая переменная объектного (ссылочного) типа, переданная извне, может оказаться null, поэтому нужно проверять входные данные. Например:

private static void add100mVToFirstElement(int[] lowVoltages) {
    if (lowVoltages == null) {
        System.out.println("add100mVToFirstElement ожидает массив, а передан null");
        return;
    }
    if (lowVoltages.length < 1) {
        System.out.println("add100mVToFirstElement ожидает массив ненулевой длины.");
        return;
    }
    lowVoltages[0] += 100;
}

Конечно, вместо того, чтобы выводить сообщение, нужно прерывать выполнение программы, потому что требуемое действие осуществить невозможно и продолжать выполнение бессмысленно. Детально разберём это ближе к концу главы, вот пример для ознакомления:

private static void add100mVToFirstElement(int[] lowVoltages) {
    if (lowVoltages == null)
        throw new NullPointerException("lowVoltages == null, ожидался массив");
    if (lowVoltages.length < 1)
        throw new IllegalArgumentException("lowVoltages.length < 1, ожидался массив хотя бы с одним элементом");
    lowVoltages[0] += 100;
}

Если аргумент lowVoltages не прошёл проверку, выполнение будет прервано с выводом соответствующего сообщения.

Если передать null:

NullPointerException

Если передать пустой массив:

IllegalArgumentException

Непустой массив:

Норомальное выполнение метода.

Успех!

Комментарии к уроку

Сообщить об ошибке