Java static关键字相关(必看)

该篇文章为对java中static关键字的解读思考和梳理,很幸运见证了你今日的努力

1.简介

在Java中,static关键字是一个非常重要的关键字,它用于定义类的静态成员。静态成员属于类本身,而不是类的任何特定实例。这意味着静态成员在类加载到JVM时被创建,并且在类的所有实例之间共享。静态成员包括静态变量(也称为类变量)、静态方法、静态初始化块和静态内部类

2.静态变量

1.从属于类,静态变量(包括静态方法中的局部变量)在类首次加载到JVM时初始化,且只初始化一次,与类的声明周期相同,在整个应用程序执行期间都有效。与之比较,普通变量从属于对象,一般在对象创建的时候初始化。

2.所有实例共享同一个静态变量的值。

3.可通过类名直接访问,无法通过this关键字调用。

4.static成员变量初始化顺序按照定义的顺序来进行初始化.

3.静态方法

1.从属于类的方法,不属于类的任何特定实例,通过类名直接调用。

2.静态方法不能调用非静态成员,编译会报错。因为在类加载时,只处理与类相关的初始化,编辑器无法获取到非成员变量,非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。因此,静态方法无法通过this关键字调用。

3.静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。先编译,后运行,才可对象操作。

4.静态方法可以访问静态变量和调用其他静态方法。

4.静态代码块

1.用于类的初始化,初始化静态变量(构造方法用于对象的初始化)。

2.在类加载时执行一次,且在静态变量初始化之前执行

3.静态初始化块可以有多个,它们按照在类中出现的顺序执行。

4.很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

5.需要注意的点

(1)静态方法可以被继承吗?

1.子类可以继承父类的静态方法,但不能覆盖它们。

如果子类定义了 一个与父类同名的静态方法,这将创建一个新的静态方法,而不是覆盖父类的方法。 因此,通常避免在子类中隐藏父类的静态方法,如需要改变父类静态代码的行为,考虑使用不同方法名或其他设计模式。

2.当一个类继承另一个类时,它会继承父类的所有非私有成员,包括静态方法,这意味着子类可以访问和使用继承来的静态方法,就像它们自己类的一部分一样,但是子类不能覆盖(override)父类的静态方法,因为override用于对象实例的继承。

(2)静态方法会有多线程问题吗?

1.静态方法本身不会直接导致多线程问题。
2.使用static关键字,需要考虑线程安全问题,因为静态成员是共享的,多个线程可能同时访问和修改它们。在多线程环境下,可能需要使用同步机制来确保线程安全。此外,过渡使用静态成员可能导致类的耦合度增加,降低代码的可测试型和可维护性,因此需要根据实际需求合理使用static关键字。

3.可以使用volatile、synchronized、final关键字,lock,ThreadLocal避免线程安全问题,选择使用哪种取决于具体的应用场景和需求。

(3)静态方法除了类加载的时候初始化,之后还会执行吗?

静态方法本身不会在类加载后自动执行,除非它们被显式调用

(4)static能作用于局部变量吗?

static是不允许用来修饰局部变量。

6.面试题

1、下面这段代码的输出结果是什么?
public class Test extends Base{

    static{
        System.out.println("test static");
    }

    public Test(){
        System.out.println("test constructor");
    }

    public static void main(String[] args) {
        new Test();
    }
}

class Base{

    static{
        System.out.println("base static");
    }

    public Base(){
        System.out.println("base constructor");
    }
}

输出结果:

base static
test static
base constructor
test constructor

1.找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类

2.加载Test类的时候,发现Test类继承Base类,于是先去加载Base类

3.加载Base类的时候,发现Base类有static块,而是先执行static块,输出base static结果

4.Base类加载完成后,再去加载Test类,发现Test类也有static块,而是执行Test类中的static块,输出test static结果

5.Base类和Test类加载完成后,然后执行main方法中的new Test(),调用子类构造器之前会先调用父类构造器

6.调用父类构造器,输出base constructor结果

7.然后再调用子类构造器,输出test constructor结果

2、这段代码的输出结果是什么?
public class Test {
    Person person = new Person("Test");
    static{
        System.out.println("test static");
    }

    public Test() {
        System.out.println("test constructor");
    }

    public static void main(String[] args) {
        new MyClass();
    }
}

class Person{
    static{
        System.out.println("person static");
    }
    public Person(String str) {
        System.out.println("person "+str);
    }
}


class MyClass extends Test {
    Person person = new Person("MyClass");
    static{
        System.out.println("myclass static");
    }

    public MyClass() {
        System.out.println("myclass constructor");
    }
}

输出结果:

test static
myclass static
person static
person Test
test constructor
person MyClass
myclass constructor

(1)找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类

(2)加载Test类的时候,发现Test类有static块,而是先执行static块,输出test static结果

(3)然后执行new MyClass(),执行此代码之前,先加载MyClass类,发现MyClass类继承Test类,而是要先加载Test类,Test类之前已加载

(4)加载MyClass类,发现MyClass类有static块,而是先执行static块,输出myclass static结果

(5)然后调用MyClass类的构造器生成对象,在生成对象前,需要先初始化父类Test的成员变量,而是执行Person person = new Person(“Test”)代码,发现Person类没有加载

(6)加载Person类,发现Person类有static块,而是先执行static块,输出person static结果

(7)接着执行Person构造器,输出person Test结果

(8)然后调用父类Test构造器,输出test constructor结果,这样就完成了父类Test的初始化了

(9)再初始化MyClass类成员变量,执行Person构造器,输出person MyClass结果

(10)最后调用MyClass类构造器,输出myclass constructor结果,这样就完成了MyClass类的初始化了

3、这段代码的输出结果是什么?
public class Test {

    static{
        System.out.println("test static 1");
    }
    public static void main(String[] args) {

    }

    static{
        System.out.println("test static 2");
    }
}

输出结果:

test static 1
test static 2

虽然在main方法中没有任何语句,但是还是会输出,原因是输出语句是类加载执行的。另外,static块可以出现类中的任何地方(只要不是方法内部,记住,任何方法内部都不行),并且执行是按照static块的顺序执行的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值