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来修饰我想你应该已经有了答案。
静态方法里不能调用非静态方法,但是反过来是可以的。因为非静态方法必须要创建对象之后通过对象来使用。

扫码关注:

你可能感兴趣的:(java)