JavaSE之static关键字

不积跬步无以至千里,不积小流无已成江海

一、认识static

我们在学习一个关键字时,首先要关注这个关键字有什么用?我们为什么要学习它?

这里给大家先介绍一下static都会出现在我们日常项目的哪些位置。

static主要会出现在工具类和测试类当中,这里讲一下什么是JavaBean类、工具类和测试类。

1、JavaBean类:用来描述一类事物的类,比如Student类、Teacher类、Dog类、Cat类等。

2、工具类:不是用来描述一类事物的类,主要是用来执行某种方法,像是Sum(求和一类的操作)类等。

3:测试类:测试一些类写的是否正确的,带有main方法的类,是程序的入口。

static关键字会有如下的使用场景。

1、静态变量:用static修饰过的变量被称为静态变量。

2、静态方法:用static修饰过的方法被称为静态方法。

3、main方法:这个我们学习过程中最常写的一个方法,必须要加这个关键字。

4、静态代码块:用static修饰过的代码块,被称为静态代码块(本文先不讲静态代码块,这个留到后续代码块章节详细讲)。

上述这些是我们编码时常见的使用情况,下面我给大家详细讲解一下,在static修饰下的代码会有哪些特点和作用。

二、使用static

1、静态变量

(1)特点

a、static修饰的成员变量,会被该类的所有对象共享。

b、static修饰的成员变量,不属于对象,属于类。

c、static修饰的成员变量,随着类的加载而加载、优先于对象存在。

(2)调用方式

a、static修饰的成员变量,可以使用类名调用(推荐)。

b、static修饰的成员变量,可以被对象调用。

(3)案例及原理

看了我上面总结的特点和调用方式,新学的小伙伴可能会问,这说的都是什么啊,说了又好像没说(来被八股文的可以跳过我讲的案例和原理,不过我感觉原理挺重要的,面试可能会问),所以我在这里给大家写个案例,并且附带一下原理讲解。

先看一下我写的案例代码:

学生类:

public class Student {
    private String name;
    private int age;
    public static String teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

学生测试类:

public class TestStudent {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("小明");
        student1.setAge(10);
        //使用对象调用
        student1.teacher = "麦芒疯狂生长!";

        System.out.println(student1.getName() + student1.getAge() + student1.teacher);

        //使用类调用
        Student.teacher = "JavaSe";
        Student student2 = new Student();
        student2.setName("小华");
        student2.setAge(11);

        System.out.println(student2.getName() + student2.getAge() + student2.teacher);

        Student student3 = new Student();
        student3.setName("小美");
        student3.setAge(10);

        System.out.println(student3.getName() + student3.getAge() + student3.teacher);
    }
}

 运行效果:

JavaSE之static关键字_第1张图片

 我写了一段非常简单的代码,想要说明关于static在声明静态变量时的特点、调用方式和原理。

static修饰的成员变量,会被该类的所有对象共享:

看我这个代码里的使用类调用那句代码,是不是一旦修改成teacher为“JavaSe”,下面的对象只要调用teacher这个成员变量都会变成JavaSe,其实这还不是最直观的,这里我建议大家去试一下,我将上图的部分代码改为我下面的代码,你再运行一下。

        //使用类调用
        Student.teacher = "JavaSe";
        Student student2 = new Student();
        student2.setName("小华");
        student2.setAge(11);
        student2.teacher = "1111";

运行结果:

JavaSE之static关键字_第2张图片

是不是这个结果就改变啦,就是我们正常的成员变量,以对象的方式赋值,它只会对所属的对象的属性值改变,而不是针对该类的所有对象,但是这个静态变量使用对象去调用,做出修改,作用的依旧是该类。 这也就是我们说的static修饰的成员变量,会被该类的所有对象共享。(原理部分我会结合这些特点一起讲)

static修饰的成员变量,不属于对象,属于类:

看了上述的代码实例和讲解,会很容易理解这一特点,当我们对象使用时,修改的是关于这一个类的成员变量,也就是说别的对象使用这个值时,也会跟着改变,所以静态成员变量不属于对象,属于类。

static修饰的成员变量,随着类的加载而加载、优先于对象存在:

这一特点我们用实例很难直观的看出,所以这里讲一下静态变量在程序运行时的原理:

当我们执行一个程序时,会先将main方法加入到栈内存,同时系统也会加载各个类,这个时候静态成员变量的特点就显示出来了,他会在堆内存里有一个固定的区,叫静态区。专门给静态变量加载时用的,保证在程序加载时,随着类一起加载进堆内存,也从下图可以看出,这时我们的对象还没有生成,所以这也就说明了为什么静态变量属于类,不属于对象。

JavaSE之static关键字_第3张图片

当我们的main方法开始向下执行,会创建对象,这时的存储结构,如下图所示,创建对象时会分配一块地址空间,以对象1为例,分配了0x12345这个地址,然后赋值给stu1,这时这个对象就可以向这块地址存东西了, 存非静态变量时,因为这些变量值最终会归于这个对象所有,所以也就会存在这个地址空间上,但是静态变量在使用时,从图中可以看出是共享的,所以就是使用地址区调用这个静态变量,去使用这个值。然后从这块的内存图以及我的讲解就能得出为什么static修饰的成员变量,随着类的加载而加载、优先于对象存在。

JavaSE之static关键字_第4张图片

 在知道原理后下面这两句就好理解了。

static修饰的成员变量,可以使用类名调用(推荐):因为静态变量存储在独立的静态区,并且随着类加载的,使用类调用更合乎常理。

static修饰的成员变量,可以被对象调用:看我们的实例可以发现对象调用也可以做到修改,所以说可以用,但是这种写法很怪,要是对静态变量充分理解后,一般不会这么写!

2、静态方法

(1)特点

a、static修饰的方法,多在测试类和工具类中。

b、static修饰的方法,JavaBean类中很少会用。

(2)调用方式

a、类名调用(推荐)

b、对象名调用

(3)案例及原理

大家其实不难看出静态方法和静态变量的调用方式很像,其实他们的原理是相似的,这里就是大致说一下原理。

静态方法:在程序运行期间只加载一次到内存中,无论创建多少个类的实例。这意味着静态方法的代码在内存中只有一份拷贝。静态方法的生命周期与应用程序的生命周期相同,从程序开始运行直到程序结束。

实例方法:每次创建类的实例时,都会为该实例分配内存空间,包括该实例的方法。虽然方法的代码本身在内存中只有一份拷贝,但是每个实例都有自己的方法调用栈和局部变量。普通方法的生命周期与实例的生命周期相关,实例被创建时方法可用,实例被销毁时方法不可用。

根据这个原理,我们可以得知静态方法和静态变量类似,在内存中有特定的静态方法区,所以用类调用更符合内存逻辑,不过对象也能调用,但是实例方法必须使用对象去调用。

下面给家看个案例,主要是为了让大家明白,静态方法有什么用。

先看代码,这是我接着之前的成员变量改编的

Student类(同静态变量案例一致)

StudentUtil类(工具类):

public class StudentUitl {
    public static void Speak(ArrayList list){
        for (Student student : list) {
            System.out.println(student.getName()+"对"+Student.teacher+"说早上好");
        }
    }
}

 StudentTest类(测试类)

public class TestStudent {
    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("小明");
        student1.setAge(10);
        //使用对象调用
        student1.teacher = "麦芒疯狂生长!";

        System.out.println(student1.getName() + student1.getAge() + student1.teacher);

        //使用类调用
        Student student2 = new Student();
        student2.setName("小华");
        student2.setAge(11);

        System.out.println(student2.getName() + student2.getAge() + student2.teacher);

        Student student3 = new Student();
        student3.setName("小美");
        student3.setAge(10);

        System.out.println(student3.getName() + student3.getAge() + student3.teacher);

        ArrayList list = new ArrayList<>();
        list.add(student1);
        list.add(student2);
        list.add(student3);
        StudentUitl.Speak(list);
    }
}

 运行效果:

JavaSE之static关键字_第5张图片

这个案例主要讲的是,每一个学生都要对老师讲一遍早上好,我们分析一下这个简单案例,这个早上好是不是只是个动作,而且我们是不是需要全体同学都做这个动作,所以这个时候很容易想对我们的工具类,因为使用一个工具类可以代替这一复杂的操作,就不用n个同学调用n次方法了(其实使用实例方法,写个数组循环,从代码角度上简洁了,但是还是调用n次方法,但是上面的案例就不会,都是在用同一静态方法)。 所以这就是静态方法的用处,当然我的例子简单,只是为了说明一些问题,如果遇到的复杂的动作,实际上抽象出来的静态方法更能体现价值!

3、main方法

在我们知道静态方法的执行原理后,我们再回头看这个main方法,可能就亲切多了,main方法使用staic关键字,主要考虑如下几点:

  1. 程序入口点:main方法是Java程序的标准入口点。当你运行一个Java应用程序时,JVM(Java虚拟机)会寻找包含 public static void main(String[] args) 签名的方法作为程序的启动点。使用static关键字允许JVM直接调用这个方法,而不需要创建类的实例。

  2. 无需对象实例:由于main方法是static的,它不需要创建类的实例就可以被调用。这意味着在程序开始执行时,不需要先创建对象,可以直接执行main方法。这在程序启动时非常有用,因为此时还没有创建任何对象。

  3. 内存管理:在main方法中,通常需要创建对象和使用类的方法。如果main方法不是static 的,那么在调用它之前,必须先创建一个对象实例。这将增加额外的内存开销,因为每次调用main方法时都会创建一个新的对象。使用 static方法可以避免这种不必要的内存使用。

  4. 调用其他静态方法和访问静态变量:在 main方法中,你可能需要调用其他静态方法或访问类的静态变量。如果main方法不是 static的,那么在不创建对象的情况下,你将无法调用这些静态方法或访问这些静态变量。使用 static关键字可以确保main方法可以直接访问类的静态成员。

  5. 兼容性和传统:从Java语言的早期版本开始,main方法就被设计为static的。这已经成为了Java编程的传统和标准,几乎所有的Java程序都遵循这一规则。

4、static使用的注意事项

(1)静态方法只能访问静态变量和静态方法

大家可以认为在程序加载时,静态变量和静态方法就已经随着加载了,所以静态方法能调用静态变量,这是一个很好理解的事情,但是在程序还没有执行到创建对象那一步,也就是说还没有一个实例存在,那么我们就没有一个非静态变量所存在的位置,这时我们用静态方法去调用非静态变量,这显然找不到东西啊,也就不可以了。

(2)非静态方法可以静态变量和静态方法,也可以访问非静态的成员变量和非静态的成员方法

首先,静态方法和静态变量一开始就随着类被加载了,所以非静态方法能调用,这很好理解,然后就是非静态方法调用非静态成员变量和非静态的成员方法,这其实也好理解,既然能用非静态方法,肯定是有实例存在了,那也就证明可以间接操作这些成员变量和方法了(其实我们平时写代码随处可见,可以说98%都是这么做的,不用细说)

(3)静态方法中没有this关键字

静态方法是属于类的,而不是属于某个对象实例的。它们在类加载到内存时就已经存在,与任何对象实例无关。因此,静态方法中没有与特定对象实例相关联的状态或行为。由于静态方法不依赖于任何对象实例,因此在静态方法中使用 this 关键字没有意义,因为 this 指向的是对象实例,而在静态方法的上下文中不存在这样的实例。静态方法在内存中只有一份拷贝,与任何对象实例无关。使用 this 关键字会暗示存在一个特定的对象实例,这与静态方法的设计意图相矛盾。

以上就是关于static的所有用法的详细介绍。

你可能感兴趣的:(JavaSE,java,开发语言)