zoukankan      html  css  js  c++  java
  • JavaSE入门学习23:Java面向对象之构造方法

           学了JavaSE面向对象这一部分,也该对构造方法做一个总结了。

           一构造方法

           在多数情况下,初始化一个对象的终于步骤是去调用这个对象的构造方法。

    构造方法负责对象的初始化工作,为

    实例变量赋予合适的初始值。构造方法必须满足下面语法规则:

           (1)方法名必须与类名同样。

           (2)不要声明返回类型;

           (3)不能被static、final、synchronized、abstract和native修饰。

    构造方法不能被子类继承,所以用final和abstract

    修饰没有意义。

    构造方法用于初始化一个新建的对象,所以用static修饰没有意义。多个线程不会同一时候创建内存地址相

    同的同一个对象,因此用synchronized修饰没有必要。

    此外,Java语言不支持native类型的构造方法。

            实例:在下面Sample类中,具有int返回类型的Sample(int x)方法仅仅是个普通的实例方法。不能作为构造方法:

    <span style="font-size:18px;">public class Sample {
        private int x;
    
        //不带參数的构造方法
        public Sample(){
    	this(1);
        }
    
        //带參数的构造方法
        public Sample(int x) { 
            this.x=x;
        }
    
        //不是构造方法,是一般的实例方法
        public int Sample(int x) { 
            return x++;
        }
    
    }</span>

           以上样例虽然能编译通过。可是把实例方法和构造方法同名。不是好的编程习惯,easy引起混淆。

           实例:下面Mystery类的Mystery()方法有void返回类型,因此是普通的实例方法:

    <span style="font-size:18px;">public class Mystery {
    	private String s;
    
    	//不是构造方法
    	public void Mystery(){
    		s = "constructor";
    	}
    
            void go() {
    		System.out.println(s);
            }
    
            public static void main(String[] args) {
    		Mystery m = new Mystery();
                    m.go();
            }
    
    }</span>

           以上程序的打印结果为null。由于用new语句创建Mystery实例时。调用的是Mystery类的默认构造方法,而不是以

    上有void返回类型的Mystery()方法。

           二重载构造方法

           当通过new语句创建一个对象时。在不同的条件下。对象可能会有不同的初始化行为。

    可通过重载构造方法来表

    达对象的多种初始化行为。下面的Employee类的构造方法有三种重载形式。在一个类的多个构造方法中。可能会出现

    一些反复操作。为了提高代码的可重用性,Java语言同意在一个构造方法中,用this语句来调用还有一个构造方法。

           比如对于公司新进来的一个雇员。在一開始的时候,有可能他的姓名和年龄是未知的,也有可能只他的姓名是

    已知的,也有可能姓名和年龄都是已知的。假设姓名是未知的,就暂且把姓名设为"无名氏"。假设年龄是未知的,就

    暂且把年龄设为-1。

           实例:Employee.java源文件代码:

    <span style="font-size:18px;">public class Employee {
        private String name;
        private int age;
    
        //当雇员的姓名和年龄都已知,就调用此构造方法
        public Employee(String name, int age) {
    	this.name = name;
            this.age=age;
        }
    
        //当雇员的姓名已知而年龄未知,就调用此构造方法
        public Employee(String name) {
    	this(name, -1);
        }
    
        //当雇员的姓名和年龄都未知。就调用此构造方法 
        public Employee() {
    	this( "无名氏" );
        }
    
        public void setName(String name){
    	this.name=name; 
        }
    
        public String getName(){
    	return name; 
        }
    
        public void setAge(int age){
    	this.age=age;
        }
    
        public int getAge(){
    	return age;
        }
    
       public static void main(String[] args){
    	Employee zhangsan=new Employee("张三",25);
            Employee lisi=new Employee("李四");
            Employee someone=new Employee();
       }
    }</span>

           上述程序中mian()方法分别通过三个构造方法创建了三个Employee对象。在Employee(String name)构造方法

    中,this(name,-1)语句用于调用Employee(String name,int age)构造方法。在Employee()构造方法中。this("无名氏")

    语句用于调用Employee(String name)构造方法。

           用this语句来调用其它构造方法时。必须遵守下面语法规则:

           (1)假如在一个构造方法中使用了this语句,那么它必须作为构造方法的第一条语句(不考虑凝视语句)。

    下面构造方

    法是非法的:

    <span style="font-size:18px;">public Employee(){
          String name="无名氏";
          this(name);//编译错误。this语句必须作为第一条语句
    }</span>

           (2)仅仅能在一个构造方法中用this语句来调用类的其它构造方法。而不能在实例方法中用this语句来调用类的其它构

    造方法。

           (3)仅仅能用this语句来调用其它构造方法。而不能通过方法名来直接调用构造方法。

    下面对构造方法的调用方式是

    非法的:

    <span style="font-size:18px;">public Employee() {
             String name= "无名氏";
             Employee(name); //编译错误,不能通过方法名来直接调用构造方法
    }</span>

           三默认构造方法

           默认构造方法是没有參数的构造方法。可分为两种:

           a隐含的默认构造方法;

           b程序显式定义的默认构造方法。

           在Java语言中。每一个类至少有一个构造方法。

    为了保证这一点。假设用户定义的类中没有提供不论什么构造方法,那

    么Java语言将自己主动提供一个隐含的默认构造方法。

    该构造方法没有參数,用public 修饰,并且方法体为空。格式为:

    <span style="font-size:18px;">//隐含的默认构造方法
    public ClassName(){
    } </span>

           在程序中也能够显式的定义默认构造方法。它能够是随意的訪问级别。比如:

    <span style="font-size:18px;">//程序显式定义的默认构造方法
    protected Employee() { 
    this("无名氏");
    }</span>

           假设类中显式定义了一个或多个构造方法,而且全部的构造方法都带參数。那么这个类就失去了默认构造方法。

    在下面程序中,Sample1类有一个隐含的默认构造方法,Sample2类没有默认构造方法。Sample3类有一个显式定义

    的默认构造方法:

    <span style="font-size:18px;">class Sample1{ 
    }
    
    class Sample2{
           public Sample2(int a){
                System.out.println("My Constructor");
          }
    }
    
    class Sample3{
           public Sample3(){
                  System.out.println("My Default Constructor");
           }
    }</span>

           能够调用Sample1类的默认构造方法来创建Sample1对象:Sample1 s=new Sample1(); //合法

           Sample2类没有默认构造方法,因此下面语句会导致编译错误:Sample2 s=new Sample2(); //编译出错

           Sample3类显式定义了默认构造方法,因此下面语句是合法的。Sample3 s=new Sample3();

           四子类调用父类的构造方法

           父类的构造方法不能被子类继承。

    下面MyException类继承了java.lang.Exception类:

    <span style="font-size:18px;">// MyException类仅仅有一个隐含的默认构造方法
    public class MyException extends Exception{
    } </span>

           虽然在Exception类中定义了例如以下形式的构造方法:

    <span style="font-size:18px;">public Exception(String msg){
    }</span>

           但MyException类不会继承以上Exception类的构造方法,因此下面代码是不合法的:

    <span style="font-size:18px;">Exception e=new MyException("Something is error");//编译出错。MyException类不存在这种构造方法</span>

           在子类的构造方法中,能够通过super语句调用父类的构造方法。比如:

    <span style="font-size:18px;">public class MyException extends Exception{
            public MyException(){
                  //调用Exception父类的Exception(String msg)构造方法
                  super("Something is error");
           }
    
           public MyException(String msg){
                 //调用Exception父类的Exception(String msg)构造方法
                 super(msg);
           }
    }</span>

            用super语句来调用父类的构造方法时,必须遵守下面语法规则。

            (1)在子类的构造方法中。不能直接通过父类方法名调用父类的构造方法,而是要使用super语句。

            下面代码是非法的:

    <span style="font-size:18px;">public MyException(String msg){
           Exception(msg); //编译错误
    }</span>

           (2)用super语句时,必须放在最前面。

           下面代码是非法的:

    <span style="font-size:18px;">public MyException(){
            String msg= "Something wrong";
            super(msg); //编译错误,super语句必须作为构造方法的第一条语句
    }</span>

           在创建子类的对象时,Java虚拟机首先运行父类的构造方法,然后再运行子类的构造方法。在多级继承的情况

    下,将从继承树的最上层的父类開始,依次运行各个类的构造方法。这能够保证子类对象从全部直接或间接父类中继

    承的实例变量都被正确的初始化。比例如以下面Base父类和Sub子类分别有一个实例变量 a和b,当构造Sub实例时,这两

    个实例变量都会被初始化。

            实例代码:

    <span style="font-size:18px;">class Base{
    	private int a;
    
            public Base(int a){
    		this.a=a;
    	}
    
            public int getA(){
    		return a;
    	}
    }
    
    public class Sub extends Base{
    	private int b;
    
            public Sub(int a,int b){
    		super(a); 
    		this.b=b;
    	}
    
            public int getB(){
    		return b;
    	}
    
            public static void main(String args[]){
    		Sub sub=new Sub(1,2);
                    System.out.println("a="+sub.getA()+",b="+sub.getB()); //打印a=1 b=2
           }
    
    }</span>

            执行结果:

            a=1,b=2

           在以以下的实例中。Son类继承Father类。Father类继承Grandpa类。这三个类都显式定义了默认的构造方法。此

    外还定义了一个带參数的构造方法。

           Son.java源文件代码:

    <span style="font-size:18px;">class Grandpa{
        protected Grandpa(){
    	        System.out.println("default Grandpa");
        }
    
        public Grandpa(String name){
                    System.out.println(name);
        }
    }
    
    class Father extends Grandpa{
        protected Father(){
    		System.out.println("default Father");
        }
    
        public Father(String grandpaName,String fatherName){
    		super(grandpaName);
    		System.out.println(fatherName);
        }
    }
    
    public class Son extends Father{
        public Son(){
    		System.out.println("default Son");
        }
    
        public Son(String grandpaName,String fatherName,String sonName){
    		super(grandpaName,fatherName);
    		System.out.println(sonName);
        }
    
        public static void main(String args[]){
    		Son s1= new Son("My Grandpa", "My Father", "My Son"); 
                    Son s2=new Son(); 
       }
    
    }</span>

           程序执行结果例如以下:


           当子类的构造方法没实用super语句显式调用父类的构造方法。那么通过这个构造方法创建子类对象时,Java虚

    拟机会自己主动先调用父类的默认构造方法。

           当子类的构造方法没实用super语句显式调用父类的构造方法。而父类又没有提供默认构造方法时,将会出现编

    译错误。我们凝视掉Grandpa类的protected级别的构造方法,这样,Grandpa类就失去了默认构造方法,这时,在编

    译Father类的默认构造方法时,由于找不到Grandpa类的默认构造方法而编译出错。假设把Grandpa类的默认构造方

    法的protected訪问级别改为private訪问级别,也会导致编译错误,由于Father类的默认构造方法无法訪问Grandpa类

    的私有默认构造方法。

           在下面样例中,子类Sub的默认构造方法没有通过super语句调用父类的构造方法。而是通过this语句调用了自身

    的还有一个构造方法Sub(int i),而在Sub(int i)中通过super语句调用了父类Base的Base(int i)构造方法。这样,不管通过

    Sub类的哪个构造方法来创建Sub实例,都会先调用父类Base的Base(int i)构造方法。

            Sub.java源文件代码:

    <span style="font-size:18px;">class Base{
    	Base(int i){
    		System.out.println("call Base(int i)");
    	}
    }
    
    public class Sub extends Base{
    	Sub(){
    		this(0); 
    		System.out.println("call Sub()");
    	}
    
           Sub(int i){
    		super(i); 
    		System.out.println("call Sub(int i)");
    	}
    
           public static void main(String args[]){
                    Sub sub=new Sub();
           }
    }</span>

           程序运行结果例如以下:


           构造方法的作用域

           构造方法仅仅能通过下面方式被调用:

           (1)当前类的其它构造方法通过this语句调用它。

           (2)当前类的子类的构造方法通过super语句调用它。

           (3)在程序中通过new语句调用它。

           注意以下的构造函数的调用的方式:

           Sub.java源文件代码:

    <span style="font-size:18px;">class Base{
        public Base(int i,int j){
        }
    
        public Base(int i){
    	this(i,0); //合法
            Base(i,0); //编译出错
        }
    }
    
    class Sub extends Base{
        public Sub(int i,int j){
    		super(i,0); //合法
        }
    
        void method1(int i,int j){
    		this(i,j); //编译出错
    		Sub(i,j); //编译出错
        }
    
        void method2(int i,int j){
    		super(i,j); //编译出错
        }
    
        void method3(int i,int j){
    		Base s=new Base(0,0); //合法
    		s.Base(0,0); //编译出错
        }
    }</span>

           六构造方法的訪问级别

           构造方法能够处于public、protected、默认和private这四种訪问级别之中的一个。本节着重介绍构造方法处于private级

    别的意义。当构造方法为private级别。意味着仅仅能在当前类中訪问它:在当前类的其它构造方法中能够通过this语句

    调用它,此外还能够在当前类的成员方法中通过new语句调用它。

           在下面场合之中的一个,能够把类的全部构造方法都声明为private类型:

           (1)在这个类中只包括了一些供其它程序调用的静态方法。没有不论什么实例方法。其它程序无需创建该类的实例,

    就能訪问类的静态方法。比如 java.lang.Math类就符合这样的情况,在Math类中提供了一系列用于数学运算的公共静态

    方法,为了禁止外部程序创建Math类的实例, Math类的惟一的构造方法是private类型的:private Math(){}

           假设一个类是抽象类,意味着它是专门用于被继承的类,能够拥有子类,并且能够创建详细子类的实例。

    而JDK

    并不希望用户创建Math类的子类,在这样的情况下。把类的构造方法定义为private类型更合适。

           (2)禁止这个类被继承。当一个类的全部构造方法都是private类型。假如定义了它的子类。那么子类的构造方法无

    法调用父类的不论什么构造方法,因此会导致编译错误。把一个类声明为final类型,也能禁止这个类被继承。这两者的区

    别是:

           A假设一个类同意其它程序用new语句构造它的实例。但不同意拥有子类。那就把类声明为final类型。

           B假设一个类既不同意其它程序用new语句构造它的实例,又不同意拥有子类。那就把类的全部构造方法声明为

    private类型。

    因为大多数类都同意其它程序用new语句构造它的实例,因此用final修饰符来禁止类被继承的做法更常

    见。

           C这个类须要把构造自身实例的细节封装起来。不同意其它程序通过new语句创建这个类的实例。这个类向其它

    程序提供了获得自身实例的静态方法,这样的方法称为静态工厂方法。



  • 相关阅读:
    Construct Binary Tree from Preorder and Inorder Traversal
    Construct Binary Tree from Inorder and Postorder Traversal
    Maximum Depth of Binary Tree
    Sharepoint 2013 创建TimeJob 自动发送邮件
    IE8 不能够在Sharepoint平台上在线打开Office文档解决方案
    TFS安装与管理
    局域网通过IP查看对方计算机名,通过计算机名查看对方IP以及查看在线所有电脑IP
    JS 隐藏Sharepoint中List Item View页面的某一个字段
    SharePoint Calculated Column Formulas & Functions
    JS 两个一组数组转二维数组
  • 原文地址:https://www.cnblogs.com/lytwajue/p/7145764.html
Copyright © 2011-2022 走看看