文章目录
- 一、final关键字
- (一)概念
- (二)修饰变量
- (三)修饰类
- (四)修饰方法
- 二、static关键字
- (一)static是什么
- (二)static修饰的成员变量是什么
- (三)static的优点
- (四)static的应用场景
- (五)static的作用
- (1)修饰成员变量
- (2)修饰成员方法
- (3)静态块
- (4)静态导包
- (5)总结
一、final关键字
(一)概念
定义
Java中,final表示最终,也可以称为完结器,表示对象是最终形态的、不可改变的。
final应用于类、方法和变量时意义是不同的,但本质是一样的,都表示不可改变。
(二)修饰变量
此处的变量包括:成员变量(属性)、局部变量(形参也属于局部变量)。
如果是基本数据类型变量,那么表示这个变量是不可变的,即表示一个常量,而且常量名通常大写。
如果是引用数据类型的变量,那么表示这个引用只能指向初始化时指向的那个对象,不能再指向别的对象。但被指向的这个对象当中的属性值并非不能修改。
final修饰的变量一定要有初始化的值,且只能初始化赋值一次,否则编译会报错。
(三)修饰类
表示这个类是最终类,是不可被继承的。
abstract
和final
是不能修饰同一个类的,因为当一个类被声明为abstract
后就是要用来被继承,这与final
是相互矛盾的。
(四)修饰方法
那么这个方法就不能被重写。
但不影响其他功能,仍可以被正常调用,也不影响重载。
同样地,final
与abstract
不能同时修饰一个方法,因为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;
,它没办法知道age
是stu1
的age
,因为它和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主要有四种用法:
- 用来修饰成员变量,将其变为类的成员,从而实现该类所有对象对于该成员的共享。
- 用来修饰成员方法,将其变为类方法,可以直接使用
类名.方法名
的方式调用,常用于工具类。 - 静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键。
- 静态导包用法,将另一个类的方法直接导入到当前类中,从而直接使用
方法名
即可调用类方法,更加方便。