语法糖
接下来几篇文章要开启一个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:代码MyEclipseTestArticleincom\xrq est21>javap -verbose TestMain.class
反编译出来的内容很多,有类信息、符号引用、字节码信息,截取一段信息:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #16 // class java/util/ArrayList
3: dup
4: invokespecial #18 // Method java/util/ArrayList."<in
it>":()V
7: astore_1
8: aload_1
9: ldc #19 // String 111
11: invokeinterface #21, 2 // InterfaceMethod java/util/List.
add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: ldc #27 // String 222
20: invokeinterface #21, 2 // InterfaceMethod java/util/List.
add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: invokeinterface #29, 1 // InterfaceMethod java/util/List.
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);
}
同样反编译一下,看一下关键的信息:
0: iconst_2
1: newarray int
3: dup
4: iconst_0
5: iconst_1
6: iastore
7: dup
8: iconst_1
9: iconst_2
10: iastore
11: astore_1
12: aload_1
13: dup
14: astore 5
16: arraylength
17: istore 4
19: iconst_0
20: istore_3
21: goto 39
24: aload 5
26: iload_3
27: iaload
28: istore_2
29: getstatic #16 // Field java/lang/System.out:Ljav
a/io/PrintStream;
32: iload_2
33: invokevirtual #22 // Method java/io/PrintStream.prin
tln:(I)V
36: iinc 3, 1
39: iload_3
40: iload 4
42: if_icmplt 24
45: return
这是完整的这段main函数对应的45个字节码指令,因为这涉及一些压栈、出栈、推送等一些计算机原理性的内容且对于这些字节码指令的知识的理解需要一些C++的知识,所以就不解释了。简单对照字节码指令表之后,我个人对于这45个字节码的理解是Java将对于数组的foreach循环转换为对于这个数组每一个的循环引用。
在这里给大家提供一个学习交流的平台,java架构师群: 867748702
具有1-5工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加群。
在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加群。
如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的可以加群。
加Java架构师进阶交流群获取Java工程化、高性能及分布式、高性能、深入浅出。高架构。
性能调优、Spring,MyBatis,Netty源码分析和大数据等多个知识点高级进阶干货的直播免费学习权限
都是大牛带飞 让你少走很多的弯路的 群号是: 867748702对了 小白勿进 最好是有开发经验
注:加群要求
1、具有工作经验的,面对目前流行的技术不知从何下手,需要突破技术瓶颈的可以加。
2、在公司待久了,过得很安逸,但跳槽时面试碰壁。需要在短时间内进修、跳槽拿高薪的可以加。
3、如果没有工作经验,但基础非常扎实,对java工作机制,常用设计思想,常用java开发框架掌握熟练的,可以加。
4、觉得自己很牛B,一般需求都能搞定。但是所学的知识点没有系统化,很难在技术领域继续突破的可以加。
5.阿里Java高级大牛直播讲解知识点,分享知识,多年工作经验的梳理和总结,带着大家全面、科学地建立自己的技术体系和技术认知!
转载于:https://blog.51cto.com/13963248/2300307