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核心技术》高阶,这里只是做一个学习记录,非原创,感兴趣的读者还请自行观看陈良育老师的课程。
吾生也有涯,而知也无涯。