3.static关键字
3.1.概述
- 关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。
- 也就是说,既然属于类,就可以不靠创建对象来调用了。
3.2.定义及其使用
- 类变量:
- 使用 static关键字修饰的成员变量。
- 当 static 修饰成员变量时,该变量称为类变量。
- 该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
- 静态方法:
- 使用 static关键字修饰的成员方法,习惯称为静态方法。
- 当 static 修饰成员方法时,该方法称为类方法 。
- 静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。调用方式非常简单。
静态方法调用的注意事项:
- 静态方法可以直接访问类变量和静态方法。
- 静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
- 静态方法中,不能使用this关键字
- 静态方法只能访问静态成员。
- Example
public class Student_static {
public static void main(String[] args) {
// 访问类变量
System.out.println(Student.numberOfStudent);
// 调用静态方法
Student.showNum();
}
}
3.3.静态代码块
- 定义在成员位置,使用static修饰的代码块{ }。
- 位置:类中方法外。
- 执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
- 作用:给类变量进行初始化赋值,调用方法等。
static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。
Example:
public class Game {
public static int number;
public static ArrayList<String> list;
static {
// 给类变量赋值
number = 2;
list = new ArrayList<String>();
// 添加元素到集合中
list.add("张三");
list.add("李四");
}
}
3.4.代码块
3.4.1.普通代码块
- 在Java中,使用大括号”{}“括起来的代码被称为代码块,根据其位置和声明方式的不同,可以分为:
- 局部代码块:对于变量的作用域有明确的限制,强制变量成为局部变量,但实际意义不大,严重影响代码可读性,却对内存回收帮助极小
- 构造代码块:创建对象时,对成员变量进行默认初始化赋值,优先于构造方法运行。
- 静态代码块:在类加载时,对于静态成员变量进行赋值(且只能给静态成员变量赋值),与静态成员变量显式初始化语句执行优先级相同,谁在上边谁先执行。
- 同步代码块(多线程使用)
- 代码块在实际开发中,使用频率并不高,可替代性也很强,但是由于其迷惑性极强,常年出没于各种面试题中。
3.4.2.构造代码块与静态代码块
public class ExerciseBlock {
static {
System.out.println("静态代码块!");
}
{
System.out.println("构造代码块!");
}
public static void main(String[] args) {
System.out.println("main方法开始执行!");
Star s = new Star();
System.out.println(Star.name);
}
}
class Star{
static {
name = "杨幂";
System.out.println("我喜欢杨幂");
}
static String name = "赵四"; //静态成员变量
{
name = "杨超越";
System.out.println("我喜欢杨超越");
}
public Star() {
name = "123";
System.out.println("Star:构造器!");
}
}
//运行结果:
静态代码块!
main方法开始执行!
我喜欢杨幂
我喜欢杨超越
Star:构造器!
123
- 解释:首先,main方法所在ExerciseBlock类要加载进入方法区,静态代码块随着ExerciseBlock类加载而执行,输出
静态代码块!
语句,继而,main方法栈帧进入栈中,main方法开始执行,输出main方法开始执行!
语句,Star类的class文件加载进入方法区,Star的静态变量String name 随之加载进入方法区,并且初始值为null
,因为静态代码块在静态变量赋值语句的上边,所以静态代码块先执行,给name赋值为杨幂
,并输出我喜欢杨幂
,随后,静态变量的赋值语句执行,修改name为赵四
,接着,在堆上创建Star类的s对象,构造代码块开始执行,将name赋值为杨超越
,输出我喜欢杨超越
,最后,构造方法开始执行,将name最后修改为123
,并输出Star:构造器!
,因为主方法的输出语句在最后,所以最后输出123
,至此执行完毕。
注:
在程序运行中,静态代码块与静态变量的初始化赋值语句,虽然在真正的程序编写中,很少会给静态变量直接赋初始值,但在面试中会偶尔考察,如上述代码中,静态代码块和静态变量显式初始化语句的优先级是一样的,都是随着类加载进方法区而执行,所以按代码的书写顺序执行,谁在上边谁先执行,而构造代码块和构造方法,都是创建了对象以后才开始执行,且构造方法永远最后执行。
个人理解,构造方法之所以永远最后执行,是为了程序员进行初始化赋值的时候,保证所赋值的内容永远是想要的内容。
另:静态的内容,只会随着类加载而执行,因此,类只需加载一次,所以静态的内容也只会执行一次。
- 额外补充:
- 对于默认初始化赋值,创建对象的时候第一件事情就是默认初始化,这时候成员变量只会赋默认初值,而调用构造方法的时候,不管是隐式的super还是显式的this,都会追溯到父类,在父类调用自己的构造方法之前,成员变量初始化(比如类中定义了int age = 10)这时候就会开始,父类调用完毕后,子类调用自己的构造方法,同理,子类调用之前,也会给自己的成员变量赋值。
class Father {
int i = 10;
public Father() {
System.out.println(getI());
}
public int getI() {
return i;
}
}
class Son extends Father {
int i = 100;
public Son(int i) {
this.i = i;
}
public int getI() {
return i;
}
}
//对于以上代码,在主方法中,执行Father father = new Son(1000),输出结果是0;
//父类方法被子类覆盖,即使在父类中调用,调用到的也是子类方法,而此时父类对象还在初始化过程中,子类对象更是还未开始初始化,其成员变量的值还是默认值。
public class MyStack {
int INIT_CAPACITY = 10;// 默认初始容量
public MyStack(){
// 如果使用者不传数组的初始长度, 默认是10
// this.arr = new Object[INIT_CAPACITY];
// this(INIT_CAPACITY); //报错Cannot reference 'MyStack.INIT_CAPACITY' before supertype constructor has been called
}
public MyStack(int capacity){
// 参数检查
if (capacity <= 0 || capacity > MAX_CAPACITY){
throw new IllegalArgumentException("capacity is Illegal");
}
arr = new Object[capacity];
}
}
//对于以上代码,虽然此时MyStack的成员变量,已经有了默认初始值,但是,父类的构造方法,即Object的构造方法,还没有被调用,此时卡在了this中,
//子类变量依赖于父类存在,JVM不允许这样做,所以直接报错,不能在super()被调用之前使用自己的成员变量。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/181097.html