1. 什么是构造方法
public class Son {
private String name;
private int age;
Son(String name,int age){
this.name = name;
this.age = age;
}
}
上面的用法并不少见,他就是平时所述的构造函数(方法)。
2. 概述
在每一次创建对象时,构造方法的作用是为了给该对象变量赋初始值。也就是说,在每一次new一个对象时,对象成员可以由构造方法来进行初始化。
public static void main(String[] args){
new Son("xiaoming",12);
}
一个类中可以有多个构造方法,但这些构造方法参数的类型、个数、排序顺序的不同来区分它们,即构造方法的重载。
public class Son {
private String name;
private int age;
Son(){
this.name = "Nick";
this.age = 30;
}
Son(String name,int age){
this.name = name;
this.age = age;
}
}
相应的,一个类中也不可能没有构造方法,如果这个类中没有写构造方法,那么在编译时会自动给这个类添加一个无参数,且方法体为空的构造方法。
public class Son {
Son(){
}
}
3. 特点
1. 构造方法的方法名必须和类名完全保持一致,包括大小写。
2. 构造方法不能在方法名前面定义方法类型,即不能定义void,也不能有返回值(不能有return语句)。
3. 构造方法的作用是完成对象的初始化,能够把定义对象时的参数传给对象的域。
4. 构造方法不能自己调用,对象初始化的时候由系统自动调用。
5. 一个类中的构造方法可以重载;在没有定义构造方法的时候,编译时自动生成一个默认构造方法(无参,方法体为空)。
4. 无参构造方法
编译系统默认为无参的构造方法,每次new一个对象出来的时候,这个构造方法就被执行了。
public class Son {
public Son(){
System.out.println("Son construction is Running");
}
}
测试类:
public class Test {
public static void main(String[] args){
new Son();
}
}
输出结果显示,这个无参构造方法被执行了。
5. 有参构造方法
在对象实例化的时候就为对象赋值,这时可以通过构造方法,直接为私有成员赋值。
该例子中可以看到有两个私有成员:name和age,传入的参数分别将这两个私有成员赋值,然后再分别通过getName()和getAge()两个方法将他们返回。
public class Son {
private String name;
private int age;
public Son(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
这里我们将Toy和13分别传入,并打印。
public class Test {
public static void main(String[] args){
Son son2 = new Son("Toy",13);
System.out.println("Son2 :name is " + son2.getName());
System.out.println("Son2 :age is " + son2.getAge());
}
}
打印结果出来了,两个私有变量分别被赋到了新的值。
6. 构造方法的重载
构造方法的重载,就可以通过调用不同的构造方法为不同的属性赋值了。
普通方法都会根据参数的不同来进行方法的重载,而构造方法是一种特殊的方法,当然也会有,如果把上面两个例子放在一起,也可以称为构造方法的重载。
public class Son {
private String name;
private int age;
public Son(){
this.name = "Tom";
this.age = 30;
}
public Son(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
分别调用Son1和Son2来对他不同的操作。
public class Test {
public static void main(String[] args){
Son son1 = new Son();
System.out.println("Son1 :name is " + son1.getName());
System.out.println("Son1 :age is " + son1.getAge());
System.out.println("****************************");
Son son2 = new Son("Toy",13);
System.out.println("Son2 :name is " + son2.getName());
System.out.println("Son2 :age is " + son2.getAge());
}
}
打印结果如下。
如果现在来看String.java,是不是有些似曾相识?
public String() {
// Android-changed: Constructor unsupported as all calls are intercepted by the runtime.
throw new UnsupportedOperationException("Use StringFactory instead.");
}
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
* @param original
* A {@code String}
*/
public String(String original) {
// Android-changed: Constructor unsupported as all calls are intercepted by the runtime.
throw new UnsupportedOperationException("Use StringFactory instead.");
}
/**
* Allocates a new {@code String} so that it represents the sequence of
* characters currently contained in the character array argument. The
* contents of the character array are copied; subsequent modification of
* the character array does not affect the newly created string.
*
* @param value
* The initial value of the string
*/
public String(char value[]) {
// Android-changed: Constructor unsupported as all calls are intercepted by the runtime.
throw new UnsupportedOperationException("Use StringFactory instead.");
}
/**
* Allocates a new {@code String} that contains characters from a subarray
* of the character array argument. The {@code offset} argument is the
* index of the first character of the subarray and the {@code count}
* argument specifies the length of the subarray. The contents of the
* subarray are copied; subsequent modification of the character array does
* not affect the newly created string.
*
* @param value
* Array that is the source of characters
*
* @param offset
* The initial offset
*
* @param count
* The length
*
* @throws IndexOutOfBoundsException
* If the {@code offset} and {@code count} arguments index
* characters outside the bounds of the {@code value} array
*/
public String(char value[], int offset, int count) {
// Android-changed: Constructor unsupported as all calls are intercepted by the runtime.
throw new UnsupportedOperationException("Use StringFactory instead.");
}
/**
* Allocates a new {@code String} that contains characters from a subarray
* of the <a href="Character.html#unicode">Unicode code point</a> array
* argument. The {@code offset} argument is the index of the first code
* point of the subarray and the {@code count} argument specifies the
* length of the subarray. The contents of the subarray are converted to
* {@code char}s; subsequent modification of the {@code int} array does not
* affect the newly created string.
*
* @param codePoints
* Array that is the source of Unicode code points
*
* @param offset
* The initial offset
*
* @param count
* The length
*
* @throws IllegalArgumentException
* If any invalid Unicode code point is found in {@code
* codePoints}
*
* @throws IndexOutOfBoundsException
* If the {@code offset} and {@code count} arguments index
* characters outside the bounds of the {@code codePoints} array
*
* @since 1.5
*/
public String(int[] codePoints, int offset, int count) {
// Android-changed: Constructor unsupported as all calls are intercepted by the runtime.
throw new UnsupportedOperationException("Use StringFactory instead.");
}
有了这些构造方法,也可以通过this.(参数)用其中的一个构造方法去调用这个类中其他的构造方法,这个类就更灵活了有木有啊。
来来来,仿写一个。
public class Son {
private String name;
private int age;
private int height;
public Son(String name,int age,int height){
this.name = name;
this.age = age;
this.height = height;
}
public Son(int hegiht){
this("Tom",18,hegiht);
}
public Son(int age,int height){
this("Nick",age,height);
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public int getHeight(){
return height;
}
}
接下来是测试类。
public class Test {
public static void main(String[] args) {
Son son1 = new Son(180);
System.out.println("Son1 : name = " + son1.getName() + " ; age = " + son1.getAge() + "; height = " + son1.getHeight());
System.out.println("********************************************");
Son son2 = new Son(38,160);
System.out.println("Son2 : name = " + son2.getName() + " ; age = " + son2.getAge() + "; height = " + son2.getHeight());
}
}
发现输出结果中未传入的参数都有了默认值,但我觉得最大的好处就是可以根据不同的参数决定调用哪个构造方法。
7. 继承类中的构造方法
这里做一个小实验。
有三个类依次继承,Son继承自Dad,而Dad又继承自Grandfather;然后分别实现他们的构造方法。
Son.java
public class Son extends Dad{
public Son(){
System.out.println("This is Son");
}
}
Dad.java
public class Dad extends Grandfather {
public Dad(){
System.out.println("This is Dad");
}
}
Grandfather.java
public class Grandfather {
public Grandfather(){
System.out.println("This is Grandfather");
}
}
三个类中各有一句打印,下面来实例化一下Son这个对象。
public class Test {
public static void main(String[] args){
new Son();
}
}
却发现输出竟然是这样。
它把Son的父类,再往上父类的构造方法全都执行了一遍。从父类依次向下的顺序,也就是说,实例化子类的时候,首先要先把它的父类实例化出来。
那么,这种情况下的构造参数都是无参的,那如果Grandfather.java的构造方法是一个有参数的呢?
public class Grandfather {
public Grandfather(String value){
System.out.println("This is Grandfather");
}
}
果然不负众望,报错了。
所以需要用super()来手动调用一下。
public class Dad extends Grandfather {
public Dad(){
super("do");
System.out.println("This is Dad");
}
}
OK,又可以成功运行了。
参考文献
https://blog.csdn.net/zw1996/article/details/52878270