Java笔记03:构造方法

前言

编程中有两个安全性问题很常见:初始化清理。C语言中很多的bug都是因为程序员忘记初始化导致的。清理则是另一个重要的问题,常常会有人在程序中使用了一些资源(特别是内存空间)而忘记回收。
Java引入了构造器机制,每个类都有一个特殊的构造方法,当创建这个类的对象时,构造方法就会自动被调用。另外Java还使用了垃圾收集器(Garbage Collector, GC)去自动回收不再被使用的对象所占的资源。

构造方法

对于一个有很多不同字段(field)的类,如果直接要去将每个字段都初始化,像下面这样:

Person ming = new Person();
ming.name = "xiaoming"
ming.age = 20
ming.score = 9

或者为了安全性考虑,类常常会将一些字段以private修饰符修饰,这样的字段只能通过类内部的方法去访问,不能直接赋值,你可能想着建一个init()方法,但是这样每次创建实例都要去记得调用一遍。
Java实现了一类特殊的构造方法,创建类实例时就会自动调用,为了解决以下两个问题:

  • 构造方法名可能和其它方法名冲突
  • Java如何知道哪个方法是构造方法并去调用

构造方法要遵循和类名称同名的命名规范。

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Xiao Ming", 15);
        System.out.println(p.getName());
        System.out.println(p.getAge());
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

Person类的定义中,有着一个同名的Person方法,这个方法会在new的时候被调用:

Person p = new Person("Xiao Ming", 15);

构造方法可以没有参数(事实上如果你没有自己定义构造方法,解释器会帮你加上一个空的构造方法),也不需要返回值,但是不需要void修饰符。

如果一个类的字段本身已经初始化过:

class Person {
    private String name = "xiao ming";
    private int age = 9;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

那么在创建对象后哪个才是这个字段的值呢?在创建实例时,会先对字段进行初始化,然后再调用构造方法。

方法重载(overload)

在同一个类中,可以有很多同名的方法,但是参数不同。我们把这称为方法重载。方法重载是很有用的,对于有时候有些功能类似的方法我们可以给它们相同的名字,但是使用不同参数,这样只要通过不同的调用就能达到不同的目的。

将人类语言细微的差别映射到编程语言中会产生一个问题。通常,相同的词可以表达多种不同的含义——它们被"重载"了。特别是当含义的差别很小时,这会更加有用。你会说"清洗衬衫"、"清洗车"和"清洗狗"。而如果硬要这么说就会显得很愚蠢:"以洗衬衫的方式洗衬衫"、"以洗车的方式洗车"和"以洗狗的方式洗狗",因为听众根本不需要区分行为的动作。大多数人类语言都具有"冗余"性,所以即使漏掉几个词,你也能明白含义。你不需要对每个概念都使用不同的词汇——可以从上下文推断出含义。

对于构造方法,同样也可以重载:

class Person {
    String name;
    Person() {
        this.name = "No one";
    }
    Person(String name) {
        this.name = name;
    }
}

现在我们有两种方式来创建对象:

Person ming = new Person();
Person hong = new Person("xiao hong");

打印这个两个对象的name,一个使用了无参数构造函数赋值的默认字符串,一个则使用了我们指定的名字。

要注意重载方法要有不同的参数或者不同顺序的参数来让编译器知道你使用的具体是哪个方法。

this关键字

this是一个特殊的关键字,它永远表示当前实例本身。

class Person {
    String name;
    Person() {
        name = "No one";
    }
}

像上面这个写法是可行的,但是当构造方法变成下面这样:

class Person {
    String name;
    Person(String name) {
        name = name;
        // this.name = name; 才是正确写法
    }
}

构造函数获取的形式参数名为name,这与我们类自身的字段同名了,对于这种命名有冲突的情况,就要使用this关键字了。this表示实例本身,this.name也就表示它自身的name字段了。

当你在一个类中写了多个构造方法,有时你想在一个构造方法中调用另一个构造方法来避免代码重复。就可以用this关键字实现这样的调用。

class Person {
    String name;
    int age;
    Person() {
        this("xiao ming", 12);
    }
    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

如上图,我们在无参数的构造方法里调用了另一个有参数的构造方法,并赋予了默认值。

注意:this的用途很多,但是不能用在static修饰的静态方法里。

static关键字

static关键字用来表示静态字段或静态方法。

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        Person hong = new Person();
        System.out.println(ming.name);
        hong.name = "xiao hong";
        System.out.println(ming.name);
    }
}

class Person {
    static String name = "xiao ming";
}

我们将Person类的name字段定义为静态,运行上面的程序你会发现,当你修改了hong.name,打印出的ming.name也被改变了。
对于一个类的静态字段,不论你创建出多少个对象,它们都共享同一个值。你创建一百个Person实例,它们的name字段也只占一个内存空间。
甚至于你不实例化对象,也是一样能访问这个字段的,直接通过类访问就行了:

System.out.println(Person.name);

我们再来看看静态方法

public class Main {
    public static void main(String[] args) {
        Person.getName();

    }
}

class Person {
    static void getName() {
        System.out.println("xiao ming");
    }
}

对于静态方法,我们根本没有实例化Person类,而是直接通过这个类就去调用这个方法了。
对于为什么main()这个入口方法一定要用static来修饰我想你应该已经有了答案。
静态方法里不能调用非静态方法,但是反过来是可以的。因为非静态方法必须要创建对象之后通过对象来使用。

扫码关注:
二维码

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章