Java 语法糖

一、什么是语法糖?

语法糖,又称糖衣语法,是英国计算机科学家发明的一个术语,指在计算机语言中加入某种语法,这种语法对语言的功能并没有影响,但是方便了程序员的操作,并且增加了代码的可读性,减少了出错的机会。

Java 是一个 “低糖语言”,因为在 JDK1.5 之前,语法糖很少出现。但是从 Java 7 开始 Java 语言层面上一直在添加各种糖,未来还会持续向着 “高糖” 的方向发展。

Java 中最常用的语法糖主要有泛型、switch 支持 String、自动装箱与拆箱、foreach 循环、内部类和枚举等。

二、解语法糖

语法糖主要是方便开发人员使用。但其实,Java 虚拟机并不支持这些语法糖,这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖

三、Java 语法糖

1、泛型与类型擦除

很多语言都是支持泛型的,但是不同的编译器对于泛型的处理方式是不同的。通常,编译器处理泛型有两种方式:Code specialization 和 Code sharing。

  • C++ 和 C# 是使用 Code specialization 的处理机制。在 C++ 和 C# 中,泛型无论在源代码还是编译后,都是切实存在的,List< Integer> 和 List< String> 就是两个不同的类型,他们在系统运行期生成,有自己的虚方法表和类型数据,这种实现称为类型膨胀,基于这种语法实现的泛型称为真实泛型。
  • Java 使用的是 Code sharing。Code sharing 方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除实现的。

也就是说,在 Java 虚拟机的运行期,ArrayList< Integer > 和 ArrayList< String > 是一样的。Java 需要在编译阶段通过类型擦除的方式进行解语法糖。

类型擦除的主要过程如下:

  • 将所有的泛型参数用其最左边界(最顶级的父类型)类型替换;
  • 移除所有的类型参数。
示例一:

源代码:

Map<String, String> map = new HashMap<String, String>(); 
map.put("kobe", "Lakers");
map.put("curry", "worries");

解语法糖之后(擦除类型后):

Map map = new HashMap(); 
map.put("kobe", "Lakers");
map.put("curry", "worries");
示例二:

源代码:

public static <A extends Comparable<A>>A max(Collection<A> c) {        
	Iterator<A> it = c.iterator();
	A w = it.next();
	while (it.hasNext()) {
		A x = it.next();
		if (w.compareTo(x) < 0) 
			w = x;
		}
	return w;
}

解语法糖之后(擦除类型后):

public static Comparable max(Collection c) {        
	Iterator it = c.iterator();
	Comparable w = (Comparable)it.next();
	while (it.hasNext()) {
		Comparable x = (Comparable)it.next();
		if (w.compareTo(x) < 0) 
			w = x;
		}
	return w;
}

Java 虚拟机中没有泛型,只有普通类和普通方法,所有泛型类的类型参数在编译时都会被擦除。泛型类并没有自己独有的 Class 类对象,比如并不存在 List< String>.class或是 List< Integer>.class,而只有 List.class。

2、switch 支持 String

在 Java 7 之前,switch 只能支持 byte、short、char、int 或者其对应的封装类以及 Enum 类型。对于 int 类型,直接进行数值的比较;对于 char 类型,则是比较其 ascii 码。所以,对于编译器来说,switch 中其实只能使用整型,任何类型的比较都要转换成整型。

从 Java 7 开始,switch 开始支持 String。测试代码:

public class Test {
	public static void main(String[] args) {
		String str = "world"; 
		switch (str) {
		case "hello": 
			System.out.println("hello"); 
			break;
		case "world": 
			System.out.println("world");
			break;
		default:
			break;
		}
	}
}

反编译后,代码如下:

public class Test {
	public static void main(String args[]){
		String str = "world";
		String s;
		switch((s = str).hashCode()){
		default:
			break;
		case 99162322:
			if(s.equals("hello"))
				System.out.println("hello");
			break;
		case 113318802:
			if(s.equals("world"))
				System.out.println("world");
			break;
		}
	}
}

由反编译的结果看出,String 的 switch 是通过 equals() 和 hashCode() 方法来实现的。hashCode() 方法返回的是 int。

3、自动装箱与拆箱

自动装箱就是 Java 自动将原始基本类型值转换成对应的对象类型,比如将 int 变量转换成 Integer 对象,这个过程叫做装箱。反之将 Integer 对象转换成 int 类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。

基本类型 byte、short、char、int、long、float、double 和 boolean 对应的封装类分别为 Byte、Short、Character、Integer、Long、Float、Double、Boolean。

(1)自动装箱测试代码:
public static void main(String[] args) {
	int i = 10;
	Integer n = i;
}

反编译后代码如下:

public static void main(String args[]) {
	int i = 10;
	Integer n = Integer.valueOf(i);
}

自动装箱的实现就是调用了 Integer.valueOf() 方法。

(2)自动拆箱测试代码:
public static void main(String[] args) {
	Integer i = 10;
	int n = i;
}

反编译后代码如下:

public static void main(String args[]) {
	Integer i = Integer.valueOf(10);
	int n = i.intValue();
}

4、foreach 循环

public static void main(String[] args) {
	List<Integer> list = new ArrayList<Integer>();
	list.add(500);
	list.add(2);
	list.add(new Integer(20));
	for(int sub : list){
		System.out.println(sub);
	}
}

反编译后,代码如下:

public static void main(String[] args) {
    List list = new ArrayList();
    list.add(Integer.valueOf(500));
    list.add(Integer.valueOf(2));
    list.add(new Integer(20));
    for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ){
    	int sub = ((Integer)localIterator.next()).intValue();
    	System.out.println(sub);
    }
}

foreach 实际上是在调用 list 的 iterator 进行遍历。这也就是为什么 foreach 循环要求被遍历的集合必须实现了 Iterator 接口。

5、内部类

内部类又称为嵌套类,可以把内部类理解为外部类的一个普通成员。内部类之所以也是语法糖,是因为它仅仅是一个编译时的概念。

6、枚举

Java SE5 提供了一种新的类型——Java 的枚举类型。关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。定义一个枚举类型:

public enum t {
	SPRING,SUMMER;
}

enum 和 class 一样,只是一个关键字,它并不是一个类。

当我们使用 enum 来定义一个枚举类型的时候,编译器会自动帮我们创建一个 final 类型的类并继承 Enum 类来实现枚举,所以枚举类型不能被继承。

参考自:

Java 语法糖详解

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值