JAVA新手入门08~语法糖

Java的语法糖,就是编译器为了方便大家写代码,对于一些代码,做了一些简化的语法,可以让大家在平时写代码的时候,更方便写出容易阅读的代码,但是在编译完之后,编译器会把这些代码,再还原回来。在Java中,有1个大家最经常使用的语法糖,就是整形变量的自动装箱,Integer a = 10;很明显10不是一个对象,Integer是一个对象,不同的东西,不能赋值,为啥还可以这么做呢? 这个东西就是Java的语法糖。

Java中的语法糖有以下几种:
  1. 泛型(JDK1.5)

  2. 自动装箱和拆箱(JDK1.5)

  3. 变长参数(JDK1.5)

  4. 实现Iterable接口的foreach语法(JDK1.5)

  5. 接口AutoCloseable支持的try-with-resource用法(JDK1.7)

  6. switch支持枚举和String(JDK1.7)

1、泛型(JDK1.5)

泛型是1个编程语言是否友好的重要标志,时下火热的Go语言,至今还没有实现泛型,Java中的泛型是通过类型擦除实现的,并不是真正意义上的泛型,由于本文不打算深入讲解泛型,在此不过多叙述。看以下泛型编译之后,被JVM实际执行的代码是什么样子的

源代码(1.5之前)

List arr = new ArrayList();
arr.add("this is first");
arr.add(123);
for(Object obj : arr) {
    System.out.println(obj);
}
// Class文件(1.5之前):
List arr = new ArrayList();
arr.add("this is first");
arr.add(123);
Iterator var6 = arr.iterator();

while(var6.hasNext()) {
   Object obj = var6.next();
   System.out.println(obj);
 }

JDK1.5之后,为了兼容1.5之前的代码,老代码可以保持不变,新代码如果是:

List<String> arr = new ArrayList<String>();
arr.add("this is first");
arr.add(123);

会报编译器错误,原理是一样的,都会在编译期转变成Object对象,然后强制转换成需要的类型。

2、自动装箱和拆箱(JDK1.5)

直接上代码吧,这个比较容易理解,就是JAVA会自动在基本类型和基本类型的包装类之间,进行自动转换,当然这只是一种语法糖,代码编译成class文件的时候,就会自动把这个转换成对应的方法:

//源代码
int a = 1290;
Integer b = 1000;
Integer a1 = a;  // 自动装箱
int b1 = b.intValue(); //自动拆箱

// Class文件:
int a = 1290;
Integer b = new Integer(1000);
Integer a1 = Integer.valueOf(a);
int b1 = b; 

其它的如:

Character ch = 'a'
String s = "abc";

Integer a = 10;
int b = a + 100; //操作符做运算触发自动拆箱

//函数调用参数是包装类,触发自动拆箱/装箱
int c = add(10,30);
private int add(Integer a,Integer b) {
return a + b;
}

Java中的以下几种类型都支持自动拆箱,装箱。

  • Primitive type Wrapper class
  • boolean Boolean
  • byte Byte
  • char Character
  • float Float
  • int Integer
  • long Long
  • short Short
  • double Double

资料来源:
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

3、变长参数(JDK1.5)

变长参数指的是,一个方法的参数,可以有多个,具体有多少个,不确定,如果没有语法糖,我们一般会传一个List的引用进去,下面来看看,Java的这个变长参数是怎么实现的(强制转换为数组),不过平时我用的也不多。

//源代码:
private void printStr(String... arr) {
    for (String s : arr) {
    	System.out.println(s);
    }
}
//class文件:
private void printStr(String... arr) {
    String[] var2 = arr;    //这里可以看出来,先把参数转为数组,然后读取数组来做的
    int var3 = arr.length;

    for(int var4 = 0; var4 < var3; ++var4) {
      String s = var2[var4];
      System.out.println(s);
    }
}
4、实现Iterable接口的foreach语法(JDK1.5)

这个是平时用到的最多的一个语法糖了,如果你刚开始写Java,会遇到很多这种代码,算是用的比较多的一个了,其实上面代码中有体现,直接转Iterator迭代实现,所以需要实现了Iterable接口的类,才可以使用这个语法糖。

// 源代码
List<String> arr = new ArrayList<String>();
for (String s : arr) {
	System.out.println(s);
}
//class文件:
Iterator var6 = arr.iterator();
while(var6.hasNext()) {
  String s = (String)var6.next();
  System.out.println(s);
}
5、接口AutoCloseable支持的try-with-resource用法(JDK1.7)

这个用的就更少了,我很少在实际的项目中见到这样的代码,也可能是因为平时读取资源的文件不多,平时很多时候都是CRUD了,仅仅是1个糖,编译后,又还原了最初的样子,还是传统的try{} catch() {} finally{} 这个结构。

//源代码:

String content = "this is what you want to write to file";
try (BufferedWriter out = new BufferedWriter(new
       FileWriter("/tmp/a.txt", true))) {
       out.write(content);
       System.out.println("content is :" + content);
} catch (IOException e) {
// error processing code
} finally {

}

//class
try {
   BufferedWriter out = new BufferedWriter(new FileWriter("/tmp/a.txt", true));
   Throwable var8 = null;
  try {
    out.write(content);
    System.out.println("content is :" + content);  
  } catch (Throwable var26) {
    var8 = var26;
    throw var26;
  } finally {
    if (out != null) {
      if (var8 != null) {
        try {
          out.close();
        } catch (Throwable var25) {
          var8.addSuppressed(var25);
        }
      } else {
        out.close();
      }
  }
}
} catch (IOException var28) {

} finally {

}
6、switch支持枚举和String(JDK1.7)

这个就比较冷门了,平时编程使用switch的时候,很少使用String来做枚举,一般都是用int的比较多。

//源代码:
String s = "abc";
  switch (s) {
    case "abc":
        System.out.println("this is abc");
        break;
    case "c":
        System.out.println("this is c");
        break;
    default:
        break;
  }


//class文件:
String s = "abc";
byte var3 = -1;
switch(s.hashCode()) {
  case 99:
    if (s.equals("c")) {
       var3 = 1;
    }
    break;
    case 96354:
      if (s.equals("abc")) {
        var3 = 0;
      }
}

switch(var3) {
    case 0:
      System.out.println("this is abc");
      break;
    case 1:
      System.out.println("this is c");
}

可见是计算了string的hashcode,然后把hashcode转换为了整型的switch,为了处理hash冲突,使用equals来做最后的确认,并不是在底层就支持String,所以具体在使用的时候,需要知道原理是什么,如果String串特别长,很明显性能就不太好,不适合使用。

结尾

语法糖是java中的一个小小的插曲,不会对你的编程生涯造成什么太大的影响,即使不专门学习,也能在项目中学习到,因为用到的实在太多了,学了一下,感觉也没有提升太多的知识,归根到底,还是因为这个东西太简单了,如果说非要有一点要注意的地方,就是整形的自动装箱和拆箱,会有一个小小的坑,那就是日常使用的时候,经常会写这样的代码:

Integer a = 10;
int b = 20;
if(a != null && a == b) { // a != null 如果省略了,可能会空指针
	do something
} else {
	do something
}

当我们在比较a和b的值的时候,需要心里清楚,这是Java的自动拆箱,有的时候也会有空指针异常,所以一定要保证a != null 说到包装类的比较,还有一个小知识点,需要注意的地方,就是包装类的缓存:

Integer a = 127;
Integer b = 127;
boolean b1 = a == b;

Integer a0 = 128;
Integer b0 = 128;
boolean b2 = a0 == b0;

System.out.println("b1 is :" + b1);
System.out.println("b2 is :" + b2)

执行上面的代码,b1和b2是true还是false呢? 实际的结果是b1 is : true b2 is : false,为什么呢? 因为Java对于包装类,有1个缓存,a和b指向的是同1个对象,这个缓存的范围是 -128 到 127 之间,在这个范围内,2个包装类都是相等的。感兴趣的话可以看Integer的源码,里面有1个IntegerCache的内部类。

文本完。

关注我的博客,获取更多Java编程知识: 双King的技术博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值