05_面向对象基础篇_02-构造方法、匿名对象、对象比较、this关键字

本章章节

> 5.1 面向对象程序设计的基本概念
> 5.2类与对象
> 5.3类的封装性
> 5.4在类内部调用方法
> 5.5引用数据类型的传递
> 5.6构造方法
> 5.7匿名对象
> 5.8对象的比较
> 5.9 this 关键字的使用
> 5.10 static 关键字的使用
> 5.11构造方法的私有
> 5.12对象数组的使用
> 5.13 Java基本数据类型的包装类
> 5.14 String类的常见方法
> 5.15 Java文档注释
.本章摘要:

 

5.6 构造方法

  在前面的Java程序里,我们将属性封装之后,需要提供公共的getXxxsetXxx函数集来初始化变量和获取变量。利用构造方法也可以实现给新创建的对象的各个成员属性赋初值。构造方法可视为一种特殊的方法,它的定义方式与普通方法类似,其语法如下所示:

 

<修饰符>  class 类名称
{
    访问权限  类名称(类型1 参数1,类型2 参数2,…)
    {
        程序语句;
        … // 构造方法没有返回值
    }
}    

 

  在使用构造方法的时候请注意以下几点:

    1构造方法的名称与类的名称相同

    2构造方法没有返回值

    3、构造方法可以重载

  如上所述,构造方法除了没有返回值,且名称必须与类的名称相同之外,它的调用时机也与一般的方法不同。一般的方法是在需要时才调用,而构造方法则是在创建对象时,便自动调用,并执行构造方法的内容。因此,构造方法无需在程序中直接调用,而是在对象产生时自动执行。

  基于上述构造方法的特性,可利用它来对对象的数据成员做初始化的赋值。所谓初始化就是为对象的赋初值。

  下面的程序TestConstruct.java说明了构造方法的使用。

范例:TestConstruct.java

class Person
{
    public Person()   // Person类的构造方法
    {
        System.out.println("public Person()");
    }    
}

public class TestConstruct
{
    public static void main(String[] args)
    {
        Person p = new Person();
    }
}        

 

输出结果:

  public Person()

程序说明:

  1、程序1~7行声明了一个Person类,此类中只有一个Person的构造方法。

  2、程序3~6行声明了一个Person类的构造方法,此方法只含有一个输出语句。

  3、程序第12行实例化一个Person类的对象p,此时会自动调用Person中的无参构造方法,即在屏幕上打印信息。

  从程序中不难发现,在类中声明的构造方法,会在实例化对象时自动调用。你可能会问,在之前的程序中用同样的方法来产生对象,但是在类中并没有声明任何构造方法,而程序不也一样正常运行了吗?

实际上,在执行javac编译java程序的时候,如果在程序中没有明确声明构造方法的话,系统会自动为类加入一个无参的且什么都不做的构造方法。类似于下面代码:

  public Person(){}

  所以,之前所使用的程序虽然没有明确的声明构造方法,也是可以正常运行的。但是需要注意的是,一旦用户自己写了构造函数之后,系统就不会再提供默认的构造函数。

5.6.1 构造方法的重载

  在Java里,不仅普通方法可以重载,构造方法也可以重载。只要构造方法的参数个数不同,或是类型不同或顺序不同,便可定义多个名称相同的构造方法。这种做法在java中是常见的,请看下面的程序。

范例:TestConstruct1.java

class Person
{
  private String name;
  private int age;
  
public Person(String n, int a)   {     name = n;     age = a;     System.out.println("public Person(String n, int a)");   }   public String talk()   {     return "我是:"+name+",今年:"+age+"岁";   } } public class TestConstruct1 {   public static void main(String[] args)   {     Person p = new Person("张三", 25);     System.out.println(p.talk());     //Person p2 = new Person();//加入此行会报错,因为没有无参的构造函数     //System.out.println(p2.talk());   } }

 

输出结果:

  public Person(String n, int a)

  我是:张三,今年:25

程序说明:

  1、程序1~15行声明了一个Person类,里面有nameage两个私有属性,和一个talk()方法。

  2、程序第5~10行,在Person类中声明一个含有两个参数的构造方法,此构造方法用于将传入的值赋给Person类的属性。

  3、程序第20行调用Person类中有的含有两个参数的构造方法“new Person("张三",25)”。

  4、程序第21行调用Person类中的talk()方法,打印信息。

  从本程序可以发现,构造方法的基本作用就是为类中的属性初始化的,在程序产生类的实例对象时,将需要的参数由构造方法传入,之后再由构造方法为其内部的属性进行初始化。这是在一般开发中经常使用的技巧。但是如果加入2223行的代码就会报错。因为在java程序中只要明确的声明了构造方法,则默认的构造方法将不会被自动生成。而要解决这一问题,只需要简单的修改一下Person类就可以了,可以在Person类中明确地声明一个无参的且什么都不做的构造方法。

范例:TestConstruct2.java

class Person
{
    private String name;
    private int age;
    public Person()
    {
    }

    public Person(String n, int a)
    {
        name = n;
        age = a;
        System.out.println("public Person(String n,int a)");
    }

    public String talk()
    {
        return "我是:" + name + ",今年:" + age + "岁";
    }
}

public class TestConstruct2
{
    public static void main(String[] args)
    {
        Person p = new Person();
        System.out.println(p.talk());
    }
}

 

  可以看见,在程序的第56行声明了一个无参的且什么都不做的构造方法,此时再编译程序的话,就可以正常编译而不会出现错误了。

5.7 匿名对象

  “匿名对象”,顾名思义,就是没有名字的对象。也可以简单的理解为只使用一次的对象,即没有任何一个具体的对象名称引用它。请看下面的范例:

范例:TestNoName.java

class Person
{
    private String name = "张三";
    private int age = 25;
    public String talk()
    {
        return "我是:" + name + ",今年:" + age + "岁";
    }
}

public class TestNoName
{
    public static void main(String[] args)
    {
        System.out.println(new Person().talk());
    }
}

 

输出结果:

  我是:张三,今年:25

程序说明:

  1、程序1~9行声明了一个Person类,里面有nameage两个私有属性,并分别赋了初值。

  2、在程序第14行,声明了一个Person匿名对象,调用Person类中的talk()方法。可以发现用new Person()声明的对象并没有赋给任何一个Person类对象的引用,所以此对象只使用了一次,之后就会被Java的垃圾收集器回收。

5.8 对象的比较

  Java程序中测试两个变量是否相等有两种方式,一种是利用==运算符,另一种是利用equals方法。

  当使用==来判断两个变量是否相等时,如果两个变量是基本数据类型的变量,且都是数值类型,只要两个变量的值相等,就会返回布尔值true。但对于两个引用类型的变量,他们必须指向同一个对象时,才会返回布尔值true。否则,返回false

  equals方法是Object类(所有类的父类)提供的一个实例方法,所有引用变量都可以调用该方法判断是否与其他变量相等。Object类提供的equals方法没有太大意义,默认的equals方法,当两个引用变量指向一个对象才会返回true,跟“==”类似。如果希望采用自定义的相等标准,可以采用重写equals方法来实现。

  先以String类为例,String类中已经重写了equals方法。==”操作符用于比较两个String对象的内存地址值是否相等,equals()方法用于比较两个String对象的内容是否一致。

范例:TestEquals.java

public class TestEquals
{
    public static void main(String[] args)
    {
        String str1 = new String("java");
        String str2 = new String("java");
        String str3 = str2;
        if (str1 == str2)
            System.out.println("str1 == str2");
        else
            System.out.println("str1 != str2");
        if (str2 == str3)
            System.out.println("str2 == str3");
        else
            System.out.println("str2 != str3");
    }

}

 

输出结果:

  str1 != str2

  str2 == str3

  由程序的输出结果可以发现,str1不等于str2str1str2的内容完全一样,为什么会不等于呢?在程序的第5和第6行分别实例化了String类的两个对象,此时,这两个对象指向不同的内存空间,所以它们的内存地址是不一样的。这个时候程序中是用的“==”比较,比较的是内存地址值,所以输出str1!=str2。程序第7行,将str2的引用赋给str3,这个时候就相当于str3也指向了str2的引用,此时,这两个对象指向的是同一内存地址,所以比较值的结果是str2==str3

  那该如何去比较里面的内容呢?这就需要采用另外一种方式——equals,请看下面的程序,下面的程序TestEquals1.java修改自程序TestEquals.java,如下所示:

范例:TestEquals1.java

public class TestEquals1
{
    public static void main(String[] args)
    {
        String str1 = new String("java");
        String str2 = new String("java");
        String str3 = str2;
        if (str1.equals(str2))
            System.out.println("str1 equals str2");
        else
            System.out.println("str1 not equals str2");
        if (str2.equals(str3))
            System.out.println("str2 equals str3");
        else
            System.out.println("str2 note equals str3");
    }
}

 

输出结果:

  str1 equals str2

  str2 equals str3

  这个时候可以发现,在程序中将比较的方式换成了equals,而且调用equals()方法的是String类的对象,所以可以知道equalsString类中的方法。在这里一定要记住:“==”是比较内存地址值的,“equals”是比较内容的。

小提示:

  你可能会问,下面两种String对象的声明方式到底有什么不同?

  String str1 = new String("java");

  String str2 = "java";

下面先来看一个范例:

public class StringDemo
{
    public static void main(String[] args)
    {
        String str1 = "java";
        String str2 = new String("java");
        String str3 = "java";
        System.out.println("str1 == str2 ? --- > " + (str1 == str2));
        System.out.println("str1 == str3 ? --- > " + (str1 == str3));
        System.out.println("str3 == str2 ? --- > " + (str3 == str2));
    }
}

 

输出结果:

  str1 == str2 ? --- > false

  str1 == str3 ? --- > true

  str3 == str2 ? --- > false

  由程序输出结果可以发现,str1str3相等,这是为什么呢?还记得上面刚提到过“==”是用来比较内存地址值的。现在str1str3相等,则证明str1str3是指向同一个内存空间的。可以用图5-11来说明:

 

5-11  String对象的声明与使用

  由图中可以看出java”这个字符串在内存中开辟的一个空间,而str1str3又同时指向同一内存空间,所以即使str1str3虽然是分两次声明的,但最终却都指向了同一内存空间(可以把它理解为C语言的文字常量区)。而str2是用new关键字来开辟的空间,所以单独占有自己的一个内存空间。

  另外,需要注意的是,如果用new关键字开辟String对象的内存空间的话,则实际上就开辟了两个内存空间,如图5-12所示:

 

5-12  String对象内容的改变

  由图 5-12A)中不难发现,如果要改变str1的值,则会先断开原有的对象引用,再开辟新的对象,之后再指向新的对象空间。

  用图 5-12B)的方法也可以实现改变String对象的声明的操作,可以发现,用new String("java")方式实例化 String 对象时,实际上是开辟了两个内存空间,所以一般在开发上都采用直接赋值的方式,即:String str1 = "java"

  再来看看普通类的实例:

范例:TestCompare.java

class Person
{
    String name;
    int age;
Person(String name,
int age) { this.name = name; this.age = age; } public boolean equals(Object obj) //可以试着将这个函数注释掉 { if (obj instanceof Person)
     { Person p
= (Person) obj; if (this.name.equals(p.name) && this.age == p.age) return true; } return false; } } public class Test { public static void main(String[] args) { Person p1 = new Person("张三", 30); Person p2 = new Person("张三", 30);     //直接==,比较的是地址 System.out.println(p1 == p2 ? "相等,是同一人!" : "不相等,不是同一人!");     //调用自己复写的equals方法,比较的是内容 System.out.println(p1.equals(p2) ? "相等,是同一人!" : "不相等,不是同一人!"); } }

 

输出结果:

  相等,是同一人!

  实例中我们复写了Object类的equals方法,此时利用p1.equals(p2)调用的是自己本身的equals方法,按照自己定义的比较规则来比较两个对象是否相同。

5.9 this 关键字的使用

  在整个java的面向对象程序设计中,this是一个比较难理解的关键字,在前面调用类内部方法时,曾经提到过thisthis强调对象本身,那么什么是对象本身呢?其实就是当前对象本身,而所谓的当前对象就是指调用类中方法或属性的那个对象。即“哪个对象调用的,则this就具备谁的身份”。

this 关键字有以下作用:

  ·通过this操作类中的属性和普通方法;

  ·使用this调用本类的构造方法;

  ·this表示当前对象;

  先来看一下下面的程序片段:

范例:Person.java

class Person
{
    private String name;
    private int age;
public Person(String name,int age) { name = name; age = age; } }

 

  看上面的程序可以发现会有些迷惑,程序的本意是通过构造方法为nameage进行初始化的,但是在构造方法中声明的两个参数的名称也同样是nameage,此时执行name=name,没能完成赋值,只是将形参变量自己赋给了自己。没能修改到类中的成员变量。为了避免这种混淆的出现,可以采用this这种方式,请看修改后的代码:

范例:Person.java

class Person
{
    private String name;
    private int age;

    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

 

  Person.java这段代码与之前的不同之处在于在第78行分别加上了this关键字。还记得之前说过的this表示当前对象吗?那么此时的this.namethis.age就分别代表类中的nameage属性,这个时候再完成赋值操作的话,就可以清楚的知道谁赋值给谁了。完整程序代码如下:

范例:TestJavaThis.java

class Person
{
    private String name;
    private int age;

    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }

    public String talk()
    {
        return "我是:" + name + ",今年:" + age + "岁";
    }
}

public class TestJavaThis
{
    public static void main(String[] args)
    {
        Person p = new Person("张三", 25);
        System.out.println(p.talk());
    }
}

 

输出结果:

  我是:张三,今年:25

程序说明:

  1、程序第1~14行声明了一个名为Person的类。

  2、第5~9行声明Person类的一个构造方法,此构造方法的作用是为类中的属性赋初值。

5.9.1 this 调用构造方法

  如果在程序中想用某一构造方法调用另一构造方法,可以用this来实现,具体的调用形式如下:

    this();

  在使用 this 关键字调用其他构造方法的时候,有以下几点限制;

    ·this()调用构造方法的语句只能放在构造方法的首行(非构造方法不可以使用)

    ·至少有一个构造方法是不用 this调用的。

范例:TestJavaThis1.java

class Person
{
    String name;
    int age;
public Person() { System.out.println("1. public Person()"); } public Person(String name, int age) {     // 调用本类中无参构造方法 this(); this.name = name; this.age = age; System.out.println("2. public Person(String name,int age)"); } } public class TestJavaThis1 { public static void main(String[] args) { new Person("张三", 25); } }

 

输出结果:

  1. public Person()

  2. public Person(String name,int age)

程序说明:

  1、程序1~17行,声明一个名为Person的类,类中声明了一个无参和一个有两个参数的构造方法。

  2、程序第12行使用this()调用本类中的无参构造方法。

  3、程序第22行声明一个Person类的匿名对象,调用了有参的构造方法。

  由程序TestJavaThis1.java可以发现,在第22行虽然调用了Person中有两个参数的构造方法,但是由于程序第12行,使用了this()调用本类中的无参构造方法,所以程序先去执行Person中无参构造方法,之后再去继续执行其他的构造方法。

注意:

  如果我把this()调用无参构造方法的位置任意调换,那不就可以在任何时候、任何方法里面都可以调用构造方法了么?实际上这样理解是错误的。构造方法是在实例化一对象时被自动调用的,也就是说在类中的所有方法里,只有构造方法是被优先调用的,所以使用this调用构造方法必须也只能放在构造方法的第一行,下面的程序就是一个错误的程序:

class Person
{
    String name;
    int age;

    public Person()
    {
        System.out.println("1. public Person()");
    }

    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;

    // 用this调用构造方法,此时不是放在构造方法的首行,错
        this();

        System.out.println("2. public Person(String name,int age)");
    }

    public String talk()
    {
    // 普通函数调用this,错
        this();

        System.out.println("我是:" + name + ",今年:" + age + "岁");
    }
}

public class TestJavaThis1
{
    public static void main(String[] args)
    {
        new Person("张三", 25);
    }
}

 

由此可见,this()的调用必须是构造函数中的第一个语句

 

 

感谢阅读。如果感觉此章对您有帮助,却又不想白瞟

                                 

 

 

 

posted @ 2020-08-23 12:33  SpringL  阅读(173)  评论(0编辑  收藏  举报