Java语法糖

Java语法糖

    语法糖:Syntactic sugar / Syntax sugar。

维基定义:语法糖是编程语言中的一种语法吗,用来使得程序语言更容易阅读或者更容易表达事务的处理过程。

        -语法糖使得程序员更容易编写程序,更清晰准确表达业务逻辑。
	-语法糖不仅仅是语法的转换,也包括一些小的改进。
	-以下主要包括JDK5~12版本之间的重要的语法糖。
		·JDK2(1.1~1.4)是普及版
		·JDK5/6/7/8/11是长期稳定版
		·JDK9/10/12是短期版本    

  

 

 

 下面列举一些语法糖:

 

 

一、for-each 和 枚举

(1)、for/for-each

        int[] n=new int[5];
	for(int i=0;i<n.length;i++){
		n[i]=i;
	}
	for(int a:n){
		System.out.println(a);
	}    

 

for VS for-each


 

  •  for-each从JDK5.0开始引入
  •  for-each语法更简洁
  •  for-each避免越界错误
  •  for 可以删除元素,for-each不可以删除/替换元素
  •  for-each遍历时,不知道当前元素的具体位置索引
  •  for-each只能正向遍历,不能反向遍历
  •  for-each不能同时遍历2个集合
  •  for和for-each性能接近

 

 注:这里 for 和 for-each 性能接近并不是绝对的,遍历不同的数据结构会有不同的表现,总的来说,遍历数组结构两者性能相近,遍历链表结构时for-each性能会比for好很多。

 有兴趣的可以自行了解:https://www.cnblogs.com/java-spring/p/9264406.html

 

 

 

(2)枚举类型

  · 枚举变量:变量的取值只在一个有限的集合内(如性别、星期几、颜色等)。

      · Java5推出enum类型

    -enum关键字声明枚举类,且都是Enum的子类(但不需也不许写 extends

      -enum内部有多少个值,就有多少个实例对象,有且仅有一个。

    -不能直接 new 枚举类对象。

    · Java的enum类型

    -除了枚举的内容,还可以添加属性/构造函数/方法

    -构造函数只能是package-private(default)或者private,内部调用。

    -所有的enum类型都是Enum的子类,也继承了相应的方法。

      -ordinal()返回枚举值所在的索引位置,从0开始。

      -compareTo()比较两个枚举值的索引位置大小

      -toString()返回枚举值的字符串表示

      -valueOf()将字符串初始化为枚举对象

      -values()返回所有的枚举值

 

enum Day{
    Monday,Tuesday,Wednesday,Thursday,FridaySaturday,Sunday;
}

  

 

总结: 

  • for-each循环(5):避免数组越界,提倡使用。
  • 枚举(5):限定取值集合,减少实例对象,提倡使用。

 

 

 

 

 

二、不定项参数和静态导入

(1)不定项参数(可变参数)

  · 普通函数的形参列表是固定个数/类型/顺修

  · JDK 5 提供了不定项参数(可变参数)功能

    -类型后面加三个点,如 int.../double.../String...

    -可变参数,本质上就是一个数组。

  · 一个方法只能有一个一个不定项参数,且必须位于参数列表的最后。

       · 重载的优先级规则1:固定参数的方法,比可变参数优先级更高。

    · 重载的优先级规则2:调用语句,同时与两个带可变参数的方法匹配,则报错。

 

 

示例代码:

public class Main {
    public static void main(String[] args){
       print();
       print("a");
       print("a","a");
       print("a","a","a");
    }
    public static void print(String... args){
        System.out.println(args.length);
        for(String arg:args){
            System.out.print(arg+" ");
        }
        System.out.println("");
    }
}

运行结果:

 

 

 

 

(2)静态导入

  ·  import 导入程序所需要的类

  ·  import static 导入一个类的静态方法和静态变量(JDK5引入)

    -少使用 * 通配符,不滥用,最好具体到静态变量或方法。

    -静态方法名具有明确特征,如有重名,需要补充类名。

 

示例:

 

import static java.lang.System.out;

out.println("Hello World");

这个还比较常用,能让代码更加简洁。

 

 

 

 

总结:

  • 不定项参数(5):注意方法重载的优先级,建议少用。
  • 静态导入(5):有大量静态方法调用时使用,注意 “ * ” 不滥用。

 

 

 

 

 

 

三、自动拆箱和装箱、多异常并列、数值类型赋值优化

(1)、自动装箱和拆箱

  •  自动装箱和拆箱(auto-boxing/auto-unboxing)

    -从JDK5.0开始引入,简化基本类型和对象转换的写法

     -基本类型:boolean、byte、char、int、short、long、float、double

     -对象:Boolean、Byte、Character、Integer、Short、Long、Float、Double

  • 装箱:基本类型的值被封装为一个包装类对象。
  • 拆箱:一个包装类对象被拆开并获取对应的值。

 

示例:

Integer obj1=5;  //自动装箱
Integer obj2=Integer.valueOf(5);

int a1=obj1;       //自动拆箱
int a2=obj1.intValue();

 

  • 自动装箱和拆箱的注意事项:

    -装箱和拆箱时编译器的工作,在 class 中已经添加转化。虚拟机没有自动装箱和拆箱的语句。

    -==:基本类型时内容相等,对象时指针是否相同(内存同一个区域)

    -基本类型没有空值,对象有null,可能触发NullPointerException。

    -当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基本数据类型进行运算。

    -谨慎使用多个非同类的数值类对象进行运算。

 

 

 

(2)、多异常并列

  • 多个异常并列在一个catch中

    -从JDK7.0开始引入,简化写法

 

示例:

try{
  
}catch(IOException | SQLException ex){
    //一个catch写多个异常,异常之间用管道符连接
    //异常处理
}

 

    -多个异常之间不能有(直接/间接)继承关系,如果有,则报错。

    -且不同异常的异常处理方式统一才可以使用多异常并列。

 

 

 

 

(3)、数值类型赋值优化

  • Java 7 的新语法:整数类型用二进制数赋值

    -避免二进制计算

    -byte、short、int、long

 

  • Java 7 的新语法:在数值字面量(literal)中使用下划线。

    -增加数字的可读性和纠错功能

    -short、int、long、float、double

    -下划线只能出现数字中间,前后必须有数字

    -允许在二/八/十/十六进制的数字中使用。

 

 

整数类型可以用二进制数直接赋值。

示例:

byte a1=(byte) 0b00100001;
short a2=(short) ob1010000101000101;
int a3=0b101;
int a4=0B101;    //B可以大小写

  

 

 

 

总结:

  • 自动拆箱和装箱(5):避免多个不同类型对象共同运算。
  • 多异常并列(7):不建议使用,针对每一异常单独处理。
  • 数值类型赋值优化(7):优化显示,避免错误,建议使用。

 

 

 

 

 

 

 

四、接口方法

接口的默认方法:

  • Java最初的设计中,接口的方法都是没有实现的,公开的。
  • Java 8 推出接口的默认方法/静态方法(都带实现的),为Lambda表达式提供支持。

    -以 default 关键字标注,其他的定义和普通函数一样。

    -规则1:默认方法不能重写Object中的方法。

    -规则2:实现类可以继承/重写父接口的默认方法

    -规则3:接口可以继承/重写父接口的默认方法

    -规则4:当父类和父接口都有(同名同参数)的默认方法,子类继承父类的默认方法,这样就可以兼容JDK7及以前的代码。

    -规则5:子类实现了2个接口(均有同名同参数的默认方法),那么编译失败,必须在子类中重写这个default方法。

 

简单示例:

public interface Animal {
    public default void move(){
        System.out.println("I can move.");
    }
}

  

 

 

 

接口的静态方法:

  • Java 8 接口的静态方法(带实现的)

    -该静态方法属于本接口,不属于子类/子接口。

    -子类(子接口)没有继承该静态方法,只能通过所在接口名来调用。

 

简单示例:

public interface Animal {
    public static void move(){
        System.out.println("I can move.");
    }
}

  

 

 

 

接口的私有方法:

  • Java 9 接口的私有方法(带实现的)

    -解决多个默认方法/静态方法的内容重复问题。

    -私有方法属于本接口,只能本接口内使用,不属于子类/子接口

    -子类(子接口)没有继承该私有方法,也无法调用。

    -静态私有方法可以被静态/默认方法调用,非静态私有方法被默认方法调用。

 

 

 

 

接口 VS 抽象类


  • 相同点(截止到 Java 12以前,接口和抽象类对比)

    -都是抽象的,都不能被实例化,即不能被new。

    -都可以有实现方法

    -都可以不需要继承者实现所有的方法。

 

  • 不同点(截至到 Java 12以前,接口和抽象类对比)

    -抽象类最多继承一个,接口可以实现多个

    -接口的变量默认时 public static final ,且必须有初值,子类不能修改,而抽象类的变量默认时 default,子类可以继承修改。

    -接口没有构造函数,抽象类有构造函数。

    -接口没有main函数,抽象类可以有main函数.

    -接口有 public / default / private 的方法,抽象类有 public / private / protected / 不写关键字的(defaule) 的方法。


 

 

 

 

 

 

总结:

  • 接口的方法(8/9):建议少用,如有 Lambda 表达式需求,可使用

    -默认方法:default标注,可以为子类/子接口所继承/改写。

    -静态方法:static 标注,只属于本接口,不属于子类/子接口,子类/子接口可访问。

    -私有方法:private标注,只属于本接口,不属于子类/子接口。

 

 

 

 

 

 

 

 

 

五、try-with-resource和Resource Bundle文件加载优化

(1)、try-with-resource

  • 程序如果打开外部资源,那么在使用后需要保证正确关闭。
  • 考虑异常因素,Java提供 try-catch-finally进行保证。
  • JDK 7 提供 try-with-resource ,比 try-catch-finally更简便。
  • JDK 7 提供 try-with-resource

    -资源要求定义在 try 中,若已经在外部定义,则需要一个本地变量。

  • JDK 9 不再要求定义临时变量,可以直接使用外部资源变量。
  • try-with-resource原理

    -资源对象必须实现 AutoCloseable接口,即实现 close 方法

 

简单示例:

    try(FileInputStream f=...){

      }catch (Exception e){
            
      }

  

 

 

 

 

(2)、ResourceBundle文件加载

  •  Java 8 及以前,ResourceBundle默认以 ISO-8859-1方式加载 Properties文件。

    -需要利用 native2ascii工具(JDK自带)对文件进行转义。

  • JDK 9 及以后,ResourceBundle默认以UTF-8方式加载 Properties文件。

    -JDK9及以后已经删除natice2ascii工具。

    -新的 Properties文件可以直接以UTF-8保存。

    -已利用native2ascii工具转化后的文件,不受影响。即ResourceBundle若解析文件不是有效的UTF-8,则以ISO-8859-1方式加载。

 

 

 

 

 

 

 

总结:

  • try-with-resource(7/9):资源自动关闭,避免错误,建议使用。
  • ResourceBundle文件加载优化(9):默认以UTF-8加载。

 

 

 

 

 

 

 

 

 

六、var类型和 switch

(1)var类型

  • Java 以前一直时一种强类型的程序语言

    -每个变量在定义时就确定了类型

    -类型固定了,就不能更改

 

  • Java 10 推出var:局部变量推断。

    -避免了信息冗余

    -对齐了变量名

    -更容易阅读

    -本质上还是强类型语言,编译器负责推断类型,并写入字节码文件。因此推断后不能更改。

 

  • var的限制

    -可以用在局部变量上,非类成员变量。

    -可以用在for/for-each循环中

    -声明时必须初始化

    -不能用在方法(形式)参数和返回类型

    -大面积滥用会使代码整体阅读性变差。

    -var只在编译时起作用,没有在字节码中引入新的内容也没有专门的JVM指令处理 var。

 

 

 

 

(2)、switch

  • switch,多分支选择语句

    -支持的类型:byte/Byte,short/Short,int/Integer,char/Character,String(7.0),Enm枚举(5.0),不支持 long/float/double。

    -多分支合并,采用 -> 直接连接到判定条件和动作(JDK12支持)

    -switch直接在表达式赋值(JDK12支持)

 

简单示例:

switch(month){
    case "Jan","Mar","May","July","Aug","Oct","Dec" -> result=31;
    case "Apr","June","Sep","Nov" -> result=30;
    default -> result=-1;
}

  

 

 

 

 

总结:

  • var类型(10):类型自动推断,不滥用,建议在局部情况下使用,如for循环。
  • switch(5/7/12):多分支判断,注意在不同版本下支持的语法不同。

 

 

 

 

 

 

 

 

 


 

综述:

  • 语法糖:了解新特性,知晓其优缺点,特别是适用的版本,谨慎使用。

 

 

 


 

 

 

 

 

 

本篇博客主要内容皆为中国大学mooc上,陈良育老师的《Java核心技术》高阶,这里只是做一个学习记录,非原创,感兴趣的读者还请自行观看陈良育老师的课程。

 

 

 

 

 

 

吾生也有涯,而知也无涯。

 

posted @ 2019-10-22 14:39  V少年如他  阅读(405)  评论(0编辑  收藏  举报