静态内部类有许多作用,由于非静态内部类的实例创建需要有外部类对象的引用,所以非静态内部类对象的创建必须依托于外部类的实例;而静态内部类的实例创建只需依托外部类;
并且由于非静态内部类对象持有了外部类对象的引用,因此非静态内部类可以访问外部类的非静态成员;而静态内部类只能访问外部类的静态成员;
内部类需要脱离外部类对象来创建实例
避免内部类使用过程中出现内存溢出
public class ClassDemo { private int a = 10; private static int b = 20; static class StaticClass{ public static int c = 30; public int d = 40; public static void print(){ //下面代码会报错,静态内部类不能访问外部类实例成员 //System.out.println(a); //静态内部类只可以访问外部类类成员 System.out.println("b = "+b); } public void print01(){ //静态内部内所处的类中的方法,调用静态内部类的实例方法,属于外部类中调用静态内部类的实例方法 StaticClass sc = new StaticClass(); sc.print(); } } } 静态导包不知道你注意到这种现象没有,比如你使用了 java.util 内的工具类时,你需要导入 java.util 包,才能使用其内部的工具类,如下
但是还有一种导包方式是使用静态导包,静态导入就是使用 import static 用来导入某个类或者某个包中的静态方法或者静态变量。
import static java.lang.Integer.*; public class StaticTest { public static void main(String[] args) { System.out.println(MAX_VALUE); System.out.println(toHexString(111)); } } static 进阶知识我们在了解了 static 关键字的用法之后,来看一下 static 深入的用法,也就是由浅入深,慢慢来,前戏要够~
关于 static 的所属类static 所修饰的属性和方法都属于类的,不会属于任何对象;它们的调用方式都是 类名.属性名/方法名,而实例变量和局部变量都是属于具体的对象实例。
static 修饰变量的存储位置首先,先来认识一下 JVM 的不同存储区域。
虚拟机栈 : Java 虚拟机栈是线程私有的数据区,Java 虚拟机栈的生命周期与线程相同,虚拟机栈也是局部变量的存储位置。方法在执行过程中,会在虚拟机栈种创建一个 栈帧(stack frame)。
本地方法栈: 本地方法栈也是线程私有的数据区,本地方法栈存储的区域主要是 Java 中使用 native 关键字修饰的方法所存储的区域
程序计数器:程序计数器也是线程私有的数据区,这部分区域用于存储线程的指令地址,用于判断线程的分支、循环、跳转、异常、线程切换和恢复等功能,这些都通过程序计数器来完成。
方法区:方法区是各个线程共享的内存区域,它用于存储虚拟机加载的 类信息、常量、静态变量、即时编译器编译后的代码等数据,也就是说,static 修饰的变量存储在方法区中
堆: 堆是线程共享的数据区,堆是 JVM 中最大的一块存储区域,所有的对象实例,包括实例变量都在堆上进行相应的分配。
static 变量的生命周期static 变量的生命周期与类的生命周期相同,随类的加载而创建,随类的销毁而销毁;普通成员变量和其所属的生命周期相同。
static 序列化我们知道,序列化的目的就是为了 把 Java 对象转换为字节序列。对象转换为有序字节流,以便其能够在网络上传输或者保存在本地文件中。
声明为 static 和 transient 类型的变量不能被序列化,因为 static 修饰的变量保存在方法区中,只有堆内存才会被序列化。而 transient 关键字的作用就是防止对象进行序列化操作。
类加载顺序我们前面提到了类加载顺序这么一个概念,static 修饰的变量和静态代码块在使用前已经被初始化好了,类的初始化顺序依次是
加载父类的静态字段 -> 父类的静态代码块 -> 子类静态字段 -> 子类静态代码块 -> 父类成员变量(非静态字段)
-> 父类非静态代码块 -> 父类构造器 -> 子类成员变量 -> 子类成员变量 -> 子类非静态代码块 -> 子类构造器
static 经常用作日志打印