"new" 出一个新未来——关于new的揭秘
2012-02-22 17:24
127 查看
看了一些书,也讨论过new的一些用法,总有朋友对New迷糊,或者感觉它只不过是一个 初始化实例的关键字,没什么大不了的。我把的总结的一些知识共享出来,1是希望大家指正错误理解,2来还希望大家补充知识。
博文按照管理,从2个类型考虑:值类型与引用类型
1.从值类型来说
2.从引用类型来说
2.1 new初始化对象
2.2 new隐藏父类方法
1.先说值类型new的作用
分析:上面这段代码肯定编译不通过,因为我们知道值类型必须赋值才能够被使用/调用,再看下面的代码:
输出结果是:0
查看下IL代码:
从IL代码可知:
下面2种写法是一样的,没什么区别。
int a=0;
Console.WriteLine(a.ToString());
int a=new int();
Console.WriteLine(a.ToString());
即:new在值类型过程中,初始化变量a值为0。
插曲:
string s1 = "*****************";
string s2 = new string('*',15);
这样写效果一样的。
2.对于引用类型来说
2.1 new用于初始化对象
查看IL代码:
即:new 1.为实例分配内存 2.调用构造函数初始化对象
(实际初始化过程比这个复杂的很多,不属于这个范畴了)
2.2 new 隐藏父类的同名方法
说隐藏功能的时候,首先要说一下方法表的结构:
内存分为:stack堆栈和heap堆(托管堆)
托管堆:又分为GC堆,GC堆由GC回收器控制内存释放,还分为Load堆,Load堆不受GC回收器控制,当Appdomain卸载时,自动释放内存。
重点说一下Load堆:
Load堆内有 类型表,类型表内有TypeMethod类型方法表:
如下代码:
如果初始化F3的对象
static void Main()
{
F3 f3= new F3();
}
内存的分配对应的方法表如图:
对应的F3类型有一张方法表,对于的F2类型也有一张类型表,对应的F1类型也有一张类型表。
下面开始重点:
案例1:
输出结果: 全部是F1
分析:
1.F1 f1=new F2(); 到底调用那一个S()方法?
调用什么方法,看的不是引用变量的类型,而是看new之后的对象是什么类型,这里是F2类型(),那么就会去找在Load堆F2的类型表,根据F2是继承F1的方法表,当类型表内出现了相同名字的方法的时候,编译器会自动区分,也就是说,此刻F2类型表内,第一个S()方法应该是F1的,第二个才是自己的S()方法,这里有一个”就近原则“,调用第一个方法,也就是F1的S()方法,因为在类型方法表内部:父类方法在子类前面。
因此,根据这个”就近原则“,输出的所有结果都是:F1。
案例2:
修改部分代码:
输出结果:还是一样 全部都是 F1。
分析:
new在这里的作用,就是 新建一个与父类方法名一样的方法。
根据我们对 Load堆类型方法表的理解,那么 父类的方法还是在 new出来的子类方法的前面,所以根据就近原则还是调用的F1的S()方法。
案例3:
输出结果:
F1
F1
F1
F2
这里有个注意点:F3 f4=new F3();调用S()方法的时候,如果F3类中有与父类同名的方法,按照就近原则,会先调用自己的S()方法,如果没有,向上查找最近的S()方法,所以输出第一个方法结果是:F2。
案例4:
new 真的可以隐藏父类方法吗?new的背后其实就是我们刚才的”就近原则“在作怪,不管new还是不new,父类方法一定是在子类方法前面,回想下Load堆内部的方法表的顺序,就可以理解了。new其实没用隐藏!
那么什么动作会导致父类方法消失呢,就是override,会覆盖父类的方法。
代码如下:
输出结果:
F1
F2
F2
分析:
对于类型F2与类型F3的类型方法表内部,已经没有F1的S()方法了,F3调用S(),就算是就近原则,也是F2的覆盖过的S()方法。
总结:这下应该理解了new的真的含义了。
博文按照管理,从2个类型考虑:值类型与引用类型
1.从值类型来说
2.从引用类型来说
2.1 new初始化对象
2.2 new隐藏父类方法
1.先说值类型new的作用
static void Main() { //这段代码编译不通过 int a; Console.WriteLine(a.ToString()); }
分析:上面这段代码肯定编译不通过,因为我们知道值类型必须赋值才能够被使用/调用,再看下面的代码:
static void Main() { //编译通过 int a=new int(); Console.WriteLine(a.ToString()); }
输出结果是:0
查看下IL代码:
.locals init ([0] int32 a) IL_0000: nop IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: ldloca.s a IL_0005: call instance string [mscorlib]System.Int32::ToString() IL_000a: call void [mscorlib]System.Console::WriteLine(string) IL_000f: nop IL_0010: ret } // end of method Class1::Main
从IL代码可知:
下面2种写法是一样的,没什么区别。
int a=0;
Console.WriteLine(a.ToString());
int a=new int();
Console.WriteLine(a.ToString());
即:new在值类型过程中,初始化变量a值为0。
插曲:
string s1 = "*****************";
string s2 = new string('*',15);
这样写效果一样的。
2.对于引用类型来说
2.1 new用于初始化对象
class Class1 { static void Main() { F1 f1 = new F1(); } } class F1 { }
查看IL代码:
.entrypoint // 代码大小 8 (0x8) .maxstack 1 .locals init ([0] class _2012._2._22_2.F1 f1) IL_0000: nop IL_0001: newobj instance void _2012._2._22_2.F1::.ctor() IL_0006: stloc.0 IL_0007: ret } // end of method Class1::Main
即:new 1.为实例分配内存 2.调用构造函数初始化对象
(实际初始化过程比这个复杂的很多,不属于这个范畴了)
2.2 new 隐藏父类的同名方法
说隐藏功能的时候,首先要说一下方法表的结构:
内存分为:stack堆栈和heap堆(托管堆)
托管堆:又分为GC堆,GC堆由GC回收器控制内存释放,还分为Load堆,Load堆不受GC回收器控制,当Appdomain卸载时,自动释放内存。
重点说一下Load堆:
Load堆内有 类型表,类型表内有TypeMethod类型方法表:
如下代码:
class F1 { public void S1() { Console.WriteLine("F1"); } } class F2 : F1 { public void S2() { Console.WriteLine("F2"); } } class F3 : F2 { public void S3() { Console.WriteLine("F3"); } }
如果初始化F3的对象
static void Main()
{
F3 f3= new F3();
}
内存的分配对应的方法表如图:
对应的F3类型有一张方法表,对于的F2类型也有一张类型表,对应的F1类型也有一张类型表。
下面开始重点:
案例1:
class Class1 { static void Main() { F1 f1 = new F1(); f1.S(); F1 f2 = new F2(); f2.S(); F1 f3 = new F3(); f3.S(); } } class F1 { public void S() { Console.WriteLine("F1"); } } class F2 : F1 { public void S() { Console.WriteLine("F2"); } } class F3 : F2 { public void S() { Console.WriteLine("F3"); } }
输出结果: 全部是F1
分析:
1.F1 f1=new F2(); 到底调用那一个S()方法?
调用什么方法,看的不是引用变量的类型,而是看new之后的对象是什么类型,这里是F2类型(),那么就会去找在Load堆F2的类型表,根据F2是继承F1的方法表,当类型表内出现了相同名字的方法的时候,编译器会自动区分,也就是说,此刻F2类型表内,第一个S()方法应该是F1的,第二个才是自己的S()方法,这里有一个”就近原则“,调用第一个方法,也就是F1的S()方法,因为在类型方法表内部:父类方法在子类前面。
因此,根据这个”就近原则“,输出的所有结果都是:F1。
案例2:
修改部分代码:
class F2 : F1 { public new void S() { Console.WriteLine("F2"); } }
输出结果:还是一样 全部都是 F1。
分析:
new在这里的作用,就是 新建一个与父类方法名一样的方法。
根据我们对 Load堆类型方法表的理解,那么 父类的方法还是在 new出来的子类方法的前面,所以根据就近原则还是调用的F1的S()方法。
案例3:
class Class1 { static void Main() { F1 f1 = new F1(); f1.S(); F1 f2 = new F2(); f2.S(); F1 f3 = new F3(); f3.S(); F3 f4 = new F3(); f4.S(); } } class F1 { public void S() { Console.WriteLine("F1"); } } class F2 : F1 { public void S() { Console.WriteLine("F2"); } } class F3 : F2 { }
输出结果:
F1
F1
F1
F2
这里有个注意点:F3 f4=new F3();调用S()方法的时候,如果F3类中有与父类同名的方法,按照就近原则,会先调用自己的S()方法,如果没有,向上查找最近的S()方法,所以输出第一个方法结果是:F2。
案例4:
new 真的可以隐藏父类方法吗?new的背后其实就是我们刚才的”就近原则“在作怪,不管new还是不new,父类方法一定是在子类方法前面,回想下Load堆内部的方法表的顺序,就可以理解了。new其实没用隐藏!
那么什么动作会导致父类方法消失呢,就是override,会覆盖父类的方法。
代码如下:
class Class1 { static void Main() { F1 f1 = new F1(); f1.S(); F1 f2 = new F2(); f2.S(); F1 f3 = new F3(); f3.S(); } } class F1 { public virtual void S() { Console.WriteLine("F1"); } } class F2 : F1 { public override void S() { Console.WriteLine("F2"); } } class F3 : F2 { }
输出结果:
F1
F2
F2
分析:
对于类型F2与类型F3的类型方法表内部,已经没有F1的S()方法了,F3调用S(),就算是就近原则,也是F2的覆盖过的S()方法。
总结:这下应该理解了new的真的含义了。
相关文章推荐
- 关于(javascript) "if"关键字的一个疑惑的地方
- 关于String str =new String("abc")和 String str = "abc"的比较
- 关于"干货集中营"的一个开源App
- 推荐一个关于"架构"的演示文稿(PPT)
- 分享关于学习new BufferedWriter()方法时常遇到的一个无厘头的问题
- 关于JFinal的单个input标签type=file的multiple="multiple"多图片上传,每次只接收到一个uploadFile对象的解决办法。
- 关于未能加载文件或程序集“Accessibilitu,Version=2.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7110d50a3a"或它的某一个依赖项
- spring中关于不能 new ClassPathXmlApplicationContext("");的问题
- 关于枚举的一个小问题:Syntax error on token "enum", interface expected
- 关于一个表达式里有多个"++" 与 "--"
- 关于"干货集中营"的一个开源App
- 关于new的一个问题
- 总结C++中三种关于"new"的使用方法
- 关于他们回答的 "怎样在桌面建一个python GUI的快捷方式" 这个问题
- 写一个方法对日期格式化 new Date().format("yyyy-MM-dd HH:mm:ss")
- 如何预测一个互联网产品的未来—一套关于产品的数学模型
- 一个关于类数组中如何使用new
- 关于"干货集中营"的一个开源App
- 关于 "New.net Startup" 键
- 关于JAVA中String="abc"和String=new String("abc")的区别与联系