当前位置: 首页 > news >正文

查icp备案是什么网站网站建设文书

查icp备案是什么网站,网站建设文书,网站定制制作,wordpress创建角色3. 编译期处理 什么是语法糖 所谓的 语法糖 #xff0c;其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中#xff0c;自动生成 和转换的一些代码#xff0c;主要是为了减轻程序员的负担#xff0c;算是 java 编译器给我们的一个额外福利#xff08;给…3. 编译期处理 什么是语法糖 所谓的 语法糖 其实就是指 java 编译器把 *.java 源码编译为 *.class 字节码的过程中自动生成 和转换的一些代码主要是为了减轻程序员的负担算是 java 编译器给我们的一个额外福利给糖吃 以下代码分析的前提 注意以下代码的分析借助了 javap 工具、idea 的反编译功能、idea 插件、jclasslib 等工具。另外 编译器转换的结果直接就是 class 字节码只是为了便于阅读给出了 几乎等价 的 java 源码方式并不是编译器还会转换出中间的 java 源码切记。 3.1 默认构造器 public class Candy1 { }编译成class后的代码 public class Candy1 {// 这个无参构造是编译器帮助我们加上的public Candy1() {super(); // 即调用父类 Object 的无参构造方法即调用 java/lang/Object. init:()V} }3.2 自动拆装箱 这个特性是 JDK 5 开始加入的 代码片段1 public class Candy2 {public static void main(String[] args) {Integer x 1;int y x;} } 这段代码在 JDK 5 之前是无法编译通过的必须改写为 代码片段2 : public class Candy2 {public static void main(String[] args) {Integer x Integer.valueOf(1);int y x.intValue();} }显然之前版本的代码太麻烦了需要在基本类型和包装类型之间来回转换尤其是集合类中操作的都是包装类型因此这些转换的事情在 JDK 5 以后都由编译器在编译阶段完成。即 代码片段1 都会在编 译阶段被转换为 代码片段2 3.3 泛型集合取值 泛型也是在 JDK 5 开始加入的特性但 java 在编译泛型代码后会执行 泛型擦除 的动作即泛型信息 在编译为字节码之后就丢失了实际的类型都当做了 Object 类型来处理 public class Candy3 {public static void main(String[] args) {ListInteger 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(); 擦除的是字节码上的泛型信息可以看到 LocalVariableTypeTable 仍然保留了方法参数泛型的信息 public cn.itcast.jvm.t3.candy.Candy3();descriptor: ()Vflags: ACC_PUBLICCode:stack1, locals1, args_size10: aload_01: invokespecial #1 // Method java/lang/Object. init:()V4: returnLineNumberTable:line 6: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lcn/itcast/jvm/t3/candy/Candy3;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: 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: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z19: pop20: aload_121: iconst_022: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;27: checkcast #7 // class java/lang/Integer30: astore_231: returnLineNumberTable:line 8: 0line 9: 8line 10: 20line 11: 31LocalVariableTable:Start Length Slot Name Signature0 32 0 args [Ljava/lang/String;8 24 1 list Ljava/util/List;LocalVariableTypeTable:Start Length Slot Name Signature8 24 1 list Ljava/util/ListLjava/lang/Integer;;使用反射仍然能够获得这些信息 public SetInteger test(ListString list, MapInteger, Object map) { } Method test Candy3.class.getMethod(test, List.class, Map.class); Type[] types test.getGenericParameterTypes(); for (Type type : types) {if (type instanceof ParameterizedType) {ParameterizedType parameterizedType (ParameterizedType) type;System.out.println(原始类型 - parameterizedType.getRawType());Type[] arguments parameterizedType.getActualTypeArguments();for (int i 0; i arguments.length; i) {System.out.printf(泛型参数[%d] - %s\n, i, arguments[i]);}} }输出 原始类型 - interface java.util.List 泛型参数[0] - class java.lang.String 原始类型 - interface java.util.Map 泛型参数[0] - class java.lang.Integer 泛型参数[1] - class java.lang.Object3.4 可变参数 可变参数也是 JDK 5 开始加入的新特性 Java代码 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);} }被编译器转换后的代码 可变参数 String... args 其实是一个 String[] args 从下面的代码中就可以看出来 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});} }注意如果调用了 foo() 则等价代码为 foo(new String[]{}) 创建了一个空的数组而不会传递 null 进去 3.5 foreach 循环 仍是 JDK 5 开始引入的语法糖 1.数组的 foreach 循环 Java代码 public class Candy5_1 {public static void main(String[] args) {int[] array {1, 2, 3, 4, 5}; // 数组赋初值的简化写法也是语法糖哦for (int e : array) {System.out.println(e);}} }被编译器转换后的代码 public class Candy5_1 {public Candy5_1() {}public static void main(String[] args) {int[] array new int[]{1, 2, 3, 4, 5};for(int i 0; i array.length; i) {int e array[i];System.out.println(e);}} }2.集合的 foreach 循环 Java代码 public class Candy5_2 {public static void main(String[] args) {ListInteger list Arrays.asList(1,2,3,4,5);for (Integer i : list) {System.out.println(i);}} }被编译器转换后的代码 实际被编译器转换为对迭代器的调用 public class Candy5_2 {public Candy5_2() {}public static void main(String[] args) {ListInteger list Arrays.asList(1, 2, 3, 4, 5);Iterator iter list.iterator();while(iter.hasNext()) {Integer e (Integer)iter.next();System.out.println(e);}} }注意foreach 循环写法能够配合数组以及所有实现了 Iterable 接口的集合类一起使用其中 Iterable 用来获取集合的迭代器 Iterator 3.6 switch 字符串从 JDK 7 开始switch 可以作用于字符串和枚举类这个功能其实也是语法糖 1. switch 字符串 Java代码 public class Candy6_1 {public static void choose(String str) {switch (str) {case hello: {System.out.println(h);break;}case world: {System.out.println(w);break;}}} } 注意 switch 配合 String 和枚举使用时变量不能为null原因分析完语法糖转换后的代码应当自然清楚 被编译器转换后的代码 public class Candy6_1 {public Candy6_1() {}public static void choose(String str) {byte x -1;switch(str.hashCode()) {case 99162322: // hello 的 hashCodeif (str.equals(hello)) {x 0;}break;case 113318802: // world 的 hashCodeif (str.equals(world)) {x 1;}}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 如果有如下Java代码 public class Candy6_2 {public static void choose(String str) {switch (str) {case BM: {System.out.println(h);break;}case C.: {System.out.println(w);break;}}} }被编译器转换后的代码 public class Candy6_2 {public Candy6_2() {}public static void choose(String str) {byte x -1;switch(str.hashCode()) {case 2123: // hashCode 值可能相同需要进一步用 equals 比较if (str.equals(C.)) {x 1;} else if (str.equals(BM)) {x 0;}default:switch(x) {case 0:System.out.println(h);break;case 1:System.out.println(w);}}} }2.switch 枚举 Java代码 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;}} } 被编译器转换后的代码 public class Candy7 {/*** 定义一个合成类仅 jvm 使用对我们不可见* 用来映射枚举的 ordinal 与数组元素的关系* 枚举的 ordinal 表示枚举对象的序号从 0 开始* 即 MALE 的 ordinal()0FEMALE 的 ordinal()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;}} } 3.7 枚举类 JDK 7 新增了枚举类以前面的性别枚举为例 Java代码 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};}/*** Sole constructor. Programmers cannot invoke this constructor.* It is for use by code emitted by the compiler in response to* enum type declarations.** param name - The name of this enum constant, which is the identifier* used to declare it.* param ordinal - The ordinal of this enumeration constant (its position* in the enum declaration, where the initial constant isassigned*/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);} }3.8 try-with-resources JDK 7 开始新增了对需要关闭的资源处理的特殊语法 try-with-resources 语法格式 try(资源变量 创建资源对象){} catch( ) {} Java代码 其中资源对象需要实现 AutoCloseable 接口例如 InputStream 、 OutputStream 、 Connection 、 Statement 、 ResultSet 等接口都实现了 AutoCloseable 使用 try-with-resources 可以不用写 finally 语句块编译器会帮助生成关闭资源代码 public class Candy9 {public static void main(String[] args) {try(InputStream is new FileInputStream(d:\\1.txt)) {System.out.println(is);} catch (IOException e) {e.printStackTrace();}} } 转换后代码 public class Candy9 {public Candy9() {}public static void main(String[] args) {try {InputStream is new FileInputStream(d:\\1.txt);Throwable t null;try {System.out.println(is);} catch (Throwable e1) {// t 是我们代码出现的异常t e1;throw e1;} 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();}} }为什么要设计一个 addSuppressed(Throwable e) 添加被压制异常的方法呢是为了防止异常信息的丢失想想 try-with-resources 生成的 fianlly 中如果抛出了异常 Java代码 public class Test6 {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 zeroat test.Test6.main(Test6.java:7)Suppressed: java.lang.Exception: close 异常at test.MyResource.close(Test6.java:18)at test.Test6.main(Test6.java:6) 如以上代码所示两个异常信息都不会丢。 3.9 方法重写时的桥接方法 方法重写时对返回值分两种情况 父子类的返回值完全一致子类返回值可以是父类返回值的子类 Java代码 class A {public Number m() {return 1;} } class B extends A {Override// 子类 m 方法的返回值是 Integer 是父类 m 方法返回值 Number 的子类public 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 test.candy.B.m() public java.lang.Number test.candy.B.m() 3.10 匿名内部类 Java代码 public class Candy11 {public static void main(String[] args) {Runnable runnable new Runnable() {Overridepublic 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();} } 引用局部变量的匿名内部类的Java代码 public class Candy11 {public static void test(final int x) {Runnable runnable new Runnable() {Overridepublic void run() {System.out.println(ok: x);}};} } 转换后代码 // 额外生成的类 final class Candy11$1 implements Runnable {int val$x;Candy11$1(int x) {this.val$x x;}public void run() {System.out.println(ok: this.val$x);} }public class Candy11 {public static void test(final int x) {Runnable runnable new Candy11$1(x);} } 注意 这同时解释了为什么匿名内部类引用局部变量时局部变量必须是 final 的因为在创建 Candy11$1 对象时将 x 的值赋值给了 Candy11$1 对象的 val$x 属性所以 x 不应该再发生变 化了如果变化那么 val$x 属性没有机会再跟着一起变化。 4. 类加载阶段 4.1 加载 将类的字节码载入方法区中内部采用 C 的 instanceKlass 描述 java 类它的重要 field 有 _java_mirror 即 java 的类镜像例如对 String 来说就是 String.class作用是把 klass 暴露给 java 使用_super 即父类_fields 即成员变量_methods 即方法_constants 即常量池_class_loader 即类加载器 _vtable 虚方法表_itable 接口方法表 如果这个类还有父类没有加载先加载父类 加载和链接可能是交替运行的 注意 instanceKlass 这样的【元数据】是存储在方法区1.8 后的元空间内但 _java_mirror 是存储在堆中可以通过前面介绍的 HSDB 工具查看 4.2 链接 链接分为验证、准备、解析三个子阶段 4.2.1 验证 验证类是否符合 JVM 规范安全性检查 用 UE 等支持二进制的编辑器修改 HelloWorld.class 的魔数检查其修改后是否能够通过安全性检查在控制台运行。 E:\git\jvm\out\production\jvmjava cn.itcast.jvm.t5.HelloWorld Error: A JNI error has occurred, please check your installation and try again Exception in thread main java.lang.ClassFormatError: Incompatible magic value 3405691578 in class file cn/itcast/jvm/t5/HelloWorldat java.lang.ClassLoader.defineClass1(Native Method)at java.lang.ClassLoader.defineClass(ClassLoader.java:763)at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)at java.net.URLClassLoader.access$100(URLClassLoader.java:73)at java.net.URLClassLoader$1.run(URLClassLoader.java:368)at java.net.URLClassLoader$1.run(URLClassLoader.java:362)at java.security.AccessController.doPrivileged(Native Method)at java.net.URLClassLoader.findClass(URLClassLoader.java:361)at java.lang.ClassLoader.loadClass(ClassLoader.java:424)at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)at java.lang.ClassLoader.loadClass(ClassLoader.java:357)at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495) 修改完 HelloWorld.class 的魔数后报错信息如上图所示说明验证没有通过 4.2.2 准备 为 static 变量分配空间设置默认值 static 变量在 JDK 7 之前存储于 instanceKlass 末尾从 JDK 7 开始存储于 _java_mirror 末尾static 变量分配空间和赋值是两个步骤分配空间在准备阶段完成赋值在初始化阶段完成如果 static 变量是 final 的基本类型以及字符串常量那么编译阶段值就确定了赋值在准备阶 段完成如果 static 变量是 final 的但属于引用类型那么赋值也会在初始化阶段完成 4.2.3 解析 将常量池中的符号引用解析为直接引用 package cn.itcast.jvm.t3.load; /** * 解析的含义 */ public class Load2 {public static void main(String[] args) throws ClassNotFoundException, IOException {ClassLoader classloader Load2.class.getClassLoader();// loadClass 方法不会导致类的解析和初始化Class? c classloader.loadClass(cn.itcast.jvm.t3.load.C);new C();System.in.read();} }class C {D d new D(); }class D { } 4.3 初始化 1.初始化时调用的方法 初始化即调用cinit()V 方法虚拟机会保证这个类的『构造方法』的线程安全 2.初始化发生的时机 概括得说类初始化是【懒惰的】 main 方法所在的类总会被首先初始化首次访问这个类的静态变量或静态方法时子类初始化如果父类还没初始化会引发子类访问父类的静态变量只会触发父类的初始化Class.forNamenew 会导致初始化 不会导致类初始化的情况 访问类的 static final 静态常量基本类型和字符串不会触发初始化类对象.class 不会触发初始化创建该类的数组不会触发初始化类加载器的 loadClass 方法Class.forName 的参数 2 为 false 时 3.实验验证初始化发生的时机 实验用到的类 class A {static int a 0;static {System.out.println(a init);} }class B extends A {final static double b 5.0;static boolean c false;static {System.out.println(b init);} }验证实验时请先全部注释每次只执行其中一个 public class Load3 {static {System.out.println(main init);}public static void main(String[] args) throws ClassNotFoundException {// 1. 静态常量基本类型和字符串不会触发初始化System.out.println(B.b);// 2. 类对象.class 不会触发初始化System.out.println(B.class);// 3. 创建该类的数组不会触发初始化System.out.println(new B[0]);// 4. 不会初始化类 B但会加载 B、AClassLoader cl Thread.currentThread().getContextClassLoader();cl.loadClass(cn.itcast.jvm.t3.B);// 5. 不会初始化类 B但会加载 B、AClassLoader c2 Thread.currentThread().getContextClassLoader();Class.forName(cn.itcast.jvm.t3.B, false, c2);// 1. 首次访问这个类的静态变量或静态方法时System.out.println(A.a);// 2. 子类初始化如果父类还没初始化会引发System.out.println(B.c);// 3. 子类访问父类静态变量只触发父类初始化System.out.println(B.a);// 4. 会初始化类 B并先初始化类 AClass.forName(cn.itcast.jvm.t3.B);} }5. 类加载器 以 JDK 8 为例 5.1 启动类加载器 用 Bootstrap 类加载器加载类 Java类 package cn.itcast.jvm.t3.load;public class F {static {System.out.println(bootstrap F init);} }执行 package cn.itcast.jvm.t3.load; public class Load5_1 {public static void main(String[] args) throws ClassNotFoundException {Class? aClass Class.forName(cn.itcast.jvm.t3.load.F);System.out.println(aClass.getClassLoader());} } 输出 E:\git\jvm\out\production\jvmjava -Xbootclasspath/a:. cn.itcast.jvm.t3.load.Load5 bootstrap F init null-Xbootclasspath 表示设置 bootclasspath 其中 /a:. 表示将当前目录追加至 bootclasspath 之后 可以用这个办法替换核心类 java -Xbootclasspath:java -Xbootclasspath/a:java -Xbootclasspath/p: 5.2 扩展类加载器 Java类 package cn.itcast.jvm.t3.load; public class G {static {System.out.println(classpath G init);} } 执行 public class Load5_2 {public static void main(String[] args) throws ClassNotFoundException {Class? aClass Class.forName(cn.itcast.jvm.t3.load.G);System.out.println(aClass.getClassLoader());} } 输出 classpath G init sun.misc.Launcher$AppClassLoader18b4aac2 写一个同名的类 package cn.itcast.jvm.t3.load; public class G {static {System.out.println(ext G init);} } 打个 jar 包 E:\git\jvm\out\production\jvmjar -cvf my.jar cn/itcast/jvm/t3/load/G.class 已添加清单 正在添加: cn/itcast/jvm/t3/load/G.class(输入 481) (输出 322)(压缩了 33%)将 jar 包拷贝到 JAVA_HOME/jre/lib/ext 重新执行 Load5_2 输出 ext G init sun.misc.Launcher$ExtClassLoader29453f445.3 双亲委派模式 所谓的双亲委派就是指调用类加载器的 loadClass 方法时查找类的规则 protected Class? loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// 1. 检查该类是否已经加载Class? c findLoadedClass(name);if (c null) {long t0 System.nanoTime();try {if (parent ! null) {// 2. 有上级的话委派上级 loadClassc parent.loadClass(name, false);} else {// 3. 如果没有上级了ExtClassLoader则委派BootstrapClassLoaderc findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {}if (c null) {long t1 System.nanoTime();// 4. 每一层找不到调用 findClass 方法每个类加载器自己扩展来加载c findClass(name);// 5. 记录耗时sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;} }例如 public class Load5_3 {public static void main(String[] args) throws ClassNotFoundException {Class? aClass Load5_3.class.getClassLoader().loadClass(cn.itcast.jvm.t3.load.H);System.out.println(aClass.getClassLoader());} } 执行流程为 1. sun.misc.Launcher$AppClassLoader //1 处 开始查看已加载的类结果没有 2. sun.misc.Launcher$AppClassLoader // 2 处委派上级 sun.misc.Launcher$ExtClassLoader.loadClass() 3. sun.misc.Launcher$ExtClassLoader // 1 处查看已加载的类结果没有 4. sun.misc.Launcher$ExtClassLoader // 3 处没有上级了则委派 BootstrapClassLoader 查找 5. BootstrapClassLoader 是在 JAVA_HOME/jre/lib 下找 H 这个类显然没有 6. sun.misc.Launcher$ExtClassLoader // 4 处调用自己的 findClass 方法是在 JAVA_HOME/jre/lib/ext 下找 H 这个类显然没有回到 sun.misc.Launcher$AppClassLoader 的 // 2 处 7. 继续执行到 sun.misc.Launcher$AppClassLoader // 4 处调用它自己的 findClass 方法在 classpath 下查找找到了 5.4 线程上下文类加载器 我们在使用 JDBC 时都需要加载 Driver 驱动不知道你注意到没有不写 Class.forName(com.mysql.jdbc.Driver) 也是可以让 com.mysql.jdbc.Driver 正确加载的你知道是怎么做的吗 让我们追踪一下源码 public class DriverManager {// 注册驱动的集合private final static CopyOnWriteArrayListDriverInfo registeredDrivers new CopyOnWriteArrayList();// 初始化驱动static {loadInitialDrivers();println(JDBC DriverManager initialized);}先不看别的看看 DriverManager 的类加载器 System.out.println(DriverManager.class.getClassLoader());打印 null表示它的类加载器是 Bootstrap ClassLoader会到 JAVA_HOME/jre/lib 下搜索类但 JAVA_HOME/jre/lib 下显然没有 mysql-connector-java-5.1.47.jar 包这样问题来了在 DriverManager 的静态代码块中怎么能正确加载 com.mysql.jdbc.Driver 呢 继续看 loadInitialDrivers() 方法 private static void loadInitialDrivers() {String drivers;try {drivers AccessController.doPrivileged(new PrivilegedActionString () {public String run() {return System.getProperty(jdbc.drivers);}});} catch (Exception ex) {drivers null;}// 1使用 ServiceLoader 机制加载驱动即 SPIAccessController.doPrivileged(new PrivilegedActionVoid() {public Void run() {ServiceLoaderDriver loadedDrivers ServiceLoader.load(Driver.class);IteratorDriver driversIterator loadedDrivers.iterator();try{while(driversIterator.hasNext()) {driversIterator.next();}} catch(Throwable t) {// Do nothing}return null;}});println(DriverManager.initialize: jdbc.drivers drivers);// 2使用 jdbc.drivers 定义的驱动名加载驱动if (drivers null || drivers.equals()) {return;}String[] driversList drivers.split(:);println(number of Drivers: driversList.length);for (String aDriver : driversList) {try {println(DriverManager.Initialize: loading aDriver);// 这里的 ClassLoader.getSystemClassLoader() 就是应用程序类加载器Class.forName(aDriver, true, ClassLoader.getSystemClassLoader());} catch (Exception ex) {println(DriverManager.Initialize: load failed: ex);}} }先看 2发现它最后是使用 Class.forName 完成类的加载和初始化关联的是应用程序类加载器因此 可以顺利完成类加载 再看 1它就是大名鼎鼎的 Service Provider Interface SPI 约定如下在 jar 包的 META-INF/services 包下以接口全限定名名为文件文件内容是实现类名称 这样就可以使用 ServiceLoader接口类型 allImpls ServiceLoader.load(接口类型.class);Iterator接口类型 iter allImpls.iterator();while(iter.hasNext()) {iter.next();}来得到实现类体现的是【面向接口编程解耦】的思想在下面一些框架中都运用了此思想 JDBCServlet 初始化器Spring 容器Dubbo对 SPI 进行了扩展 接着看 ServiceLoader.load 方法 public static S ServiceLoaderS load(ClassS service) {// 获取线程上下文类加载器ClassLoader cl Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl); }线程上下文类加载器是当前线程使用的类加载器默认就是应用程序类加载器它内部又是由 Class.forName 调用了线程上下文类加载器完成类加载具体代码在 ServiceLoader 的内部类 LazyIterator 中 private S nextService() {if (!hasNextService())throw new NoSuchElementException();String cn nextName;nextName null;Class? c null;try {c Class.forName(cn, false, loader);} catch (ClassNotFoundException x) {fail(service,Provider cn not found);}if (!service.isAssignableFrom(c)) {fail(service,Provider cn not a subtype);}try {S p service.cast(c.newInstance());providers.put(cn, p);return p;} catch (Throwable x) {fail(service,Provider cn could not be instantiated,x);}throw new Error(); // This cannot happen }5.5 自定义类加载器 什么时候需要自定义类加载器 1想加载非 classpath 随意路径中的类文件2都是通过接口来使用实现希望解耦时常用在框架设计3这些类希望予以隔离不同应用的同名类都可以加载不冲突常见于 tomcat 容器 步骤  1. 继承 ClassLoader 父类 2. 要遵从双亲委派机制重写 findClass 方法 注意不是重写 loadClass 方法否则不会走双亲委派机制 3. 读取类文件的字节码 4. 调用父类的 defineClass 方法来加载类 5. 使用者调用该类加载器的 loadClass 方法 6.运行期优化 6.1 即时编译
http://www.yutouwan.com/news/363059/

相关文章:

  • 中国建设信息网站权重查询
  • c做项目的网站河北邯郸ktv
  • 济南住建局官方网站网站建设营销平台
  • 怎么查看网站是否被百度收录代理注册个公司一般需要多少钱
  • 茌平网站建设公司赤峰市做网站公司
  • 网站开发全程实例课本代码成立网站是不是需要先成立公司
  • 成都网站运营维护厂家网址提交大全
  • 网站和域名低价网站设计多少钱
  • 龙江手机网站建设网站开发岗位职责
  • 平面设计类网站有哪些如何用html做班级网站
  • 做理财的网站有哪些在线生成头像
  • 网站正在建设代码手机端网站建设备案
  • 什么公司网站建设做的好flash如何制作网站
  • 个人网站 前置审批中交路桥建设有限公司网站
  • 网站备案的幕布视频网站源码下载
  • 网站免费认证联盟网站在哪备案
  • 济南市莱芜区网站西安未央区做网站
  • 赶集网网站建设分析茶叶包装设计
  • mip网站实例西安cms建站模板
  • 深圳市建设交易中心网站首页汽车专业科技网站建设
  • 网站微信认证阳江招聘网最新招聘信息网美容框
  • 开江网站建设潍坊关键词优化软件
  • 邯郸做网站流程虚拟服务器怎样做网站
  • 可以用什么网站做mc官方宝安网站设计服务
  • wordpress制作视频站网络服务商怎么查询
  • 法库网站建设.php的网站是怎么做的
  • 杭州 电商设计网站建设公司网站如何做分录
  • 织梦可以做移动网站吗短网址批量在线生成
  • 网站建设 国外玉树北京网站建设
  • 盐山县做网站网页设计收费标准需要多少钱