参考致谢:
[static关键字解析-例子] https://www.cnblogs.com/dolphin0520/p/3799052.html
[static补充] https://www.cnblogs.com/dolphin0520/p/10651845.html
[static\final区别] https://blog.csdn.net/qq1623267754/article/details/36190715
[final内存语义-重排序] https://blog.csdn.net/weixin_42546729/article/details/113867060?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.base【final深入文章】https://juejin.im/entry/58c4811161ff4b005d94fed2#comment
1 static关键字解析
1.1 static的用途
1、static方法–静态方法
静态方法是为了方便在不创建对象的情况下调用。比如jdk的Collections类中的一些方法、单例模式的getInstance方法、工厂模式的create/build方法、util工具类中的方法。
(1) 对于静态方法是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了
(2) 在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
2、static变量–静态变量
把一个变量声明为静态变量通常基于以下三个目的:
- 作为共享变量使用
- 减少对象的创建
- 保留唯一副本—单例模式
3、static 代码块
静态代码块通常来说是为了对静态变量进行一些初始化操作,比如单例模式、定义枚举类:
为什么说static块可以用来优化程序性能?
因为它的特性:只会在类加载的时候执行一次
—>因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
4、静态内部类
使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。称为静态内部类(也可称为类内部类)。
- 除了写在一个类的内部以外,static内部类具备所有外部类的特性(内部类中的顶级类)。
- static内部类,不能访问外部类的实例成员,只能访问外部类的类成员。
静态内部类和非静态内部类区别:
非静态内部类对象持有外部类对象的引用,而静态内部类对象不会持有外部类对象的引用;
注意:
1.非静态内部类中不允许定义静态成员;
2.外部类的静态成员不可以直接使用非静态内部类;
5、静态导入
静态导入其实就是import static,用来导入某个类或者某个包中的静态方法或者静态变量。
注意:static和“this、super”势不两立,static跟具体对象无关,而this、super正好跟具体对象有关。
1.2 static关键字的误区
1.static关键字会改变类中成员的访问权限吗?否
Java中的static关键字不会影响到变量或者方法的作用域。在Java中能够影响到访问权限的只有private、public、protected(包括包访问权限)这几个关键字。
2.能通过this访问静态成员变量吗?可以
静态成员变量虽然独立于对象,但可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。
3.static能作用于局部变量么?不可以
在Java中切记:static是不允许用来修饰局部变量。这是Java语法的规定。
常见的笔试面试题—static执行顺序
public class Test extends Base{
//2.加载发现继承Base类 4.加载Test,执行静态代码块
static{
System.out.println("test static");
}
//7.Test构造方法执行
public Test(){
System.out.println("test constructor");
}
//1.执行开始找main,加载Test类
//5.都加载完之后,main方法执行new Test()的时候会先调用父类的构造器Base
public static void main(String[] args) {
new Test();
}
}
class Base{
//3.Base类加载,执行静态代码块
static{
System.out.println("base static");
}
//6.Base构造方法执行
public Base(){
System.out.println("base constructor");
}
}
//提问:输出结果
base static
test static
base constructor
test constructor
1.3 static变量和普通成员变量区别
- 所属不同。static变量属于类,不单属于任何对象;普通成员变量属于某个对象
- 存储区域不同。static变量位于方法区;普通成员变量位于堆区。
- 生命周期不同。static变量生命周期与类的生命周期相同;普通成员变量和其所属的对象的生命周期相同。
- 不可对象序列化。在对象序列化时(Serializable),static变量会被排除在外(因为static变量是属于类的,不属于对象)
1.4 类的构造器到底是不是static方法?【深挖】
关于类的构造器是否是static方法有很多争议,在《java编程思想》一书中提到“类的构造器虽然没有用static修饰,但是实际上是static方法”,个人认为这种说法有点欠妥,原因如下:
1)是否与对象关联
- 构造器负责在创建一个实例对象的时候,对实例进行初始化操作,实际上构造器有一个隐藏的参数this引用,this是跟已经创建的对象绑定的。(即jvm在堆上为实例对象分配了相应的存储空间后,需要调用构造器对实例对象的成员变量进行初始化赋值操作)
- static方法,由于static不依赖于任何对象就可以进行访问,也就是说和this是没有任何关联的。
故从有没有this引用去讲,类的构造器不是static方法。
2)JVM指令不同
- 类的构造器时实执行的是invokespecial指令,他是用来调用实例方法,用来特殊调用父类方法、private方法和类的构造器。
- static方法是调用的是invokestatic指令,就是用来调用执行static方法。
2 final
final表示不可改变,可以修饰:属性,方法,类,局部变量(方法中的变量)
2.1 final的使用
1、final修饰的属性:
- final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变。
- final修饰的属性跟具体对象有关,在运行期初始化的final属性,不同对象可以有不同的值。
- final修饰的属性表明是一个常数(创建后不能被修改)。
2、 final修饰的方法:
表示该方法在子类中不能被重写,
3、final修饰的类:
表示该类不能被继承。
4、final修饰的基本类型数据:
-final会将值变为一个常数(创建后不能被修改);
5、final修饰的引用(对象句柄/引用/指针)
final会将句柄变为一个常数,固定到一个具体对象,不再只想其他对象,但对象值可变。(这一限制也适用于数组,数组也属于对象,数组本身也是可以修改的)
6、final修饰的方法参数中的final句柄:
意味着在该方法内部,我们不能改变参数句柄指向的实际东西,也就是说在方法内部不能给形参句柄再另外赋值。
2.2 final的内存语义
对于final域,编译器和处理器要遵循两个重排序规则:
1.写final重排约束:在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
2.读final重拍约束:初次读一个包含final域的对象的应用,与随后初次读这个final域,这两个操作之间不能重排序。
3 static final
1、static final 修饰的属性:
强调它们只有一个,final修饰的属性表明是一个常数(创建后不能被修改)。static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问。
2、static final修饰的方法:
表示该方法不能重写,可以在不new对象的情况下调用
4 static\final\static final区别
- static修饰的属性强调它们只有一个;
- final修饰的属性表明是一个常数,不可改变(创建后不能被修改)。
- static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问。
static final也可以修饰方法,表示该方法不能重写,可以在不new对象的情况下调用。
static final和final static没什么区别,一般static写在前面。
private/public static final 和static final区别只是访问权限不同