Spring学习笔记 关于Bean属性的初始化

2012年06月17日 发表评论

依赖注入,可以使Bean中的属性通过Spring进行配置,从HelloWorld中可以看到,由Spring的XML配置文件决定了对象被创建时它的属性是如何被初始化的。Spring配置文件中定义Bean同时就定义了它的属性的值或引用的对象,HelloWorld中是通过Bean中的Setter方法实现的。算起来Spring中初始化类属性值有两种方法,第一种,使用Bean中的Setter方法,第二种使用类的构造方法进行初始化,Spring在配置文件中进行定义的时候同样可以定义它的构造函数所需的参数。

例如HelloWorld笔记中的MessageLooper类,初始化 numTimes属性值可以使用带有int参数的构造方法进行初始化,也可以通过XML配置文件中Bean的定义使用numTimes的Setter方法进行初始化。如下:

(7ZY`JZFXX0)PPMAWDCK6[Va

使用Setter初始化属性值在XML中Bean的定义示例:

IF9SQW[(O$D0ATE`CM(%C{B

使用构造函数初始化属性值:

GZ$}A8NH4`46KLTF{0%CIUH 

可以注意到MessageLooper的带参构造方法的参数类型为int,上图中Spring会根据参数类型来进行转换。

以上为简单示例,如果构造方法有多个参数值,XML中定义的方法为:

UXM$BJ$PSTSIFKKBA49PMUF

对于有多个参数的构造方法使用index确定构造方法参数与定义中值的对应关系。

假如有一种情况,一个方法有两个构造方法,如下

CX)M%M{Y1DQ(2P6BULVZFIU

InjectionMail类中有两个都是带有一个参数的构法方法,其中一个参数为String类型,另外一个参数为int型。对于这种情况如果仍然使用上边示例XML中Bean的构造方法定义(例如:<constructor-arg  value=“235”/>),Spring将会无法分清创建对象时应该使用哪个构造方法。这时我们需要对参数类型进行定义,定义参数类型使用type属性,如下

[D]Y]Z0W9UUPB1HD$`)_Q77

构造方法的参数也有可能是引用变量,例如:

X_HNF~D$8FMS[{R4FD]S](8

定义引用变量类型参数可以使用ref属性

I2WJS0S$5FE}BO6@IMHH[ON

使用<constructor-arg value=“×××” /> 定义的参数值value属性的值可以根据构造方法中定义的参数类型进行转换。以下为转换示例:

RB`)JZI40GPA]H1VB1RM%@V

上述配置中部分参数定义的其他可选方法:

L4J`P0][I_EJR)M$E6SLD%0

分类:未分类

All About JAVA 关于JAVA对象的创建以及构造方法(2)

2010年09月25日 发表评论

      如果一个类A继承自另一个类B,当创建这个A类的对象时,构造函数是怎样运作的? 我们知道一个类继承了另一个类后,这个类会包含父类的所有的实例变量和方法。也包括父类中的private实例变量,虽然在子类中无法直接访问父类中标记为private类型的实例变量,但是仍然可以通过父类中定义的GETTER与SETTER来进行操作。并且继承自父类的一些其他方法也有可能会用到父类中的一些实例变量,如果父类中的实例变量没有进行初始化,很可能进行A的初始化时会遇到问题,所以在我们初始化A类对象之前应该先对B类(父类)进行初始化。

public class Animal {
    public Animal() {
         System.out.println("I am making a new Animal");
    }
}

public class Dog extends Animal {
    public Dog() {
         System.out.println ("I am making a new Dog");
    }
}

public class TestDog {
    public static void main(String[] args) {
        System.out.println("I am Starting");
        Dog d = new Dog();
    }
} // 这段代码,当我们运行了 TestDog 类后 正确的输出应该为
  // I am Starting
  // I am making a new Animal
  // I am making a new Dog

      可以看出,当我们创建Dog对象时首先完成了Animal类的构造方法,最后才完成了Dog类的构造方法,可以用栈来理解这个构造方法的调用,当我们new 一个 Dog 时,Dog()构造方法被压入栈中,随后Animal()构造方法被压入栈中,实际上JAVA中所有类都继承自Object,所以Object()构造方法也被压入栈中,也就是栈的最顶层。然后首先完成了Object()构造方法,Object()从栈中弹出,再完成了Animal()构造方法并从栈中弹出,最后完成了Dog()构造方法并从栈中弹出,至此Dog对象的创建工作才算完成。
      通过以上代码及输出可以看到,在Dog的构造函数运行时自动调用了它的父类Animal类的构造方法,而Animal构造方法实际上自动调用了Object类的构造方法。因为实际上JVM在我们每个构造方法的第一行添加了 super(); 这行语句。它的作用就是调用父类的构造方法,当然我们也可以手动调用super(),比如当父类的构造方法带有参数时,我们可以使用super(xx, xx);在子类的构造方法中调用父类的构造方法,但是需要注意super()语句需要放在子类构造方法的第一行进行调用,否则会报错。一旦我们自己手动调用了super() JVM将不再自动为我们添加 super() 的调用。
      另外如果我有两个或多个构造方法,当一个A构造方法只是对另一个B构造方法额外添加了一些处理,我们可能会想到代码重用,我们可以在A构造方法中使用this(); 来调用B构造方法实现代码重用。例如:

      public class Dog {
            int height;
      
            public Dog() {
                 this(40);
                 // 这里还可以做一些其他初始化处理
                 // ******  我们需要注意,this 也必须放在构造方法中的第一行,还记得之前谈到 super(); 也是要求放在第一行的,
                 // 所以 this(); 和 super(); 是不能同时使用的 ****************************************************************
             }
            public Dog( int newHeight ) {
                 height = newHeight;
            } 
      }

分类:JAVA

All About JAVA 关于JAVA对象的创建以及构造方法(1)

2010年09月25日 发表评论

      对于JAVA对象的创建,我们使用这样的语句:Object myObj = new Object(); 它实际分为三个步骤:

            1. Object myObj  — 分配存储空间并创建一个引用变量。
            2. new Object()  — 创建一个Object对象。
            3. =  — 将创建的对象引用赋值给 myObj 这个引用变量。

      在上边的第二个步骤,当我们使用了 new 这个关键词时,实际上是做了额外的一些工作来创建我们需要的对象,我们调用了 Object 对象的构造方法。构造方法与类同名,并且不能定义返回值类型,例如 Object 类的构造方法可能为:

public Object() {
     // 这里做一些初始化对象的操作
} 

public Object(int size, String name) {
     // 这里做一些类似将size 与 name赋值给实例变量等初始化对象的操作
     // 从这两个构造方法可以看出,构造方法可以带参数,并且在一个类中可以存在多个构造方法,只要它们符合重载的条件即可。
     // 符合重载的条件分为两点:参数的个数不同,相同位置上的参数的类型不同
}

public Object(String name, int size) {
     // 这个构造函数与上一个是两个不同的构造函数,因为他们满足重载的条件
}

      构造方法类似一个类的普通方法,但是它并不是类的普通方法,构造方法并没有定义返回值类型,而类的方法是必须定义返回值类型的,这是它与普通类方法的唯一区别。 很多时候在我们编写一个类时可能并没有手动编写一个构造方法,但它的确存在,因为 JVM 会自动对没有定义构造方法的类添加一个不带参数的空的构造方法。

      为什么系统已经为我们添加了构造方法,我们还需要来创建自己的构造方法?构造方法最大的作用就是让我们新创建的的对象在使用前被完整的初始化,想象一个Dog类,它有一个private int height; 实例变量,如果没有构造方法,它刚刚被创建为对象时height值默认是0的,这样我们就有了一个0高度的狗狗,显然这样不好。所以我们需要使用构造函数来初始化我们的对象,例如:

public class Dog {
    int height;

    public Dog() {
        height = 30;
    } // 当我们添加了自己的构造方法后,系统将不再为我们手动添加不带参数的构造函数
       // 这样当我们的类的使用者使用不带参数的构造方法创建对象时会产生错误,所以一般情况下建议
       // 为类添加一个不带参数的构造方法,并在其中给类的一些重要的实例变量一个默认值。

    public Dog(int newHeight) {
        height = newHeight;
    } // 对不带参数的构造方法的重载 
}

public class TestDog {
    Dog myDog = new Dog(40);
    System.out.print ( myDog.height );
}
分类:JAVA

The NOTE of learning ASP.NET [19] 关于GC(内存回收机制)、对象的销毁和using的使用

2010年06月1日 发表评论
    这两天看了用来回收无用对象并释放其占用的内存空间的GC(内存回收机制)的相关内容。。。一个教学视频整整看了5遍才理解他说的意思,都是英文太难为我了。看后知道了什么样的对象是在GC的回收范围内的,并且了解了GC只有在内存不够用时才进行内存的回收。接下来就应该瞧瞧对象销毁和GC之间运作的流程与关系了。
     当GC回收对象时会调用对象中的一个Finalize方法,虽然我们可能在我们的类中没有写这个方法,但是它确实存在。因为我们的所有类都是继承自System.Object类的,而Finalize方法是Object类的一个protected的方法。我们从来不会手动去调用那个方法,因为那个方法是当GC销毁对象时才会由GC自动调用的。而且大多数时候我们并不用去关心这个方法GC会适时的自动处理对象并释放内存等资源。那什么时候才需要关注这个Finalize方法呢?答案是:当我们调用了不是由.NET运行时环境管理的资源时,像打开了系统中的文件,数据流等,最典型的是我们的对象在运行时打开了一个数据库连接,这个数据库连接资源不会因为我们的对象被销毁而自动释放,所以我们需要在Finalize方法中设置相应的释放数据库连接的代码,这样即使我们在编码时没有手动关闭数据库连接,GC也会在回收对象时为我们释放数据库连接资源。可是System.Object的Finalize方法是被保护的不能够进行重载,如果我们需要自定义对象被销毁前要做的工作就需要使用.NET为我们提供的析构函数,析构函数与类名相同,在类名前加上~来标识。例如 Person类的析构函数就应该为 ~Person(){ //这里执行我们释放资源等处理的代码 }
    说到这可能会发现就这个例子来说如果我们在对象中打开了数据库连接而且没有关闭,即使我们提供了释放数据库资源连接的析构函数,那个数据库连接也不会马上关闭,因为GC只有当内存不足时才去回收那些无用对象并执行它的析构函数。那么对于这种使用过后需要立即释放的资源我们则需要使我们的类实现IDisposable接口,并实现接口中的Dispose方法。我们把释放数据库连接资源或其他不是由.NET管理的资源的代码放到Dispose方法中。似乎Dispose方法和析构函数做的工作是一样的,确实,但是他们两个还是有区别的,析构函数由GC调用,而Dispose由我们自己调用。这样我们就可以在使用完这个对象后立即执行其Dispose方法来释放其中的非.NET管理的资源(例如数据库连接)。
 
        下面是一个实现了IDisposable接口及Dispose方法的类:
using System;
using System.Collections.Generic;
using System.Text;
namespace ObjectsAndClasses
{
  class DisposeDemo : IDisposable
  {
    private System.IO.FileStream fs = null;
    //alreadyDisposed变量用来监视我们的对象是否已经执行过Dispose方法
    private bool alreadyDisposed = false;
    public void Dispose()
    {
      if (!this.alreadyDisposed)
      {
        if (fs != null)
          fs.Close();
        Console.WriteLine("In Dispose");
      }
      this.alreadyDisposed = true;
      // 下面这行代码告诉GC在销毁对象的时候不用
      // 执行这个对象的析构函数,因为我们已经在Dispose
      // 方法中做了析构函数需要做的工作。这样可以使GC
      // 在回收对象时提高一些效率
     GC.SuppressFinalize(this);
    }
  }
}
 
下面是调用上面所写的类的示例:
    private static void TestDispose()
    {
      // Declare the variable:
      DisposeDemo demo = null;
      try
      {
        // Assign the variable a value:
        demo = new DisposeDemo();
      }
      finally
      {
        if (demo != null)
        {
          demo.Dispose();
        }
      }
    }
 
上面的方法可能使用起来稍微麻烦,其实我们还可以使用using,代码如下:
    private static void TestUsing()
    {
      // 下面代码同样是创建了一个对象赋值给了demo这个引用变量,
      // 只不过它被包含在using后边的括号中,使用了using 后,demo
      // 这个对象的域就限制在了using后的大括号中。当大括号中的代码
      // 执行到了末尾后则会自动调用demo对象的Dispose方法来对资源
      // 进行释放。实际上这段代码的作用和上一段是一样的,只是使用起
      // 来更为简便。需要注意的是,只有实现了IDisposable接口的类才能
      // 使用using来操作。
      using (DisposeDemo demo = new DisposeDemo())
      {
        // Do something with demo in here…
      }
    }
分类:ASP.NET

The NOTE of learning ASP.NET [18] 关于.NET(GC)内存回收机制

2010年06月1日 发表评论
    在.NET中不管是哪种类型的变量都占据着内存中的一段空间。int,datetime,float等基本数据类型在对应的内存空间中存放的是实际的数据,而引用变量则不同,引用变量所占的内存空间中存放的不是实际的对象数据,而是一段指向存放实际对象所在内存的地址,这个在C或者C++中被叫做指针,指向存放实际对象的内存的地址。一般创建一个引用变量并为它赋值的的流程(以创建Person类的对象为例):
    Person personOne = new Person();
    这条语句实际上分为三个步骤,
   (1)使用new命令在内存中创建了一个新的对象
   (2)创建Person类型的引用变量personOne(引用变量在使用前必须为其赋值,否则会编译报错,引用变量被声明后若未赋值它的值为null即未引用到任何对象)
   (3)将新建的对象所在内存的信息赋值给应用变量personOne。
    可以看出如果我们做了如上操作后再使用 personOne = null; 将personOne赋予null值的话,虽然引用变量被设置为空,但是它原先引用的对象实际上仍然存在于内存中。而我们其实并不用担心如何释放那个已经不再使用的对象所占的内存。.NET中的内存回收机制即GC(Garbage Collector)负责回收引用变量及其引用的实际对象的内存空间。
    我们需要关心两个问题:具备什么条件的对象会被GC回收并释放内存空间?GC什么时候才会回收那些已经无用的对象?
   (1)当一个内存中的对象不再被任何引用变量所引用时,它符合了被GC回收的条件。比如Person personOne = new Person();当我们把personOne设置成null时,personOne不再指向任何对象,而同时我们也无法再次访问之前personOne所指向的对象,所以这个对象对我们来说已经是没有用处的了。
   (2)GC不会在对象符合被回收的条件时立即进行回收并释放内存,它会判断在内存不够用时再去回收内存中的对象,如果内存足够大的话,它可能永远不会去回收那些仍存在于内存中但是已经无用的对象。
    了解了以上问题,就可以在编程中更好的控制资源的使用并更好的理解一些方法的用途。
   
分类:ASP.NET

The NOTE of learning ASP.NET [17] 关于VS2008中使用注释生成智能提示

2010年05月26日 发表评论
    在使用VS2008编写程序时,代码的智能提示带给我们很多实惠啊~当我们调用一个方法时,比如这里是一个Program类的RandomNumber方法,这个方法是static型的,我们在编程界面输入 Program.  输入了点后会弹出一个列表显示我们在这里可以调用的方法,我们继续输入RandomNumber,让列表中的RandomNumber处于选中状态,这时我们就能看到右侧会有一个黄色的提示框,显示了我们当前方法的一些信息,如重载情况以及注释说明。如图:
    我们可以为我们自己写的类里的方法添加注释来实现这样的效果,在我们想添加注释的方法上方输入三个’/’字符,VS就会自动帮你生成注释模板,然后我们在其中输入自己的注释即可。比如我们在Main函数上方输入///,即VS为我们生成了如下的注释:
      /// <summary>
      ///
      /// </summary>
      /// <param name="args"></param>
我们在<summary></summary>之间和<param name="args"></param>之间输入的注释会被显示到智能提示中。
分类:ASP.NET

The NOTE of learning ASP.NET [16] 关于类和对象以及封装

2010年05月25日 发表评论
    在很多测试代码中都是在Main函数中写了很多过程来演示我们的功能,但是我们知道我们的程序需要使用类,使用面向对象的方法来进行更好的设计。类是我们开发程序的基础。类作为对象的蓝图,描述了对象的共有属性和功能(方法)。很多教学里边的一个比喻很形象,如果说对象是房子,那类就是设计这个房子的设计图,我们可以使用这一个设计图来建设出很多相同的房子,这就是类和对象的关系。类由属性和方法组成。
    Class(类)中可以包含static或者instance的属性和方法。
    1.被声明为static型的属性是被所有这个类的对象所共享的,即每个对象中的这个属性都指向同一个值。
    2.默认的即instance型的属性是独立于单个对象的,也就是每个对象里的同一个instance属性都可以拥有不同的值。
    3.调用被声明为static型的方法不需要使用new关键字来显式的创建一个对象,可以直接由类来调用,一般被声明为static型的方法可以看作是类对外提供的服务。比如Directory.CreateDirectory(); 这个方法用来在我们的计算机中创建文件夹,这个方法就是一个static方法。我们并没有一个已经存在的文件夹供我们操作,我们只是使用了Directory类为我们提供的创建文件夹的服务。另外我们需要注意,因为static方法可以直接由类调用,而且我们在调用static方法的时候并没有创建那些类中定义的instance型的属性,所以我们不能在static方法中使用非static型的属性。
    4.默认的instance型的方法必须由具体对象来调用,即必须使用new关键字新建对象后由具体对象来调用。
    由下面的代码可以更好理解。
   using System;
    using System.Collections.Generic;
    using System.Text;
    namespace ClassesAndObjects
    {
        class MyCustomer
        {
            // 客户姓名 instance类型的属性,默认设置"客户姓名"属性为DefaultCustomName的值
            private string _CustomerName = DefaultCustomName;
            private string _CustomerID; // 客户ID instance属性
            public static string DefaultCustomName = "Unknow"; // 默认的客户姓名 static类型的属性
          
            // 类MyCustomer的构造函数,当类被创建时自动调用。与类名同名,不需要定义返回值,可以添加
            // 参数,可以提供多个具有不同参数类型或不同参数个数的构造函数,这样当我们
            // 新建对象时会根据我们传入参数的类型和个数来决定使用哪个构造函数,即重载。
            public MyCustomer(string CustomerID)
            {
                 _CustomerID = CustomerID;
            }
           
            // 以下CustomerID和CustomerName可以看到并不是普通的方法,实际上是对我们类的属性_CustomName和
            // _CustomID的封装如果我们类的属性可以直接被外界访问并随意设置它的值对我们的程序来说是很
            // 危险的。比如使用者把客户的姓名设置成数字"123"很明显,名字是数字是说不通的,所以我们可以使用封装来
            // 限制使用者对我们类的属性的操作。封装里包含了get和set两个关键字,对_CustomerID的封装我们只提供了get,
            // 这样_CustomerID对外界来说实际上是一个只读属性,CustomerName提供了对_CustomName
            // 的最简单的封装,在实际使用中我们可以在set里判断value的值,当value符合一定规则后才赋值给我们
            // 的_CustomName属性。
            public string CustomerID
            {
                get { return _CustomerID; }
            }
            public string CustomerName
            {
                get { return _CustomerName; }
                set { _CustomerName = value; }
            }
 
            static void Main(string[] args)
            {
                 MyCustomer custA = new MyCustomer("AAAA"); //创建一个对象,使用了带一个字符串参数的构造函数
                 MyCustomer custB = new MyCustomer("BBBB");
 
                 Console.WriteLine("custA’s CustomerID is {0}",custA.CustomerID); // 输出:custA’s CustomerID is AAAA
                 Console.WriteLine("custB’s CustomerID is {0}",custB.CustomerID); // 输出:custB’s CustomerID is BBBB
 
                 // 输出:custA’s CustomerName is Unknow
                 Console.WriteLine("custA’s CustomerName is {0}",custA.CustomerName);
                  // 输出:custB’s CustomerName is Unknow
                 Console.WriteLine("custB’s CustomerName is {0}",custB.CustomerName); 
                 Console.WriteLine();
 
                 // 对custA对象的_CustomerName属性赋值
                 custA.CustomerName = "CUSTA’S NAME";
                  //在这里通过类名来调用更改static型属性DefaultCustomName 的值。 
                 MyCustomer.DefaultCustomName = "Arvin";
                 // 这次创建的对象的默认客户名字将会是上一行设置的 “Arvin”
                 MyCustomer custC = new MyCustomer("CCCC");
                
                 // 输出:custA’s CustomerName is CUSTA’S NAME
                 Console.WriteLine("custA’s CustomerName is {0}",custA.CustomerName);
                 // 输出:custB’s CustomerName is Unknow
                 Console.WriteLine("custB’s CustomerName is {0}",custB.CustomerName);
                  // 输出:custC’s CustomerName is Arvin
                 Console.WriteLine("custC’s CustomerName is {0}",custC.CustomerName);
            }
        }
    }
分类:ASP.NET
关注

每发布一篇新博文的同时向您的邮箱发送备份。