Java的static关键字的历史与应用

“静态”的历史

  1. 静态最初是c语言为了表示退出一个块后依然存在的局部变量,这时候的”静态“是有实际意义的,也就是块中的变量一直保留,再次进入时变量依然存在。

  2. 之后的”静态“在C中有了第二种含义:表示不能从其他文件访问的全局变量和函数,为了不加入新得关键字,所以重用了static关键字

  3. 第三次是C++中重用了static,指示属于类而不属于类对象的变量和函数,也就是java后来定义的”静态“的作用

静态字段

static作用于类的字段,这个字段从此将变为类所拥有的,也就是说,这个类实例化的所有对象,都将共享这个字段。

  • 实际上我们如果想要让字段对于对象共享,会将字段设置为private static int count = 0; 然后在构造方法中添加id = count++,这样就可以实现自动的对象id字段绑定,private 可以保证从类外无法改变这个字段。

在一些面向对象的编程语言中,静态字段被称为类字段,“静态”只是沿用了C++的用法

public class TestStatic {
    public static int number;
    public int id;
    public void setId(){
        id = number;
        number++;
    }
}

之后实例化的Student对象,只要调用setId()方法,就可以为对象id赋值。number由于是类所属的,与实例化的对象无关,所以会在最初的数值上加一。不会因为实例化的对象而初始为1。

  • 由于number字段是类所属的,所以可以通过类名调用:
int studentID = TestStatic.number;nm tg j
  • static不可以修饰局部变量,因为static修饰的变量会一直存在于方法区.而局部变量的生命周期仅在局部代码块中

观察下方代码,this代表当前对象,而t1就是当前的对象,相当于t1.number,静态字段被所有对象共享,所以自然结果是33。

public class TestStatic {
    public static int number = 33;
        
    public void setId() {
        int number = 4;
        System.out.println(this.number);
        System.out.println(number);
    }
    public static void main(String[] args) {
        TestStatic t1 = new TestStatic();
        t1.setId();
    }
}
结果:33
     4

静态常量

  • 由于静态字段可以被每个实例化的对象访问修改,所以是不安全的
  • 而的静态常量由于有final关键字,一旦定义了数值,就只能访问,无法修改,所以是安全的

比如Math类中定义的PI常量

public class Math{
    public static final double PI = 3.141592653589323846;
}
//System类中的out就是一个静态常量
public static final PrintStream out = null;

注意:System类中有一个setOut的方法可以设置System.out为不同的流,是因为他是一个原生native方法,不是在java语言中实现,所以可以绕过java的访问控制机

静态常量也可以通过类名调用

静态方法

静态方法也是独立于实例对象的存在,依附于类,比如Math.pow(x,a),它不需要任何Math对象的参数(非静态的字段),并且他也无法在对象上进行操作。但是由于静态字段也是依附于类的,所以静态方法可以调用静态字段。

可以通过类名调用静态方法

Math.pow(2,4);

当然也是可以通过对象调用静态方法的

  • 但往往静态方法都是与实例对象无关的,通过对象调用实例方法会给人一种有关系的错觉,所以建议使用类名调用。
  • 静态方法会在类加载时存入内存方法区,直到程序结束内存在释放
public class Test {
    public static void main(String[] args) {
        TestStatic t1 = new TestStatic();
        t1.setId();
    }
}

静态方法与非静态方法的区别:

  • 静态方法只能访问静态字段,非静态方法则无限制。(因为实例字段属于具体的对象,静态字段属于类)
  • 非静态方法只有实例化对象后才会存在于方法区,而静态方法在类加载时创建
  • 如果方法是一个工具,不是某个对象特有的,就可以声明 为静态

一个有意思的问题:构造器是静态方法吗?

在Java编程思想中认为构造器是静态方法,但构造器实际上不符合java对方法的定义,并且构造器有一个隐式参数:this,静态方法是不能有this的。

需要注意的是:Java语言中,实例构造器只能在new表达式(或者其他构造器,如子类构造器)中被调用(super()会调用父类构造器)

当继承了父类的子类实例对象(main方法在子类中)时,在执行main方法前,会先进行父类的类加载,然后进行子类加载,然后进入main方法,实例对象时会进入子类构造器,由于子类构造器会隐式的在第一行添加super(),所以会进入父类,但父类匿名代码块优先级更好,如果父类中有成员变量,则需要先初始化成员变量,之后才是父类构造器,之后回到子类执行匿名代码,最后才是子类构造器。

以下是先后顺序:

父类静态代码块
子类静态代码块
父类匿名代码块
父类构造器
子类匿名代码
子类构造器

面试题:

public class Test {
    Person person = new Person();
    static{
        System.out.println("test 静态代码块");
    }
    {
        System.out.println("Test的匿名代码块");
    }

    public Test() {
        System.out.println("test 构造器");
    }

    public static void main(String[] args) {
        new MyClass();
    }
}

class Person{
    {
        System.out.println("Person的匿名代码块");
    }
    static{
        System.out.println("person 静态代码块");
    }
    public Person() {
        System.out.println("person构造器");
    }
}


class MyClass extends Test {
    Person person = new Person();
    {
        System.out.println("MyClass的匿名代码块");
    }
    static{
        System.out.println("MyClass 静态代码块");
    }

    public MyClass() {
        System.out.println("MyClass 构造器");
    }
}
/*
test 静态代码块
MyClass 静态代码块
person 静态代码块
Person的匿名代码块
person构造器
Test的匿名代码块
test 构造器
Person的匿名代码块
person构造器
MyClass的匿名代码块
MyClass 构造器*/

静态代码块

static代码块就是静态代码块,可以有多个,不在任何一个方法体内,会随着类加载时执行一次。

public class TestStatic {
    public static int number;
    public int id;
    {
        System.out.println("匿名代码块");
    }
    static {
        System.out.println("静态代码块");
    }
    public TestStatic() {
        System.out.println("构造器");
    }
    public static void main(String[] args) {
        TestStatic t1 = new TestStatic();

    }
}
//执行顺序,匿名代码块每次都会在实例对象时执行一次,匿名代码块在构造器之前执行
静态代码块
匿名代码块
构造器

静态代码块的作用:优化性能

class Person{
  private Date birthDate;
  
  public Person(Date birthDate) {
    this.birthDate = birthDate;
  }
  
  boolean isBornBoomer() {
    Date startDate = Date.valueOf("1946");
    Date endDate = Date.valueOf("1964");
    return  birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
  }
}
//分析以上代码,会发现isBornBoomer()方法每次都会生成俩个对象,造成了空间的浪费
class Person{
  private Date birthDate;
  static{
    	Date startDate = Date.valueOf("1946");
   		Date endDate = Date.valueOf("1964");
  }
  public Person(Date birthDate) {
    this.birthDate = birthDate;
  }
  boolean isBornBoomer() {
    return birthDate.compareTo(startDate) >= 0&& birthDate.compareTo(endDate) < 0;
  }
}
//优化后的代码可以让所有对象共享这两个对象

匿名代码块

匿名代码块每次都会在实例对象时执行一次,匿名代码块在构造器之前执行

静态导包

加了static的导包语句,并在最后加上**.*** ,会将包中的所有静态方法导入到当前包,与非static导入不同的是,在类名不冲突的情况下,无需使用类名.方法名的形式调用类方法。

/* PrintHelper.java文件 */
package com.dotgua.study;

public class PrintHelper {

    public static void print(Object o){
        System.out.println(o);
    }
}
/* App.java文件 */
import static com.dotgua.study.PrintHelper.*;
public class App 
{
    public static void main( String[] args )
    {
        print("Hello World!");
    }
    /**Output
     * Hello World!
     *///~
}

总结

  • 只要类被加载了,那么从静态的字段、方法、代码块都会被以一同加载,并且只加载一次。之后可以直接调用。
  • static关键字的基本作用:方便在不创建对象的情况下调用字段、方法。
  • 静态变量的基本作用:对象之间共享值,方便访问变量
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值