Java中的语法糖

前言

语法糖

语法糖是一种用来方便程序员代码开发的手段,简化程序开发,但是不会提供实质性的功能改造,但可以提高开发效率或者语法的严谨性或者减少编码出错的机会。
总而言之,语法糖可以看作是编译器实现的一种小把戏。

解语法糖

语法糖的存在主要是方便开发人员使用。但其实,Java虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。

编译

说到编译,大家肯定都知道,Java语言中,javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。

反编译

如果你去看com.sun.tools.javac.main.JavaCompiler的源码,你会发现在compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的实现的。

Java 中最常用的语法糖主要有泛型、变长参数、条件编译、自动拆装箱、foreach ,枚举,内部类,switch,枚举

语法糖1–泛型

如:ArrayList、Map<K,V>、public void methodName(T t)。
Java 语言中的泛型,它只在程序的源码中存在,在编译后的字节码文件中,就是已经替换为原来的原生类型(Raw Type,也称为裸类型)了,并且在相应的地方插入了强制转换代码,因此,对于运行期的java语言来说,ArrayList与ArrayList就是同一个类,所以泛型技术实际上是java语言的一颗语法糖,java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。

 private static void demo1() {
        Map<String, String> map = new HashMap<>();
        map.put("你好","恩");
        map.put("在吗", "吃了没?");
        System.out.println(map.get("你好"));
        System.out.println(map.get("在吗"));
    }

反编译结果

private static void demo1() {
    HashMap map = new HashMap();
    map.put("你好", "恩!");
    map.put("在吗", "吃了没?");
    System.out.println((String)map.get("你好"));
    System.out.println((String)map.get("在吗"));
}

语法糖2–变长参数

private static void demo1() {
    method1("123", "456","789");
}
private static void method1(String... str) {
    System.out.println(Arrays.toString(str));
}

反编译结果

private static void demo() {
   method(new String[]{"123", "456"."789"});
}
private static void method(String[] str) {
    System.out.println(Arrays.toString(str));
}

语法糖3–条件编译

    public static void main(String[] args) {
     private static final int NUMBER_INT = 1;
        if (true) {
            System.out.println("JAVA");
        }
        if (false) {
            System.out.println("JAVAEE");
        } 
        if (NUMBER_INT == 1){
            System.out.println("NUMBER_INT == 1");
         }
    }

反编译结果

      System.out.println("JAVA");
      System.out.println("NUMBER_INT == 1");

语法糖–4自动拆装箱

   Integer a = 0;
   int b = a;

反编译结果

Integer a = Integer.valueof(0);
int b = a.intValue();

语法糖–5 ForEach循环

集合

private static void demo() {
    List<String> list = new LinkedList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    for (String str : list) {
        System.out.println(str);
    }
}

反编译结果

private static void demo() {
    List list = new LinkedList();
    list.add("a");
    list.add("b");
    list.add("c");
    for (Iterator iterator =list.iterator();iterator.hasNext()) {
    System.out.println((String)iterator.next());
           }
 }

数组

  private static void demo() {
    int[] array = {1,2,3,4};
    for (int integer : array) {
        System.out.println(integer);
   }
}

反编译结果

private static void demo() {
    int[] array;
    int[] arrinteger = array = new int[]{1, 2, 3, 4};
    int n = arrinteger.length;
    for (int i = 0; i < n; ++i) {
        int integer = arrinteger[i];
        System.out.println(integer);
    }
}

因为只是遍历所以重新定义了一个新数组,也是为什么在for each遍历的时候赋新值也不会改变的原因。

语法糖6–枚举

public enum  EnumDemo {
    SPRING,
    SUMMER,
}

反编译结果

public final class EnumDemo extends Enum
{
    private EnumDemo(String s, int i)
    {
        super(s, i);
    }
    public static EnumDemo[] values()
    {
        EnumDemo enum2[];
        int i;
        EnumDemo enum1[];
        System.arraycopy(enum2 = ENUM_VALUES, 0, enum1 = new EnumDemo[i = enum2.length], 0, i);
        return enum1;
    }

    public static EnumDemo valueOf(String s)
    {
        return (EnumDemo)Enum.valueOf(s);
    }

    public static final EnumDemo SPRING;
    public static final EnumDemo SUMMER;
    private static final EnumDemo ENUM_VALUES[];
    static
    {
        SPRING = new T("SPRING", 0);
        SUMMER = new T("SUMMER", 1);
        ENUM_VALUES = (new EnumDemo[] {
            SPRING, SUMMER
        });
    }
}

语法糖7–内部类

public class OutterClass {
    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文件:OutterClass$InnerClass.class 、OutterClass.class 。
当我们尝试使用jad对OutterClass.class文件进行反编译的时候,他会把两个文件全部进行反编译,然后一起生成一个OutterClass.jad文件。内容如下:
反编译结果


public class OutterClass
{
    class InnerClass
    {
        public String getName()
        {
            return name;
        }
        public void setName(String name)
        {
            this.name = name;
        }
        private String name;
        final OutterClass this$0;

        InnerClass()
        {
            this.this$0 = OutterClass.this;
            super();
        }
    }

    public OutterClass()
    {
    }
    public String getUserName()
    {
        return userName;
    }
    public void setUserName(String userName){
        this.userName = userName;
    }
    public static void main(String args1[])
    {
    }
    private String userName;
}

语法糖8-- switch 支持 String 与枚举

字符串

public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
        case "hello":
            System.out.println("hello");
            break;
        case "world":
            System.out.println("world");
            break;
        default:
            break;
        }
    }
}

反编译结果

public class switchDemoString
{
    public switchDemoString()
    {
    }
    public static void main(String args[])
    {
        String str = "world";
        String s;
        switch((s = str).hashCode())
        {
        default:
            break;
        case 99162322:
            if(s.equals("hello"))
                System.out.println("hello");
            break;
        case 113318802:
            if(s.equals("world"))
                System.out.println("world");
            break;
        }
    }
}

Java中的swith自身原本就支持基本类型。比如int、char等。
对于int类型,直接进行数值的比较。对于char类型则是比较其ascii码。所以,对于编译器来说,switch中其实只能使用整型,任何类型的比较都要转换成整型。比如byte。short,char(ascii码是整型)以及int。switch是通过equals()和hashCode()方法来实现的.
仔细看下可以发现,进行switch的实际是哈希值,然后通过使用equals方法比较进行安全检查,这个检查是必要的,因为哈希可能会发生碰撞。

枚举

class enum Enum{
      A,B,C,D;
}
public class Demo{
     Enum grade = Enum.A;
        switch (grade) {
            case A:
                System.out.println("Excellent");
                break;
            case B:
                System.out.println("Good");
                break;
            case C:
                System.out.println("Not bad");
                break;
            case D:
                System.out.println("Not good");
                break;
            default:
                System.out.println("unknown");
        }
 }

反编译结果

class enum Enum{
      A,B,C,D;
}
public class Demo{
     Enum grade = Enum.A;
        switch (grad.ordinal()) {
            case 0:
                System.out.println("Excellent");
                break;
            case 1:
                System.out.println("Good");
                break;
            case 2:
                System.out.println("Not bad");
                break;
            case 3:
                System.out.println("Not good");
                break;
            default:
                System.out.println("unknown");
        }
 }

可见枚举的反编译匹配是根据父类里面的 public final int ordinal()方法获取的值

总结网上的,加了一点自己的理解

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值