本博文主要介绍一下几点内容,文中观点皆为个人观点,如有错误,望各位看官批评指正
- static主要含义
- static主要用途及特征
- 静态变量
- 静态方法
- 静态代码块
- 静态导入
- 静态内部类
- static初始化过程
- static 关键字:是java语法中基础的关键字,我们都知道static关键字可以修饰,类,方法,变量,它所定义的内容都是属于类的。可以不需要实例化而直接通过类名进行调用。(ps:关于static方法调用的各种各样的情况请看上一篇博文。)
- static用法
- static变量是所属类的变量,它的访问权限通过访问控制符(public,private,protected,默认访问权限)进行控制。它在内存中只有一份,相同类的不同实例都指向同一份内存。也就是一个实例中变更该变量,其他实例获取的该变量的值也会发生变化。
- 静态方法:通过static修饰的方法。该方法也是独立于任何实例的,该方法中不能直接调用实例方法和实例变量
- 静态代码块:如果希望在类实例化之前对类进行一些初始化操作,可以将这些操作定义到静态代码块中。当父子类都存在静态代码块,它们的调用关系如示例所示父类静态代码块》子类静态代码块》父类构造代码块》父类构造方法》子类构造代码块》子类构造方法
public class Father { static{ System.out.println("我是父类的static代码块"); } { System.out.println("我是父类的构造代码块"); } Father(){ System.out.println("我是父类的构造方法"); } } // public class Son extends Father { static{ System.out.println("我是子类的static代码块"); } { System.out.println("我是子类的构造代码块"); } public Son(){ System.out.println("我是子类的构造方法"); } public static void main(String[] args){ Son son=new Son(); } } //结果 我是父类的static代码块 我是子类的static代码块 我是父类的构造代码块 我是父类的构造方法 我是子类的构造代码块 我是子类的构造方法
- 导入静态资源:可以在import static **(类的全路径名)。在你编写java代码时,使用这个import static 这个语法可以引入你需要的所有类的所有静态资源。之后可以直接通过方法名调用静态方法。不需要使用类名.方法名进行使用。减少了代码量。但同时削减了代码的可读性。
- 静态内部类:static可以修饰类,但只能修饰内部类。一旦内部类标记了static关键字。它就变成了顶级类,可以直接创建实例。而不依赖外部类的实例进行创建。Java中4种内部类区别再此不做赘述。1,静态内部类可以访问外部类的静态属性和方法。但不能访问直接访问外部类实例方法,需要生成外部类实例后才能访问外部类的实例方法。2,静态内部类可以定义静态方法和静态属性。
//实例内部类创建方式 OutObject outObject=new OutObject(); OutObject.Inner inner=outObject.new Inner(); //静态内部类创建方式 OutObject.Inner inner=new OutObject.Inner();
- static的初始化过程
- 类加载:因为static定义的属性,代码块,方法,类都是属于类的。所以static的初始化就绕不开类的加载。当一个类编译生成字节码文件。通过以下步骤进行加载
- 加载:主要完成3件事情
- 通过一个类的全限定名来获取定义此类的二进制字节流
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
- 在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口
- 连接:
- 验证(以下你可以理解为检查加载的字节码是否合法即可)
- 文件格式验证,第一个阶段验证字节流是否符合class文件格式的规范
- 元数据验证,是对字节码描述的信息进行语义分析,以保证其描述的信息符合java语言规范的要求,从定义的角度来说
- 这个类是否继承了不允许被继承的类
- 这个类是否有父类
- 字节码验证,这个过程复杂的,是通过数据流和控制流分析语义是否合法是否符合逻辑,从运行的阶段
- 例如运行一个方法是否会超出方法体
- 符号引用验证:它是对自身类以外的信息进行匹配性校验,确保解析动作可以正常执行
- 准备:正式为类变量分配内存并设置类变量初始值的阶段(int赋值为0等),仅包括类的静态变量设置内存空间并赋予默认值
- 解析:将虚拟机常量池中符号引用替换为直接引用的过程
- 验证(以下你可以理解为检查加载的字节码是否合法即可)
- 初始化:是执行类构造器方法的过程。类构造器方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并而成。编译器收集的顺序是由语句在源文件中出现的顺序所决定。
- 加载:主要完成3件事情
- 何时进行初始化。jvm对每类只会进行一次初始化但并不是程序中的每个类都会进行初始化。所有的Java程序虚拟机实现必须在每个类或者接口被Java程序“首次主动使用”时才会初始化他们
- 7大主动使用
- 创建类的实例
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射(如Class.forName("com.test.Test"))
- 初始化一个类的子类
- Java虚拟机启动时被表明为启动类的类
- JDK1.7开始提供的动态语言支持:Java.lang.invoke.MethodHandle实例的解析结果REF_getStatic,REF_pitStatic,REF__invokeStatic句柄所对应的类没有进行初始化则进行初始化
- 7大主动使用
- static初始化示例
public class TestStatic { public static int a; static{ b=2; //System.out.println(b); 报错非法向前引用 System.out.println(TestStatic.b); } public static int b = 1; public static void main(String[] args){ System.out.println("static b==="+b); } } //结果 2 static b===1
两个问题:为什么报非法向前引用,为什么会出现这个结果
-
非法向前引用:在类的变量初始化中满足一下四点会报非法向前引用
-
1.设定C为直接包含该成员变量的类或者接口
2. 如果a所在为C的静态成员/非静态成员初始化 或C的静态或非静态代码块中
3. 如果a不是 一个赋值不等式的左值
4. 通过简单名称来访问
-
-
类根据代码编写顺序进行初始化,当执行静态代码块时,静态变量b已经在类的准备阶段进行所占内存的划分并赋予默认值0,执行静态代码块时,b=2,然后在执行b的初始化所以b等于1.
-
- 类加载:因为static定义的属性,代码块,方法,类都是属于类的。所以static的初始化就绕不开类的加载。当一个类编译生成字节码文件。通过以下步骤进行加载