Вопрос 7 (значения по умолчанию)
Локальные переменные объявляются в методах, конструкторах или блоках (далее МКБ). Для них не существует значения по умолчанию. Переменные экземпляра и класса объявляются в классе, но за пределами МКБ. Они имеют значения по умолчанию, поэтому явная инициализация не является для них обязательной. С другой стороны обращение к локальной переменной без предварительной инициализации приведет к ошибке при компиляции.
скрытый текст 2. Таблица значений по умолчанию для переменных разных типов:
boolean: false
char: u0000 (пустой символ)
int,short,byte / long: 0 / 0L
float /double: 0.0f / 0.0d
ссылочные типы: null
FAQ по var в Java
Локальные переменные — это «рабочие лошадки» Java. Они используются для хранения промежуточных результатов вычислений. И, в отличие от полей, объявляются, инициализируются и используются в одном блоке. Для понимания кода часто более важны имя и инициализатор, чем тип локальной переменной. В имени и инициализаторе столько же информации, сколько и в типе:
Person person = new Person();
Вместо явного указания типа можно использовать var :
var person = new Person();
Компилятор определяет тип локальной переменной по инициализатору. Это особенно важно, когда тип параметризован wildcard , или упоминается в инициализаторе. Использование var может сделать код более кратким без ущерба для удобочитаемости, а в некоторых случаях может улучшить читаемость благодаря устранению избыточности.
Вопрос 2. Делает ли это Java динамически типизированным языком? Это то же самое, что и var в JavaScript?
Нет и нет. Java остается языком со статической типизацией, и появление var не изменяет этого. var используется в объявлении локальной переменной без явного указания типа. При использовании var компилятор Java определяет тип переменной во время компиляции, используя информацию о типе, полученную от инициализатора переменной. Далее выведенный тип используется как статический тип этой переменной. Как правило, будет тот же тип, который вы бы указали явно, поэтому переменная, объявленная с помощью var , ведет себя точно так же, как при явном указании типа.
В компиляторе Java выведение типов присутствует уже много лет. Например, параметры лямбда-выражения в Java 8 не нуждаются в явном указании типа, так как компилятор определяет их по тому, как используется лямбда-выражение:
List list = . list.stream().filter(p -> p.getAge() > 18) .
В приведенном выше фрагменте кода тип параметра p в лямбде выводится как Person . Если класс Person будет изменен так, что в нем больше не будет метода getAge , или в списке будет храниться что-то отличное от Person , то выведение типа завершится ошибкой при компиляции.
Вопрос 3. Переменная var неявно final?
Нет. Локальные переменные, объявленные с помощью var , по умолчанию не являются final . Однако к var можно добавить модификатор final :
final var person = new Person();
Для final var в Java нет никакого сокращения. Например, в Scala для объявления неизменяемых переменных используется val . В Scala это хорошо работает, потому что все переменные (и локальные, и поля) объявляются с помощью синтаксиса следующего вида.
val name : type
var name : type
Указывать » : type » в объявлении необязательно — это зависит от того, хотите ли вы использовать выведение типа или нет. В Scala выбор между изменяемостью и неизменяемостью ортогонален к выведению типов.
В Java var можно использовать только там, где требуется выведение типа. Его нельзя использовать, если тип объявлен явно. Если в Java добавить val , то его тоже можно было бы использовать только там, где используется выведение типов. В Java нельзя было бы использовать var или val для управления иммутабельностью при явном объявлении типа.
Кроме того, Java позволяет использовать var только для локальных переменных, но не для полей. Для полей иммутабельность более значима. Неизменяемые локальные переменные используются сравнительно редко.
Использование ключевых слов var/val для управления иммутабельностью — это то, что действительно стоит позаимствовать из Scala в Java. Однако в Java это было бы гораздо менее полезно, чем в Scala.
Вопрос 4. Не будут ли плохие разработчики злоупотреблять var, чтобы писать ужасный код?
Да, плохие разработчики будут писать ужасный код, что бы мы ни делали. Отказ от var не помешает им это сделать. Но при правильном применении выведение типов позволит писать более качественный код.
Один из моментов, благодаря которому var может побудить разработчиков писать более качественный код, заключается в том, что var снижает накладные расходы на объявление новой переменной. Разработчики часто избегают объявления новой переменной, если это требует больших затрат, и создают сложные конструкции, ухудшающие читаемость, только из-за того, чтобы избежать объявления дополнительных переменных. С var накладные расходы на извлечение части кода в именованную переменную уменьшаются, поэтому разработчики с большей вероятностью будут это делать, что приведет к более чистому коду.
Часто программисты начинают очень активно использовать новые возможности языка, возможно, злоупотребляя ими, и требуется некоторое время, чтобы сообщество выработало рекомендации по разумному использованию.
Начиная с выведения типа локальной переменной (Local Variable Type Inference, LVTI), мы публикуем материалы о назначении и рекомендации по использованию (например, данный FAQ и LVTI Style Guidelines) почти одновременно с появлением этой функциональности. Мы надеемся, что это ускорит понимание в сообществе, когда разумно использовать var , и поможет избежать злоупотребления.
Вопрос 5. Где можно использовать var?
var может использоваться при объявлении локальных переменных, включая индексные переменные цикла for и ресурсные переменные оператора try-with-resources .
var нельзя использовать для полей, параметров методов и возвращаемых типов методов. Причина заключается в том, что типы в этих местах явно присутствуют в class-файлах и в спецификациях Javadoc. При выведении типа изменение инициализатора может легко привести к изменению предполагаемого типа переменной. Для локальных переменных это не проблема, так как область видимости локальных переменных ограничена, и их типы напрямую не записываются в class-файлы. Однако для полей, параметров методов и типов, возвращаемых методом, выведение типа может легко вызвать проблему.
Например, возвращаемый тип метода был выведен из выражения в операторе return . Изменение реализации метода может привести к изменению типа выражения в return . Это, в свою очередь, может изменить тип возвращаемого значения. Что может привести к бинарной несовместимости или несовместимости в исходном коде. Такие несовместимые изменения не должны возникать из-за безобидных на вид изменений в реализации.
Предположим, что также будет выводиться тип поля. Тогда изменение инициализатора поля может привести к изменению типа поля, что может неожиданно нарушить рефлексивный код.
Выведение типа допустимо в реализации, но не в API. API-контракты должны быть объявлены явно.
Как насчет private-полей и методов, которые не являются частью API? Теоретически мы могли бы реализовать поддержку var для private-полей и возвращаемых типов для private-методов, не опасаясь, что это вызовет несовместимость из-за раздельной компиляции и динамической линковки. Но для простоты мы решили ограничить область выведения типов. Если расширить границы и включить отдельные виды полей и типы, возвращаемые некоторыми методами, то это сделает все значительно более сложным и трудным для понимания, и лишь незначительно более полезным.
Вопрос 6. Почему для var всегда нужен инициализатор в правой части?
Тип переменной вычисляется на основе инициализатора. Это означает, что var можно использовать только при наличии инициализатора. Можно было бы сделать выведение типа из присвоений переменной, но это значительно усложнило бы реализацию и потенциально могло привести к вводящим в заблуждение или трудно диагностируемым ошибкам. Для простоты мы определили var так, чтобы для выведения типа использовалась только локальная информация.
Допустим, мы разрешили выведение типа на основе присваивания в нескольких местах, отдельно от объявления переменной. Тогда рассмотрим следующий пример:
var order; . order = "first"; . order = 2;
Если тип был выбран на основе, например, первого присваивания, то это может привести к ошибке в другом операторе, весьма удаленном от местоположения настоящей ошибки. (Иногда это называют проблемой «дальнодействия».)
В качестве альтернативы можно выбрать тип, совместимый со всеми присваиваниями. И в этом случае можно ожидать, что выведенный тип будет Object , как общий суперкласс String и Integer . К сожалению, ситуация сложнее. Поскольку и String , и Integer являются Serializable и Comparable , общий супертип будет пересечением типов, что-то вроде
Serializable & Comparable>
Обратите внимание, что невозможно явно объявить переменную этого типа. Также это приводит к упаковыванию (boxing), когда order присваивается 2, что может быть неожиданным и нежелательным.
Чтобы избежать подобных проблем, проще потребовать вывод типа с использованием явного инициализатора.
Вопрос 7. Почему нельзя использовать var с null?
Посмотрите на такое объявление (оно некорректно):
var person = null; // ОШИБКА
Литерал null обозначает значение специального типа null (JLS 4.1), который является подтипом всех ссылочных типов в Java. Единственным значением типа null является сам null , поэтому единственное значение, которое может быть присвоено переменной типа null — это null . Это не очень полезно.
Можно ввести специальное правило, чтобы объявление var , инициализированное значением null , имело тип Object . Но возникает вопрос о намерениях программиста. Предположим, что переменная инициализируется значением null , чтобы позже ей присвоить какое-либо другое значение. Но в этом случае маловероятно, что определение типа переменной как Object будет правильным.
Вместо того чтобы создавать какие-то специальные правила для обработки этого случая, мы его запретили. Если вам нужна переменная типа Object , объявите ее явно.
Вопрос 8. Можно ли использовать var с diamond-оператором справа?
Да, это работает, но, скорее всего, не так, как вы ожидаете. Пример:
var list = new ArrayList<>();
Тип списка будет ArrayList . В общем, лучше указывать явный тип слева с diamond-оператором справа или использовать var слева с явным типом справа. См. особенности использования var с diamond-оператором и дженериками в LVTI Style Guidelines (Руководство по стилю LVTI).
Приглашаем всех желающих на открытое занятие «Реляционные базы данных для начинающих Java-разработчиков». Поговорим о месте реляционных баз данных в архитектуре информационных систем. Рассмотрим основные компоненты и возможности РСУБД на примере PostgreSQL. Сделаем обзор основных технологий по работе с реляционными БД в Java (JDBC, JPA/Hibernate, Spring Data и др.) Регистрация по ссылке.
- Блог компании OTUS
- Программирование
- Java
Типы, Значения, и Переменные
Язык программирования Java является языком со строгим контролем типов, что означает, что у каждой переменной и каждого выражения есть тип, который известен во время компиляции. Типы ограничивают значения, которые может содержать переменная (§4.5) или что выражение может произвести, ограничить операции, поддерживаемые на тех значениях, и определить значение операций. Строгий контроль типов помогает обнаружить ошибки во время компиляции.
Типы языка программирования Java делятся на две категории: типы примитивов и ссылочные типы. Типы примитивов (§4.2) boolean введите и числовые типы. Числовые типы являются целочисленными типами byte , short , int , long , и char , и типы с плавающей точкой float и double . Ссылочные типы (§4.3) являются типами классов, интерфейсными типами, и выстраивают типы. Есть также специальный нулевой тип. Объект (§4.3.1) является динамически создаваемым экземпляром типа класса или динамически создаваемым массивом. Значения ссылочного типа являются ссылками на объекты. Все объекты, включая массивы, поддерживают методы класса Object (§4.3.2). Строковые литералы представляются String объекты (§4.3.3).
Имена типов используются (§4.4) в объявлениях, бросках, выражениях создания экземпляра класса, выстраивают выражения создания, литералы класса, и instanceof выражения оператора.
Переменная (§4.5) является местом хранения. Переменная типа примитива всегда содержит значение того точного типа. Переменная типа класса T может содержать нулевую ссылку или ссылку на экземпляр класса T или любого класса, который является подклассом T. Переменная интерфейсного типа может содержать нулевую ссылку или ссылку на любой экземпляр любого класса, который реализует интерфейс. Если T является типом примитива, то переменная типа «массив T» может содержать нулевую ссылку или ссылку на любой массив типа «массив T«; если T является ссылочным типом, то переменная типа «массив T» может содержать нулевую ссылку или ссылку на любой массив типа «массив S» так, что, тип S присваиваем (§5.2) типу T. Переменная типа Object может содержать нулевую ссылку или ссылку на любой объект, ли экземпляр класса или массив.
4.1 Виды Типов и Значений
Есть два вида, вводит язык программирования Java: типы примитивов (§4.2) и ссылочные типы (§4.3). Есть, соответственно, два вида значений данных, которые могут быть сохранены в переменных, передали как параметры, возвращенные методами, и работали на: примитивные значения (§4.2) и ссылочные значения (§4.3).
Type: PrimitiveType ReferenceType
4.2 Типы примитивов и Значения
Тип примитива предопределяется языком программирования Java и называется его зарезервированным словом (§3.9):
PrimitiveType: NumericTypeboolean
NumericType: IntegralType FloatingPointType IntegralType: one ofbyte short int long char
FloatingPointType: one offloat double
Числовые типы являются целочисленными типами и типами с плавающей точкой.
Целочисленные типы byte , short , int , и long , чьи значения являются 8-разрядными, 16-разрядными, 32-разрядными и 64-разрядными two’s-дополнительными целыми числами со знаком, соответственно, и char , чьи значения являются 16-разрядными целыми без знака, представляющими символы Unicode.
Типы с плавающей точкой float , чьи значения включают 32-разрядный IEEE 754 числа с плавающей точкой, и double , чьи значения включают 64-разрядный IEEE 754 числа с плавающей точкой.
boolean у типа есть точно два значения: true и false.
4.2.1 Целочисленные типы и Значения
Значения целочисленных типов являются целыми числами в следующих диапазонах:
- Для byte , от-128 до 127, включительно
- Для short , от-32768 до 32767, включительно
- Для int , от-2147483648 до 2147483647, включительно
- Для long , от-9223372036854775808 до 9223372036854775807, включительно
- Для char , от ‘\u0000’ к ‘\uffff’ включительно, то есть, от 0 до 65535
4.2.2 Целочисленные операции
Язык программирования Java предоставляет многим операторам, которые действуют на интегральные значения:
- Операторы сравнения, которые приводят к значению типа boolean :
- Числовые операторы сравнения < , , и >= (§15.20.1)
- Числовые операторы равенства == и != (§15.21.1)
- Унарное плюс и минус операторы + и — (§15.15.3, §15.15.4)
- Мультипликативные операторы * , / , и % (§15.17)
- Аддитивные операторы + и — (§15.18.2)
- Инкрементный оператор ++ , и префикс (§15.15.1) и постфикс (§15.14.1)
- Декрементный оператор — , и префикс (§15.15.2) и постфикс (§15.14.2)
- Операторы сдвига без знака и со знаком > , и >>>(§15.19)
- Поразрядный дополнительный оператор ~ (§15.15.5)
- Целочисленные логические операторы & , | , и ^ (§15.22.1)
Если у целочисленного оператора кроме оператора сдвига есть по крайней мере один операнд типа long , тогда работа выполняется, используя 64-разрядную точность, и результат числового оператора имеет тип long . Если другой операнд не long , это сначала расширяется (§5.1.4), чтобы ввести long числовым продвижением (§5.6). Иначе, работа выполняется, используя 32-разрядную точность, и результат числового оператора имеет тип int . Если любой операнд не int , это сначала расширяется до типа int числовым продвижением.
Встроенные целочисленные операторы не указывают на переполнение или потерю значимости всегда. Единственные числовые операторы, которые могут выдать исключение (§11), являются целочисленным оператором дележа / (§15.17.2) и целочисленный оператор остатка % (§15.17.3), которые бросают ArithmeticException если правый операнд является нулем.
class Test < public static void main(String[] args) < int i = 1000000; System.out.println(i * i); long l = i; System.out.println(l * l); System.out.println(20296 / (l - i)); >>
производит вывод:
-727379968 1000000000000
и затем встречается ArithmeticException в подразделении l — i , потому что l — i нуль. Первое умножение выполняется в 32-разрядной точности, тогда как второе умножение является a long умножение. Значение -727379968 десятичное значение низких 32 битов математического результата, 1000000000000 , который является значением, слишком большим для типа int .
Любое значение любого целочисленного типа может быть брошено к или от любого числового типа. Нет никаких бросков между целочисленными типами и типом boolean .
4.2.3 Типы с плавающей точкой, Форматы, и Значения
Типы с плавающей точкой float и double , которые концептуально связываются с 32-разрядной одинарной точностью и двойная точность 64-разрядные значения формата IEEE 754 и операции как определено в Стандарте IEEE для Двоичной Арифметики С плавающей точкой, Стандарт ANSI/IEEE 754-1985 (IEEE, Нью-Йорк).
Стандарт IEEE 754 включает не только положительные и отрицательные числа, которые состоят из знака и величины, но также и положительных и отрицательных нулей, положительные и отрицательные бесконечности, и специальные значения Не-числа (после этого сокращал НЭН). Значение НЭН используется, чтобы представить результат определенных недопустимых операций, таких как делящийся нуль нулем. Константы НЭН обоих float и double тип предопределяется как Float.NaN и Double.NaN .
Каждая реализация языка программирования Java обязана поддерживать два стандартных набора значений с плавающей точкой, названных набором значений плавающим и двойным набором значений. Кроме того, реализация языка программирования Java может поддерживать или или обе из двух расширенных экспонент наборы значений с плавающей точкой, названные набором значений «расширенная экспонента плавающая» и набор значений «двойная расширенная экспонента«. Эти наборы значений расширенной экспоненты, при определенных обстоятельствах, могут использоваться вместо стандартных наборов значений, чтобы представить значения выражений типа float или double (§5.1.8, §15.4).
Конечные ненулевые значения любого набора значений с плавающей точкой могут все быть выражены в форме, где s +1 или-1, м. является положительным целым числом меньше чем, и e является целым числом между и, включительно, и где N и K являются параметрами, которые зависят от набора значений. Некоторые значения могут быть представлены в этой форме больше чем одним способом; например, если значение v в наборе значений могло бы быть представлено в этой форме, используя определенные значения для s, м., и e, затем если бы это произошло, которым м. был даже, и e были меньше чем, то можно было разделить на два м. и увеличить e на 1, чтобы произвести второе представление для того же самого значения v. Представление в этой форме вызывают нормализованное если; иначе представление, как говорят, денормализовывается. Если значение в наборе значений не может быть представлено таким способом, которым, то значение, как говорят, является денормализованным значением, потому что у этого нет никакого нормализованного представления.
Ограничения на параметры N и K (и на полученные параметры Эмин и Эмэкс ) для этих требуемых двух и два дополнительных набора значений с плавающей точкой получаются в итоге в Таблице 4.1.
Где один или оба набора значений расширенной экспоненты поддерживаются реализацией, затем для каждого поддерживаемого набора значений расширенной экспоненты есть определенный зависящий от реализации постоянный K, значение которого ограничивается Таблицей 4.1; это значение K поочередно диктует значения для Эмина и Эмэкса .
Каждый из этих четырех наборов значений включает не только конечные ненулевые значения, которые приписываются ему выше, но также и значения НЭН и четыре значения положительный нуль, отрицательный нуль, положительная бесконечность, и отрицательная бесконечность.
Отметьте, что ограничения в Таблице 4.1 разрабатываются так, чтобы каждый элемент набора значений плавающего был обязательно также элементом набора значений «расширенная экспонента плавающая», двойной набор значений, и набор значений «двойная расширенная экспонента». Аналогично, каждый элемент двойного набора значений является обязательно также элементом набора значений «двойная расширенная экспонента». Каждый набор значений расширенной экспоненты имеет больший диапазон значений экспоненты чем соответствующий стандартный набор значений, но не имеет большей точности.
Элементы набора значений плавающего являются точно значениями, которые могут быть представлены, используя единственный формат с плавающей точкой, определенный в стандарте IEEE 754. Элементы двойного набора значений являются точно значениями, которые могут быть представлены, используя двойной формат с плавающей точкой, определенный в стандарте IEEE 754. Отметьте, однако, что элементы «плавания, расширенная экспонента» и наборы значений «двойная расширенная экспонента», определенная здесь, не соответствуют значениям, которые могут быть представлены, используя IEEE 754 единственные расширенные и двойные расширенные форматы, соответственно.
Плавание, «расширенная экспонента плавающая», дважды, и наборы значений «двойная расширенная экспонента» не является типами. Это всегда корректно для реализации языка программирования Java, чтобы использовать элемент набора значений плавающего, чтобы представить значение типа float ; однако, может быть допустимо в определенных областях кода для реализации использовать элемент набора значений «расширенная экспонента плавающая» вместо этого. Точно так же это всегда корректно для реализации, чтобы использовать элемент двойного набора значений, чтобы представить значение типа double ; однако, может быть допустимо в определенных областях кода для реализации использовать элемент набора значений «двойная расширенная экспонента» вместо этого.
За исключением НЭН, упорядочиваются значения с плавающей точкой; расположенный от самого маленького до самого большого, они — отрицательная бесконечность, отрицательные конечные ненулевые значения, положительный и отрицательный нуль, положительные конечные ненулевые значения, и положительная бесконечность.
IEEE 754 позволяет многократные отличные значения НЭН для каждого из его единственных и двойных форматов с плавающей точкой. В то время как каждая аппаратная архитектура возвращает определенную комбинацию двоичных разрядов для НЭН, когда новая НЭН сгенерирована, программист может также создать NaNs с различными комбинациями двоичных разрядов, чтобы закодировать, например, ретроспективную диагностическую информацию.
По большей части платформа Java обрабатывает значения НЭН данного типа, как если бы свернутый в единственное каноническое значение (и следовательно эта спецификация обычно обращается к произвольной НЭН как если бы к каноническому значению). Однако, версия 1.3 платформа Java представленные методы, позволяющие программисту различать значения НЭН: Float.floatToRawIntBits и Double.doubleToRawLongBits методы. Заинтересованный читатель относится в спецификации для Float и Double классы для получения дополнительной информации.
Положительный нулевой и отрицательный нуль сравнивается равный; таким образом результат выражения 0.0==-0.0 true и результат 0.0>-0.0 false . Но другие операции могут отличить положительный и отрицательный нуль; например, 1.0/0.0 имеет значение положительная бесконечность, в то время как значение 1.0/-0.0 отрицательная бесконечность.
НЭН неупорядочивают, таким образом, числовые операторы сравнения < , , и >= возвратиться false если или или оба операнда НЭН (§15.20.1). Оператор равенства == возвраты false если любым операндом является НЭН, и оператор неравенства != возвраты true если любым операндом является НЭН (§15.21.1). В частности x!=x true если и только если x НЭН, и (x
=y) будет false если x или y НЭН. Любое значение типа с плавающей точкой может быть брошено к или от любого числового типа. Нет никаких бросков между типами с плавающей точкой и типом boolean .
4.2.4 Операции с плавающей точкой
Язык программирования Java предоставляет многим операторам, которые действуют на значения с плавающей точкой:
- Операторы сравнения, которые приводят к значению типа boolean :
- Числовые операторы сравнения < , , и >= (§15.20.1)
- Числовые операторы равенства == и != (§15.21.1)
- Унарное плюс и минус операторы + и — (§15.15.3, §15.15.4)
- Мультипликативные операторы * , / , и % (§15.17)
- Аддитивные операторы + и — (§15.18.2)
- Инкрементный оператор ++ , и префикс (§15.15.1) и постфикс (§15.14.1)
- Декрементный оператор — , и префикс (§15.15.2) и постфикс (§15.14.2)
Если по крайней мере один из операндов к бинарному оператору имеет тип с плавающей точкой, то работа является работой с плавающей точкой, даже если другой является неотъемлемой частью.
Если по крайней мере один из операндов числовому оператору имеет тип double , тогда работа выполняется, используя 64-разрядную арифметику с плавающей точкой, и результатом числового оператора является значение типа double . (Если другой операнд не является a double , это сначала расширяется до типа double числовым продвижением (§5.6).) Иначе, работа выполняется, используя 32-разрядную арифметику с плавающей точкой, и результатом числового оператора является значение типа float. Если другой операнд не является a float , это сначала расширяется до типа float числовым продвижением.
Операторы на числах с плавающей точкой ведут себя как определено IEEE 754 (за исключением оператора остатка (§15.17.3)). В частности язык программирования Java требует поддержки IEEE 754 денормализованные числа с плавающей точкой и постепенная потеря значимости, которые облегчают доказывать требуемые свойства определенных числовых алгоритмов. Операции с плавающей точкой «не сбрасывают, чтобы обнулить», если расчетным результатом является денормализованное число.
Язык программирования Java требует, чтобы арифметика с плавающей точкой вела себя как будто каждый оператор с плавающей точкой, округленный ее результат с плавающей точкой к точности результата. Неточные результаты должны быть округлены к представимому значению, самому близкому к бесконечно точному результату; если два самых близких представимых значения одинаково рядом, тот с его младшим значащим разрядным нулем выбирается. Это — значение по умолчанию стандарта IEEE 754 округление режима, известного как вокруг самому близкому.
Язык использует вокруг к нулю, преобразовывая плавающее значение в целое число (§5.1.3), который действует, в этом случае, как если бы число было усеченным, отбрасывая биты мантиссы. Округление к нулю выбирает в его результате значение формата, самое близкое к и не больше в величине чем бесконечно точный результат.
Операторы с плавающей точкой не производят исключений (§11). Работа, которая переполнения производят бесконечность со знаком, работа, которая потери значимости производят денормализованное значение или нуль со знаком, и работа, у которой нет никакого математически определенного результата, производит НЭН. Все числовые операции с НЭН как операнд производят НЭН в результате. Как был уже описан, НЭН неупорядочивают, таким образом, числовая работа сравнения, включающая один или два возврата NaNs false и любой != сравнение, включающее возвраты НЭН true , включая x!=x когда x НЭН.
class Test < public static void main(String[] args) < // An example of overflow: double d = 1e308; System.out.print("overflow produces infinity: "); System.out.println(d + "*10==" + d*10); // An example of gradual underflow: d = 1e-305 * Math.PI; System.out.print("gradual underflow: " + d + "\n "); for (int i = 0; i < 4; i++) System.out.print(" " + (d /= 100000)); System.out.println(); // An example of NaN: System.out.print("0.0/0.0 is Not-a-Number: "); d = 0.0/0.0; System.out.println(d); // An example of inexact results and rounding: System.out.print("inexact results with float:"); for (int i = 0; i < 100; i++) < float z = 1.0f / i; if (z * i != 1.0f) System.out.print(" " + i); >System.out.println(); // Another example of inexact results and rounding: System.out.print("inexact results with double:"); for (int i = 0; i < 100; i++) < double z = 1.0 / i; if (z * i != 1.0) System.out.print(" " + i); >System.out.println(); // An example of cast to integer rounding: System.out.print("cast to int rounds toward 0: "); d = 12345.6; System.out.println((int)d + " " + (int)(-d)); > >
производит вывод:
overflow produces infinity: 1.0e+308*10==Infinity gradual underflow: 3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0 0.0/0.0 is Not-a-Number: NaN inexact results with float: 0 41 47 55 61 82 83 94 97 inexact results with double: 0 49 98 cast to int rounds toward 0: 12345 -12345
Этот пример демонстрирует, между прочим, что постепенная потеря значимости может привести к постепенной потере точности. Результаты, когда i 0 включите подразделение нулем, так, чтобы z становится положительной бесконечностью, и z * 0 НЭН, которая не равна 1.0 .
4.2.5 boolean Введите и boolean Значения
boolean тип представляет логическое количество с двумя возможными значениями, обозначенными литералами true и false (§3.10.3). Булевы операторы:
- Операторы отношения == и != (§15.21.2)
- Логически-дополнительный оператор ! (§15.15.6)
- Логические операторы & , ^ , и | (§15.22.2)
- Условное выражение — и и условное выражение — или операторы && (§15.23) и || (§15.24)
- Условный оператор ? : (§15.25)
- Оператор конкатенации строк + (§15.18.1), который, когда дано a String операнд и булев операнд, преобразует булев операнд в a String (также «true» или «false» ), и затем произведите недавно создаваемый String это — связь двух строк
- if оператор (§14.9)
- while оператор (§14.11)
- do оператор (§14.12)
- for оператор (§14.13)
Только boolean выражения могут использоваться в операторах управления и как первый операнд условного оператора ? : . Целое число x может быть преобразован в a boolean , после соглашения языка C, которое любое ненулевое значение true , по выражению x!=0 . Ссылка на объект obj может быть преобразован в a boolean , после соглашения языка C, что любая ссылка кроме null true , по выражению obj!=null .
Бросок a boolean оцените типу boolean позволяется (§5.1.1); никто другой не набирает тип boolean позволяются. A boolean может быть преобразован в строку преобразованием строк (§5.4).
4.3 Ссылочные типы и Значения
Есть три вида ссылочных типов: типы классов (§8), интерфейсные типы (§9), и типы массива (§10).
ReferenceType: ClassOrInterfaceType ArrayType ClassOrInterfaceType: ClassType InterfaceType ClassType: TypeName InterfaceType: TypeName ArrayType: Type
[ ]
class Point < int[] metrics; >interface Move
объявляет тип класса Point , интерфейсный тип Move , и использует тип массива int[] (массив int ) объявить поле metrics из класса Point .
4.3.1 Объекты
Объект является экземпляром класса или массивом.
Ссылочные значения (часто только ссылки) являются указателями на эти объекты, и специальной нулевой ссылкой, которая не обращается ни к какому объекту.
Экземпляр класса явно создается выражением создания экземпляра класса (§15.9). Массив явно создается выражением создания массива (§15.9).
Новый экземпляр класса неявно создается, когда оператор конкатенации строк + (§15.18.1) используется в выражении, приводящем к новому объекту типа String (§4.3.3). Новый объект массива неявно создается, когда выражение инициализатора массива (§10.6) оценивается; это может произойти, когда класс или интерфейс инициализируются (§12.4), когда новый экземпляр класса создается (§15.9), или когда оператор объявления локальной переменной выполняется (§14.4).
Многие из этих случаев иллюстрируются в следующем примере:
class Point < int x, y; Point() < System.out.println("default"); >Point(int x, int y) < this.x = x; this.y = y; >// A Point instance is explicitly created at class initialization time: static Point origin = new Point(0,0); // A String can be implicitly created by a + operator: public String toString() < return "(" + x + "," + y + ")"; >> class Test < public static void main(String[] args) < // A Point is explicitly created using newInstance: Point p = null; try < p = (Point)Class.forName("Point").newInstance(); >catch (Exception e) < System.out.println(e); >// An array is implicitly created by an array constructor: Point a[] = < new Point(0,0), new Point(1,1) >; // Strings are implicitly created by + operators: System.out.println("p: " + p); System.out.println("a: < " + a[0] + ", " + a[1] + " >"); // An array is explicitly created by an array creation expression: String sa[] = new String[2]; sa[0] = "he"; sa[1] = "llo"; System.out.println(sa[0] + sa[1]); > >
который производит вывод:
default p: (0,0) a: < (0,0), (1,1) >hello
Операторы на ссылках на объекты:
- Доступ к полю, используя или полностью определенное имя (§6.6) или выражение доступа к полю (§15.11)
- Вызов метода (§15.12)
- Оператор броска (§5.5, §15.16)
- Оператор конкатенации строк + (§15.18.1), который, когда дано a String операнд и ссылка, преобразует ссылку на a String вызывая toString метод объекта, на который ссылаются (использование «null» если или ссылка или результат toString нулевая ссылка), и затем произведет недавно создаваемый String это — связь двух строк
- instanceof оператор (§15.20.2)
- Ссылочные операторы равенства == и != (§15.21.3)
- Условный оператор ? : (§15.25).
class Value < int val; >class Test < public static void main(String[] args) < int i1 = 3; int i2 = i1; i2 = 4; System.out.print("i1==" + i1); System.out.println(" but i2==" + i2); Value v1 = new Value(); v1.val = 5; Value v2 = v1; v2.val = 6; System.out.print("v1.val==" + v1.val); System.out.println(" and v2.val==" + v2.val); >>
производит вывод:
i1==3 but i2==4 v1.val==6 and v2.val==6
потому что v1.val и v2.val сошлитесь на ту же самую переменную экземпляра (§4.5.3) в том Value объект создается единственным new выражение, в то время как i1 и i2 различные переменные.
См. §10 и §15.10 для примеров создания и использования массивов. У каждого объекта есть связанная блокировка (§17.13), который используется synchronized методы (§8.4.3) и synchronized оператор (§14.18), чтобы обеспечить управление параллельным доступом, чтобы утвердить многократными потоками (§17.12).
4.3.2 Объект Класса
Класс Object суперкласс (§8.1) всех других классов. Переменная типа Object может содержать ссылку на любой объект, является ли это экземпляром класса или массива (§10). Весь класс и типы массива наследовали методы класса Object , которые получаются в итоге здесь:
package java.lang; public class Object public final Class getClass() < . . . >public String toString() < . . . >public boolean equals(Object obj) < . . . >public int hashCode() < . . . >protected Object clone() throws CloneNotSupportedException < . . . >public final void wait() throws IllegalMonitorStateException, InterruptedException < . . . >public final void wait(long millis) throws IllegalMonitorStateException, InterruptedException < . . . >public final void wait(long millis, int nanos) < . . . >throws IllegalMonitorStateException, InterruptedException < . . . >public final void notify() < . . . >throws IllegalMonitorStateException public final void notifyAll() < . . . >throws IllegalMonitorStateException protected void finalize() throws Throwable < . . . >>
Элементы Object следующие:
- Метод getClass возвраты Class объект, который представляет класс объекта. A Class объект существует для каждого ссылочного типа. Это может использоваться, например, чтобы обнаружить полностью определенное имя класса, его элементов, его непосредственного суперкласса, и любых интерфейсов, которые это реализует. Метод класса, который объявляется synchronized (§8.4.3.6) синхронизируется на блокировке, связанной с Class объект класса.
- Метод toString возвраты a String представление объекта.
- Методы equals и hashCode очень полезны в хеш-таблицах такой как java.util.Hashtable . Метод equals определяет понятие объектного равенства, которое основано на значении, не ссылке, сравнении.
- Метод clone используется, чтобы сделать копию объекта.
- Методы wait , notify , и notifyAll используются в параллельном программировании, используя потоки, как описано в §17.
- Метод finalize выполняется непосредственно перед тем, как объект уничтожается и описывается в §12.6.
4.3.3 Строка Класса
Экземпляры класса String представьте последовательности символов Unicode. A String у объекта есть постоянное (неизменное) значение. Строковые литералы (§3.10.5) являются ссылками на экземпляры класса String .
Оператор конкатенации строк + (§15.18.1) неявно создает новое String объект.
4.3.4 Когда Ссылочные типы Являются Тем же самым
Два ссылочных типа являются тем же самым типом времени компиляции, если у них есть то же самое двоичное имя (§13.1), когда они, как иногда говорят, являются тем же самым классом или тем же самым интерфейсом.
Во время выполнения несколько ссылочных типов с тем же самым двоичным именем могут быть загружены одновременно различными загрузчиками класса. Эти типы могут или, возможно, не представляют то же самое описание типа. Даже если два таких типа действительно представляют то же самое описание типа, их считают отличными.
Два ссылочных типа являются тем же самым типом времени выполнения если:
- Они — оба класс или оба интерфейсных типа, загружаются тем же самым загрузчиком класса, и имеют то же самое двоичное имя (§13.1), когда они, как иногда говорят, являются тем же самым классом времени выполнения или тем же самым интерфейсом времени выполнения.
- Они — и типы массива, и их компонентные типы являются тем же самым типом времени выполнения (§10).
4.4 Где Типы Используются
Типы используются, когда они появляются в объявлениях или в определенных выражениях.
Следующий фрагмент кода содержит один или более экземпляров большинства видов использования типа:
import java.util.Random; class MiscMath < int divisor; MiscMath(int divisor) < this.divisor = divisor; >float ratio(long l) < try < l /= divisor; >catch (Exception e) < if (e instanceof ArithmeticException) l = Long.MAX_VALUE; else l = 0; >return (float)l; > double gausser() < Random r = new Random(); double[] val = new double[2]; val[0] = r.nextGaussian(); val[1] = r.nextGaussian(); return (val[0] + val[1]) / 2; >>
В этом примере типы используются в объявлениях следующего:
- Импортированные типы (§7.5); здесь тип Random , импортированный из типа java.util.Random из пакета java.util , объявляется
- Поля, которые являются переменными класса и переменными экземпляра классов (§8.3), и константы интерфейсов (§9.3); здесь поле divisor в классе MiscMath как объявляют, имеет тип int
- Параметры метода (§8.4.1); здесь параметр l из метода ratio как объявляют, имеет тип long
- Результаты метода (§8.4); здесь результат метода ratio как объявляют, имеет тип float , и результат метода gausser как объявляют, имеет тип double
- Параметры конструктора (§8.8.1); здесь параметр конструктора для MiscMath как объявляют, имеет тип int
- Локальные переменные (§14.4, §14.13); локальные переменные r и val из метода gausser как объявляют, имеют типы Random и double[] (массив double )
- Параметры обработчика исключений (§14.19); здесь параметр обработчика исключений e из catch пункт, как объявляют, имеет тип Exception
- Создания экземпляра класса (§15.9); здесь локальная переменная r из метода gausser инициализируется выражением создания экземпляра класса, которое использует тип Random
- Создания массива (§15.10); здесь локальная переменная val из метода gausser инициализируется выражением создания массива, которое создает массив double с размером 2
- Броски (§15.16); здесь return оператор метода ratio использование float введите бросок
- instanceof оператор (§15.20.2); здесь instanceof оператор тестирует ли e присвоение, совместимое с типом ArithmeticException
4.5 Переменные
Переменная является местом хранения и имеет связанный тип, иногда называемый его типом времени компиляции, который является или типом примитива (§4.2) или ссылочным типом (§4.3). Переменная всегда содержит значение, которое является присвоением, совместимым (§5.2) с его типом. Значение переменной изменяется присвоением (§15.26) или префиксом или постфиксом ++ (инкремент) или — (декрементный) оператор (§15.14.1, §15.14.2, §15.15.1, §15.15.2).
Совместимость значения переменной с ее типом гарантируется проектом языка программирования Java. Значения по умолчанию являются совместимыми (§4.5.5), и все присвоения на переменную проверяются на совместимость присвоения (§5.2), обычно во время компиляции, но, в единственных массивах включения случая, проверка на этапе выполнения делается (§10.10).
4.5.1 Переменные Типа примитива
Переменная типа примитива всегда содержит значение того точного типа примитива.
4.5.2 Переменные Ссылочного типа
Переменная ссылочного типа может содержать любое из следующего:
- Нулевая ссылка
- Ссылка на любой объект (§4.3), чей класс (§4.5.6) является присвоением, совместимым (§5.2) с типом переменной
4.5.3 Виды Переменных
Есть семь видов переменных:
- Переменная класса является полем, объявил использование ключевого слова static в пределах объявления класса (§8.3.1.1), или с или без ключевого слова static в пределах интерфейсного объявления (§9.3). Переменная класса создается, когда ее класс или интерфейс готовятся (§12.3.2) и инициализируются к значению по умолчанию (§4.5.5). Переменная класса эффективно прекращает существование, когда ее класс или интерфейс разгружаются (§12.7).
- Переменная экземпляра является полем, объявленным в пределах объявления класса, не используя ключевое слово static (§8.3.1.1). Если у класса T есть поле a , которое является переменной экземпляра, то новая переменная экземпляра создаваемого и инициализированного к значению по умолчанию (§4.5.5) как часть каждого недавно создаваемого объекта класса T или любого класса, который является подклассом T(§8.1.3). Переменная экземпляра эффективно прекращает существование, когда, объектом которого это — поле, больше не ссылается, после любого необходимого завершения объекта был завершен (§12.6).
- Компоненты массива являются неназванными переменными, которые создаются и инициализируются к значениям по умолчанию (§4.5.5) всякий раз, когда новый объект, который является массивом, создается (§15.10). Компоненты массива эффективно прекращают существование, когда на массив больше не ссылаются. См. §10 для описания массивов.
- Параметры метода(§8.4.1) называют значения аргументов переданными к методу. Для каждого параметра, объявленного в объявлении метода, новая переменная параметра создается каждый раз, когда метод вызывается (§15.12). Новая переменная инициализируется с соответствующим значением аргумента от вызова метода. Параметр метода эффективно прекращает существование, когда выполнение тела метода полно.
- Параметры конструктора(§8.8.1) называют значения аргументов переданными конструктору. Для каждого параметра, объявленного в объявлении конструктора, новая переменная параметра создается каждый раз, когда выражение создания экземпляра класса (§15.9) или явный вызов конструктора (§8.8.5) вызывает того конструктора. Новая переменная инициализируется с соответствующим значением аргумента от выражения создания или вызова конструктора. Параметр конструктора эффективно прекращает существование, когда выполнение тела конструктора полно.
- Параметр обработчика исключений создается каждый раз, когда исключение поймано a catch пункт a try оператор (§14.19). Новая переменная инициализируется с фактическим объектом, связанным с исключением (§11.3, §14.17). Параметр обработчика исключений эффективно прекращает существование когда выполнение блока, связанного с catch пункт полон.
- Локальные переменные объявляются операторами объявления локальной переменной (§14.4). Всякий раз, когда поток управления вводит блок (§14.2) или for оператор (§14.13), новая переменная создается для каждой локальной переменной, объявленной в операторе объявления локальной переменной, сразу содержавшем в пределах того блока или for оператор. Оператор объявления локальной переменной может содержать выражение, которое инициализирует переменную. Локальная переменная с выражением инициализации не инициализируется, однако, до оператора объявления локальной переменной, который объявляет, что это выполняется. (Правила определенного присвоения (§16) препятствуют тому, чтобы значение локальной переменной использовалось прежде, чем это было инициализировано или иначе присвоило значение.) Локальная переменная эффективно прекращает существование когда выполнение блока или for оператор полон.
class Point < static int numPoints; // numPoints is a class variable int x, y; // x and y are instance variables int[] w = new int[10]; // w[0] is an array component int setX(int x) < // x is a method parameter int oldx = this.x; // oldx is a local variable this.x = x; return oldx; >>
4.5.4 заключительные Переменные
Переменная может быть объявлена final . Заключительная переменная может только быть присвоена однажды. Это — ошибка времени компиляции, если заключительная переменная присваивается тому, если это не является определенно неприсвоенным (§16) сразу до присвоения.
Пустой финал является заключительной переменной, объявление которой испытывает недостаток в инициализаторе.
Однажды a final переменная была присвоена, она всегда содержит то же самое значение. Если a final переменная содержит ссылку на объект, тогда состояние объекта может быть изменено операциями на объекте, но переменная будет всегда обращаться к тому же самому объекту. Это применяется также массивам, потому что массивы являются объектами; если a final переменная содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями на массиве, но переменная будет всегда обращаться к тому же самому массиву.
Объявление переменной final может служить полезной документацией, которую ее значение не будет изменять и может помочь избежать программировать ошибки.
class Point < int x, y; int useCount; Point(int x, int y) < this.x = x; this.y = y; >final static Point origin = new Point(0, 0); >
класс Point объявляет a final переменная класса origin . origin переменная содержит ссылку на объект, который является экземпляром класса Point чьи координаты (0, 0). Значение переменной Point.origin никогда не может изменяться, таким образом, это всегда обращается к тому же самому Point объект, тот создается его инициализатором. Однако, работа на этом Point объект мог бы изменить свое состояние например, изменяя его useCount или даже, обманчиво, x или y координата.
4.5.5 Начальные значения Переменных
У каждой переменной в программе должно быть значение прежде, чем его значение будет использоваться:
- Каждая переменная класса, переменная экземпляра, или компонент массива инициализируются со значением по умолчанию, когда это создается (§15.9, §15.10):
- Для типа byte , значение по умолчанию является нулем, то есть, значением (byte)0 .
- Для типа short , значение по умолчанию является нулем, то есть, значением (short)0 .
- Для типа int , значение по умолчанию является нулем, то есть, 0 .
- Для типа long , значение по умолчанию является нулем, то есть, 0L .
- Для типа float , значение по умолчанию является положительным нулем, то есть, 0.0f .
- Для типа double , значение по умолчанию является положительным нулем, то есть, 0.0d .
- Для типа char , значение по умолчанию является нулевым символом, то есть, ‘\u0000’ .
- Для типа boolean , значение по умолчанию false .
- Для всех ссылочных типов (§4.3), значение по умолчанию null .
class Point < static int npoints; int x, y; Point root; >class Test < public static void main(String[] args) < System.out.println("npoints=" + Point.npoints); Point p = new Point(); System.out.println("p.x=" + p.x + ", p.y=" + p.y); System.out.println("p.root=" + p.root); >>
печатные издания:
npoints=0 p.x=0, p.y=0 p.root=null
иллюстрирование инициализации по умолчанию npoints , который происходит когда класс Point готовится (§12.3.2), и инициализация по умолчанию x , y , и root , который происходит когда новое Point инстанцируется. См. §12 для полного описания всех аспектов загрузки, соединения, и инициализации классов и интерфейсов, плюс описание инстанцирования классов, чтобы сделать новые экземпляры класса.
4.5.6 Типы, Классы, и Интерфейсы
В языке программирования Java, каждой переменной и каждом выражении имеет тип, который может быть определен во время компиляции. Тип может быть типом примитива или ссылочным типом. Ссылочные типы включают типы классов и интерфейсные типы. Ссылочные типы представляются описаниями типа, которые включают объявления класса (§8.1) и интерфейсные объявления (§9.1). Мы часто используем термин тип, чтобы обратиться к классу или к интерфейсу.
Каждый объект принадлежит некоторому определенному классу: класс, который был упомянут в выражении создания, которое произвело объект, класс чей Class объект использовался, чтобы вызвать отражающий метод, чтобы произвести объект, или String класс для объектов неявно создается оператором конкатенации строк + (§15.18.1). Этот класс вызывают классом объекта. (У массивов также есть класс, как описано в конце этого раздела.) Объект, как говорят, является экземпляром своего класса и всех суперклассов его класса.
Иногда у переменной или выражения, как говорят, есть «тип времени выполнения». Это обращается к классу объекта, упомянутого значением переменной или выражения во время выполнения, предполагая, что значение не null .
Тип времени компиляции переменной всегда объявляется, и тип времени компиляции выражения может быть выведен во время компиляции. Тип времени компиляции ограничивает возможные значения, которые может содержать переменная, или выражение может произвести во время выполнения. Если значение времени выполнения является ссылкой, которая не является null , это обращается к объекту, или выстройте, у которого есть класс, и тот класс обязательно будет совместимым с типом времени компиляции.
Даже при том, что у переменной или выражения может быть тип времени компиляции, который является интерфейсным типом, нет никаких экземпляров интерфейсов. Переменная или выражение, тип которого является интерфейсным типом, могут сослаться на любой объект, класс которого реализует (§8.1.4) тот интерфейс.
Вот пример создания новых объектов и различия между типом переменной и классом объекта:
public interface Colorable < void setColor(byte r, byte g, byte b); >class Point < int x, y; >class ColoredPoint extends Point implements Colorable < byte r, g, b; public void setColor(byte rv, byte gv, byte bv) < r = rv; g = gv; b = bv; >> class Test < public static void main(String[] args) < Point p = new Point(); ColoredPoint cp = new ColoredPoint(); p = cp; Colorable c = cp; >>
В этом примере:
- Локальная переменная p из метода main из класса Test имеет тип Point и первоначально присваивается ссылка на новый экземпляр класса Point .
- Локальная переменная cp так же имеет как его тип ColoredPoint , и первоначально присваивается ссылка на новый экземпляр класса ColoredPoint .
- Присвоение значения cp к переменной p причины p содержать ссылку на a ColoredPoint объект. Это разрешается потому что ColoredPoint подкласс Point , так класс ColoredPoint присвоение, совместимое (§5.2) с типом Point . A ColoredPoint объект включает поддержку всех методов a Point . В дополнение к его определенным полям r , g , и b , у этого есть поля класса Point , а именно, x и y .
- Локальная переменная c имеет как его тип интерфейсный тип Colorable , таким образом, это может содержать ссылку на любой объект, класс которого реализует Colorable ; определенно, это может содержать ссылку на a ColoredPoint .
- Отметьте что выражение такой как» new Colorable() «не допустимо, потому что не возможно создать экземпляр интерфейса, только класса.
У классов для массивов есть странные имена, которые не являются допустимыми идентификаторами; например, класс для массива int у компонентов есть имя» [I «и так значение выражения:
new int[10].getClass().getName()
строка «[I» ; см. спецификацию Class.getName для деталей.
Содержание | Предыдущий | Следующий | Индекс Спецификация языка Java
Второй ВыпускАвторское право © Sun Microsystems, Inc 2000 года. Все права защищены
Пожалуйста, отправьте любые комментарии или исправления к jls@java.sun.comСсылочные типы данных — Java: Введение в ООП
Классы в Java особым образом связаны с типами данных. Посмотрите на пример:
var user = new User("Danil", "Miloshin");
Каким будет реальный тип в данном случае? Классы, сами по себе, ведут себя как типы. Поэтому типом переменной user будет User , то есть так:
User user = new User("Danil", "Miloshin");
В Java все типы данных делятся на две категории: примитивные и ссылочные. К примитивным относятся все виды чисел, символы и логический тип данных (булеан). К ссылочным — классы, массивы, строки. В зависимости от категории, значительно меняется поведение кода и об этом нужно знать. В этом уроке мы разберем отличия между этими категориями и научимся правильно с ними работать.
Для изучения нам понадобится пример какого-то класса, чьи объекты мы используем в примерах кода. Возьмем для простоты класс User с двумя полями и одним конструктором:
class User public String firstName; public String lastName; public User(String firstName, String lastName) this.firstName = firstName; this.lastName = lastName; > >
Значение по умолчанию
Примитивные данные всегда имеют значение, даже если они определяются без инициализации:
int a; System.out.println(a); // => 0
У ссылочных в качестве значения по умолчанию используется null . Это специальное значение, которое может быть использовано в качестве любого объекта
User u; System.out.println(u); // => null // Можно присваивать и явно // User u = null;
Присваивание
Примитивное значение всегда копируется при присваивании:
// Содержимое a и b не связаны var a = 5; var b = a;
Ссылочные же данные не копируются. При присваивании переменные начинают указывать (ссылаться) на один и тот же объект:
var u1 = new User("Igor", "Mon"); // В реальности это один и тот же пользователь var u2 = u1; u2.firstName = "Nina"; System.out.println(u1.firstName); // => "Nina" u1 == u2; // true // Вот теперь тут другой пользователь u2 = new User("Igor", "Mon");
Больше всего это проявляется при передаче данных в методы и их возврате оттуда. Ссылочное значение передается по ссылке, а значит его можно изменить изнутри метода.
class UserController // Ничего не нужно возвращать, потому что пользователь будет изменен напрямую public static void replaceName(User user, String newFirstName) user.firstName = newFirstName; > > var u = new User("Igor", "Mon"); UserController.replaceName(u, "Nina"); System.out.println(u.firstName); // => "Nina"
Сравнение
Примитивные данные сравниваются по значению. Пять всегда равно пяти, истина всегда равна истине:
var a = 5; var b = 5; a == b; // true var t1 = true; var t2 = true; t1 == t2; // true
Ссылочные сравниваются по ссылкам, а не по содержимому. Объекты равны только сами себе. То что хранится внутри них — не важно.
var u1 = new User("Igor", "Mon"); var u2 = new User("Igor", "Mon"); // Возможно вас это удивит, но сравнение объектов не учитывает содержимое
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях: