自学内容网 自学内容网

Java初学-8-1--类变量和类方法(静态变量和静态方法)

类变量:

在Java中,类变量是一种特殊的静态变量,它属于类本身而不属于类的任何特定实例。这意味着无论创建了多少个该类的对象,类变量只有一个副本存在于内存中,并且这个副本对所有对象都是共享的。类变量通常用于需要在多个对象之间共享信息或状态的场景。

类变量的重要概念和特征:

声明:类变量使用static关键字在类中声明。即这个变量是与类相关联的,而不是与类的实例相关联的。

存储位置:类变量存储在方法区中,这是JVM的一个区域,用于存储类的信息、常量、静态变量等。当类加载到JVM时,类变量就会被初始化并分配内存。

初始化:类变量在类加载时被初始化。如果没有显式初始化,它们会被自动初始化为其类型的默认值(例如,数字类型为0,布尔类型为false,引用类型为null)。

访问:由于类变量是静态的,因此可以通过类名直接访问,而不需要创建类的实例。例如,如果有一个名为A的类,其中包含一个名为a的类变量,那么可以通过A.a来访问它。

修改:类变量可以在不创建类实例的情况下修改,修改会影响到所有已经创建和将来创建的该类的所有实例。

示例:

package StaticVariable;

public class StaticVariableTest001 {
    public static int myStaticVariable = 10; // 声明并初始化一个类变量
    public static void main(String[] args) {
        System.out.println("第一次输出: " + StaticVariableTest001.myStaticVariable);
        StaticVariableTest001.myStaticVariable += 5; // 修改类变量,此时myStaticVariable的值为10+5
        System.out.println("第二次输出: " + StaticVariableTest001.myStaticVariable);

        StaticVariableTest001 obj1 = new StaticVariableTest001();
        StaticVariableTest001 obj2 = new StaticVariableTest001();

        obj1.myStaticVariable += 10; // 修改类变量,影响所有实例,此时myStaticVariable的值为10+5+10
        //不同对象,享受同一个变化
        System.out.println("Obj1: " + obj1.myStaticVariable);
        System.out.println("Obj2: " + obj2.myStaticVariable); // Obj2也显示更新后的值
        //再次输出它的值
        System.out.println("第三次输出: " + StaticVariableTest001.myStaticVariable);
        //即同一个类享受同一个变化
    }
}

在这个例子中,myStaticVariable是一个类变量,可以直接通过MyClass类名访问和修改,而不需要创建MyClass的实例。当在main方法中修改myStaticVariable时,所有MyClass的实例都受到这个修改的影响。

在Java中,类变量(也就是静态变量)的内存管理与其他类型的变量不同。当我们讨论类变量的内存时,主要关注的是它们的存储位置以及生命周期。

???存储位置???

类变量确实存储在方法区。在Java 8及之前的版本中,方法区通常被称为永久代。从Java 9开始,永久代的概念已被移除,方法区现在被称为“元空间”,并且它使用本地内存而非堆内存。

生命周期:

类变量的生命周期与类的生命周期绑定。这意味着当类被加载到JVM中时,类变量被创建并初始化;当类卸载时,类变量才会被销毁。因此,类变量在整个程序执行期间存在,只要类没有被卸载。

初始化

当类第一次被加载到JVM中时,类变量将被初始化。如果未指定初始值,类变量将被初始化为其类型的默认值,如整型变量为0,浮点型变量为0.0,布尔型变量为false,引用类型为null。

访问和修改:类变量可以通过类名直接访问,而不需要创建类的实例。由于类变量是共享的,因此对它的任何修改都会影响所有通过该类创建的对象。

内存模型

Java的内存模型区分了不同的存储区域,类变量位于方法区。对象实例的非静态成员变量存储在堆上,局部变量和方法参数存储在栈上。这种分离确保了类变量的共享性和持久性,同时也避免了每次创建新对象时重复分配内存给相同的类变量。

总之,类变量的内存是在方法区中分配的,它们随着类的加载而存在,直到类被卸载为止。

定义格式:访问修饰符 static 数据类型 变量名;

访问格式:类名.变量名或者对象名.变量名

注意:类变量与成员变量的访问权限是相同的。

类变量与成员变量的区别:

存储位置

成员变量(也称为实例变量):存储在堆内存中每个对象的实例空间内。这意味着每当创建一个新的对象时,成员变量就会为该对象分配一份独立的存储空间。

类变量(也称为静态变量):存储在方法区(在JVM中),是所有对象共享的。无论创建多少个对象,类变量只有一份拷贝。

生命周期

成员变量:随着对象的创建而创建,随着对象的销毁而销毁。

类变量:随着类的加载而创建,随着类的卸载而销毁,这意味着类变量在整个应用程序运行期间都存在。

可见性

成员变量:只对创建该对象的实例可见,不同对象的成员变量是独立的。

类变量:对所有该类的实例都可见,它们是共享的。

访问方式

成员变量:必须通过对象实例来访问,即使用objectReference.variableName的形式。

类变量:可以通过类名直接访问,即使用ClassName.variableName的形式,也可以通过对象实例访问,但由于它是共享的,这种方式访问的也是同一个变量。

初始化

成员变量:默认初始化为它们的数据类型的默认值,也可以在构造函数中初始化。

类变量:同样初始化为数据类型的默认值,但如果在类体中显式初始化,则所有对象都将使用这个初始化值。

类方法

类方法(也常被称为静态方法)是在面向对象编程中的一种特殊类型的方法,它与类关联而不是与类的实例关联。在Java中,类方法通过static关键字来声明。类方法有几个关键特性:

不需要实例化:类方法可以直接通过类名来调用,而不需要先创建一个类的实例。这是因为类方法不依赖于类的任何实例状态。

不能访问实例变量:由于类方法不依赖于特定的对象实例,所以它们不能直接访问类的非静态(实例)变量。如果需要访问这些变量,必须先通过某个实例来引用它们。

可以访问类变量和类方法:类方法可以访问类中的所有静态(类)变量和静态(类)方法,因为这些都是与类本身相关的,而不依赖于具体的对象实例。

生命周期:类方法随类的加载而可用,并且在整个应用程序运行期间都存在,直到类被卸载。

内存占用:类方法只有一份副本存在于内存中,无论创建了多少个类的实例。

初始化:类方法可以包含初始化代码,这些代码会在第一次调用类方法或第一次访问类的静态变量时执行。

实例:

package StaticMethod;

public class StaticMethodTest01 {
    public static void main(String[] args) {
        StaticMethodTest001.classMethod(); // 直接调用类方法 不再创建实例
        StaticMethodTest001.classVariable = 20; // 直接修改类变量
        System.out.println("修改后: " + StaticMethodTest001.classVariable);
        StaticMethodTest001 myObject = new StaticMethodTest001();
        myObject.instanceMethod(); // 需要通过实例调用成员(实例)方法
        System.out.println("修改后: " + myObject.classVariable);
    }
}
class StaticMethodTest001 {
    static int classVariable = 10;

    //静态方法/类方法
    public static void classMethod() {
        System.out.println("调用StaticMethodTest001方法的静态方法。");
        System.out.println("调用StaticMethodTest001方法的静态方法及静态变量(修改前): " + classVariable);
    }

    //成员方法
    public void instanceMethod() {
        System.out.println("调用StaticMethodTest001中的成员方法。");
    }
}

类方法(也称为静态方法)和成员方法(也称为实例方法)的主要区别:

定义

成员方法:没有static关键字,它们是类的实例的一部分,每个对象实例拥有自己的一套方法。

类方法:由static关键字定义,表明方法与类相关联,而不是与类的任何特定实例相关联。

调用

成员方法:需要通过类的实例来调用,即先创建类的一个实例,然后使用objectReference.methodName()的形式调用。

类方法:可以直接通过类名调用,使用ClassName.methodName(),无需创建类的实例。

访问变量

成员方法:可以访问类的所有变量,包括实例变量和类变量。

类方法:不能直接访问实例变量,因为它们在调用时不依赖于特定的实例。类方法只能访问类的类变量(静态变量)和其他类方法。

生命周期

成员方法:随类的实例的创建而存在,随实例的销毁而消失。

类方法:随类的加载而存在,随类的卸载而消失,与类的任何实例的生命周期无关。

内存占用

成员方法:每个对象实例都有其自身的一套成员方法,即使方法代码是相同的。

类方法:无论创建多少个类的实例,类方法在内存中只有一份拷贝。

* this关键字是一个引用,指向当前对象的实例。它在成员方法(实例方法)中非常有用,可以用来引用当前对象的属性或调用其他成员方法。然而,在类方法(静态方法)中,情况则完全不同。

成员方法中的this

在成员方法中,this关键字可以用于:引用当前对象的实例变量。调用当前对象的其他成员方法。传递当前对象作为方法参数。构造当前类的另一个实例。

类方法中的this

在类方法中,this关键字是不可用的,类方法不与任何特定的实例绑定,它们是与类本身相关的。因此,类方法中没有当前对象实例的概念,也就没有this引用的意义。

示例:

package StaticMethodTest;

public class StaticMethodTest04 {
    public static void main(String[] args) {
        StaticMethodTest04001 S = new StaticMethodTest04001(10);
        S.instanceMethod(); // 成功调用实例方法并使用this
        StaticMethodTest04001.classMethod(); // 成功调用类方法,但不能使用this
    }
}
class StaticMethodTest04001 {
    private int a;

    //无参构造器
    public StaticMethodTest04001(int b) {
        this.a = b;
    }

    //成员方法
    public void instanceMethod() {
        System.out.println("成员方法instanceMethod()及传入值: " + this.a);
        this.otherMethod();
    }

    //成员方法
    public void otherMethod() {
        System.out.println("成员方法otherMethod()");
    }

    //类方法
    public static void classMethod() {
        // 下面这行会出错,因为在类方法中不能使用this关键字
        // System.out.println("类方法 classMethod()及传入的值" + this.a);

        // 下面这行也会出错,因为不能在类方法中调用非静态方法
        // this.otherMethod();

        // 可以调用其他类方法
        anotherClassMethod();
    }

    public static void anotherClassMethod() {
        System.out.println("类方法anotherClassMethod()");
    }
}

* super关键字用于引用当前对象的直接父类的对象。super可以用来调用父类的构造方法,访问父类的字段,或者调用父类的方法。与this类似,super的行为在成员方法和类方法中有显著的区别。

成员方法中的super

在成员方法中,super可以用来:访问父类的实例变量。调用父类的成员方法。调用父类的构造方法(通常在子类构造方法的第一行)。

类方法中的super

在类方法中,super关键字的行为不同于成员方法:

类方法中不能使用super来调用父类的非静态方法或访问非静态字段,因为类方法与任何特定实例无关,而super涉及到父类的实例。然而,类方法可以调用父类的静态方法,因为这些方法也不依赖于特定的实例。

示例:

package StaticMethodTest;

public class StaticMethodTest03 {
    public static void main(String[] args) {
        StaticMethodTest03002 s = new StaticMethodTest03002();
        s.childMethod();
        s.parentMethod();

        StaticMethodTest03002.childClassMethod(); // 调用类方法,可以调用父类的静态方法
    }
}
class StaticMethodTest03001 {
    public int a = 10;

    //无参构造器
    public StaticMethodTest03001() {
        System.out.println("父类的无参构造器。");
    }

    //成员方法
    public void parentMethod() {
        System.out.println("父类的成员方法parentMethod()");
    }

    //类方法
    public static void parentClassMethod() {
        System.out.println("父类的类方法parentClassMethod()");
    }
}

class StaticMethodTest03002 extends StaticMethodTest03001 {
    //无参构造器
    public StaticMethodTest03002() {
        //super(); // 使用super调用父类构造器
        System.out.println("子类的无参构造器。");
    }

    @Override
    public void parentMethod() {
        super.parentMethod(); // 使用super调用父类方法
    }

    public void childMethod() {
        System.out.println("子类的成员方法childMethod()a  " + super.a);
    }

    public static void childClassMethod() {
        // 下面这行会出错,因为不能在类方法中使用super来访问父类的非静态成员
        //System.out.println("静态方法 childClassMethod()及a" + super.a);

        // 下面这行也是错误的,不可以使用super调用父类的静态方法
        //super.parentClassMethod();
        parentClassMethod();
        StaticMethodTest03001.parentClassMethod();
    }
}

*调用

从类方法调用成员方法

类方法不能直接调用成员方法,因为成员方法属于特定的实例。但是,如果你有一个类的实例,你可以在类方法内部通过该实例调用成员方法。如果没有实例,你不能直接从类方法调用成员方法,因为你没有一个具体的对象来调用这个方法。

从成员方法调用类方法

成员方法可以直接调用类方法,因为类方法是与类本身关联的,不需要特定的实例。成员方法可以通过类名直接调用类方法,或者通过this关键字调用类方法。

示例:

package StaticMethodTest;


public class StaticMethodTest02 {
    private int a;

    public StaticMethodTest02(int value) {
        this.a = value;
    }

    public void instanceMethod() {
        System.out.println("调用成员方法instanceMethoda : " + a);
    }
    
    public static void staticMethod() {
        System.out.println("调用类方法。");
        // 创建类的实例
        StaticMethodTest02 instance = new StaticMethodTest02(10);
        System.out.println("从类方法调用成员方法: ");
        instance.instanceMethod(); // 从类方法调用成员方法
    }
    
    public void instanceMethod01() {
        System.out.println("调用成员方法instanceMethod01a : " + a);
        StaticMethodTest02.staticMethod();
        staticMethod();
    }
    
    public static void main(String[] args) {
        System.out.println("main方法调用类方法: ");
        System.out.println("1、类名加方法名调用:");
        StaticMethodTest02.staticMethod();// main方法调用类方法
        System.out.println("2、直接调用:");
        staticMethod();//直接调用
        StaticMethodTest02 instance01 = new StaticMethodTest02(10);
        instance01.instanceMethod01();
    }
}

类方法其他需要注意的:

  1. 类方法不能访问类的构造器,因为构造器是在创建对象实例时才运行的,而类方法不依赖于任何特定实例。
  2. 类方法可以接受参数和返回值,就像普通的方法一样。这些参数可以是任何类型,包括基本类型和对象。
  3. 类方法不能被子类重写,因为它们是与类而不是实例绑定的。如果在子类中定义一个与父类中类方法相同签名的方法,那么这将被视为一个全新的方法,而不是对父类类中方法方法的重写。
  4. 类方法可以从其他类方法中调用,甚至可以从不同类的类方法中调用,只要这些方法是可访问的(遵循访问控制规则)。

原文地址:https://blog.csdn.net/jhdyjbxsryokvdry/article/details/140642834

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!