Java语法糖

Java语法糖

Syntactic Sugar


常见的Java语法糖:

·for-each
·enum
·不定项参数
·静态导入
·自动装箱与拆箱
·多异常并列
·数值赋值优化
·接口方法
·try-with-resource
·var类型
·switch


for-each

// this is an example for 'for-each'

enum

// this is an example for 'enum'

不定项参数(可变参数)

普通函数的参数要求是固定的个数、类型与顺序
不定项参数JDK5开始

使用

class SugarExample { 
    // String(type)... sth.
    public static void print(String... args) {
        for (String arg : args) 
            System.out.println(arg);
    }
    
    /* ↓ */
    public static void main(String[] args) {
    	print("one");
    	print("one", "two");
    	print("one", "two", "three");
    }
}

可变参数的本质就是数组

🚩注意

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

重载时的优先级规则也发生了变化

固定参数的方法比可变参数方法的优先级更高
如果调用时同时与两个带有可变参数的方法匹配,则报错

class Example { 
    public static void print(String... args) {
        for (String arg : args)
            System.out.println(arg + arg.length());
    }
    
    public static void print(String s) {
	    System.out.println(s);
    }
    
    // 错误示范
    /* public static void print(int... args_1, String... args_2) { } */
    /* public static void print(String s, String... args) {} -> 由于print的存在相矛盾 */
    
    public static void main(String[] args) {
        // 如果print调用时只有一个参数,则会优先选择固定参数方法
        print("one");       // one
        print("one", "two");// one3 two3
    }
}

静态导入

import static - 便于简写

import导入程序所需要的类
import static导入一个雷的静态方法or静态变量
// example
import static java.lang.Math.pow;
import static java.lang.System.out;

class Example {
    public static void main(String[] args) {
        int c = pow(1, 2);
        out.println(c);
    }
}

🚩注意

建议在import时少使用*通配符,最好具体到需要导入的某个类的方法或静态变量

如果重名时,需要加上类名

自动装箱与拆箱

auto boxing | unboxing

引入该功能的目的是为了简化基本类型与对象转换的写法

基本类型(内容)对象(指针、内存)
booleanBoolean
byteByte
charCharacter
intInteger
shortShort
longLong
floatFloat
doubleDouble

解释

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

示例

class Boxing {
    public static void main(String[] args) {
        // 不使用装箱
        Integer i1 = Integer.valueOf(5);
        // 自动装箱
        Integer i2 = 5;
        
        // 不使用拆箱
        int i3 = i1.intValue();
        // 自动拆箱
        int i4 = i1;
    }
}

🚩注意

装箱和拆箱发生在编译器中,在class中已经添加转化,虚拟机不含有自动装箱和拆箱的语句


基本类型没有null,但是对象有,需要注意转换时是否会产生NullpointerException

  • 基础数据类型和封装的对象进行运算时,会先自动拆箱,然后对基础数据类型进行运算
  • 一般来说,最好不要使用多个非同类的数值类对象进行运算

特别的是,进行==运算时,如果有至少一方是基本类型,则比较的是内容(数值),而非指针(内存),也即先进行拆箱

如果两方都是对象的话,比较的则是指针是否指向同一个位置

在使用equals方法是,由于要求参数为对象,且比较得到的结果是类同+内容同,因此会进行包装

多异常并列

JDK7

即:将多个异常并列在一个catch中

import java.io.IOException;
import java.sql.SQLException;

// 不使用多异常并列
class Example {
    public static void main(String[] args) {
        try {
            // pass
        } catch (IOException ex) {
            // do sth.
        } catch (SQLException ex) {
            // do the same thing.
        }
    }
}

// 使用多异常并列 -> 管道符号的使用
class Example {
    public static void main(String[] args) {
        try {
            // pass
        } catch (IOException | SQLException ex) {
            // do sth.
        }
    }
}

🚩注意

并列在一个catch中的多个异常之间不能有直接/间接的基础关系

// 错误展示
class ErrorExample {
  public static void main() {
      try {
          // pass
      } 
      catch (IOException | FileNotFoundException ex) {
       	/** @warning 该两种异常之间为继承的关系,不可以这样使用 **/
      }
  }
}

数值赋值优化

JDK7 - 整数类型使用二进制数赋值

目的:避免二进制计算,适用于byte、short、int、long

使用

class Example {
    public static void main(String[] args) {
        // 0010-0001
        byte a1 = (byte) 0b00100001;
        // 1010-0001-0100-0101
        short a2 = (short) 0b1010000101000101;
        // 1010-0001-0100-0101-1010-0001-0100-0101
        int a3 = (int) 0b10100001010001011010000101000101;
    }
}

下划线

这个真的超好用!

class Example {
    public static void main(String[] args) {
        int a1 = 0b0111_1011_0001;
        long a2 = 9_999_999_999L;
    }
}

这种方法来赋值,增加了数字的可读性与纠错功能

  • 使用范围:short、int、long、float、double
  • 下划线只能出现在数字中间,也即前后必须都有数字
  • 允许在2、8、10、16进制的数字中使用,下划线不可以分割开头
// 正确使用:
class RightExample {
    public static void main(String[] args) {
        int i1 = 0b0111_1011_0001;// 二进制,0b开头
        int i2 = 02_014;// 八进制,0开头
        int i3 = 0x7_B_1;// 十六进制,0x开头
        
        int i4 = 12345;
        int i5 = 123_45;
        int i6 = 123__45;
        
        float f1 = 3.14_159f;
        double d1 = 1.4_14159;
    }
}

接口方法

JDK8 plus lambda表达式

在最初的java接口中,所有的接口都是没有实现的、公开的


Java 8时,推出的接口的默认方法/静态方法都是可带有实现的,而且开始支持Lambda表达式


Java 9时,接口拥有了私有方法

// JDK1~7
public interface Animal {
    public void move();
}

// JDK8~?
public interface NewAnmail {
    public default void move() {
        System.out.println("I can move");
    }
}

🚩注意

对于接口的默认方法:

  1. 默认方法以default作为关键字的标注
  2. 默认方法不允许重写Object中的方法,如equals、toString等
  3. 具体实现类可以继承、重写父接口的默认方法
  4. 接口可以继承、重写父接口的默认方法
  5. 为了向前兼容,如果一个类的父类和父接口都有(同名同参数的)默认方法,子类会继承父类的默认方法
  6. 如果子类实现了两个接口(均含有同名同参数的默认方法),则会导致编译失败,需要在子类中重写这个default方法

默认方法-示例

public interface Animal {
    public void move();
}

public interface NewAnimal {
    public void move() {
        System.out.println("moving");
    }
}

public class Lion implements Animal, NewAnimal {
    public static void main(String[] args) {
        new Lion().move();
    }
    
    // 当实现的两个接口都含有同名方法,且至少有一个是默认方法时
    // 子类需要重写该方法,以免歧义
    public void move() {
        // 设置为默认方法的形式
        NewAnimal.super.move();
    }
}

静态方法-注意

静态方法属于接口,不属于子类或者子接口,也即子类/子接口没法继承该静态方法,只能通过所在的接口名称来调用

私有方法

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


私有方法只属于本接口,只在本接口内使用,不属于子类/子接口;子类/子接口没有继承该私有方法,也无法调用


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

try-with-resource

类似python的with语句

try-with-resource语句简化了try-catch-finally

使用try-with-resource,可以保证在使用完资源后,将资源自动关闭

import java.io.FileInputStream;

class Example {
    public static void main(String[] args) {
        // 使用try-catch-finally
        FileInputStream fis = "**/**.json";
        try { 
            /* pass */ 
        } catch (Exception e) {
            /* do sth. */
        } finally {
            // 关闭资源
            if (fis != null) fis.close();
        }
        
        // 使用try-with-resource
        try(FileInputStream newFis = "**/**/txt") {
            /* pass */
        } catch (Exception e) {
            /* do sth. */
        }
    }
}

🚩注意

JDK7 如果使用try-with-resource,则必须保证资源定义在try中。如果已经在外面定义,则需要一个本地变量。

class Example {
    public static void main(String[] args) {
        FileInputStream fis = "**/**.json";
        
        try(FileInputStream fis_2 = fis) {}
        catch (Exception e) {}
    }
}

JDK9 不再要求定义临时变量,可以直接使用外部资源变量。

class Example {
    public static void main(String[] args) {
        FileInputStream fis = "**/**.json";
        
        try(fis) {}
        catch (Exception e) {}
    }
}

🌊原理

如果需要使用try-with-resource语句,则需要保证资源对象实现类AutoCloseable接口,也即实现close方法


public class FileInputStream extends InputStream

public abstract class InputStream implements Closeable

使用

class Example {
    public static void main(String[] args) {
        try (MyResource m = new MyResource()) {
            // do sth.
        } catch (Exception e) {
            // then
        }
    }
}

class MyResource implements AutoCloseable {
     public void doSomething() throws Exception {
         System.out.println("do sth.");
     }
    
     public void close() throws Exception {
         System.out.println("Close...");
     }
}

var类型

Java 10

在java10之前,java都是一种强类型的程序语言

// 强类型指
class Example {
    int a = 1;
    double b = 1.0;
}

在java10之后,推出局部变量推断,在一定程度上可以避免信息冗余


不过本质上java还是强类型的语言,由编译器负责推断类型写入字节码文件,推断完成后不可更改

class Example {
    public static void main(String[] args) {
        var a = 5;
    }
}

🚩注意

  1. var可以用在局部变量上,而非类成员变量
  2. var可以用在for/for-each上
  3. var在声明时必须进行初始化
  4. var不可以使用在返回值以及方法的参数部分
  5. var不适合大面积的使用
  6. var的处理只在编译时进行,JVM不会对其进行单独的处理

switch

switch支持的类型

支持类型不支持类型
byte/Bytelong
short/Shortfloat
int/Integerdouble
char/Character
String
Enum

使用

class Example {
    public static void main(String[] args) {
        Integer s = 10;
        switch (s) {
            case 1:
                System.out.println("s = 1");
            case 10:
                System.out.println("s = 10");
            case 100:
                System.out.println("s = 100");
            default:
                System.out.println("s = 0");
        }
    }
}

switch的多分支合并 - JDK12

例子

class Example {
    public static void main(String[] args) {
        int month;
        int day;
        
        // 方式一
        switch (month) {
            case 1, 3, 5, 7, 8, 10, 12 -> day = 31;
            case 4, 6, 9, 11 -> day = 30;
            case 2 -> day = 28;
            default -> day = 0;
        }
        
        // 方式二
        int the_day = switch (month) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            default -> {
                // 此处特殊
                int Day = 28;
                break Day;
            }
        };
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值