WEB-школа Java: Фишки

Это пример лабораторной работы, предлагаемой учащимся. Некоторые опыты и выводы могут показаться глупыми или банальными: ничего страшного, одна из целей учебы - научиться действовать в соответствии с логикой машины ( а кому, хотя бы раз, машина не показалась дурой! )

Главная Java Incognita Обзор систем Студент Пробные уроки Коммуникатор Вниз

Лабораторная работа: Исследование сущности null

Опыт № 1. Присвоение ссылке значения null-типа и присвоение выражению null значения ссылки.
  
  class Zexpnull{
     	
    public static void main(String [] args) {
	Zexpnull p=new Zexpnull();
	p=null;
	null=p; // cannot assign a value to final variable null
		// incompatible types found : Zexpnull  required: <nulltype>
               
            System.out.println("Hi!");
           
    }
  }
Результаты:
  1. Ссылке на реальный объект можно присвоить значение выражения null:
    	p=null;
  2. Нельзя присвоить выражению null новое значение, потому что null - финальная переменная:
    	null=p; // неправильно
  3. Выражению null соответствует <nulltype>, у которого нет своего ключевого слова:
    	null=p; // типы слева и справа от равенства - несовместимы 
Опыт № 2. Условия: по ср. с № 1 ссылка неинициализирована
   class Zexpnull{
       
	public static void main(String []args){
		Zexpnull p;
		null=p;
		System.out.println("Hi!");
           
	}
   }
Результаты:
  1. Нельзя присвоить выражению null новое значение, потому что null - финальная переменная:
    		null=p; // неправильно
  2. Обнаружены несовместимые по присвоению типы. Найден Zexpnull, а требуется <nulltype>:
    		null=p; // типы слева и справа от равенства - несовместимы
Хотя неинициализированной ссылке автоматически присваивается значение null, ее тип по прежнему несовместим с <nulltype>

Опыт № 3. Условия: по ср. с № 2 ссылка инициализирована значением null

   class Zexpnull{
       
	public static void main(String []args){
		Zexpnull p;
		p=null;
		null=p;
		System.out.println("Hi!");

	}
   }
Результаты:
  1. Нельзя присвоить выражению null новое значение, потому что null - финальная переменная:
    		null=p; // неправильно
  2. Обнаружены несовместимые по присвоению типы. Найден Zexpnull, а требуется <nulltype>
    		null=p; // типы слева и справа от равенства - несовместимы
    
Вывод тот же, что и в предыдущих опытах

Опыт № 4. Условия: присвоение выражению null значения null

   class Zexpnull{

	public static void main(String []args){
	
		null=null;

		System.out.println("Hi!");

	}
   }
Результаты:

Нельзя присвоить выражению null новое значение, потому что null - финальная переменная:

		null=null;// неправильно
Выражение null является финальной переменной в коде ей вообще невозможно присвоить значение, то есть это выражение не может находиться справа от оператора присвоения.

Опыт № 5. Условия: явное преобразование null к другому типу

   public class Zexpnull{
	public static void main(String []args){
		Zexpnull p;
		p=(Zexpnull)null;	//ok

	}
   }
Результаты:

выражение null можно привести к произвольному типу. Мы сделали это путем явного приведения типа, но обычно это делается автоматически - при любом присвоении объектной ссылке значения null. Во время такого присвоения типом выражения null становится тип ссылки. Итак, <nulltype> может приводится к любому ссылочному типу.

Опыт № 6. Условия: проверка контроля типа со стороны компилятора. Мы приводим выражение null к типу, отличающемуся от типа выражения стоящего слева от знака равенства.

   public class Zexpnull{
	public static void main(String []args){
		Pnull p;
		p=(Zexpnull)null;
	}
   }  

   public class Pnull{
	public void mainHi(){
		System.out.println("Hi!");
	}
   }
Результаты:

При компиляции класса Zexpnull возникает ошибка:

	incompatible types found: Zexpnull, required: Pnull
p=(Zexpnull)null;
Кроме того, что компилятор следит за типом, тут нечего сказать

Опыт № 7. Апофеоз программирования

   public class Zexpnull{
	public void nHi(){
		System.out.println("dry-bob");
	}
	public static void main(String []args){
		Pnull q=new Pnull();
		Zexpnull p=new Zexpnull();
		int s;
		q.mainHi();
		p.nHi();
		p=(Zexpnull)null;	//присвоение p значения null типа Zexpnull
		q=(Pnull)null;	//присвоение q значения null типа Pnull
		s=(int) null;	//inconvertible types found: <nulltype> required: int

	}
   }
Результаты:

Все работает, кроме последней инструкции: <nulltype> неприводим к примитивному типу.

Опыт № 8. Проверим можно ли литерал подставлять в выражения с преобразованием типа значения этого литерала.

   public class Expnull{
	public static void main(String []args){
		int p;	
		p=(byte)2; // приведение литерала int к типу byte
		System.out.println(p);
	}
   }  
Результаты:

все нормально. Значение литерала 2 приводится к типу byte, перед присвоением автоматически промотируется в int и присваивается переменной p.

Опыт № 9. Проверим можно ли литерал подставлять в выражения с преобразованием типа значения этого литерала.

   public class Expnull1{
	public static void main(String []args){
		int p;	
		p=(long)2;	//несоответствие типа
		System.out.println(p);
	}
   }  
Результат: ошибка компиляции "недопустимое по типу присвоение"
	possible loss of precision
	found: long   required: int
		p=(long)2;

В начале значение литерала 2 преобразуется к типу long. Чтобы осуществить операцию присвоения компилятору нужно привести тип выражения стоящего справа от оператора присвоения к типу выражения стоящего слева. В данном случае требуется конвертировать тип long в тип int, однако такое преобразование может привести к потере точности (loss of precision) и запрещено Из опытов № 8 и № 9 следует, что целые литералы также могут преобразовываться из типа по умолчанию (int) в другие целые типы.

Опыт № 10 Попытка использовать null как название типа:

   public class Zexpnull{
	public void nHi(){
		System.out.println("dry-bob");
	}
	public static void main(String []args){
		Pnull q=new Pnull();
		Zexpnull p=new Zexpnull();
		q.mainHi();
		p.nHi();
		p=(Zexpnull)null;
		q=(Pnull)null;
		q=(null)p;//неправильно воспринимается: ';' expected
                		    //q=(null)p ;
                       			      ^

	}
   } 
Результаты:

компилятор считает null выражением а не типом, поэтому скобки не воспринимает как оператор приведения типа.

Выводы

  1. Выражение null имеет тип, упоминаемый компилятором как <nulltype> . Компилятор контролирует соответствие типов с участием <nulltype>.
  2. Единственным значением типа <nulltype> является значение выражения null вне текста программы или в ошибочной инструкции, когда null стоит справа от знака присваивания
  3. Тип стоящий справа от знака равенства диктует, каким должен быть тип слева.
  4. Слово null не является названием типа.
  5. Выражение null имеет постоянное значение не поддающееся изменению
  6. null рассматривается компилятором как final-переменная.
  7. Возможно явное приведение типа <nulltype> к любому ссылочному типу.
  8. Невозможно явное приведение типа <nulltype> к примитивному типу.
Теперь сравним это с тем, что пишется в спецификации:

"Имеется также особый null-тип: тип выражения null, который не имеет названия. Поскольку null-тип не имеет названия, то нет возможности объявить переменную null-типа или сделать преобразование к null-типу. Для выражения null-типа ссылка null - единственное возможное значение. Ссылка null всегда может быть преобразована к любому ссылочному типу. На практике, программист может игнорировать null-тип и делать вид, что null является просто особым литералом, тип которого может быть любым ссылочным типом".

Последняя фраза весьма любопытна: null - это литерал или все-таки - выражение? Прежде всего, согласно спецификации Java даже отдельный идентификатор является выражением (имеющим тип и значение), кроме того, любой литерал является выражением. Следовательно, null либо и выражение, и литерал, либо - выражение, но не литерал. Первый случай - банален и не заслуживал бы упоминания, а второй объяснил бы многозначительную фразу "делать вид, что null является просто особым литералом"

Итак, null является выражением, но не литералом. Действительно, литерал - это символьный эквивалент значения имеющего определенный тип. Это значение и этот тип попадают в память в виде переменной с тем же значением и того же типа что и литерал.То есть литерал представлен и на этапе компиляции и на этапе выполнения.

Выражение null имеет тип и значение, но этот тип и это значение не может, в принципе, попасть в память в виде переменной. Каждый раз во время использования null, его значение сперва приводится к определенному ссылочному типу, а только затем присваивается, то есть попадает в байт-код, а потом и в память. Значение null находясь в памяти имеет совершенно другой тип, несовместимый с первоначальным null-типом. Именно по причине того, что ссылка null со значением null-типа существует только во время компиляции, не попадает в байт -код и отсутствует во время выполнения, выражение null не является литералом, вернее сказать, требуется слегка напрячься, чтобы представить себе, что null - как бы литерал.

Конечно, задача такого плана вряд ли встретится на экзамене, но благодаря лабораторной работе мы хорошо разобрались с сущностью ссылки null и с тем, что такое литерал (и то и другое - весьма туманно излагаются во всех учебниках.

Главная
Java Incognita
Обзор систем
Студент
Пробные уроки
Коммуникатор
Вверх

Хостинг от uCoz