Java 语法糖以及常见的应用

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 语法糖以及常见的应用相关的知识,希望对你有一定的参考价值。

文章目录

Java 语法糖

我们先了解一下 语法糖 的概念:语法糖(Syntactic sugar),也叫做糖衣语法,是英国科学家发明的一个术语,通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

语法糖指的是计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。

总结:它不是吃的糖!Java 语法糖指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。

因为 Java 代码需要运行在 JVM 中,JVM 是并不支持语法糖的,语法糖在程序编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖

所以在 Java 中,真正支持语法糖的是 Java 编译器

Java 中提供了有很多语法糖来方便程序开发,虽然语法糖不会提供实质性的功能改进,但是它能提升开发效率、语法的严谨性、减少编码出错的机会。

下面是Java中常见的语法糖,我们可以从中了解到Java运行过程中我们看不到的那些概念:

常见应用场景

泛型与类型擦除

泛型顾名思义就是类型泛化,本质是参数化类型的应用,也就是说操作的数据类型被指定为一个参数。这种参数可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

Java 语言中泛型只在程序源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型,并且在相应的地方插入了强制类型转换的代码

因此对于运行期的 Java 语言来说, ArrayList<Integer>ArrayList<String> 是同一个类型,所以泛型实际上是 Java 语言的一个语法糖,这种泛型的实现方法称为类型擦除。

例如下面代码:

// 泛型与类型擦除
List<Integer> integers = new ArrayList<>();
List<String> strings = new ArrayList<>();
System.out.println(integers.getClass() == strings.getClass());

运行结果如下:

List<Ineger> List<String>被认为是不同的类型,但是输出却得到了相等的结果,这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。

但是,如果将一个 Integer 类型的数据放入到 List 中或者将一个 String 类型的数据放在 List 中是不允许的,这个在编译阶段就会识别错误。

自动装箱、拆箱与遍历循环

自动拆箱和自动装箱是一种语法糖,它说的是八种基本数据类型的包装类和其基本数据类型之间的自动转换。简单的说,装箱就是自动将基本数据类型转换为包装器类型;拆箱就是自动将包装器类型转换为基本数据类型。

基本数据类型的包装类如下所示:


就例如:

// 自动拆箱装箱
Integer integer = 66;   // 自动拆箱
int i1 = integer;       // 自动装箱

其实这背后的原理是编译器做了优化。将基本类型赋值给包装类其实是调用了包装类的 valueOf() 方法创建了一个包装类再赋值给了基本类型。

int i1 = Integer.valueOf(1);

源码实现如下:

而包装类赋值给基本类型就是调用了包装类的 xxxValue() 方法拿到基本数据类型后再进行赋值。

Integer i1 = new Integer(1).intValue(); 

源码实现如下:

条件编译

Java 语言中条件编译的实现也是一颗语法糖,根据布尔常量值的真假,编译器会把分支中不成立的代码块消除:

if (true) 
    System.out.println("true");
 else 
    System.out.println("false");

上述代码经过编译后 class 文件的反编译结果:

System.out.println("true");

变长参数

变长参数也是一个比较小众的用法,所谓变长参数,就是方法可以接受长度不定确定的参数。一般我们开发不会使用到变长参数,而且变长参数也不推荐使用,它会使我们的程序变的难以处理。但是我们有必要了解一下变长参数的特性。

其基本用法如下:

public class Test02 
    public static void printMessage(String... args)
        for(String str : args)
            System.out.println("str = " + str);
        
    

    public static void main(String[] args) 
        Test02.printMessage("wo","shi","不","是");
    

运行结果如下:

实际上 printMessage() 的参数就是使用了一个该参数类型的数组来接收

变长参数特性是在 JDK 1.5 中引入的,使用变长参数有两个条件

  • 一是变长的那一部分参数具有相同的类型
  • 二是变长参数必须位于方法参数列表的最后面。

增强 for 循环

普通 for 循环我们需要知道遍历次数,每次还需要知道数组的索引是多少,这种写法明显有些繁琐。

增强 for 循环与普通 for 循环相比,功能更强并且代码更加简洁,你无需知道遍历的次数和数组的索引即可进行遍历。增强 for 循环的对象要么是一个数组,要么实现了 Iterable 接口。这个语法糖主要用来对数组或者集合进行遍历,其在循环过程中不能改变集合的大小。

例如下面代码:

我们只需要在数组或者集合后面打for就能自动填入增强for循环的语法:


完整代码如下:

public class Test03 
    public static void main(String[] args) 
        String[] params = new String[]"hello", "world";
        // 增强for循环对象为数组
        for (String param : params) 
            System.out.println(param);
        

        List<String> lists = Arrays.asList("hello", "world");
        // 增强for循环对象实现Iterable接口
        for (String list : lists) 
            System.out.println(list);
        
        lists.stream().forEach(s -> System.out.println(s));
    


经过编译后的 class 文件如下:

  • 对数组进行增强 for 循环的话,其内部还是对数组进行遍历,只不过语法糖把你忽悠了,让你以一种更简洁的方式编写代码。
  • 而对继承于 Iterator 迭代器进行增强 for 循环遍历的话,相当于是调用了 Iterator 的 hasNext() 和 next() 方法

你在 Java 中使用过断言作为日常的判断条件吗?

断言:也就是所谓的 assert 关键字,是 jdk 1.4 后加入的新功能。它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。

当软件正式发布后,可以取消断言部分的代码。它其实也是一种语法糖,我们先来看一下 assert 如何使用:

如果要开启断言检查,则需要用开关 -enableassertions-ea来开启,可以通过配置运行时jvm参数设置。

编写代码如下:

public class Test04 
    //这个成员变量的值可以变,但最终必须还是回到原值5
    static int i = 4;
    public static void main(String[] args) 
        assert i == 5;
        System.out.println("如果断言正常,我就被打印");
    

运行报错

如果设置为5

static int i = 5;

就可以正常打印:

其实断言的底层实现原理 就是 if 判断,如果断言结果为 true,则什么都不做,程序继续执行,如果断言结果为 false,则程序抛出 AssertError 来打断程序的执行。

assert 断言就是通过对布尔标志位进行了一个 if 判断

以上是关于Java 语法糖以及常见的应用的主要内容,如果未能解决你的问题,请参考以下文章

JVM:Java中的语法糖

Java语法糖1:可变长度参数以及foreach循环原理

深入理解java虚拟机 Java 语法糖背后的真相

什么是语法糖?

Java 语法糖详解

Java 中的 6 颗语法糖