编程中有两个安全性问题很常见: 初始化 与 清理 。C语言中很多的bug都是因为程序员忘记初始化导致的。清理则是另一个重要的问题,常常会有人在程序中使用了一些资源(特别是内存空间)而忘记回收。
Java引入了 构造器 机制,每个类都有一个特殊的构造方法,当创建这个类的对象时,构造方法就会自动被调用。另外Java还使用了 垃圾收集器 (Garbage Collector, GC)去自动回收不再被使用的对象所占的资源。
对于一个有很多不同字段(field)的类,如果直接要去将每个字段都初始化,像下面这样:
Person ming = new Person(); ming.name = "xiaoming" ming.age = 20 ming.score = 9
或者为了安全性考虑,类常常会将一些字段以 private
修饰符修饰,这样的字段只能通过类内部的方法去访问,不能直接赋值,你可能想着建一个 init()
方法,但是这样每次创建实例都要去记得调用一遍。
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; } }
那么在创建对象后哪个才是这个字段的值呢?在创建实例时,会先对字段进行初始化,然后再调用构造方法。
在同一个类中,可以有很多同名的方法,但是参数不同。我们把这称为 方法重载 。方法重载是很有用的,对于有时候有些功能类似的方法我们可以给它们相同的名字,但是使用不同参数,这样只要通过不同的调用就能达到不同的目的。
将人类语言细微的差别映射到编程语言中会产生一个问题。通常,相同的词可以表达多种不同的含义——它们被"重载"了。特别是当含义的差别很小时,这会更加有用。你会说"清洗衬衫"、"清洗车"和"清洗狗"。而如果硬要这么说就会显得很愚蠢:"以洗衬衫的方式洗衬衫"、"以洗车的方式洗车"和"以洗狗的方式洗狗",因为听众根本不需要区分行为的动作。大多数人类语言都具有"冗余"性,所以即使漏掉几个词,你也能明白含义。你不需要对每个概念都使用不同的词汇——可以从上下文推断出含义。
对于构造方法,同样也可以重载:
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
是一个特殊的关键字,它永远表示当前实例本身。
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
关键字用来表示静态字段或静态方法。
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
来修饰我想你应该已经有了答案。
静态方法里不能调用非静态方法,但是反过来是可以的。因为非静态方法必须要创建对象之后通过对象来使用。
扫码关注: