深圳公司社保网站,如何利用seo赚钱,如何查看网站是否开启gzip,山西大同网站建设哪家好1. 语法糖 
所谓的语法糖#xff0c;其实就是指java编译器把*.java源码编译为*.class字节码的过程中#xff0c;自动生成和转换的一些代码#xff0c;主要是为了减轻程序员的负担#xff0c;算是java编译器给我们第一个额外福利 
以下代码的分析#xff0c;借助了javap工具…1. 语法糖 
所谓的语法糖其实就是指java编译器把*.java源码编译为*.class字节码的过程中自动生成和转换的一些代码主要是为了减轻程序员的负担算是java编译器给我们第一个额外福利 
以下代码的分析借助了javap工具idea的反编译功能idea插件jclasslib等工具。 另外编译器转换的结果直接就是class字节码只是为了便于阅读给出了几乎等价的java源码方式并不是编译器还会转换出中间的java源码。 
1.1 默认构造器 
public class Candy1{}编译成class后的代码 
public class Candy1{// 这个无参构造是编译器帮助我们加上的public Candy1(){super();// 即父类Object的无参构造方法即调用 java/lang/Object.init:()V}
}1.2 自动拆装箱 
这个特性是JDK5开始加入的代码片段1 
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();}
}显然之前版本的代码太麻烦了需要在基本类型和包装类型之间来回转换尤其是集合类中操作的都是包装类型因此这些转换的事情在JDK5以后都由编译器在编译阶段完成。 
1.3 泛型集合取值 
泛型也是在JDK5开始加入的特性但java在编译泛型代码会执行泛型擦除的动作即泛型信息在编译为字节码之后就丢失了实际的类型都当作了Object类型来处理 
public class demo4 {public static void main(String[] args) {ArrayListInteger list  new ArrayList();list.add(10); // 实际调用的是 List.add(Object e)Integer x  list.get(0); // 实际调用的是 Object obj  List.get(int index);}
}所以在取值时编译器真正生成字节码中还要额外做一个类型转换的操作 
// 需要将Object转为Integer类型
Integer x  (Integer)list.get(0);如果前面的x变量类型修改为int基本类型那么最终生成的字节码是 
// 需要将Object转为Integer并执行拆箱操作
int x  ((Integer)list.get(0)).intValue();泛型擦除在字节码中所有的对象类型都被转为了Object了 所幸这些麻烦事不用我们自己做 擦除的是字节码上的泛型信息可以看到LocalVariableTypeTable仍然保留了方法参数泛型的信息 
Classfile /E:/Java/学习案例/JVM/JVM/src/test/java/Class/demo4.classLast modified 2024年10月29日; size 478 bytesMD5 checksum 4d2758e590977925b85935c072dc7242Compiled from demo4.java
public class Class.demo4minor version: 0major version: 55flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #8                          // Class/demo4super_class: #9                         // java/lang/Objectinterfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:#1  Methodref          #9.#18         // java/lang/Object.init:()V#2  Class              #19            // java/util/ArrayList#3  Methodref          #2.#18         // java/util/ArrayList.init:()V#4  Methodref          #7.#20         // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;#5  Methodref          #2.#21         // java/util/ArrayList.add:(Ljava/lang/Object;)Z#6  Methodref          #2.#22         // java/util/ArrayList.get:(I)Ljava/lang/Object;#7  Class              #23            // java/lang/Integer#8  Class              #24            // Class/demo4#9  Class              #25            // java/lang/Object#10  Utf8               init#11  Utf8               ()V#12  Utf8               Code#13  Utf8               LineNumberTable#14  Utf8               main#15  Utf8               ([Ljava/lang/String;)V#16  Utf8               SourceFile#17  Utf8               demo4.java#18  NameAndType        #10:#11        // init:()V#19  Utf8               java/util/ArrayList#20  NameAndType        #26:#27        // valueOf:(I)Ljava/lang/Integer;#21  NameAndType        #28:#29        // add:(Ljava/lang/Object;)Z#22  NameAndType        #30:#31        // get:(I)Ljava/lang/Object;#23  Utf8               java/lang/Integer#24  Utf8               Class/demo4#25  Utf8               java/lang/Object#26  Utf8               valueOf#27  Utf8               (I)Ljava/lang/Integer;#28  Utf8               add#29  Utf8               (Ljava/lang/Object;)Z#30  Utf8               get#31  Utf8               (I)Ljava/lang/Object;
{public Class.demo4();descriptor: ()Vflags: (0x0001) ACC_PUBLICCode:stack1, locals1, args_size10: aload_01: invokespecial #1                  // Method java/lang/Object.init:()V4: returnLineNumberTable:line 5: 0public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack2, locals3, args_size10: new           #2                  // class java/util/ArrayList3: dup4: invokespecial #3                  // Method java/util/ArrayList.init:()V7: astore_18: aload_19: bipush        1011: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;14: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z17: pop18: aload_119: iconst_020: invokevirtual #6                  // Method java/util/ArrayList.get:(I)Ljava/lang/Object;23: checkcast     #7                  // class java/lang/Integer26: astore_227: returnLineNumberTable:line 7: 0line 8: 8line 9: 18line 10: 27
}
SourceFile: demo4.java1.4 可变参数 
可变参数也是JDK5开始加入的新特性 
public class demo5 {public static void foo(String... args){String[] arr  args;// 直接赋值System.out.println(arr);}public static void main(String[] args) {foo(hello,world);}
}可变参数String… args 其实是一个String[] args, 从代码的赋值语句中就可以看出来 
同样java编译器会在编译期间就将上述代码转为 
public class demo5 {public static void foo(String... args){String[] arr  args;// 直接赋值System.out.println(arr);}public static void main(String[] args) {foo(new String[]{hello,world});}
}如果调用了foo()则等价代码为foo(new String[]{})创建了一个空的数组而不会传递null进去 1.5 foreach循环 
仍是jdk5开始引入的语法糖数组的循环 
public class demo6 {public static void main(String[] args) {int[] array  {1,2,4,5}; // 数组赋值的简化写法也算是语法糖for (int i : array) {System.out.println(i);}}
}编译器编译后转换为 
public class demo6 {public static void main(String[] args) {int[] array  new int[]{1,2,4,5};for (int i0;iarray.length;i) {int x  array[i]; // x 就是自己定义接受的变量 i System.out.println(x);}}
}集合的循环 
public void test1_List(int[] args){ListInteger list  Arrays.asList(1,2,3,4,5,6);for (Integer i : list) {System.out.println(i);}
}实际被编译器转换为迭代器的调用 
public class demo6 {public static void main(String[] args) {}public void test1_List(int[] args){ListInteger list  Arrays.asList(1,2,3,4,5,6);Iterator list  list.iterator(); // 获取迭代器while(iter.hasNext()){// 对迭代器中包含的对象进行循环Integer e  (Integer)iter.next();// Object类型转为Integer类型System.out.println(e);}}
}foreach循环写法能够配合数组以及所有实现了iterable接口的集合类一起使用其中Iterable用来获取集合的迭代器Iterator 1.6 switch字符串 
从JDK7开始switch可以作用于字符串和枚举类这个功能其实也是语法糖例如 
package Class;public class demo7 {public static void main(String[] args) {}public static void cc(String str){switch (str){case hello:{System.out.println(h);break;}case world:{System.out.println(w);break;}}}
}switch配合String和枚举使用时变量不能为null原因分析完语法糖转换后的代码应当自然清除 会被编译器转换为 
public static void cc(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)){x1;}break;}switch (x){case 0:System.out.println(h);break;case 1:System.out.println(w);}
}可以看到执行了两边switch第一遍根据字符串的hashCode和equals将字符串的转换为相应byte类型第二遍才是利用byte执行进行比较。 
为什么第一遍时必须即比较hashCode又利用equals比较呢hashCode是为了提高效率减少可能的比较而equals是为了防止hashCode冲突例如BM和C.这两个字符串的hashCode值都是2123。 
1.7 switch枚举 
switch枚举的例子原始代码 
enum Sex{MALE,FEMALE
}public class demo8 {public static void foo(Sex sex){switch (sex){case MALE:System.out.println(男);break;case FEMALE:System.out.println(女);break;}}
}/*** 定义一个合成类仅JVM使用对我们不可见* 用来映射枚举的 ordinal 与数组元素的关系* 枚举的 ordinal 表示枚举对象的序号从 0 开始* 即 MALE 的 ordinal()0,FEMALE 的 ordinal()  1*/
static class $MAP{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;}
}1.8 枚举类 
JDK 7 新增了枚举类以前面的性别枚举为例 
enum Sex{MALE,FEMALE
}转换后的代码 
public final class Sex extends EnumSex{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);}
}本质上枚举还是创建了一个对象只不过继承了一个枚举类型的对象 枚举中的值是对象中的成员变量 1.9 try-with-resources 
JDK7开始新增了对需要关闭的资源处理的特殊语法try-with-resources: 
try(资源变量创建资源对象){}catch(){}其中资源对象需要实现 AutoCloseable 接口例如InputStream、OutputStream、Connection、Statement、ResultSet 等接口都实现了AutoCloseable使用try-with-resources可以不用谢finally语句块编译器会帮助生成关闭资源代码 
public class demo9{public static void main(String[] args){try(InputStream is  new FileInputStream(./1.text)){System.out.println(is);}catch(IOException e){e.printStackTrace();}}
}转换为 
public class demo10 {public static void main(String[] args) {try{FileInputStream is  new FileInputStream(./1.text);Throwable t  null;try{System.out.println(is);}catch (Throwable e1){t  e1;throw e1;// t 就是代码出现的异常}finally {if (is!null){if (t!null){try {is.close();}catch (Throwable e2){// 如果close出现异常那么会作为被压制异常添加t.addSuppressed(e2);}}else {// 当代码并未出现异常close出现的异常就是最后catch块中的eis.close();}}}}catch (IOException e){e.printStackTrace();}}
}为什么要设计一个addSuppresed(Throwable e)添加被压制异常的方法呢 
是为了防止异常信息的丢失 
1.10 方法重写时的桥接方法 
都知道方法重写时对放回值分为两种情况 
父子类的返回值完全一致子类返回值可以是父类返回值的子类 
class A{public Number m(){return 1;}
}
class B extends A{// 子类m 方法返回值是 Integer 是父类 m 方法返回值 Number 的子类Overridepublic Integer m(){return 2;}
}对于子类编译器会做如下处理 
class B extends A{public Integer m(){return 2;}// 此方法才是真正重写了父类public Number m() 方法public synthetic bridge Number m(){// 调用 public Integer m()return m();}
}其中桥接方法比较特殊仅对java虚拟机可见并且与原来的public Integer m() 没有命名冲突可以用下面的反射代码来验证 
for(Method m: B.class.getDeclaredMethods()){System.out.println(m);
}输出 
public java.lang.Integer 方法地址;
public java.lang.Number 方法地址;1.11 匿名内部类 
public class demo12{public static void test(final int x){Runnable runnable  new Runnable(){Overridepublic void run(){System.out.println(ok: x);}};}
}转换后 
final class demo12$1 implements Runnable{int val$x;demo12$1(int x){this.val$xx;}public void run(){System.out.println(ok:   this.val$x);}
}在编译后会创建一个新的类并实现Runnable接口以此来实现run方法 在创建 demo12$1 对象时将x的值赋值给 demo$1 对象的val$x属性x不应该再发生改变如果改变了那么val$x属性没有机会再跟着一起变化这解释了为什么匿名内部类引用局部变量时局部变量必须时final的原因 2. 即时编译 
2.1 分层编译 
TieredCompliation 
例 
public static void main(String[] args) {for (int i  0; i  200; i) {long start  System.nanoTime();for (int j  0; j  1000; j) {new Object();}long end  System.nanoTime();System.out.println(触发数i,耗时(end-start));}
}JVM将执行状态分成 5 个层次 
解释执行Interpreter使用c1即时编译执行不带 profiling使用c1即时编译执行带基本的 profiling使用c1即时编译执行带完全的profiling使用c2即时编译执行 profiling 是指在运行过程中收集一些程序执行状态的数据例如 方法的调用次数循环的回边次数 这种优化手段称之为 逃逸分析 发现新建的对象是否可以逃逸可以使用如下选项关闭逃逸分析 -XX: -DoEscapeAnalysis 即时编译器JIT与解释器的区别 
解释器是将字节码解释为机器码下次即使遇到相同的字节码仍会执行重复的解释JIT是将一些字节码编译为机器码并存入Code Cache下次遇到相同的代码直接执行无需再编译解释器是将字节码解释为针对所有平台都通用的机器码JIT 会根据平台类型生成平台特定的机器码 
对于占据大部分的不常用代码我们无需耗费事件将其编译成机器码而是采用解释执行的方式运行。 
另一方面对于仅占据小部分的热点代码我们则可以将其编译成机器码以达到理想的运行速度。执行效率上简单比较一下 Interpreter  C1  C2 总的目标是发现热点代码hotspot名称的由来优化之 
2.2 方法内联 
/*** 方法内联*/
Test
public void test1(){int x  0;for (int i  0; i  500; i) {long start  System.nanoTime();for (int j  0; j  1000; j) {x  square(9);}long end  System.nanoTime();System.out.println(触发数i,耗时(end-start),\t 相乘数值x);}
}
private static int square(final int a){return a * a;
}如果发现square是热点方法并且长度不太长时会进行内联。 内联操作把方法内代码拷贝、粘贴到调用者的位置  这种方法可以大大降低对方法的解析速度 2.3 反射优化 
public class demo2 {public static void foo(){System.out.println(foo...);}public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException {Method foo  demo2.class.getMethod(foo);for (int i 0; i16;i){System.out.printf(%d\t,i);// 调用反射foo.invoke(null);}System.in.read();}
}在进行反射时底层代码会进行判断当调用超过15次时JVM会直接使用类进行调用 
private static int     inflationThreshold  15;static int inflationThreshold() {return inflationThreshold;
}public Object invoke(Object obj, Object[] args)throws IllegalArgumentException, InvocationTargetException
{// 从工厂中拿取反射次数阈值默认15if (numInvocations  ReflectionFactory.inflationThreshold() !method.getDeclaringClass().isHidden() generated  0 U.compareAndSetInt(this, GENERATED_OFFSET, 0, 1)) {try {MethodAccessorImpl acc  (MethodAccessorImpl)new MethodAccessorGenerator(). // 方法访问器生成器 在这里就会直接生成调用方法generateMethod(method.getDeclaringClass(),method.getName(),method.getParameterTypes(),method.getReturnType(),method.getExceptionTypes(),method.getModifiers());parent.setDelegate(acc);} catch (Throwable t) {// Throwable happens in generateMethod, restore generated to 0generated  0;throw t;}}return invoke0(method, obj, args);
}故而在超出反射阈值后JVM会给你使用使用类调用方法 demo2.foo(); // 就是这行代码 通过查看 ReflectionFactory源码可知 sun.reflect.noInflation 可以用来禁用膨胀直接生成GeneratedMethodAccessor1但首次生成比较耗时如果仅反射调用以此不划算sun.reflect.inflationThreshold 可以修改膨胀阈值 3. 前篇知识点 
深入JAVA底层 JVMJava 虚拟机带你认识JVM、程序计数器、JVM栈和方法栈还有堆内存在JVM中类是如何被加载的呢带你认识类加载的一整套流程 
4. 好文相推 
还不了解Git分布式版本控制器本文将带你全面了解并掌握带你认识依赖、继承和聚合都是什么有什么用2-3树思想与红黑树的实现与基本原理 !全网最全! ElasticSearch8.7 搭配 SpringDataElasticSearch5.1 的使用