什么是构造器?(详解与应用)
构造器的简介
作用构造器概念
构造器最大的用处就是在创建对象时执行初始化,当创建一个对象时,系统会为这个对象的实例进行默认的初始化。如果想改变这种默认的初始化,就可以通过自定义构造器来实现。
构造器是为了创建一个类的实例化对象的时候用到:InstanceObject IO = new InstanceObject(); 构造器可以用来在
一个类可以有多个构造器。一个类的构造器的名称必须与该类的名称一致。要退出构造,可以使用返回语句“return;”。
相反,方法的作用是为了执行java代码。
package oop.Demo08;
//java---->class
//构造器必须要掌握
public class Person {
//一个类即使什么都不写,它也会存在一个方法
//显示的定义构造器
String name;
int age;
//使用new关键字,本质是在调用构造器
//用来初始化值
public Person(){
//无参
}
//有参构造:一旦定义了有参构造,无参就必须显示定义
// public Person(String.name){
// this.name = name;
//有参
public Person(String name,int age){
this.name =name;
this.age = age;
}
}
//} 定义有参无参快捷键: alt+insert
/**
* public static void main(String[] args) {
* //实例化了一个对象
* Person person = new Person();
*
* System.out.println(person.name);
* }
* 总结
* 构造器:
* 1.和类名相同
* 2.没有返回值
* 3.作用:
* 1.new 本质在调用构造方法
* 2.初始化对的象值
* 注意点:
* 1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
* 快捷键:Alt+Insert
* this. =
*/
构造器与普通方法的区别与联系
相同点:构造器也是Java类中的一种方法。
不同点:
构造器和方法在下面三个方面的区别:修饰符,返回值,命名。
(1)
(2)返回类型:方法能返回任何类型的值或者无返回值(void),构造器没有返回值,也不需要void。
(3)两者的命名:构造器通常用首字母为大写的一个名词开始命名,并且使用和类相同的名字命名。而方法则不同,方法通常用小写字母英文动词开始,其后跟随首字母为大写的名称、形容词等等组成的驼峰命名方式,方法通常更接近动词,因为它说明一个操作。
方法中调用构造器的用法
this用法
使用
public class InstanceObject {
String name;
InstanceObject(String input) {
name = input;
}
InstanceObject() {
this("Java language");
}
public static void main(String args[]) {
InstanceObject IO1 = new InstanceObject();
InstanceObject IO2= new InstanceObject();
}
}
在上面的代码中,有2个不同参数列表的构造器。第一个构造器,给类的成员name赋值,第二个构造器,调用第一个构造器,给
在构造器中,如果要使用
super用法
构造器和方法,都用关键字super指向
class Mammal {
void getBirthInfo() {
System.out.println("born alive.");
}
}
class Platypus extends Mammal {
void getBirthInfo() {
System.out.println("hatch from eggs");
System.out.print("a mammal normally is ");
super.getBirthInfo();
}
}
在上面的例子中,使用super.getBirthInfo()去调用超类Mammal中被
构造器使用super去调用
public class SuperClassDemo {
SuperClassDemo() { }
}
class Child extends SuperClassDemo {
Child() {
super();
}
}
在上面这个例子中,构造器 Child()包含了 super,它的作用就是将超类中的构造器SuperClassDemo实例化,并加到 Child类中。
super被编译器自动加入
编译器自动加入代码到构造器,对于这个,java程序员新手可能比较混淆。当写一个没有构造器的类,编译的时候,编译器会自动加上一个不带参数的构造器,例如:public class Example {}
编译后将如下代码:
public class Example {
Example() {}
}
仔细想一下,就知道下面的代码会被编译器加super 代码形如:
public class Example {
Example() {
super();
}
}
在构造器的第一行,没有使用super,那么编译器也会自动加上,例如:
public class TestConstructors {
TestConstructors() {}
}
public class TestConstructors {
TestConstructors() {
super();
}
}
继承方式
显式继承
如果要显式继承基类的构造函数(也叫作构造器),必须使用base关键字来实现。看看下面的代码
接口代码:
interfaceIlandBound
{
intNumberOfLegs();
}
Horse类代码:
classHorse: Mammal,IlandBound
{
publicHorse(stringname)
: base(name)//如果基类构造函数 public Mammal(string name)
//中传入参数string name那么派生类的构造函数就必须通过base关键字显示继承该构造函数
{
Console.Write("调用Horse类的构造函数:Horse's "+ name + "\n");
}
publicstaticstringWrite(stringname)
{
Console.Write("调用Horse类的Write方法:Horse's "+ name + "\n");
returnname;
}
publicintNumberOfLegs()
{
return4;
}
}
}
mammal类代码:
classMammal
{
publicstaticintname = 12345;// public static int name;缺省值为0
publicMammal(stringname)
{
name = (("调用基类构造函数:")+name +"\n");
Console.WriteLine(name);
}
staticvoidMain(string[] args)
{
Console.WriteLine("调用基类构造器:");
Console.WriteLine(newMammal(name:"哺乳动物") + "\n");
Console.WriteLine("new一个实例后调用派生类的构造函数:");
Console.WriteLine(newHorse(name: "马") + "\n");
Console.WriteLine("调用Horse类的Write方法:");
Horse.Write(name: "马");
Console.WriteLine("\n调用IlandBoud接口:");
HorseLegs = newHorse("马");
Console.WriteLine("\n马有"+ Legs.NumberOfLegs() + "条腿。");
Console.ReadKey();
}
}
}
输出:
调用基类构造器:
调用基类构造函数:哺乳动物
com.Constructor.Explicit.Mammal
new一个实例后调用派生类的构造函数:
调用基类构造函数:马
调用Horse类的构造函数:Horse's 马
com.Constructor.Explicit.Horse
调用Horse类的Write方法:
调用Horse类的Write方法:Horse's 马
调用IlandBoud接口:
调用基类构造函数:马
调用Horse类的构造函数:Horse's 马
马有4条腿。
隐式继承
派生类继承自基类之后,自动的隐式的继承基类的构造函数叫作隐式继承。如果没有为基类写构造函数,那么派生类继承自基类的构造函数默认不进行显式的操作。看看下面的例子:
接口代码:
interfaceIlandBound
{
intNumberOfLegs();
}
Horse类代码:
classHorse: Mammal,IlandBound
{
publicHorse(stringname)
{
Console.Write("调用Horse类的构造函数:Horse's "+ name + "\n");
}
publicstaticstringWrite(stringname)
{
Console.Write("调用Horse类的Write方法:Horse's "+ name + "\n");
returnname;
}
publicintNumberOfLegs()
{
return4;
}
}
}
mammal类代码:
classMammal
{
publicstaticintname = 12345;// public static int name;缺省值为0
publicMammal()
{
Console.WriteLine("调用基类构造函数:"+name);
}
staticvoidMain(string[] args)
{
Console.WriteLine("调用基类构造器:");
Console.WriteLine(newMammal() + "\n");
Console.WriteLine("new一个实例后调用派生类的构造函数:");
Console.WriteLine(newHorse(name: "马") + "\n");
Console.WriteLine("调用Horse类的Write方法:");
Horse.Write(name: "马");
Console.WriteLine("\n调用IlandBoud接口:");
HorseLegs = newHorse("马");
Console.WriteLine("\n马有"+ Legs.NumberOfLegs() + "条腿。");
Console.ReadKey();
}
}
}
显式继承输出 | 隐式继承输出 |
---|---|
调用基类构造器:**调用基类构造函数:哺乳动物com.Constructor.Explicit.Mammalnew一个实例后调用派生类的构造函数:调用基类构造函数:马调用Horse类的构造函数:Horse's 马com.Constructor.Explicit.Horse调用Horse类的Write方法:调用Horse类的Write方法:Horse's 马调用IlandBoud接口:调用基类构造函数:马调用Horse类的构造函数:Horse's 马马有4条腿。** | 调用基类构造器:**调用基类构造函数:12345com.Constructor.Explicit.Mammalnew一个实例后调用派生类的构造函数:调用基类构造函数:12345调用Horse类的构造函数:Horse's 马com.Constructor.Explicit.Horse调用Horse类的Write方法:调用Horse类的Write方法:Horse's 马调用IlandBoud接口:调用基类构造函数:12345调用Horse类的构造函数:Horse's 马马有4条腿。** |
子类可以继承
public class Example {
public void sayHi {
system.out.println("Hi");
}
Example() {}
}
public class SubClass extends Example {
}
类 SubClass 自动继承了父类中的sayHi方法,但是,父类中的构造器 Example()却不能被继承。
析构器
由于.NET平台的自动垃圾收集机制,C#语言中类的
using System;
class MyClass1
{
~MyClass1()
{
Console.WriteLine("MyClass1's destructor");
}
}
class MyClass2: MyClass1
{
~MyClass2()
{
Console.WriteLine("MyClass2's destructor");
}
}
public class Test
{
public static void Main()
{
MyClass2 MyObject = new MyClass2();
MyObject = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
编译程序并运行可以得到下面的输出:
MyClass2's destructor
MyClass1's destructor
其中程序中最后两句是保证类的
析构器不会被继承,也就是说类内必须明确的声明析构器,该类才存在析构器。用户实现析构器时,
那么析构器是怎么被自动调用的?这在 .Net垃圾回收机制由一种称作终止化(Finalizaion)的操作来支持。.Net系统缺省的终止化操作不做任何操作,如果用户需要释放非受管资源,用户只要在析构器内实现这样的操作即可--这也是C#推荐的做法。我们看下面这段代码:
using System;class MyClass1{ ~MyClass1() { Console.WritleLine("MyClass1 Destructor"); }}
而实际上,从生成的
using System;class MyClass1{ protected override void Finalize() { try {Console.WritleLine("My Class1 Destructor"); } finally { base.Finalize(); } }}
实际上C#
using System;class MyClass{ override protected void Finalize() {}// 错误 public void MyMethod() { this.Finalize();// 错误 }}
但下面的代码却是正确的:
using System;class MyClass{ public void Finalize() { Console.WriteLine("My Class Destructor"); }}public class Test{ public static void Main() { MyClass MyObject=new MyClass(); MyObject.Finalize(); }}
实际上这里的Finalize方法已经彻底脱离了“终止化操作”的语义,而成为C#语言的一个一般方法了。值得注意的是这也屏蔽了父类System.Object的Finalize方法,所以要格外小心!
终止化操作在.Net运行时里有很多限制,往往不被推荐实现。当对一个对象实现了终止器(
大多数时候,垃圾收集应该交由.Net运行时来控制,但有些时候,可能需要人为地控制一下垃圾回收操作。例如在操作了一次大规模的对象集合后,我们确信不再在这些对象上进行任何的操作了,那我们可以强制垃圾回收立即执行,这通过调用System.GC.Collect() 方法即可实现,但频繁的收集会显著地降低系统的性能。还有一种情况,已经将一个对象放到了终止化对象引用集的链上了,但如果我们在程序中某些地方已经做了终止化的操作,即明确调用了Dispose()方法,在那之后便可以通过调用System.GC.SupressFinalize()来将对象的引用从终止化对象引用集链上摘掉,以忽略终止化操作。终止化操作的系统负担是很重的。
在深入了解了.NET运行时的自动垃圾收集功能后,我们便会领会C#中的
分类
构造器分类
缺省构造器是在一个类没有声明任何构造器的情况下,
子类没有声明任何构造器;
编译器为子类加的缺省构造器一定为无参数的构造器;
父类一定要存在一个无参数的构造器。
看下面例子的输出:
using System;
public class MyClass1
{
public MyClass1() {
Console.WriteLine(“MyClass1 Parameterless Contructor!”);
}
public MyClass1(string param1) {
Console.WriteLine(“MyClass1 Constructor Parameters : ”+param1);
}
}
public class MyClass2:MyClass1
{
}
public class Test
{
public static void Main() {
MyClass2 myobject1=new MyClass2();
}
}
MyClass1 Parameterless Contructor!
读者可以去掉MyClass1的无参构造器public MyClass1()看看编译结果。
在继承时需要特别的注意,为了保证父类
using System;
public class MyClass1{
public MyClass1() {
Console.WriteLine("MyClass1 Parameterless Contructor!");
}
public MyClass1(string param1) {
Console.WriteLine("MyClass1 Constructor Parameters : "+param1);
}
}
public class MyClass2:MyClass1{
public MyClass2(string param1):
base(param1) {
Console.WriteLine("MyClass2 Constructor Parameters : "+param1);
}
}
public class Test{
public static void Main() {
MyClass2 myobject1=new MyClass2("Hello");
}
}
编译程序并运行可以得到下面的输出:
MyClass1 Constructor Parameters : Hello
MyClass2 Constructor Parameters : Hello
C#支持变量的声明初始化。类内的
using System;
public class MyClass1{
public MyClass1() {
Print();
}
public virtual void Print() {}
}
public class MyClass2:MyClass1{
int x = 1;
int y;
public MyClass2() {
y = -1; Print();
}
public override void Print() {
Console.WriteLine("x = {0}, y ={1} ", x, y);
}
}
public class Test{
static void Main() {
MyClass2 MyObject1 = new MyClass2();
}
}
编译程序并运行可以得到下面的输出:
x = 1, y = 0
x = 1, y = -1
容易看到初始化语句在父类构造器调用之前,最后执行的才是本构造器内的语句。也就是说变量初始化的优先权是最高的。
我们看到类的构造器的声明中有public
类的构造器没有返回值,这一点是不言自明的。
静态构造器
初始化类中的
在一个程序的执行过程中,静态构造器最多只执行一次。
静态构造器在类的
静态构造器在任何类的静态成员被引用之前执行。
静态构造器在任何类的实例变量被分配之前执行。
看下面例子的输出:
using System;class MyClass1{
static MyClass1()
{
Console.WriteLine("MyClass1 Static Contructor");
}
public static void Method1()
{
Console.WriteLine("MyClass1.Method1");
}
}
class MyClass2
{
static MyClass2()
{
Console.WriteLine("MyClass2 Static Contructor");
}
public static void Method1()
{ Console.WriteLine("MyClass2.Method1"); }
}
class Test
{
static void Main()
{
MyClass1.Method1();
MyClass2.Method1();
}
}
编译程序并运行可以得到下面的输出:
MyClass1 Static Contructor
MyClass1.Method1
MyClass2 Static Contructor
MyClass2.Method1
当然也可能输出:
MyClass1 Static Contructor
MyClass2 Static Contructor
MyClass1.Method1
MyClass2.Method1
值得指出的是实例构造器内可以引用实例变量,也可引用
实际上如果我们能够深刻地把握类的构造器的唯一目的就是保证类内的