基础 | 关键字static

考虑篇幅问题,Java基础系列的「关键字static与final」部分将分两次进行梳理,本次主要总结「static关键字」。

面试时也经常被问到「static关键字」的相关问题,如 「谈谈对static关键字的理解?static有哪几种用法?静态内部类与普通内部类有什么区别?Java中变量的初始化顺序是怎样的?」,建议重点关注。


谈谈对关键字static的理解?

关键字static表示「静态的」,主要思想是保证无论该类是否产生对象或无论产生多少对象的情况下,某些特定的数据在内存空间中只有一份。

在Java类中,可用static修饰属性、方法、代码块和内部类,而不能修饰构造器。

其中,被修饰后的成员具有如下特点:

  • 随着类的加载而加载,故优先于对象存在;
  • 所修饰的成员,被该类的所有对象所共享;
  • 访问权限允许时,可不创建对象,直接被类调用。

static有哪几种用法?

关键字static大体上有一下五种用法:

  • 静态导入
  • 静态变量
  • 静态方法
  • 静态代码块
  • 静态内部类

静态导入:

静态导入极少使用,在梳理本篇文章之前真的不知道还有此奇技淫巧,仅作了解即可,示例代码如下:

// 静态导包,在类中使用Math的静态方法和属性时可以省略「Math.」
import static java.lang.Math.*; 
public class StaticImport { 
    public static void main(String[] args) { 
        double a = cos(PI / 2); //已省略「Math.」 
        double b = pow(2.4,1.2); 
        double r = max(a,b); 
        System.out.println(r); 
    } 
}

当在程序中多次使用某类型的静态成员(静态属性和静态方法)时,即可使用静态导入,作用是将该类型的静态成员引入到当前的命名空间,那么在程序中调用该类型的静态成员时可以像调用本类内定义的成员一样,直接调用,而无需采用「类名.静态成员名」的方式。

缺点:虽然简化代码,但可读性大大降低,几乎不使用。

静态变量:

在Java类中,用static修饰的属性为静态变量(类变量或类属性),而非static修饰的属性为实例变量,区别如下:

静态变量VS实例变量

静态方法:

在Java类中,用static修饰的方法为静态方法,也叫类方法,其与非静态方法的对比如下:

静态方法VS非静态方法

注意:静态的结构随着类的加载而加载,其生命周期早于非静态的结构,同时被回收也要晚于非静态的结构。

静态代码块:

静态代码块仅在类加载时运行一次,主要用于对Java类的静态变量(类属性)进行初始化操作。

执行顺序:静态代码块 > 构造代码块(非静态代码块) > 构造方法。

静态内部类:

内部类的一种,静态内部类不依赖于外部类,即可以不依赖于外部类实例对象而被实例化,且不能访问外部类的非静态成员(属性和方法)。

建议重点关注下一个问题「静态内部类与普通内部类有什么区别?」。


静态内部类与普通内部类有什么区别?

在对象创建方面,静态内部类可以不依赖于外部类的实例对象而被实例化,而普通内部类需要在外部类实例化后才能进行实例化。示例代码如下:

// 设类ClassA有静态内部类ClassB和普通内部类ClassC
ClassA classA = new ClassA();
ClassA.B classB = new ClassA.B();
ClassA.C classC = classA.new ClassC();

在是否能够拥有静态成员方面,静态内部类可以拥有静态成员,而普通内部类则不能拥有静态成员。

在访问外部类成员方面,静态内部类只能访问外部类的静态成员,而非静态内部类可以访问外部类的所有成员。

备注:该问题在面试Synopsys安全软件工程师职位时被问到过,一定要重点关注。


Java中变量的初始化顺序是怎样的?

在有继承关系的情况下,变量初始化顺序如下:

  • 父类的静态变量和静态代码块
  • 子类的静态变量和静态代码块
  • 父类的实例变量和普通代码块
  • 父类的构造函数
  • 子类的实例变量和普通代码块
  • 子类的构造函数

扩展问题:指出下面程序的运行结果

class A {
    static {
        System.out.print("1");
    }
    public A() {
        System.out.print("2");
    }
}

class B extends A{
    static {
        System.out.print("a");
    }
    public B() {
        System.out.print("b");
    }
}

public class Hello {
    public static void main(String[] args) {
        A ab = new B();
        ab = new B();
    }
}

执行结果为:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。


扩展面试题

问:静态方法内部是否可调用非静态方法?

答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。

问:内部类可以引用它的包含类(外部类)的成员吗?

答:静态内部类只能访问外部类的静态成员,包括私有的;而普通内部类可以访问外部类的所有成员,包括私有的。


推荐阅读


欢迎关注

Java名企面试吧,每天10点24分,我们不见不散!

丙子先生的宗旨是,每天以短篇幅讲高频面试题,不增加太多负担,但需要持之以恒。

能力有限,欢迎指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值