前言
编程中有两个安全性问题很常见:初始化与清理。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
来修饰我想你应该已经有了答案。
静态方法里不能调用非静态方法,但是反过来是可以的。因为非静态方法必须要创建对象之后通过对象来使用。
扫码关注: