一,将自己注入到一个静态变量中实现静态类,如下写法
以上方法的目的是要实现一个静态类,方便用类名获取对象实例,一般情况下调用普通方法需要对象实例.这对象要么new出来,要么spring的注入如下是spring注入对象,
将对象赋值给一个静态变量后,则可以用类名先调用该类变量获取实例,再调用方法.
一般情况下static不能直接在类名前修饰,但是静态内部类可以,因此static也用于静态内部类
以下代码是内部类的作用域
/** * 1,内部类一般只为其外部类使用; * 2,内部类提供了某种进入外部类的窗户; * 3,每个内部类都能独立的继承一个接口,而无论外部类是否已经继承了某个接口,因此内部类使得多重继承得以实现 * @Author jisen * @Date 2019/3/5 14:30 */ public class OutClass extends ClassB { //常量,类变量,普通变量 private static final Object OUTREFERENCECONSTANT = new Object();//外部常量 private static Object outStaticRefField = new Object();//外部类变量 private Object outField = new Object();//外部普通变量 //外部类方法 public void outmethod(){}//外部普通方法 public static void outstaticmethod(){}//外部静态方法 //静态内部类可继承可实现 static class InnerStaticClass extends ClassA implements ClassC{ private static final Object INREFERENCECONSTANT2 = new Object();//静态内部类常量 private static Object inStaticRefField = new Object();//静态内部类,类变量 private Object inField = new Object();//静态内部类普通变量 //静态内部类的静态方法只能访问静态内外成员 public static void innerStaticClassMethod(){ //outmethod(); //innerClassMethod(); outstaticmethod(); innerStaticClassMethod(); System.out.println(OUTREFERENCECONSTANT); System.out.println(INREFERENCECONSTANT2); System.out.println(outStaticRefField); System.out.println(inStaticRefField); //System.out.println(outField); //System.out.println(inField); } ////静态内部类的普通方法只能访问外部静态成员,以及内部所有成员 public void innerClassMethod(){ //outmethod();//静态方法不能访问外部非静态方法 innerClassMethod(); outstaticmethod(); innerStaticClassMethod(); System.out.println(OUTREFERENCECONSTANT); System.out.println(INREFERENCECONSTANT2); System.out.println(outStaticRefField); //System.out.println(outField); System.out.println(inField); } } //普通内部可继承可实现 class InnerClass extends ClassA implements ClassC{ private final Object INREFERENCECONSTANT3 = new Object();//可以定义不可变量 //private static final Object INREFERENCECONSTANT4 = new Object();//无法定义常量 //private static Object inStaticRefField2 = new Object();//不可定义类变量 private Object inField2 = new Object();//可定义普通变量 //普通内部类只能有普通方法,可访问外部类的所有成员 public void innerMethod(){ System.out.println(OUTREFERENCECONSTANT); System.out.println(outStaticRefField); System.out.println(outField); } //public static void innerMethod2(){} } }
内部类的使用方法
/** * @Desc 内部类的使用 * @Author jisen * @Date 2019/3/5 14:34 */ public class TestStatic { public void method(){ //通过外部类对象所能到达的所有操作 OutClass outClass = new OutClass(); outClass.outmethod(); OutClass.InnerClass innerClass = outClass.new InnerClass(); innerClass.innerMethod(); //通过外部类名所能达到的操作 OutClass.outstaticmethod();//访问外部公共静态成员 OutClass.InnerStaticClass.innerStaticClassMethod();//访问公共静态成员,再访问静态成员内部的静态成员 //静态内部类对象,静态内部类可以不通过外部实例对象,直接new产生 OutClass.InnerStaticClass innerStaticClass = new OutClass.InnerStaticClass(); innerStaticClass.innerClassMethod();//用静态内部类实例对象访问成员 //普通静态内部类,需要外部类的实例对象 OutClass.InnerClass innerClass1 = new OutClass().new InnerClass(); innerClass1.innerMethod(); } }
二,static用于单例
//饿汉式,静态常量,类加载的时候在准备阶段这个常量就分配了内存并赋值, //始终只有一个实例,避免了线程安全问题 public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } } //饿汉式静态代码块,原理跟静态常量一样,在类加载的准备阶段赋初值null, //初始化阶段执行<client>(),实例化对象并赋值给静态成员 public class Singleton { private static Singleton instance; static { instance = new Singleton(); } private Singleton(){} public static Singleton getInstance(){ return instance; } } //静态内部类,本质上跟第一种是一样的,内部类常量,在类加载的准备阶段就实例化了. //只有一份避免了线程竞争产生多分 public class Singleton { private Singleton() {} private static class SingletonInstance { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return SingletonInstance.INSTANCE; } }
类加载的过程:
1,加载是jvm类加载器通过一个类的全限定名获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,并在内存中
生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口.
2,验证,语法,以及class文件的验证
3,准备阶段,
类变量分配内存地址并设置类变量初始值阶段,
类变量分配内存地址并设置类变量初始值阶段,
类变量分配内存地址并设置类变量初始值阶段
重话三,类变量就是被static修饰的变量,不包含普通实例变量,普通实例变量会在对象实例化的时候随着对象一起分配在java堆中,比如 public static int value=123;它
在准备阶段的初始值是0而不是123,因为准备阶段尚未执行任何java方法,而把value赋值为123的putstatic指令时程序被编译后存放于类构造器<client>()方法中,执
行java方法是在初始化阶段.各类型的零值,int 0,long 0L,short (short)0,char 'u0000',byte (byte)0,boolean false,float 0.0f,double 0.0d,reference null
如果是public static final Object value=new Object();这个类字段还被final修饰被定义为ConstantValue,所以常量在准备阶段就被赋值new Object()的内存地址
4解析阶段,将常量池中的符号引用替换为直接引用的过程
5,初始化,在准备阶段类变量已经赋值过一次系统要求的初值,而初始化阶段则将执行类构造器<client>()方法,这个不是类的构造函数,<client>()方法是由编译器
自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,其顺序是由语句在源文件中出现的顺序所决定的.所以static修饰的语句块或者类变量,如
果他们先后对一个值赋值那取决于最后赋值的那个.其次<client>()方法执行之前会默认先执行父类的<client>()的方法,因此出现在父类的static修饰的类变量和静态
语句块会先执行.
本文通过注入静态变量,静态内部类,单例这三种应用场景来展示了static的使用特点,并详细描述jvm类加载过程,揭示static的内在逻辑
现在做如下总结
1,static是类级别的,被static修饰的变量叫类变量.可以不需要对象(this)访问,而能够通过类名直接访问
2,被static修饰的变量,常量,静态代码块,静态方法,静态类.他们的执行顺序如下分析:
a,基本数据类型静态常量在类加载的准备阶段就分配内存并赋值最终值,如int类型常量 OUTREFERENCECONSTANTINT=3
b,引用数据类型常量和类变量在类加载的准备阶段会赋初值,引用类型为null int为0如下图所示,并在类加载的初始化阶段执行<client>()方法会给它赋代码
值,idea能进入到debug模式表示类加载的初始化阶段调用<client>()方法
c.静态代码块,会在类加载的初始化阶段,也就是执行<client>()方法的时候执行,这个时候与引用数据类型常量类变量的赋值动作,根据代码先后顺序执行
d, 非静态类型的字段在类加载完成并且给对象分配内存的时候初始化.
e,说完了字段静内部态类,它只有在调用的时候才会被加载,就像正常类一样被加载,静态内部类的常量字段,类变量,初始化与正常类的字段一样一
f.静态方法只有在调用它的时候才会执行
文章首发地址:https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554779&idx=1&sn=1d08bcc184c9f4224af14a02d11059c5&chksm=f3f8330dc48fba1b7f87d1fe8487e9d8753cf0e9b8a9df23d6a1ab6f7215fa8845913431abf1&token=2005887224&lang=zh_CN#rd
公众号: