在平时编码时,咱们可能只注意到static、final、volatile等关键字的使用,而忽略了它们更深层次的意义。html
当static修饰类的属性或者方法时,那么就能够在没有建立对象的状况下使用该属性或方法。java
静态块也是static的一个应用,用于初始化类时的一些操做。react
被static修饰后的属性或者方法,使用时不须要new一个类,用类.属性名或方法名访问。dom
好比java.lang.Math就存放了不少静态资源,能够直接使用Math.random()来获取随机数。工具
注:this
一、非静态方法是能够直接访问静态资源的,静态方法是不能直接引用非静态资源的。编码
二、.静态属性和方法能够经过类.属性名或方法名,并且,该类的对象也是访问静态属性和变量的。spa
三、Java的语法规定,static不能修饰局部变量。没有为何,这就是规定。线程
示例:code
编译时就会报错,main方法是静态方法,变量i是非静态的。解决方案:一是将变量i加上static修饰,二是如上采用实例访问非静态属性。
为何非静态方法能够访问静态资源,而静态方法不能访问非静态资源呢?
从类加载机制上讲,静态资源是类初始化的时候加载的,而后非静态资源是new一个该类的对象的时候加载的。
这就带来一个疑问:
加载类时默认先加载静态资源的,当new一个对象以后,才会加载其余资源,因此在new对象以前,静态资源是不知道类有哪些非静态资源的,
可是当对象new出来以后,该类的全部属性和方法都知道。
静态块也是在类加载的时候执行,并且只执行一次。
静态块注意要点:
示例:
package main.keyword; public class StaticDemoTest2 extends ParentClass{ public static String subStaticField = "子类静态变量"; public String subField = "子类非静态变量"; public static StaticClass staticClass = new StaticClass("子类"); static { System.out.println("子类 静态块初始化"); } { System.out.println("子类 [非]静态块初始化"); } public StaticDemoTest2(){ System.out.println("子类构造器初始化"); } public static void main(String[] args) { new StaticDemoTest2(); } } class ParentClass{ public static String parentStaticField = "父类静态变量"; public String parentField = "父类[非]惊天变量"; public static StaticClass staticClass = new StaticClass("父类"); static { System.out.println("父类 静态块初始化"); } { System.out.println("父类 [非]静态块初始化"); } public ParentClass(){ System.out.println("父类 构造器初始化"); } } class StaticClass{ public StaticClass(String name){ System.out.println(name+" 静态变量加载"); } }
执行结果:
父类 静态变量加载 父类 静态块初始化
子类 静态变量加载
子类 静态块初始化
父类 [非]静态块初始化 父类 构造器初始化
子类 [非]静态块初始化
子类构造器初始化
下面是总结类加载流程,能够对照着这个流程,能够再从新看一下上面的例子,会有新的理解。
1. 加载父类静态
1.1 为静态属性分配存储空间并赋初始值
1.2 执行静态初始化块和静态初始化语句(从上至下)
2. 加载子类静态
2.1 为静态属性分配存储空间
2.2 执行静态初始化块和静态初始化语句(从上至下)
3. 加载父类非静态
3.1 为非静态块分配空间
3.2 执行非静态块
4. 加载子类非静态
4.1 为非静态块分配空间
4.2 执行非静态块
5. 加载父类构造器
5.1 为实例属性分配存数空间并赋初始值
5.2 执行实例初始化块和实例初始化语句
5.3 执行构造器内容
6. 加载子类构造器
6.1 为实例属性分配存数空间并赋初始值
6.2 执行实例初始化块和实例初始化语句
6.3 执行构造器内容
示例2:
package main.keyword; public class StaticDemoTest3 { Person person = new Person("TestStaticLoad");//4 static{ System.out.println("TestStaticLoad static");//1 } public StaticDemoTest3() { System.out.println("TestStaticLoad constructor");//5 } public static void main(String[] args) { new God(); } } class Person{ static{ System.out.println("person static");//3 } public Person(String str) { System.out.println("person "+str);//4 -TestStaticLoad 6-God } } class God extends StaticDemoTest3 { Person person = new Person("God"); //6 static{ System.out.println("God static");//2 } public God() { System.out.println("God constructor");//7 } }
执行结果:
TestStaticLoad static God static person static person TestStaticLoad TestStaticLoad constructor person God God constructor
一步一步地解析:
static关键字总结:
总结:
示例:
public class TestFinal { public static void main(String args[]){ String a = "test1"; final String b = "test"; String d = "test"; String c = b + 1; String e = d + 1; System.out.println((a == c)); System.out.println((a.equals(e))); } }
执行结果:
true true
由于final变量是基本类型以及String时,在编译期的时候就把它当作常量来使用,不须要在运行时候使用。“==”是对比两个对象基于内存引用,若是两个对象的引用彻底相同,则返回true,因此这里b是用访问常量的方式去访问,d是连接的方式,因此a的内存引用和c的内存引用是相等的,因此结果为true,a和e两个对象的值是相等的,因此结果为true。
示例:
在编译的时候,或报错, 不能指向一个final对象。
具体定义请看这里
应用
线程池(ThreadPoolExecutor)中一些变量的定义:
private volatile ThreadFactory threadFactory; private volatile RejectedExecutionHandler handler; private volatile long keepAliveTime; private volatile boolean allowCoreThreadTimeOut; private volatile int corePoolSize; private volatile int maximumPoolSize;
能够看到线程工厂threadFactory,拒绝策略handler,没有任务时的活跃时间keepAliveTime,keepAliveTime的开关allowCoreThreadTimeOut,核心池大小corePoolSize,最大线程数maximumPoolSize
都是被volatile修饰中,由于在线程池中有若干个线程,这些变量必需保持对线程可见性,否则会引发线程池运行不正确。
assert断言
在目前的java编码中,是不推荐使用的,这里只是稍微了解一下:
使用方式:
public class AssertDemoTest { public static void main(String[] args) { assert true; System.out.println("断言1成功执行"); System.out.println("-----------"); assert false:"error"; System.out.println("断言2成功执行"); } }
执行结果:
断言1成功执行 ----------- 断言2成功执行
关于锁关键字,有如下几个总结:
Java中,一个类想要序列化,能够经过实现Serilizable接口的方式来实现,实现该接口以后,该类全部属性和方法都会自动序列化。
可是若是属性或方法被transient修饰,那么将不会被序列化。