• Java基础(二)-static关键字分析


    一、前言

    static关键字是我们在编程中经常会使用到的,但有些可能只知其然而不知其所以然。下面介绍static关键字的作用再通过例子结合说明。

    static关键字共有五种作用(先说明static所修饰的不会改变其(private、protected、default和public)作用域的范围):

    •  修饰成员变量(非局部变量)
    •  修饰成员方法
    •  修饰代码块
    •  修饰内部类
    •  静态导包

    怎么会有五种呢,大部分初学者对前面两种或者加上第三种还是很熟悉的,第四种情况可能一开始的人就比较少知道了,第五种就更少人知道了。下面一一介绍:

    二、修饰成员变量

    • 说明及特点:static所修饰的变量也叫做静态变量,该变量在类初次加载时会被初始化,所以它在内存中只有一个副本,被所有对象所共享(前提也是public所修饰的),其实也叫做类变量,直接通过类可以调用。而非静态变量是在创建对象的时候被初始化,存在多个副本但各个副本互不影响,它也叫做实例化对象的变量。其中要注意的是static不能修饰局部变量(定义在函数内,语句内等,只在所属的区域有效)
    • 好处:优先加载、内存利用率高(类初始化时加载且仅分配一次内存);  调用简单
    • 案例解析:
     1 package com.yuanfy.test.statics;
     2 
     3 class Student {
     4     //非静态变量
     5     private String name;
     6     int age;
     7     //静态变量、全局变量
     8     public static String schoolName = "清华大学";
     9 
    10     public Student(String name, int age) {
    11         this.name = name;
    12         this.age = age;
    13     }
    14     
    15     public void say(){
    16         static int count = 0;//编译报错,不能定义局部变量
    17     }
    18 
    19     public String getName() {
    20         return name;
    21     }
    22     
    23 }
    24 public class StaticTest {
    25     public static void main(String[] args) {
    26         Student s1 = new Student("张三", 18);
    27         //1、只能通过实例对象调用
    28         System.out.println("name: " + s1.getName() + ", age: " + s1.age);
    29         //2、可以通过类直接调用,如果schoolName是public在其他包下也是可以直接通过类调用的,这样的变量也叫做全局变量
    30         System.out.println("school:" + Student.schoolName);
    31         
    32         //3、非静态变量只属于实例对象,所以对应成员变量的值互不影响。
    33         Student s2 = new Student("李四", 19);
    34         System.out.println("测试非静态变量是否会影响其他实例对象下的使用:");
    35         System.out.println("name: " + s1.getName() + ", age: " + s1.age);//name: 张三, age: 18
    36         System.out.println("name: " + s2.getName() + ", age: " + s2.age);//name: 李四, age: 19
    37         
    38         //静态变量会影响使用。
    39         System.out.println("静态变量会影响使用:");
    40         s2.schoolName = "北京大学";
    41         System.out.println("s2 school:" + s1.schoolName);//s1的学校名称肯定会改变成s2的学校名
    42     }
    43 }

    output:

    name: 张三, age: 18
    school:清华大学
    测试非静态变量是否会影响其他实例对象下的使用:
    name: 张三, age: 18
    name: 李四, age: 19
    静态变量会影响使用:
    s2 school:北京大学

    三、修饰成员方法

    • 说明:static所修饰的方法叫做静态方法,也称类方法。
    • 特点:

      1、跟静态变量一样,静态方法属于类而不属于对象,所以静态方法可以通过类直接调用,而不需要创建对象实例来调用。
      2、由于静态方法是通过类直接调用的,所以静态方法中没有this对象。
      3、静态方法可以调用静态变量或方法,但是不能调用非静态变量或方法
      4、静态方法不能被覆盖。因为静态方法是编译时静态绑定的,而覆盖是基于运行时动态绑定的。

      注意:构造方法不是静态方法,可以参考这里

    • 案例解析:
     1 class Student {
     2     //非静态变量
     3     private String name;
     4     int age;
     5     //静态变量、全局变量
     6     public static String schoolName = "清华大学";
     7 
     8     public Student(){}
     9     public Student(String name, int age) {
    10         this.name = name;
    11         this.age = age;
    12     }
    13     
    14     public static void say(){
    15         //2、由于静态方法是通过类直接调用的,所以静态方法中没有this对象。
    16         System.out.println("调用者:" + this);//编译失败
    17         //3、静态方法可以调用静态变量或方法,但是不能调用非静态变量或方法
    18         //Cannot make a static reference to the non-static 
    19         System.out.println("调用非静态变量name:" + name);//编译失败
    20         //但是可以调用静态方法
    21         System.out.println("调用静态变量schoolName:" + schoolName);
    22     }
    23     public void setName(){
    24         //非静态方法可以使用this,知道是哪个实例对象在调用。
    25         System.out.println("调用者:" + this);
    26     }
    27 }
    28 class StudentDTO extends Student{
    29 
    30     //编译报错; 4、静态方法不能被覆盖覆盖。因为静态方法是编译时静态绑定的,而覆盖是基于运行时动态绑定的。
    31     @Override
    32     public static void say(){
    33         
    34     }
    35     
    36 }

    四、修饰代码块

    • 说明:static修饰在代码块前用来形成静态代码块以优化程序性能。
    • 特点:

      1、在类初次被加载的时候在加载,只加载一次;

      2、一个类可以有多个代码块,按代码块的顺序执行。

    • 案例分析:
     1 public class StaticTest {
     2     static {
     3         System.out.println("第一次加载静态代码块内容");
     4     }
     5     
     6     public static void main(String[] args) {
     7         //不需要实例化对象,只要类加载就会执行static块内容。
     8         // 与执行顺序与代码块的顺序一致。
     9     }
    10     
    11     static {
    12         System.out.println("第二次加载静态代码块内容");
    13     }
    14 }    

    output:

    第一次加载静态代码块内容
    第二次加载静态代码块内容

    五、修饰内部类

    • 说明:static修饰的内部类成为静态内部类。
    • 特点:

      1、普通类不可以用static修饰,只有内部类才可以。(语法)

      2、非静态内部类无法声明静态方法和静态变量,只有静态内部类才可以。原因很简单:当加载该类时,会自动解析类中static所修饰的所有变量、方法、代码块和内部类,而静态变量定义在非静态内部类,所以不会加载。而jvm虚拟机是要求静态变量必须是在类加载时初始化的。所以会编译报错,无法声明。

      3、非静态内部类可以随意访问外部类的成员或方法,而静态内部类只能访问静态变量或方法。

      4、调用静态内部类不需要持有外部内的引用可以直接调用,而非静态内部类就必须持有外部内的引用才能调用

    • 案例分析:
     1 public class StaticTest {
     2     private static int startPrice = 10;//起步价
     3     
     4     private static int kmPrice = 2;//每一公里2块钱
     5     
     6     private int kmCount;//里程计时器
     7     
     8     static class Car1{
     9         //编译成功, 2、非静态内部类无法声明静态方法和静态变量,只有静态内部类才可以。
    10         public static void start(){
    11             //编译失败,3、非静态内部类可以随意访问外部类的成员或方法,而静态内部类只能访问静态变量或方法。
    12 //            kmCount = 0;
    13             System.out.println("car1汽车启动,起步价是:" + startPrice);
    14         }
    15     }
    16     class Car2{
    17         //编译报错。 2、非静态内部类无法声明静态方法和静态变量,只有静态内部类才可以。
    18 //        public static void start(){
    19 //            System.out.println("开始启动");
    20 //        }
    21         
    22         public void start2(){
    23             //编译成功,3、非静态内部类可以随意访问外部类的成员或方法,而静态内部类只能访问静态变量或方法。
    24             kmCount = 0;
    25             System.out.println("car2汽车启动,起步价是:" + startPrice);
    26         }
    27     }
    28     public static void main(String[] args) {
    29         StaticTest.Car1.start();
    30         //编译报错 4、调用静态内部类不需要持有外部内的引用可以直接调用,而非静态内部类就必须持有外部内的引用才能调用
    31 //        StaticTest.Car2.start();
    32         
    33         //编译成功
    34         StaticTest.Car2 car2 = new StaticTest().new Car2();
    35         car2.start2();
    36     }
    37 }

    output:

    car1汽车启动,起步价是:10
    car2汽车启动,起步价是:10

    六、静态导包

    • 说明:其实很简单,跟平常导包差不多,只是在import后面加上static,然后再在类后面引入所有成员(变量、方法、内部类等)用.*代替。格式如下:import static classPath.*;这个不是很常用,也不建议使用,请看下面特点及不足之处。
    • 特点:

      对于导入静态包可以直接使用其静态方法。

    • 不足之处:

      1、代码不直观:如果导入了多个静态包,不仔细看是不知道这个方法来源于哪个类 。

      2、会存在方法冲突。

    • 案列分析1:
    package com.yuanfy.test;
    /**
     * @description 字符串工具类案例
     * @author YuanFY 
     * @date 2017年12月10日 上午11:35:25
     * @version 1.0
     */
    public class StringUtil {
        public static boolean isEmpty(String str){
            return true;
        }
    }
    package com.yuanfy.test.statics;
    //导入静态包
    import static com.yuanfy.test.StringUtil.*;
    //import static com.yuanfy.test.scope2.StringUtil.*;
    public class StaticTest2 {
        
        public static void main(String[] args) {
            String str1 = "123";
            String str2 = new String("123");
            //方便之处直接使用静态方法,不直观
            if (isEmpty(str1)){
                
            }
        }
    }
    • 案列分析2(导入多个静态包):
    package com.yuanfy.test.scope2;
    //注意这是不同包下的工具类
    public class StringUtil {
        public static boolean isEmpty(String str){
            return true;
        }
    }
    package com.yuanfy.test.statics;
    //导入多个静态包
    import static com.yuanfy.test.StringUtil.*;
    import static com.yuanfy.test.scope2.StringUtil.*;
    public class StaticTest2 { public static void main(String[] args) { String str1 = "123"; String str2 = new String("123"); //编译报错,The method isEmpty(String) is ambiguous for the type StaticTest2 if (isEmpty(str1)){ } } }

    拓展(static所修饰加载的先后顺序):静态代码块和静态变量无优先顺序,只跟其定义的前后顺序有关;静态代码块和变量优先静态方法。

     1 class Test{
     2     public Test(){
     3         System.out.println("test()");
     4     }
     5 }
     6 public class StaticTest3 {
     7     static {
     8         System.out.println("代码块1");
     9     }
    10     private static Test test = new Test();
    11     static {
    12         System.out.println("代码块2");
    13     }
    14     
    15     public static void test(){
    16         System.out.println("StaticTest3 test()");
    17     }
    18     
    19     public static void main(String[] args) {
    20         StaticTest3.test();
    21     }
    22 }

    output:

    代码块1
    test()
    代码块2
    StaticTest3 test()

    本篇文章分析到此结束,参考文献如下:

    http://rednaxelafx.iteye.com/blog/652719

    https://www.cnblogs.com/dolphin0520/p/3799052.html

  • 相关阅读:
    Vue学习之监听methods、watch及computed比较小结(十一)
    Vue学习之路由vue-router传参及嵌套小结(十)
    Vue学习之路由vue-router小结(九)
    Vue学习之组件切换及父子组件小结(八)
    Vue学习之全局和私有组件小结(七)
    Vue学习之动画小结(六)
    Vue学习之vue-resource小结(五)
    Vue学习之生命周期钩子小结(四)
    Vue学习之过滤器和自定义指令小结(三)
    Vue学习之品牌案例部分代码小结(二)
  • 原文地址:https://www.cnblogs.com/yuanfy008/p/8012906.html
Copyright © 2020-2023  润新知