Class
Java的语法是为了更容易地模拟真实世界而设计的. 比如用程序实现一只狗, 可以用定义一个类class来描述它.
类class里面包括变量Variable,方法method(可以理解为Python的函数function)。变量可以储存数据,方法可以处理数据。变量必须在类中声明(即不能离开类独立存在),不像Python或Matlab这样的语言可以在运行时添加新的变量。
构造对象的过程:
- 声明(declaration)引用变量:
Dog smalldog;
- 创建对象:实例化
new Dog(20)
, 如果没有把它作为值赋给一个类声明变量, 那么这个实例化的值会被垃圾回收. - 连接对象和引用:赋值对象给引用
Dog smalldog = new Dog(5)
创建对象这一步,调用了Dog()
, 不是普通的方法, 而是类的构造函数 Constructors.
构造函数
构造函数在初始化一个对象时执行, 构造函数与类名同名且没有返回类型, 而且可以带参数:
/** 注意:构造函数与class类同名
但没有返回类型 */
public Dog(int w) {
weight = w;
}
然后在DogLauncher
里实例化一只狗时, 直接Dog d = new Dog(20);
即可.
在以上代码的基础上, 后续当我们想使用new和参数创建一只狗时,可以随时调用public Dog(int w)
构造函数。对于熟悉Python的人来说,你可以理解java的构造函数为Python的__init__
。
Java可以有与类同名的方法,只是要指明返回类型。 构造函数无法被继承 如果类有一个以上的构造函数,则参数一定要不一样,包括参数顺序和类型
构造函数链
执行new
指令会启动构造函数的连锁反应(Constructor Chaining), 首先会执行其父类的构造函数, 依此类推连锁反应到Object
类为止. 就算是抽象类, 也会有构造函数, 虽然不能被直接实例化, 但也会被唤醒. 理论上,每个类的构造函数需要先调用其父类的构造函数super()
,依次入栈
public class Duck extends Animal {
int size;
public Duck(int newSize) {
super(); // 调用父类构造函数, 且必须是在函数中的第一行
size = newSize;
}
}
如果明确写了super();
, 则必须位于构造函数第一行. 但很多构造函数没有写super();
也可以编译通过, 甚至连自身的构造函数public Duck(int newSize)
也不一定是必须的。
- 如果没有
super()
, 编译器会帮我们加上. - 如果连自身的构造函数都没有, 编译器会自动为没有构造函数的类提供一个无参数的默认构造函数。这个默认构造函数将调用其超类的(可调用的)无参构造函数。
- 此时, 如果其
extends
的父类没有无参数构造函数,编译会出错。 - 如果没有显式的超类,那么就调用隐式的超类
Object
的无参构造函数。
- 此时, 如果其
但如果在类中已经实现了带参数的构造函数,那么编译器不会再帮你构造无参数的构造函数,你需要自己编写。如果某个父类只有带参数的构造函数, 那么继承该父类的子类必须有构造函数, 且要有带参数的super(args)
.
如果存在不同重载版本的构造函数, 其中有某个构造函数可以负责大部分构造工作, 我们这个时候肯定希望能够让同类的其他构造函数先调用该构造函数, 完成大部分构造工作, 已达到代码重用的目的, 这样可以更好地维护代码.
this()
就是用来从某个构造函数中调用同一个类的另外一个构造函数.
class Mini extends Car {
Color color;
// 这里无参数的构造函数使用默认颜色调用阵阵的构造函数
public Mini() { this(Color.Red); }
public Mini(Color c) {
super("Mini");
color = c;
}
}
this()
只能存在于构造函数中, 且必须要在第一行, 所以会跟super()
冲突, 二者不能同时调用.