Java面向对象(高级)-- static关键字的使用

news2024/11/30 8:34:45

文章目录

  • 一、static关键字
    • (1)类属性、类方法的设计思想
    • (2) static关键字的说明
    • (3)static修饰属性
      • 1. 复习变量的分类
      • 2. 静态变量
        • 2.1 语法格式
        • 2.2 静态变量的特点
        • 2.3 举例
          • 2.3.1 举例1
          • 2.3.2 举例2
          • 2.3.3 举例3
        • 2.4 静态变量的存储位置演进
          • 2.4.1 jdk6
          • 2.4.2 jdk7
          • 2.4.3 jdk8
        • 2.5 对比静态变量与实例变量
        • 2.6 内存解析
          • 2.6.1 举例1
          • 2.6.2 举例2
    • (4)static修饰方法
      • 1. 语法格式
      • 2. 静态方法的特点
      • 3.重点注意事项
      • 4. 举例
        • 4.1 举例1
        • 4.2 举例2
        • 4.3 举例3
        • 4.4 举例4
        • 4.5 举例5
    • (5)开发中的注意事项
  • 二、练习
    • (1)练习1
    • (2)练习2
    • (3)练习3
    • (4)面试题

一、static关键字

回顾类中的实例变量(即非static的成员变量)

class Circle{
	private double radius;	//之前写的属性没有static声明-->实例变量(每个对象有一份)
	public Circle(double radius){
        this.radius=radius;
	}
	public double findArea(){
        return Math.PI*radius*radius;
    }
}

创建两个Circle对象:(当我们创建类的多个对象的时候,每个对象都各自一份,互不影响)

Circle c1=new Circle(2.0);	//c1.radius=2.0
Circle c2=new Circle(3.0);	//c2.radius=3.0

Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,c1中的radius变化不会影响c2的radius,反之亦然。

如果想让一个成员变量(如radius)被类的所有实例所共享,就用static修饰即可,称为类变量(或类属性)!

实例变量:每个对象一份

类变量:所有对象共用一个,归类多有

(1)类属性、类方法的设计思想

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用

我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份

例如,所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
image.png

此外,在类中声明的实例方法,在类的外面必须要先创建对象,才能调用。但是有些方法的调用者和当前类的对象无关,这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

这里的类变量(成员变量)、类方法,只需要使用static修饰即可。所以也称为静态变量静态方法

(2) static关键字的说明

static: 静态的

static 用来修饰的结构:属性、方法; 代码块、内部类;(构造器不能够用static修饰)

  • 使用范围:
    • 在Java类中,可用static修饰属性、方法、代码块、内部类
  • 被修饰后的成员具备以下特点:
    • 随着类的加载而加载
    • 优先于对象存在
    • 修饰的成员,被所有对象所共享
    • 访问权限允许时,可不创建对象,直接被类调用

(3)static修饰属性

1. 复习变量的分类

方式1:按照数据类型

  • 基本数据类型
  • 引用数据类型

方式2:按照类中声明的位置

  • 成员变量:直接声明在类内部,方法、构造器外面的
    • 按照是否使用static修饰进行分类:
      • 使用static修饰的成员变量:静态变量、类变量
      • 不使用static修饰的成员变量:非静态变量、实例变量
  • 局部变量:方法内、方法形参、构造器内、构造器形参、代码块内等。

2. 静态变量

2.1 语法格式

使用static修饰的成员变量就是静态变量(或类变量、类属性)

[修饰符] class{
	[其他修饰符] static 数据类型 变量名;
}
2.2 静态变量的特点
  • 静态变量的默认值规则和实例变量一样。
  • 静态变量值是所有对象共享。
  • 静态变量在本类中,可以在任意方法、代码块、构造器中直接使用。
  • 如果权限修饰符允许,在其他类中可以通过“类名.静态变量”直接访问,也可以通过“对象.静态变量”的方式访问(但是更推荐使用类名.静态变量的方式)。
  • 静态变量的get/set方法也静态的,当局部变量与静态变量重名时,使用“类名.静态变量”进行区分。
2.3 举例
2.3.1 举例1
class Chinese{
    //实例变量
    String name;
    int age;
    //类变量
    static String nation;//国籍

    public Chinese() {
    }

    public Chinese(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Chinese{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", nation='" + nation + '\'' +
                '}';
    }
}
public class StaticTest {
    public static void main(String[] args) {
        Chinese c1 = new Chinese("康师傅",36);
        c1.nation = "中华人民共和国";

        Chinese c2 = new Chinese("老干妈",66);

        System.out.println(c1);
        System.out.println(c2);

        System.out.println(Chinese.nation);
    }
}

对应的内存结构:(以经典的JDK6内存解析为例,此时静态变量存储在方法区)

image.png


2.3.2 举例2
package com.atguigu.keyword;

public class Employee {
    private static int total;//这里私有化,在类的外面必须使用get/set方法的方式来访问静态变量
    static String company; //这里缺省权限修饰符,是为了方便类外以“类名.静态变量”的方式访问
    private int id;
    private String name;

    public Employee() {
        total++;
        id = total;//这里使用total静态变量的值为id属性赋值
    }

    public Employee(String name) {
        this();
        this.name = name;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

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

    public static int getTotal() {
        return total;
    }

    public static void setTotal(int total) {
        Employee.total = total;
    }

    @Override
    public String toString() {
        return "Employee{company = " + company + ",id = " + id + " ,name=" + name +"}";
    }
}
package com.atguigu.keyword;

public class TestStaticVariable {
    public static void main(String[] args) {
        //静态变量total的默认值是0
        System.out.println("Employee.total = " + Employee.getTotal());

        Employee e1 = new Employee("张三");
        Employee e2 = new Employee("李四");
        System.out.println(e1);//静态变量company的默认值是null
        System.out.println(e2);//静态变量company的默认值是null
        System.out.println("Employee.total = " + Employee.getTotal());//静态变量total值是2

        Employee.company = "尚硅谷";
        System.out.println(e1);//静态变量company的值是尚硅谷
        System.out.println(e2);//静态变量company的值是尚硅谷

        //只要权限修饰符允许,虽然不推荐,但是也可以通过“对象.静态变量”的形式来访问
        e1.company = "超级尚硅谷";

        System.out.println(e1);//静态变量company的值是超级尚硅谷
        System.out.println(e2);//静态变量company的值是超级尚硅谷
    }
}

2.3.3 举例3
package yuyi01;

/**
 * ClassName: ChineseTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/14 0014 10:00
 */
public class ChineseTest {
    public static void main(String[] args) {
        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;

        Chinese c2=new Chinese();
        c2.name="索隆";
        c2.age=19;

        System.out.println(c1);
        System.out.println(c2);
    }
}

class Chinese{  //中国人
    //非静态变量/实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //toString方法
    @Override
    public String toString() {
        return "Chinese{" +
        "name='" + name + '\'' +
        ", age=" + age +
        '}';
    }
}

这里造了两个对象c1与c2,对于name和age来说,它们各自有一份,所以这里打印的时候,它们的信息互相不影响。如下:

image.png

现在我们引入静态变量static String nation;,并通过c1给它赋值c1.nation="China";,如下:

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {
        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;
        c1.nation="China";

        Chinese c2=new Chinese();
        c2.name="索隆";
        c2.age=19;

        /*System.out.println(c1);
        System.out.println(c2);*/

        System.out.println(c1.nation);	//China
        System.out.println(c2.nation);	//China
    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation;   //国籍

    //toString方法
    @Override
    public String toString() {
        return "Chinese{" +
        "name='" + name + '\'' +
        ", age=" + age +
        '}';
    }
}

上面代码输出结果如下:

image.png

可以看到,虽然c2没有给nation赋值,但是打印仍然有China。

因为nation在整个内存当中只有一份,通过任何一个对象去调用都会影响其他对象。

现在通过c2改变nation的值c2.nation="CHN";,如下:

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {
        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;
        c1.nation="China";

        Chinese c2=new Chinese();
        c2.name="索隆";
        c2.age=19;

        c2.nation="CHN";
        System.out.println(c1.nation);	//CHN
        System.out.println(c2.nation);	//CHN
    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation;   //国籍

    //toString方法
    @Override
    public String toString() {
        return "Chinese{" +
        "name='" + name + '\'' +
        ", age=" + age +
        '}';
    }
}

输出结果为:

image.png

因为内存中nation只有一份,所以无论谁去改,都会导致别人调用的时候也会被改变。


若此时将nation的static修饰去掉String nation;,结果就会变化:

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {
        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;
        c1.nation="China";

        Chinese c2=new Chinese();
        c2.name="索隆";
        c2.age=19;

        System.out.println(c1);
        System.out.println(c2);

        System.out.println(c1.nation);  //China
        System.out.println(c2.nation);  //null

        c2.nation="CHN";
        System.out.println(c1.nation);  //China
        System.out.println(c2.nation);  //CHN
    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    String nation;   //国籍

    //静态变量 / 类变量
    //static String nation;   //国籍

    //toString方法
    @Override
    public String toString() {
        return "Chinese{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

输出结果:

image.png

静态变量:整个内存中就一份,被类的多个对象所共享。

2.4 静态变量的存储位置演进
2.4.1 jdk6

image.png

永久代

栈、堆、方法区的概念是JVM规范说的(Oracle公司),具体的有实打实的虚拟机,比如Oracle官方发布的HotSpot,如下:

image.png

这个虚拟机在实现这套规范的时候,将“方法区”叫做“永久代”。

在HotSpot里面,永久代就可以看作方法区的一个具体实现。

在JDK6中,静态变量放在永久代中

2.4.2 jdk7

image.png

jdk7的时候有个变化,将静态变量从方法区移到堆里面了。

因为在堆里面GC比较频繁,方法区很少去GC,会导致回收不及时。

2.4.3 jdk8

image.png

jdk8及以后,方法区换了一个时间,叫“元空间”,开始使用本地内存。

静态变量还是在堆里面。

2.5 对比静态变量与实例变量

静态变量:类中的属性使用static进行修饰。

对比静态变量与实例变量:

  • ① 个数

    • 静态变量:在内存空间中只有一份,被类的多个对象所共享。
    • 实例变量:类的每一个实例(或对象)都保存着一份实例变量
  • ② 内存位置

    • 静态变量:jdk6及之前:存放在方法区。 jdk7及之后:存放在堆空间
    • 实例变量:存放在堆空间的对象实体中
  • ③ 加载时机

    • 静态变量(类变量):随着类的加载而加载,由于类只会加载一次,所以静态变量也只有一份。
    • 实例变量:随着对象的创建而加载。每个对象拥有一份实例变量。

举例

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {
        System.out.println(Chinese.nation); //不造对象直接打印nation
    }
}

class Chinese{  //中国人
    //静态变量 / 类变量
    static String nation="中国";   //国籍
}

输出结果:

image.png

此时对象还没有创建,就已经有nation属性了,可见静态变量随着类的加载而加载,有了类就有了静态变量,可以去调用。

  • ④ 调用者

    • 静态变量:可以被类直接调用,也可以使用对象调用
    • 实例变量:只能使用对象进行调用。(类不能调用)
  • ⑤ 判断是否可以调用 —> 从生命周期的角度解释

类变量实例变量
yesno
对象yesyes
  • ⑥ 消亡时机
    • 静态变量:随着类的卸载而消亡。
    • 实例变量:随着对象的消亡而消亡。

image.png

2.6 内存解析
2.6.1 举例1

image.png

2.6.2 举例2

image.png

(4)static修饰方法

1. 语法格式

用static修饰的成员方法就是静态方法。(类方法、静态方法)

[修饰符] class{
	[其他修饰符] static 返回值类型 方法名(形参列表){
        方法体
    }
}

2. 静态方法的特点

  • 静态方法在本类的任意方法、代码块、构造器中都可以直接被调用。
  • 只要权限修饰符允许,静态方法在其他类中可以通过“类名.静态方法“的方式调用。也可以通过”对象.静态方法“的方式调用(但是更推荐使用类名.静态方法的方式)。
  • 在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。
  • 静态方法可以被子类继承,但不能被子类重写。
  • 静态方法的调用都只看编译时类型。
  • 因为不需要实例就可以访问static方法,因此static方法内部不能有this,也不能有super。如果有重名问题,使用“类名.”进行区别。

3.重点注意事项

  • 随着类的加载而加载
  • 可以通过“类.静态方法”的方式,直接调用静态方法。
  • 静态方法内可以调用静态的属性或静态的方法。(属性和方法的前缀使用的是当前类,可以省略),不可以调用非静态的结构。(比如:属性、方法)
  • static修饰的方法内,不能使用this和super。
  • 补充:在类的非静态方法中,可以调用当前类中的静态结构(属性、方法)或非静态结构(属性、方法)。
类方法(静态方法)实例方法(非静态方法)
yesno
对象yesyes

4. 举例

4.1 举例1
package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {

        System.out.println(Chinese.nation); //不造对象直接打印nation
        Chinese.show(); //在造对象之前就可以调用

    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation="中国";   //国籍

    //静态方法
    public static void show(){  //此方法是静态方法,随着类的加载而加载
        System.out.println("我是一个中国人");
    }
}

输出结果:

image.png

可以通过“类.静态方法”的方式,直接调用静态方法。

4.2 举例2
public static void show(){  //此方法是静态方法,随着类的加载而加载
    System.out.println("我是一个中国人");

    //调用静态结构  -- 可以
    System.out.println("name= "+nation);
    method1();

    //调用非静态结构 -- 不可以
    System.out.println("name= "+name);  //这里的name就是this.name,this表示当前对象,通过类调用show方法可能还没有对象,所以不能调用name属性
    eat("饺子");  //这里的eat()相当于this.eat(),同样这时候可能还没有对象呢,所以不能调用

}

注意:

image.png

静态方法里面没有this之说,省略的是当前类

image.png

因此可以这样来写:

image.png

static修饰的方法内,不能使用this和super。(this和super都是基于当前对象去调用结构,但是static方法内还没有对象之说)

整体代码:

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {
        System.out.println(Chinese.nation); //不造对象直接打印nation
        Chinese.show(); //在造对象之前就可以调用

    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation="中国";   //国籍

    public void eat(String food){
        System.out.println("我喜欢吃"+food);
    }

    public static void method1(){
        System.out.println("我是静态的测试方法");
    }

    public static void show(){  //此方法是静态方法,随着类的加载而加载
        System.out.println("我是一个中国人");

        //调用静态结构  -- 可以
        System.out.println("name= "+nation);
        method1();

        //调用非静态结构 -- 不可以
        //System.out.println("name= "+name);  //这里的name就是this.name,this表示当前对象,通过类调用show方法可能还没有对象,所以不能调用name属性
        //eat("饺子");  //这里的eat()相当于this.eat(),同样这时候可能还没有对象呢,所以不能调用

    }
}

输出结果:

image.png

静态方法内可以调用静态的属性或静态的方法。(属性和方法的前缀使用的是当前类,可以省略),不可以调用非静态的结构。(比如:属性、方法)

4.3 举例3
package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {

        System.out.println(Chinese.nation); //不造对象直接打印nation
        Chinese.show(); //在造对象之前就可以调用(通过类调用静态方法)

        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;
        c1.nation="China";

        c1.show();//通过对象调用静态方法
    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation="中国";   //国籍

	//非静态方法
    public void eat(String food){
        System.out.println("我喜欢吃"+food);
    }

    //静态方法
    public static void method1(){
        System.out.println("我是静态的测试方法");
    }

    public static void show(){  //此方法是静态方法,随着类的加载而加载
        System.out.println("我是一个中国人");

        //调用静态结构  -- 可以
        System.out.println("name= "+nation);
        method1();

        //调用非静态结构 -- 不可以
        //System.out.println("name= "+name);  //这里的name就是this.name,this表示当前对象,通过类调用show方法可能还没有对象,所以不能调用name属性
        //eat("饺子");  //这里的eat()相当于this.eat(),同样这时候可能还没有对象呢,所以不能调用

    }


}

可以通过类调用静态方法:Chinese.show();

也可以通过对象调用静态方法:c1.show();

image.png

输出结果:

image.png

4.4 举例4

若此时有一个普通的非静态方法method2()。

public void method2(){
    System.out.println("我是非静态测试方法");
    //调用非静态结构   -- 可以
    System.out.println("name= "+this.name); //this就是方法的调用者,这个调用者一定是一个对象
    this.eat("饺子");

    //调用静态结构    -- 可以
    System.out.println("name= "+nation);
    method1();
}

整体代码:

package yuyi01;

public class ChineseTest {
    public static void main(String[] args) {

        System.out.println(Chinese.nation); //不造对象直接打印nation
        Chinese.show(); //在造对象之前就可以调用(通过类调用静态方法)

        Chinese c1=new Chinese();
        c1.name="路飞";
        c1.age=18;
        c1.nation="China";

        c1.show();//通过对象调用静态方法
    }
}

class Chinese{  //中国人
    //非静态变量 / 实例变量(每个 对象--实例 一份)
    String name;
    int age;

    //静态变量 / 类变量
    static String nation="中国";   //国籍

    //非静态方法
    public void eat(String food){
        System.out.println("我喜欢吃"+food);
    }

    public void method2(){
        System.out.println("我是非静态测试方法");
        //调用非静态结构   -- 可以
        System.out.println("name= "+this.name); //this就是方法的调用者,这个调用者一定是一个对象
        this.eat("饺子");

        //调用静态结构    -- 可以
        System.out.println("name= "+nation);
        method1();
    }


    //静态方法
    public static void method1(){
        System.out.println("我是静态的测试方法");
    }

    public static void show(){  //此方法是静态方法,随着类的加载而加载
        System.out.println("我是一个中国人");

        //调用静态结构  -- 可以
        System.out.println("name= "+nation);
        method1();

        //调用非静态结构 -- 不可以
        //System.out.println("name= "+name);  //这里的name就是this.name,this表示当前对象,通过类调用show方法可能还没有对象,所以不能调用name属性
        //eat("饺子");  //这里的eat()相当于this.eat(),同样这时候可能还没有对象呢,所以不能调用

    }

}

对象可以调用类方法、类变量,实例变量、实例方法。

4.5 举例5
package com.atguigu.keyword;

public class Father {
    public static void method(){
        System.out.println("Father.method");
    }

    public static void fun(){
        System.out.println("Father.fun");
    }
}
package com.atguigu.keyword;

public class Son extends Father{
//    @Override //尝试重写静态方法,加上@Override编译报错,去掉Override不报错,但是也不是重写
    public static void fun(){
        System.out.println("Son.fun");
    }
}
package com.atguigu.keyword;

public class TestStaticMethod {
    public static void main(String[] args) {
        Father.method();
        Son.method();//继承静态方法

        Father f = new Son();
        f.method();//执行Father类中的method
    }
}

(5)开发中的注意事项

①开发中,什么时候需要将属性声明为静态的?

  • 判断当前类的多个实例是否能共享此成员变量,且此成员变量的值是相同的
  • 开发中,常将一些常量声明是静态的。比如:Math类中的PI

②开发中,什么时候需要将方法声明为静态的?

  • 方法内操作的变量如果都是静态变量(而非实例变量)的话,则此方法建议声明为静态方法。
  • 开发中,常常将工具类中的方法,声明为静态方法。比如:Arrays类、Math类

【举例1】

Arrays工具类:

image.png

比如调用sort方法,直接拿Arrays.(类.)来调用,因为它是静态方法,可以直接调用。

若不是静态方法,不能直接拿类调用,就需要new一个Arrays对象调用,但我们会发现两个对象a1与a2调用Arrays方法也没有什么区别,因为主要的区别在于形参。既然如此,工具类拿哪个对象调用都没有什么区别,不妨就静态化用类来调用。

之前说面向对象的时候,通过对象调用属性和方法。现在还有一类方法,不需要用对象去调用,可以用类来调用,它就是静态的方法。

【举例2】

若一个类里面有一个静态的变量(比如static String nation=“中国”;),这个变量被私有化了,就要有get/set方法,此时对这个属性设置get/set方法,写成静态还是非静态呢?

设置为非静态的get/set方法:语法上可以,因为非静态的方法可以调用静态的属性。

但是一般都设置为静态的。(让它们生命周期一致,既然属性都被静态化了,同时又私有化了,那么设置get/set就自然而然用类去调用)

编译器自动生成的也是静态方法。

class Chinese{  //中国人
    //静态变量 / 类变量
    static String nation="中国";   //国籍

    public static String getNation() {
        return nation;
    }

    public static void setNation(String nation) {
        Chinese.nation = nation;
    }
}

【举例3】

在main方法里面不能直接调用非静态方法,需要先创建测试类ChineseTest的对象,再通过对象调用test()方法。

如下:

public class ChineseTest {
    public static void main(String[] args) {
        test();	//直接调用不被允许
    }

    public void test(){	//非静态方法
        System.out.println("我是static的测试方法");    
    }

}

若将test()方法声明为static就可以,因为静态里面可以调静态,不需要再造对象了,前面省略了“ChineseTest.”。

如下:

public class ChineseTest {
    public static void main(String[] args) {
        test();	//可以,静态里面可以调静态,ChineseTest.test()
    }

    public static void test(){	//非静态方法
        System.out.println("我是static的测试方法");    
    }

}

二、练习

(1)练习1

看下面一段代码,输出结果是?

package yuyi01;

/**
 * ClassName: CircleTest
 * Package: yuyi01
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/15 0015 16:33
 */
public class CircleTest {
    public static void main(String[] args) {
        Circle c1=new Circle(); //创建的第一个圆
        System.out.println(c1);

        Circle c2=new Circle(); //创建的第二个圆
        System.out.println(c2);
    }
}

//类-圆
class Circle{
    double radius;  //每个圆的半径不一样,不需要静态(实例变量)
    int id; //给每个圆一个编号(实例变量)

    //每个对象去调用total的值都一样,可以设置它为静态
    static int total;   //记录创建了几个对象,即Circle实例的个数,与具体圆没啥关系,用类调用就行,可以静态(静态变量)

    //id自动赋值,创建第一个圆的对象是1001,第二个就是1002...
    public Circle(){    //通过构造器造对象
        this.id=init;
        init++;
    }

    private int init=1001;  //初始值默认为1001 (不对外暴露),,自动给id赋值的基数


    //重写toString方法
    @Override
    public String toString() {
        return "Circle{" +
        "radius=" + radius +
        ", id=" + id +
        '}';
    }
}

输出结果:

image.png

①先来看init的问题。

可以看到,半径都是0.0,没有问题,因为在构造器里面本身就没有赋值。

但是id是怎么回事?明明init++了,为啥第二个圆的id还是1001?

image.png

这是因为init变量没有静态修饰,每个对象都有一份。

如图:
image.png

这就有悖于我们的初衷,希望在原有的init基础之上给id赋值,所以init不应该每个对象一份:private static int init=1001;

再次运行会发现第二个圆圈的id变成了1002,如下:

image.png

若再创建一个Circle对象,id会自动生成1003.

因为init在内存中只有一份,用它来体现自动编号。


②现在来看total的问题,创建了几个对象如何体现?

现在调用肯定不太靠谱,结果是0,如下:

image.png

可以在构造器里面实现,每次创建一个对象,就让total++。如下:

class Circle{
    double radius;  //每个圆的半径不一样,不需要静态(实例变量)
    int id; //给每个圆一个编号(实例变量)

    //每个对象去调用total的值都一样,可以设置它为静态
    static int total;   //记录创建了几个对象,即Circle实例的个数,与具体圆没啥关系,用类调用就行,可以静态(静态变量)

    //id自动赋值,创建第一个圆的对象是1001,第二个就是1002...
    public Circle(){    //通过构造器造对象
        this.id=init;
        init++;
        total++;
    }
	//...
}

输出可以看到结果为2(创建了两个对象):

image.png

若此时total不是静态变量,首先就不能用Circle.total来调用了,需要使用对象来调用。

比如此时用c1来调用,那么结果就成1了,先看代码:

package yuyi01;

public class CircleTest {
    public static void main(String[] args) {
        Circle c1=new Circle(); //创建的第一个圆
        System.out.println(c1);

        Circle c2=new Circle(); //创建的第二个圆
        System.out.println(c2);

        //System.out.println(Circle.total);   //total为非静态变量时不可以这样用类调用

        System.out.println(c1.total);
    }
}

//类-圆
class Circle{
    double radius;  //每个圆的半径不一样,不需要静态(实例变量)
    int id; //给每个圆一个编号(实例变量)

    //每个对象去调用total的值都一样,可以设置它为静态
    //static int total;   //记录创建了几个对象,即Circle实例的个数,与具体圆没啥关系,用类调用就行,可以静态(静态变量)
    int total;  //若total是非静态变量

    //id自动赋值,创建第一个圆的对象是1001,第二个就是1002...
    public Circle(){    //通过构造器造对象
        this.id=init;
        init++;
        total++;
    }

    private static int init=1001;  //初始值默认为1001 (不对外暴露),自动给id赋值的基数


    //重写toString方法
    @Override
    public String toString() {
        return "Circle{" +
        "radius=" + radius +
        ", id=" + id +
        '}';
    }
}

输出结果:

image.png

此时每个对象都有一份total,各自将自己的total从0改为了1。


③针对上面这个问题,若此时提供带参的构造器

再创建一个Circle对象,通过带参构造器创建。如下:

package yuyi01;

public class CircleTest {
    public static void main(String[] args) {
        Circle c1=new Circle(); //创建的第一个圆
        System.out.println(c1);

        Circle c2=new Circle(); //创建的第二个圆
        System.out.println(c2);

        Circle c3=new Circle(2.3);  //调用有参数的构造器
        System.out.println(c3);

        System.out.println(Circle.total);   //total为非静态变量时不可以这样用类调用

        //System.out.println(c1.total);
    }
}

//类-圆
class Circle{
    double radius;  //每个圆的半径不一样,不需要静态(实例变量)
    int id; //给每个圆一个编号(实例变量)

    //每个对象去调用total的值都一样,可以设置它为静态
    static int total;   //记录创建了几个对象,即Circle实例的个数,与具体圆没啥关系,用类调用就行,可以静态(静态变量)

    //int total;  //若total是非静态变量--不推荐这样

    //id自动赋值,创建第一个圆的对象是1001,第二个就是1002...
    public Circle(){    //通过构造器造对象
        this.id=init;
        init++;
        total++;
    }

    public Circle(double radius){
        this(); //将本类的空参构造器里面的东西拿过来
        this.radius=radius;
    }

    private static int init=1001;  //初始值默认为1001 (不对外暴露),自动给id赋值的基数


    //重写toString方法
    @Override
    public String toString() {
        return "Circle{" +
        "radius=" + radius +
        ", id=" + id +
        '}';
    }
}

输出结果:

image.png

(2)练习2

🌋题目描述

编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,

定义封装这些属性的方法。账号要自动生成。

编写主类,使用银行账户类,输入、输出3个储户的上述信息。

考虑:哪些属性可以设计成static属性。

💨代码

【Account.java】

package yuyi02;

/**
 * ClassName: Account
 * Package: yuyi02
 * Description:
 *  编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,
 *  定义封装这些属性的方法。账号要自动生成。
 * @Author 雨翼轻尘
 * @Create 2023/11/15 0015 21:45
 */
public class Account {
    //属性
    private int id; //账号 (不需要静态,每个人账号不一致)
    private String password;    //密码 (不需要静态,每个人密码不一致)
    private double balance; //存款余额  (不需要静态,每个人存款余额不一致)
    private static double interestRate; //利率 (可以静态,每个人面对同样的银行,所以利率是一样的)
    private static double minBalance=1.0;   //最小余额 (可以静态,每个人面对同样的银行,所以最小余额是一样的)

    private static int init=1001;   //用于自动生成id的基数

    //构造器
    public Account() {
        this.id=init;
        init++;

        password="000000";  //默认密码
    }

    //只有非静态的属性,因为造对象,相当于设置对象的情况,所以这里只设置非静态的,静态直接拿类去调用即可,和构造器关系就不大了(编译器自动生成也没有提供静态的属性)
    public Account(String password, double balance) {   //题目要求id是自动生成,所以设置另外两个非静态即可
        this.id=init;
        init++;

        this.password = password;
        this.balance = balance;
    }

    //get/set方法
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public static double getInterestRate() {
        return interestRate;
    }

    public static void setInterestRate(double interestRate) {
        Account.interestRate = interestRate;
    }

    public static double getMinBalance() {
        return minBalance;
    }

    public static void setMinBalance(double minBalance) {
        Account.minBalance = minBalance;
    }

    //toString方法
    @Override
    public String toString() {
        return "Account{" +
        "id=" + id +
        ", password='" + password + '\'' +
        ", balance=" + balance +
        '}';
    }
}

【AccountTest.java】

package yuyi02;

/**
 * ClassName: AccountTest
 * Package: yuyi02
 * Description:
 *  编写主类,使用银行账户类,输入、输出3个储户的上述信息。
 * @Author 雨翼轻尘
 * @Create 2023/11/15 0015 22:45
 */
public class AccountTest {
    public static void main(String[] args) {
        Account acct1=new Account();
        System.out.println(acct1);

        Account acct2=new Account("123456",2000);
        System.out.println(acct2);

        Account.setInterestRate(0.0123);    //设置银行存款的利率
        Account.setMinBalance(10);  //设置银行最小存款额度
        System.out.println("银行存款的利率为: "+Account.getInterestRate());  //打印利率 (静态变量可以直接用类来调用)
        System.out.println("银行最小存款额度为: "+Account.getMinBalance());
    }

}

👻输出结果

image.png

(3)练习3

🌋题目描述

自定义一个数组的工具类,封装常用的数组算法。

💨代码

【MyArrays.java】

package yuyi03;

/**
 * ClassName: MyArrays
 * Description:
 *      根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
 *      涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、
 *              数组排序(默认从小到大排序)、查找等
 * @Author 雨翼轻尘
 * @Create 14:13
 * @Version 1.0
 */
public class MyArrays {

    /**
     * 获取int[]数组的最大值
     * @param arr 要获取最大值的数组
     * @return 数组的最大值
     */
    public static int getMax(int[] arr){
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(max < arr[i]){
                max = arr[i];
            }
        }
        return max;
    }

    /**
     * 获取int[]数组的最小值
     * @param arr 要获取最小值的数组
     * @return  数组的最小值
     */
    public static int getMin(int[] arr){
        int min = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(min > arr[i]){
                min = arr[i];
            }
        }
        return min;
    }

    public static int getSum(int[] arr){
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }

    public static int getAvg(int[] arr){

        return getSum(arr) / arr.length;
    }

    public static void print(int[] arr){ //[12,231,34]
        System.out.print("[");

        for (int i = 0; i < arr.length; i++) {
            if(i == 0){
                System.out.print(arr[i]);
            }else{
                System.out.print("," + arr[i]);
            }
        }

        System.out.println("]");
    }

    public static int[] copy(int[] arr){
        int[] newArr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            newArr[i] = arr[i];
        }
        return newArr;
    }

    public static void reverse(int[] arr){
        for(int i = 0,j = arr.length - 1;i < j;i++,j--){
            //交互arr[i] 与 arr[j]位置的元素
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    public static void sort(int[] arr){
        for(int j = 0;j < arr.length - 1;j++){
            for (int i = 0; i < arr.length - 1 - j; i++) {
                if(arr[i] > arr[i + 1]){
                    //交互arr[i] 和 arr[i + 1]
                    int temp = arr[i];
                    arr[i] = arr[i + 1];
                    arr[i + 1] = temp;
                }

            }
        }
    }

    /**
     * 使用线性查找的算法,查找指定的元素
     * @param arr 待查找的数组
     * @param target 要查找的元素
     * @return target元素在arr数组中的索引位置。若未找到,则返回-1
     */
    public static int linearSearch(int[] arr,int target){

        for(int i = 0;i < arr.length;i++){
            if(target == arr[i]){
                return i;
            }

        }

        //只要代码执行到此位置,一定是没找到
        return -1;

    }

}

【MyArraysTest.java】

package yuyi03;

/**
 * ClassName: MyArraysTest
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 14:16
 * @Version 1.0
 */
public class MyArraysTest {
    public static void main(String[] args) {

        //MyArrays arrs = new MyArrays();
        int[] arr = new int[]{34,56,223,2,56,24,56,67,778,45};

        //求最大值
        //System.out.println("最大值为:" + arrs.getMax(arr));
        System.out.println("最大值为:" + MyArrays.getMax(arr));

        //求平均值
        //System.out.println("平均值为:" + arrs.getAvg(arr));
        System.out.println("平均值为:" + MyArrays.getAvg(arr));

        //遍历
        //arrs.print(arr);
        MyArrays.print(arr);

        //查找
        //int index = arrs.linearSearch(arr,24);
        int index = MyArrays.linearSearch(arr,24);
        if(index >= 0){
            System.out.println("找到了,位置为:" + index);
        }else{
            System.out.println("未找到");
        }

        //排序
        //arrs.sort(arr);
        MyArrays.sort(arr);

        //遍历
        //arrs.print(arr);
        MyArrays.print(arr);

    }
}

👻输出结果

image.png

⚡注意

之前这些方法写的都是非静态的,这就意味着在调用的时候,就需要先造一个对象。

其实咱们造一个对象也没啥用,无论是哪个对象,调用这个方法也没什么区别,主要区别在于形参

这就是工具类,主要看形参不一样。(工具类里面的方法通常加上static)

(4)面试题

package yuyi03;

/**
 * ClassName: StaticTest
 * Package: yuyi03
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2023/11/16 0016 9:56
 */
public class StaticTest {
    public static void main(String[] args) {
        Order order = null;
        order.hello();
        System.out.println(order.count);
    }
}

class Order {
    public static int count = 1;    //静态变量

    public static void hello() {    //静态方法
        System.out.println("hello!");
    }
}

输出结果:

image.png

可以看到并没有空指针异常

静态方法和静态变量随着Order类的创建而创建。(静态不依赖于对象

创建对象时没有在堆空间里面给对象分配空间,没有加载成员变量和方法,静态变量和方法是加载了的。

order只是没有在堆空间中创建对象实体,但静态变量和方法和对象实体在堆空间中并不是存放在同一个位置。


PS:
终于进入面向对象高级部分了,知识点越来越多,要及时复习。
粉丝破5w了,在此纪念一下,快毕业了,也不知道以后敲博客的时间还会有多少,迷茫又有点惆怅,未来很长,我会慢慢去走,你呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1215572.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣每日一题-最长奇偶子数组-2023.11.16

力扣每日一题&#xff1a;最长奇偶子数组 题目链接:2760.最长奇偶子数组 题目描述 代码思路 利用单指针进行扫描&#xff0c;符合子数组起点要求时&#xff0c;开始记录子数组长度。题目本身不难理解&#xff0c;就是判断的条件比较多&#xff0c;需要耐心和细心。 代码纯享…

一文搞懂GPU的概念、工作原理,以及与CPU的区别

中午好&#xff0c;我的网工朋友。 最近GPTs热度很高啊&#xff0c;你们都用上了吗&#xff1f; ChatGPT到现在热度仍不减&#xff0c;人工智能还在快速发展&#xff0c;这都离不开高性能、高算力的硬件支持。 如果以英伟达A100GPU的处理能力计算&#xff0c;运行ChatGPT将需…

时间序列数据集——可用于预测和分类

文章目录 一.UCI数据集 一.UCI数据集 UCI官方网站 UCI数据集是由加州大学欧文分校维护的用于机器学习的数据库。官方网站收集了622个数据集&#xff0c;可用于时间序列预测、数据分类回归等多种任务&#xff0c;包含交通流量、电力、生物、空气质量、互联网等等各个方面的数据…

git宝藏干货

git命令 怎样删除gitee仓库 Gitee上传代码 在Gitee上创建该项目的远程仓库 进入你想要上传的文件目录下&#xff0c;右键单击空白处&#xff0c;点击Git Bash Here 命令行输入下列命令&#xff0c;初始化本地仓库 git init 添加项目目录下所有文件至本地仓库 git add . …

ATE测试设备功能、原理、特点详解

ATE(Automatic Test Equipment)自动测试设备是用于检测电子产品、电气设备的自动化测试系统&#xff0c;是电测行业首选的一种测试方式&#xff0c;被广泛应用于通信、消费电子、汽车电子、智能家居、半导体、电源模块、医疗电子、航天航空等领域。ATE测试设备在电子设计、研发…

【excel技巧】Excel表格里的图片如何批量调整大小?

Excel表格里面插入了很多图片&#xff0c;但是每张图片大小不一&#xff0c;如何做到每张图片都完美的与单元格大小相同&#xff1f;并且能够根据单元格来改变大小&#xff1f;今天分享&#xff0c;excel表格里的图片如何批量调整大小。 方法如下&#xff1a; 点击表格中的一…

c语言:解决判断两个字符串是不是互为旋转字符的问题

题目&#xff1a; 思路&#xff1a;先判断两个字符长度是否相等&#xff0c;如果不相等&#xff0c;肯定不是互为旋转字符串。 方法一&#xff1a;将第一份字符串右旋0-n&#xff08;字符个数&#xff09;次&#xff0c;判断是否有一次右旋完的字符串等于第二个字符串。如果有&…

MySQL分页查询的工作原理

前言 MySQL 的分页查询在我们的开发过程中还是很常见的&#xff0c;比如一些后台管理系统&#xff0c;我们一般会有查询订单列表页、商品列表页等。 示例&#xff1a; SELECT * FROM goods order by create_time limit 0,10; 在了解order by和limit的工作原理之前&#xff0c…

HarmonyOS开发:动态共享包的依赖问题

一、共享包的依赖方式 在需要依赖的模块包目录下oh-package.json5文件中添加依赖&#xff1a; "dependencies": {"ohos/srpaasUI": "file:../../srpaasUI","ohos/srbusiness": "file:../../feature/srbusiness"} 引入之后…

掌握接口自动化测试,看这篇文章就够了,真滴简单

前言&#xff1a; 接口测试在我们测试工作当中&#xff0c;经常会遇到&#xff0c;对于接口自动化操作&#xff0c;也越来越多的公司进行实践起来了&#xff0c;市面上有很多工具可以做接口自动化比如&#xff1a;Postman、JMeter、SoapUI等。这一篇安静主要介绍通过代码的形式…

阿尔法狗的算法解析-增强学习和蒙特卡洛树搜索算法

阿尔法狗(AlphaGo)是谷歌旗下DeepMind开发的一个著名的增强学习算法,它在围棋领域取得了显著的成就。本文主要探讨其中两个重要的算法:增强学习算法和蒙特卡洛树搜索算法。 AlphaGo涉及的算法 AlphaGo是DeepMind团队开发的一个由多种算法和技术组合而成的系统,其包括以下…

基于单片机C51全自动洗衣机仿真设计

**单片机设计介绍&#xff0c; 基于单片机C51全自动洗衣机仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机C51的全自动洗衣机仿真设计是一个复杂的项目&#xff0c;它涉及到硬件和软件的设计和实现。以下是对这…

uniapp 微信小程序分享功能 onShareAppMessage(options)

一、背景 在微信小程序中需要完成转发分享功能&#xff0c;接收人未登录小程序情况下&#xff0c;在微信上打开不用强制登录&#xff0c;可以查看部分分享的内容&#xff1b;如果用户要查看更多内容&#xff0c;可以点击【查看全部】按钮&#xff0c;触发登录逻辑&#xff0c;…

【2023云栖】郭瑞杰:阿里云搜索产品智能化升级

本文根据 2023 云栖大会演讲实录整理而成&#xff0c;演讲信息如下&#xff1a; 演讲人&#xff1a;郭瑞杰 | 阿里云资深技术专家、搜索负责人 演讲主题&#xff1a;阿里云搜索产品智能化升级发布 近日在2023云栖大会上&#xff0c;阿里云搜索负责人郭瑞杰对阿里云搜索产品智…

轻松实现文件改名:让新文件名与目录名称一致

在日常工作中&#xff0c;我们经常需要处理文件改名的问题。有时候&#xff0c;我们需要将新文件名设置为与目录名称相同&#xff0c;以方便管理和查找。然而&#xff0c;这个过程可能很繁琐&#xff0c;尤其是当你有大量的文件需要改名时。幸运的是&#xff0c;现在有一种简单…

【Mquant】7:构建价差套利(三) ——空间误差校正模型

文章目录 1. 上节回顾2. 纯碱价差套利3. 什么是协整性分析4. 通过协整性检验5. 空间误差校正模型&#xff08;VECM&#xff09;6. 构建交易策略7 总结 1. 上节回顾 【Mquant】6&#xff1a;构建价差套利(二)上节带领大家编写了统计套利均值回归的程序&#xff0c;通过历史回测…

服务器数据恢复—服务器raid5离线磁盘上线同步失败的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 某品牌DL380服务器中有一组由三块SAS硬盘组建的RAID5阵列。数据库存放在D分区&#xff0c;数据库备份存放在E分区。 服务器上有一块硬盘的状态灯显示红色&#xff0c;D分区无法识别&#xff0c;E分区可识别&#xff0c;但是拷贝文件报…

如何通过 wireshark 捕获 C# 上传的图片

一&#xff1a;背景 1. 讲故事 这些天计划好好研究下tcp/ip&#xff0c;以及socket套接字&#xff0c;毕竟工控中设计到各种交互协议&#xff0c;如果只是模模糊糊的了解&#xff0c;对分析此类dump还是非常不利的&#xff0c;而研究协议最好的入手点就是用抓包工具 wireshar…

从哪里下载 Oracle database 11g 软件

登入My Oracle Support&#xff0c;选择Patches & Updates 标签页&#xff0c;点击下方的Latest Patchsets链接&#xff1a; 然后单击Oracle Database&#xff0c;就可以下载11g软件了&#xff1a; 安装单实例数据库需要1和2两个zip文件&#xff0c;安装GI需要第3个zip文…

01.智慧商城——项目介绍与初始化

智慧商城 - 授课大纲 接口文档&#xff1a;https://apifox.com/apidoc/shared-12ab6b18-adc2-444c-ad11-0e60f5693f66/doc-2221080 演示地址&#xff1a;http://cba.itlike.com/public/mweb/#/ 01. 项目功能演示 1.明确功能模块 启动准备好的代码&#xff0c;演示移动端面…