写点什么

Java 之 static 关键字【实例变量与类变量、实例方法与类方法】

作者:Fire_Shield
  • 2022 年 9 月 19 日
    浙江
  • 本文字数:4389 字

    阅读完需:约 14 分钟

Java之static关键字【实例变量与类变量、实例方法与类方法】

Hello 大家好,开始更新有关 Java 的文章,第一篇就是关于 static 关键字的,个人认为这个 this 关键字是 Java 中蛮重要的,一定要清楚地掌握才可以:mortar_board:


@TOC

一、static 关键字是什么?

static 是静态的意思,是一个修饰符,就像是一个形容词,是用来形容类,变量,方法的。在声明 static 关键字时,可以在前面加上 static 修饰,用 static 修饰的成员变量称做类变量(static 变量、静态变量)

二、加了 static 关键字有什么用?

  • 被 static 关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问

  • static 还有一个很关键的作用就是可以通过 static 来构建一个静态代码块,在这个静态代码块中,你可以将一些变量初始化,在程序加载的时候 static 代码块是优先加载的,因此可以说成是用来==形成静态代码块以优化程序性能==,【这一块我会在下一篇文章中具体讲到】

三、static 静态变量和实例变量

说了一些 static 关键字的基本理念和它的作用,空谈不行,要将其应用到实例化代码中,接下来讲讲静态变量和实例变量的声明

1、两种变量的声明

  • 比方说我们下面定义一个 System 类:christmas_tree:


class System{    int name;    //姓名    int num;    //编号    static int onlineNumber;  //在线人数}
复制代码


  • 可以看出,这里的姓名和编号都是属于普通的实例成员变量,但是对于在线人数 onlineNumber,前面却加了 static 关键字,表明其是一个静态变量


==注意:只要成员变量可以用 static 关键字修饰,普通的局部变量不可以,是非法的==

2、两种变量的区别

  • 对于实例变量,改变其中一个对象的实例变量不会影响其他对象的实例变量,因为它们在堆内存中是一块块独立的区域,各自有各自的内存,是互不相干的;但是你通过任何对象去修改一个 static 修饰的成员变量,都可以将其修改,改变其中一个对象的类变量会影响其他对象的这个类变量,通俗一点来讲就是==对象共享类变量==

  • 在 Java 中,当程序执行的时候,类的【字节码文件】会被加载到内存中,如果类没有创建对象,则类的成员变量不会被分配内存,但对于类变量,也就是静态变量,它有在==堆中有一个专属的静态变量区==,,当 JVM 虚拟机将字节码加载到内存时,就会为这个 onlineNumber 在线人数在堆区中分配出一段空间用于放置:bulb:

  • 当你去通过类定义对象的时候,这个时候才会在堆内存中为不同的对象分配不同的内存空间,上面我们说过,==所有对象的类变量都是相同的一块内存空间==,光这么说太抽象了,我们来看个

【堆栈内存原理分布图】


  • 然后在配合一个示例,大家可以先对照着我前面讲的把这个逻辑理一遍:mag:

具体案例

public class User {    //static修饰的成员变量,内存中只有一份,可以被共享    public static int onlineNumber = 16;    //静态成员变量,直接用类名访问
//实例成员变量 - 用对象访问 private String name; private int age;
public static void main(String[] args) { //直接用类名访问,不用对象 //1.类名.静态成员变量 System.out.println(User.onlineNumber);
//2.对象名.实例成员变量 User u = new User(); //System.out.println(User.name); 不能用类名去访问实例成员变量 u.name = "张三"; u.age = 21; System.out.println(u.name); System.out.println(u.age); System.out.println("-----------"); u.onlineNumber++; //新来了一个人
User u2 = new User(); u2.name = "李四"; u2.age = 22; System.out.println(u2.name); System.out.println(u2.age); System.out.println("-----------"); u2.onlineNumber++; //体现了共享的特点

System.out.println(u.onlineNumber); System.out.println(User.onlineNumber); System.out.println(onlineNumber); //同一个类中静态成员变量的访问可以省略类型 }}
复制代码


这个是运行结果



  • 好,我们继续回来,通过这个案例我们可以很清楚的看到,类的实例变量和静态变量在调用和分步时是如何实现的。每个对象都指向堆区中的一块内存地址,每一块内存地址都是独立的,互不干扰,但是后面可以看出,两块不同的堆区内存均指向同一块静态变量区的地址,这就很好地印证了我上面说的那一点,==所有对象的类变量都是相同的一块内存空间==

  • 从运行结果可以看出,通过下面两条语句,看运行结果,都可以实现在线人数的递增,这就印证了==对象共享类变量==这句话:book:


u.onlineNumber++;u2.onlineNumber++;
复制代码


  • 有一点要注意的是这句代码是不可行的,上面讲到过,当还没有通过类去构建对象的时候,是不会在堆内存中为其分配空间的,也就是说,这个时候堆内存中完全没有 name 这个变量,那你去直接用类名访问的话,是在访问哪个对象的姓名呢?


//System.out.println(User.name); 不能用类名去访问实例成员变量
复制代码


  • 好,我们讲本小块的最后一个知识点,就是如何去访问这个静态成员变量,一共是有下述==三种方法==,这三种方法就对应最后的这三句代码①类名.静态成员变量(推荐)②对象名.静态成员变量(不推荐)③同一个类中,访问静态成员可以省略类名不写


 System.out.println(u.onlineNumber); System.out.println(User.onlineNumber); System.out.println(onlineNumber); //同一个类中静态成员变量的访问可以省略类型
复制代码

四、static 静态方法和实例方法

1、两种方法的声明

//静态方法 - 实现通用功能public static int getMax(int x,int y){    return x > y ? x : y;}
//实例方法 - 直接访问对象的实例成员public void speak(){ name = "张三"; System.out.println(name + "在说话");}
复制代码


  • 通过以上代码,相信大家对静态方法和实例方法有了一个基本的认识,在声明方法时候如果你想要实现一个通用功能,那就用静态方法;但如果你想要直接访问对象的实例成员,则用实例方法


==注意: static 关键字需要放在方法的类型之前,而不可以乱放置;而且不可以用 static 修饰构造方法==

2、两种方法的区别

接下来说一说两种方法之间的差异性:open_file_folder:


  • 上面在说到类加载时的字节码文件会被加载到内存中,而方法中存在一个入口地址,类的实例方法不会被分配入口地址,只有当该类创建了对象后,类中的实例方法才会被分配入口地址。而且当你创建了一个对象之后再继续创建对象时,不会再为实例方法分配入口地址,通俗地说就是,==方法的入口地址所有对象共享==

  • 那如何将变量和方法联系起来呢,接下来我们就来说说

  • 在实例方法中你不仅可以操作实例变量,而且可以操作类变量,也就是 static 变量

  • 对于实例方法,它还可以调用类中其他的实例方法和类方法(不包括构造方法)


public static void main(String[] args) {    //1.类名.静态成员方法    System.out.println(Student.GetMax(10, 30));    System.out.println(GetMax(50, 99));     //可不用类名
//study(); 需要对象名去访问 //2.对象名.实例方法 Student st = new Student(); st.name = "孙悟空"; st.study();
//3.对象名.静态成员方法(语法可行,但是不推荐) System.out.println(st.GetMax(1,9));}
复制代码


  • 从上述代码我们可以看出静态成员方法和实例成员方法的调用格式,对于静态成员方法,它的格式有两种,我们也是推荐第一种,因为系统也是支持第一种调用方法,

  • ①类名.静态成员方法 ②对象名.静态成员方法(语法可行,但是不推荐) 对于这个,大家可以记住一句话

  • ==对于静态的东西,直接用类名调用即可,不会出错==

  • 而对于这个实例成员方法,则只能用对象名去调用,因为其是属于对象的一部分,和实例成员变量一样,不可以用类名去调用,更不能直接调用,因为具体的对象其所对应的具体方法,内部的成员变量 也是相互对应的

【内存原理剖析】

下面是一句代码的内存结构图,可以很形象地展示出用类名调用类方法的过程:triangular_ruler:



  • 从下面这张图可以看出,对于同一类中直接访问静态方法,它会找到方法区中的 getMax()方法,然后访问输出,但是对于实例方法,只可以用定义好的,实际存在的对象去访问,已经加载完之后会归属到对象中,因此会通过对象的引用去访问方法区中对应的 study()实例方法,打印出来的就是孙悟空在做什么,就体现了==实例化==这个特性

五、static 关键字注意事项

1、静态方法只能访问静态的成员,不可以'直接'访问实例成员 2、实例方法可以访问静态的成员,也可以访问实例成员 3、静态方法中是不可以出现 this 关键字的


我们到具体的代码中来看


package com.itheima.d1_static_field;//static 访问注意事项:
public class Test3 { public static int OnlineNum = 100; String name;
//1.静态方法只能访问静态的成员,不可以'直接'访问实例成员 public static void gg() { System.out.println(OnlineNum); System.out.println(Test3.OnlineNum); // System.out.println(name); //不可以直接访问实例成员
Test3 t3 = new Test3(); System.out.println(t3.name); //但是可以创建临时变量来间接访问 }}
复制代码


  • 从中可以看出,对于静态方法,可以去访问静态变量,但是不可以访问实例成员变量,如果一定要访问的话,可以去构建一个当前类的对象,因为实例成员变量只能通过对象去访问




public static void hold(){    System.out.println("====ff====");}//2.实例方法可以访问静态的成员,也可以访问实例成员public void run(){    OnlineNum = 200;    hold();             //体现了静态变量和静态方法共享的特性    name = "张三";    System.out.println(this);       //可以出现在实例方法中}
复制代码


  • 上述代码可以看出,对于实例方法,不仅可以访问实例成员变量,还可以访问静态成员变量,因为静态成员变量是共享的。不仅如此,静态方法也可以访问,也就是 hold()这个方法,很好地体现了==静态变量和静态方法共享的特性==


//3.静态方法中是不可以出现this关键字的public static void ff(){    //System.out.println(this);   //this代表当前对象}
复制代码


  • 最后一点,就是对于静态方法中不可以出现 this 关键字,因为 this 代表的是当前对象,静态方法中是可以不用声明实例对象的,但是 this 可以出现在实例方法中,就如上一个注意事项中的最后一句代码,this 代表正好是这个对象所对应的地址,就可以访问其所对应的成员变量

六、总结与回顾

好的,我们本文的内容就讲到这里,关于 static 在 Java 中的基础知识,通过这篇文章,相信大家已经有所收获,如果还觉有些模糊,可以自己去编译器中运行一下(我用的 IDEA),感受一下这个关键字的魅力


有关 static 关键字的应用知识会在下篇文章讲到,如果想知道记得关注我哦,本文如果有任何疑问,可以于评论区或私信留言,感谢您的观看:rose:


发布于: 刚刚阅读数: 6
用户头像

Fire_Shield

关注

语言观决定世界观 2022.09.02 加入

高校学生,热爱编程,喜欢写作

评论

发布
暂无评论
Java之static关键字【实例变量与类变量、实例方法与类方法】_Java_Fire_Shield_InfoQ写作社区