zoukankan      html  css  js  c++  java
  • 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()的调用必须是构造函数中的第一个语句

     

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

                                     

  • 相关阅读:
    example_php文件上传处理
    andorid SQLite数据库创建文件
    andorid 手机外部储存
    手机界面
    andorid 数据储存、SharedPreferences存储和手机内部储存
    andorid 数据储存
    andorid 多线程handler用法
    andorid 配置器组件和提示消息
    andorid 网格视图GridView
    andorid 列表视图之SimpleAdapter
  • 原文地址:https://www.cnblogs.com/springl/p/13548824.html
Copyright © 2011-2022 走看看