Java dərsi
Здесь мы используем аннотацию в определении локальной переменной. Обработчик аннотаций времени компиляции теперь может читать аннотацию @NotNull и выдавать ошибку, если переменной присваивается значение NULL.
Java Language
операторы
Операторы на языке программирования Java – это специальные символы, которые выполняют определенные операции над одним, двумя или тремя операндами, а затем возвращают результат.
замечания
Оператор – это символ (или символы), который сообщает программе Java выполнить операцию над одним, двумя или тремя операндами . Оператор и его операнды образуют выражение (см. Раздел «Выражения»). Операнды оператора сами являются выражениями.
В этом разделе описываются 40 или около того различных операторов, определенных Java. В разделе «Отдельные выражения» объясняется:
- как операторы, операнды и другие вещи объединяются в выражения,
- как оцениваются выражения, и
- как работают выражения, преобразования и выражения.
Оператор конкатенации строк (+)
Символ + может означать три разных оператора в Java:
- Если нет операнда до + , то это унарный оператор Plus.
- Если есть два операнда, они оба являются числовыми. то он является двоичным оператором сложения.
- Если есть два операнда, и по крайней мере один из них является String , то он является двоичным оператором Concatenation.
В простом случае оператор Concatenation соединяет две строки, чтобы дать третью строку. Например:
String s1 = "a String"; String s2 = "This is " + s1; // s2 contains "This is a String"
Если один из двух операндов не является строкой, он преобразуется в String следующим образом:
- Операнд, тип которого является примитивным типом, преобразуется, как если бы он вызывал toString() по размеру в штучной упаковке.
- Операнд, тип которого является ссылочным типом, преобразуется путем вызова метода toString() операнда. Если операнд имеет значение null , или если метод toString() возвращает значение null , вместо него используется строковый литерал “null” .
Объяснение примера s5 заключается в том, что метод toString() для типов массивов наследуется из java.lang.Object , и поведение заключается в создании строки, состоящей из имени типа и идентификатора hashcode объекта.
Оператор Concatenation задается для создания нового объекта String , за исключением случая, когда выражение является константным выражением. В последнем случае выражение оценивается по типу компиляции, а его значение времени выполнения эквивалентно строковому литералу. Это означает, что для разделения длинного строкового литерала нет накладных расходов во время выполнения:
String typing = "The quick brown fox " + "jumped over the " + "lazy dog"; // constant expression
Оптимизация и эффективность
Как отмечено выше, за исключением постоянных выражений, каждое выражение конкатенации строк создает новый объект String . Рассмотрим этот код:
public String stars(int count) < String res = ""; for (int i = 0; i < count; i++) < res = res + "*"; >return res; >
В вышеприведенном методе каждая итерация цикла создаст новую String которая будет на один символ длиннее предыдущей итерации. Каждая конкатенация копирует все символы в строках операндов, чтобы сформировать новую String . Таким образом, stars(N) будут:
- создать N новых объектов String и выбросить все, кроме последнего,
- копировать N * (N + 1) / 2 символа и
- сгенерировать O(N^2) байты мусора.
Это очень дорого для больших N Действительно, любой код, который объединяет строки в цикле, может иметь эту проблему. Лучший способ написать это будет следующим:
public String stars(int count) < // Create a string builder with capacity 'count' StringBuilder sb = new StringBuilder(count); for (int i = 0; i < count; i++) < sb.append("*"); >return sb.toString(); >
В идеале вы должны установить емкость StringBuilder , но если это нецелесообразно, класс автоматически вырастет массив поддержки, который строитель использует для хранения символов. (Примечание: реализация расширяет базовый массив экспоненциально. Эта стратегия сохраняет количество копий символов в O(N) а не O(N^2) .)
Некоторые люди применяют этот шаблон для всех конкатенаций строк. Однако это не нужно, поскольку JLS позволяет компилятору Java оптимизировать конкатенации строк в одном выражении. Например:
String s1 = . ; String s2 = . ; String test = "Hello " + s1 + ". Welcome to " + s2 + "\n";
как правило , оптимизируется компилятором байт-кода на что-то вроде этого;
StringBuilder tmp = new StringBuilder(); tmp.append("Hello ") tmp.append(s1 == null ? "null" + s1); tmp.append("Welcome to "); tmp.append(s2 == null ? "null" + s2); tmp.append("\n"); String test = tmp.toString();
(Компилятор JIT может оптимизировать это, если он сможет определить, что s1 или s2 не может быть null .) Но обратите внимание, что эта оптимизация разрешена только в одном выражении.
Короче говоря, если вас беспокоит эффективность конкатенаций строк:
- Ручная оптимизация, если вы повторяете конкатенацию в цикле (или аналогичном).
- Не ручная оптимизация одного выражения конкатенации.
Арифметические операторы (+, -, *, /,%)
Язык Java предоставляет 7 операторов, выполняющих арифметические операции с целыми и плавающими значениями.
- Есть два + операторов:
- Оператор двоичного сложения добавляет одно число к другому. (Существует также двоичный + оператор, который выполняет конкатенацию строк. Это описано в отдельном примере.)
- Оператор унарного плюса ничего не делает, кроме запуска числового продвижения (см. Ниже)
- Оператор двоичного вычитания вычитает одно число из другого.
- Унарный минус-оператор эквивалентен вычитанию его операнда с нуля.
1. Это часто неправильно называют оператором «модуль». «Остаток» – это термин, который используется JLS. «Модуль» и «остаток» – это не одно и то же.
Операнд и типы результатов, а также числовое продвижение
Операторы требуют числовых операндов и получают числовые результаты. Типы операндов могут быть любыми примитивными числовыми типами (то есть byte , short , char , int , long , float или double ) или любым числовым типом оболочки, определяемым в java.lang ; т.е. ( Byte , Character , Short , Integer , Long , Float или Double .
Тип результата определяется базой по типам операнда или операндов следующим образом:
- Если любой из операндов является double или Double , тогда тип результата будет double .
- В противном случае, если любой из операндов является float или Float , тогда тип результата будет float .
- В противном случае, если любой из операндов long или Long , тогда тип результата long .
- В противном случае тип результата – int . Это охватывает byte , short и char операнды, а также `int.
Тип результата операции определяет, как выполняется арифметическая операция, и как обрабатываются операнды
- Если тип результата double , операнды повышаются до double , а операция выполняется с использованием 64-битной (двойной точности двоичной) IEE 754 с плавающей запятой.
- Если тип результата является float , операнды продвигаются на float , и операция выполняется с использованием 32-битной (одиночной точности) IEE 754 с плавающей точкой арифметики.
- Если тип результата long , операнды продвигаются до long , и операция выполняется с использованием битовой целочисленной арифметики двоичного кода с двоичным кодом, состоящей из 64 бит.
- Если типом результата является int , операнды продвигаются до int , а операция выполняется с использованием 32-разрядной двоичной двоичной двоичной арифметики двоичного кода.
Продвижение осуществляется в два этапа:
- Если тип операнда является типом-оболочкой, значение операнда будет опущено на значение соответствующего примитивного типа.
- При необходимости примитивный тип продвигается до требуемого типа:
- Продвижение целых чисел в int или long потерь.
- Продвижение float в double потерь.
- Продвижение целого числа в значение с плавающей запятой может привести к потере точности. Преобразование выполняется с использованием семантики «круг-к-ближайшему» IEE 768.
Смысл разделения
Оператор / делит левый операнд n ( дивиденд ) и правый операнд d ( делитель ) и выдает результат q ( фактор ).
Явное целочисленное деление округляется до нуля. В разделе JLS 15.17.2 указано поведение целочисленного деления Java следующим образом:
Фактор, созданный для операндов n и d представляет собой целое значение q , величина которого как можно больше, при условии, что |d ⋅ q| ≤ |n| , Более того, q положительно, когда |n| ≥ |d| и n и d имеют один и тот же знак, но q отрицателен при |n| ≥ |d| и n и d имеют противоположные знаки.
Есть несколько особых случаев:
- Если n – MIN_VALUE , а делитель равен -1, то происходит переполнение целого числа, а результат – MIN_VALUE . Никакое исключение не возникает в этом случае.
- Если d равно 0, то генерируется «ArithmeticException».
Разделение с плавающей запятой Java требует рассмотрения более кратных случаев. Однако основная идея состоит в том, что результат q является значением, наиболее близким к удовлетворяющему d . q = n .
Разделение с плавающей точкой никогда не приведет к исключению. Вместо этого операции, делящиеся на ноль, приводят к значениям INF и NaN; увидеть ниже.
Значение остатка
В отличие от C и C ++, оператор остатка в Java работает как с целыми, так и с плавающей запятой.
Для целых случаев результат a % b определяется как число r такое, что (a / b) * b + r равно a , где / , * и + – соответствующие Java-целые операторы. Это применяется во всех случаях, кроме случаев, когда b равно нулю. В этом случае остаток приводит к ArithmeticException .
Из приведенного выше определения следует, что a % b может быть отрицательным, только если a отрицательно, и оно положительно, только если a положительно. Более того, величина a % b всегда меньше величины b .
Операция останова с плавающей запятой является обобщением целочисленного случая. Результат a % b – остаток r определяется математическим соотношением r = a – (b ⋅ q) где:
- q – целое число,
- это отрицательно, только если a / b отрицательно положительно, только если a / b положительно и
- его величина как можно больше, не превышающая величину истинного математического отношения a и b .
Остаток с плавающей точкой может производить значения INF и NaN в крайних случаях, например, когда b равно нулю; увидеть ниже. Это исключение не будет.
Результат операции останова с плавающей запятой, вычисляемой по % , не совпадает с результатом операции остатка, определенной IEEE 754. Остаток IEEE 754 может быть вычислен с использованием библиотечного метода Math.IEEEremainder .
Целочисленное переполнение
Значения целых чисел на Java 32 и 64 бит подписаны и используют двоичное представление двоичного кода. Например, диапазон чисел, представляемых как (32 бит) int -2 31 – +2 31 – 1.
Когда вы добавляете, вычитаете или несколько двух N битовых целых чисел (N == 32 или 64), результат операции может быть слишком большим для представления в виде N битового целого. В этом случае операция приводит к целочисленному переполнению , и результат может быть вычислен следующим образом:
- Математическая операция выполняется, чтобы дать промежуточное представление с двумя дополнениями всего числа. Это представление будет больше, чем N бит.
- В результате используются нижние 32 или 64 бита промежуточного представления.
Следует отметить, что целочисленное переполнение не приводит к исключениям ни при каких обстоятельствах.
Значения INF и NAN с плавающей запятой
Java использует представления с плавающей точкой IEE 754 для float и double . Эти представления имеют некоторые специальные значения для представления значений, которые выходят за пределы области действительных чисел:
- Значения «бесконечный» или «INF» означают слишком большие числа. Значение +INF обозначает слишком большие и положительные числа. Значение -INF обозначает слишком большие и отрицательные числа.
- «Неопределенный» / «не число» или NaN обозначают значения, возникающие в результате бессмысленных операций.
Значения INF производятся плавающими операциями, которые вызывают переполнение или делением на ноль.
Значения NaN производятся путем деления нуля на ноль или вычисления нуля нуля.
Удивительно, но можно выполнить арифметику с использованием операндов INF и NaN без возникновения исключений. Например:
- Добавление + INF и конечное значение дает + INF.
- Добавление + INF и + INF дает + INF.
- Добавление + INF и -INF дает NaN.
- Разделение по INF дает либо +0.0, либо -0.0.
- Все операции с одним или несколькими операндами NaN дают NaN.
Для получения полной информации см. Соответствующие подразделы JLS 15 . Обратите внимание, что это в значительной степени «академическое». Для типичных вычислений INF или NaN означает, что что-то пошло не так; например, у вас есть неполные или неправильные входные данные, или расчет был запрограммирован неправильно.
Операторы равенства (==,! =)
Операторы == и != – это двоичные операторы, которые оценивают true или false зависимости от того, являются ли операнды равными. Оператор == задает значение true если операнды равны и false противном случае. Оператор != Дает false если операнды равны и true противном случае.
Этими операторами могут быть использованы операнды с примитивными и ссылочными типами, но поведение существенно отличается. Согласно JLS, на самом деле существует три различных набора этих операторов:
- Операторы Boolean == и != .
- Операторы Numeric == и != .
- Операторы Reference == и != .
Однако во всех случаях тип результата операторов == и != Является boolean .
Операторы Numeric == и !=
Когда один (или оба) операндов оператора == или != Является примитивным числовым типом ( byte , short , char , int, long , float или double ), оператор представляет собой числовое сравнение. Второй операнд должен быть либо примитивным числовым типом, либо коробочным числовым типом.
Поведение других числовых операторов выглядит следующим образом:
- Если один из операндов является коробочным, он распаковывается.
- Если любой из операндов теперь является byte , short или char , он продвигается до int .
- Если типы операндов не совпадают, то операнд с «меньшим» типом продвигается к «более крупному» типу.
- Затем сравнение проводится следующим образом:
- Если продвинутые операнды являются int или long тогда значения тестируются, чтобы убедиться, что они идентичны.
- Если продвинутые операнды являются float или double тогда:
- две версии нуля ( +0.0 и -0.0 ) считаются равными
- значение NaN рассматривается как не равное чему-либо, и
- другие значения равны, если их представления IEEE 754 идентичны.
Примечание: вам нужно быть осторожным при использовании == и != Для сравнения значений с плавающей запятой.
Операторы Boolean == и !=
Если оба операнда являются boolean , или один является boolean а другой Boolean , эти операторы являются операторами Boolean == и != . Поведение выглядит следующим образом:
- Если один из операндов является Boolean , он распаковывается.
- Проверяются незанятые операнды и вычисляется логический результат в соответствии со следующей таблицей истинности
Есть две «подводные камни», которые позволяют использовать == и != Экономно с значениями истинности:
- Если вы используете == или != Для сравнения двух Boolean объектов, то используются операторы Reference. Это может дать неожиданный результат; см. Pitfall: использование == для сравнения объектов примитивных оберток, таких как Integer
- Оператор == может быть легко ошибочен как = . Для большинства типов операндов эта ошибка приводит к ошибке компиляции. Однако для boolean и Boolean операндов ошибка приводит к неправильному поведению во время выполнения; см. Pitfall – использование ‘==’ для проверки логического
Операторы Reference == и !=
Если оба операнда являются объектными ссылками, операторы == и != Проверяют, относятся ли эти два операнда к одному и тому же объекту . Это часто не то, что вы хотите. Чтобы проверить, являются ли два объекта равными по значению , вместо этого следует использовать метод .equals() .
String s1 = "We are equal"; String s2 = new String("We are equal"); s1.equals(s2); // true // WARNING - don't use == or != with String values s1 == s2; // falseО краях NaN
Если либо операнд NaN , то результат == является false но результат != true . Действительно, тест x != x true тогда и только тогда, когда значение x равно NaN .
Такое поведение (для большинства программистов) неожиданно. Если вы проверите, соответствует ли значение NaN самому себе, ответ будет «Нет, это не так!». Другими словами, == не рефлексивно для значений NaN .
Однако это не «странность» Java, это поведение указано в стандартах с плавающей точкой IEEE 754, и вы обнаружите, что оно реализовано большинством современных языков программирования. (Для получения дополнительной информации см. Http://stackoverflow.com/a/1573715/139985 . отметив, что это написано кем-то, кто был «в комнате, когда были приняты решения»!)
Операторы Increment / Decrement (++ / -)
Переменные могут быть увеличены или уменьшены на 1 с помощью операторов ++ и — соответственно.
Когда операторы ++ и — следуют за переменными, они называются post-increment и post-декремент соответственно.
int a = 10; a++; // a now equals 11 a--; // a now equals 10 againКогда операторы ++ и — предшествуют переменным, операции называются пред-приращениями и пред-декрементами соответственно.
int x = 10; --x; // x now equals 9 ++x; // x now equals 10Если оператор предшествует переменной, значение выражения будет значением переменной после того, как будет увеличиваться или уменьшаться. Если оператор следует за переменной, значение выражения будет значением переменной до того, как будет увеличиваться или уменьшаться.
int x=10; System.out.println("x=" + x + " x=" + x++ + " x=" + x); // outputs x=10 x=10 x=11 System.out.println("x=" + x + " x=" + ++x + " x=" + x); // outputs x=11 x=12 x=12 System.out.println("x=" + x + " x=" + x-- + " x=" + x); // outputs x=12 x=12 x=11 System.out.println("x=" + x + " x=" + --x + " x=" + x); // outputs x=11 x=10 x=10Будьте осторожны, чтобы не перезаписывать пост-приращения или декременты. Это происходит, если вы используете оператор post-in / decment в конце выражения, которое переназначается самой переменной in / decmented. Значение in / decment не будет иметь эффекта. Несмотря на то, что переменная с левой стороны увеличивается правильно, ее значение будет немедленно перезаписано ранее оцененным результатом с правой стороны выражения:
int x = 0; x = x++ + 1 + x++; // x = 0 + 1 + 1 // do not do this - the last increment has no effect (bug!) System.out.println(x); // prints 2 (not 3!)int x = 0; x = x++ + 1 + x; // evaluates to x = 0 + 1 + 1 x++; // adds 1 System.out.println(x); // prints 3Условный оператор (? 🙂
Синтаксис
? :
Как показано в синтаксисе, Условный оператор (также известный как Тернарный оператор 1 ) использует ? (знак вопроса) и : (двоеточие), чтобы разрешить условное выражение двух возможных результатов. Он может использоваться для замены более длинных блоков if-else для возврата одного из двух значений на основе условия.
result = testCondition ? value1 : value2if (testCondition) < result = value1; >else
Его можно прочитать как «Если testCondition истинно, установите результат в value1; в противном случае установите результат в значение2 “.
// get absolute value using conditional operator a = -10; int absValue = a < 0 ? -a : a; System.out.println("abs = " + absValue); // prints "abs = 10"// get absolute value using if/else loop a = -10; int absValue; if (a < 0) < absValue = -a; >else < absValue = a; >System.out.println("abs = " + absValue); // prints "abs = 10"Общее использование
Вы можете использовать условный оператор для условных присвоений (например, для проверки нуля).
String x = y != null ? y.toString() : ""; //where y is an objectЭтот пример эквивалентен:
String x = ""; if (y != null)
Поскольку Условный оператор имеет второе наименьшее приоритетное значение, выше Операторов присваивания , редко требуется использовать скобки вокруг условия , но скобки требуются вокруг всей конструкции условного оператора в сочетании с другими операторами:
// no parenthesis needed for expressions in the 3 parts 10 0 ? 2 : 5)Условные операторы вложения также могут выполняться в третьей части, где она больше похожа на цепочку или как оператор switch.
a ? "a is true" : b ? "a is false, b is true" : c ? "a and b are false, c is true" : "a, b, and c are false" //Operator precedence can be illustrated with parenthesis: a ? x : (b ? y : (c ? z : w))1 - Как Java Language Specification, так и Java Tutorial вызывают оператор ( ? : 🙂 Условный оператор . В учебнике говорится, что он также известен как Тернарный оператор, поскольку он (в настоящее время) является единственным тройным оператором, определенным Java. Терминология «Условный оператор» согласуется с C и C ++ и другими языками с эквивалентным оператором.
Побитовые и логические операторы (~, &, |, ^)
Язык Java предоставляет 4 оператора, которые выполняют побитовые или логические операции с целыми или булевыми операндами.
- Оператор дополнения ( ~ ) является унарным оператором, который выполняет поразрядное или логическое обращение битов одного операнда; см. JLS 15.15.5. ,
- Оператор AND ( & ) является двоичным оператором, который выполняет побитовое или логическое «и» из двух операндов; см. JLS 15.22.2. ,
- Оператор OR ( | ) является двоичным оператором, который выполняет побитовое или логическое «включение» или «из двух операндов»; см. JLS 15.22.2. ,
- Оператор XOR ( ^ ) является двоичным оператором, который выполняет побитовое или логическое «исключение» или «из двух операндов»; см. JLS 15.22.2. ,
Логические операции, выполняемые этими операторами, когда операнды являются логическими, можно суммировать следующим образом:
В ~ A A & B A | В A ^ B 0 0 1 0 0 0 0 1 1 0 1 1 1 0 0 0 1 1 1 1 0 1 1 0 Обратите внимание, что для целых операндов приведенная выше таблица описывает, что происходит для отдельных бит. Операторы фактически работают со всеми 32 или 64 битами операнда или операндов параллельно.
Типы операндов и типы результатов.
Обычные арифметические преобразования применяются, когда операнды являются целыми числами. Обычные прецеденты для побитовых операторов
Оператор ~ используется для изменения логического значения или изменения всех битов в целочисленном операнде.
Оператор & используется для «маскировки» некоторых битов целочисленного операнда. Например:
int word = 0b00101010; int mask = 0b00000011; // Mask for masking out all but the bottom // two bits of a word int lowBits = word & mask; // -> 0b00000010 int highBits = word & ~mask; // -> 0b00101000| оператор используется для объединения значений истинности двух операндов. Например:
int word2 = 0b01011111; // Combine the bottom 2 bits of word1 with the top 30 bits of word2 int combined = (word & mask) | (word2 & ~mask); // -> 0b01011110Оператор ^ используется для переключения или «переворачивания» битов:
int word3 = 0b00101010; int word4 = word3 ^ mask; // -> 0b00101001Дополнительные примеры использования побитовых операторов см. В разделе « Манипуляция бит»
Оператор экземпляра
Этот оператор проверяет, имеет ли объект определенный тип класса / интерфейса. Оператор instanceof записывается как:
( Object reference variable ) instanceof (class/interface type)public class Test < public static void main(String args[])< String name = "Buyya"; // following will return true since name is type of String boolean result = name instanceof String; System.out.println( result ); >>Это приведет к следующему результату:
trueЭтот оператор по-прежнему будет возвращать true, если сравниваемый объект - это присвоение, совместимое с типом справа.
class Vehicle <> public class Car extends Vehicle < public static void main(String args[])< Vehicle a = new Car(); boolean result = a instanceof Car; System.out.println( result ); >>Это приведет к следующему результату:
trueОператоры присваивания (=, + =, - =, * =, / =,% =, > =, >>> =, & =, | = и ^ =)
Левый операнд для этих операторов должен быть либо не конечной переменной, либо элементом массива. Правый операнд должен быть совместимым с левым операндом. Это означает, что либо типы должны быть одинаковыми, либо правильный тип операнда должен быть конвертирован в тип левых операндов комбинацией бокса, распаковки или расширения. (Подробнее см. JLS 5.2 .)
Точное значение операторов «операция и назначение» указано в JLS 15.26.2 следующим образом:
Составляющее выражение присваивания формы E1 op= E2 эквивалентно E1 = (T) ((E1) op (E2)) , где T - тип E1 , за исключением того, что E1 оценивается только один раз.
Обратите внимание, что перед окончательным присваиванием подразумевается неявный тип.
1. =
Простой оператор присваивания: присваивает значение правого операнда левому операнду.
Пример: c = a + b добавит значение a + b к значению c и назначит его c
2. +=
Оператор «добавить и присваивать»: добавляет значение правого операнда к значению левого операнда и присваивает результат левому операнду. Если левый операнд имеет тип String , то это оператор «конкатенировать и присваивать».
Пример: c += a примерно такой же, как c = c + a
3. -=
Оператор «вычесть и присваивать»: вычитает значение правого операнда из значения левого операнда и присваивает результат левому операнду.
Пример: c -= a примерно совпадает с c = c - a
4. *=
Оператор «умножить и присваивать»: умножает значение правого операнда на значение левого операнда и присваивает результат левому операнду. ,
Пример: c *= a примерно совпадает с c = c * a
5. /=
Оператор «делить и присваивать»: делит значение правого операнда на значение левого операнда и присваивает результат левому операнду.
Пример: c /*= a примерно совпадает с c = c / a
6. %=
Оператор «модуль и назначение» вычисляет модуль значения правого операнда по значению левого операнда и присваивает результат левому операнду.
Пример: c %*= a примерно совпадает с c = c % a
Оператор «сдвиг влево и назначение».
8. >>=
Оператор «арифметический сдвиг вправо и назначение».
Пример: c >>= 2 примерно совпадает с c = c >> 2
9. >>>=
Оператор «Логический сдвиг вправо и назначение».
Пример: c >>>= 2 примерно совпадает с c = c >>> 2
10. &=
Оператор «побитовой и назначающий».
Пример: c &= 2 примерно совпадает с c = c & 2
11. |=
Оператор «побитовое или назначить».
Пример: c |= 2 примерно совпадает с c = c | 2
12. ^=
Оператор «побитовое исключение и присваивание».
Пример: c ^= 2 примерно такой же, как c = c ^ 2
Условные и условные операторы (&& и ||)
Java предоставляет условный и условный или оператор, которые берут один или два операнда типа boolean и создают boolean результат. Это:
- && - оператор условного-И,
- || - операторы условного ИЛИ. Оценка && эквивалентна следующему псевдокоду:
< boolean L = evaluate(); if (L) < return evaluate(); > else < // short-circuit the evaluation of the 2nd operand expression return false; >>Оценка || эквивалентен следующему псевдокоду:
< boolean L = evaluate(); if (!L) < return evaluate(); > else < // short-circuit the evaluation of the 2nd operand expression return true; >>Как видно из приведенного выше псевдокода, поведение операторов короткого замыкания эквивалентно использованию операторов if / else .
Пример - использование && в качестве защиты в выражении
В следующем примере показан наиболее распространенный шаблон использования для оператора && . Сравните эти две версии метода, чтобы проверить, является ли поставленное Integer равным нулю.
public boolean isZero(Integer value) < return value == 0; >public boolean isZero(Integer value)
Первая версия работает в большинстве случаев, но если аргумент value имеет value null , тогда будет NullPointerException .
Во второй версии мы добавили тест «guard». Выражение value != null && value == 0 оценивается, сначала выполняя value != null test. Если null тест завершается успешно (т.е. он оценивается как true ), тогда вычисляется выражение value == 0 . Если null тест терпит неудачу, то оценка value == 0 пропущена (закорочена), и мы не получаем NullPointerException .
Пример. Используя &&, чтобы избежать дорогостоящего расчета
В следующем примере показано, как && можно использовать, чтобы избежать относительно дорогостоящего расчета:
public boolean verify(int value, boolean needPrime) < return !needPrime | isPrime(value); >public boolean verify(int value, boolean needPrime)
В первой версии оба операнда | всегда будет оцениваться, поэтому (дорогостоящий) метод isPrime будет вызван без необходимости. Вторая версия позволяет избежать ненужного вызова, используя || вместо | ,
Операторы сдвига (> и >>>)
Язык Java предоставляет три оператора для выполнения поразрядного смещения по 32 и 64-битным целым значениям. Это все двоичные операторы, первый операнд которых является смещаемым значением, а второй операнд говорит, как далеко сдвинуться.
- Оператор левого сдвига сдвигает значение, заданное первым операндом влево , на число битных позиций, заданных вторым операндом. Пустые позиции на правом конце заполняются нулями.
- Оператор «>>» или арифметического сдвига сдвигает значение, заданное первым операндом, вправо , на количество битных позиций, заданных вторым операндом. Пустые позиции на левом конце заполняются копированием самого левого разряда. Этот процесс известен как расширение знака .
- Оператор «>>>» или логического сдвига сдвигает значение, заданное первым операндом, вправо на число битных позиций, заданных вторым операндом. Пустые позиции в левом конце заполняются нулями.
- Этим операторам требуется значение int или long в качестве первого операнда и выдает значение с тем же типом, что и первый операнд. (Вам нужно будет использовать явный тип приведения при назначении результата сдвига byte , short или переменной char .)
- Если вы используете оператор сдвига с первым операндом, который является byte , char или short , он продвигается до int а операция производит int .)
- Второй операнд уменьшается по модулю количества бит операции, чтобы дать величину сдвига. Подробнее о математической концепции мод см. Примеры модулей .
- Биты, сдвинутые с левого или правого конца с помощью операции, отбрасываются. (Java не предоставляет примитивный оператор «rotate».)
- Оператор арифметического сдвига эквивалентен делению числа (двух) дополнений на мощность 2.
- Оператор сдвига влево эквивалентен, умножая число (2) дополнител на 2.
Следующая таблица поможет вам увидеть эффекты трех операторов сдвига. (Числа были выражены в двоичной нотации, чтобы помочь визуализации.)
Operand1 operand2 >> >>> 0b0000000000001011 0 0b0000000000001011 0b0000000000001011 0b0000000000001011 0b0000000000001011 1 0b0000000000010110 0b0000000000000101 0b0000000000000101 0b0000000000001011 2 0b0000000000101100 0b0000000000000010 0b0000000000000010 0b0000000000001011 28 0b1011000000000000 0b0000000000000000 0b0000000000000000 0b0000000000001011 31 0b1000000000000000 0b0000000000000000 0b0000000000000000 0b0000000000001011 32 0b0000000000001011 0b0000000000001011 0b0000000000001011 . . . . . 0b1000000000001011 0 0b1000000000001011 0b1000000000001011 0b1000000000001011 0b1000000000001011 1 0b0000000000010110 0b1100000000000101 0b0100000000000101 0b1000000000001011 2 0b0000000000101100 0b1110000000000010 0b00100000000000100 0b1000000000001011 31 0b1000000000000000 0b1111111111111111 0b0000000000000001 Там приведены примеры пользователя операторов сдвига в манипуляции бит
Оператор Лямбды (->)
Начиная с Java 8, оператор лямбды ( -> ) является оператором, используемым для введения выражения Лямбды. Существуют два распространенных синтаксиса, которые иллюстрируются следующими примерами:
a -> a + 1 // a lambda that adds one to its argument a -> < return a + 1; >// an equivalent lambda using a block.Выражение лямбда определяет анонимную функцию или, вернее, экземпляр анонимного класса, который реализует функциональный интерфейс .
(Этот пример включен здесь для полноты. См. Раздел « Лямбда-выражения» для полного лечения.)
Реляционные операторы (,> =)
Операторы < , и >= являются двоичными операторами для сравнения числовых типов. Смысл операторов, как и следовало ожидать. Например, если a и b объявлены как byte , short , char , int , long , float , double или соответствующие типы в коробке:
- `a < b` tests if the value of `a` is less than the value of `b`. - `a b` tests if the value of `a` is greater than the value of `b`. - `a >= b` tests if the value of `a` is greater than or equal to the value of `b`.Тип результата для этих операторов boolean во всех случаях.
Операторы отношения могут использоваться для сравнения чисел с разными типами. Например:
int i = 1; long l = 2; if (i
Операторы отношения могут использоваться, когда оба или оба числа являются экземплярами числовых типов в штучной упаковке. Например:
Integer i = 1; // 1 is autoboxed to an Integer Integer j = 2; // 2 is autoboxed to an Integer if (i
Точное поведение суммируется следующим образом:
- Если один из операндов является коробочным, он распаковывается.
- Если любой из операндов теперь является byte , short или char , он продвигается до int .
- Если типы операндов не совпадают, то операнд с «меньшим» типом продвигается к «более крупному» типу.
- Сравнение выполняется по итоговым значениям int , long , float или double .
Вы должны быть осторожны с реляционными сравнениями, которые включают числа с плавающей запятой:
- Выражения, которые вычисляют числа с плавающей запятой, часто приводят к ошибкам округления из-за того, что представления с плавающей запятой компьютера имеют ограниченную точность.
- При сравнении целочисленного типа и типа с плавающей точкой преобразование целочисленного числа в плавающую точку также может приводить к ошибкам округления.
Наконец, Java немного поддерживает использование реляционных операторов с любыми типами, отличными от перечисленных выше. Например, вы не можете использовать эти операторы для сравнения строк, массивов чисел и т. Д.
Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow- Начало работы с Java Language
- 2D-графика в Java
- Apache Commons Lang
- API Reflection
- API стека
- AppDynamics и TIBCO BusinessWorks для легкой интеграции
- Autoboxing
- BigDecimal
- BigInteger
- BufferedWriter
- ByteBuffer
- CompletableFuture
- Enum, начиная с номера
- FileUpload для AWS
- FTP (протокол передачи файлов)
- HttpURLConnection
- InputStreams и OutputStreams
- Java Pitfalls - использование исключений
- Java Pitfalls - синтаксис языка
- JavaBean
- Java-агенты
- Java-версии, версии, выпуски и дистрибутивы
- JAXB
- JAX-WS
- JMX
- JNDI
- JShell
- JSON в Java
- LinkedHashMap
- log4j / log4j2
- NIO - Сеть
- NumberFormat
- ServiceLoader
- SortedMap
- Streams
- StringBuffer
- StringBuilder
- sun.misc.Unsafe
- ThreadLocal
- TreeMap и TreeSet
- Varargs (переменный аргумент)
- WeakHashMap
- XJC
- XOM - Объектная модель XML
- Альтернативные коллекции
- Анализ XML с использованием API JAXP
- Аннотации
- Апплеты
- Атомные типы
- аудио
- Безопасность и криптография
- Безопасность и криптография
- Бит-манипуляция
- Валюта и деньги
- Ведение журнала (java.util.logging)
- Видимость (контроль доступа к членам класса)
- Виртуальная машина Java (JVM)
- Виртуальный доступ Java
- Вложенные и внутренние классы
- Возможности Java SE 7
- Возможности Java SE 8
- Выбор коллекций
- Выражения
- Генерация случайных чисел
- Геттеры и сеттеры
- Даты и время (java.time. *)
- Двигатель JavaScript Nashorn
- Дженерики
- Документирование кода Java
- Загрузчики классов
- Защищенные объекты
- Изменение байтового кода
- Инкапсуляция
- Интерфейс Dequeue
- Интерфейс Java Native
- Интерфейс инструмента JVM
- Интерфейсы
- Исключения и обработка исключений
- Исполнители, Исполнительные службы и пулы потоков
- Использование ThreadPoolExecutor в приложениях MultiThreaded.
- Использование других языков сценариев в Java
- Использование ключевого слова static
- Итератор и Итерабель
- Календарь и его подклассы
- Карта Enum
- Карты
- Класс - отражение Java
- Класс EnumSet
- Класс java.util.Objects
- Класс даты
- Класс свойств
- Классы и объекты
- Клонирование объектов
- Кодировка символов
- Коллекции
- Команда Java - «java» и «javaw»
- Команды выполнения
- Компилятор Java - «javac»
- Компилятор Just in Time (JIT)
- Консольный ввод-вывод
- Конструкторы
- литералы
- Локализация и интернационализация
- Лямбда-выражения
- Массивы
- Менеджер по безопасности
- Местное время
- Местный внутренний класс
- Методы и конструкторы классов объектов
- Методы по умолчанию
- Методы сбора коллекции
- Модель памяти Java
- Модификаторы без доступа
- Модули
- наборы
- наследование
- Настройка производительности Java
- Неизменяемые объекты
- Неизменяемый класс
- Необязательный
- Новый ввод-вывод файлов
- Обработка аргументов командной строки
- Общие ошибки Java
- Одиночки
- операторы
- Операции с плавающей точкой Java
- Ориентиры
- Основные управляющие структуры
- Отправка динамического метода
- Оценка XML XPath
- Очереди и Deques
- Ошибки Java - Nulls и NullPointerException
- Ошибки Java - потоки и параллелизм
- Ошибки Java - проблемы с производительностью
- пакеты
- Параллельное программирование (темы)
- Параллельное программирование с использованием структуры Fork / Join
- Параллельные коллекции
- Перечисления
- Полиморфизм
- предпочтения
- Преобразование в строки и из них
- Преобразование типа
- Примитивные типы данных
- Процесс
- Путь Класса
- Разборка и декомпиляция
- Развертывание Java
- Разделение строки на части с фиксированной длиной
- Реализации Java-плагинов
- Регулярные выражения
- Рекурсия
- Ресурсы (на пути к классам)
- Розетки
- Свободный интерфейс
- Сериализация
- сетей
- сканер
- Служба печати Java
- Создание изображений программно
- Создание кода Java
- Сокеты Java
- Списки
- Список против SET
- Сравнение C ++
- Сравнительный и компаратор
- Ссылки на объекты
- Стандарт официального кода Oracle
- Строковый токенизатор
- Струны
- супер ключевое слово
- Тестирование устройства
- Типы ссылок
- Типы ссылочных данных
- Удаленный вызов метода (RMI)
- Управление памятью Java
- Установка Java (стандартная версия)
- Утверждая
- Файловый ввод-вывод
- Файлы с несколькими релизами JAR
- Флаги JVM
- Функциональные интерфейсы
- Хеш-таблица
- Читатели и писатели
- Шифрование RSA
Java dərsi
C, C+, C#, Java SE, Java EE və Python proqramlaşdırma dillərini və Oracle VBİS 19c (azərbaycan, ingilis, rus dillərində) əyani və ya Skype-la online tədris edirəm. Tələbə olanlara gündəlik tapşırıqların və layihələrinin
Mart ayının 18-dək qeydiyyatdan keçən şəxslər üçün Peşəkar Kibertəhlükəsizlik və Etik hacker təlimi 250 AZN deyil, 150 AZN və Full Stack Veb Proqramlaşdırma təlimi isə 250 AZN deyil, 145 AZN olacaq! Kibertəhlükəsizlik
Kurs axtarmaqdan bezmisiniz? Bizə müraciət edin,razı qalacağınıza əminik ∆∆ Endirimli dərslərə start derdik ✓ Dərslərin keyfiyyətindən əmin olmaq üçün ilk dərsimiz ödənişsiz sınaq dərsi olacaq; ✓ Dərslər
Vergi sahəsi üzrə peşəkar olmaq istəyən şəxslər qeydiyyatdan keçməyə tələsin! Təlim vergi mütəxəssisi və ya bu sahədə çalışan və özünü təkmilləşdirmək istəyən şəxslər üçün nəzərdə tutulub. Təlim Onlayn və əyani
Mart ayının 18-dək qeydiyyatdan keçən şəxslər üçün Peşəkar İnteryer Dizayn təlimi 200 AZN deyil, 145 AZN olacaq! Təlimçimiz Emin Savaylov 13 illik iş təcrübəsinə malikdir. GAİD (Gök Kuşağı Mimari Dizayn) təşkilatının
Zinyət Tədris Mərkəzində Məntiq kursları təcrübəli müəllimlər tərəfindən yüksək səviyyədə keçirilir. Dərsin keyfiyyətinə və tələbənin mükəmməl şəkildə mənimsəməsinə 100% zəmanət verilir. Məntiq kursları üç istiqamətdə
Zinyət Tədris Mərkəzində ArcGİS Proqramından dərslər 10 illik təcrübəyə sahib müəllim tərəfindən yüksək səviyyədə keçirilir. Dərsin keyfiyyətinə və tələbənin proqramı mükəmməl şəkildə mənimsəməsinə 100% zəmanət verilir.
Zinyət Tədris Mərkəzində Adobe Illustrator proqramından dərslər 20 illik təcrübəyə sahib müəllimə tərəfindən yüksək səviyyədə keçirilir. Dərsin keyfiyyətinə və tələbənin proqramları mükəmməl şəkildə mənimsəməsinə 100%
Kurslarımız veb üzrə texniki təhsili və ya iş təcrübəsi olmayan insanlar üçün də. veb proqramlaşdırma mütəxəssisi peşəsinə yiyələnməyə kömək edəcək. Həm bu sahədə hələ işləməmiş insanlar, həm də biliklərini
Duolingo English Test - ingilis dili bilik səviyyənizi müəyyən etmək üçün olan online imtahandır. Digər beynəlxalq imtahanlarda olduğu kimi Duolingoda da danışıq, dinləmə, yazı və oxu kimi bacarıqlarınız yoxlanılır.
Fransız dili kursu Online və kursda Təcrübəli müəllimlər Xətai və Qarayev filialları Bir pulsuz sınaq dərsi Keyfiyyət və zəmanət
Full Stack Javascript Dersler Online Sekilde Kecirilir Bu dərslər boyunca siz Javascript Proqramlaşdirma dilini həm front end və backend olaraq öyrənmiş olacaqsiniz. dərslər boyunca keçiriləçək javascript kitabxanalari
Java təlim proqramına daxildir: Java platformasının təsnifatı və əsas imkanları Java dilinin təkamülü Software Architecture MySQL-in Javada istifadəsi Desktop proqramlaşdırma-SWING JDBC Java Swing (Desktop
C, C+, C#, Java və Python proqramlaşdırma dillərini (azərbaycan, ingilis, rus dillərində) əyani və ya Skype-la online tədris edirəm. Tələbə olanlara gündəlik tapşırıqların və layihələrinin yerinə yetirilməsində və
Peşəkar Veb developer olmaq istəyənlərin nəzərinə! 0-dan başlayaraq peşəkar mütəxəssis səviyyəsində formalaşmanız üçün proqramlaşdırma dillərini peşəkarlardan öyrənməyin vaxtıdır! Mart ayının 18-dək qeydiyyatdan keçən
Bütün kompüter kursları tək bir ünvanda cəmlədik Corel Draw, Photoshop kursu 80 azn Autocad Archicad kursu 80 azn 3ds Max, Vray kursu 80 azn 1C Kursları 150 azn Adobe Premiere kursu 170 azn Adobe illustrator kursu, 100
Tez bir zamanda mükemmel Alman dili Almaniyada tehsil almiş peşeker müellim terefinden öyrenin. A1-C1 seviyyesi. 3 aya kimi danishiq zemaneti verilir. 6aya öyrənəcəklərinizi menimle 3aya öyrənirsiz. Ders vaxti coxlu
Ze akademy Kursun müddəti 6 ay Dərslər həftədə 2 dəfə 2 saat Kursu bitirən tələbələrə fiplom və sertifikat verilir İlk dərs sınaq dərsidir Dərs dizi qane etmədə ödəniş etməfən çıxıb gedə bilərsiniz Öyrənməyə100%
Home Education Academy ümumi ingilis dilini Beynəlxalq standartlar əsasında öyrənmək istəyənləri, dil üzrə əsas bacarıqlarını, Danışıq, Dinləmə, Yazma və Oxuma, Qramatika və Söz Ehtiyyatını artırmağı (Reading, Writing,
Peşəkar bərbər olmaq istəyirsən və 0-dan bu peşəni öyrənməlisənKursumuz sizin üçün əla fürsətdir Bərbərlik kursu haqqında məlumat: Kişi saç ustası kursu 6 aylıq kursdur. Sertifikat verilir Həftədə 3 dəfə 2 saat (120
Əziz izləyicilerimiz İngilis dili dərslərinə endirim kompaniyasına start veririk Dərslərimiz qrup və fərdi şəkildə keçirilir Həftədə 3dəfə 60 dəqiqə olmaqla Dərs qrafiki sizin istəyinizə uyğun seçilir Kursu
Salam Azərbaycan və rus bölməsi İlk dərs ödənişsiz sınaq dərsi Sərbəst qrafik Axşam dərsləri Münasib qiymətə keyfiyyətli dərs Peşakar müəllimlər
Zinyət Tədris Mərkəzində Bütün Kompyuter Proqramlarından dərslər 20 illik təcrübəyə sahib müəllimə tərəfindən yüksək səviyyədə keçirilir. Dərsin keyfiyyətinə və tələbənin proqramları mükəmməl şəkildə mənimsəməsinə 100%
Çin dilinə yiyələnərək gələcəyə böyük yatırım etmiş olursuz. Azərbaycanda BDU-da təhsil almışam, Çində isə Mərkəzi Çin Universitetində, Çin, Türk və İngilis dillərinə mükəmməl dərəcədə yiyələnmişəm. Altı ildən daha
Çin dilinə yiyələnərək gələcəyə böyük yatırım etmiş olursuz. Azərbaycanda BDU-da təhsil almışam, Çində isə Mərkəzi Çin Universitetində, Çin, Türk və İngilis dillərinə mükəmməl dərəcədə yiyələnmişəm. Altı ildən daha
5yaşdan yuxarı uşaqlara(oğlan,qız fərqi yoxdur)piano dərsi keçirilir.Sumqayıt və Bakı şəhərlərində evlərə gedərək fərdi dərslər keçirilir.Bakıda evlərdə fərdi dərslər 80manata, Sumqayıtda 60manata keçirilir.Daha ətraflı
KOMPÜTER VƏ DİZAYN KURSLARI. OFİS, QRAFİKA VƏ MEMARLIQ PROQRAMLARI. PULSUZ SINAQ DƏRSİ. İŞƏ HAZIRLIQ. Əgər Siz kompüter və dizaynla bağlı iş tapmaq istəyirsinizsə və ya hal-hazırda bu sahədə işləyirsinizsə,
Magistr hazırlığı 2-ci sans Xətai və Q. Qarayev filialları Kursda və online Məntiq İnformatika Xarici dil Pulsuz sınaq dərsi Təcrübəli müəllimlər.
Digər elanlar
java dərsi
java dərsi kurslari 2023 , Bakida java dərsi kurslar, java dərsi dərsləri ucuz qiymete java dərsi kurslar keçirilir . java dərsi xidmeti. Azerbaycanda ucuz qiymete java dərsi . Online hazirliq kurslari.
Kurslar.az Bakida kurslar və Azərbaycanda kurslari bir yerə toplayan onlayn platformadır. Saytda müxtəlif kurslar ve təlimlər tapa bilərsiz. Kurslari saytda yerləşdirmək pulsuzdur. Onlayn Kursu sayta elavə edəndə kurs haqqında ətraflı məlumat və kursun qiymətini yazın. Müxtəlif kateqoriyada olan kursları sayta yerləşdirmək mümkündür. Rus dili, İngilis dili , Xarici dil kursları, Sürücülük kursları, Biznes təlimlər, Seminarlar, Onlayn kurslar, suruculuk kurslari, imtahana hazirliq ve abituriyent kurslari, komputer kurslari, proqramlasdirma ve dizayn kurslari.
© 2016 KURSLAR.AZ | info [@] kurslar.az | Bizimlə əlaqəВозможности Java — от Java 8 до Java 17
С момента появления в 1995 году до сегодняшнего дня в Java многое изменилось. Java 8 была революционным выпуском, вернувшим Java на пьедестал лучших языков программирования.
Мы рассмотрим большинство изменений в языке Java, которые произошли с Java 8 в 2014 году до сегодняшнего дня. Мы постараемся быть как можно более краткими по каждой функции. Намерение состоит в том, чтобы иметь ссылку на все новые фичи языка Java версий 8 - 17 включительно.
Эта статья сопровождается примером рабочего кода на GitHub.
Эта статья переведена по просьбе одного из читателей Хабр. Надеюсь она будет полезна как краткий справочник по новым фичам языка Java 8 - 17.
Java 8
Основные изменения в выпуске Java 8:
- Лямбда-выражения и Stream API
- Ссылка на метод
- Методы по умолчанию
- Аннотации типов
- Повторяющиеся аннотации
Лямбда-выражения и Stream API
Java всегда была известна наличием большого количества шаблонного кода. С выпуском Java 8 это утверждение стало немного менее актуальным. Stream API и лямбда-выражения - это новые возможности, которые приближают нас к функциональному программированию.
В наших примерах мы увидим, как мы используем лямбды и потоки в различных сценариях.
Мир до лямбда-выражений
Допустим у нас есть автосалон. Чтобы избавиться от всей бумажной работы, мы хотим создать программу, которая находит все доступные в настоящее время автомобили с пробегом менее 50 000 км.
Давайте посмотрим, как наивно реализовать функцию для чего-то вроде этого:
public class LambdaExpressions < public static ListfindCarsOldWay(List cars) < ListselectedCars = new ArrayList<>(); for (Car car : cars) < if (car.kilometers < 50000) < selectedCars.add(car); >> return selectedCars; > >Чтобы реализовать ее, мы создаем статическую функцию, которая принимает список автомобилей List . Он должен возвращать отфильтрованный список в соответствии с указанным условием.
Использование потока и лямбда-выражения
У нас та же проблема, что и в предыдущем примере.
Наш клиент хочет найти все автомобили по одинаковым критериям.
Давайте посмотрим на решение, в котором мы использовали API потока и лямбда-выражение:
public class LambdaExpressions < public static ListfindCarsUsingLambda(List cars) < return cars.stream().filter(car ->car.kilometers < 50000) .collect(Collectors.toList()); >>Нам нужно передать список машин в поток, вызвав метод stream() . Внутри метода filter() мы устанавливаем наше условие. Мы сравниваем каждую запись с желаемым условием. Мы сохраняем только те записи, которые имеют пробег менее 50 000 километров. Последнее, что нам нужно сделать, это свернуть результат в список.
Подробнее о лямбда-выражениях можно найти в документации.
Ссылка на метод
Без ссылки на метод
У нас все еще есть автосалон, и мы хотим распечатать все автомобили в магазине. Для этого мы будем использовать ссылку на метод.
Ссылка на метод позволяет нам вызывать функции в классах, используя особый синтаксис :: . Есть четыре вида ссылок на методы:
- Ссылка на статический метод
- Ссылка на метод экземпляра объекта
- Ссылка на метод экземпляра для типа
- Ссылка на конструктор
Давайте посмотрим, как это сделать с помощью стандартного вызова метода:
public class MethodReference < ListwithoutMethodReference = cars.stream().map(car -> car.toString()) .collect(Collectors.toList()); >Мы используем лямбда-выражение для вызова метода toString() на каждого объекта car.
Использование ссылки на метод
Теперь давайте посмотрим, как в той же ситуации использовать ссылку на метод:
public class MethodReference < ListmethodReference = cars.stream().map(Car::toString) .collect(Collectors.toList()); >Мы снова используем лямбда-выражение, но теперь мы вызываем метод toString() по ссылке на метод. Мы видим, насколько он лаконичнее и легче читается.
Чтобы узнать больше о справочнике по методам, посмотрите документацию.
Методы по умолчанию
Представим, что у нас есть простой метод, log(String message) , который при вызове выводит сообщения журнала. Мы поняли, что хотим предоставлять сообщениям отметки времени, чтобы в журналах было легко найти их. Мы не хотим, чтобы наши клиенты перестали работать после того, как мы внесем это изменение. Мы сделаем это, используя реализацию метода по умолчанию в интерфейсе.
Реализация метода по умолчанию - это функция, которая позволяет нам создать резервную реализацию метода интерфейса.
Сценарий использования
Посмотрим, как выглядит наш контракт:
public class DefaultMethods < public interface Logging < void log(String message); >public class LoggingImplementation implements Logging < @Override public void log(String message) < System.out.println(message); >> >Мы создаем простой интерфейс с помощью всего одного метода и реализуем его в классе LoggingImplementation .
Добавление нового метода
Мы добавим новый метод внутрь интерфейса. Метод принимает второй аргумент с именем date, который представляет отметку времени.
public class DefaultMethods < public interface Logging < void log(String message); void log(String message, Date date); >>Мы добавляем новый метод, но не реализуем его во всех клиентских классах. Компилятор выдаст исключение:
Class 'LoggingImplementation' must either be declared abstract or implement abstract method 'log(String, Date)' in 'Logging'`.Использование методов по умолчанию
После добавления нового метода внутри интерфейса наш компилятор выдавал исключения. Мы собираемся решить эту проблему, используя реализацию метода по умолчанию для нового метода.
Давайте посмотрим, как создать реализацию метода по умолчанию:
public class DefaultMethods < public interface Logging < void log(String message); default void log(String message, Date date) < System.out.println(date.toString() + ": " + message); >> >Ввод ключевого слова default позволяет нам добавить реализацию метода внутри интерфейса. Теперь наш класс LoggingImplementation не завершается с ошибкой компилятора, даже если мы не реализовали новый метод внутри него.
Чтобы узнать больше о методах по умолчанию, обратитесь к документации.
Аннотации типов
Аннотации типов - еще одна функция, представленная в Java 8. Несмотря на то, что и раньше у нас были аннотации, теперь мы можем использовать их везде, где используется тип. Это означает, что мы можем использовать их при:
- определении локальной переменной
- вызове конструктора
- приведении типов
- в дженериках
- в throw и многое другое
Такие инструменты, как IDE, могут затем читать эти аннотации и выдавать предупреждения или ошибки на основе аннотаций.
Определение локальной переменной
Давайте посмотрим, как сделать так, чтобы наша локальная переменная не получала значение null :
public class TypeAnnotations < public static void main(String[] args) < @NotNull String userName = args[0]; >>Здесь мы используем аннотацию в определении локальной переменной. Обработчик аннотаций времени компиляции теперь может читать аннотацию @NotNull и выдавать ошибку, если переменной присваивается значение NULL.
Вызов конструктора
Мы хотим убедиться, что мы не можем создать пустой ArrayList :
public class TypeAnnotations < public static void main(String[] args) < Listrequest = new @NotEmpty ArrayList<>(Arrays.stream(args).collect( Collectors.toList())); > >Это прекрасный пример того, как использовать аннотации типов в конструкторе. Опять же, обработчик аннотаций может определить аннотацию и проверить, не пуст ли список массивов.
Generic тип
Пусть одно из наших требований - каждый электронный адрес должен быть в формате @.com . Если мы используем аннотации типов, мы можем сделать это легко:
public class TypeAnnotations < public static void main(String[] args) < Listemails; > >Это определение списка адресов электронной почты. Мы используем аннотацию @Email , которая гарантирует, что каждая запись внутри этого списка находится в желаемом формате.
Инструмент может использовать рефлексию для определения аннотации и проверки того, что каждый из элементов в списке является допустимым адресом электронной почты.
Дополнительные сведения об аннотациях типов см. в документации.
Повторяющиеся аннотации
Представим, что у нас есть приложение с полностью реализованной системой безопасности. Оно имеет разные уровни авторизации. Несмотря на то, что мы все реализовали тщательно, мы хотим убедиться, что мы регистрируем каждое несанкционированное действие. При каждом несанкционированном действии мы отправляем электронное письмо владельцу компании и нашей группе администраторов безопасности. Повторяющиеся аннотации - это наш способ продолжить этот пример.
Повторяющиеся аннотации позволяют нам размещать несколько аннотаций в одном классе.
Создание повторяющейся аннотации
В этом примере мы собираемся создать повторяющуюся аннотацию под названием @Notify :
public class RepeatingAnnotations < @Repeatable(Notifications.class) public @interface Notify < String email(); >public @interface Notifications < Notify[] value(); >>Мы создаем обычную аннотацию @Notify , но добавляем к ней (мета) аннотацию @Repeatable . Кроме того, мы должны создать аннотацию «контейнер» Notifications , содержащую массив объектов Notify . Обработчик аннотаций теперь может получить доступ ко всем повторяющимся аннотациям Notify через аннотацию контейнера Noifications .
Обратите внимание, что это фиктивная аннотация только для демонстрационных целей. Эта аннотация не будет отправлять электронные письма без процессора аннотаций, который читает ее, а затем отправляет электронные письма.
Использование повторяющихся аннотаций
Мы можем добавить повторяющуюся аннотацию несколько раз к одной и той же конструкции:
@Notify(email = "admin@company.com") @Notify(email = "owner@company.com") public class UserNotAllowedForThisActionException extends RuntimeException < final String user; public UserNotAllowedForThisActionException(String user) < this.user = user; >>У нас есть собственный класс исключений, которые мы будем генерировать всякий раз, когда пользователь пытается сделать что-то, что ему не разрешено. Наши аннотации к этому классу говорят, что мы хотим высылать уведомления в двух электронных письмах, когда код генерирует это исключение.
Чтобы узнать больше о повторяющихся аннотациях, обратитесь к документации.
Java 9
В Java 9 представлены следующие основные функции:
- Система модулей Java
- Try-with-resources
- Diamond оператор для анонимных внутренних классов
- Private методы в интерфейсах
Система модулей Java
Модуль - это группа пакетов, их зависимости и ресурсы. Он предоставляет более широкий набор функций, чем пакеты.
При создании нового модуля нам необходимо предоставить несколько атрибутов:
- Имя
- Зависимости
- Публичные пакеты - по умолчанию все пакеты являются private модулями.
- Предлагаемые сервисы
- Потребляемые сервисы
- Разрешения на рефлексию
Не вдаваясь в подробности, давайте создадим наш первый модуль. В нашем примере мы покажем несколько параметров и ключевых слов, которые можно использовать при создании модуля.
Создание модулей внутри IntelliJ IDEA
Сначала рассмотрим простой пример. Мы создадим приложение Hello World, в котором мы напечатаем «Hello» из одного модуля и вызываем второй модуль, чтобы вывести «World!».
Поскольку я работаю в IntelliJ IDEA, нам нужно кое-что понять в первую очередь. IntelliJ IDEA имеет концепцию модулей. Поэтому каждый модуль Java должен соответствовать одному модулю IntelliJ.
У нас есть два Java модуля: hello.module и world.module . Они соответствуют IntelliJ модулям hello и world соответственно. Внутри каждого из них мы создали файл module-info.java . Этот файл определяет наш Java-модуль. Внутри мы объявляем, какие пакеты нам нужно экспортировать и от каких модулей мы зависим.
Определение нашего первого модуля
Мы используем модуль hello для печати слова: «Hello». Внутри мы вызываем метод внутри модуля world , который выведет «World!». Первое, что нам нужно сделать, это объявить внутри module-info.java экспорт пакета, содержащего наш класс World.class :
module world.module
Мы используем ключевое слово module с именем модуля для ссылки на модуль.
Следующее ключевое слово, которое мы используем, - это exports . Он сообщает модульной системе, что мы делаем наш пакет com.reflectoring.io.app.world видимым за пределами нашего модуля.
Можно использовать еще несколько ключевых слов:
- requires
- requires transitive
- exports to
- uses
- provides with
- open
- opens
- opens to
Из них мы покажем только декларацию requires . Остальные можно найти в документации.
Определение нашего второго модуля
После того, как мы создали и экспортировали модуль world , мы можем приступить к созданию модуля hello :
module hello.module
Мы определяем зависимости с помощью ключевого слова requires . Мы ссылаемся наш вновь созданный модуль hello.module . Пакеты, которые не экспортируются, по умолчанию являются private для модуля и не могут быть видимы извне модуля.
Чтобы узнать больше о модульной системе Java, обратитесь к документации.
Try-with-resources
Try-with-resources - это фича, которая позволяет нам объявлять новые автоматически закрываемые ресурсы в блоке try-catch . Объявление их внутри блока try-catch указывает JVM освободить их после выполнения кода. Единственное условие - декларированный ресурс реализует интерфейс Autoclosable .
Закрытие ресурса вручную
Мы хотим читать текст, используя BufferedReader . BufferedReader является закрываемым ресурсом, поэтому нам нужно убедиться, что он правильно закрыт после использования. До Java 8 мы делали это так:
public class TryWithResources < public static void main(String[] args) < BufferedReader br = new BufferedReader( new StringReader("Hello world example!")); try < System.out.println(br.readLine()); >catch (IOException e) < e.printStackTrace(); >finally < try < br.close(); >catch (IOException e) < e.printStackTrace(); >> > >В блоке finally мы бы вызывали close() . Блок finally гарантирует, что reader всегда правильно закрыт.
Закрытие ресурса с помощью try-with-resources
В Java 8 появилась фича try-with-resource, которая позволяет нам декларировать наш ресурс внутри блока try . Это гарантирует, что наш закрываемый объект будет закрыт без использования finally .
Примечание переводчика. В оригинале ошибка. Оператор try-with-resources появился в Java 7. См. Java SE 7 Features and Enhancements.
В Java 9 разрешено использовать final переменные в качестве ресурсов в операторе try-with-resources . См. What’s New for the Java Language in JDK 9
Давайте посмотрим на пример использования BufferedReader для чтения строки:
public class TryWithResources < public static void main(String[] args) < final BufferedReader br3 = new BufferedReader( new StringReader("Hello world example3!")); try (BufferedReader reader = br3) < System.out.println(reader.readLine()); >catch (IOException e) < System.out.println("Error happened!"); >> >Внутри блока try мы назначаем наш ранее созданной BufferedReader новой переменной. Теперь мы уверены, что наш reader всегда закрывается.
Чтобы узнать больше о try-with-resources, обратитесь к документации.
Diamond оператор для анонимных внутренних классов
До Java 9 мы не могли использовать ромбовидный оператор (<>) во внутреннем анонимном классе.
В нашем примере мы создадим абстрактный класс StringAppender . У класса есть только один метод, который добавляет две строки с разделителем - между ними. Мы будем использовать анонимный класс для реализации метода append() :
public class DiamondOperator < StringAppenderappending = new StringAppender<>() < @Override public String append(String a, String b) < return new StringBuilder(a).append("-").append(b).toString(); >>; public abstract static class StringAppender < public abstract T append(String a, String b); >>Мы используем ромбовидный оператор, чтобы опустить тип при вызове конструктора new StringAppender<>() . Если мы используем Java 8, в этом примере мы получим ошибку компилятора:
java: cannot infer type arguments for com.reflectoring.io.java9.DiamondOperator.StringAppender reason: '<>' with anonymous inner classes is not supported in -source 8 (use -source 9 or higher to enable '<>' with anonymous inner classes)В Java 9 эта ошибка компилятора больше не возникает.
Private методы в интерфейсах
Мы уже упоминали, как мы используем методы по умолчанию в интерфейсах.
Как разделить реализацию на несколько методов? При работе с классами мы можем добиться этого с помощью private методов. Может ли это быть выходом в нашем случае?
Что касается Java 9, да. Мы можем создавать private методы внутри интерфейсов.
Использование private метода в интерфейсе
В нашем примере мы хотим напечатать набор имен.
Для интерфейса, содержащего эту функцию, определен метод по умолчанию. Мы решили, что должны, если клиент не предоставляет реализацию, предоставить набор предопределенных имен, которые мы читаем из папки ресурсов:
public class PrivateInterfaceMethods < public static void main(String[] args) < TestingNames names = new TestingNames(); System.out.println(names.fetchInitialData()); >public static class TestingNames implements NamesInterface < public TestingNames() < >> public interface NamesInterface < default ListfetchInitialData() < try (BufferedReader br = new BufferedReader( new InputStreamReader(this.getClass() .getResourceAsStream("/names.txt")))) < return readNames(br); >catch (IOException e) < e.printStackTrace(); return null; >> private List readNames(BufferedReader br) throws IOException < ArrayListnames = new ArrayList<>(); String name; while ((name = br.readLine()) != null) < names.add(name); >return names; > > >Мы используем BufferedReader для чтения файла, содержащего имена по умолчанию, которыми мы делимся с клиентом. Чтобы инкапсулировать наш код и, возможно, сделать его повторно используемым в других методах, мы решили переместить код для чтения и сохранения имен в List в отдельный метод. Этот метод является private, и теперь мы можем использовать его где угодно в нашем интерфейсе.
Как уже упоминалось, основным преимуществом этой функции внутри Java 9 является лучшая инкапсуляция и возможность повторного использования кода.
Java 10
Вывод типа локальной переменной
Java всегда требовала явное указание типа локальных переменных.
При написании и чтении кода мы всегда знаем, какой тип ожидаем. С другой стороны, большая часть кода - это просто указания типа, что не очень удобно.
Тип var позволяет нам опускать тип в левой части наших операторов.
Старый способ
Давайте рассмотрим пример. Мы хотим создать небольшую группу людей, поместить все в один список, а затем просмотреть этот список в цикле for и распечатать имя и фамилию:
public class LocalTypeVar < public void explicitTypes() < Person Roland = new Person("Roland", "Deschain"); Person Susan = new Person("Susan", "Delgado"); Person Eddie = new Person("Eddie", "Dean"); Person Detta = new Person("Detta", "Walker"); Person Jake = new Person("Jake", "Chambers"); Listpersons = List.of(Roland, Susan, Eddie, Detta, Jake); for (Person person : persons) < System.out.println(person.name + " - " + person.lastname); >> >Это код, который мы можем видеть в большинстве случаев в Java. Мы используем явные типы, чтобы убедиться, что мы знаем, чего ожидает метод.
Неявный тип с var
Теперь мы рассмотрим тот же пример, но с использованием ключевого слова var , введенного в Java 10. Мы по-прежнему хотим создать несколько объектов Person и поместить их в список. После этого мы просмотрим этот список и распечатаем имя каждого человека:
public class LocalTypeVar < public void varTypes() < var Roland = new Person("Roland", "Deschain"); var Susan = new Person("Susan", "Delgado"); var Eddie = new Person("Eddie", "Dean"); var Detta = new Person("Detta", "Walker"); var Jake = new Person("Jake", "Chambers"); var persons = List.of(Roland, Susan, Eddie, Detta, Jake); for (var person : persons) < System.out.println(person.name + " - " + person.lastname); >> >Мы видим несколько наиболее типичных примеров использования типа var для локальных переменных. Во-первых, мы используем их для определения локальных переменных. Это может быть отдельный объект или даже список с ромбовидным оператором.
Дополнительные сведения о выводе типа локальной переменной см. в документации.
Java 11
Вывод типа локальной переменной в лямбда-выражениях
В Java 11 внесены улучшения в ранее упомянутый вывод типа локальной переменной. Они позволяют нам использовать тип var внутри лямбда-выражения.
Мы снова создадим несколько Person, соберем их в список и отфильтруем записи, в имени которых нет буквы «а»:
public class LocalTypeVarLambda < public void explicitTypes() < var Roland = new Person("Roland", "Deschain"); var Susan = new Person("Susan", "Delgado"); var Eddie = new Person("Eddie", "Dean"); var Detta = new Person("Detta", "Walker"); var Jake = new Person("Jake", "Chambers"); var filteredPersons = List.of(Roland, Susan, Eddie, Detta, Jake) .stream() .filter((var x) ->x.name.contains("a")) .collect(Collectors.toList()); System.out.println(filteredPersons); > >Внутри метода filter() мы используем var для вывода типа переменной вместо явного упоминания типа.
Обратите внимание, что не имеет значения, используем ли мы var или нет, вывод типа локальной переменной в лямбда-выражениях будет работать одинаково для обоих случаев.
Java 14
Switch выражения
Switch выражения позволили нам опускать break внутри каждого case блока. Это помогает улучшить читаемость и понимание кода.
В этом разделе мы увидим несколько способов использования Switch выражений.
Старый способ c оператором Switch
У нас есть метод, в котором клиент указывает желаемый месяц, а мы возвращаем количество дней в этом месяце.
Первое, что приходит в голову, - это построить его с помощью оператора switch-case :
public class SwitchExpression < public static void main(String[] args) < int days = 0; Month month = Month.APRIL; switch (month) < case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER : days = 31; break; case FEBRUARY : days = 28; break; case APRIL, JUNE, SEPTEMBER, NOVEMBER : days = 30; break; default: throw new IllegalStateException(); >> >Нам нужно убедиться, что мы поместили оператор break внутри каждого case блока кода. В противном случае будут проверяться другие условия после совпадения с первым.
Использование Switch выражений
Мы рассмотрим тот же пример, что и раньше. Пользователь хочет отправить месяц и получить количество дней в этом месяце:
public class SwitchExpression < public static void main(String[] args) < int days = 0; Month month = Month.APRIL; days = switch (month) < case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER ->31; case FEBRUARY -> 28; case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30; default -> throw new IllegalStateException(); >; > >В блоке case мы используем немного другие обозначения. Мы используем -> вместо двоеточия. Несмотря на то, что мы не указываем оператор break , мы все равно выйдем из оператора switch при первом выполненном условии.
Этот код будет делать то же самое, что и код, показанный в предыдущем примере.
Ключевое слово yield
Логика внутри case блока может быть немного сложнее, чем просто возврат значения. Например, мы хотим записать в лог, какой месяц нам отправил пользователь:
public class SwitchExpression < public static void main(String[] args) < int days = 0; Month month = Month.APRIL; days = switch (month) < case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> < System.out.println(month); yield 31; >case FEBRUARY -> < System.out.println(month); yield 28; >case APRIL, JUNE, SEPTEMBER, NOVEMBER -> < System.out.println(month); yield 30; >default -> throw new IllegalStateException(); >; > >В многострочном блоке кода мы должны использовать ключевое слово yield для возврата значения из case блока.
Чтобы узнать больше об использовании Switch выражений, обратитесь к документации.
Java 15
Текстовые блоки
Текстовый блок усовершенствует форматирование строковых переменных. Начиная с Java 15, мы можем написать String, занимающую несколько строк, как обычный текст.
Пример без использования текстовых блоков
Мы хотим отправить HTML-документ по электронной почте. Мы сохраняем шаблон электронного письма в переменной:
public class TextBlocks < public static void main(String[] args) < System.out.println( "\n" + "\n" + " \n" + "Example \n" + " \n" + " \n" + "This is an example of a simple HTML " + "page with one paragraph.
\n" + " \n" + "\n"); > >Мы форматируем нашу строку, как в примере выше. Нам нужно позаботиться о переходе текста на новую строку и соединить все строки в одну строку.
Пример использования текстовых блоков
Давайте рассмотрим тот же пример HTML-шаблона для электронной почты. Мы хотим отправить пример электронного письма с простым форматированием HTML. На этот раз мы будем использовать текстовый блок:
public class TextBlocks < public static void main(String[] args) < System.out.println( """Example This is an example of a simple HTML page with one paragraph.
""" ); > >Мы использовали специальный синтаксис для открытия и закрытия кавычек: """ . Это позволяет нам трактовать нашу строку как будто мы записываем ее в файл .txt.
Есть некоторые правила, которые мы должны соблюдать при использовании текстового блока. Нам нужно убедиться, что мы поместили текст на новую строку после открывающих кавычек, иначе наш компилятор выдаст ошибку:
Illegal text block start: missing new line after opening quotes.Если мы хотим закончить нашу строку символом \n мы можем сделать это, поместив новую строку перед закрывающими кавычками """ , как в примере выше.
Чтобы узнать больше о текстовых блоках, обратитесь к документации.
Java 16
Сопоставление с образцом instanceof
Сопоставление с образцом instanceof позволяет нам преобразовать нашу переменную в строку и использовать ее внутри желаемого if-else блока без явного преобразования.
Пример без сопоставления с образцом
У нас есть вызываемый базовый класс Vehicle и два расширяющих его класса: Car и Bicycle . Мы опустили код для них - вы можете найти его в репозитории GitHub.
Наш алгоритм расчета цен зависит от экземпляра автомобиля:
public class PatternMatching < public static double priceOld(Vehicle v) < if (v instanceof Car) < Car c = (Car) v; return 10000 - c.kilomenters * 0.01 - (Calendar.getInstance().get(Calendar.YEAR) - c.year) * 100; >else if (v instanceof Bicycle) < Bicycle b = (Bicycle) v; return 1000 + b.wheelSize * 10; >else throw new IllegalArgumentException(); > >Поскольку мы не используем сопоставление с образцом, нам нужно преобразовать транспортное средство в правильный тип внутри каждого if-else блока. Как мы видим, это типичный пример шаблонного кода, которым славится Java.
Использование сопоставления с образцом
Давайте посмотрим, как мы можем избежать шаблонного кода в приведенном выше примере:
public class PatternMatching < public static double price(Vehicle v) < if (v instanceof Car c) < return 10000 - c.kilomenters * 0.01 - (Calendar.getInstance().get(Calendar.YEAR) - c.year) * 100; >else if (v instanceof Bicycle b) < return 1000 + b.wheelSize * 10; >else throw new IllegalArgumentException(); > >Следует отметить область видимости приведенной переменной. Ее видно только внутри оператора if.
Дополнительные сведения о сопоставлении с образцом в методе instanceof см. в документации.
Записи
Сколько POJO (Plain Old Java Objects) вы написали?
Что ж, могу ответить за себя: «Слишком много!».
У Java плохая репутация написания шаблонного кода. Lombok позволил нам перестать беспокоиться о геттерах, сеттерах и т. д. В Java 16 наконец-то появились Records (записи) для удаления большого количества шаблонного кода.
Класс записи - это не что иное, как обычный POJO, для которого большая часть кода генерируется из определения.
Обычное определение POJO
Давайте посмотрим на пример POJO класса до того, как в Java 16 были введены записи:
public class Vehicle < String code; String engineType; public String getCode() < return code; >public void setCode(String code) < this.code = code; >public String getEngineType() < return engineType; >public void setEngineType(String engineType) < this.engineType = engineType; >public Vehicle(String code, String engineType) < this.code = code; this.engineType = engineType; >@Override public boolean equals(Object o) . @Override public int hashCode() . @Override public String toString() . >Он включает почти 50 строк кода для объекта, который содержит только два свойства. IDE сгенерировала этот код, но, тем не менее, он существует и должен поддерживаться.
Определение Record
Определение записи (Record) vehicle с теми же двумя свойствами может быть выполнено всего в одной строке:
public record VehicleRecord(String code, String engineType) <>В этой строке есть все те же геттеры, сеттеры, конструкторы и т. д., что и в приведенном выше примере. Следует отметить, что класс Record по умолчанию является final, и мы должны этому подчиняться. Это означает, что мы не можем расширить класс Record, но для нас доступно большинство других вещей.
Чтобы узнать больше о классах записи, обратитесь к документации.
Java 17
Sealed классы
Модификатор final в классе не позволяет никому расширить его. А как насчет того, чтобы расширить класс, но разрешить это только для некоторых классов?
Вернемся в автосалон. Мы так гордимся нашим алгоритмом расчета цен, что хотим его опубликовать. Однако мы не хотим, чтобы кто-либо использовал наше представление Vehicle. Оно справедливо только для нашего бизнеса. Здесь мы видим небольшую проблему. Нам нужно раскрыть класс, но также ограничить его.
Именно здесь в игру вступает Java 17 с запечатанными (Sealed) классами. Запечатанный класс позволяет нам сделать класс final для всех, кроме явно упомянутых классов.
public sealed class Vehicle permits Bicycle, Car
Мы добавили модификатор sealed к нашему классу Vehicle , и нам пришлось добавить ключевое слово permits со списком классов, которым мы разрешаем расширять его. После этого изменения мы по-прежнему получаем ошибки от компилятора.
Здесь нам нужно сделать еще одну вещь.
Нам нужно добавить модификаторы final , sealed или non-sealed в классы, которые расширяют наш класс.
public final class Bicycle extends Vehicle
Ограничения
Для работы запечатанного класса необходимо выполнить несколько ограничений:
- Разрешенные подклассы должны быть доступны запечатанному классу во время компиляции.
- Разрешенные подклассы должны напрямую расширять запечатанный класс
- Разрешенные подклассы должны иметь один из следующих модификаторов:
- final
- sealed
- non-sealed
Более подробную информацию о запечатанных классах можно найти в документации.
Comments are closed, but trackbacks and pingbacks are open.