要使用类,必须有两个步骤,一个是加载类,而后是初始化。java
建立一个类的实例可使用到的方法:网络
在什么状况下使用类,不须要初始化类函数
经过子类调用父类的静态变量,子类不会被初始化,可是子类会被加载。ui
public class Parent { static { System.out.println("Parent init"); } public static int v = 100; }
public class Child extends Parent { static { System.out.println("Child init"); } }
public class UseParent { public static void main(String[] args) { System.out.println(Child.v); } }
经过JVM参数-XX:+TraceClassLoading运行获得如下结果spa
[Loaded com.guanjian.Parent from file:/E:/classload/out/production/classload/]
[Loaded com.guanjian.Child from file:/E:/classload/out/production/classload/]
Parent init
100.net
由结果可知,只有父类被初始化了,子类没有被初始化,可是被加载了。日志
直接调用某个类的final修饰的静态变量,不但不会初始化该类,连加载都不会。code
public class FinalFieldClass { public static final String constString = "CONST"; static { System.out.println("FinalFieldClass init"); } }
public class UseFinalField { public static void main(String[] args) { System.out.println(FinalFieldClass.constString); } }
经过JVM参数-XX:+TraceClassLoading运行获得如下结果orm
[Loaded java.net.URI$Parser from C:\Program Files\Java\jdk1.8.0_91\jre\lib\rt.jar]
[Loaded java.lang.Class$MethodArray from C:\Program Files\Java\jdk1.8.0_91\jre\lib\rt.jar]
[Loaded java.lang.Void from C:\Program Files\Java\jdk1.8.0_91\jre\lib\rt.jar]
CONST继承
由结果可知,不但没有打印FinalFieldClass init,连Loaded FinalFieldClass的日志都没有。这是由于final常量直接存放到了常量池中,所以FinalFieldClass类不会被加载。
加载类的步骤
咱们都知道,java在编译类后并非产生固有机器的机器码,而是一段字节码,这段字节码能够存放于任何地方,如.class文件,jar包中,能够经过网络传输。JVM虚拟机在拿取到这段二进制数据流字节码后,就会处理这些数据,并最终转换成一个java.lang.Class的实例(注意,这里并非类自己的实例)。java.lang.Class实例是访问类型元数据的接口,也是实现反射的关键数据。经过Class类提供的接口,能够访问一个类型的方法,字段等信息。
public class ClassTest { public static void main(String[] args) throws ClassNotFoundException { Class clzStr = Class.forName("java.lang.String"); //获取该类的全部方法 Method[] ms = clzStr.getDeclaredMethods(); for (Method m:ms) { //获取该方法的修饰符 String mod = Modifier.toString(m.getModifiers()); //打印方法的修饰符,方法名跟起始括号 System.out.print(mod + " " + m.getName() + " ("); //获取方法的全部参数类型 Class<?>[] ps = m.getParameterTypes(); //若是没有参数,直接打印结束括号 if (ps.length == 0) { System.out.print(')'); } else { //取出全部的参数类型名称,以逗号分隔 for (int i = 0; i < ps.length; i++) { char end = i == ps.length - 1 ? ')' : ','; System.out.print(ps[i].getSimpleName() + end); } } System.out.println(); } } }
运行结果:
public equals (Object)
public toString ()
public hashCode ()
public compareTo (String)
public volatile compareTo (Object)
public indexOf (String,int)
public indexOf (String)
public indexOf (int,int)
public indexOf (int)
static indexOf (char[],int,int,char[],int,int,int)
static indexOf (char[],int,int,String,int)
public static valueOf (int)
public static valueOf (long)
public static valueOf (float)
public static valueOf (boolean)
public static valueOf (char[])
public static valueOf (char[],int,int)
public static valueOf (Object)
public static valueOf (char)
public static valueOf (double)
public charAt (int)
private static checkBounds (byte[],int,int)
public codePointAt (int)
public codePointBefore (int)
public codePointCount (int,int)
public compareToIgnoreCase (String)
public concat (String)
public contains (CharSequence)
public contentEquals (CharSequence)
public contentEquals (StringBuffer)
public static copyValueOf (char[])
public static copyValueOf (char[],int,int)
public endsWith (String)
public equalsIgnoreCase (String)
public static transient format (Locale,String,Object[])
public static transient format (String,Object[])
public getBytes (int,int,byte[],int)
public getBytes (Charset)
public getBytes (String)
public getBytes ()
public getChars (int,int,char[],int)
getChars (char[],int)
private indexOfSupplementary (int,int)
public native intern ()
public isEmpty ()
public static transient join (CharSequence,CharSequence[])
public static join (CharSequence,Iterable)
public lastIndexOf (int)
public lastIndexOf (String)
static lastIndexOf (char[],int,int,String,int)
public lastIndexOf (String,int)
public lastIndexOf (int,int)
static lastIndexOf (char[],int,int,char[],int,int,int)
private lastIndexOfSupplementary (int,int)
public length ()
public matches (String)
private nonSyncContentEquals (AbstractStringBuilder)
public offsetByCodePoints (int,int)
public regionMatches (int,String,int,int)
public regionMatches (boolean,int,String,int,int)
public replace (char,char)
public replace (CharSequence,CharSequence)
public replaceAll (String,String)
public replaceFirst (String,String)
public split (String)
public split (String,int)
public startsWith (String,int)
public startsWith (String)
public subSequence (int,int)
public substring (int)
public substring (int,int)
public toCharArray ()
public toLowerCase (Locale)
public toLowerCase ()
public toUpperCase ()
public toUpperCase (Locale)
public trim ()
Class.forName能够获得表明String类的Class实例,它是反射中最重要的方法。
二进制数据流字节码被加载到虚拟机以后,会进行一系列的验证检查,主要步骤以下。
以上比较复杂,暂略过。
当一个类验证经过时,虚拟机就会进入准备阶段。分配内存空间,分配初始值。若是类存在常量字段,若是被final修饰,就会被直接放入常量池中。若是没有final修饰,就会在初始化中赋值,而不是直接放入常量池。
准备阶段完成后,就是解析类,解析类就是把字节码中的类,接口,字段,方法放入JVM虚拟机实际内存的地址中,方便程序能够真正执行,好比说类的方法会有一个方法表,当须要调用一个类的方法时,就要知道这个方法在方法表中的偏移量,直接调用该方法。
类的初始化是类装载的最后一个阶段。初始化的重要工做就是执行类的初始化方法<clinit>。方法<clinit>是由编译器自动生成的,它是由类静态成员的赋值语句以及static语句合并产生的。相似代码以下
public class SimpleStatic { public static int id = 1; public static int number; static { number = 4; } }
在<clinit>函数中,前后对id和number两个成员变量进行赋值。但若是成员变量是final修饰的,则不在此阶段赋值,而是在准备阶段赋值的。