01-019 【jvm】语法糖概览

1.默认构造器

public  class  Candy1{
}

编译成class的代码:

public  class  Candy1{
		//这个无参构造器是编译器帮我们加上的
		public  Candy1(){
				super();//调用父类Object无参构造方法
		}
}

2.自动拆装箱

public  class  Candy2{
		public static void main(String[] args) {//现在编译时会自动转换成下面的
        	Integer   x=  1;
        	int  y= x;
    	}
}

在jdk5前必须要手动转换

public  class  Candy2{
		public static void main(String[] args) {
        	Integer   x=  Integer.valueOf(1);
        	int  y= x.intValue;
    	}
}

3.泛型集合取值

jdk5开始加入的特性,编译泛型代码后会执行泛型擦除的动作,即泛型信息在编译为字节码之后就丢失了,实际的类型都当做Object类型来处理

public  class  Candy3 {
		public static void main(String[] args) {
        		List<Integer>  list = new ArrayList<>();
        		list.add(10);//实际调用list.add(Object 0)
        		Integer x = list.get(0);//实际调用的是Object object = list.get(int  index);
    	}
}

所以在取值时,编译器真正生成的字节码中,还要额外做一个类型转换的操作:

Integer x = (Integer)list.get(0);

如果前面x变量类型修改为int基本类型那么最终生成的字节码是:

int x = ((Integer)list.get(0)).intValue();

泛型反射,获取信息

4.可变参数

jdk5加入

public class Candy4 {
    public static void foo(String... args){
        String[] array = args;
        System.out.println(array);
    }

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

编译器在编译期间将上述代码变换为:

public class Candy4 {
    public static void foo(String[] args){
        String[] array = args;
        System.out.println(array);
    }

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

5.foreach循环

6.switch字符串

jdk7开始可以配合字符串和枚举类使用,相当于拆成两个switch,为什么用hashcode呢?当比较的字符串数量较多时,用equals速度很慢,显然是不合理的,会尽可能减少比较次数,一般hashCode是惟一的,比逐一比较字符串快,但为什么还要再比较一次字符串呢?因为这样可以防止hashCode冲突

public class Candy6{
    public static void choose(String str){
        switch (str){
            case "hello":
                System.out.println("h");
                break;
            case "world":
                System.out.println("w");
                break;
            default:
                break;
        }
    }
}

上面代码会被编译器转换为

class Candy6{
    public static void choose(String str){
    	byte x = -1;
        switch (str.hashCode()){
            case 99162322://"hello"的哈希码
                if(str.equals("hello")){
                	x = 0;
				}
                break;
            case 113318802://"world"的哈希码
                 if(str.equals("world")){
                	x = 1;
				}
                break;
            default:
                break;
        }
        switch(x){
        	case  0:
        		System.out.println("h");
        		break;
        	case  1:
        		System.out.println("w");
        		break;
        	default:
        		break;
		}
    }
}

7.switch 枚举

enum Sex{
    MALE,FEMALE
}
public class Candy7 {
    public static void foo(Sex sex){
        switch (sex){
            case MALE:
                System.out.println("男");
                break;
            case FEMALE:
                System.out.println("女");
                break;
            default:
                break;
        }
    }
}

转换后代码

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

    public static void foo(Sex sex){
    	int  x = $MAP.map[sex.ordinal()];
        switch (x){
            case 1:
                System.out.println("男");
                break;
            case 2:
                System.out.println("女");
                break;
            default:
                break;
        }
    }
}

8.枚举类

enum Sex{//本质Class,实例个数有限,这里仅有两个
    MALE,FEMALE
}

转换后代码

public  final  class  Sex  extends  Enum<Sex>{
	public  static final Sex MALE;
	public  static final Sex  FEMALE;
	public  static final Sex   Sex[]  $VALUES;

	static {
		MALE = new Sex("MALE",0);
		FEMALE = new Sex("FEMALE",1);
		$VALUE$ = 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);
	}
}

9.try-with-resources

jdk7 开始新增了对需要关闭的资源处理的特殊语法
前提:资源对象需要实现AutoCloseable接口,例如InputStream、OutputStream、Connection、Statement、ResultSet等接口都实现类AutoCloseable,使用try-with-resources可以不用写finally语句块,编译时会自动将finally部分加上,并且考虑的十分到位

try(Inputstream  is = new FileInputStream("d://test.txt")){
	
} catch(Exception e){
	
}

甚至考虑到了关闭资源时的异常,t.addSuppressed(e2)压制异常,防止异常信息的丢失。(想想try-with-resources生成的finally中如果抛出了异常,也会打印出来)

10.方法重写时的重写方法

方法重写时对返回值分两种情况:(如下)

  • 父子类的返回值完全一致
  • 子类的返回值可以是父类返回值的子类
class A{
	public Number tt(){
		return 1;
	}
}

class B extends A{
	@Override
	public Integer tt(){
		return 2;
	}
}

对于子类,java编译器会做如下处理:

class B extends A{
	public Integer tt(){
		return 2;
	}
	//合成方法真正重写了父类 public Number tt()方法,仅对对虚拟机可见,
	//与原来的public Integer tt()没有命名冲突
	public synthetic bridge Number tt(){
		return tt();
	}
	//可以用反射代码验证
	for(Method m :  B.class.getDeclaredMethods()){
		System.out.println(m);
	}
	//输出:
	//public java.lang.Integer test.candy.B.m()
	//public java.lang.Number test.candy.B.m()
	
}

11.匿名内部类

public class Candy11 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("ok");
            }
        };
    }
}

转换后代码:

//产生额外的类
final  class  Candy11$1  implements  Runnable{
	Candy11$1(){
	}
	public void  run(){
		System.out.println("ok");
	}
}
public class Candy11 {
    public static void main(String[] args) {
        Runnable runnable = new Candy11$1();
    }
}

引用局部变量的匿名内部类,源代码:局部变量必须final修饰

public class Candy11 {
    public static void test(final  int  x) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("ok"+x);
            }
        };
    }
}

转换后代码:

final  class  Candy11$1  implements  Runnable{
	int  val$x;//用来存x
	Candy11$1(int x){//把x传过来
		this.val$x = x;
	}
	public void  run(){
		System.out.println("ok"+this.val$x);
	}
}
public class Candy11 {
    public static void main(String[] args) {
        Runnable runnable = new Candy11$1();
    }
}

为什么要求局部变量是final的?x不可修改,这样可以保持和匿名内部类中的x一致

学习整理于解密jvm.

发布了55 篇原创文章 · 获赞 0 · 访问量 518

猜你喜欢

转载自blog.csdn.net/weixin_40778497/article/details/104073075