JAVA static关键字与final关键字

一、final关键字

(一)概念

定义

  Java中,final表示最终,也可以称为完结器,表示对象是最终形态的、不可改变的。

  final应用于类、方法和变量时意义是不同的,但本质是一样的,都表示不可改变。

(二)修饰变量

  此处的变量包括:成员变量(属性)、局部变量(形参也属于局部变量)。

  如果是基本数据类型变量,那么表示这个变量是不可变的,即表示一个常量,而且常量名通常大写。

  如果是引用数据类型的变量,那么表示这个引用只能指向初始化时指向的那个对象,不能再指向别的对象。但被指向的这个对象当中的属性值并非不能修改。

  final修饰的变量一定要有初始化的值,且只能初始化赋值一次,否则编译会报错。

(三)修饰类

  表示这个类是最终类,是不可被继承的。

  abstractfinal是不能修饰同一个类的,因为当一个类被声明为abstract后就是要用来被继承,这与final是相互矛盾的。

(四)修饰方法

  那么这个方法就不能被重写。

  但不影响其他功能,仍可以被正常调用,也不影响重载。

  同样地,finalabstract不能同时修饰一个方法,因为abstract修饰方法的目的就是让子类去重写,而final的作用是不允许该方法被子类重写,这是互相矛盾的。

二、static关键字

(一)static是什么

  • static是静态的意思,可以用来修饰成员变量、成员方法(不能修饰别的)。
  • static修饰成员变量之后称为静态成员变量(类变量),修饰方法之后称为静态方法(类方法)。
  • static修饰后的成员变量,可以被类的所有对象共享(访问、修改)。

(二)static修饰的成员变量是什么

  静态成员变量(有static修饰;属于类、加载一次;内存中只有一份),访问格式:

  • 类名.静态成员变量——推荐
  • 对象.静态成员变量——不推荐

  实例成员变量(无static修饰;属于对象),访问格式:

  • 对象.实例成员变量

(三)static的优点

  • 属于类级别,不需要创建对象就可以使用。
  • 全局唯一,内存中唯一,静态变量可以唯一标识某些状态。
  • 在类加载的时候初始化,常驻在内存中,调用快捷方便。

(四)static的应用场景

  • 使用频繁的方法可以定义为静态方法,提升系统性能,比如工具类(文件操作、日期操作、ftp工具类、加密解密工具等)。
    • 说明:
      • 如果不是static方法,那么每次创建对象都会在内存中为类的每一个部分分配空间,很浪费内存空间。
      • 引入static就很好地解决了内存的问题。虽然static修饰的方法会一直在内存中、直到程序结束,但是static方法所占用的内存要远小于频繁的非静态方法所消耗的内存。
  • static适合全局变量的定义。

(五)static的作用

(1)修饰成员变量

  根据某个类(如Person)构造出的每一个对象都是独立存在的,在堆中有各自独立的空间,保存有自己独立的成员变量,相互不会影响。

  static关键字可以修饰成员变量,让它变成类的所属,而不再是对象的所属。比如我们将Person类中的age属性用static进行修饰,那么age属性会统一交给Person类去处理,而多个Person的实例化对象只会对应同一个age属性。此时,age属性已经不再与对象绑定、处于堆空间中了,而是在另一片区域——静态存储区当中。

(2)修饰成员方法

  成员方法本来就是存放在类当中的,即静态存储区当中,而不是各个对象的堆空间中。static修饰成员方法最大的作用在于,可以使用类名.方法名的方式操作方法,避免了要先new出对象的繁琐和资源消耗。(相当于定义了一个全局的函数)

  不过在使用时也需要注意,一个static修饰的方法中,只能使用static修饰的成员变量和方法,不能使用非static修饰的成员变量和方法。一方面,初始化的优先级从高到低为静态属性、静态方法、普通属性、构造函数...,当静态方法执行时,非static修饰的成员变量和方法还没有初始化,因此无法使用;另一方面,static修饰的方法是属于类的,和某个具体对象并不处于同一个空间里,如果直接去使用对象的成员变量,它是不知道这是哪一个对象的(比如age = 18;,它没办法知道agestu1age,因为它和stu1并不位于同一个空间中——它在静态存储区中,stu1在堆区中;同理,即使使用this关键字也不行)。

(3)静态块

  首先,梳理一下一个对象的初始化过程。以下面的代码为例:

class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}

public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");
    
    public static void main(String[] args) {
        Person p1 = new Person("p1初始化");
    }
}

/**输出结果如下:
 * static成员book2成员变量初始化
 * static成员book4成员变量初始化
 * book1成员变量初始化
 * book3成员变量初始化
 * p1初始化
 */

  上例中,Person类中组合了四个Book成员变量,其中两个是普通成员变量,两个是static修饰的类成员(静态成员变量)。可见,当我们new一个Person对象时,static修饰的成员变量首先被初始化,随后是普通成员,最后调用Person类的构造方法完成初始化。

  也就是说,在创建对象时,static修饰的成员会首先被初始化。而且我们可以看到,对于多个static修饰的成员,按照它们的先后位置进行初始化。

  实际上,static修饰的成员的初始化可以更早的进行,如下面的例子:

class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}

public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2 = new Book("static成员book2成员变量初始化");
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4 = new Book("static成员book4成员变量初始化");
    
    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }
    
    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("**********");
        Person p1 = new Person("p1初始化");
    }
}

/**输出结果如下:
 * static成员book2成员变量初始化
 * static成员book4成员变量初始化
 * static修饰的funStatic方法
 * **********
 * book1成员变量初始化
 * book3成员变量初始化
 * p1初始化
 */

  从上面的例子中,我们可以发现两个现象。

  第一,当我们没有创建对象,而是通过类去调用类方法时,尽管该方法没有调用任何的类成员,但类成员还是在方法调用之前就初始化了,这说明,当我们第一次去使用一个类时,就会触发该类的成员初始化。

  第二,当我们使用了类方法,完成类的成员初始化后,再new该类对象时,static修饰的类成员没有进行再次的初始化,这说明,static修饰的类成员,在程序运行过程中,只需进行一次初始化,不会进行多次初始化。


  至此,我们理解“静态块”就非常简单了。当我们初始化static修饰的成员时,可以将它们统一放在一个以static开始,用花括号包裹起来的块中:

class Book {
    public Book(String msg) {
        System.out.println(msg);
    }
}

public class Person {
    Book book1 = new Book("book1成员变量初始化");
    static Book book2;
    
    static {
        book2 = new Book("static成员book2成员变量初始化");
        book4 = new Book("static成员book4成员变量初始化");
    }
    
    public Person(String msg) {
        System.out.println(msg);
    }
    
    Book book3 = new Book("book3成员变量初始化");
    static Book book4;
    
    public static void funStatic() {
        System.out.println("static修饰的funStatic方法");
    }
    
    public static void main(String[] args) {
        Person.funStatic();
        System.out.println("**********");
        Person p1 = new Person("p1初始化");
    }
}

/**输出结果如下:
 * static成员book2成员变量初始化
 * static成员book4成员变量初始化
 * static修饰的funStatic方法
 * **********
 * book1成员变量初始化
 * book3成员变量初始化
 * p1初始化
 */
(4)静态导包

  先看例子:

/* PrintHelper.java 文件 */
package com.wyl.study;

public class PrintHelper {
    public static void myprint(Object o) {
        System.out.println(o);
    }
}

/* App.java 文件 */
import static com.wyl.study.PrintHelper.*;

public class App {
    public static void main(String[] args) {
        myprint("Hello World!");
    }
}

/**输出结果:
 * Hello World!
 */

  上面的代码分别来自两个不同的.java文件,其中的PrintHelper包含了一个用于打印的static方法。而在App.java文件中,我们首先将PrintHelper类导入,在这里导入时,我们使用了static关键字,而且在引入类的最后还加上了.*,它的作用就是将PrintHelper类中的所有类方法直接导入。

  不同于非static导入,采用static导入包后,在不与当前类的方法名冲突的情况下,无需使用类名.方法名的方法去调用类方法,而是直接可以采用方法名去调用类方法。(注:如果与当前类的方法名冲突,则仍需要使用类名.方法名的方式来加以区分)

(5)总结

  static主要有四种用法:

  • 用来修饰成员变量,将其变为类的成员,从而实现该类所有对象对于该成员的共享。
  • 用来修饰成员方法,将其变为类方法,可以直接使用类名.方法名的方式调用,常用于工具类。
  • 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键。
  • 静态导包用法,将另一个类的方法直接导入到当前类中,从而直接使用方法名即可调用类方法,更加方便。
  • 28
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋秋秋叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值