【Java基础篇】static关键字
Java中的static关键字表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以修饰代码块。
当JVM加载一个类的时候,如果该类存在static修饰的成员变量和成员方法,就会为这些成员变量和成员方法在固定的位置开辟一个固定大小的内存区域;同时被static修饰的成员变量和成员方法是被该类的所有实例共享的,不依赖于某个特定的实例变量,任何一个实例对其的修改都会导致其他实例的变化。
静态类与非静态类
静态类
- 无法实例化,不能包含实例构造函数,但是可以有静态构造函数
- 仅包含静态变量和静态方法。
非静态类
- 每次实例化都是一个新的对象,并且可以不显式实例化,因为内部有一个默认的静态构造函数(不可重载),当创建类实例或引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次;
- 可以包含非静态成员,也可以包含静态成员;
- 可以通过 “类名.方法名 或者 变量名” 访问静态成员;
静态方法与非静态方法
- 被static修饰的方法就是静态方法,即类方法;没有被static修饰的方法就是非静态方法,即实例方法。
- 因为静态方法不属于类的实例成员,所以静态方法只能被重载,而不能被重写,在静态方法中也不能使用 this 或 super。
- 静态方法会在类加载的时候被分配和加载入内存中;非静态方法属于对象的具体实例,只有在类对象创建时,在对象的内存中才有这个方法的代码块。
- 静态方法中不能调用非静态方法和非静态变量;非静态方法可以调用静态方法,也可以调用非静态方法。
- 静态方法可以由实例对象调用,也可以由类名直接调用,即“类名.方法名 或者 对象名.方法
静态变量与非静态变量的区别
- 内存分配时间:静态变量在应用程序初始化时,即类加载的时候分配内存,直到它所在类的应用程序运行结束时才会消亡;非静态变量需要被实例化后才会分配内存。
- 生存周期:静态变量的生存周期为应用程序的存在周期;非静态变量的存在周期取决于实例化的类的存在周期。
- 共享方式:静态变量是全局变量,他在内存中仅有一个,被类的所有实例对象共享,即一个实例改变了静态变量的值,其他同类的实例读取到的就是变化后的值;非静态变量是局部变量,不共享,每次实例化都是一个新的变量。
- 调用方式:静态变量只能通过“类名.变量名”调用;非静态变量当该变量所在的类被实例化之后,可以通过实例化后的类名直接访问。
- 访问方式:静态成员不能访问非静态成员;非静态成员可以访问静态成员。
静态代码块与非静态代码块
- 执行时机:静态代码块是对类进行初始化,在JVM加载类的时候就会执行,而且只执行一次,如果一个类包含多个静态代码块,那就是按照代码顺序执行。非静态代码块是对类对象的初始化,在每次创建对象的时候它都会执行。
- 都是在JVM加载类的时候且在构造方法之前执行,在类中都可以定义多个,一般在代码块中对一些static变量进行赋值。
- 静态代码块在非静态代码块之前执行,非静态代码块可以在普通方法中定义(不过作用不大),而静态代码块不行。
静态构造函数与非静态构造函数
- 静态构造函数可以用于静态类,也可用于非静态类,静态构造函数不可以继承;
- 静态构造函数不可被直接调用,当创建类实例或者引用任何静态成员之前,静态构造函数被自动执行,并且只执行一次。
- 静态构造函数无访问修饰符、无参数,只有一个static标志。
相关问题
父类及子类的静态代码块、构造代码块、构造函数、静态成员变量、普通成员变量的执行顺序?
(1)执行父类的静态代码块,并初始化父类静态成员变量;
(2)执行子类的静态代码块,并初始化子类静态成员变量;
(3)执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量;
(4)执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量;
构造代码块与构造函数的区别?
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。
全局变量,即静态变量的优缺点
(1)优点:可以减少变量的个数,减少由于实际参数和形式参数的传递带来的时间消耗。
(2)缺点:
① 会占用更多的内存空间。全局变量保存在静态存储区,程序开始运行时为其分配内存,程序结束时释放内存。与局部变量的动态分配、动态释放相比,他的生命周期比较长,因此过多的全局变量会占用较多的内存单元。
② 全局变量会破坏函数的封装性。如果使用了全局变量。那么函数体内的语句就可以绕过函数参数和返回值进行存取,这种情况破坏了函数的独立性,是函数对全局变量产生依赖性,也会降低函数的可移植性。
③ 使得函数代码的可读性差。由于多个函数都可能使用全局变量,函数执行时全局变量的值可能随时发生变化,对于程序的查错和调试都非常不利。
④ 会使递归更加难以正确完成,因为递归在程序调用自身时才发生。
⑤ 在多线程的情况下,需要同步各个线程对于全局对象的读写操作。
参考: |