Java中static关键字的用法及面试题

static

一、什么是static

static:可以修饰 java类、方法、变量、代码块的方法

二、static的用途

1、static修饰类

static修饰类时,这个类不能是普通的类,只能是内部类。具体使用:

public class StaticDemo1 {
    public static class InnerClass {
        InnerClass() {
            System.out.println("===========静态内部类=============");
        }
        public void InnerMethod() {
            System.out.println("===========静态内部类的方法==========");
        }
    }

    public static void main(String[] args) {
        InnerClass innerClass = new InnerClass();
        innerClass.InnerMethod();
    }
}
/*
    输出结果:
        ===========静态内部类=============
        ===========静态内部类的方法==========
 */

内部类不用static修饰时写法:

public class StaticDemo11 {
    public class InnerClass {
        InnerClass() {
            System.out.println("===========内部类=============");
        }
        public void innnerMethod() {
            System.out.println("===========内部类的方法=======");
        }
    }

    public static void main(String[] args) {
        InnerClass innerClass = new StaticDemo11().new InnerClass();
        innerClass.innnerMethod();
    }
}

/*
    输出:
        ===========内部类=============
        ===========内部类的方法=======
 */

结论:

如果没有用static修饰InnerClass,则只能先new一个外部实例,再通过外部实例创建内部类对象。

2、static修饰方法

修饰方法的时候,其实跟类一样,可以直接通过类名来进行调用:

public class StaticDemo2 {
    public static void staticMethod2() {
        System.out.println("========静态方法========");
    }

    public static void main(String[] args) {
        
        // 方式1、通过类名调用
//        StaticDemo2.staticMethod();
        // 方式2、创建对象调用
        StaticDemo2 demo2 = new StaticDemo2();
        demo2.staticMethod2();

    }
}
/*
    输出:
        ========静态方法========
 */

**结论:**调用有两种方式,一般选择方式一

注意:静态方法不能调用非静态成员。

3、static修饰变量

被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象,没有被static修饰的成员变量叫做实例变量,说明这个变量是属于某个具体的对象的。

public class StaticDemo3 {
    public static String string = "静态变量";
    public static void main(String[] args) {
        // 直接通过类名称调用
        System.out.println(StaticDemo3.string);
    }
}
/*
    输出:
        StaticDemo1
 */

**结论:**可以直接通过类名称调用

  • 静态变量和非静态变量的区别:
  1. 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化
  2. 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
  • 能通过this访问静态成员变量吗 ?
public class StaticDemo5 {
    static int value = 33;

    public static void main(String[] args) {
        new StaticDemo5().printValue();
    }

    private void printValue() {
        int value = 3;
        System.out.println(this.value);
    }
}

大家猜猜这里输出会是多少呢?这里主要考察了this和static的理解。this代表的是当前对象, 那么通过new Main()来调用printValue的话,当前对象就是通过new Main()生成的对象 。 而static变量是被对象所享有的,因此在printValue中的this.value的值毫无疑问是33 。 在这里永远要记住一点:静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限足够)。

4、static修饰代码块

  • 静态代码块在类第一次被加载的时候执行。

  • 类的初始化顺序:先父类后子类

    1. 静态代码块
    2. 普通代码块
    3. 构造函数

    详细过程:

    父类静态变量、父类静态代码块

    子类静态变量、子类静态代码块

    父类普通变量、父类普通代码块、父类构造函数

    子类普通变量、子类普通代码块、子类构造函数

  • 静态块里面的代码只执行一次,且只在初始化类的时候执行 。

代码验证:

public class Fu {
    //父类静态代码块
    static {
        System.out.println("=====父类静态代码块=====");
    }
    // 父类构造函数
    Fu() {
        System.out.println("=====父类构造函数=======");
    }

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

class Zi extends Fu {
    // 子类静态代码块
    static {
        System.out.println("=====子类静态代码块=====");
    }

    Zi() {
        System.out.println("====子类构造函数=====");
    }
}
/*
    输出:
        =====父类静态代码块=====
        =====子类静态代码块=====
        =====父类构造函数=======
        ====子类构造函数=====
        =====父类构造函数=======
        ====子类构造函数=====
 */

静态代码块需要注意的问题:

  • 静态代码块对于定义在它之后的静态变量,可以赋值,但是不能访问
public class StaticDemo4 {
    static {
        c = 10;
        System.out.println(c);
    }

    public StaticDemo4() {
        System.out.println(c);
    }
    public static void main(String[] args) {
        new StaticDemo4();
    }
    private static int c;
}

报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IsOlDpOy-1584609134902)(C:\Users\惠秋丽\AppData\Roaming\Typora\typora-user-images\1584605560802.png)]

如果将private static int c;这行代码放到static静态代码块之前就为正确的

5、总结

1、static是一个修饰符,用于修饰成员。(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量。

2、static修饰的成员被所有的对象共享。

3、static优先于对象存在,因为static的成员随着类的加载就已经存在。

4、static修饰的成员多了一种调用方式,可以直接被类名所调用,(类名.静态成员)。

5、static修饰的数据是共享数据,对象中的存储的是特有的数据。

三、面试题

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

分析:

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

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

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

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

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

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

  • 然后再调用子类构造器,输出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

分析:

  • 找到main方法入口,main方法是程序入口,但在执行main方法之前,要先加载Test类
  • 加载Test类的时候,发现Test类有static块,而是先执行static块,输出test static结果
  • 然后执行new MyClass(),执行此代码之前,先加载MyClass类,发现MyClass类继承Test类,而是要先加载Test类,Test类之前已加载
  • 然后加载MyClass类,发现MyClass类有static块,而是先执行static块,输出myclass static结果
  • 然后调用MyClass类的构造器生成对象,在生成对象前,需要先初始化父类Test的成员变量,而是执行Person person = new Person(“Test”)代码,发现Person类没有加载
  • 转去又加载Person类,发现Person类有static块,而是先执行static块,输出person static结果
  • 接着刚才Test类的Person person = new Person("Test") 执行Person构造器,输出person Test结果
  • 然后调用父类Test构造器,输出test constructor结果,这样就完成了父类Test的初始化了
  • 转回来再初始化MyClass类成员变量,执行Person构造器,输出person MyClass结果
  • 最后调用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方法入口,但在执行main方法之前需要先加载Test类
  • 加载Test类的时候,发现有静态代码块,执行静态代码块,按照顺序执行。

你可能感兴趣的:(Java中static关键字的用法及面试题)