构造方法

7.3 构造方法

前面所介绍的由Cylinder类所创建的对象,其成员变量都是在对象建立之后,再由相应的方法来赋值。如果一个对象在被创建时就完成了所有的初始化工作,将会很简洁。因此,Java语言在类里提供了一个特殊的成员方法——构造方法。

作用:对象在被创建时就完成了初始化工作。(成员变量赋值)

7.3.1 构造方法的作用与定义

作用:在对象被创建时初始化对象成员的方法。

  1. 构造方法(constructor)是一种特殊的方法。
  2. 构造方法的名称必须与它所在的类名完全相同。
  3. 构造方法没有返回值,但在定义构造方法时,构造方法名前不能用修饰符void来修饰
  4. 因为一个类的构造方法的返回值类型就是该类本身。
  5. 构造方法定义后,创建对象时就会自动调用它,因此构造方法不需要在程序中直接调用,而是在对象创建时自动调用并执行。
  6.  

注意:在构造方法中不含返回值的概念是不同于void的,对于public void Cylinder(double r,inth)这样的写法就不再是构造方法,而变成了普通方法,所以在定义构造方法时若加了void修饰符,则这个方法就不再被自动调用了。前面已经提到过,构造方法没有返回值,这是因为一个类的构造方法的返回值类型就是该类本身。

由此可以看出,构造方法是一种特殊的、与类名相同的方法,专门用于在创建对象时完成初始化工作。

构造方法的特殊性主要体现在如下几个方面:

(1)构造方法的方法名与类名相同;

(2)构造方法没有返回值,但不能写void;

(3)构造方法的主要作用是完成对类对象的初始化工作;

(4)构造方法一般不能由编程人员显式地直接调用,而是用new来调用;

(5)在创建一个类的对象的同时,系统会自动调用该类的构造方法为新对象初始化。

我们知道,在声明成员变量时可以为它赋初值,那么为什么还需要构造方法呢?这是因为构造方法可以带上参数,而且构造方法还可以完成赋值之外的其他一些复杂操作,这在以后的内容中会进行讲解。

7.3.2 默认的构造方法

如果省略构造方法,Java编译器会自动为该类生成一个默认的构造方法(default constructor),程序在创建对象时会自动调用默认的构造方法。默认的构造方法没有参数,在其方法体中也没有任何代码,即什么也不做。如果上面例子中的Cylinder类没有定义构造方法,则编译系统会自动为其生成默认的构造方法如下:

  1. 如果class前面有public修饰符,则默认的构造方法也会是public的。
  2. 一旦用户为该类定义了构造方法,系统就不再提供默认的构造方法,这是Java的覆盖(overriding)所致。
  3.  

注意:若在一个类里只定义了有参数的构造方法,但却调用无参数的构造方法创建对象,则编译不能通过。例如,若将例7.4中的第25行改为“Cylinder volu=new Cylinder();”,则在编译时报错。

例如:

public class Cylinder {

    private String name;
    private long id;

    public Cylinder(String name, long id) {
        super();
        this.name = name;
        this.id = id;
    }
    
}

 

7.3.3 构造方法的重载

一般情况下,每个类都有一个或多个构造方法。但由于构造方法与类同名,所以当一个类有多个构造方法时,则这多个构造方法可以重载。

 

7.3.4 从一个构造方法内调用另一个构造方法

为了某些特定的运算,Java语言允许在类内从某一个构造方法内调用另一个构造方法。利用这个方法,可缩短程序代码,减少开发程序时间。从某一个构造方法内调用另一构造方法,是通过使用this()语句来调用的。下面举例说明。

注意:

(1)在某一个构造方法内调用另一个构造方法时,必须使用this()语句来调用,否则编译时将出现错误。

(2)this()语句必须写在构造方法内的第一行位置。

 

7.4 静态成员

static称为静态修饰符,它可以修饰类中的成员。被static修饰的成员称为静态成员,也称为类成员,而不用static修饰的成员称为实例成员。

7.4.1 实例成员

在类定义中如果成员变量或成员方法没有用static来修饰,则该成员就是实例成员。对实例成员,我们并不陌生,因为在此之前编写的程序中,用到的都是实例成员。如在例7.5的主方法main()中分别用new运算符创建两个新的对象volu1和volu2,这两个对象都各自拥有自己保存自己成员的存储空间,而不与其他对象共享,如图7.1所示。

由图7.1可以看出,所创建的对象volu1和volu2均有各自的存储空间来保存自己的值,而不与其他对象共享。因为这些成员变量各自独立,且存于不同的内存之中,因此若修改了volu1的某个成员变量的值,volu2的成员变量并不受影响。具有此特性的成员变量,在Java中称为实例变量(instancevariable)。

 

7.4.2 静态变量

用static修饰的成员变量称为静态变量,也称为类变量。

静态变量是隶属于类的变量,而不是属于任何一个类的具体对象。

也就是说,对于该类的任何一个具体对象而言,静态变量是一个公共的存储单元,不是保存在某个对象实例的内存空间中,而是保存在类的内存空间的公共存储单元中。

 

或者说,对于类的任何一个具体对象而言,静态变量是一个公共的存储单元,类的任何一个对象访问它时,取到的都是一个相同的数值。

同样,类的任何一个对象去修改它时,也都是在对同一个内存单元做操作。

静态变量在定义时用static来修饰。静态变量在某种程度上与其他语言的全局变量相似,如果不是私有的就可以在类的外部进行访问,此时不需要创建类的实例对象,只需要类名就可以引用。换句话说就是,静态变量不需要实例化就可以使用。当然,也可以通过实例对象来访问静态变量。使用格式有如下两种:

类中若含有静态变量,则静态变量必须独立于方法之外,就像其他高级语言在声明全局变量时必须在函数之外声明一样。

7.4.3 静态方法

与静态变量相似,用static修饰符修饰的方法属于类的静态方法,又称为类方法。静态方法的实质是属于整个类的方法,而不加static修饰符的方法是属于某个具体对象的方法。将一个方法声明为static有以下几重含义。

(1)非static的方法是属于某个对象的方法,在创建这个对象时,对象的方法在内存中拥有属于自己专用的代码段。而static的方法是属于整个类的,它在内存中的代码段将被所有的对象所共用,而不被任何一个对象所专用。

(2)由于static方法是属于整个类的,所以它不能操纵和处理属于某个对象的成员,而只能处理属于整个类的成员,即static方法只能访问static成员变量或调用static成员方法,或者说在静态方法中不能访问实例变量与实例方法。

(3)在静态方法中不能使用this或super。因为this是代表调用该方法的对象,但现在静态方法既然不需要对象来调用,this也自然不应存在于静态方法内部。关于super关键字的用法,将在第8章再讨论。

(4)调用静态方法时,可以使用类名直接调用,也可以用某一个具体的对象名来调用。其格式如下:

注意:对于静态方法的调用,建议采用“类名.静态方法名();”的形式来访问。当一个类在被Java虚拟机解释器装载运行时,由于Java程序是从main()开始运行的,所以,这个类中必须有main()方法作为程序执行的入口点。现在有了静态方法的知识后,读者现在可以理解main()方法的定义了。由于Java虚拟机需要在类外调用main()方法,所以该方法的访问权限必须是public;又因为Java虚拟机运行时系统在开始执行一个程序前,并没有创建main()方法所在类的一个实例对象,所以它只能通过类名来调用main()方法作为程序的入口,即调用main()方法的是类名,而不是由类所创建的对象,因而该方法必须是static的。

7.4.4 静态初始化器

静态初始化器是由关键字static修饰的一对花括号“{}”括起来的语句组。它的作用与类的构造方法有些相似,都是用来初始化工作的,但静态初始化器与构造方法有几点根本的不同。

(1)构造方法是对每个新创建的对象进行初始化,而静态初始化器是对类自身进行初始化。

(2)构造方法是在用new运算符创建新对象时由系统自动执行,而静态初始化器一般不能由程序调用,它是在所属的类被加载入内存时由系统调用执行的。

(3)用new运算符创建多少个新对象,构造方法就被调用多少次,但静态初始化器则在类被加载入内存时只执行一次,与创建多少个对象无关。

(4)不同于构造方法,静态初始化器不是方法,因而没有方法名、返回值和参数。

尽管在该例中创建了类Cylinder的两个新对象,但其中的静态初始化器中的代码只执行了一次。这个例子也反过来说明,当一个程序中用到了其他类时,才会去装载那个类。因此,可以得出如下结论:类是在第一次被使用的时候才被装载的,而不是在程序启动时就装载程序中的所有可能要用到的类。

说明:如果有多个静态初始化器,则它们在类的初始化时会依次执行。

总之,静态初始化器的作用是对整个类完成初始化操作,包括给static成员变量赋初值,它在系统向内存加载时自动完成。

 

7.5 对象的应用

在该例的主方法main()中,声明了volu1,volu2两个类Cylinder类型的变量,但只创建了一个对象volu1,通过第29行的对象赋值语句“volu2=volu1;”,将两个不同名的引用变量指向了同一个对象,所以通过任意一个引用变量对对象做修改,另一个引用变量所指向的对象内容也会随着更改。

由于引用变量中存放的是对象在内存中的首地址,那么对象被赋值后,到底是它们所共同指向的同一对象的内容相等,还是这两个引用变量中所保存的地址相等?在6.4节中已经讨论过,当参数是基本数据类型时,是传值方式调用,而当参数是引用变量时,则是传址方式调用。所以牢记这个结论,对理解参数传递非常有意义。另外,引用变量也可以作为方法的参数来使用。下面通过一个例子来说明这些问题。

该例中,Cylinder类的compare()方法接收的参数是对象,并用if(this==volu)语句判断两个引用变量是否相等。在主方法main()中,声明了三个引用变量volu1、volu2和volu3,并在第24、25行用相同的实参创建了两个对象volu1和volu2。但在第27、28行分别用volu2、volu3调用volu1的compare()方法,从输出结果上看,volu1与volu2不相等,而volu1与volu3却相等。这是因为volu1和volu2分别指向了两个新创建的Cylinder类对象,尽管创建的两个对象看上去完全相同,但它们是两个彼此独立的对象,是两个占据不同内存空间地址的不同对象,而引用变量volu1与volu2的值分别是这两个对象在内存中的首地址,显然它们是不相等的。而volu1和volu3是指向同一个对象的两个变量,它们的值是同一对象在内存中的首地址,所以它们是相等的。比较两个对象的相等还可用equals()方法,详见8.1.5节。

7.5.2 引用变量作为方法的返回值

引用变量不但可以作为参数进行传递,而且也可以作为方法的返回值。若要方法返回类类型的变量,只需在方法声明的前面加上要返回的类名即可。

 

7.5.3 类类型的数组

在第5章中介绍过数组,数组元素可以是存放各种类型的数据,当然数组也可以用来存放对象。用数组来存放对象,一般要经过如下两个步骤:

(1)声明类类型的数组变量,并用new运算符分配内存空间给数组;

(2)用new创建新的对象,分配内存空间给它,并让数组元素指向它。

7.5.4 以对象数组为参数进行方法调用

通过例7.13可以知道,数组也可以用来存放对象。因此,也可将对象数组作为参数传递到方法里。下面举例说明。

 

 

 

 

 

 

 

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值