|
|
本帖最后由 pansong291 于 2019-3-27 11:54 编辑
上篇:36.类与对象(二)---面向对象
下篇:38.this引用句柄---面向对象
注意:如果你的回复只是“感谢分享”、“辛苦了”之类的与教程内容无关的字眼,还请不要回复本帖,谢谢合作!!!
注意:如果你的回复只是“感谢分享”、“辛苦了”之类的与教程内容无关的字眼,还请不要回复本帖,谢谢合作!!!
注意:如果你的回复只是“感谢分享”、“辛苦了”之类的与教程内容无关的字眼,还请不要回复本帖,谢谢合作!!!
构造函数的定义与作用
我们先来看一个程序:
class Person
{
public Person()
{
System.out.println("the constructor 1 is calling!");
}
private int age = 10;
public void shout()
{
System.out.println("age is "+age);
}
}
class TestPerson
{
public static void main(String[] args)
{
Person p1=new Person();
p1.shout();
Person p2=new Person();
p2.shout();
Person p3=new Person();
p3.shout();
}
}
运行结果如下:
the constructor 1 is calling!
age is 10
the constructor 1 is calling!
age is 10
the constructor 1 is calling!
age is 10
通过运行的结果读者会发现,在 TestPerson.main 方法中并没有调用刚才新加的
Person()方法,但它却被自动调用了,而且我们每创建一个 Person 对象,这个方法都会被
自动调用一次。这就是“构造方法”。关于这个 Person 方法,有几点不同于一般方法的特征:
1. 它具有与类相同的名称;
2. 它不含返回值;
3. 它不能在方法中用 return 语句返回一个值
关于第二点,读者也可以理解为它具有固定的返回值,这个返回值就是新构造出来的对象;
关于第三点,它不能在方法中用 return 语句返回一个值不代表不可以用 return 语句提前
返回,只是它不能返回具体的值罢了。
在一个类中,具有上述特征的方法就是“构造方法”。构造方法在程序设计中非常有用,
它可以为类的成员变量进行初始化工作,当一个类的实例对象刚产生时,这个类的构造方法
就会被自动调用,我们可以在这个方法中加入要完成初始化工作的代码。这就好像我们规定
每个“人”一出生就必须先洗澡,我们就可以在“人”的构造方法中加入完成“洗澡”的程
序代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一
个一个地告诉他们要“洗澡”了。
虽然初始化在程序设计中是一项非常重要的工作,可是仍然还有很多程序员在编程过程
中时常会疏忽对变量的初始化,使用构造方法就可以避免这种情况,并且可以实现一次完成
对类的所有实例对象的初始化,这免除了调用程序对每个实例对象都要进行初始化的繁琐工
作。
读者站在 Java 设计者的角度去想一想:构造方法的名称和类的名称为什么要相同呢?
这是因为构造方法由 Java 编译器负责调用,而编译器必须知道哪一个才是构造方法,采用
与类同名的方式应该是最简单合理的。
脚下留心:在构造方法里不含返回值的概念是不同于“void”的,对于“public void
Person()”这样的写法就不再是构造方法,而变成了普通方法,很多人都会犯这样的错误,
在定义构造方法时加了“void”,结果这个方法就不再被自动调用了。
构造方法的重载
上一章我们讲了方法的重载,构造方法也可以被重载,这种情况其实是很常见的,先来
看这段程序:
class Person
{
private String name="unknown";
private int age = -1;
public Person()
{
System.out.println("constructor1 is calling");
}
public Person(String n)
{
name = n;
System.out.println("constructor2 is calling");
System.out.println("name is "+name);
}
public Person(String n, int a)
{
name = n;
age = a;
System.out.println("constructor3 is calling");
System.out.println("name and age is "+name+";"+age);
}
public void shout()
{
System.out.println("listen to me!!");
}
}
class TestPerson
{
public static void main(String[] args)
{
Person p1=new Person();
P1.shout();
Person p2=new Person("Jack");
P2.shout();
Person p3=new Person("Tom", 18);
P3.shout();
}
}
在 Person 类里我们又添加了两个构造方法,其中一个构造方法接受外部传入的姓名,
再赋值给类的成员变量,另外一个构造方法接受外部传入的姓名和年龄,再赋值给类的成员
变量。上面定义了多个 Person 构造方法,这就是构造方法的重载。和一般的方法重载一样,
它们具有不同个数或不同类型的参数,编译器就可以根据这一点判断出用 new 关键字产生
对象时,该调用哪个构造方法了。
在 TestPerson.main 方法中,我们用 new 关键字产生了三个 Person 的实例对象,产生
对象的格式是:
new 类名(参数列表)
Java 会根据我们在括号中传递的参数,自动为我们选择相应的构造方法,程序运行的
结果如下:
constructor1 is calling
listen to me!!
constructor2 is calling
name is Jack
listen to me!!
constructor3 is calling
name and age is Tom;18
listen to me!!
这三个对象调用了不同的构造方法,可见,因为括号中传递的参数个数或类型不同,调
用的构造方法也不同。
重载构造方法可以完成不同初始化的操作,当我们要创建一个 Person 实例对象的同时,
就直接给对人的姓名和年龄赋值,我们就可以使用下面的方式去产生这个 Person 实例对象。
Person p1=new Person("Tom", 18);
而不必先用 Person p1=new Person();语句产生 Person 实例对象,再单独对这个 Person
实例对象的姓名和年龄赋值。
我们来分析一下 p3=new Person("Tom", 18);语句都干了些什么?
它会做这样几件事:创建指定类的新实例对象,在堆内存中为实例对象分配内存空间,
并调用指定类的构造方法,最后将实例对象的首地址赋值给引用变量 p3。
首先,等号左边定义了一个类 Person 类型的引用变量 p3,等号右边使用 new 关键字创
建了一个 Person 类的实例对象。此时的内存状态如图 3.9 所示:
接着,调用相应的构造方法,构造方法接受外部传入的姓名和年龄,在执行构造方法中
的代码之前,进行属性的显式初始化,也就是执行在定义成员变量时就对其进行赋值的语句,
即程序 Person 类中的:
private String name="unknown"; //显式初始化
private int age = -1;// 显式初始化
此时的内存状态如图 3.10 所示:
然后,执行构造方法中的代码,用从外部接受到的姓名和年龄对成员变量重新赋值。此
时的内存状态如图 3.11 所示:
最后,把刚刚创建的对象赋给引用变量,如图 3.12 所示:
构造方法的一些细节
1).在 Java 的每个类里都至少有一个构造方法,如果程序员没有在一个类里定义构造方
法,系统会自动为这个类产生一个默认的构造方法,这个默认构造方法没有参数,在其方法
体中也没有任何代码,即什么也不做。
下面程序的 Construct 类两种写法完全是一样的效果。
class Construct
{
}
class Construct
{
public Construct(){}
}
对于第一种写法,类虽然没有声明构造方法,但我们可以用 new Construct()语句来创
建 Construct 类的实例对象。
由于系统提供的默认构造方法往往不能满足编程者的需求,我们可以自己定义类的构造
方法,来满足我们的需要,一旦编程者为该类定义了构造方法,系统就不再提供默认的构造
方法了。
class Construct
{
int age;
public Construct(int x)
{
age = x;
}
}
上面的 Construct 类中定义了一个对成员变量赋初值的构造方法,该构造方法有一个形
式参数,这时系统就不再产生默认的构造方法,我们再编写一个调用 Construct 类的程序。
class TestConstruct
{
public static void main(String [] args)
{
Construct c = new Construct();
}
}
写好上面的程序,编译器会报错:
错误的原因就是,调用 new Construct()创建 Construct 类的实例对象时,要调用的是
没有参数的那个构造方法,而我们又没有定义无参数的构造方法,但我们定义了一个有参数
的构造方法,系统不再为我们自动生成无参数的构造方法。针对这种情况,我们只要自己定
义构造方法,都需要带上一个无参数的构造方法,否则,就会经常碰到上面这样的错误。
2).思考一下,声明构造方法时,可以使用 private 访问修饰符吗?运行这段程序,看
看有什么结果:
class Person
{
private Person()
{
System.out.println("the constructor 1 is calling!");
}
}
class TestPerson
{
public static void main(String[] args)
{
Person p1=new Person();
}
}
程序会报错:
出现这段信息原因是 Person()构造方法是私有的,不可以被外部调用,可见构造方法一般都
是 public 的,因为它们在对象产生时会被系统自动调用的。
上篇:36.类与对象(二)---面向对象
下篇:38.this引用句柄---面向对象 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|