Java语法糖

Java语法糖

指的是:在计算机 语言中添加的某种语法,这种语法对语言功能没有影响,只是方便使用,看起来更简洁,可读性更好

Java常见的语法糖

注意:Java虚拟机不支持语法糖,这些语法糖在编译阶段会被还原成简单的基础语法结构,这个过程叫解语法糖

javac命令将.java文件编译成.class文件,然后.class文件可以运行在Java虚拟机中,它的底层源码除了compile()外还有一个步骤就是调用desugar(),这个方法就是解语法糖的实现

Java比较常用的语法糖有:泛型、变长参数、条件编译、自动拆装箱、内部类等


switch支持String和枚举

Javaswitch开始支持String

通过反编译可知,字符串的switch是通过equals()hashCode()实现的,而且还需要注意,避免哈希碰撞带来的错误


泛型

一个编译器处理泛型有两种方式:

  • Code speccializationC++/C#使用这个机制
  • Code sharingJava使用这个机制

对于Java虚拟机他看不懂Map<String, String> map这样的语法,需要在编译阶段通过类型擦除的方式进行解语法糖

类型擦除的过程:

  • 将所有泛型参数用其最左边界(最顶级父类型)类型替换
  • 移除所有的类型参数

虚拟机中没有泛型,只有普通类和普通方法,所有泛型类的类型参数在编译时都会被擦除,泛型并没有自己独立的Class类对象,比如并不存在List<String>.classList<Integer>.class,而只有List.class


自动装箱和自动拆箱

自动装箱: Java将原始类型值转换成对应的对象,也可以认为是基本数据类型转换成引用数据类型,例如int类型的变量转换成Integer对象

**自动拆箱:**将Integer对象转换成int类型值

原始类型:byte、short、char、int、long、float、double、boolean

对应的封装类:Byte、Short、Character、Integer、Long、Folat、Double、Boolean

代码示例

java 复制代码
public static void main(String[] agrs){
  int i = 10;//基本类型/原始类型
  Integer n = i;//自动装箱
}

自动装箱底层调用的是:Integer n = Integer.valueOf(i);

java 复制代码
public static void main(String[] args){
  Integer i = 10;//先定义一个原始类型对应的对象
  int n = i;//自动拆箱
}

自动拆箱底层调用的是:int n = i.intValue();


可变长参数

variable arguments是在Java1.5中引入的一个特性,他允许一个方法把任何数量的值作为参数

代码实现

java 复制代码
public static void main(String[] args) {
  print("JunLa", "CSDN:JunLa", "博客:https.github.JunLa.io");
}

//print接收可变长参数
public static transient print(String... strs) {
  for(int i = 0; i < strs.length; i++){
    System.out.println(strs[i]);
  }
}

可变长参数底层本质上就是:首先创建一个数组,然后数组的长度就是调用该方法时传递的实际参数个数,然后把参数值全部放入这个数组中,然后再将这个数组作为实际参数传递给要调用的这个方法,然后执行方法即可


枚举

关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件来使用,这是一种非常有用的功能

注意:enum是一个关键字

java 复制代码
public enum t {
  SPRING,SUMMER;
}

注意:Java编译器会自动将枚举名处理为合法类名,即首字母要大写,t -> T

反编译看底层:实际上是:public final class T extends EnumT类基础了enum类,同时final关键字告诉我们,这个类不能被继承

所以当我们使用enum定义一个枚举类的时候,编译器会自动给我们创建一个final关键字修饰的、继承了enum类的类,所以枚举类型不能被继承


内部类

内部类也叫嵌套类,可以把内部类理解成是外部类的一个普通成员

内部类之所以也是语法糖,是因为他仅仅是一个编译时的概念,outer.java里定义了一个内部类inner,一旦编译成功后,就会生成两个完全不同的.class文件,分别是outer.classouter$inner.class。所以内部类的名字完全可以和外部类名字相同

java 复制代码
public class OuterClass {
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public static void main(String[] args) {

    }

    class InnerClass{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

代码编译后会生成两个class文件,分别是OuterClass$InnerClass.class、OuterClass.class,当我们尝试对OuterClass.class文件进行反编译的时候,命令行会打印以下内容:Parsing OuterClass.class...Parsing inner class OuterClass$InnerClass.class... Generating OuterClass.jad,他会把两个文件都进行反编译操作,然后一起生成一个OuterClass.jad文件


  • 匿名内部类、局部内部类、静态内部类也是通过桥方法来获取private属性
  • 静态内部类没有this$0的引用
  • 匿名内部类、局部内部类通过赋值使用局部变量,该变量初始化之后就不能别修改

条件编译

在一般情况下,程序中的每一行代码都要参加编译,但有时候出于对程序代码优化的考虑,希望只对其中的一部份代码进行编译,此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是条件编译

C/CPP中可以通过预编译处理语句来实现条件编译,在Java中也可以实现条件编译:

java 复制代码
public class ConditionalCompilation {
    public static void main(String[] args) {
        final boolean DEBUG = true;
        if(DEBUG) {
            System.out.println("Hello, DEBUG!");
        }

        final boolean ONLINE = false;

        if(ONLINE){
            System.out.println("Hello, ONLINE!");
        }
    }
}

通过反编译可知:代码中没有了System.out.println("Hello, ONLINE");,这就是条件编译,因为在源代码中if(ONLINE)false,那么编译器就对其代码不进行编译

所以Java语言实现条件编译就是通过if判断来实现的,根据条件判断的真假来决定是否进行编译


断言

assert关键字就是断言,在Java程序执行的时候默认不开启断言检测,这个时候所有的断言语句都会被忽略,如果开启断言间车,则需要用开关-enableassertions-ea来开启

java 复制代码
public class AssertTest {
    public static void main(String args[]) {
        int a = 1;
        int b = 1;
        assert a == b;
        System.out.println("公众号:Hollis");
        assert a != b : "Hollis";
        System.out.println("博客:www.hollischuang.com");
    }
}

反编译之后会发现底层代码比原代码要复杂很多,所以使用assert这个语法糖之后可以节省很多带阿们,本质上断言就是使用if来实现的,如果断言结果是true什么都不做,程序继续执行,如果是false则抛出AssertError来打断程序的执行


数值字面量

不管是浮点数还是整数都允许在数字之间插入任意多个下划线,这些下划线不会对字面量的数值产生影响,就是为了方便阅读

java 复制代码
public class Test {
    public static void main(String... args) {
        int i = 10_000;
        System.out.println(i);
    }
}

反编译后,就是把下划线给删除了,即编译器是不认识数字字面量的下划线的,,所以在编译阶段会把它去掉


for-each

增强for循环可以比for循环少写很多代码

java 复制代码
public static void main(String... args) {
    String[] strs = {"Hollis", "公众号:Hollis", "博客:www.hollischuang.com"};
    for (String s : strs) {
        System.out.println(s);
    }
    List<String> strList = ImmutableList.of("Hollis", "公众号:Hollis", "博客:www.hollischuang.com");
    for (String s : strList) {
        System.out.println(s);
    }
}

反编译后,增强for循环的底层其实就是普通for循环实现的

java 复制代码
public static transient void main(String args[])
{
    String strs[] = {
        "Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u535A\u5BA2\uFF1Awww.hollischuang.com"
    };
    String args1[] = strs;
    int i = args1.length;
    for(int j = 0; j < i; j++)
    {
        String s = args1[j];
        System.out.println(s);
    }

    List strList = ImmutableList.of("Hollis", "\u516C\u4F17\u53F7\uFF1AHollis", "\u535A\u5BA2\uFF1Awww.hollischuang.com");
    String s;
    for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s))
        s = (String)iterator.next();

}

try-with-resource

Java里,对于文件操作IO流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close方法将其关闭释放资源,否则资源会一直处于打开状态,可能会导致内存泄漏等问题

关闭资源的常用方式就是在finally块里释放,即调用close方法,比如:

java 复制代码
public static void main(String[] args) {
    BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\hollischuang.xml"));
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    } finally {
        try {
            if (br != null) {
                br.close();
            }
        } catch (IOException ex) {
            // handle exception
        }
    }
}
java 复制代码
public static void main(String... args) {
    try (BufferedReader br = new BufferedReader(new FileReader("d:\\ hollischuang.xml"))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    }
}

反编译后的底层也很简单,就是没有做关闭资源的操作,编译器都帮我们做了


相关推荐
JAVA社区4 小时前
Java进阶全套教程(八)—— Docker超详细实战详解
java·运维·开发语言·docker·容器·面试·职场和发展
财经资讯数据_灵砚智能4 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(夜间-次晨)2026年5月21日
大数据·人工智能·python·信息可视化·自然语言处理
Mr.Java.4 小时前
Spring AI MCP Server分布式翻车现场:Streamable协议的甜蜜与危险,以及无状态救赎
java·后端·spring·ai·负载均衡
夕除4 小时前
spring boot 11
java·spring boot·后端
水木流年追梦4 小时前
大模型入门-RL基础
开发语言·python·算法·leetcode·正则表达式
TechPioneer_lp4 小时前
就业指导|中九非科班毕业,华为 OD 做 Java 后端想转 C++,能找到深度学习挂钩的岗工作吗?
java·c++·华为od·华为·就业指导·校招指导
Cthy_hy4 小时前
基于首届中国互联网数据挖掘竞赛数据集的行为相似网络分析
python·信息可视化·数据挖掘
AI玫瑰助手4 小时前
Python运算符:逻辑运算符(and/or/not)的短路特性
开发语言·python·信息可视化
Dicky-_-zhang4 小时前
分布式ID生成方案详解与实战
java·jvm