语法糖
编译期处理(javac)
定义
- 编译器在编译期javac会进行一些优化处理(语法糖)
- 是指在java编译器把*.java源码编译成*.class字节码大的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是java编译器给的一些福利
默认构造器
- 不写构造方法时 ,编译器会默认生成一个无参的构造方法
自动拆装箱
-
JDK5之后加入
-
Integer x = 1 ; int y = x;
-
Integer x = Integer.valueOf(1); // -128 到 127 重用对象 int y = x.intValue();
-
D:\ideaworkspace\untitled\out\production\untitled>javap -v Test.class Classfile /D:/ideaworkspace/untitled/out/production/untitled/Test.class Last modified 2021-7-26; size 522 bytes MD5 checksum 35e7d1e3c1ce663bac233354ea39616f Compiled from "Test.java" public class Test minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #5.#23 // java/lang/Object."<init>":()V #2 = Methodref #24.#25 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #3 = Methodref #24.#26 // java/lang/Integer.intValue:()I #4 = Class #27 // Test #5 = Class #28 // java/lang/Object #6 = Utf8 <init> #7 = Utf8 ()V #8 = Utf8 Code #9 = Utf8 LineNumberTable #10 = Utf8 LocalVariableTable #11 = Utf8 this #12 = Utf8 LTest; #13 = Utf8 main #14 = Utf8 ([Ljava/lang/String;)V #15 = Utf8 args #16 = Utf8 [Ljava/lang/String; #17 = Utf8 x #18 = Utf8 Ljava/lang/Integer; #19 = Utf8 y #20 = Utf8 I #21 = Utf8 SourceFile #22 = Utf8 Test.java #23 = NameAndType #6:#7 // "<init>":()V #24 = Class #29 // java/lang/Integer #25 = NameAndType #30:#31 // valueOf:(I)Ljava/lang/Integer; #26 = NameAndType #32:#33 // intValue:()I #27 = Utf8 Test #28 = Utf8 java/lang/Object #29 = Utf8 java/lang/Integer #30 = Utf8 valueOf #31 = Utf8 (I)Ljava/lang/Integer; #32 = Utf8 intValue #33 = Utf8 ()I { public Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=3, args_size=1 0: iconst_1 1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: astore_1 5: aload_1 6: invokevirtual #3 // Method java/lang/Integer.intValue:()I 9: istore_2 10: return LineNumberTable: line 8: 0 line 9: 5 line 10: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; 5 6 1 x Ljava/lang/Integer; 10 1 2 y I } SourceFile: "Test.java"
泛型擦除
-
JDK 5之后加入的特性,在java编译泛型代码后会执行泛型擦除的动作,即泛型信息在编译为字节码之后就消失了,实际类型都当作了Object类型来处理
-
import java.util.ArrayList; import java.util.List; /** * @Author: sunyang * @Date: 2021/7/26 * @Description: */ public class Test { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(10); // 实际调用的是List.add(Object e) 先把10转成Integer对象类型 再转成Object类型 list.add(Object(.valueOf(10)) Integer x = list.get (0); // 实际调用的是Object ojb = List.get(int index); } }
-
Classfile /D:/ideaworkspace/untitled/out/production/untitled/Test.class Last modified 2021-7-26; size 751 bytes MD5 checksum 553b43afaaf7fb2325a1cbe57547837c Compiled from "Test.java" public class Test minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #9.#29 // java/lang/Object."<init>":()V #2 = Class #30 // java/util/ArrayList #3 = Methodref #2.#29 // java/util/ArrayList."<init>":()V #4 = Methodref #7.#31 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #5 = InterfaceMethodref #32.#33 // java/util/List.add:(Ljava/lang/Object;)Z #6 = InterfaceMethodref #32.#34 // java/util/List.get:(I)Ljava/lang/Object; #7 = Class #35 // java/lang/Integer #8 = Class #36 // Test #9 = Class #37 // java/lang/Object #10 = Utf8 <init> #11 = Utf8 ()V #12 = Utf8 Code #13 = Utf8 LineNumberTable #14 = Utf8 LocalVariableTable #15 = Utf8 this #16 = Utf8 LTest; #17 = Utf8 main #18 = Utf8 ([Ljava/lang/String;)V #19 = Utf8 args #20 = Utf8 [Ljava/lang/String; #21 = Utf8 list #22 = Utf8 Ljava/util/List; #23 = Utf8 x #24 = Utf8 Ljava/lang/Integer; #25 = Utf8 LocalVariableTypeTable #26 = Utf8 Ljava/util/List<Ljava/lang/Integer;>; #27 = Utf8 SourceFile #28 = Utf8 Test.java #29 = NameAndType #10:#11 // "<init>":()V #30 = Utf8 java/util/ArrayList #31 = NameAndType #38:#39 // valueOf:(I)Ljava/lang/Integer; #32 = Class #40 // java/util/List #33 = NameAndType #41:#42 // add:(Ljava/lang/Object;)Z #34 = NameAndType #43:#44 // get:(I)Ljava/lang/Object; #35 = Utf8 java/lang/Integer #36 = Utf8 Test #37 = Utf8 java/lang/Object #38 = Utf8 valueOf #39 = Utf8 (I)Ljava/lang/Integer; #40 = Utf8 java/util/List #41 = Utf8 add #42 = Utf8 (Ljava/lang/Object;)Z #43 = Utf8 get #44 = Utf8 (I)Ljava/lang/Object; { public Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 9: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: bipush 10 11: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 14: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 19: pop 20: aload_1 21: iconst_0 22: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 27: checkcast #7 // class java/lang/Integer 30: astore_2 31: return LineNumberTable: line 11: 0 line 12: 8 line 13: 20 line 14: 31 LocalVariableTable: Start Length Slot Name Signature 0 32 0 args [Ljava/lang/String; 8 24 1 list Ljava/util/List; 31 1 2 x Ljava/lang/Integer; LocalVariableTypeTable: // 局部变量类型表 Start Length Slot Name Signature 8 24 1 list Ljava/util/List<Ljava/lang/Integer;>; // 泛型 }
-
所以在取值时,编译器真正生成的字节码中,还要额外做一个强制类型转换的操作
-
// 需要将Object 转化为Integer Integer x = (Integer)list.get(0);
-
-
如果前面的x变量类型修改为int基本类型,那么最终生成的字节码是
-
// 伪代码 需要将Object转为Integer int x = ((Integer)list.get(0)).intValue();
-
-
局部变量中的泛型不会被擦除,但不能通过反射获得,仍然能通过反射获得的这些泛型信息:
-
返回值 及参数列表
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { public static void main(String[] args) throws NoSuchMethodException { Method test = SynchronizedDemo.class.getMethod("test", List.class, Map.class); Type[] genericParameterTypes = test.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { if (genericParameterType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericParameterType; System.out.println("原始类型 - " + parameterizedType.getRawType()); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (int i = 0 ; i < actualTypeArguments.length; i ++ ){ System.out.printf("泛型参数[%d] - %s\n", i, actualTypeArguments[i]); } } } } public Set<Integer> test(List<String> list, Map<Integer, Object> map) { return null; } }
-
原始类型 - interface java.util.List 泛型参数[0] - class java.lang.String 原始类型 - interface java.util.Map 泛型参数[0] - class java.lang.Integer 泛型参数[1] - class java.lang.Object
-
可变参数
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { public static void main(String[] args){ foo("hello", "word"); } public static void foo (String... args) { String[] array = args; // 直接赋值 System.out.println(array); } /** * public static void foo (String[] args) { * String[] array = args; // 直接赋值 * System.out.println(array); * } * public static void main(String[] args){ * * foo(new String[] {"hello", "word"}); * * } * * **/ }
-
可变参数String… args其实是一个String[] args, 编译器会在编译期间 将上面代码转化成下面注释掉的代码,就是动态生成了一个长度为二的数组,
-
如果传的参数为空,则传过去的不是null 而是new String[]{}
foreach 循环
-
JDK5以后
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { public static void main(String[] args){ int[] array = {1, 2, 3, 4, 5}; // 数组赋初值也是语法糖 for (int i : array) { System.out.println(i); } } // 编译器编译后的伪代码 // public static void main(String[] args){ // // int[] array = new int[]{1, 2, 3, 4, 5}; // 数组赋初值也是语法糖 // for (int i = 0 ; i < array.length; ++i) { // int i = array[i]; // System.out.println(i); // } // } }
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 数组赋初值也是语法糖 for (Integer i : list) { System.out.println(i); } } // 编译器编译后的伪代码 // public static void main(String[] args){ // // List<Integer> list = Arrays.asList(1,2,3,4,5); // Iterator iter = list.iterator(); // while (iter.hasNext()) { // Iteger i = (Integer)iter.next(); // System.out.println(i); // } // } }
-
foreach循环写法 ,能够配合数组,以及实现了Iterable 接口的集合类一起使用,其中Iterable用来获取集合的迭代器(Iterator)
Switch字符串
-
JDK 7开始,switch可以使用字符串和枚举类,语法糖
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { public static void choose (String str) { switch (str) { case "hello": { System.out.println("h"); break; } case "world": { System.out.println("w"); break; } } } // public static void choose (String str) { // byte x = -1; // switch (str.hashCode()) { // case 99162322: // hello 的 hashCode // if (str.equals("hello")) { // 避免发生hash冲突 // x = 0; // } // break; // case 113318802: // world的hashCode // if (str.equals("world")) { // x = 1; // } // } // // switch (x) { // case 0: // System.out.println("h"); // break; // case 1: // System.out.println("w"); // } // } }
switch 枚举
-
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-22 22:40 **/ public class SynchronizedDemo { enum Sex { MALE, FEMALE } public static void main(String[] args) { choose(Sex.MALE); } public static void choose(Sex sex) { switch (sex) { case MALE: { System.out.println("男"); break; } case FEMALE: { System.out.println("女"); break; } } } /** * 定义一个合成类(仅jvm使用,对我们不可见) * 用来映射枚举的ordinal与数组元素的关系(每个枚举都有自己的ordinal枚举编号 正好和数组下标一样) * 枚举的ordinal 表示枚举对象的序号,从 0开始 * 即MALE 的ordinal() = 0, FEMALE 的ordinal() = 1 * $MAP 字节码中的名字不叫这个 * **/ // static class $MAP { // // 数组大小即为枚举元素的个数,里面存储case用来对比的数字 // static int [] map = new int[2]; // 定义一个和枚举个数相同的数组 // static { // map[Sex.MALE.ordinal()] = 1; // map[Sex.FEMALE.ordinal()] = 2; // } // } // // public static void choose(Sex sex) { // int x = $MAP.map[sex.ordinal()]; // switch (x) { // case 1: // System.out.println("男"); // break; // case 2: // System.out.println("女"); // break; // } // } }
枚举类
-
/** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-26 21:20 **/ public enum Sex { MALE, FEMALE } /** * 本质上也是一个类, * 两个枚举值实际上就是类两个实例对象 * 枚举类的实例个数是有限的,而正常类是无限的 * **/ //public final class Sex extends Enum<Sex> { // public static final Sex MALE; // public static final Sex FEMALE; // private static final Sex[] $VALUES; // // static { // MALE = new Sex("MALE", 0); // FEMALE = new Sex("FEMALE", 1); // $VALUES = new Sex[]{MALE, FEMALE}; // } // // // 防止被使用者 创建实力对象,仅本类使用 // private Sex(String name, int ordinal) { // super(name, ordinal); // } // public static Sex[] values(){ // return $VALUES.clone(); // } // public static Sex valueOf(String name) { // return Enum.valueOf(Sex.class, name); // } //}
try-with-resources
-
JDK 7以后新增,其中资源对象需要实现AutoCloseable接口, 例如 InputStream.,OutputStream,Connection.Statement,ResultSet等接口都实现了AutoCloseable。使用try-with-resources可以不用写finally语句 编译器会自动帮助生成关闭资源代码
-
import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-26 21:37 **/ //public class TryWithResourcesDemo { // public static void main(String[] args) { // try(InputStream is = new FileInputStream("C:\\备份1.txt")) { // System.out.println(is); // } catch (IOException e) { // e.printStackTrace(); // } // } //} public class TryWithResourcesDemo { public TryWithResourcesDemo(){} public static void main(String[] args) { try { InputStream is = new FileInputStream("C:\\备份1.txt"); Throwable t = null; try { System.out.println(is); } catch (Throwable e1) { // t 是我们代码中出现的异常(try块可能出现的异常赋值给临时的Throwable对象) t = e1; throw e1; } finally { // 判断了资源是否为空 if (is != null) { // 如果我们的代码有异常 if (t != null) { try { is.close(); }catch (Throwable e2) { // 如果close 出现异常,作为被压制异常添加 为了把我们try块中的异常和关闭资源时的异常都保留下来 为了防止异常丢失 t.addSuppressed(e2); } } else { // 如果我们代码没有异常, close出现的异常就是最后catch块中的e is.close(); } } } } catch (IOException e) { e.printStackTrace(); } } }
-
压制异常添加
-
/** * @program: jvmstudy * @description: Demo * @author: SunYang * @create: 2021-07-26 21:53 **/ public class TWR2 { public static void main(String[] args) { try(MyResource resource = new MyResource()) { int i = 1/0; } catch (Exception e) { e.printStackTrace(); } } } class MyResource implements AutoCloseable { public void close() throws Exception { throw new Exception("close 异常"); } }
-
java.lang.ArithmeticException: / by zero at TWR2.main(TWR2.java:10) Suppressed: java.lang.Exception: close 异常 at MyResource.close(TWR2.java:19) at TWR2.main(TWR2.java:11) Process finished with exit code 0
-
两个异常都不会丢失
-
方法重写时的桥接方法
-
我们都知道方法重写时对返回值分两种情况
- 父子类的返回值完全一致
- 子类返回值可以是父类返回值的子类
-
/** * @program: jvmstudy * @description: * @author: SunYang * @create: 2021-07-26 21:59 **/ public class FuZiDemo { public Number m() { return 1; } } class Zi extends FuZiDemo { @Override // 子类m方法的返回值是Integer是父类m 方法返回值Number 的子类 public Integer m() { return 2; } } //class Zi extends FuZiDemo { // // 自己写的原方法会被保留 // public Integer m() { // return 2; // } // // // 此方法才是真正的重写了父类public Number m() 方法 // // 此方法是一个合成方法,仅供JVM使用,我们不可见 // 允许方法同名,参数一致 // public synthetic bridge Number m() { // // 调用 public Integer m() 向上转型 Integer -> Number // return m(); // } //}
-
用反射代码验证(会有两个m()方法)
匿名内部类
-
/** * @program: jvmstudy * @description: * @author: SunYang * @create: 2021-07-26 22:11 **/ public class NiMingDemo { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { System.out.println("ok"); } }; } } // 额外生成了一个类 实现了Runnable接口 //final class NiMingDemo$1 implements Runnable { // // 一个无参构造 // NiMingDemo$1(){} // // // 并重写了run方法 // @Override // public void run() { // System.out.println("ok"); // } //} // //public class NiMingDemo { // public static void main(String[] args) { // Runnable runnable = new NiMingDemo$1(); // // } //}
-
匿名内部类将这个局部变量的值传入到内部类中,以成员变量的形式存在,值传递由构造函数完成,保证线程安全
-
为了避免方法内的变量脱离方法而存在的现象发生,于是1.8之前规定内部类不能访问一般的局部变量,但能访问被final修饰的局部变量
-
1.8之后jvm会自动添加final修饰符 称为effectively final
-
/** * @program: jvmstudy * @description: * @author: SunYang * @create: 2021-07-26 22:11 **/ public class NiMingDemo { public static void test(final int x) { Runnable runnable = new Runnable() { @Override public void run() { System.out.println("ok" + x); } }; } } // 额外生成了一个类 实现了Runnable接口 //final class NiMingDemo$1 implements Runnable { // // int val$x; // NiMingDemo$1(int x){ // this.val$x = x; // } // // // 并重写了run方法 // @Override // public void run() { // System.out.println("ok" + this.val$x); // } //} // //public class NiMingDemo { // public static void main(String[] args) { // Runnable runnable = new NiMingDemo$1(x); // // } //}
-
将x作为参数传过来,然后赋值给匿名内部类自己的局部变量,将来用的就是自己内部类的局部变量,
-
为什么匿名内部类引用外部局部变量时,局部变量为什么必须是final的
- 因为在创建匿名内部类对象时,将x的值赋值给了NiMingDemo$1 对象的valx属性,所以x不应该在发生变化了,如果发生了变化,那么valx属性也没办法再跟着变化,就会造成数据不一致。
) {
@Override
public void run() {
System.out.println(“ok” + x);
}
};
}
}
// 额外生成了一个类 实现了Runnable接口
//final class NiMingDemoKaTeX parse error: Expected '}', got 'EOF' at end of input: … // int valx;
// NiMingDemoKaTeX parse error: Expected '}', got 'EOF' at end of input: … // this.valx = x;
// }
//
// // 并重写了run方法
// @Override
// public void run() {
// System.out.println(“ok” + this.val$x);
// }
//}
//
//public class NiMingDemo {
// public static void main(String[] args) {
// Runnable runnable = new NiMingDemo$1(x);
//
// }
//}
- 将x作为参数传过来,然后赋值给匿名内部类自己的局部变量,将来用的就是自己内部类的局部变量,
- 为什么匿名内部类引用外部局部变量时,局部变量为什么必须是final的
- 因为在创建匿名内部类对象时,将x的值赋值给了NiMingDemo$1 对象的valx属性,所以x不应该在发生变化了,如果发生了变化,那么valx属性也没办法再跟着变化,就会造成数据不一致。