构造方法
首先,提一下类和对象的概念,因为下边解释的时候,会有用到!
类和对象
类(class)是构造对象的模板,或者说是一件图纸!
由类构造( construct)对象的过程被称为创建类的实例。
更加通俗的一种理解就是: 类是对现实生活中事物的描述,对象就是这类事物,实实在在的个体。
那么在java这两个,描述就是class定义的类,具体对象就是在java的堆内存中用关键字new建立的实体。用一个汽车图纸和汽车来举例!
上面这张图,很形象的表现出了类和对象的关系。汽车图纸就是一个类,一个汽车类。很显然,你要用汽车的话,不可能直接拿着图纸就去开吧。当然是要生产汽车啊,然后去开呗~
这个时候就需要去建立对象了,底下的那些汽车1、汽车2、汽车3自然就是那些实例出来的对象了。
我们知道java中建立对象一般是用new关键字的,除了一些特殊的对象以外(比如:String类的匿名对象可以是“我真帅!”,显然没用用到new,但是它却是在堆内存中开辟了一块空间,并且还把内容装了进去。)。
那么现在有了对象怎么去使用对象呢?这就牵扯到了我们今次的主角了,构造方法。要想使用对象,就必须首先建立对象,然后再指定其初始状态,然后才是对对象引用方法。
构造器
在java程序设计语言中,使用构造器(constructor)构造新实例。构造器时一种特殊方法,用来构造并初始化对象。
首先要注意的是,对象使用之前,必须要实例化,而实例化就是在调用构造器(有时也叫构造方法)。如果一个对象没有实例化而直接调用了对象中的属性或者方法,就会出现空指针异常。下面一个例子来说明这一情况(为了演示的清楚,并没有用到封装):
package 面向对象方面;
public class PersonTest {
public static void main(String[] args) {
Person p1 = null;
Person p2;
// p2.age = 12;//Initialize variable
p1.name = "东方不败";
p1.age = 10;
p1.say();
}
}
class Person{
int age;
String name;
void say() {
System.out.println("age =" + age +", name =" + name);
}
}
解释一下。这个程序会出现
Exception in thread “main” java.lang.NullPointerException
这个是空指针异常,这就是因为你都没有实例化对象,就让对象办事,根本就不会成的好吗! 那么解决这样问题的方法就是建立对象实例,再让对象去做,就可以了。
同样,问题也来了,在封装之后的程序基本都持有公有方法,可以对其初始化赋值。那么,有没有什么方法能一劳永逸,或者说是你想要的那个对象即将拥有的属性,你就可以赋值给它,不用再去main方法中一个一个的初始化了。
答案当然就是有了,见名知意嘛!构造,就是搭好框架,造东西搬砖。
所以构造方法的作用也就出来了,对对象进行初始化赋值(一般都是属性,还有特殊的需求时,可以自己定义)。
在构造方法的使用上,每新建立一个对象,就会调用一次该对象对应的构造方法。也就是说,它和对象同时产生,同时消亡。
构造方法书写上的注意事项
其名称必须和本类名完全相同。
其没有返回值类型。
不能使用return关键字跳出该方法(这一点,从它没有返回值类型就可以看出了)。
还需要注意的是,只有当调用new关键字实例化对象时才会调用构造方法。
一般地,没有定义构造方法的话,java会有一个隐藏着的没有参数的空构造方法,这也就为什么我们可以不用写构造方法,还能去建立对象实例的原因了。
下面1个例子来看看,没有使用有参数构造方法给对象赋值和使用有参数构造方法给对象赋值的过程。
public static void main(String[] args) {
//用有参数构造器初始化
Person person1 ;
person1 = new Person("老冯",22);
person1.personSay();
//不用有参数构造器初始化
Person person2;
person2 = new Person();
person2.setName("老王");
person2.setAge(10);
person2.personSay();
}
}
class Person{
private int age;
private String name;
//这里写了6个构造器,可以看出它们是可以重载的!
Person(String name,int age){
this.setName(name);
this.setAge(age);
}
Person(int age,String name){
this.setName(name);
this.setAge(age);
}
// Person(int a,String n){
// 编译错误!!与参数列表为(int,String)的构造器是一样的。
// }
Person(String name){
this.setName(name);
}
Person(int age){
this.setAge(age);
}
Person(){
//无参数构造器。没有些任何显示构造器时,它就存在。
}
public void setAge(int age) {
if(age >= 0 && age < 130)
this.age = age;
}
public int getAge() {
return this.age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void personSay() {
System.out.println(name + "的" +"年龄是:" + age);
}
}
上述例子运行的结果是:
老冯的年龄是:22
老王的年龄是:10
两种方式达到了同样的效果。 其中的演化过程就不多说了。
上面提到了构造方法的重载,其实它和普通方法的最大区别在于它没有返回值类型,至于其的的特性,普通方法有的,它也有。
另:构造方法的互相调用,如下代码。
Person(String name,int age){
this(name);
this.setAge(age);
}
Person(int age,String name){
this(name,age);
}
Person(String name){
this.setName(name);
}
Person(int age){
this.setAge(age);
}
Person(){
}
这段调用,是构造方法间的调用,使用this关键字,this不能用于普通方法调用构造器。(注意这时的this只能放在第一行最开始执行。)
this(name); 就是调用了只有name为参数的那个构造器。类似于普通方法间的调用效果。
这样就大大增加了程序的灵活性。
还有一个建议就是,我们平常在写代码的时候,写两个构造器,一个有参数,一个无参数的。这是因为,如果只写了有参数的或者只写了无参数的构造器,在使用该构造器时会有很多麻烦。
比如:有时你不需要参数,但是只写了一个有参数的构造器,隐式的无参数构造器就会消失掉,要是想用它调用一些方法,就得再到之前的地方加上无参数构造器。所以,与其用到了才写,不如我们提前写好!
总结和扩展
和一般方法相比,构造方法的不同点
1. 构造方法的作用:给对象初始化。
注意,当一个类中,没有定义构造函数时,系统会默认给该类加入一个人空参数的构造函数。当在类中自定义了构造函数后,默认的构造函数会消失。
2. 和一般方法比较:写法上不同。
在运行上也不一样,构造方法是对象建立就运行,给对象初始化;而一般方法是对象调用才执行,是给对象添加其具备的功能。
3. 一个对象建立,构造方法只调用一次;而一般方法可以被对象调用多次。
4. 鉴于构造函数只能调用一次,有时却又需要更多的操作(针对属性),这是就要用到封装时的set和get方法。
那什么时候定义构造方法呢?
当我们分析事物时,该事物存在一些特性或行为,那么将这些内容定义在构造函数中。
构造代码块的作用?
作用是给对象进行初始化,对象一经建立就运行,而且优先于构造函数执行。
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化。
一个例子说明构造代码块:
class Person1 {
private String name;
private int age;
{//构造代码块
System.out.println("我是构造代码块,每个对象建立都有我");
}
// 构造函数(默认的无参数)
Person1() {
System.out.println("我是Person的无参数构造");
System.out.println("name=" + this.name + "," + "age=" + this.age);
cry();
}
//构造函数(有1个参数的)
Person1(String name){
this.name = name;
System.out.println("我有1个参数");
System.out.println("name=" + this.name + "," + "age=" + this.age);
cry();
}
//构造函数(有2个参数的)
Person1(String name ,int age){
this.name = name;
this.age = age;
System.out.println("我有2个参数");
System.out.println("name=" + this.name + "," + "age=" + this.age);
cry();
}
void cry(){
System.out.println("cry");
}
}
public class PersonDemo01 {
public static void main(String[] args) {
//无参数实例化
Person1 p1 = new Person1();
System.out.println("-----------------------");
//1个参数实例化
Person1 p2 = new Person1("老王");
System.out.println("-----------------------");
//2个参数实例化
Person1 p3 = new Person1("老冯",22);
System.out.println("-----------------------");
//一般方法可以被调用多次。
p1.cry();
p1.cry();
p1.cry();
}
}
上述代码的运行结果如下;
我是构造代码块,每个对象建立都有我
我是Person的无参数构造
name=null,age=0
cry
——————————————————
我是构造代码块,每个对象建立都有我
我有1个参数
name=老王,age=0
cry
——————————————————
我是构造代码块,每个对象建立都有我
我有2个参数
name=老冯,age=22
cry
——————————————————
cry
cry
cry
因为初始化要先执行,而且只执行1次。this只能在构造函数间使用。不能用于一般函数来调用构造函数。
关于this和构造方法
package com.home.demo1;
/**
* this 和构造函数。
* this只能在构造函数间使用。不能用于一般函数来调用构造函数。
* 初始化的构造函数中,若还调用了其他构造方法,就先执行那个被调用的构造函数。
* 调用时应注意,不能用A构造去调用B构造的同时,还用B构造调用A构造,这样相当于死循环。
* @author Feng
* 2018年2月19日下午4:19:24
*/
class Person3{
private String name;
private int age;
private Person3(String name){//可以对它进行封装,私有。
this.name = name;
}
Person3(String name, int age){
//调用1个参数的构造函数,this必须放在第一行。
//this(name)相当于new Person3(name);
this(name);//参数是多个的时候,可以节省写代码数量,建议用this。
this.name = name;//效果相当于this(name),调用了1个参数的构造函数之后,这句可以省略。
this.age = age;
}
}
public class PersonDemo03 {
public static void main(String[] args) {
Person3 p = new Person3("LIS",4);
//当LIS和4被赋值进去的时候,先执行this(name),把LIS赋进去,然后再去赋值4给age。
//假如在只有name的那个构造函数中有了自己的初始化,比如是this.name = "ggg";
//那么,为了不出错,要在调用了其他构造函数之后,还要把自身的局部变量赋值,不能省略。
}
}
以上陈述希望能帮到各位!有任何疑问我们可以讨论,再次抛转了哈哈。