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

文章目录

  • 一、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了,在此纪念一下,快毕业了,也不知道以后敲博客的时间还会有多少,迷茫又有点惆怅,未来很长,我会慢慢去走,你呢?

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux套接字-Socket

1.概念 局域网和广域网 局域网&#xff1a;局域网将一定区域内的各种计算机、外部设备和数据库连接起来形成计算机通信的私有网络。广域网&#xff1a;又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程公共网络。IPInternet Protocol&#xff09;&#…

无需云盘,不限流量实现Zotero跨平台同步:内网穿透+私有WebDAV服务器

&#x1f525;博客主页&#xff1a; 小羊失眠啦. &#x1f3a5;系列专栏&#xff1a;《C语言》 《数据结构》 《Linux》《Cpolar》 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 无需云盘&#xff0c;不限流量实现Zotero跨平台同步&#xff1a;内网穿透私有WebDAV服务器 文章目…

系列八、堆(Heap)

一、概述 一个JVM实例只存在一个堆内存&#xff0c;堆内存的大小是可以手动调节的。类加载器读取了类文件后&#xff0c;需要把类、方法、常变量放到堆内存中&#xff0c;保存所有引用类型的真实信息&#xff0c;以方便执行器执行&#xff0c;堆内存分为三个部分&#xff0c;即…

高压开关柜实现无线测温监控关键点在哪里?

近年来&#xff0c;电力系统已发生多起因设备过热而发生火灾和大面积停电事故。据统计分析&#xff0c;我国每年发生的电力事故&#xff0c;有40&#xff05;是由高压电气设备过热所致&#xff1b;而在采用高压开关柜和电力电缆的供电系统中&#xff0c;有70&#xff05;以上的…

36、Flink 的 Formats 之Parquet 和 Orc Format

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

教育案例分享 | 安全狗云安全体系为高校提升立体化纵深防御能力

一、客户情况 某高校有服务器500台&#xff0c;对外站点200个&#xff0c;核心交换流量20G。 二、客户痛点 校园网系统分类较多&#xff0c;并且每类网站中安全级重要程度又各不相同&#xff0c;同时有多个网络出口(如&#xff1a;教育网、电信网、移动网等)&#xff0c;二级学…

你不懂API接口是什么?怎么和程序员做朋友

说到开发平台就一定离不开接口&#xff0c;作为PM&#xff0c;我们不需要对接口了解的特别细。只需要知道接口是什么&#xff0c;有什么用&#xff0c;有哪些要素就行。 1. 接口是什么 (1) 硬件接口 生活中我们经常会接触接口&#xff0c;最常见的就是HDMI接口和USB接口&…

软件测试/测试开发丨人工智能产品质量保障:挑战与创新

点此领取人工智能课程 人工智能产品的质量保障与测试是当前软件开发领域最具挑战性的任务之一。随着人工智能技术的迅猛发展&#xff0c;产品日益复杂&#xff0c;传统测试方法逐渐显得力不从心。在这个背景下&#xff0c;我们需要创新性地思考并采用新的策略&#xff0c;以确…

使用 Java 枚举和自定义数据类型

介绍 在 Java 编程领域&#xff0c;理解并有效利用枚举和自定义数据类型对于编写健壮、可维护且高效的代码至关重要。这篇文章旨在深入研究 Java 枚举和自定义数据类型的概念&#xff0c;提供见解和示例&#xff0c;以增强您的编码技能和知识。 理解 Java 中的枚举 枚举是枚…

十秒钟学会Mac系统和Linux之间的文件传输

前言 在我们的mac系统上&#xff0c;大家应该要先学会用我们的终端远程连接Linux的虚拟机或者云服务器&#xff0c;教程在这篇博客&#xff1a;http://t.csdnimg.cn/KQzgc 大家如果想安装iterm2和on-my-zsh&#xff08;非常推荐&#xff0c;很好用&#xff09;的话&#xff0c;…

CentOS中安装常用环境

一、CentOS安装 redis ①&#xff1a;更新yum sudo yum update②&#xff1a;安装 EPEL 存储库 Redis 通常位于 EPEL 存储库中。运行以下命令安装 EPEL 存储库 sudo yum install epel-release③&#xff1a;安装 Redis sudo yum install redis④&#xff1a;启动 Redis 服…

java 批量更改

直接上代码 void batchUpdateSpecificationId(Param("infos") List<GoodsInfo> infos);<update id"batchUpdateSpecificationId">update goods_infoset specification_id <foreach collection"infos" item"info" open&…

打开PDF文件之后不能编辑,有哪些原因?

打开PDF文件之后发现没有办法编辑PDF文件&#xff0c;都有哪些原因呢&#xff1f; 首先我们可以考虑一下&#xff0c;PDF文件中的内容是否是图片&#xff0c;如果确认是图片文件&#xff0c;那么我们想要编辑&#xff0c;就可以先使用PDF编辑器中的OCR扫描功能&#xff0c;将图…

利用jquery对HTML中的名字进行替代

想法&#xff1a;将网页中经常要修改的名字放在一个以jquery编写的js文件中&#xff0c;如果需要修改名字&#xff0c;直接修改js文件中的名字即可。 新建name_07.html文件&#xff0c;写入下面的代码&#xff1a; <!DOCTYPE html> <html> <head><meta …

如何选择一款车规级电源开关/驱动器芯片?NCV8405ASTT3G自保护低压侧驱动器

关于车规级芯片&#xff08;Automotive Grade Chip&#xff09;&#xff0c;车规级芯片是专门用于汽车行业的芯片&#xff0c;具有高可靠性、高稳定性和低功耗等特点&#xff0c;以满足汽车电子系统的严格要求。这些芯片通常用于车载电子控制单元&#xff08;ECU&#xff09;和…

echart 雷达图

详细信息 // 图表 var myChart echarts.init(document.getElementById(echart)); var option {radar: {radius: 60%,axisLabel: {//坐标轴刻度标签的相关设置。fontSize: 12//文字的字体大小},splitLine: {// 是否显示网格线show: true,lineStyle: {//分隔线颜色&#xff0c;…

算法通关村第九关-白银挑战二分查找与高频搜索树

大家好我是苏麟,今天看看二分查找相关的题目 . 大纲 二分查找拓展问题山脉数组的峰顶索引寻找旋转排序数组中的最小值 中序与搜索树二叉搜索树中的搜索验证二叉搜索树 二分查找拓展问题 山脉数组的峰顶索引 描述 : 符合下列属性的数组 arr 称为 山脉数组 &#xff1a; arr…

JumpServer2023漏洞复现合集

本文主要复现JumpServer2023年出现的大批量漏洞&#xff0c;既是分享也是为了记录自己的成长&#xff0c;近期会持续更新。 1. JumpServer MongoDB远程代码执行漏洞&#xff08;CVE-2023-43651&#xff09; 1.1 漏洞级别 高危 1.2 漏洞描述 经过身份验证的用户可以利用Mon…

检验Pdfsharp.dll 支持的语言及对应的字体

文章目录 检验所支持语言的字体使用字体绘制文本并显示 PdfSharp 语言和字体的支持有限&#xff0c;有时候再本地电脑上能正常显示文本&#xff0c;但在其它电脑上就显示乱码或一个正方体&#xff0c;或&#xff1f;&#xff1f;。不同操作系统可能自带的字体本身就不一样&…

LeetCode2-两数相加

大佬解法 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) { val x; }* }*/ class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode pre new ListNode(0);ListNo…