一, 前言
关于这两个关键字, 应该是在开发工作中比较常见的, 使用频率上来说也比较高. 接口中, 常量, 静态方法等等. 但是, 使用频繁却不代表一定是能够清晰明白的了解, 能说出个子丑演卯来. 下面, 对这两个关键字的常见用法做点总结记录, 方便之后的回顾以及突击知识点.
二, 关键字 final
final, 一如字面意思 "最终的", 大体在 Java 中表示 "不可变的". 可用来修饰类, 方法, 方法参数以及变量.
1, 修饰类
final 在修饰类的时候, 代表的是此类不能被继承. 也就是说如果一个类确定不会被继承使用, 则可以设计成 final 类型的. 典型的例子就是 String 类.
2, 修饰方法
final 修饰的方法, 能被继承, 但是不能重写. 可以重载.
3, 修饰方法参数
final 在修饰方法参数的时候, 表示的是在执行方法的内部, 不能够去改变参数的值. 但是如果是引用对象, 是可以改变应用对象的属性值.
4, 修饰变量
final 在修饰变量, 代表的是不可变, 也即是常说的 "常量". final 在修饰的时候, 允许一次赋值, 之后在生命周期类, 不允许对其进行修改.
修饰变量存在两种情况: 基本类型的数据 和 对象数据. 在修饰基本类型数据的时候, 值是不可变的. 在修饰对象数据的是, 对象的引用是不可改变的, 但是, 可以修改对象内部的属性值.
final 修饰的变量必须在使用前进行初始化, 一种方式是在声明的时候就给出默认值. 还有一种就是通过构造方法去设置.
5, 代码示例
- package com.cfang;
- import java.util.Calendar;
- import lombok.Data;
- import lombok.extern.slf4j.Slf4j;
- @Slf4j
- public class T3 {
- public static void main(String[] args) {
- // -- 修饰变量
- final int b = 0;
- /**
- * 编译报错: The final local variable b cannot be assigned. It must be blank and not using a compound assignment
- */
- // b = 2;
- // -- 修饰方法参数
- DemoCls cls = new DemoCls();
- cls.setAge(10);
- log.info("democls age : {}", cls.getAge());
- int a = 10;
- sayHello(a, cls);
- log.info("after call sayHello, democls age : {}", cls.getAge());
- }
- private static void sayHello(final int a, final DemoCls cls) {
- /**
- * 编译报错: The final local variable a cannot be assigned. It must be blank and not using a compound assignment
- */
- // a = 11;
- // cls = new DemoCls();
- cls.setAge(11);
- }
- }
- @Data
- class DemoCls{
- private int age;
- }
- // -- 修饰方法
- @Slf4j
- class Person {
- public final void saySth() {
- log.info("i am person");
- }
- }
- @Slf4j
- class Male extends Person{
- /**
- * 编译出错 : Cannot override the final method from Person
- * 修饰的方法不能够被重写
- */
- // public final void saySth() {
- // log.info("i am male");
- // }
- public final void saySth(String msg) {
- log.info("i am male");
- }
- }
其中, 修饰方法参数, 如果是引用对象, 是可以改变对象的属性值, 这一点也很好理解: cls 是引用变量, final 修饰引用变量, 只是限定了此引用变量 cls 的引用不能改变, 而实际引用的对象的本身的值是可以进行修改的. 文字语言组织可能不是很清晰, 如下图所示, 一目了然的就知道说要表述的意思了.
三, 关键字 static
static, 静态的. 在 Java 中, static 通常可被用于修饰 变量, 方法以及代码块.
1, 修饰变量
static 修饰的变量, 叫做静态变量. static 变量被所有类对象共享, 在内存中仅一份, 随着类的初始化而被加载. 与之对应的非静态变量, 是属于每个实例对象本身, 内存中存在多份, 相互间不影响.
2, 修饰方法
static 修饰的方法, 叫做静态方法. 调用静态方法, 不依赖于实例对象就可以进行访问, 所以, 静态方法是没有 this 的. 由于此特性以及非静态方法依赖于实例对象调用, 所以静态方法中是不能够直接使用非静态的成员属性和方法. 与之相反的是, 非静态方法是可以直接访问使用静态成员变量和方法. 同样的, 静态方法也是没有 super 的. 可以一句话总结下: 由于 static 和具体的实例对象无关, 而 this,super 和具体的实例对象息息相关, 所以, static 和 this,super 势如水火, 一如白天与黑夜.
3, 修饰代码块
static 修饰代码块, 在类初始化加载的时候, 会按照 static 块的顺序进行加载, 并且, 生命周期内, 只加载一次. 基于此特性, 可以设计优化程序的性能, 一些只需要一次性初始化加载的内容, 就可以放在 static 块中进行.
4, 代码示例
- package com.cfang;
- import lombok.extern.slf4j.Slf4j;
- @Slf4j
- public class T4 {
- private static int a;
- private static int b;
- private static int c;
- static {
- log.info("init value a");
- a = 10;
- }
- {
- /**
- * 普通代码块, 依赖于实例对象
- */
- log.info("init value c");
- c = 12;
- }
- public static void main(String[] args) {
- // -- 静态方法调用非静态
- /**
- * 不可直接访问
- */
- // sayHello1();
- new T4().sayHello1();
- // -- 静态方法直接调用
- sayHello();
- // -- 静态代码块初始化资源
- log.info("value a : {}", a);
- log.info("value b : {}", b);
- log.info("value c : {}", c);
- }
- private static void sayHello() {
- log.info("say hello");
- }
- private void sayHello1() {
- log.info("say hello1");
- // -- 非静态方法直接调用
- sayHello();
- }
- static {
- log.info("init value b");
- b = 11;
- }
- }
5,static 常见误区
- package com.cfang;
- import lombok.Data;
- import lombok.extern.slf4j.Slf4j;
- @Slf4j
- public class T5 {
- private static int a = 10;
- public static void main(String[] args) {
- new T5().printVal();
- //-- The field DemoCls01.b is not visible
- DemoCls01.b;
- }
- private void printVal() {
- int a = 11;
- log.info("value a : {}", this.a);
- }
- }
- @Data
- class DemoCls01{
- private static int b;
- }
5.1,static 的 this
上述代码中, 最终运行 printVal 方法, 输出的结果是 :value a : 10 . 其实这也很好理解: this 指代的当前对象, 而通过 new T5().printVal() 调用的话, this 指代的就是当前新创建的实例对象, static 修饰的变量本身是被所有类对象所共享的, 而 printVal 方法中 a 属于局部变量, 跟 this 实例对象并没有关联. 所以, 输出的就是 10.
5.2,static 与 可见性
static 并不能改变变量或者方法的可见性. 上述代码中, main 方法中, DemoCls01.b 会编译报错.
5.3,static 与 局部变量
Java 规范中, 明确说明了 :static 关键字不可修饰局部变量.
四, 总结
final 和 static , 联合使用修饰属性表示一旦给值, 就不可修改, 并且可以通过类名访问; 修饰方法, 表示该方法不能重写, 可以在不 new 对象的情况下调用.
突然想到, 接口 interface 中, 成员变量的默认修饰符为 public static final, 方法的默认修饰符 public abstract .
来源: https://www.cnblogs.com/eric-fang/p/11593570.html