夯实Java基础(七)——Static关键字

2019-08-09 00:00:00 关键字 基础 夯实

1、static介绍

static关键字一直是各大企业中面试常常会问到的问题,主要考察面试者的基础是否扎实,下面来介绍一下static关键字。

Java中static表示“全局”或者“静态”的意思,可以用来修饰成员变量、成员方法、代码块、内部类和导包。在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载了,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象,所以被static修饰的成员变量和成员方法可以直接使用类名调用。

class Person{
    private static int num=0;

    public Person() {
        num++;
    }

    public static void plus(){
        System.out.println(Person.num);
    }

    public static void main(String[] args) {
        new Person();
        new Person();
        Person.plus();
        plus();
    }
}
//结果:2、2

2、static变量

static修饰的成员变量称作静态变量,静态变量被所有的对象所共享,在内存中只有一个,它会随着类的加载而加载。

另外主要:static是不允许用来修饰局部变量

提到静态变量我们来看静态变量和非静态变量的区别:

静态变量(类变量):静态变量被所有的对象所共享,也就是说我们创建了一个类的多个对象,多个对象共享着一个静态变量,如果我们修改了静态变量的值,那么其他对象的静态变量也会随之修改。

非静态变量(实例变量):如果我们创建了一个类的多个对象,那么每个对象都有它自己该有的非静态变量。当你修改其中一个对象中的非静态变量时,不会引起其他对象非静态变量值得改变。

class Person{
    private static int num;
    private int num1;

    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person();

        Person.num=10;
        p1.num1=11;

        Person.num=100;
        p2.num1=111;

        System.out.println(Person.num);
        System.out.println(p1.num);
        System.out.println(p2.num);
        System.out.println(p1.num1);
        System.out.println(p2.num1);
    }
}

《夯实Java基础(七)——Static关键字》

从运行结果来看,static变量的值是相同的,说明是共享的,而非静态变量他们的值则不相同,说明依赖于实例。虽然static修饰的变量它不依赖类特定的实例,但它毕竟也是类中的一个属性,也是可以通过类的实例来调用的,只不过属性的值是共享的而已。但是最好还是用类名调用

3、static方法

static修饰的成员方法称作静态方法,这样我们就可以通过“类名. 方法名”进行调用。由于静态方法在类加载的时候就存在了,所以它不依赖于任何对象的实例就可以进行调用,因此对于静态方法而言,是木有当前对象的概念,即没有this、super关键字的。因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。

并且由于独立于任何实例,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。举个简单的例子:

class Person{

    public static void main(String[] args) {
        Person p=new Person();
        p.show1();
    }

    public void show1(){
        System.out.println("非静态方法show1()...");
        show2();
        show3();
    }

    public static void show2(){
        System.out.println("静态方法show2()...");
        //这里编译会报错Non-static method 'show1()' cannot be referenced from a static context
        //show1();
    }
    public static void show3(){
        System.out.println("静态方法show3()...");
        show2();
    }
}

4、static代码块

被static修饰的代码块也叫静态代码块,会随着JVM加载类的时候而加载这些静态代码块,并且会自动执行它们可以有多个,可以存在于该类的任何地方。JVM会按照它们的先后顺序依次执行它们,而且每个静态代码块只会被初始化一次,不会进行多次初始化。

示例:

public class Person{

    static {
        System.out.println("Person类静态块");
    }

    public Person() {
        System.out.println("Person类构造器");
    }

    public static void main(String[] args) {
        new Son();
        System.out.println("-------");
        new Son();
    }
}

class Son extends Person{
    static {
        System.out.println("Son类静态块");
    }

    public Son() {
        System.out.println("Son类构造器");
    }
}

运行结果:

《夯实Java基础(七)——Static关键字》

从运行结果分析:首先运行main()方法,然后JVM就会加载类,因为Son类继承了Person类,所以会先加载父类Person类,再去加载子类Son。由于静态代码块会随着类的加载而加载,所以先输出父类中静态代码块内容”Person类静态块”,然后输出子类中静态代码块内容”Son类静态块”。加载完类之后执行main()方法内容,先new了第一个Son实例,由于子类构造器中默认调用了super(),所以先输出父类构造器的内容,再输出子类构造器的内容。之后又new了第二个Son实例,却是输出的构造器的内容,说明static静态块只加载了一次。结论:静态代码块是先加载父类的静态代码块,然后再加载子类静态代码块,是随着类的加载而加载,而且只会加载一次。

补充:因为入口main()是个方法,也需要用类去调用,所以类的加载优先级>main()方法。

5、static内部类

static修饰内部类的用法很少,毕竟内部类用的就不是很多,一般在源码中才能看见。但是就是有这么一个特殊的用法是用static修饰内部类。普通类是不允许声明为静态的,只要内部类才可以,被static修饰的内部类可以直接作为一个普通类来使用,而不需先实例一个外部类。

static修饰内部类注意几点:

  • 静态内部类只能访问外部类的静态成员,否则编译会报错。
  • 不管是静态方法还是非静态方法都可以在非静态内部类中访问。
  • 如果需要调用内部类的非静态方法,必须先new一个OuterClass的对象outerClass,然后通过outer。new生成内部类的对象,而static内部类则不需要。

简单举例:

public class OuterClass {
    private static int num=6;
    // 静态内部类
    public static class InnerStaticClass{
        public void print() {
            System.out.println("静态内部类方法print()=="+num);
        }
    }
    //非静态内部类
    public class InnerClass{

        public void display(){
            System.out.println("非静态内部类方法display()=="+num);
            show();
        }
    }
    public void show(){
        System.out.println("外部类的show()方法=="+num);
    }

    public static void main(String[] args) {
        //非static对象实例
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass innerClass = outer.new InnerClass();
        innerClass.display();
        //static对象实例
        OuterClass.InnerStaticClass staticClass=new OuterClass.InnerStaticClass();
        staticClass.print();
    }
}

6、static导包

这个知识点非常的冷门,基本上很少的地方会用,我们只需要了解一下即可,用static修饰导包的格式是 import static  包名,static不能写在import前面,这样可以指定导入某个类中的指定静态资源,并且不需要使用类名.资源名,可以直接使用资源名。

来看一下案例:

import static java.lang.Math.*;

public class StaticTest {
    public static void main(String[] args) {
        System.out.println(sqrt(9));
        System.out.println(abs(-12));
    }
}
//结果:3、12

从上面的案例看出,使用import static我们导入了Math类下的所有静态资,所以我们就可以直接使用sqrt(9)、abs(-12)静态方法了。

这样在写代码的时候确实能省一点代码,但是会影响代码可读性,所以一般情况下不建议这么使用。

7、总结

static是非常重要的一个关键字,它的用法也很丰富,以下总结为:

①、static可以用来修饰成员变量、成员方法、代码块、内部类和导包。

②、用来修饰成员变量,将其变为静态变量,从而实现所有对象对于该成员的共享,通过“类名.变量名”即可调用。

③、用来修饰成员方法,将其变为静态方法,可以直接使用“类名.方法名”的方式调用,常用于工具类。

④、静态方法中不能使用关键字this和super 。

⑤、静态块用法,将多个类成员放在一起初始化,使得程序更加规整,其中理解对象的初始化过程非常关键。

⑥、静态代码块是先加载父类的静态代码块,然后再加载子类静态代码块,而且只会加载一次。

⑦、static修饰成员变量、成员方法、代码块会随着类的加载而加载。

⑧、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”即可调用类方法,更加方便,但是代码的可读性降低。

相关文章