zoukankan      html  css  js  c++  java
  • static 关键字介绍

    大家都知道,我们可以基于一个类创建多个该类的对象,每个对象都拥有自己的成员,互相独立。然而在某些时候,我们更希望该类所有的对象共享同一个成员。此时就是 static 大显身手的时候了!!

    Java 中被 static 修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,(这块涉及到JVM的内存模型,静态成员在堆内存的永久带)即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。当然,鉴于他作用的特殊性更推荐用类名访问~~

    例如:

    public class HelloWorld {
        
        // 定义静态变量,保存班级名称
         static  String className = "JAVA开发一班";
    
        public static void main(String[] args) {
            
            // 访问静态变量,输出班级名称
            System.out.println(HelloWorld.className );
            HelloWorld helloWorld = new HelloWorld();
            System.out.println(helloWorld.className) ;
        }
    }

    使用 static 可以修饰变量、方法和代码块。

    需要注意:

    1、 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员,如果要想在静态方法中调用非静态变量,可以通过实例化对象,通过对象.属性访问对象的属性。

    例如:

    public class HelloWorld {
    
        // 定义静态变量score1
        static int score1 = 86;
        String name = "张三" ;
        public static void main(String[] args) {
            System.out.println(HelloWorld.score1);
            System.out.println(HelloWorld.name); //此处的代码在idea会直接报错。在静态方法中不能访问非静态变量。可以通过实例化对象,通过对象访问。
            HelloWorld helloWorld = new HelloWorld();
            System.out.println(helloWorld.name);
        }
    }

    静态代码块

    我们知道,Java 中可以通过初始化块进行数据赋值

    例如:

    public class HelloWorld {
    
         String name ; //定义成员变量
        {
            name = "zhangsan"; //在初始化块中初始化成员变量的值。
        }
    }

    在类的声明中,可以包含多个初始化块,当创建类的实例时,就会依次执行这些代码块。如果使用 static 修饰初始化块,就称为静态初始化块。

    需要特别注意:静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量。

    上面这段红字标注的很重要,解读一下,创建类的实例(new 类的时候),初始化块new一次就会执行一次,但是static修饰的只会执行一次,因为他是属于类成员,只在类加载的时候初始化一次,所以不管new多少次,static 修饰的代码块只会执行一次。(这块如果对jvm的内存模型不理解,可能感觉有点抽象。后面我们会有专门博客介绍jvm内存模型)

    上面的说明的例子如下:

    public class HelloWorld {
    
        String name; // 声明变量name
        String sex; // 声明变量sex
        static int age;// 声明静态变量age
    
        // 构造方法
        public HelloWorld() {
            System.out.println("通过构造方法初始化name");
            name = "tom";
        }
    
        // 初始化块
        {
            System.out.println("通过初始化块初始化sex");
            sex = "男";
        }
    
        // 静态初始化块
        static {
            System.out.println("通过静态初始化块初始化age");
            age = 20;
        }
    
    
        public static void main(String[] args) {
            // 创建对象
            HelloWorld hello = new HelloWorld();
            HelloWorld h1 = new HelloWorld();
    
        }
    }

    运行这个例子,我还想证明一个问题,构造方法,初始化块,静态代码块,执行的先后顺序,及上面初始化块每次实例化都会执行,static修饰的静态代码块只会执行一次。

    执行结果如下:

    通过静态初始化块初始化age
    通过初始化块初始化sex
    通过构造方法初始化name
    通过初始化块初始化sex
    通过构造方法初始化name

    由上面的执行结果,我们可以看出,静态代码块最先执行,初始化块,构造方法依次。并且初始化块和构造方法每次实例化都会执行,静态代码块只会执行一次。

    再深入一点,子类和父类,都含有静态代码块,代码块,构造函数,在new子类的过程中,上面子类父类的(静态代码块,代码块,构造函数)初始化顺序顺序是如何的?

    package com.sysware.cloud.acl.controller;
    
    /**
     * Created by tianwenqing on 2019/4/1.
     */
    public class Parent{
    
        /**
         * 静态代码块
         */
        static {
            System.out.println("执行了父类静态代码块....");
        }
    
        /**
         * 代码块
         */
        {
            System.out.println("执行了父类代码块");
        }
    
        private String name ;
        private Integer age ;
    
        public Parent(){
    
            System.out.println("执行了父类构造函数");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }

    子类:

    package com.sysware.cloud.acl.controller;
    
    /**
     * Created by tianwenqing on 2019/4/1.
     */
    public class Parent{
    
        /**
         * 静态代码块
         */
        static {
            System.out.println("执行了父类静态代码块....");
        }
    
        /**
         * 代码块
         */
        {
            System.out.println("执行了父类代码块");
        }
    
        private String name ;
        private Integer age ;
    
        public Parent(){
    
            System.out.println("执行了父类构造函数");
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }

    测试类:

    package com.sysware.cloud.acl.controller;
    
    /**
     * Created by tianwenqing on 2019/4/1.
     */
    public class Test {
    
        public static void main(String[] args) {
           Child child = new Child();
            System.out.println("第二次new........");
            Child child1 = new Child();
    
        }
    }

    输出结果如下:

    如图可知初始化顺序为:先父类的静态代码块--》子类的静态代码块--》父类的代码块--》父类的构造函数--》子类的代码块--》子类的构造函数。

  • 相关阅读:
    149. Max Points on a Line(js)
    148. Sort List(js)
    147. Insertion Sort List(js)
    146. LRU Cache(js)
    145. Binary Tree Postorder Traversal(js)
    144. Binary Tree Preorder Traversal(js)
    143. Reorder List(js)
    142. Linked List Cycle II(js)
    141. Linked List Cycle(js)
    140. Word Break II(js)
  • 原文地址:https://www.cnblogs.com/wenq001/p/10299797.html
Copyright © 2011-2022 走看看