JAVA基础-语法糖

了解语法糖

语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin(彼得.兰丁)发明的一个术语,指在计算机语言中添加的某种语法。语法糖是对现有语法的一个封装。语法糖让程序更加简洁,有更高的可读性。
JAVA中语法糖只存在于编译期,在编译器将将.java源文件编译成class字节码时,如果你去看com.sun.tools.javac.main.JavaCompiler的源码,你会发现compile()中有一个步骤就是调用desuger(),这个方法负责解语法糖而实现的。
在编程领域,除了语法糖还有语法盐和语法糖精。

糖块一、switch支持String与枚举

从Java7开始,Java语言中的语法糖在逐渐丰富,Java7中switch语句开始支持String。
switch自身原本支持byte、short、int、char。对于char比较其ascii码。
字符串:switch括号里其实比较的实际是哈希值。通过比较switch的字符串调用hashCode()获得哈希值,再用equels()判断case值和switch值是否相等,判断是否break。


```java
public class SwitchStringDemo {

    public static void main(String[] args) {
        String str;
        String string = str = "world";
        int n = -1;
        switch (string.hashCode()) {
            case 99162322: {
                if (!string.equals("hello")) break;
                n = 0;
                break;
            }
            case 113318802: {
                if (!string.equals("world")) break;
                n = 1;
            }
        }
        switch (n) {
            case 0: {
                System.out.println("hello");
                break;
            }
            case 1: {
                System.out.println("world");
                break;
            }
        }
    }
}

糖块二、泛型和类型擦除

编译器处理泛型有两种方式:Code specialization和Code sharing。
C++哈C#是使用Code specializetion的处理机制,而Java使用的Code Sharing的机制。
Code Sharing方式为每个泛型类型创建唯一的字节码标识,并且将该泛型类型都映射到这个唯一的字节码表示上。将多种泛型类型实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

Map<String, String> map = new HashMap<String, String>();  
map.put("name", "JourWon");  
map.put("wechat", "JourWon");  
map.put("blog", "https://blog.csdn.net/ThinkWon");  
解语法糖之后会变成:

Map map = new HashMap();  
map.put("name", "JourWon");  
map.put("wechat", "JourWon");  
map.put("blog", "https://blog.csdn.net/ThinkWon");   

糖块三、自动装箱与拆箱

将基本类型自动转换成包装类叫做自动装箱。自动调用包装类的静态方法valueOf(基本类型)转换成包装类。
将包装类自动转换成基本类型叫做自动拆箱。自动调用包装类的.xxxValue()将包装类转变成基本类型。

糖块四、方法变长参数

可变参数是在java1.5中引入的一个特性。它允许一个方法把任意数量的值作为参数。
将可变长参数转换成数组。

public static void main(String[] args) {
    print("CSDN-ThinkWon", "简书-JourWon", "博客:https://blog.csdn.net/ThinkWon");
}

public static void print(String... strs) {
    for (int i = 0; i < strs.length; i++) {
        System.out.println(strs[i]);
    }
}

```java
public static void main(String[] args) {
    print(new String[]{"CSDN-ThinkWon", "\u7b80\u4e66-JourWon", "\u535a\u5ba2:https://blog.csdn.net/ThinkWon"});
}

public static void print(String[] strs) {
    for (int i = 0; i < strs.length; i++)
        System.out.println(strs[i]);
}

糖块五、枚举

public enum T {
    SPRING,SUMMER;
}
// 反编译
public final class T extends Enum<T> {
    public static final /* enum */ T SPRING = new T("SPRING", 0);
    public static final /* enum */ T SUMMER = new T("SUMMER", 1);
    private static final /* synthetic */ T[] $VALUES;

    public static T[] values() {
        return (T[])$VALUES.clone();
    }

    public static T valueOf(String name) {
        return Enum.valueOf(T.class, name);
    }

    private T(String string, int n) {
        super(string, n);
    }

    static {
        $VALUES = new T[]{SPRING, SUMMER};
    }
}

当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承。

糖块六、内部类

内部类又称嵌套类。内部类是编译时的概念,outer.java里面定义了内部类Inner,一旦编译成功,就会生成两个完全不同的.class文件。分别是outer.class和outer$inner.class。所以内部类的名字完全可以和外部类名字相同

糖块七、条件编译

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!");
        }
    }
}
//反编译后代码如下:
public class ConditionalCompilation {
    public static void main(String[] args) {
    boolean DEBUG = true;
    
    System.out.println("Hello, DEBUG!");
    
    boolean ONLINE = false;
}

糖块八、断言

public class AssertTest {
    public static void main(String args[]) {
        int a = 1;
        int b = 1;
        assert a == b;
        System.out.println("CSDN-ThinkWon");
        assert a != b : "ThinkWon";
        System.out.println("博客:https://blog.csdn.net/ThinkWon");
    }
}
public class AssertTest {
	static final /* synthetic */ boolean $assertionsDisabled;

    public static void main(String[] args) {
        boolean a = true;
        boolean b = true;
        if (!$assertionsDisabled && a != b) {
            throw new AssertionError();
        }
        System.out.println("CSDN-ThinkWon");
        if (!$assertionsDisabled && a == b) {
            throw new AssertionError((Object)"ThinkWon");
        }
        System.out.println("\u535a\u5ba2:https://blog.csdn.net/ThinkWon");
    }

    static {
        $assertionsDisabled = !AssertTest.class.desiredAssertionStatus();
    }

}

反编译之后的代码要比我们自己的代码复杂的多,使用assert这个语法糖我们节省很多代码。

糖块九、数字字面量

在java7中,字面量允许加下划线。

public class Test {
    public static void main(String[] args) {
        int i = 10_000;
        System.out.println(i);
    }
}
public class Test {
    public static void main(String[] args) {
        int i = 10000;
        System.out.println(i);
    }
}

糖块十、增强for循环

增强for循环(for-each)

public static void main(String args[]) {
    String[] strs = {"CSDN-ThinkWon", "简书-JourWon", "博客:https://blog.csdn.net/ThinkWon"};
    for (String s : strs) {
        System.out.println(s);
    }
    System.out.println();

    List<String> strList = Arrays.asList(strs);
    for (String s : strList) {
        System.out.println(s);
    }
}

反编译后代码如下:

public static void main(String args[]) {
    String[] strs;
    String[] arrstring = strs = new String[]{"CSDN-ThinkWon", "\u7b80\u4e66-JourWon", "\u535a\u5ba2:https://blog.csdn.net/ThinkWon"};
    int n = arrstring.length;
    for (int i = 0; i < n; ++i) {
        String s = arrstring[i];
        System.out.println(s);
    }
    System.out.println();

    List<String> strList = Arrays.asList(strs);
    Iterator<String> iterator = strList.iterator();
    while (iterator.hasNext()) {
        String s = iterator.next();
        System.out.println(s);
    }
}

糖块十一、try-with-resource语句

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

public static void main(String args[]) {
    BufferedReader br = null;
    try {
        String line;
        br = new BufferedReader(new FileReader("d:\\hello.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
        }
    }
}

java7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句

public static void main(String args[]) {
    try (BufferedReader br = new BufferedReader(new FileReader("d:\\ hello.xml"))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        // handle exception
    }
}

糖块十二、Lambda表达式
Lambda表达式不是匿名内部类的语法糖,他依赖了几个JVM底层提供的Lambda相关API
先来看一个简单的Lambda表达式,遍历一个list:

public static void main(String[] args) {
    List<String> strList = new ArrayList<>();
    strList.add("CSDN-ThinkWon");
    strList.add("简书-JourWon");
    strList.add("博客:https://blog.csdn.net/ThinkWon");

    strList.forEach(s -> System.out.println(s));
}
public static void main(String[] args) {
    ArrayList<String> strList = new ArrayList<String>();
    strList.add("CSDN-ThinkWon");
    strList.add("\u7b80\u4e66-JourWon");
    strList.add("\u535a\u5ba2:https://blog.csdn.net/ThinkWon");
    strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}

private static /* synthetic */ void lambda$main$0(String s) {
    System.out.println(s);
}

可能遇到的坑

当泛型遇到重载

public class GenericTypes {

    public static void method(List<String> list) {  
        System.out.println("invoke method(List<String> list)");  
    }  

    public static void method(List<Integer> list) {  
        System.out.println("invoke method(List<Integer> list)");  
    }  
}  

当泛型遇到catch

异常处理是由JVM在运行时来进行的。由于类型信息被擦除。泛型的类型参数不能用在java异常处理的catch语句。

当泛型内包含静态变量

所有泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。

自动装箱与拆箱

整数值区间-128~127。只适用于自动装箱。使用构造函数创建对象不使用

增强for循环

Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出java.util.ConcurrentModificationException异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法remove()来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

总结
前面介绍了12种Java中常用的语法糖。所谓语法糖就是提供给开发人员便于开发的一种语法而已。但是这种语法只有开发人员认识。要想被执行,需要进行解糖,即转成JVM认识的语法。

当我们把语法糖解糖之后,你就会发现其实我们日常使用的这些方便的语法,其实都是一些其他更简单的语法构成的。有了这些语法糖,我们在日常开发的时候可以大大提升效率,但是同时也要避免过渡使用。使用之前最好了解下原理,避免掉坑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值