首先,描述了static关键字去修饰java类、方法、变量、代码块的方法然后,从底层分析static关键字,接下来,给出static的一些使用场景和案例最后,对static进行一个总结,包括和普通变量的区分。
1、static关键字基本概念
我们可以一句话来概括:方便在没有创建对象的情况下来进行调用。
也就是说:被static关键字修饰的不需要创建对象去调用,直接根据类名就可以去访问。对于这个概念,下面根据static关键字的四个基本使用来描述。然后在下一部分再来去分析static的原理,希望你能认真读完。
2、static关键字修饰类
java里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。下面看看如何使用。
public class StaticTest { //static关键字修饰内部类 public static class InnerClass{ InnerClass(){ System.out.println("===静态内部类==="); } public void InnerMethod(){ System.out.println("===静态内部方法==="); } } public static void main(String[] args) { //直接通过StaticTest类名访问静态内部类InnerClass InnerClass inner = new StaticTest.InnerClass(); //静态内部类可以和普通类一样使用 inner.InnerMethod(); } }
3、static关键字修饰方法
修饰方法的时候,其实跟类一样,可以直接通过类名来进行调用:
4、static关键字修饰变量
被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的初始化就绕不开类的加载。当一个类编译生成字节码文件。通过以下步骤进行加载
何时进行初始化。jvm对每类只会进行一次初始化但并不是程序中的每个类都会进行初始化。所有的Java程序虚拟机实现必须在每个类或者接口被Java程序“首次主动使用”时才会初始化他们
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为直接包含该成员变量的类或者接口
答案引用自:https://segmentfault.com/q/1010000002569214
类根据代码编写顺序进行初始化,当执行静态代码块时,静态变量b已经在类的准备阶段进行所占内存的划分并赋予默认值0,执行静态代码块时,b=2,然后在执行b的初始化所以b等于1.