面向对象基础02(static关键字)

本文详细介绍了Java中的static关键字,它用于声明静态成员变量和静态方法。通过实例展示了static如何节省内存和提高效率,比如在学生类中,所有学生共享同一所学校名称。静态变量存储在方法区的静态区,非静态变量存储在堆内存中。静态方法无需对象即可调用,而静态变量可通过类名或对象访问,但推荐使用类名。文中还给出了静态方法在工具类中的应用,如数组操作工具类。最后强调了静态方法不能直接访问非静态成员和使用this、super关键字的限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

static 关键字

static关键字是我们从”hello world“开始,就频繁使用的一个关键字

例如我们的main方法,例如我们一直在写的static方法

  • 但是我们经常用,却不知道为什么这么用
  • 那么今天就正式学习static关键字

  • 引例
练习:
	创建一个学生类,用来描述我们班全体同学,要求
		属性:姓名,性别,年龄,学号,学校信息
		行为:吃饭,学习
  • 建议性别用gender,禁止使用布尔类型的isMale,isFemale这种形式
  • 创建多个学生对象,想一想会有什么问题?

对于我们班级的同学来说,学校名字都是固定的“训练营”

我们虽然知道这一点,但是我们还是要,不厌其烦的在每次创建对象的时候,给schoolName赋值

这显然是没有必要的,浪费时间的同时也浪费内存空间

  • 怎么改进这个设计?

    • 可以把成员变量schoolName中直接初始化赋值为“训练营”,这样可以实现功能
    • 但是每个对象中都存有一个成员变量,浪费空间
    • 最重要的,我们在设计类的时候其实已经知道这个属性,是全体对象都有的
      • 我们希望这个属性不是属于某一个对象的,而是一种类的特征,是属于类的
  • 上面这个改进不是我们最希望的样子

    • 那么能不能在内存中找一块区域,每个对象都共用这片区域,把schoolName放进去?
    • 这样做既节省时间,又节省空间
    • 最重要的体现了该属性属于类——只要是这个类的对象都该有这个属性

在JVM内存的设计当中,确实在方法区中开辟了一块单独的空间,称之为静态区

用于存放这些 “所有对象共享,整个类的对象都只有一份的数据”

在Java当中把存放在这个区域的成员,称之为静态成员,包括静态成员变量和静态成员方法

语法:
	1,成员变量在数据类型前加static,静态成员变量或者简称静态变量
	2,成员方法在返回值类型前加static,是为静态成员方法或者简称静态方法
  • 修改一下schoolName为static修饰,验证一下该成员是否成为所有对象共享
public class Demo {
    public static void main(String[] args) {
        //创建几个学生类对象
        Student s1 = new Student();
        Student s2 = new Student();
        Student s3 = new Student();
        /*s1.schoolName = "训练营";
        s2.schoolName = "训练营";
        s3.schoolName = "训练营";*/

        //写了很多重复代码,怎么改进?
        /*不推荐的访问方式
        System.out.println(s1.schoolName);
        System.out.println(s2.schoolName);
        System.out.println(s3.schoolName);*/
        System.out.println(Student.schoolName);
        //Static member 'com.cskaoyan.javase.oop.statickeyword.introduction.Student.schoolName' accessed via instance reference
        //被访问 通过 实例=对象 创建对象的过程也被称之为实例化 引用
        //警告:一个静态的成员通过一个对象引用去访问了
        //所以不应该用对象访问静态成员

        System.out.println("-----------------------------");
        Student.schoolName = "C++训练营";
        /*
        不推荐的访问方式
        System.out.println(s1.schoolName);
        System.out.println(s2.schoolName);
        System.out.println(s3.schoolName);*/
        System.out.println(Student.schoolName);
    }
}

class Student {
    //定义属性 姓名
    String name;
    //定义性别
    String gender; //sex可以表示性别,开发中使用gender
    int age;
    int id;
    //String schoolName = "训练营";
    static String schoolName = "Java训练营";
    
    //定义行为,成员方法
    //eat
    public void eat() {
        System.out.println("吃饭的时候也是要想着学习!");
    }
    //学习
    public void study() {
        System.out.println("疯狂的学习");
    }
    //睡觉
    public void sleep() {
        System.out.println("梦中学习大法");
    }
    public Student() {
    }
}
Java训练营
-----------------------------
C++训练营
  • 很容易就可以验证出这一结果,那么接下来,我们结合我们的内存图去深入的学习static关键字

接下来我们通过一个案例来深入了解static关键字

宠物公司新进了一批同样颜色的宠物,创建一个类描述这批宠物

  • 属性:颜色,性别
  • 行为:(static)跑,跳
public class Demo {
    public static void main(String[] args) {
        //创建Rabbit对象
        Rabbit r = new Rabbit();
        r.jump();//对于没有用static关键字的成员方法,必须new一个对象来调用
        Rabbit.run();//对于有static关键字的成员方法,可用类直接调用
    }
}
class Rabbit{
    //定义属性
    static char color;
    String gender; //isMale/isFemale的布尔类型不要使用
    //定义行为
    public static void run(){
        System.out.println("和乌龟一起跑!");
    }
    public void jump(){
        System.out.println("蹦蹦跳跳真可爱!");
    }
}

输出:

蹦蹦跳跳真可爱!
和乌龟一起跑!

r.jump();//对于没有用static关键字的成员方法,必须new一个对象来调用

这种访问方式不推荐

> 总结static

  • static修饰成员,称之为静态成员,包括静态成员变量和静态成员方法
  • 随着类加载完毕,静态成员就存在,并且能够使用了
    • 静态成员变量在类加载过程中完成初始化,并且具有默认值
    • 静态成员方法的二进制指令集合在类加载过程也准备完毕,可以调用了
  • 静态成员领先于对象存在,不依赖于对象而存在
    • 静态成员变量在创建对象之前就已经创建了,只有一份且被类所有对象共享
    • 静态方法被所有类对象共享且无需创建对象就能调用
  • 仍然可以创建对象去调用静态成员,但是规范的Java代码只建议通过类名直接访问
    • 静态成员变量:类名.变量名
    • 静态成员方法:类名.方法名
    • 对象调用的方式,好像该成员属于对象,但显然静态成员属于全体对象,属于类
  • 在很多书籍博客中,由于静态成员的随着类加载而存在的特点
    • 静态成员是该类全体对象共有,属于类
    • 所以也称类成员,类属性,类变量,类方法
    • 知道这种称呼即可,但是大家还是习惯叫静态成员

>比较静态成员变量普通成员变量

  • 所属不同
    • 静态成员变量属于,所以也称为为类变量
    • (普通)成员变量属于对象,所以也称为对象变量(对象变量)
  • 在内存中的位置不同
    • 静态变量存储于方法区的静态区,被所有对象共享
    • 成员变量存储于堆内存,每个对象独享自己的成员变量
  • 在内存中出现时间不同
    • 静态变量随着类的加载而加载,比成员变量出现的要早
    • 成员变量随着对象的创建而存在
  • 调用不同
    • 静态变量可以通过类名调用,也可以通过对象调用(不推荐/ 不合理的方式)
    • 成员变量只能通过对象名调用,必须创建对象

静态成员方法和普通成员方法的区别和上述类似

> 那么我们使用static的场景是什么?

  • static的使用场景
    • 静态成员变量:当存在需要所有对象共享变量时,应该使用static
    • 静态成员方法:当不需要对象只是需要便捷的调方法时,使用static,广泛应用于工具类中,方便访问调用
      • 数组工具类
  //使用工具方法
public class Tools {
    public static void main(String[] args) {
        int[] a = {2, 7, 8, 5, 43, 3, 76, 8, 4};
        int max = ArrayTool.max(a);
        ArrayTool.max(a);
        ArrayTool.min(a);
        System.out.println("最大值为:" + max);
        int min = ArrayTool.min(a);
        System.out.println("最小值为:" + min);
        int firstindex = ArrayTool.find(a, 3);
        System.out.println("找到第一个位置为:" + firstindex);
        int endindex = ArrayTool.findEnd(a, 8);
        System.out.println("找到最后一个位置为;" + endindex);
        ArrayTool.reverse(a);
        System.out.println("逆转后排序:");
        System.out.println(Arrays.toString(a));
    }
}

class ArrayTool {
    public static int max(int[] a) {
        int max = a[0];
        for (int i = 0; i < a.length; i++) {
            if (max < a[i]) {
                max = a[i];
            }
        }
        return max;
    }


    public static int min(int[] a) {
        int min = a[0];
        for (int i = 0; i < a.length; i++) {
            if (min > a[i]) {
                min = a[i];
            }
        }
        return min;
    }

    //逆值数组元素
    public static void reverse(int[] a) {
        int temp;
        for (int i = 0; i < a.length / 2; i++) {
            temp = a[i];
            a[i] = a[a.length - i - 1];
            a[a.length - i - 1] = temp;
        }
    }

    //查表(第一个元素出现的位置)
    public static int find(int a[], int value) {
        for (int i = 0; i < a.length; i++) {
            if (a[i] == value) {
                return i;
            }
        }
        return -1;
    }

    //查表(最后一个元素出现的位置)
    public static int findEnd(int a[], int value) {
        for (int i = a.length - 1; i >= 0; i--) {
            if (a[i] == value) {
                return i;
            }
        }
        return -1;

    }

}

输出:

最大值为:76
最小值为:2
找到第一个位置为:5
找到最后一个位置为;7
逆转后排序:
[4, 8, 76, 3, 43, 5, 8, 7, 2]

> 注意事项:

  • 一个类中,静态方法无法直接调用非静态的方法和属性,也不能使用this,super关键字
    • 经典错误:Non-static field/method xxx cannot be referenced from a static context
    • 原因:静态方法调用的时候,可能还没有对象,直接访问属于对象的成员变量和成员方法显然不合适
  • 反过来,一个非静态的方法,可以访问静态的成员
    • 因为有对象的时候,一定有静态成员
    • 建议采用这种访问形式的时候,使用类名.变量名的形式访问,以示区别,增加代码可读性
  • 只存在静态成员变量,不存在“静态局部变量”
    • 局部变量只有在调用的时候才有意义
    • 而静态变量在类加载时就初始化,就存在了
    • 如果我一直不调用这个方法,这个“静态局部变量”就一直占着空间,没有意义
  • 静态方法是类所有,那么静态方法的局部变量就也是类所有,为什么静态方法中也不能有静态局部变量?
    • 局部变量一定是方法所有
    • 静态方法也是方法,不调用其中的局部变量也没意义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值