文章目录
零、本讲学习目标
1、掌握构造方法的定义
2、掌握构造方法的重载
3、掌握this关键字
一、构造方法
(一)构造方法的作用
实例化一个类的对象后,如果要为这个对象中的属性赋值,则必须通过直接访问对象的属性或调用setXxx()方法的方式才可以。如果需要在实例化对象的同时就为这个对象的属性进行赋值,可以通过构造方法来实现。简而言之,构造方法(constructor)是在利用类这个模板创建对象时自动调用的,用于初始化对象的属性。
(二)构造方法的特点
1、构造方法名与类名相同
构造方法不能由编程人员调用,而要系统自动调用,如果构造方法名与类名不同,随便取一个名字,那么系统是无法识别出来的,因此,构造方法名必须与类名相同,初始化对象时,系统直接调用与类名相同的方法就很容易。
2、构造方法没有返回值类型声明
方法头不能有返回值类型声明,连void返回值类型也不行。方法体里不能用return返回一个值,当然可以单独写一个return语句来作为方法的结束。
3、构造方法必须是public的
如果构造方法私有化,那么就不能用new来实例化对象,必须通过其它方式来得到实例,比如提供一个获得对象实例的静态方法,后面我们会演示如何操作。
4、一个类可以不定义构造方法
如果在定义类时没有定义构造方法,则编译系统会自动插入一个无参数的默认构造方法,并且这个构造方法不执行任何代码。
还记得我们第一个上Java课所创建的HelloWorld.java吧。
大家可以看到,HelloWorld这个类是没有定义构造方法的,但是编译成HelloWorld.class字节码文件之后,编译系统会自动添加一个默认的无参构造方法,如下图所示:
5、构造方法可以重载
一个类允许定义多个构造方法,即构造方法可以重载,提供对象不同的初始化方式,以参数的个数,类型,或排列顺序区分。
(三)构造方法的分类
1、无参构造方法
public 类名() {
……
}
2、有参构造方法
public 类名(参数列表) {
……
}
(四)构造方法定义案例演示
在上一讲里我们创建的Person没有显式地声明构造方法,编译系统会自动添加一个默认的无参构造方法,里面不执行任何代码,因此我们仍然可以用new Person()语句来创建Person类的实例对象。当然我们也可以显式地声明一个无参构造方法。下面我们就给上一讲我们创建的Person类添加一个无参构造方法。
1、显式声明无参构造方法
- 给Person类添加一个无参构造方法
package net.hw.lesson11;
/**
* 功能:显示声明无参构造方法
* 作者:华卫
* 日期:2020年4月29日
*/
public class Person {
private String name;
private int character;
/**
* 无参构造方法
*/
public Person() {
System.out.println("无参构造方法被调用了~");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCharacter() {
return character;
}
public void setCharacter(int character) {
if (character >= 1 && character <= 10) {
this.character = character;
} else {
System.err.println("温馨提示:人品值必须在[1, 10]范围内!");
}
}
/**
* 自我介绍方法
*/
public void speak() {
System.out.println("嗨,我叫" + name + ",人品值:" + character);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", character=" + character +
'}';
}
}
- 创建Example1101类,实例化Person类
’
2、定义有参构造方法用于初始化对象属性
-
修改Person类,添加一个有参构造方法
说明:this.name = name;
由于构造方法参数名与属性名相同,因此要在属性名之前加一个前缀this
,this
代表当前类的一个实例对象,语句this.name = name;
表明是要把参数值赋给实例对象的同名属性。 -
创建Example1102,通过有参构造方法实例化对象,调用对象方法
3、删除(注释)无参构造方法,保留有参构造方法
注释掉无参构造方法:
当一个类定义了有参构造方法,如果此时没有无参构造法,编译系统是不会自动生成一个默认的无参构造方法的,不信,我们查看Example1101,就会发现程序要报错,如下图所示:
如果我们恢复无参构造方法:
此时,Example1101就没有问题,也可以正常运行了。
其实,Person类现在有两个构造方法,一个无参构造方法,一个有参构造方法(对两个属性初始化),这就已经是构造方法的重载了。
(五)构造方法的重载
1、案例演示:给Person类添加重载的构造方法
- 修改Person类,添加一个单参构造方法
构造方法可以自己写,其实可以利用集成开发环境帮我们自动生成所需的构造方法。下面我就演示如何添加一个只初始化name属性的单参构造方法。
利用Code | Genderate…,也可以利用组合键<Ctrl> + <Insert>
,弹出Generate菜单,选择Constructure,然后选择需要构造方法初始化的字段或属性(按着Ctrl键,可以同时选中多个字段或属性),单击【OK】即可。
- 再添加一个tell()方法
- 创建Example1103类,测试两个有参构造方法初始化对象
Python构造方法没有重载的概念,但是可利用变长参数*args
来实现类似于构造方法的重载。下面的Person类,在创建对象时,可以接收0、1或2个参数。对于1个参数,如果是字符串,那么作为姓名传入,否则作为人品值传入,但必须是整数。
"""
演示构造方法的重载
"""
class Person:
def __init__(self, *args):
if len(args) == 0:
self.name = "null"
self.character = 0
elif len(args) == 1:
if isinstance(args[0], str):
self.name = args[0]
self.character = 0
else:
self.name = "null"
self.character = args[0]
else:
self.name, self.character = args
def setName(self, name):
self.name = name
def getName(self):
return self.name
def setCharacter(self, character):
self.character = character
def getCharacter(self):
return self.character
def speak(self):
print("嗨, 我叫" + self.name + ",人品值:" + str(self.character))
def __str__(self):
return "Person {name='" + self.name + "', character=" + str(self.character) + "}"
if __name__ == '__main__':
p1 = Person()
p1.speak()
p2 = Person("吴小雨")
p2.speak()
p3 = Person(6)
p3.speak()
p4 = Person("王宏宇", 9)
p4.speak()
运行程序,结果如下:
2、课堂练习:给Dog类添加重载的构造方法
上一讲,我们创建狗类Dog:
- 添加无参构造方法,调用之后会输出一句“无参构造方法被调用”
- 添加有参构造方法,对name属性进行初始化
- 添加有参构造方法,对name属性与age属性进行初始化
- 创建测试类,分别利用三种构造方法初始化三个狗对象,然后调用eat()方法
(六)私有化构造方法案例演示
1、创建Manager类,私有化构造方法
3、创建Example1104类,测试Manager类是否能实例化
错误提示:‘Manager()’ has private access in ‘net.hw.lesson11.Manager’,也就是说构造方法是私有的,无法调用它来实例化对象。
一般来说,如果一个类提供了公共的构造方法,那么我们可以利用new关键字基于该类创建很多对象。现在我们既然已经私有化构造方法了,那么别人自然无法利用new来实例化对象,但是我们可以提供一个唯一的实例供外界使用,这就是大家常说的单例模式。
4、修改Manager类,提供获取实例的静态方法
5、修改Manager类,添加自我介绍方法speak()
6、修改Example1104,获取Manager实例,调用自我介绍方法
当然,可以重新设置对象的属性:
二、this关键字
1、为什么要使用this关键字
(1)假设场景
在本讲的Person类,使用变量表示姓名时,构造方法中使用的参数是a,成员变量使用的是name,虽然在语法上没有任何问题,但这样的程序可读性很差。
这时可以将Person类中表示姓名的变量进行统一命名,例如都声明为name,但是这样做又会导致成员变量和局部变量的名称冲突,在方法中将无法访问成员变量name。
(2)解决方案
为了解决这个问题,Java中提供了一个关键字this来指代当前对象,用于在方法中访问对象的其他成员。
2、this关键字三种常见用法
(1)通过this关键字调用成员变量,解决与局部变量名称冲突问题
public class Person {
private String name; // 成员变量name
public Person(String name) { // 局部变量name
this.name = name; // 将局部变量name的值赋给成员变量name
}
}
在上面的代码中,构造方法的参数被定义为name,它是一个局部变量,在类中还定义了一个成员变量,名称也是name。在构造方法中如果使用“name”,则是访问局部变量,但如果使用“this.name”则是访问成员变量。
(2)通过this关键字调用成员方法
public void tell() {
System.out.println("Hello,我叫" + name);
}
public void express() {
this.tell();
}
在express()方法代码里,使用this关键字调用了tell()方法。需要注意的是,此处的this关键字可以省略不写,也就是说上面的代码中,写成“this.tell()”和“tell()”效果是完全一样的。
(3)通过this关键字调用构造方法
package net.hw.lesson11;
/**
* 功能:演示this调用构造方法
* 作者:华卫
* 日期:2020年4月30日
*/
class Student {
private String name;
private String gender;
private int age;
public Student() {
}
public Student(String name) {
this();
this.name = name;
}
public Student(String name, String gender) {
this(name);
this.gender = gender;
}
public Student(String name, String gender, int age) {
this(name, gender);
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void speak() {
System.out.println("我叫" + name + "," + gender + ",今年" + age + "岁了。");
}
}
public class Example1105 {
public static void main(String[] args) {
Student s1 = new Student();
s1.speak();
Student s2 = new Student("张三丰");
s2.speak();
Student s3 = new Student("张三丰", "男");
s3.speak();
Student s4 = new Student("张三丰", "男", 18);
s4.speak();
}
}
运行程序,查看结果:
三、课后作业
1、判断程序是否有错,如果没错,写出程序执行的结果
package net.hw.lesson11;
class Car {
private String brand;
private double price;
public Car() {
}
public void Car(String brand, double price) {
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
'}';
}
}
public class Exercise1101 {
public static void main(String[] args) {
Car car1 = new Car();
car1.setBrand("宝马");
car1.setPrice(500000);
System.out.println("car1 = " + car1);
Car car2 = new Car("奔驰", 1000000);
System.out.println("car2 = " + car2);
}
}
2、给三角形类Triangle添加构造方法并测试
- 添加无参构造方法 public Triangle() {}
- 添加三参构造方法 public Triangle(double a, double b, double c) {}
- 创建测试类TestTriangle,利用无参构造方法与三参构造方法创建三角形对象,设置对象属性,调用获取面积方法getArea(),输出两个三角形的面积