虛擬機是有規範的,HotSpot是sun公司也就是如今的oracle公司提供的,虛擬機並非只有oracle有,IBM也有虛擬機,虛擬機是有規範的, 只要遵循這個規範就能夠本身造虛擬機。虛擬機也有商用的,收費很高。Oracle是免費的虛擬機。Openjdk是基於jdk的開源實現,open是開源的意思。html
HotSpot是oracle的,是使用的很普遍的。java
Java代碼中,類型的加載,連接,初始化都是在程序運行期間runtime完成的(其餘的一些語言是編譯期間完成的)。類型是定義的一個類、接口、枚舉,不是對象。數據庫
動態代理是類型在運行期間動態的生成出來,在編譯期間是沒有的。windows
Java是靜態語言,可是也有不少動態的特性。數組
加載:class文件從磁盤到內存。安全
鏈接:類與類的關係肯定好,字節碼的驗證校驗。字節碼有問題虛擬機就不會去執行。字節碼文件是能夠人爲操縱的。網絡
初始化:類的靜態變量賦值。都是在運行期間完成的,不是編譯期間。數據結構
類加載器:每個類型(類)會被歸入jvm管轄範圍中。加載類的工具。oracle
虛擬機就是一個進程,進程在某些狀況下就會停掉:dom
符號引用就是字符串,寫的時候,直接引用就是去找相應的地址。
類的卸載:類的信息從內存移除。
加載:類加載到內存,.class文件的二進制數據讀入到內存,放入到方法區中,而後在內存建立一個Class對象,虛擬機規範並無規定Class對象放在哪裏,Oracle的HotSpot虛擬機將其放在了方法區中(沒有放到堆區),Class對象封裝類在方法區的數據結構。全部的實例都會對應一份Class對象。因此反射要使用Class對象。
驗證:驗證類的正確性
準備:靜態變量賦予初始值,分配相應的空間大小。
解析:符號引用轉換爲直接引用
初始化:賦予正確的初始值
加載連接初始化:加載到內存、校驗,賦予默認值、正確值,符號引用轉換。
加載class文件的方式:
Java對類的使用方式:
類或接口必須首次主動使用才初始化(初始化就會執行裏面的static代碼塊)。不首次主動使用也會加載,只是不初始化。
JVM規範容許加載器預料某個類將要使用時候預先加載它,若是預先加載中遇到了class文件錯誤,類加載器要在首次主動使用這個類時候報告錯誤。入股這個類一直沒有首次使用,就一直不報錯。
主動使用7種:
其餘使用類的方式都是被動使用,都不會致使類的初始化。有可能致使類的加載和連接。
調用ClassLoader類的loadClass方法加載一個類,並非對類的主動使用,不會致使類的初始化。
先讓父類加載器加載,加載不了在本身加載,是爲了安全。
表面看是一種繼承關係,其實是一種包含關係。
public class 額鵝鵝鵝 { public static void main(String[] args) { System.out.println(Child.p); } } class Parent{ static String p = "parent"; static { System.out.println("parent static"); } } class Child extends Parent{ static String c = "Child"; static { System.out.println("Child static"); } } /* System.out.println(Child.c) parent static Child static Child 使用子類的靜態屬性,是對子類的主動使用,父類的靜態代碼塊執行,子類的靜態代碼塊會執行。 System.out.println(Child.p) parent static parent 使用父類的靜態屬性,不是對子類的主動使用,父類的靜態代碼塊執行,子類的靜態代碼塊不會執行。 */
-XX:+TraceClassLoading,追蹤類的加載信息
-XX:+TraceClassUnloading,追蹤類的卸載信息
-XX:+<option>:開啓option選項
-XX:-<option>:關閉option選項
-XX:<option>=value:設置option選項值
Jvm的參數有上千個,
http://www.javashuo.com/article/p-garuezop-cr.html
package com.ssss; class A { static final String c = "Child";//final是常量,編譯階段,常量放在調用這個常量的方法所在的類的常量池中。就是TT類的常量池中。 //TT類並無直接引用A類,所以使用A.c這個常量的時候,不會觸發A類的初始化,所以Child static不會打印。 //由於是在TT類的常量池中,所以TT類和A類沒有關係了。甚至將A類的class文件刪除均可以。(不能刪源文件,不然就不能編譯了) volatile int s = 1; static { System.out.println("Child static"); } public synchronized int j() { return s++; } }/* Child */ // javap 查看java編譯器爲咱們生成的字節碼彙編代碼。 /* C:\Users\Admin\Desktop\圖片\111>javap -c A.class class ssss.A { static final java.lang.String c;//屬性 static {};//靜態 Code: 0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream :System.out.println 3: ldc #19 // String Child static 5: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V :System.out.println 8: return ssss.A();//方法 Code: 0: aload_0 1: invokespecial #30 // Method java/lang/Object."<init>":()V 4: return } C:\Users\Admin\Desktop\圖片\111>javap -c TT.class Compiled from "TT.java" public class ssss.TT { public ssss.TT(); Code: 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream :System.out.println 3: ldc #22 // String Child ,A.c已是Child了,編譯時候就已是Child了。ldc將int,float,String推到棧頂。 5: invokevirtual #24 // Method java/io/PrintStream.println:(Ljava/lang/String;)V :System.out.println 8: return } */ public class TT { public static void main(String[] args) { System.out.println(A.c); } }
class abc { //常量final UUID.randomUUID()不是編譯期間能夠肯定值,那麼編譯時候就不會把str的值放入 //DDD類的常量池中,運行時候,就會主動使用abc類,致使abc類的初始化。不然abc類是不會初始化的(abc的static代碼塊就不會執行)。 public static final String str = UUID.randomUUID().toString(); static { System.out.println("static"); } } public class DDD{ public static void main(String[] args) { System.out.println(abc.str); } }
class abc { static { System.out.println("static abc"); } } public class DDD{ public static void main(String[] args) { abc a = new abc();//靜態代碼塊執行 System.out.println(a.getClass());//class com.ssss.abc System.out.println(a.getClass().getSuperclass());//class java.lang.Object abc[] ss = new abc[1];//不會執行靜態代碼塊,數組不是主動使用, System.out.println(ss.getClass());//class [Lcom.ssss.abc; System.out.println(ss.getClass().getSuperclass());//class java.lang.Object int[] i = new int[1];//,不是主動使用, System.out.println(i.getClass());//class [I I是int類型 System.out.println(i.getClass().getSuperclass());//class java.lang.Object char[] c = new char[1];//,不是主動使用, System.out.println(c.getClass());//class [C C是char類型 System.out.println(c.getClass().getSuperclass());//class java.lang.Object boolean[] b = new boolean[1];//,不是主動使用, System.out.println(b.getClass());//class [Z Z是boolean類型 System.out.println(b.getClass().getSuperclass());//class java.lang.Object short[] s = new short[1];//,不是主動使用, System.out.println(s.getClass());//class [S S是short類型 System.out.println(s.getClass().getSuperclass());//class java.lang.Object long[] l = new long[1];//,不是主動使用, System.out.println(l.getClass());//class [J J是long類型 System.out.println(l.getClass().getSuperclass());//class java.lang.Object byte[] by = new byte[1];//,不是主動使用, System.out.println(by.getClass());//class [B B是byte類型 System.out.println(by.getClass().getSuperclass());//class java.lang.Object } }
//一個接口初始化,並不要求父接口也初始化。 //真正使用父接口時候(如使用接口中的常量時候)纔不會初始化。類的初始化是會初始化父類的。 //final常量是會放到調用類的常量池中去的,不會引發定義常量的類的初始化,運行期間就放進去了。 //若是常量是隨機數運行期間才能肯定的,那麼就會引發定義常量的類的初始化。 public class sd { public static void main(String[] args) { System.out.println(Ic.b); } } interface Ip { public static int a = 5; } interface Ic extends Ip { public static int b = 6; }
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton類,最後調用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//0 } } @SuppressWarnings("static-access") class Singleton { public static int i; /*static { System.out.println("s "+s );//s報錯,找不到定義。 }*/ public static Singleton s = new Singleton();//看到new就會去執行構造函數。在執行下面的靜態塊。不然不執行構造函數。 static { System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//1 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0, i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1,j這裏是1,可是後面把j賦值爲了0,因此get時候是0. System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } // public static int j = 0;//就是執行這裏,再去執行get方法,把對象的屬性j賦值成了0 public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:0 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//0 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//1 } public static int j = 0;//就是執行這裏,再去執行get方法,把對象的屬性j賦值成了0。即便這行代碼放在這裏,j也會是0 //由於先初始化Singleton類,get方法最後調用。即便get方法是靜態的,也是在main函數中調用的時候再去執行,不會初始化執行,初始化只會執行靜態代碼塊。構造函數也只是有new纔會執行。初始化從上到下執行。 static { System.out.println("s.i4:"+s.i);//1 System.out.println("s.j4:"+s.j);//0 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:1 s.i3:1 s.j3:1 s.i4:1 s.j4:0 i1:1 j1:0 s.i1:1 s.j1:0 1 0*/
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton類,最後調用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//0 } } @SuppressWarnings("static-access") class Singleton { public static int i; public static Singleton s = new Singleton();//看到new就會去執行構造函數。在執行下面的靜態塊。不然不執行構造函數。 public static int j = 0; static {//不加static,則是最早執行,而後執行構造函數,且每一個對象都會執行一遍 System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//0 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0, i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1, System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:0 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//0 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//0 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:0 s.i3:1 s.j3:0 i1:1 j1:0 s.i1:1 s.j1:0 1 0*/
package com.ssss; public class sd { @SuppressWarnings("static-access") public static void main(String[] args) { Singleton ss = Singleton.get();//先去初始化Singleton類,最後調用get方法。 System.out.println(ss.i);//1 System.out.println(ss.j);//1 } } @SuppressWarnings("static-access") class Singleton { public static int i; public static int j = 0; public static Singleton s = new Singleton();//看到new就會去執行構造函數。在執行下面的靜態塊。不然不執行構造函數。 static { System.out.println("s.i2:"+s.i);//1 System.out.println("s.j2:"+s.j);//1 } private Singleton() { System.out.println("i0:"+i);//i:0 System.out.println("j0:"+j);//j:0 i++; j++; System.out.println("i:"+i);//i:1 System.out.println("j:"+j);//j:1 System.out.println("s.i:"+s.i);//1 System.out.println("s.j:"+s.j);//1 } public static Singleton get() { System.out.println("i1:"+i);//i1:1 System.out.println("j1:"+j);//j1:1 System.out.println("s.i1:"+s.i);//1 System.out.println("s.j1:"+s.j);//1 return s; } static { System.out.println("s.i3:"+s.i);//1 System.out.println("s.j3:"+s.j);//1 } } /*i0:0 j0:0 i:1 j:1 s.i:1 s.j:1 s.i2:1 s.j2:1 s.i3:1 s.j3:1 i1:1 j1:1 s.i1:1 s.j1:1 1 1 */
類加載器:
1.1根類加載器(Bootstrap)
1.2擴展類加載器(Extension)
1.3系統(應用)類加載器(System,App)
Loader1發起加載Sample類,可是根加載器和擴展加載器都是加載特定目錄的類,所以最終是由系統加載器加載成功,並把結果返回給loader1加載器。首先是向上委託到根,加載不了又向下委託,一個雙向循環。
rt.jar是使用最多的類,平時使用的都是這個裏面的。
定義類加載器:加載這個類成功的類加載器。Sample類是由系統類加載器加載成功的,系統類加載器就是定義類加載器。
package com.ssss; public class TT { public static void main(String[] args) throws Exception { Class cl = Class.forName("java.lang.String"); System.out.println(cl.getClassLoader());//null。返回類加載器,若是是null就是bootStrap類加載器加載的。 Class cl1 = Class.forName("com.ssss.C");//反射,主動使用,static會執行 System.out.println(cl1.getClassLoader());//sun.misc.Launcher$AppClassLoader@73d16e93,,內部類,系統加載器/應用加載器。 //應用加載器 加載的是 工程環境變量 classpath裏面的類。 System.out.println("-------------------------------"); ClassLoader l = ClassLoader.getSystemClassLoader();//系統加載器 Class dd = l.loadClass("com.ssss.C");//只是loadClass,不是對類的主動使用,不是初始化,不執行static代碼塊 System.out.println(dd);//class com.ssss.C System.out.println("=============================="); ClassLoader l1 = l.getParent(); System.out.println(l1);//sun.misc.Launcher$ExtClassLoader@15db9742 ClassLoader l2 = l1.getParent(); System.out.println(l2);//null } } class C { static { System.out.println("ccccc"); } }
public class TT { public static void main(String[] args) throws Exception { ClassLoader t = Thread.currentThread().getContextClassLoader(); System.out.println(t);//sun.misc.Launcher$AppClassLoader@73d16e93。加載應用的加載器。 String s = "com\\ssss\\C.class"; Enumeration<URL> urls = t.getResources(s); while(urls.hasMoreElements()) { URL u = urls.nextElement(); System.out.println(u);//"com\\ssss\\C.class" } System.out.println("----=======-------========-----======="); Class c = TT.class; System.out.println(c.getClassLoader());//sun.misc.Launcher$AppClassLoader@73d16e93 Class c1 = String.class; System.out.println(c1.getClassLoader());//null。根加載器。String在rt.jar中 } }
類加載器加載的是類,不是對象。線程上下文clssloader是AppClassLoader