【JVM类加载及字节码技术】编译期处理-语法糖-可变参、foreach、switch(一)

文章目录


四、可变参数

1.案例代码

代码如下:

public class Demo4 {
   public static void foo(String... args) {
      //将args赋值给arr,可以看出String...实际就是String[] 
      String[] arr = args;
      System.out.println(arr.length);
   }

   public static void main(String[] args) {
      foo("hello", "world");
   }
}

可变参数 String… args 其实是一个 String[] args , java 编译器会在编译期间将上述代码转换。

2.编译优化

代码如下:

public class Demo4 {
   public Demo4 {}

    
   public static void foo(String[] args) {
      String[] arr = args;
      System.out.println(arr.length);
   }

   public static void main(String[] args) {
   	  //这里其实就相当于一个字符串数组,可以将参数都囊括进去。
      foo(new String[]{"hello", "world"});
   }
}

注意:如果调用的是foo(),即未传递参数时,等价代码为foo(new String[]{}),创建了一个空数组,而不是直接传递的null


五、foreach

1.案例代码

代码如下:

public class Demo5 {
	public static void main(String[] args) {
        //数组赋初值的简化写法也是一种语法糖。
		int[] arr = {1, 2, 3, 4, 5};
		for(int x : arr) {
			System.out.println(x);
		}
	}
}

数组赋初值的简化写法也是一种语法糖。

2.编译优化

代码如下:

public class Demo5 {
    public Demo5 {}

	public static void main(String[] args) {
		//优化后
		int[] arr = new int[]{1, 2, 3, 4, 5};
		
		//优化后,底层是for循环
		for(int i=0; i<arr.length; ++i) {
			int x = arr[i];
			System.out.println(x);
		}
	}
}

foreach底层就是for循环

六、集合使用foreach

1.案例代码

代码如下:

public class Demo5 {
   public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
      for (Integer x : list) {
         System.out.println(x);
      }
   }
}

集合要使用foreach,需要该集合类实现Iterable接口,因为集合的遍历需要用到迭代器Iterator

2.编译优化

代码如下:

public class Demo5 {
   public Demo5 {}
    
   public static void main(String[] args) {
      List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
      
      //获得该集合的迭代器
      Iterator<Integer> iterator = list.iterator();
      while(iterator.hasNext()) {
         Integer x = iterator.next();
         System.out.println(x);
      }
   }
}

七、switch字符串

1.案例代码

代码如下:

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

2.编译优化

代码如下:

public class Demo6 {
   public Demo6() {
      
   }
   public static void main(String[] args) {
      String str = "hello";
      int x = -1;
      
      //通过字符串的hashCode+value来判断是否匹配
      switch (str.hashCode()) {
      
         //hello的hashCode
         case 99162322 :
            //再次比较,因为字符串的hashCode有可能相等
            if(str.equals("hello")) {
               x = 0;
            }
            break;
            
         //world的hashCode
         case 11331880 :
            if(str.equals("world")) {
               x = 1;
            }
            break;
         default:
            break;
      }

      //用第二个switch在进行输出判断
      switch (x) {
         case 0:
            System.out.println("h");
            break;
         case 1:
            System.out.println("w");
            break;
         default:
            break;
      }
   }
}

过程分析

  1. 在编译期间,单个的switch被分为2个switch。
第一个: switch (str.hashCode()) {...}
第二个: switch (x) {...}
  1. 第一个switch使用hashCode获取第二个switch的入口值。
    这里使用的是hashCode+equals判断字符串是否相等。
    hashCode提高效率equals防止hashCode冲突
//通过字符串的hashCode+value来判断是否匹配
switch (str.hashCode()) {
      
         //hello的hashCode
         case 99162322 :
            //再次比较,因为字符串的hashCode有可能相等
            if(str.equals("hello")) {
               x = 0;
            }
            ....
}
  1. 第二个switch获取到第一个switch获取到的值,获取最终用户想要获取的值。
//用第二个switch在进行输出判断
      switch (x) {
         case 0:
            System.out.println("h");
            break;
         	....
      }

七、switch枚举

1.案例代码

代码如下:

public class Demo7 {
   public static void main(String[] args) {
      SEX sex = SEX.MALE;
      
      switch (sex) {
         case MALE:
            System.out.println("man");
            break;
         case FEMALE:
            System.out.println("woman");
            break;
         default:
            break;
      }
   }
}

//枚举类
enum SEX {
   MALE, FEMALE;
}

2.编译优化

代码如下:

public class Demo7 {
   /**     
    * 定义一个合成类(仅 jvm 使用,对我们不可见)     
    * 用来映射枚举的 ordinal 与数组元素的关系     
    * 枚举的 ordinal 表示枚举对象的序号,从 0 开始     
    * 即 MALE 的 ordinal()=0,FEMALE 的 ordinal()=1     
    */ 
   static class $MAP {
      //数组大小即为枚举元素个数,里面存放了case用于比较的数字
      static int[] map = new int[2];
      static {
         //ordinal即枚举元素对应所在的位置,MALE为0,FEMALE为1
         map[SEX.MALE.ordinal()] = 1;
         map[SEX.FEMALE.ordinal()] = 2;
      }
   }

   public static void main(String[] args) {
      SEX sex = SEX.MALE;
      //将对应位置枚举元素的值赋给x,用于case操作
      int x = $MAP.map[sex.ordinal()];
      switch (x) {
         case 1:
            System.out.println("man");
            break;
         case 2:
            System.out.println("woman");
            break;
         default:
            break;
      }
   }
}

enum SEX {
   MALE, FEMALE;
}

过程分析

  1. 在底层会生成一个合成类,仅JVM可见。
  2. 映射枚举的 ordinal 与数组元素有对应关系,
  3. ordinal枚举元素对应所在的位置,MALE为0,FEMALE为1
  4. 将对应位置枚举元素的值赋予x,进行case操作。

本博客根据黑马JVM课程所总结。

上一篇:阿里二面:main 方法可以继承吗?


下一篇:简单的while循环