语法糖

接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖。语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了。这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能、或能提升语法的严谨性、或能减少编码出错的机会。Java提供给了用户大量的语法糖,比如泛型、自动装箱、自动拆箱、foreach循环、变长参数、内部类、枚举类、断言(assert)等。

可变长度参数

先讲可变长度参数,看一段代码:

public static void main(String[] args)
{print("000", "111", "222", "333");
}public static void print(String... strs)
{for (int i = 0; i < strs.length; i++){System.out.println(strs[i]);}
}

print方法的参数的意思是表示传入的String个数是不定的,看一下代码的运行结果:

000
111
222
333

我用数组遍历的方式成功地将输入的参数遍历出来了,这说明两个问题:

1、可以使用遍历数组的方式去遍历可变参数

2、可变参数是利用数组实现的

既然这样,那我其实main函数也可以这么写,完全可以:

String[] strs = {"000", "111", "222", "333"};
print(strs);

那直接传入一个数组不就好了?问题是,数组是要指定长度的,万一这次我想传2个String,下次我想传3个String怎么办呢?

最后,注意一点,可变长度参数必须作为方法参数列表中的的最后一个参数且方法参数列表中只能有一个可变长度参数

foreach循环原理

以前对foreach循环就是这么用着,触动我去研究foreach循环的原理的原因是大概两个月前,自己写了一个ArrayList,想用foreach循环遍历一下看一下写的效果,结果报了空指针异常。本文就写写foreach循环的原理,先看一下这么一段代码:

public static void main(String[] args)
{List<String> list = new ArrayList<String>();list.add("111");list.add("222");for (String str : list){System.out.println(str);}
}

用foreach循环去遍历这个list,结果就不说了,都知道。看一下Java是如何处理这个foreach循环的,javap反编译一下:

F:\代码\MyEclipse\TestArticle\bin\com\xrq\test21>javap -verbose TestMain.class

反编译出来的内容很多,有类信息、符号引用、字节码信息,截取一段信息:

 1   public static void main(java.lang.String[]);
 2     flags: ACC_PUBLIC, ACC_STATIC
 3     Code:
 4       stack=2, locals=4, args_size=1
 5          0: new           #16                 // class java/util/ArrayList
 6          3: dup
 7          4: invokespecial #18                 // Method java/util/ArrayList."<in
 8 it>":()V
 9          7: astore_1
10          8: aload_1
11          9: ldc           #19                 // String 111
12         11: invokeinterface #21,  2           // InterfaceMethod java/util/List.
13 add:(Ljava/lang/Object;)Z
14         16: pop
15         17: aload_1
16         18: ldc           #27                 // String 222
17         20: invokeinterface #21,  2           // InterfaceMethod java/util/List.
18 add:(Ljava/lang/Object;)Z
19         25: pop
20         26: aload_1
21         27: invokeinterface #29,  1           // InterfaceMethod java/util/List.
22 iterator:()Ljava/util/Iterator;

看不懂没关系,new、dup、invokespecial这些本来就是字节码指令表内定义的指令,虚拟机会根据这些指令去执行指定的C++代码,完成每个指令的功能。关键看到21、22这两行就可以了,看到了一个iterator,所以得出结论:在编译的时候编译器会自动将对for这个关键字的使用转化为对目标的迭代器的使用,这就是foreach循环的原理。进而,我们再得出两个结论:

1、ArrayList之所以能使用foreach循环遍历,是因为ArrayList所有的List都是Collection的子接口,而Collection是Iterable的子接口,ArrayList的父类AbstractList正确地实现了Iterable接口的iterator方法。之前我自己写的ArrayList用foreach循环直接报空指针异常是因为我自己写的ArrayList并没有实现Iterable接口

2、任何一个集合,无论是JDK提供的还是自己写的,只要想使用foreach循环遍历,就必须正确地实现Iterable接口

实际上,这种做法就是23中设计模式中的迭代器模式

数组呢?

上面的讲完了,好理解,但是不知道大家有没有疑问,至少我是有一个疑问的:数组并没有实现Iterable接口啊,为什么数组也可以用foreach循环遍历呢?先给一段代码,再反编译:

public static void main(String[] args)
{int[] ints = {1,2,3,4,5};for (int i : ints)System.out.println(i);
}

同样反编译一下,看一下关键的信息:

 1          0: iconst_2
 2          1: newarray       int
 3          3: dup
 4          4: iconst_0
 5          5: iconst_1
 6          6: iastore
 7          7: dup
 8          8: iconst_1
 9          9: iconst_2
10         10: iastore
11         11: astore_1
12         12: aload_1
13         13: dup
14         14: astore        5
15         16: arraylength
16         17: istore        4
17         19: iconst_0
18         20: istore_3
19         21: goto          39
20         24: aload         5
21         26: iload_3
22         27: iaload
23         28: istore_2
24         29: getstatic     #16                 // Field java/lang/System.out:Ljav
25 a/io/PrintStream;
26         32: iload_2
27         33: invokevirtual #22                 // Method java/io/PrintStream.prin
28 tln:(I)V
29         36: iinc          3, 1
30         39: iload_3
31         40: iload         4
32         42: if_icmplt     24
33         45: return

这是完整的这段main函数对应的45个字节码指令,因为这涉及一些压栈、出栈、推送等一些计算机原理性的内容且对于这些字节码指令的知识的理解需要一些C++的知识,所以就不解释了。简单对照字节码指令表之后,我个人对于这45个字节码的理解是Java将对于数组的foreach循环转换为对于这个数组每一个的循环引用

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

  1. java语法糖效率高吗_打包 Java将持续向“高糖”方向发展,你真的了解Java语法糖吗? _好机友...

    Java语法糖概念 1. 语法糖Syntactic Sugar 糖衣语法,方便开发人员使用,JVM并不识别,会在编译阶段解语法糖,还原为基础语法. 2. com.sun.tools.javac.mai ...

  2. JAVA语法糖(全)

    JAVA语法糖(全) 目录 概述 字符串拼接 条件编译 断言 枚举与Switch语句 字符串与Switch语句 可变参数 自动装箱/拆箱 枚举 内部类 泛型擦除 增强for循环 lambda表达式 t ...

  3. 65.Java语法糖

    65.Java语法糖 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没 ...

  4. Java 语法糖详解

    语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序 ...

  5. for-each 循环原理

    for-each 循环原理 1,for-each 是在java5 之后出现的.for是java 上的一个关键字,在jdk 找不到任何for的底层实现的.是因为for的底层实现被封装到了编译器中.所以通 ...

  6. Java语法糖之foreach

    语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语 ...

  7. Jvm 系列(十一)Java 语法糖背后的真相

    语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言 ...

  8. Java中switch参数传null会引起异常——Java 语法糖

    问题 switch 参数不能是null,swicth(null)会报java.lang.NullPointerException异常 查找原因 为什么会这样呢,查找一下原因: 找到编译后的class文 ...

  9. 全网最全的 Java 语法糖指南

    写在前面 本文隶属于专栏<100个问题搞定Java虚拟机>,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢! 本专栏目录结构和文献引用请见100个问题搞定Java ...

最新文章

  1. 很好的Markdown开源库
  2. 简单的利用IDEA搭建SpringBoot+Maven+Mybatis+自动生成代码
  3. 百度UEditor编辑器关闭抓取远程图片功能(默认开启)
  4. 成功解决Future Warning: The sklearn.neighbors.dist_metrics module is deprecated in version 0.22 and wil
  5. Open WebRTC Toolkit实时视频分析系统
  6. C++数据抽象和问题求解(第6版)
  7. sql server版本 性能_迁移到高版本 SQL 数据库后,性能变差了
  8. windows10风格 springboot mybatis 项目框架源码 shiro 集成代码生成器
  9. unix 时间戳转化为 日期格式
  10. php异步表单,利用ajax实现表单的异步互动——2018年4月10日
  11. 提取excel表数据成json格式的以及对图片重命名
  12. 程序员代码面试指南-左PDF
  13. 在VM安装最新版Linux镜像
  14. 软件测试思想者 - 初识抑郁症
  15. 树莓派编译工作空间卡死
  16. Accuracy and precision 意义
  17. 概率算法-均匀分布产生正态分布
  18. 求生之路服务器未响应,求生之路2玩起来速度快,但是过几秒后,就卡住显示未响应,这是为什么...
  19. 悍将余承东「调防」,华为云能翻盘吗?
  20. 机动车驾驶科一/科四

热门文章

  1. VTK:VTK嵌入MFC成功
  2. 实践:使用FLANN.LSH进行检索
  3. .NET多线程编程(7)——C#多线程编程传递参数解决方案
  4. Javascript中的async await
  5. 谈谈CTO、技术总监、首席架构师的区别
  6. python 的下划线_
  7. 并行开发 —— 第三篇 plinq的使用
  8. 多个Activity关闭问题
  9. Discrete Logging hunnu10590 pku2417 fzu 1352 hit 1928 zoj 1898
  10. Android中AVD(Android Virtual Device)不能启动的处理方法