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


    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);
        }
    }

    从运行结果来看,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类构造器");
        }
    }

    运行结果:

    从运行结果分析:首先运行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修饰成员变量、成员方法、代码块会随着类的加载而加载。

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

  • 相关阅读:
    vue路由
    vue-cli目录结构介绍
    andriodiphone视频禁止全屏播放
    JS判断Android、iOS或浏览器的多种方法(四种方法)
    vue中移动端自适应方案
    移动端视频不兼容
    vue之router-link
    移动端网页开发注意点
    AWS EC2服务器的HTTPS负载均衡器配置过程
    Kubernetes概念介绍和v1版本部署过程
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/11220387.html
Copyright © 2020-2023  润新知