【Java】static的注意事项

一、static的注意事项

  • 静态方法只能访问静态变量和静态方法
  • 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
  • 静态方法中是没有this关键字的

总结:

  • 静态方法中没有this关键字和super关键字的
  • 静态方法中,只能访问静态。
  • 非静态方法可以访问所有。

二、代码角度解释

1)代码

为了让大家更简单理解,下面用的就是最简单的JavaBean

Student.java

public class Student {
    String name;
    int age;
    static String teacherName;
    
    public void show1() {
        System.out.println(name + "," + age + "," + teacherName);
    }
    
    public static void method() {
        System.out.println("静态方法");
    }
}

StudentTest.java

public class StudentTest {
    public static void main(String[] args) {
        Student.teacherName = "阿玮老师"; // 对共享变量 teacherName 进行了赋值
        
        Student s1 = new Student();
        s1.name = "zhangsan";
        s1.age = 23;
        s1.show1();
        
        System.out.println("==============");
        
        Student s2 = new Student();
        s2.name = "lisi";
        s2.age = 24;
        s2.show1();
    }
}

首先将 StudentStudentTest 类都点开。

我们在观看的时候可以这样:右键点击上面测试类的选项卡,选择这里的 Split and Move Right(切割 and 移动到右边)。

image-20240411084232455

这样可以一左一右对比着看,相对来讲比较方便。

image-20240411084417215


2)静态方法中没有this关键字 的解释

1、非静态方法中的 this 调用成员变量

看到这句话能不能反过来理解:普通的非静态方法中有一个隐藏的 this

确实是这样的,那么它在哪呢?

在我们非静态方法的形参中,它有一个隐藏的 thisStudent this)。

要注意的是,这个 this 并不是我们调用方法手动赋值的,我们不能手动去给这个this赋值。这个this是在调用方法的时候,虚拟机给这个方法赋值的 —— 谁调用当前的方法,this 就表示谁的地址值。

// this:表示当前方法调用者的地址值。
// 这个this:可以把它理解成是一个变量,类型是当前这个类的类型。由于现在是 Student 类,所以现在这个 this 就是 Student 类型的。这个变量记录的就是调用者的地址值。
public void show1(Student this) {
    System.out.println(name + "," + age + "," + teacherName);
}

虚拟机是怎么给它赋值的?

在右边的测试类当中,对 show1() 方法调用了两次。

image-20240411085357119

在第一次调用的时候,虚拟机它就会把它的调用者 s1 赋值给这里的 this

第二次调用的时候,虚拟机它就会把它的第二个调用者 s1 赋值给这里的 this

image-20240411085702232

我们可以写代码去验证一下,在右边的测试类中创建完 s1s2 后分别打印出它们的地址。

在左边的 show1 方法中打印 this 的地址。要注意的是,第一次是 s1 调用的,第一次打印的应该就是 s1 的地址;第二次是 s2 调用的,第二次打印的应该就是 s2 的地址。

image-20240411085833482

右键运行看下效果,程序运行完后,可以看见两个地址都是一样的。

image-20240411090145852

正是因为 this 有了这样的特性,所以在这个方法里面,调用成员变量的时候,就可以区分不同的对象了。

又因为我们在调用成员变量的时候,其实每个成员变量前面都有一个隐含的 this

public void show1(Student this) {
    System.out.println(this.name + "," + this.age + "," + teacherName);
}

这样当你的 s1 调用 show1() 的时候,此时在 show1() 方法中打印的就是 s1names1age

第二次,通过s2 调用 show1() 的时候,此时在 show1() 方法中打印的就是 s2names2age

只不过我们平时在局部位置,也就是在方法里面没有跟成员位置重名,因此这里的 this. 其实是可以省略不写的,但是我们在这里写上也没有问题。


2、非静态方法中的 this 调用其他方法

show1() 方法中再写一个 show2() 方法。

public void show2() {
    System.out.println("show2");
}

此时我们要注意一点,在 show1 中调用其他方法,在之前我们直接写方法名就可以了

image-20240411091326113

但是前面它其实也有一个隐含的 this.,这个 this 就是拿着当前调用 show1 的那个对象,再来调用 show2

public void show1(Student this) {
    // 调用其他方法
    this.show2();
}

解释

第一次的时候,是 s1 去调用的 show1,所以说这里的 this 就被赋值成了 s1 的地址。这个时候再来看13行代码,既然 thiss1,那么13行这句话我能不能理解成:使用 s1 再去调用 show2,是可以的。

image-20240411092421605

第二次,它是拿 s2 去调用的 show1,因此这个 this 就表示的是 s2。下面的 this.show2() 方法就是表示拿着 s2 再去调用的 show2()

所以我们知道了,在这种普通的成员方法里面,它是含有一个隐含的 this 的。这个 this 在形参的最前面,这个东西我们不能自己赋值,如果我们强行给它赋值就会报错。

image-20240411092710907

它的赋值是方法在调用的时候,由虚拟机把调用者的地址值去赋值给它的。


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

在静态方法中的形参位置如果加上 this,就会报错,翻译过来就是:'com.itheima.a03staticdemo4.Student.this' 不能从静态上下文中引用。因此在静态方法中,是没有 this 关键字的。

image-20240411092949503


4、Java这样设计的原因

因为像这种非静态的东西,它往往是跟对象相关的,例如 show1() 方法里面的方法体,它打印出来的就是某个对象的 name、某个对象的 age,因此在这个里面它必须要有 this

image-20240411093351478

那静态的东西呢?静态的东西一般都是共享的,共享的它跟某一个对象是没有什么太大关系的,既然你跟某一个对象没有什么关系,因此Java在设计的时候,在静态方法里面就没有 this 关键字。

有了这样一个结论之后,后面的两句话就很好理解了。

image-20240411093712975

3)静态方法中,只能访问静态

这句话我们返回来理解就是:静态方法中不能访问非静态的东西。非静态的东西有两个:成员变量成员方法

我们现在就在静态方法中强行调用一下 成员变量成员方法

此时它就报错了,翻译过来就是:不能从静态上下文引用非静态字段 "name"

image-20240411093958255

报错的原因其实很简单,你在这打印 name,你是打印哪个对象的 name?如果需要表示是哪个对象的 name ,前面就需要有一个 this.,但是我们刚刚才说过,静态方法里面没有 this。因此静态方法不能调用非静态的成员变量。


同样,我们在静态方法中调用非静态成员方法 show1() 也是会报错的。

翻译过来就是:不能从静态上下文中引用非静态方法 "show1()"

image-20240411094727360

报错的原因其实也很简单,如果说我们想要调用其他的非静态的成员方法,在前面也有一个隐含的 this.,又因为静态方法中压根就没有 this,所以它也调不了。


4)非静态方法可以访问所有

这句话也很好理解,它在调用 static 修饰的成员变量 teacherName 的时候,也相当于它的前面有一个 this.

而静态有两种调用方式:1、类名调用(推荐);2、对象名调用

第二种使用对象名调用,也是可以访问到共享的数据的。因此这么去调用一点问题也没有。

image-20240411095515967

同样的在非静态方法 show1() 中调用非静态的 method 方法,也是必须可以的。

调用其他的方法,前面也有一个隐含的 this.,静态的成员方法是可以使用对象名调用的。

因此这种写法也是ok的。

image-20240411095912445


搞懂了从代码方面解释这三句话,那么从语法的角度也就很好理解这三句话了。

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

2、静态方法中,只能访问静态内容

3、非静态方法中,可以访问所有


三、内存角度解释:静态方法中,只能访问静态内容

1)前置认知

在讲解内存之前,需要大家现有一个认知,这块内存就好比是我们程序运行的内存,静态的数据跟非静态的数据加载到内存中的时机是不一样的。

静态:随着类的加载而加载。

非静态:跟对象有关系的。

例如我们刚刚写的 Student 里面的 name,你要想那个 name 什么时候有值,是不是我们在创建学生对象的时候它才有值。那如果我们没有创建对象,在内存当中就不会有那个 name

image-20240411101454050

所以说当静态加载到内存之后,如果说我们没有创建对象,这个时候内存中它是没有非静态的数据的。而静态的东西它是可以互相调用的,这个是没有问题的。

image-20240411101645321

但是非静态的东西是跟对象有关的,只要没创建对象,非静态的数据就不会出现在内存当中。因此静态是无法调用非静态的。

有了这个认知后,我们就从内存的角度去解释这个概念。


2)从内存角度解释

1、静态方法不能调用实例变量

也就是说,在静态方法中,它只能调用其他的静态方法和静态变量,是不能调用 非静态的方法非静态的成员变量 的。即静态方法不能访问非静态。

来看下面这段代码,首先在 Student 类中有一个成员方法 name,和一个静态变量 teacherName

再往下有一个静态方法 method,静态方法中调用了 nameteacherName。当然我们知道这种方式的调用是错的,所以 name 用红色标记了,因为在 IDEA 中,红色就表示 错误 的意思。

下面还有一个非静态的 show() 方法,里面同样的调用了 nameteacherName

在下面的测试类中,我们首先先用类名调用 teacherName ,对静态变量做了一个赋值。然后再调用静态方法 mathod()

接下来就来看一下右边的内存。

image-20240411102558159

在刚开始的时候,main方法首先肯定还是要进栈,这个是死套路。然后执行main方法中的第一行代码:Student.teacherName = "阿玮老师"

这行代码就用到了 Student,用到了 Student 类,就会把 Student 的字节码文件加载到方法区,在这个里面会有所有的成员变量成员方法

并在内存中创建了一个 单独存放 静态变量的空间,我们可以把这个空间叫做 静态区

因此当 Student 的字节码文件加载到方法区后,静态区 就出现了。

在JDK8以前,静态区是在方法区里面的。JDK8以后,就挪到了堆空间当中。

image-20240411104144025

现在在静态区中有一个静态的 teacherName,因此它就会出现在静态区当中。由于 teacherNameString,属于引用数据类型,它默认初始化值就是 null

image-20240411104237373

然后又因为,在第一行代码中,给 teacherName 做了一个赋值,此时 null 就会被 '阿玮老师' 这个字符串所替代。

image-20240411104301809

这个就是第一行代码。第二行代码 Student.method() 是调用了静态方法 method()Student.method 就会找到方法区中的 Student 字节码文件,然后找到里面的 method ,然后把 method 加载到栈中,在方法里面它要获取两个变量:1、name;2、teacherName

但由于当前的方法,是用 Student 这个类名去调用的,它就会到右边的 Student 类的静态区中找 nameteacherName。首先 teacherName 是可以找到的没有问题,但是 name 是找不到的,因为 name 不是静态的,既然不是静态的,就不会出现在静态区中。

通过这个我们就可以得出一个结论:静态方法不能调用非静态成员变量。

这种 非静态的成员变量 我们也称作 实例变量,这里的实例就是对象的意思。通过 实例变量 这个名字我们也能发现,这个变量是跟 对象 有关的,而现在在我们的代码中并没有对象,对象都没有,那怎么调用对象里面的示例变量呢?

image-20240411104947917


2、静态方法不能调用非静态的成员方法

例如,在左边的 method 方法中,我添加了一段 show() 方法,show() 方法是非静态的,所以这是一种错误的调用方式,这里也把它加上了红色标记,IDEA中红色就是错误的意思。

image-20240411105705638

那为什么不能调用呢?其实我们可以把它反过来理解,假设它可以调用

要注意的是,在刚刚我们说过,像这种普通的成员方法在调用的时候,它必须要有一个调用者

在方法里面打印的 name,实际上是调用者里面的 name。如果说我在静态方法 mehtod 里面,强行调用 show() 方法,这个时候它并没有调用者,没有调用 show() 方法的对象?是没有的。

那么既然这个对象都没有,那么下面的 name,它去哪找?没地找。

因此:在静态方法中同样的也不能调用非静态成员方法。

image-20240411110016796

到现在为止,我们就已经知道了,静态方法中不能调用非静态的原因。

所以我们在书写方法的时候需要知道这个结论:静态方法中只能调用静态的内容,不能调用非静态的。


四、内存角度解释:非静态方法中,可以访问所有

先来看一下这段代码。Student 类里面有两个成员变量 nameage,有一个静态变量 teacherName

静态方法 method,普通的成员方法 show()show() 方法里打印一个 name,再打印一个 teacherName

再往下,里面有一个测试类:创建一个 Student 对象,给 nameage 分别赋值,最后再去调用一下 show() 方法,最后再来看一下右边的内存图。

image-20240411110538304

在一开始的时候还是main方法先进栈,然后执行到 Student s1 = new Student();。现在用到 Student 类了,它首先将 Student 类的字节码文件加载到方法区,在这个方法区里面会有所有的成员变量,和成员方法。

并在内存中创建了一个 单独存放 静态变量的空间,我们可以把这个空间叫做 静态区

因此当 Student 的字节码文件加载到方法区后,静态区 就出现了。

在JDK8以前,静态区是在方法区里面的。JDK8以后,就挪到了堆空间当中。

而现在只有一个静态变量 teacherName,所以 teacherName 就被存放在了 静态区中,默认初始化值为 null

等上面这些工作做好之后,到这里才叫做加载字节码文件成功。

image-20240411111727369

加载完成之后,它才开始创建对象。等号的左边就是在栈中的 main方法 中定义了一个 s1 变量,等号的右边有 new 关键字,所以这里在堆中开辟了一个空间,假设这个空间的地址值是 0x0011,这个空间也就是我们平时所说的对象。

它里面存的是跟对象相关的,即所有的非静态的内容:nameage,默认初始化为 null0

最后再将 0x0011 赋值给 s1,这个才是创建一次对象。

如果我们想通过变量去访问静态也是可以的,我们能通过对象去访问到这个类中静态区中所有的内容。

image-20240411112159088

然后再往下,去给 name 赋值,它是把 "张三" 赋值给了 s1names10x0011,所以就是把 "张三" 赋值给 0x0011name,原来的 null 就被覆盖成了 "张三"

同理 23 赋值给 s1age,那么上面的 0 就被 23 给覆盖了。

image-20240411112452576

最后用 s1 调用 show(),这个时候要注意了,show() 方法是 s1 调用的,s1 是调用者,下面在获取 name 的时候,它获取的就是 s1name。并且通过 s1 也是可以找到 teacherName 的。

因此在控制台中就会打印 张三...null

image-20240411112643294

由上述的内存分析,静态的特点就是:随着类的加载而加载,字节码文件刚开始加载到内存的时候,这里的静态的 teacherName 在内存里面就已经存在了,它是优先于对象存在的。

因此在 show() 方法当中,是可以通过调用者 s1 是可以找到 静态区 中的静态变量 teacherName 的。


那么在普通的成员方法中,能不能调用静态方法呢?能不能找到这个 method() 呢?

其实也是可以的:现在 show() 方法是 s1 调用的,s1 会找到这边的对象,这个对象又是 Student 类型的,所以说它在调方法的时候,就会找到下面的方法区。方法区里面是有静态方法 method 的。

image-20240411113153369

既然找到了 method() 方法,那它就可以加载到内存当中,因此非静态的方法中可以调用所有的东西。


五、总结

static翻译过来就表示:静态的意思,它是Java中的一个修饰符,可以修饰成员方法,成员变量。

其中,被 static 修饰的成员变量叫做静态变量。它的特点就是这个变量被该类所有对象都共享。

并且这个静态变量跟对象是没有关系的,随着类的加载而加载,优先于对象存在在内存当中。

在调用的时候它有两种方式:1、类名调用(推荐);2、对象名调用。


其中,被static修饰的成员方法叫做静态方法。

它是多用在测试类和工具栏当中,在JavaBean中很少会去用,因为在实际开发当中,如果你需要在 JavaBean中去写静态方法,还需要集合到后面的一些知识才能解释,因此我们只需要记住这个结论就行了。

在调用的时候它有两种调用方式:1、类名调用(推荐);2、对象名调用。

但是我们推荐,只要是静态的,不管是变量还是方法,都用类名调用,这样更好一些。

image-20240411113909316


static注意事项

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

2、静态方法中,只能访问静态内容

3、非静态方法中,可以访问所有


这里是给有基础的同学看的,没基础的同学可以直接跳过一下内容。

有一些课程中在这里还会多讲单例设计模式,单例设计模式虽然说是用到了 static,但是它里面的核心亮点并不是 static,而是跟多线程相关的。只有在多线程里面才能把单例设计模式讲的非常的精彩。因此单例设计模式我们在static中先不讲,等后面学习到多线程的时候,再带着你慢慢去分析。


六、重新认识main方法

main方法我们每天都会写,现在我们已经能知道里面每个单词的意思了。

  • public :因为main方法是被JVM调用的,访问权限需要足够的大。

  • static:main方法是静态的,所以虚拟机在调用的的时候不需要创建对象,直接类名就可以调用了

    而且因为main方法是静态的,所以测试类中其他的方法也是需要用静态修饰。

  • void:表示方法的返回值,它表示main方法被虚拟机调用的时候,不需要给虚拟机做一个数据的返回

  • main:main是一个方法的名字,是Java规定程序主入口方法的名。

    它是一个通用的名字,虽然它不是关键字,但是只有main才能被虚拟机识别,如果你写成其他名字了,虚拟机是不认识的

  • String[] args:以前用于接收键盘录入数据的,现在是没有用的。

    但是Java为了上下兼容,将这个参数还是保留了。


七、以前接收数据的方式

在这需要带着大家再来读一下这里的参数:String[] args

在参数中看到一个 [],表示的是数组。

String 表示的是数组里面数据的类型,即数组里面只能存字符串。

args 是数组的名字。

public class Test {
    public static void main(String[] args) {
        // 看到数组我就有一种浑身不舒服的感觉,看到数组我就忍不住来遍历它
        //在遍历数组前先打印 args.length
        System.out.println(args.length); // 打印的结果长度为0,因为默认情况下,这个数组中是没有数据的
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
    }
}

打印的结果长度为0,因为默认情况下,这个数组中是没有数据的

在低版本的JDK中,args 是用来接收键盘输入的数据,但并不是在控制台中接收的,而是在IDEA右上角中按下图点击

image-20240411132743395

此时会弹出一个窗口,如下图,在 Program arguments(程序参数) 中,我们可以把要传递给程序的参数写在这个里面,数据之间要用空格隔开。

如果不隔开,全部写在一块,它就会把这个参数作为一个整体,交给 String[] args 数组,数组的长度就是 1

image-20240411132806666

例如我在这里传入 Hello World Java ,三个参数用空格隔开,0 索引 Hello1 索引 World2 索引 Java

image-20240411133100139

然后应用、保存一下。

image-20240411133325568

此时再来运行。此时数组的长度已经变成 3 了,刚刚的 Hello World Java 就已经传递给这个程序了。

image-20240411133358924

这种方式不需要大家掌握,了解一下就可以了,因为我们现在还想要键盘接收数据的话,直接用 Scanner 就好了,而刚刚学习的是一以前的接收方式。

  • 19
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 在Java中,static方法是没有this关键字的方法,它可以在没有创建任何对象的前提下,仅仅通过类本身来调用。\[1\]在static方法内部不能调用非静态方法,但可以调用其他的静态方法。这使得static方法成为执行一些只需要执行一次的初始化操作的好选择,可以提高程序的效率。\[2\] 另外,需要注意的是,只有在创建对象时才会执行该对象的构造方法和非静态代码块。\[3\]在继承关系中,父类的静态代码块会在子类的静态代码块之前执行,而非静态方法和构造方法则会在静态代码块之后执行。这是因为静态代码块在类加载时就会执行,而非静态方法和构造方法需要在创建对象时才会执行。 总结起来,使用static方法时需要注意以下几点: 1. static方法没有this关键字,只能通过类名来调用。 2. static方法内部不能调用非静态方法,但可以调用其他的静态方法。 3. 可以将一些只需要执行一次的初始化操作放在static代码块中执行,有利于提高程序的效率。 4. 静态代码块在类加载时执行,而非静态方法和构造方法需要在创建对象时才会执行。 #### 引用[.reference_title] - *1* *2* [【Java 基础】static 关键字的用法](https://blog.csdn.net/yuxiangdeming/article/details/125373125)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [java中的static属性详细介绍](https://blog.csdn.net/liu918458630/article/details/122474849)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值