【JavaSE-10】详解static关键字

1、再谈学生类

在 java 中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。

例子:

class Student {
    //属性:姓名 年龄
    private String name;
    private int age;
    public String classRoom;
    //行为:学习 吃饭
    public void study(){
        System.out.println("学生学习...");
    }
    public void eat(String name){
        this.name = name;
        System.out.println(name+"吃饭...");
    }
    //封装方法
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    //构造方法
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
}
public class Test01 {
    public static void main(String[] args) {
        Student stu1 = new Student("小明",15);
        Student stu2 = new Student("小张",20);
        Student stu3 = new Student("小李",30);
    }
}

打断点,然后使用IDEA调试可以发现如下图所示:

【分析】:我们通过学生类,创建了3个对象,然后每创建一个对象,都会在堆中申请一块内存,用于存储成员属性,也就是name age classRoom,但是如果说遇到情况:3个对象的classRoom值需要相同,那还是需要每个对象在申请的内存空间里存储属性变量classRoom就比较浪费内存,这个时候就可以使用static关键字去修饰该成员属性。

class Student {
    //属性:姓名 年龄
    private String name;
    private int age;
    public static String classRoom;
    //行为:学习 吃饭
    public void study(){
        System.out.println("学生学习...");
    }
    public void eat(String name){
        this.name = name;
        System.out.println(name+"吃饭...");
    }
    //封装方法:
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    //构造方法
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
}
public class Test01 {
    public static void main(String[] args) {
        Student stu1 = new Student("小明",15);
        Student stu2 = new Student("小张",20);
        Student stu3 = new Student("小李",30);
        stu3.classRoom = "109";
    }
}

打断点,然后使用IDEA调试可以发现如下图所示:

这个时候,成员变量classRoom就不属于每个对象,而是属于

此时,对于这种类型的成员变量的调用,就不再仅仅是之前的方式:先通过类实例化一个对象,然后对象名.成员属性;而且还可以是类名.成员属性(常用),如下图所示:

2、static修饰成员变量

static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象共享的

如果没有经过static修饰,访问方式就是先实例化一个对象,然后是对象名.成员变量(属性)


如果有static修饰,则不依赖于对象,访问方式是类名.静态成员变量

也可以使用对象访问,但是静态成员变量是所有实例化对象共享的,所以不推荐。


【特性】

  1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的内存空间中,在静态方法区中。
  2. 既可以通过对象名访问,也可以通过类名访问,一般推荐类名访问
  3. 生命周期伴随类的一生(即随类的加载而创建(先于对象的创建),随类的卸载而销毁)。
  4. static不能修饰局部变量
  5. 如何判断是否使用static:即如果这个东西需要共享,这个时候就使用static修饰。

3、static修饰成员方法

因为封装的实现,所以一般成员属性是用private修饰,成员方法是用public修饰,如果是静态成员属性呢?也就是说成员属性用private static修饰,那么这种情况下是否可以正常访问这个成员属性?

例子1:

创建学生类

public class Student {
    private String name;
    private String gender;
    private int age;
    private double score;
    private static String classRoom = "412";
}

创建测试类

public class StudentDemo {
    public static void main(String[] args) {
        System.out.println();
    }
}

在上述介绍中,调用静态成员变量的方法是类名.成员变量名;但是此时利用这种方法调用时,发现没有对应的静态变量,如下图所示:

这是因为此时的静态成员变量是被private修饰,和普通成员变量相同,为了保证封装的特性,所以不能直接访问,所以需要创建其对应的getset方法,才能在其他类中调用使用。

Student类中增加代码:

public static String getClassRoom() {
    return classRoom;
}
public static void setClassRoom(String classRoom) {
    Student.classRoom = classRoom;
}

此时在测试类中在调用则发现可以输出静态变量的值


上述通过static修饰的成员方法就被称为静态方法。

4、静态方法的使用场景

可以用在测试类和工具类中,在javabean类中很少使用

  • javabean类:用来描述一类事物的类,如:Student Teacher Dog Cat
  • 测试类:用来检查其他类是否书写正确,带有main方法的类,是程序的入口。
  • 工具类:不是用来描述一类事物的,而是帮我们做一些事情的类。

工具类注意点:

  • 类名见名知意
  • 私有化构造方法(因为工具类是做一些事情的,不是描述事物的,例如Student类,创建这个对象表示一个学生,有意义;如果创建一个printArr类,用来打印数组,创建这个对象是没有意义的,所以私有化构造方法)
  • 方法定义为静态方法,方便调用,可以使用类名进行调用。

5、总结

  • 静态方法中,只能访问静态,即静态变量和静态方法。
    • 这就是为什么main函数中访问其他方法时,其他方法是static修饰的。
  • 非静态中可以访问所有,即可以访问静态变量或静态方法,也可以访问非静态的成员变量和非静态的成员方法。
  • 静态方法中没有this关键字
  • 非静态的东西一般和对象有关,而静态的东西都是类共享的,不是单独属于类的

5.1、代码方面理解

  1. 静态方法中没有this关键字:

在面向对象章节中我们讲过this本身代表的是当前对象的应用,我们可以把this看成一个变量名,存储的是当前对象的地址值,也就是哪个对象调用方法,那么方法的参数列表其实默认有这个this,同时变量类型就是这个类。

如下所示:

创建学生类Student

public class Student {
    String name;
    int age;
    static String teacherName;

    public void show1(){
        System.out.println(name + "," + age
                + "," + teacherName);
        System.out.println("this的地址值是:" + this);
    }
    //静态方法
    public static void method(){
        System.out.println("静态方法");
    }
}

创建测试类StudentDemo

public class StudentDemo {
    public static void main(String[] args) {
        //调用静态成员变量:类名.成员变量名
        Student.teacherName = "熊大";

        Student student1 = new Student();
        System.out.println("student1的地址值是:"+ student1);
        student1.name = "zhangsan";
        student1.age = 20;
        student1.show1();

        System.out.println("===================");

        Student student2 = new Student();
        System.out.println("student2的地址值是:"+ student2);
        student2.name = "lisi";
        student2.age = 30;
        student2.show1();
    }
}

【分析】

【运行结果】

student1的地址值是:staticDemo3.Student@6d311334
zhangsan,20,熊大
this的地址值是:staticDemo3.Student@6d311334
===================
student2的地址值是:staticDemo3.Student@3feba861
lisi,30,熊大
this的地址值是:staticDemo3.Student@3feba861

可以发现this的地址值和当前对象的地址值是一样的,也就是说其实this表示的就是当前对象的地址值。

那么这个代码可以写成如下所示:

而如果是在本类的非静态方法show1中调用其他非静态方法,其实本质也是使用this来进行确认是哪一个对象进行调用,只不过我们平常不写这个this。如下所示:

但是如果是在静态方法中写this,则会报错,如下所示:

至于为什么?我们可以这样理解:

非静态的东西一般都是属于对象,也就是说哪一个对象调用,这个东西就是属于哪个对象;但是静态的东西,是属于整个类,而不是单属于这个对象,但是this表示的是当前对象的引用,本质是和对象有关,所以不能在静态方法中使用。


  1. 静态方法中,只能访问静态
    1. 静态方法中不能调用非静态变量:因为调用非静态变量时,是和对象有关,这个时候其实需要指定this来表示这个非静态变量来自哪个对象,但是在静态方法中没有this关键字,所以说,静态方法中不能调用非静态变量。
    2. 静态方法中不能调用非静态方法:因为如果想要在本类中的方法里调用其他非静态方法,其实在方法前隐含着this关键字,但是静态方法中无this,所以不能访问。
  2. 非静态中可以访问所有
    1. 非静态中访问非静态的成员变量或非静态的成员方法:这是显然的,可以通过this来完成访问。
    2. 非静态中访问静态的成员变量或静态的成员方法:也是可以的,因为静态成员或方法的访问可以用两种,一种是类名访问,另一种是对象名访问,而对象名访问借助的就是this。如下所示:

5.2、内存方面理解

  1. 静态方法中,只能访问静态。

首先我们要知道:jdk7以前,不论是静态的还是非静态的成员方法或成员变量,都是存在方法区中,jdk7之后,静态变量存储在堆的静态区中

【分析】

第一步:从main函数开始运行代码,执行代码Student.teacherName = "熊大";,这个时候涉及到类Student,所以会将类相关的字节码文件加载方法区中,同时将静态变量加载到堆内存的静态存储区,默认初始值为null,之后将熊大赋值给静态变量。

第二步:执行代码Student.method(),这个时候会加载method方法到栈内存中,创建一个属于method方法的栈帧,而因为方法的调用者是Student,所以这个时候在访问方法method中的变量时,会寻找属于类Student的变量,而此时只有静态变量teacherName属于这个类,只能访问它,是不能访问成员变量name的,所以说静态方法中不能访问非静态变量。


同样的,如果在静态方法method中加入调用非静态方法show,那么如果将方法show加载到栈内存时,这个时候是谁调用这个方法show?因为静态方法中没有this,所以无法判断,那么怎么访问show里的变量呢?也不能访问,所以说静态方法中也不能访问非静态方法。

如果真的想访问非静态变量怎么办?这个时候我们可以在静态方法中创建一个对象,利用这个对象就可以访问到这个成员方法了。

如下所示:

所以这就是为什么在main函数中调用非静态的成员属性或成员方法的时候,需要先实例化一个对象,通过对象名.成员方法(属性)去调用;因为main函数是static修饰的静态方法,它是不能直接调用非静态的成员变量或成员方法的。

  1. 非静态中可以访问所有。

【分析】

第一步:从main函数中开始执行代码Student stu = new Student();前,这个时候将Student类的字节码文件加载到方法区中,同时将静态变量加载到堆内存的静态存储位置,并且初始化静态变量为null

第二步:执行new Student();,即在堆内存中申请一片内存空间,用来存储类Student的成员变量,同时可以这个对象访问到静态变量所在的静态区;然后将内存地址赋值给stu

第三步:执行代码stu.name = "zhangsan";stu.age = 23;,更新成员变量值。

第四步:执行代码stu.show(),这个时候,会在栈空间中申请一片属于方法show的内存,因为调用方法是stu,即是一个对象,那么访问这个方法里的变量时,则会访问对象中的变量;即show方法中要打印name teacherName,那么就去看stu这个对象所申请的内存空间中是否可以找的到这两个变量,显然,可以找到。所以说非静态中可以访问静态变量

第五步:执行完方法show的第一句代码,然后执行代码method();,这个时候因为是对象stu调用的这个方法,所以还是去这个对象所在的内存空间找方法;借助stu的内存空间,然后可以去访问方法区,这个时候方法区中属于Student类的字节码文件中有方法method,所以可以访问,即非静态中可以访问静态方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿宾爱干饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值