8.构造方法和静态

本章目标

  • 构造方法
  • 属性初始化顺序
  • 对象数组
  • 类的静态成员

本章内容

对象的属性挨个赋值太麻烦,能不能创建对象时直接赋值?

我们在创建对象时使用new Person(),那么这个Person()是什么意思呢?

贯穿案例中客户的积分,每次创建一个新的用户时默认积分应该是一致的,这该怎么处理?

一、构造方法

java创建对象的三个步骤:申请内存、调用构造方法、返回对象引用

1、什么是构造方法

构造函数是类的一种特殊方法,每次创建类的实例都会调用它,用来初始化对象

对象的创建顺序:

  1. 在使用对象之前,必须要根据类的定义构造并初始化一个对象(指定其属性的值)
  2. 在构造对象时,通过new运算符调用类的构造方法
  3. 用来将对象的各个属性初始化为指定的状态
  4. 对象一旦构造完成,就不能再一次使用构造方法来重新设置属性的值

2、特征

  • 构造方法的方法名与类名一样
  • 构造方法可以带多个参数,也可以没有参数
  • 构造方法没有返回值
  • 构造方法总是通过new运算符
  • 使用构造方法可以重载
  • 程序中如果没有为类提供任何构造方法,编译器将自动提供一个默认的构造方法(没有参数)
  • 不能对已经存在的对象调用构造方法

3、语法及使用

在带有参数的构造函数中,类在实例化时必须传递参数,否则该构造函数不能被执行

 [访问修饰符] <类名> (参数列表){
    // 构造函数的主体
 }

3.1、 默认构造方法

每个类都有一个默认不带参的构造方法,无论是否显示声明

在直接创建对象时都是调用默认不带参的构造方法

 public Employee() {
     // TODO Auto-generated constructor stub
 }

3.2、声明带参构造方法

当显示声明了带参构造方法之后 ,默认的构造方法将失效,除非我们显示的声明

 public Employee(int id) {
     this.id = id;
 }

3.3、构造方法重载

当我们显示的声明了默认构造方法之后 ,此时类中有两个构造方法,也就实现了构造方法重载

创建对象时,调用不同构造方法,给对象初始化状态不同

     public Employee() {
         // TODO Auto-generated constructor stub
     }
     public Employee(int id) {
         this.id = id;
     }

3.4、定义全参构造方法

 public class Employee {

     public Employee() {
         // TODO Auto-generated constructor stub
     }
     public Employee(int id) {
         this.id = id;
     }
 
     public Employee(int id, String name, String sex, int age, double height) {
         this.id = id;
         this.name = name;
         this.sex = sex;
         this.age = age;
         this.height = height;
     }
     ……
 }

3.5、测试调用

 public class Test {
 
     public static void main(String[] args) {
         Employee Employee = new Employee();
         Employee Employee2 = new Employee(1001);
         Employee Employee3 = new Employee(1002,"姚明","男",43,226);
         System.out.println(Employee.getId());
         System.out.println(Employee2.getId());
         System.out.println(Employee3.getId());
     }
 
 }

3.6、运行结果

 0
 1001
 1002

4、this的使用

this关键字在类定义中提供了当前对象的成员或构造方法的途径,this仅限于在构造函数方法类的实例中使用。

  • 用来访问当前对象的数据成员,使用形式如下:

    this.数据成员

  • 用来访问当前对象的成员方法,使用形式如下:

    this.成员方法(参数)

  • 当有重载的构造方法时,用来引用同类的其他构造方法,其使用形式如下:

    this(参数)

注:在访问成员属性或成员方法时可以省略,如果方法中传入参数和类属性同名,一定需在类字段前加上this

5、成员变量与局部变量

5.1、概念

  • 成员变量:直接定义在类中。
  • 局部变量:定义在方法、构造函数或块内以及方法的参数。

5.2、作用域

  • 成员变量:作用范围是整个类,可以在类的所有方法中访问;
  • 局部变量:域仅限于定义它的方法,不能被其它方法调用;

5.3、初始值

  • 成员变量:每个成员变量一个默认初始值;
  • 局部变量:局部变量没有初始值,在使用之前必须手动赋值;

5.4、成员变量和局部变量同名时

如果局部变量和成员变量同时,局部变量的赋值具有更高的优先级,优先取局部变量的值;

5.5、示例

 public class Demo {
     int globalVar;//成员变量/全局变量/字段/属性
 
     public void test() {
         int localVar = 11;
         System.out.println(localVar);
     }
 
     public void test2() {
         int globalVar = 11;//同名时,局部优先
         //System.out.println(localVar);无法调用
         System.out.println(globalVar);
     }
 }

二、属性初始化顺序(理解)

Java语言里,new表达式总体负责两个动作:

  1. 分配对象空间并对其做默认初始化。默认初始化会将对象的所有成员字段设到其类型对应的默认值(零值)。
  2. 初始化对象
  3. 其中构造器只负责第2点,第1点是包含在new表达式里的语义

1、属性默认初始值

JVM为每一个类的每一个构造方法都创建一个()方法,用于初始化实例变量,()方法是虚拟机自己调用的,不在我们的程序中显性表示,

如果对象在实例化时,其属性(状态)既没有通过构造方法进行初始化,也没有通过声明属性时赋值进行显式初始化,将会自动地赋予默认值:

  • 数值类型使用 0;
  • 布尔类型使用 false;
  • 引用类型使用 null;

注意:局部变量不会被自动赋值,必须被明确地初始化

  • 给Employee类加个flag字段

    private boolean flag;
    public boolean isFlag() {
      return flag;
    }
    public void setFlag(boolean flag) {
      this.flag = flag;
    }
    
  • 测试类中输出

    public class Test {
     
      public static void main(String[] args) {
      Employee Employee = new Employee();
      System.out.println(“数字类型默认值:”+Employee.getId());
      System.out.println(“引用类型默认值:”+Employee.getName());
      System.out.println(“布尔类型默认值:”+Employee.isFlag());
      }
    }
    
  • 结果

    数字类型默认值:0
    引用类型默认值:null
    布尔类型默认值:false
    

2、声明赋值

对象实例化时,其属性(状态)初始化的方法有很多,除了使用构造方法进行初始化以外,还可以在声明属性时通过简单地赋值进行初始化,以设置一个基本的内容

  private String address = "地址不详";
  private int point = 0;

3、初始化块

初始化块是java类的一个成员,与属性、方法、构造器属于平等地位

它的主要作用是初始化类属性和对象属性

初始化块是用花括号括起来的一段代码块

 {
     this.address = "未知地址";
 }

4、对象初始化顺序

在对象实例化的过程中,有多种方法对对象的各个属性进行初始化。通常Java在构造对象时按照以下顺序进行操作:

  1. 所有属性被初始化为默认值;
  2. 按照声明初始化属性;
  3. 执行初始化块;
  4. 构造方法
  5. 嵌套调用构造函数(暂不接触);

示例

 public class Employee {
     private String address = "地址不详";

     {
         System.out.println("初始化块之前用户地址:"+this.address);
         this.address = "未知地址";
     }
     public Employee(int id, String address) {
         System.out.println("构造方法之前用户地址:"+this.address);
         this.id = id;
         this.address = address;
     }
     ……
 }

调用

 public class Test {
 
     public static void main(String[] args) {
         Employee Employee = new Employee(1001,"科技一路");
         System.out.println("用户最终地址="+Employee.getAddress());
     }
 
 }

运行结果

 初始化块之前用户地址:地址不详
 构造方法之前用户地址:未知地址
 用户最终地址=科技一路

三、对象数组

1、什么是对象数组

在对象数组中每一个元素都是指向某个对象的引用(对象属于同一个类型)。

例如:定义一个”学生”类,班级里每一名学生都是这个类型的一个实例,都有各自的姓名、性别、年龄和考试成绩

 Student[] stu = new Student[10];
 stu[0] = new Student("张三丰", "male", 20 ,77);

2、案例 (贯穿项目相关)

用Employee[]数组来保存5员工的信息.

挑出其中的未成年人(不满18岁),输出他们的姓名和年龄?

 public class Test {
 
     public static void main(String[] args) {
         Employee[] employees = new Employee[10];
         employees[0] = new Employee(1001, "姚明", "男", 43, 7226);
         employees[1] = new Employee(1002, "张三", "男", 18, 8178);
         employees[2] = new Employee(1003, "李四", "男", 16, 7168);
         employees[3] = new Employee(1004, "王五", "男", 19, 9180);
         employees[4] = new Employee(1005, "男七", "男", 17, 8170);
         for (Employee employee : employees) {
             if (employee != null && employee.getAge() < 18) {
                 System.out.println(employee.getName() + ":" + employee.getAge());
             }
         }
 
     }
 
 }

3、案例 (贯穿项目相关)

输出对象时看不到对象详细信息?这时怎么办

重写Employee类的toString()方法

  • 重写toString()

    @Override
    public String toString() {
        return "Employee [address=" + address + ", id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + ", salary=" + salary + ", flag=" + flag + "]";
    }
    
  • 输出对象测试

    public class Test {

    public static void main(String[] args) {
        Employee[] employees = new Employee[10];
        employees[0] = new Employee(1001, "姚明", "男", 43, 7226);
        employees[1] = new Employee(1002, "张三", "男", 18, 8178);
        employees[2] = new Employee(1003, "李四", "男", 16, 7168);
        employees[3] = new Employee(1004, "王五", "男", 19, 9180);
        employees[4] = new Employee(1005, "男七", "男", 17, 8170);
        for (Employee employee : employees) {
            if (employee != null && employee.getAge() < 18) {
                System.out.println(employee);
            }
        }
    }
    
  • 结果

    Employee [address=未知地址, id=1003, name=李四, sex=男, age=16, salary=8168.0, flag=false]
    Employee [address=未知地址, id=1005, name=男七, sex=男, age=17, salary=9170.0, flag=false]
    

四、类的静态成员

思考 :

1、上周我们一直在使用static修饰方法,而面向对象之后很少再使用static,那么什么时候该使用static定义方法?

2、Employee类中添加一个count字段,来统计一共创建了多少个用户,这个该使用什么字段来修饰?

1、static关键字

静态分配:类加载时分配内存

用static修饰符修饰,可以是变量、方法或代码块

  • 类中的静态变量、方法或代码块属于类,而不属于某个特定的对象。
  • 类的静态成员可以与类的名称一起使用,而无需创建类的对象。
  • 静态变量或方法也称为类的变量或方法

2、静态属性

用static修饰符修饰的数据成员属于类的静态数据成员

2.1、静态属性特点:

  • 不管创建了类的多少实例,整个类中静态变量的副本只有一个。
  • static类数据成员仍属于类的作用域,还可以使用public static、 private static等进行修饰。修饰符不同,可访问的层次也不同。

2.2、引用静态变量的方法:

  • 在类的内部可以直接使用静态变量
  • 从类的外部可以通过类名访问静态的公共变量
  • 从类的外部可以通过类的任一实例访问静态的公共变量(不推荐使用)

2.3、示例

    public static int count = 0;
    public Employee(int id, String name, String sex, int age, double salary) {
        System.out.println("欢迎您"+name+"你是第"+(++count)+"个用户");
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.salary = salary;
    }

2.4、测试

public class Test {

    public static void main(String[] args) {
        Employee[] employees = new Employee[10];
        employees[0] = new Employee(1001, "姚明", "男", 43, 7226);
        employees[1] = new Employee(1002, "张三", "男", 18, 8178);
        employees[2] = new Employee(1003, "李四", "男", 16, 7168);
        employees[3] = new Employee(1004, "王五", "男", 19, 9180);
        employees[4] = new Employee(1005, "男七", "男", 17, 8170);
        for (Employee employee : employees) {
            if (employee != null && employee.getAge() < 18) {
                System.out.println(employee);
            }
        }
    }
}

2.5、结果

欢迎您姚明你是第1个用户
欢迎您张三你是第2个用户
欢迎您李四你是第3个用户
欢迎您王五你是第4个用户
欢迎您男七你是第5个用户
一共有5个用户

3、静态方法

3.1、与静态方法相关的几个要点:

  • 类的静态方法只能访问其他的静态成员
  • 静态方法没有this
  • 静态方法不能被覆盖
  • 可以通过类名调用静态方法(也可以使用对象调用,但不推荐);

3.2、示例

非静态方法中既可以调用实例方法和属性,又可以调用静态方法属性

 public class StaticDemo {
     int x;
     static int y;
 
     void foo() {
         x = 1; // 正确,等价于this.x = 1
         y = 1; // 正确,等价于Test.y = 1
     }
 
     static void goo() {
         // x = 1; // 错误不能访问 this.x
         y = 1; // 正确,等价于Test.y = 1
     }
 
 }

3.3、测试

在类外部,静态成员通过类来调用

 public class TestStatic {
 
     public static void main(String[] args) {
         StaticDemo.goo();
         //StaticDemo.foo(); 报错
         StaticDemo staticDemo = new StaticDemo();
         staticDemo.foo();
     }
 
 }

4、静态初始化块(了解)

如果需要通过计算来初始化静态变量,可以声明一个静态块

  • 静态块仅在该类被加载时执行一次

  • 只能初始化类的静态数据成员

  • 静态初始化器不是方法,没有方法名、返回值和参数列表

    static int[] values = new int[10]; // 静态数组成员
    static {
    for (int i = 0; i < values.length; i++) {
        values[i] = (int) (100.0 * Math.random());
    }
    }
    

5、几种成员赋值时机(了解)

使用 javap -v Test.class 命令查看其字节码:

  • 静态变量(类变量)在类加载过程的初始化阶段才会被赋值
  • 常量在编译的时候就会被分配具体值:
  • 成员变量在对象初始化时赋值(这里指的是调用()方法时,不是构造方法)

思维导图

image

posted @ 2025-03-31 17:23  icui4cu  阅读(27)  评论(0)    收藏  举报