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

    输出结果如下:

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

  • 相关阅读:
    java基础知识回顾之java Thread类学习(三)--java线程实现常见的两种方式实现好处:
    java基础知识回顾之java Thread类--java线程实现常见的两种方式实现Runnable接口(二)
    java基础知识回顾之java Thread类--java线程实现常见的两种方式(一)
    java基础知识回顾之javaIO类--RandomAccessFile类
    java基础知识回顾之javaIO类--java序列化和反序列化
    X明X源面试题《三》
    X明X源面试题《二》
    SqlServer查询计划
    X明X源面试题《一》
    索引笔记《二》确定需要建立索引的列
  • 原文地址:https://www.cnblogs.com/wenq001/p/10299797.html
Copyright © 2011-2022 走看看