Я скомпилировал свои предыдущие статьи на Github, приветствую всех в звездахGitHub.com/Next Day Picks/Не голоден…
Мы часто используем такие вещи, какОбобщения, автоматическая распаковка и упаковка, внутренние классы, улучшенные циклы for, синтаксис try-with-resources, лямбда-выраженияПодождите, мы только думаем, что это очень круто использовать, потому что эти функции могут помочь нам уменьшить нагрузку на разработку; но мы серьезно не изучали, в чем суть этих функций, поэтому в этой статье cxuan придет раскрыть причины эти особенности для вас правда.
синтаксический сахар
Прежде чем говорить, нам нужно понять语法糖
Концепция чего-либо:语法糖(Syntactic sugar)
, также известный как синтаксис с сахарным покрытием, — это термин, изобретенный британскими учеными, вообще говоря, использование синтаксического сахара может увеличить производительность программы.可读性
, тем самым уменьшая вероятность ошибок программного кода, что действительно ароматно и сладко.
Синтаксический сахар относится к определенному синтаксису, добавленному к языку программирования.Этот синтаксис никак не влияет на функциональность языка, но более удобен для использования программистами.. Поскольку код Java должен выполняться в JVM,JVM не поддерживает синтаксический сахар. Синтаксический сахар будет восстановлен до простой базовой синтаксической структуры на этапе компиляции программы.解语法糖
. Итак, в Java то, что действительно поддерживает синтаксический сахар, — это компилятор Java. . . . . .
Давайте посмотрим на эти синтаксические сахара в Java.
Дженерики
Дженерики — это синтаксический сахар. В JDK1.5 был введен универсальный механизм, но сам общий механизм类型擦除
Для этого в JVM нет дженериков, только обычные типы и обычные методы, а параметры типов дженериков будут стираться во время компиляции. У дженериков нет собственного уникального типа класса. как показано в коде ниже
List<Integer> aList = new ArrayList();
List<String> bList = new ArrayList();
System.out.println(aList.getClass() == bList.getClass());
List<Ineger>
иList<String>
считаются разными типами, но на выходе получается один и тот же результат, потому чтоОбщая информация существует только на этапе компиляции кода. Перед входом в JVM информация, связанная с дженериками, будет стерта.Технический термин называется стиранием типа.. Однако, если вы поместите данные типа Integer вList<String>
или поместите данные типа String вList<Ineger>
не разрешено.
Как показано ниже
Невозможно поместить данные типа Integer вList<String>
и не может поместить данные типа String вList<Integer>
То же самое не скомпилируется.
Автоматическая распаковка и автоматическая упаковка
Автораспаковка и автоупаковка — это синтаксический сахар, говорящий об автоматическом преобразовании между классами-оболочками восьми примитивных типов данных и их примитивными типами данных. Проще говоря, бокс — это автоматическое преобразование примитивных типов данных в包装器
тип; распаковка — это автоматическое преобразование типа-оболочки в примитивный тип данных.
Давайте сначала посмотрим на классы обертки для основных типов данных
Другими словами, вышеуказанные базовые типы данных и классы-оболочки будут автоматически упакованы/распакованы в процессе преобразования, например следующий код
Integer integer = 66; // 自动拆箱
int i1 = integer; // 自动装箱
Целочисленный объект в приведенном выше коде будет использовать базовый тип данных для присваивания, но базовый тип данных i1 присваивает его объектному типу, что, как правило, невозможно, но компилятор позволяет нам это сделать, что на самом деле является синтаксическим. сахар. Этот синтаксический сахар делает удобным для нас выполнение числовых операций.Если синтаксического сахара нет, то при выполнении числовых операций нужно преобразовать объект в базовый тип данных, а базовый тип данных также нужно преобразовать в обертку type использовать его встроенные методы, что, несомненно, увеличивает избыточность кода.
Так как же работает автоматическая распаковка и автоматическая упаковка?
На самом деле принцип, лежащий в основе этого, заключается в том, что компилятор выполняет оптимизацию. Присвоение базового типа классу-оболочке фактически вызывает класс-оболочку.valueOf()
Метод создает класс-оболочку и присваивает его базовому типу.
int i1 = Integer.valueOf(1);
Присвоение класса-оболочки базовому типу заключается в вызове метода xxxValue() класса-оболочки для получения базового типа данных и последующего его присвоения.
Integer i1 = new Integer(1).intValue();
Давайте используем javap -c для декомпиляции вышеприведенной автоматической упаковки и автоматической распаковки для проверки
Как видите, он вызывается по коду 2.invokestatic
В то же время это эквивалентно тому, что компилятор автоматически добавляет для нас метод Integer.valueOf для преобразования базового типа данных в тип-оболочку.
звонил по коду 7invokevirtual
В то же время это эквивалентно добавлению компилятором метода Integer.intValue() для преобразования значения Integer в базовый тип данных.
перечислить
Мы часто используем его в нашей повседневной разработкеenum
иpublic static final ...
такой синтаксис. Итак, когда использовать константы, такие как enum или public static final? Кажется, это возможно.
Но в структуре байт-кода Java нет типа перечисления.Перечисление — это всего лишь синтаксический сахар, после завершения компиляции он будет скомпилирован в обычный класс, который также украшен Class. Этот класс наследуется от java.lang.Enum и украшен ключевым словом final.
Возьмем пример
public enum School {
STUDENT,
TEACHER;
}
Это перечисление Школы, которое включает в себя два поля, одно СТУДЕНТ, другое УЧИТЕЛЬ, и больше ничего.
Ниже мы используемjavap
Декомпилируйте этот School.class . Результат после декомпиляции такой
Как видно из рисунка, перечисление на самом деле является наследством отjava.lang.Enum
класс класса. Свойства СТУДЕНТ и УЧИТЕЛЬ внутри по существуpublic static final
Измененное поле. На самом деле это оптимизация компилятора, ведь СТУДЕНТ гораздо красивее и лаконичнее, чем публичный статический финал Школьный СТУДЕНТ.
Вдобавок к этому компилятор сгенерирует нам два метода,values()
Методы иvalueOf
метод, оба из которых являются методами, добавленными компилятором для нас.Используя метод values(), мы можем получить все значения свойств Enum, а метод valueOf используется для получения одного значения свойства.
Обратите внимание, что метод values() в Enum не является частью JDK API, а в исходном коде Java нет соответствующей аннотации для метода values().
Использование заключается в следующем
public enum School {
STUDENT("Student"),
TEACHER("Teacher");
private String name;
School(String name){
this.name = name;
}
public String getName() {
return name;
}
public static void main(String[] args) {
System.out.println(School.STUDENT.getName());
School[] values = School.values();
for(School school : values){
System.out.println("name = "+ school.getName());
}
}
}
внутренний класс
Внутренний класс является Java-классом.小众
Причина, по которой я говорю «ниша», заключается не в том, что внутренние классы бесполезны, а в том, что мы редко используем их в нашей повседневной разработке, но, глядя на исходный код JDK, мы обнаруживаем, что во многих исходных кодах есть структуры внутренних классов. такие как общиеArrayList
В исходном коде естьItr
Внутренние классы наследуются отIterator
класс; другой примерHashMap
построилNode
Унаследовано от Map.Entry
Причина, по которой в язык Java вводятся внутренние классы, заключается в том, что иногда класс хочет быть полезным только в одном классе и не хочет, чтобы его использовали в других местах, то есть чтобы скрыть внутренние детали от внешнего мира.
Внутренний класс на самом деле является синтаксическим сахаром, потому что это только концепция времени компиляции.После завершения компиляции компилятор создаст отдельный файл класса для внутреннего класса с именем external$innter.class.
Давайте проверим это на примере.
public class OuterClass {
private String label;
class InnerClass {
public String linkOuter(){
return label = "inner";
}
}
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
InnerClass innerClass = outerClass.new InnerClass();
System.out.println(innerClass.linkOuter());
}
}
После того, как приведенный выше абзац будет скомпилирован, будут сгенерированы два файла классов, один из которыхOuterClass.class
,одинOuterClass$InnerClass.class
, что означает, что внешний класс может быть связан с внутренним классом, внутренний класс может изменять свойства внешнего класса и т. д.
Посмотрим на результат компиляции внутреннего класса
Как показано на рисунке выше, скомпилированный метод linkOuter() внутреннего класса создаст ссылку this на внешний класс, которая является ссылкой, соединяющей внешний класс и внутренний класс.
параметр переменной длины
Параметры переменной длины также являются относительно нишевым использованием.Так называемые параметры переменной длины — это методы, которые могут принимать параметры неопределенной длины. Как правило, мы не используем параметры переменной длины в нашей разработке, и параметры переменной длины не рекомендуются, что затруднит работу с нашими программами. Но нам нужно понимать характеристики параметров переменной длины.
Его основное использование заключается в следующем
public class VariableArgs {
public static void printMessage(String... args){
for(String str : args){
System.out.println("str = " + str);
}
}
public static void main(String[] args) {
VariableArgs.printMessage("l","am","cxuan");
}
}
Параметры переменной длины также являются синтаксическим сахаром, так как же это реализовано? Мы можем догадаться, что его внутренняя часть должна состоять из массивов, иначе он не может принимать несколько значений, тогда мы декомпилируем его, чтобы посмотреть, реализован ли он массивами.
Как видите, параметры printMessage() принимаются с использованием массива, поэтому не используйте параметры переменной длины.忽悠
!
Функция параметров переменной длины была введена в JDK 1.5. Существует два условия для использования параметров переменной длины. Одно из них заключается в том, что параметры переменной длины должны иметь один и тот же тип, а второе состоит в том, что параметры переменной длины должны быть в конец списка параметров метода.
Улучшенный цикл for
Почему существует расширенный цикл for после обычного цикла for? Подумайте об этом, разве вам не нужно знать количество итераций для обычного цикла for? Вам также нужно каждый раз знать индекс массива, что, очевидно, громоздко. Усовершенствованный цикл for является более мощным и более кратким, чем обычный цикл for, вам не нужно знать количество итераций и индекс массива для обхода.
Объект, улучшающий цикл for, представляет собой либо массив, либо реализует интерфейс Iterable. Этот синтаксический сахар в основном используется для обхода массива или коллекции и не может изменять размер коллекции во время цикла.
public static void main(String[] args) {
String[] params = new String[]{"hello","world"};
//增强for循环对象为数组
for(String str : params){
System.out.println(str);
}
List<String> lists = Arrays.asList("hello","world");
//增强for循环对象实现Iterable接口
for(String str : lists){
System.out.println(str);
}
}
Скомпилированный файл класса выглядит следующим образом
public static void main(String[] args) {
String[] params = new String[]{"hello", "world"};
String[] lists = params;
int var3 = params.length;
//数组形式的增强for退化为普通for
for(int str = 0; str < var3; ++str) {
String str1 = lists[str];
System.out.println(str1);
}
List var6 = Arrays.asList(new String[]{"hello", "world"});
Iterator var7 = var6.iterator();
//实现Iterable接口的增强for使用iterator接口进行遍历
while(var7.hasNext()) {
String var8 = (String)var7.next();
System.out.println(var8);
}
}
Как показано в приведенном выше коде, если вы улучшите цикл for в массиве, он по-прежнему будет проходить внутри массива, но синтаксический сахар обманывает вас, позволяя писать код более лаконичным образом.
А расширенный обход цикла for итератора, унаследованный от Iterator, эквивалентен вызову IteratorhasNext()
иnext()
метод.
Switch поддерживает строки и перечисления
switch
Ключевое слово изначально поддерживает только整数
тип. Если за переключателем следует тип String, компилятор преобразует его в StringhashCode
Значение , так что на самом деле синтаксис переключателя сравнивает хэш-код String .
как показано в коде ниже
public class SwitchCaseTest {
public static void main(String[] args) {
String str = "cxuan";
switch (str){
case "cuan":
System.out.println("cuan");
break;
case "xuan":
System.out.println("xuan");
break;
case "cxuan":
System.out.println("cxuan");
break;
default:
break;
}
}
}
Давайте декомпилируем и посмотрим, верна ли наша догадка
В соответствии с байт-кодом видно, что фактический переключатель является хэш-кодом для оценки, а затем используйте метод equals для сравнения, поскольку строка может вызвать конфликт хэшей.
Условная компиляция
Это сбивает с толку моих друзей Что такое условная компиляция? На самом деле, если вы использовали C или C++, вы знаете, что условную компиляцию можно выполнить с помощью подготовленных операторов.
Так что же такое условная компиляция?
В нормальных условиях в компиляции участвуют все строки исходной программы. Но иногда вы хотите скомпилировать какой-то контент только при соблюдении определенных условий, то есть указать условия компиляции для некоторого контента, т.е.条件编译(conditional compile)
.
#define DEBUG
#IFDEF DEBUUG
/*
code block 1
*/
#ELSE
/*
code block 2
*/
#ENDIF
Но в Java нет предварительной обработки и определения макросов, так что же нам делать, если мы хотим добиться условной компиляции?
Условная компиляция может быть достигнута с помощью комбинации final + if. как показано в коде ниже
public static void main(String[] args) {
final boolean DEBUG = true;
if (DEBUG) {
System.out.println("Hello, world!");
} else {
System.out.println("nothing");
}
}
Что происходит с этим кодом? Декомпилируем и посмотрим
Мы видим, что мы явно использовали оператор if...else, но компилятор только скомпилировал для нас условие DEBUG = true,
Следовательно, условная компиляция синтаксиса Java реализуется путем определения оператором if, что условие является постоянным, и компилятор не будет компилировать для нас код, который переходит в false.
утверждение
Вы когда-нибудь использовали утверждения в Java в качестве ежедневного условия суждения?
Утверждение: т. н.assert
Ключевое слово — это новая функция, добавленная после jdk 1.4. Он в основном используется во время разработки кода и тестирования для оценки некоторых ключевых данных.Если эти ключевые данные не соответствуют данным, ожидаемым вашей программой, программа выдаст предупреждение или завершит работу. Когда программное обеспечение будет официально выпущено, код в части утверждения может быть отменен. Это тоже синтаксический сахар? Не буду сейчас рассказывать, давайте посмотрим, как используется assert.
//这个成员变量的值可以变,但最终必须还是回到原值5
static int i = 5;
public static void main(String[] args) {
assert i == 5;
System.out.println("如果断言正常,我就被打印");
}
Если вы хотите включить проверку утверждений, вам нужно использовать переключатель -enableassertions или -ea, чтобы включить его. На самом деле базовая реализация утверждения — это суждение if.Если результат утверждения истинен, ничего не будет сделано, и программа продолжит выполнение.Если результат утверждения ложен, программа выдаст AssertError, чтобы прервать выполнение программы.
Утверждение утверждения является суждением о бите логического флага.
try-with-resources
Начиная с JDK 1.7, в java появился оператор try-with-resources, упрощающий try-catch-finally до try-catch, который на самом деле является своего рода语法糖
, который преобразуется в оператор try-catch-finally во время компиляции. Новое объявление состоит из трех частей: объявления try-with-resources, блока try и блока catch. Он требует, чтобы переменные, определенные в объявлении try-with-resources, реализовывали интерфейс AutoCloseable, чтобы их метод close мог автоматически вызываться системой, заменяя функцию finally закрытия ресурсов.
как показано в коде ниже
public class TryWithResourcesTest {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream(new File("xxx"))) {
inputStream.read();
}catch (Exception e){
e.printStackTrace();
}
}
}
Мы можем взглянуть на код после декомпиляции try-with-resources.
Видно, что сгенерированный try-with-resources по-прежнему использует оператор try...catch...finally после компиляции, но эту часть работы делает за нас компилятор, что может сделать наш код более лаконичным, Таким образом, устраняя шаблонный код.
Добавление строки
Это наверное все должны знать, есть два вида склейки строк, если результат склейки можно определить во время компиляции, то используйте+
Конкатенированные строки будут напрямую оптимизированы компилятором в результате добавления.Если результат конкатенации не может быть определен во время компиляции, нижний слой будет напрямую использоватьStringBuilder
изappend
Сращивание выполняется, как показано на рисунке ниже.
public class StringAppendTest {
public static void main(String[] args) {
String s1 = "I am " + "cxuan";
String s2 = "I am " + new String("cxuan");
String s3 = "I am ";
String s4 = "cxuan";
String s5 = s3 + s4;
}
}
Приведенный выше код содержит результат двух видов конкатенации строк, давайте декомпилируем и посмотрим
Давайте сначала посмотрим на s1.Поскольку справа от знака = есть две константы, конкатенация двух строк будет напрямую оптимизирована, чтобы статьI am cxuan
. Поскольку s2 размещает объект cxuan в пространстве кучи, сращивание строк с обеих сторон знака + будет напрямую преобразовано в StringBuilder, вызовет его метод добавления к сплайсингу и, наконец, вызовет метод toString() для преобразования его в строку.
Поскольку два объекта, склеенные s5, не могут определить результат сращивания во время компиляции, StringBuilder будет использоваться непосредственно для сращивания.
Узнать значение синтаксического сахара
В эпоху Интернета одна за другой появляется множество нетрадиционных идей и фреймворков, но мы должны понять суть технологии обучения. Тем не менее, программная инженерия — это искусство сотрудничества. Что касается разработки, то как улучшить качество разработки и как повысить эффективность разработки также является нашей заботой. Поскольку эти синтаксические сахара могут помочь нам лучше писать популярный код, мы, почему программисты抵制
Шерстяная ткань?
Синтаксический сахар — это тоже своего рода прогресс, который подобен вашему письму.Если просторечие может рассказать историю ясно, то рассказывать историю живо и живо будет не приятнее, чем язык.
Нам нужно раскрыть свои объятия, чтобы принять изменения, в то же время овладев их屠龙之技
.
Кроме того, я сам перелил шесть PDF-файлов.После того, как программист поиска WeChat cxuan обратил внимание на официальный аккаунт, я ответил cxuan в фоновом режиме и получил все PDF-файлы.Эти PDF-файлы выглядят следующим образом