Java基本语法
本文主要记载了在Java课程学习中遇到的与类的构造函数函数有关的问题及个人理解
Java的构造函数,也叫构造方法,是Java中一种特殊的函数。构造函数的函数名必须与对应的类名相同,且无返回值。`
唯一能够调用构造函数的方法就是新建一个类(或者是新建一个这个类的子类,并在创建这个类的子类的时候,在该子类的构造函数里面调用)。
语法结构为:
[修饰符列表] 构造方法名 (形式参数列表){
构造方法体;
}
作为一个特殊的方法,构造函数的形式与普通的方法类似,但需要注意的是,构造函数不可以有返回值,同时也说明,在构造函数中,不可以有return语句(因为函数无返回类型)!
代码如下(示例):
// 假设我们现在有一个class为Duck,则该类最基础的构造函数为
public Duck() {
}
需要注意的是,如果你在创建对象的时候,没有显式的编写构造函数,那么编译器会隐式的为你的class创建一个与上面代码相类似的最基本的构造函数。
如果在类中重写(overload)构造函数,可以向构造函数中 传递参数。
代码如下(示例):
// 使用与上一个示例相同的class Duck
public Duck(int size, String name) {
// code
}
这里只需要知道可以向构造函数中传入参数即可,用法在下面具体阐述
需要注意的是,当你定义了一个有参的构造函数后,编译器将不会再自动为你生成无参的构造函数,如果需要使用无参的构造函数,需要自己手动编写(这一点在继承中十分重要)。
可以打印相关语句来表明是否成功创建对象,同时也可以为对象设置默认的初始值(通常我们很需要这点)。
代码如下(示例):
// 使用与上一个示例相同的class Duck,并在class DuckTest中创建一个Duck
public class DuckTest {
public static void main(String[] args) {
Duck myDuck = new Duck();
}
}
class Duck {
private int size;
private String name;
public Duck() {
size = 10;
name = "duck";
System.out.println("Quark");
}
}
输出为:Quark
当我不希望单独使用set方法去繁琐地设置每一个实例变量。也就相当于完成了setter的功能。
// 继续使用与上一个示例相同的class Duck,并在class DuckTest中创建一个Duck
import java.util.Scanner;
public class DuckTest {
public static void main(String[] args) {
Scanner ip = new Scanner(System.in);
// 创建一个大小为50,名为Donald的Duck
Duck myDuck = new Duck(ip.nextInt(), ip.next());
System.out.println("The duck size is " + myDuck.size + ", the duck name is " + myDuck.name);
}
}
class Duck {
private int size;
private String name;
public Duck() {
System.out.println("Quark");
}
public Duck(int size, String name) {
this.name = name;
this.size = size;
}
}
输入为:50,Donald
输出为:The duck size is 50, the duck name is Donald
p.s. 为了方便输出,此处的Duck的实例变脸设置为public
当我们在创建某个对象的时候(我们只能通过new来产生新对象),对象会取得所有实例变量所需的空间,同时也包括这个对象所继承的所有东西。
因此我们可以知道,当我们创建一个新对象时,所有继承下来的构造函数都会被执行。 也就是说,当子类的构造函数在被执行的时候,他所做的第一件事就是去执行父类的构造函数,这会如同连锁反应般,一直执行到Object这个类(虽然没什么用)。
可以这样理解,在有子类之前,肯定会先有父类(不然子类无法继承,也就没有办法创建一个新的子类对象,可以在cmd中使用javac去尝试),就好比没有父亲就不会有儿子。同时,子类可能会根据父类的状态来继承方法(即父类的实例变量)。综上所述,并结合前面的定义我们可以得出结论:父类的构造函数,在子类被创建的时候,一定会被执行。
一个在子类中调用父类方法的方法(详细使用方法可以移步csdn搜索)
若要在子类的构造函数中显式的调用父类的构造函数,则需要使用super()方法,此时super()可以等价与父类的类名。
需要注意的是,因为super()此时在子类的构造函数中代表的是父类的构造函数,所以必须在子类构造函数的第一行使用。可以理解为,在执行创建子类之前,必须先创建父类,即使用父类的构造函数。
和前面一样,当我们不在子类的构造函数中使用super()方法的时候,编译器会隐式的调用super()方法。
先介绍一种会报错的写法:
public class Creature {
int weight;
String name;
Creature(int weight, String name) {
this.weight = weight;
this.name = name;
}
}
class Rabbit extends Creature {
String furType;
int speed;
Rabbit(String furtype, int speed){
this.furType = furtype;
this.speed = speed;
}
}
这是一个一开始一直困扰我和我同学的问题。此时我们会发现,编译器会报错:“没有可以使用的构造函数”。
这是因为,父类overload了一个有参的构造函数,这时候编译器将不会给class Creature自动生成一个无参的构造函数,而子类的构造函数会隐式的使用super()去调用父类的无参构造函数,这就导致了子类的构造函数无法使用父类的构造函数去构造一个新的子类。
这个问题有两个解决方法:1.在父类中显示的编写一个无参构造函数;2. 在子类构造函数的第一行,显示的使用super()去调用父类的有参构造函数。
下面来看正确的写法:
public class Creature {
int weight;
String name;
Creature() {}
Creature(int weight, String name) {
this.weight = weight;
this.name = name;
}
}
class Rabbit extends Creature {
String furType;
int speed;
Rabbit(String furtype, int speed){
// 父类中有无参构造函数,可以隐式调用
this.furType = furtype;
this.speed = speed;
}
}
class Turtle extends Creature {
String shellType;
int speed;
Turtle(String shelltype, int speed) {
super(10, "TMNT"); // 显式调用父类的有参构造函数,也需要传参
this.shellType = shelltype;
this.speed = speed;
}
}
如果有某个overload后的构造函数除了不能处理不同类型的参数之外,可以处理所有的工作,这个时候,我们可以使用一个调用this()方法,对对象本身进行引用。
概括来说,使用this()方法可以从某个构造函数调用同一个类的另外一个构造函数。
this()方法只能用于构造函数的第一行,也就是说,一个构造函数只能选择this或者super之一。
代码如下(示例):
public class Creature {
int weight;
String name;
Creature() {}
Creature(int weight, String name) {
this.weight = weight;
this.name = name;
}
public static void main(String[] args) {
Turtle t = new Turtle("Blue", 20);
System.out.println(t.weight + ", " + t.name + ", " + t.shellType + ", " + t.speed);
}
}
class Turtle extends Creature {
String shellType;
int speed;
Turtle() {
this("Green", 500); // 无参构造函数以默认的shellType和speed调用真正的构造函数
}
// 这才是真正的构造函数
Turtle(String shelltype, int speed) {
super(10, "TMNT");
System.out.println(shelltype + ", " + speed);
this.shellType = shelltype;
this.speed = speed;
}
}
输出为:
Blue, 20
10, TMNT, Blue, 20
(方法修饰符中有static时)普通方法调用:类名.方法名(实参)
(方法修饰符中没有有static时): 引用.方法名(实参)
对于构造方法来说,”返回值类型“不需要指定。并且不能写void。
每一个构造方法执行完之后,都有返回值。但是类似“return”的语句不需要写(无返回值类型)。
构造方法结束之后,java程序自动返回值。返回类型是构造方法所在类的类型。由于返回类型是类本身,所以返回值类型不需要编写。