Java中的语法糖

for/for-each

1、for-each从JDK5.0开始引入

2、for-earch语法更简洁

3、for-earch避免越界错误

4、for可以删除元素,for-each不可以删除/替换元素

5、for-each遍历的时候,是不知道当前元素的具体位置索引

6、for-each只能正向遍历,不能反向遍历

7、for-each不能同时遍历2个集合

枚举类型

  • 枚举类型:变量的取值只在一个有限集合内,如性别、星期几等
  • Java5推出enum类型
  • enum关键字声明枚举类型,且都是Enum的子类(但不需要写extends)
  • enum内部有多少个值,就有多少个实例对象
  • 不能new枚举类对象
/**
 * @ClassName: Main01
 * @Author: Tang
 * @Date: 2021/6/2 20:21
 */
public class Main01 {
    public static void main(String[] args) {
        SizeEnum size1 = SizeEnum.SMALL;
        SizeEnum size2 = SizeEnum.MEDIUM;
        SizeEnum size3 = SizeEnum.EXTRA_lARGE;
        SizeEnum size4 = SizeEnum.LARGE;
        System.out.println(size1 + " " + size2 + " " + size3 + " " + size4);
    }
}
enum SizeEnum {
    SMALL,MEDIUM,LARGE,EXTRA_lARGE;
}
输出结果为

SMALL MEDIUM EXTRA_lARGE LARGE

  • Java中的enum类型
  • 除了枚举的内容,还可以添加属性/构造函数/方法
  • 构造函数只能是package-private(default)或者private,内部调用
  • 所有的枚举类型都是Enum的子类,也继承了对应的方法
  • ordinal()返回枚举值所在的索引位置,从0开始
  • compareTo()比较两个枚举值的索引位置大小
  • toString()返回枚举值的字符串表示
  • valueOf()将字符串初始化为枚举对象
  • values()返回所有的枚举值
同一个类同一个包不同子包不同包的非子类
private
default
protected
public
public class Main01 {
    public static void main(String[] args) {
        SizeEnum size1 = SizeEnum.APPLE;
        SizeEnum size2 = SizeEnum.ORANGE;
        System.out.println(size1.getPrice() + " " + size2.getPrice() + " ");
    }
}
enum SizeEnum {
    APPLE(10),ORANGE(8);
    private int price;
    SizeEnum(int price) {
        this.price = price;
    }

    public int getPrice() {
        return this.price;
    }
}
运行结果:

10 8

不定项参数

  • 普通函数的形参列表是固定个数/类型/顺序
  • JDK5提供了不定项参数(可变参数)功能
  • 类型后加3个点,如int…/double…/String…/
  • 可变参数,本质是一个数组
  • 一个方法只能有一个不定项参数,且必须位于参数列表的最后
  • 重载的优先级规则1:固定参数的方法,比可变参数优先级更高
  • 重载的优先级规则2:调用语句,同时与两个带可变参数的方法匹配则会报错
public class Main02 {
    public static void main(String[] args) {
        print();
        print("aaa", "bbb");
        print("aaa", "bbb", "ccc");
    }

    public static void print(String... args) {
        System.out.println(args.length);
        for (String arg : args) {
            System.out.println(arg);
        }
    }
}
运行结果:

0
2
aaa
bbb
3
aaa
bbb
ccc

静态导入

  • import导入程序所需要的类
  • import static导入一个类的静态方法和静态变量(JDK5引入)
  • import static导入一个类的静态方法和静态变量
  • 少使用*通配符,不滥用,最好具体到静态遍历或方法
  • 静态方法名具有明确特征如parseDouble()和parseInt(),如有重名,需要补充类名
import static java.lang.Math.*;
import static java.lang.System.*;
import static java.lang.System.out;
/**
 * @ClassName: Main
 * @Author: Tang
 * @Date: 2021/6/2 21:29
 */
public class Main03 {
    public static void main(String[] args) {
        int a = 3,b = 4,c = 0;
        // 使用import static后可以省略Math
        c = (int) sqrt(pow(a, 2) + pow(b, 2));
        c = (int) Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2));
        out.println("c is " + c);
        System.out.println("c is " + c);
    }
}

自动装箱和自动拆箱

  • 从JDK5.0开始引入,简化基本数据类型和对象转换的写法
  • 基本数据类型:boolean / byte / char / int / short / long / float / double
  • 对象: Boolean / Byte / Character / Integer / Short / Long / Float / Double
public class Main04 {
    public static void main(String[] args) {
        // 自动装箱
        Integer obj1 = 5;
        // 没有自动装箱
        Integer obj2 = Integer.valueOf(5);

        // 自动拆箱
        int a = obj1;
        // 没有自动拆箱
        int a2 = obj1.intValue();
    }
}

装箱可以理解为将基本的数据类型包装成对象,拆箱则反之

自动装箱和拆箱注意事项

  • 装箱和拆箱是编译器的工作,在class中已经添加转化。虚拟机没有自动装箱和拆箱的语句。
  • ==:基本数据是否内容相同,对象是指针是否相同(内存同一个区域)
  • 基本数据类型没有空值,对象有null
  • 当一个基础数据类型与封装类进行==、+、-、*、/运算时,会将封装类进行拆箱,对基础数据类型进行运算。
  • 谨慎使用多个非同类的数值类对象进行运算。
 public static void main(String[] args) {
        Integer a1 = 1000;
        int a2 = 1000;
        Integer a3 = 2000;
        Long a4 = 2000L;
        long a5 = 2000L;

        // 拆箱再进行数值比较
        System.out.println(a1 == a2); // true
        System.out.println(a3 == (a1 + a2)); // true
        System.out.println(a5 == (a1 + a2)); // true

        // equals()要求同类,且内容相同
        System.out.println(a3.equals(a1 + a2)); // true
        System.out.println(a4.equals(a1 + a2)); // false
        // a4的Long类型和(a1 + a2)的long类型等价,所以为true
        System.out.println(a4.equals((long) (a1 + a2))); // true

        // 不同类型不能比较
        // System.out.println(a3 == a4);
    }
Long.equals()源码:
public boolean equals(Object obj) {
        if (obj instanceof Long) {
            return value == ((Long)obj).longValue();
        }
        return false;
    }
运行结果:

true
true
true
true
false
true

多异常并列

  • 多个异常并列在以catch中
  • 从JDK7.0开始引入,简化写法
try {
    test();
} catch (IOException ex) {
    // 异常处理
} catch (SQLException ex) {
    // 异常处理
}
简化后
try {
    test();
} catch (IOException | SQLException ex) {
    // 异常处理
}
  • 多个异常并列在一个catch中
  • 多个异常之间不能有(直接 / 间接)继承关系,如果有,则报错
如下
try {
    test();
} catch (IOException | FileNoFoundException ex) {
    // IOException和FileNoFoundException之间有继承关系
    // 所以报错了
}

数字中的下划线

  • Java7的新语法:在数值字面量(literal)中使用下划线
  • 增加数字的可读性和纠错性功能
  • short / int / long / float / double均可用
  • 下划线只能出现在数字之间,前后必须有数字
  • 允许二 / 八 / 十 / 十六进制的数字中使用
public static void main(String[] args) {
        // 二进制以0b开头
        int a3 = 0b0111_1011_0001;
        // 八进制以0开头
        int a4 = 02_014;
        // 可以多个下划线
        int a5 = 1234__45;
        // 十六进制
        int a6 = 0x7_B_1;
        float a7 = 3.56_78f;
        double a8 = 1.3_45_67;
}

接口方法

  • Java最初设计中,接口的方法都是没有实现的,公开的
  • Java8推出接口的默认方法 / 静态方法(都带实现的),为了Lambda表达式提供支持
interface NewAnimal {
    // 必须有default关键字来表示接口默认方法
    public default void move() {
        System.out.println("move ....");
    }
}
  • 规则1:默认方法不能重写Object中的方法
  • 规则2:实现类可以继承 / 重写父接口的默认方法
  • 规则3:接口可以继承 / 重写父接口的默认方法
  • 规则4:当父类和父接口都有(同名同参数)默认方法,子类继承父类的默认方法,这样可以兼容JDK7及以前的代码
  • 规则5:子类实现了2个接口(均有同名同参数的默认方法),那么编译失败在子类中重写这个default方法
基础接口
public interface Animal {
    public void move();
}
带默认方法的接口
public interface NewAnimal {
    public default void move() {
        System.out.println("NewAnimal move....");
    }

    // 不能重写Object中的方法
    /*public default String toString() {
        return "NewAnimal";
    }*/
}
抽象类实现基础接口
public abstract class NewFlyAnimal implements NewAnimal {
    public void move() {
        System.out.println("abstract class NewFlyAnimal move ...");
    }
}
接口继承基础接口
public interface NewLandAnimal extends NewAnimal{
    public default void move() {
        System.out.println("NewLandAnimal move ...");
    }
}
NewLandAnimal接口实现
public class NewLion implements NewLandAnimal {
    public static void main(String[] args) {
        new NewLion().move();
    }
}
结果:

NewLandAnimal move …

继承NewFlyAnimal并同时实现NewLandAnimal
public class NewLion extends NewFlyAnimal implements NewLandAnimal {
    public static void main(String[] args) {
        // 当父类和父接口都有同名的方法,以父类的为主
        // 这样就可以兼容JDK7以前的代码
        new NewLion().move();
    }
}
结果:

abstract class NewFlyAnimal move …

实现两个接口都有同名同参数的方法
public class NewLion implements NewLandAnimal, Animal{
    public static void main(String[] args) {
        new NewLion().move();
    }
    // 当实现的两个接口都含有同名方法,且至少有一个默认方法
    // 则子类需要重写改方法避免歧义
    public void move() {
        // 必须指明指定接口的move方法
        NewLandAnimal.super.move();
    }
}
结果:

NewLandAnimal move …

接口的静态方法

  • Java8接口的静态方法(带实现)
  • 该静态方法属于本接口,不属于子类 / 子接口
  • 子类(子接口)没有继承该静态方法,只能通过所在的接口名来调用
public class StaticSwan implements StaticAnimal {
    public static void main(String[] args) {
        StaticAnimal.move();
        // 报错
        // StaticSwan.move();
        //报错
        // new StaticSwan().move();
    }
}

interface StaticAnimal {
    public static void move() {
        System.out.println("StaticAnimal move ...");
    }
}

接口的私有方法

  • 解决多个默认方法 / 静态方法的内容重复问题
  • 私有方法属于本接口的,只能在本接口内使用,不属于子类 / 子接口
  • 子类(子接口)没有继承该私有方法,也无法调用
  • 静态私有方法可以被静态 / 默认方法调用,非静态私有方法被默认方法调用

接口与抽象类

相同点(截止至java12以前,接口和抽象类对比)
  • 都是抽象的,都不能被实例化,即不能被new
  • 都可以由实现方法
  • 都可以不需要继承者实现所有的方法
不同点(截止至java12以前,接口和抽象类对比)
  • 抽象类最多只能继承一个,接口可以实现多个
  • 接口的变量默认是public static final,且必须有初值,子类不能修改;而抽象类的变量默认是default,子类可以继承修改
  • 接口没有构造函数,抽象类有构造函数
  • 接口没有main函数,抽象类可以有main函数
  • 接口有public / default / private的方法,抽象类有public / private / protected不能写关键字的(default)的方法

try-with-resource语句

  • 程序如果打开外部资源,那么在使用后需要正确关闭资源
  • 考虑异常因素,Java提供try-catch-finally进行保证资源关闭
  • JDK7提供try-with-resource,比try-catch-finally更简便
FileInputStream fis = ......;
try {
    .......
} catch (Exception ex) {
    .......
} finally {
    if (null != fis) {
        fis.close();
    }
}
简化后:
// 使用完后会自动关闭小括号中的资源
try (FileInputStream fis = .......) {
    ......
} catch (Exception ex) {
    .......
}
  • JDK7提供的try-with-resource
  • 资源要求定义在try中.若已经在外面定义,则需要一个本地变量
  • JDK9不再要求定义临时变量,可以直接使用外部资源变量
// JDK7
FileInputStream fis = ....;
try (FileInputStream fis2 = fis) {
    ......
} catch (Exception ex) {
    .......
}
// JDK9
FileInputStream fis = ....;
try (fis) {
    .....
} catch (Exception ex) {
    ......
}
多个资源
try (FileInputStream fis = new FileInputStream(); FileOutputStream fos = new FileOutputStream) {
    ......
} catch (Exception ex) {
    .......
}

ResourceBundle文件加载

  • Java8及以前,ResourceBundle默认以ISO-8895-1方式加载Properties文件
  • 需要利用native2ascii工具(JDK自带)对文件进行转义
  • JDK9及以后,ResourceBundle默认以UTF-8方式加载Properties文件
  • JDK9及以后,已经删除native2ascii工具
  • 新的Properties文件可以直接以UTF-8保存
  • 已利用native2ascii工具转化后的文件,不受影响.即ResourceBundle若解析文件不是有效的UTF-8,则以ISO-8895-1方式加载

var类型

  • Java以前一直是一种强类型的编程语言
  • 每个变量在定义是就确定了类型
  • 类型固定了,就不能更改
  • Java10推出了var: 局部变量推断
  • 避免信息冗余
  • 对齐了变量名
  • 更容易阅读
  • 本质上还是强类型语言,编译器负责推断类型,并写入字节码文件.因此推断后不能更改!!!
public static void main(String[] args) {
    	// current jdk13
        int a1 = 5;
        float a2 = 0.25f;
        String a3 = "abc";
        System.out.println(a1 + " " + a2 + " " + a3);

        // 使用var类型
        var b1 = 5;
        var b2 = 0.25;
        var b3 = "abc";
        System.out.println(b1 + " " + b2 + " " + b3);
    }
结果:

5 0.25 abc
5 0.25 abc

var的限制
  • 可以使用在局部变量上,非类成员变量
  • 可以用在for / for-each 循环中
  • 声明时必须初始化
  • 不能用在方式(形式)参数和返回类型
  • 大面积滥用会使代码阅读性差
  • var只在编译时起作用,没有在字节码中引入新的内容,也没有专门的JVM指令处理var

泛型

  • 泛型:编写的代码可以被很多不同类型的对象所重用
  • 泛型类: ArrayList , HashSet , HashMap等
  • 泛型方法: Conllections.binarySearch , Arrays.sort等
  • 泛型接口: List , Iterator等
泛型类
  • 具有泛型变量的类
  • 在类名后用<T>代表引入类型
    • 多个字母表示多个引入类型,如<T,U>等
    • 引入类型可以修饰成员变量 / 局部变量 / 参数 / 返回值
    • ArrayList<E> E表示元素Element;
    • HashMap<K,V> K表示Key, V表示Value
    • 自定义泛型变量常用T, T表示Template
class Interval<T> {
    private T lower;
    private T upper;
    public Interval(T lower, T upper) {
        this.lower = lower;
        this.upper = upper;
    }

    public T getLower() {
        return lower;
    }
}
泛型方法
  • 具有泛型参数的方法
  • 该方法可在普通类 / 泛型类中
  • <T>在修饰符后,返回类型前
public class ArrayUtil {
	/**
     * <T>表示泛型方法的引入类型
     * T... 本质就是一个数组
     *
     * @param a 
     * @return T
     * */
    public static <T> T getMinddle(T... a) {
        return a[a.length/2];
    }
}
public class Demo5 {
    public static void main(String[] args) {
        // 调用泛型方法时可以不指定类型
        String s1 = ArrayUtil.getMinddle("abc", "def", "ghi");
        // 调用泛型方法时指定对应的类型
        Integer i1 = ArrayUtil.<Integer>getMinddle(1, 2, 3);
        // 支持null
        String s2 = ArrayUtil.<String>getMinddle("abc", "def", null);
        // 传入的类型必须统一
        Integer i2 = ArrayUtil.getMinddle(1, 2.5f, 3L); // error
    }
}
泛型接口
  • 和泛型类相似, 在类名后加<T>
  • T用来指定方法返回值和参数
  • 实现接口时, 指定类型
interface Calculator<T> {
    public T add(T operand1, T operand2);
}
class IntegerCalculator implements Calculator<Integer> {

    @Override
    public Integer add(Integer operand1, Integer operand2) {
        return operand1 + operand2;
    }
}
  • 当接口中的<T>本身也是一个泛型类时
class Interval<T> {
    private T lower;
    private T upper;
    public Interval(T lower, T upper) {
        this.lower = lower;
        this.upper = upper;
    }

    public T getLower() {
        return lower;
    }

    public T getUpper() {
        return upper;
    }
}

interface Calculator<T> {
    public T add(T operand1, T operand2);
}

class IntegerCalculator implements Calculator<Interval<Integer>> {

    @Override
    public Interval<Integer> add(Interval<Integer> operand1, Interval<Integer> operand2) {
        int lower = operand1.getLower() + operand2.getLower();
        int upper = operand1.getUpper() + operand2.getUpper();
        return new Interval<Integer>(lower, upper);
    }
}
泛型的限定
  • <T extends Comparable> 约定T必须是Comparable的子类
  • extends固定, 后面可以多个, 以&拼接, 如<T extends Comparable & Serializable>
  • extends限定可以有多个接口, 但只能一个类, 但类必须排第一位
  • 逗号隔参, <T extends File & Cloneable, U extends Serializable>, 其中Cloneable和Serializable是接口
泛型之间的继承
  • Pair<S>和Pair<T>没有任何关系, 无论S和T之间是什么关系
  • 泛型类可以扩展或实现其他的类, 如ArrayList<T>实现List<T>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值