1、值类型引用类型
(1)值类型均隐式派生自System.ValueType
数值类型、bool、结构、枚举
查看IL,隐式继承自ValueType
(2)引用类型派生自System.Object
字符串、数组、类、接口等
查看IL隐式继承自Object
引用类型变量的赋值只复制对对象的引用。
值类型变量赋值会拷贝一个副本。
值类型、引用类型作为参数传递
值传递,刚才讨论的参数传递方式都是值传递。
引用传递,ref,传递的是变量的地址。
2、 接口
(1)什么是接口?
接口就是一种规范,协议(*),约定好遵守某种规范就可以写通用的代码。
定义了一组具有各种功能的方法。(只是一种能力,没有具体实现,像抽象方法一样,“光说不做”)
(2)接口存在的意义:多态。多态的意义:程序可扩展性。最终→节省成本,提高效率。
接口解决了类的多继承的问题
接口解决了类继承以后体积庞大的问题。
接口之间可以实现多继承
(3)案例(继承了一个类,同时实现了其他接口)
鸟-麻雀sparrow[‘spærəu],鸵鸟ostrich[‘ɔstritʃ],企鹅penguin[‘pengwin] ,鹦鹉parrot[‘pærət]
鸟能飞,鸵鸟,企鹅不能。。。你怎么办
(4)子类继承抽象类,实现接口
接口中的成员必须不能有实现,接口不能实例化。
接口中的成员不能有访问修饰符,隐式公开public
接口中可以有属性、方法、索引器等(其实都是方法),但不能有字段
接口中的所有成员必须被子类中全部实现,除非子类是抽象类,把接口中的成员标记为抽象的。
(5)显示实现接口(*)
为什么要显示实现接口?
方法重名后的解决办法。
显示实现接口后,只能通过接口来调用。不能通过类对象本身来调用为什么要有“显示实现接口”?
(6)接口的特点总结
a.接口是一种规范。为了多态。
b.接口不能被实例化。
c.接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。(默认为public)
d.接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。
e.接口中只能有方法、属性、索引器、事件,不能有“字段”。
f.接口与接口之间可以继承,并且可以多继承。
g.实现接口的子类必须实现该接口的全部成员。
h.一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。class MyClass:A,IA{},因为类是单继承的。
i.当一个抽象类实现接口的时候,如果不想把接口中的成员实现,可以把该成员实现为abstract。(抽象类也能实现接口,用abstrac标记)
j.“显示实现接口”,只能通过接口变量来调用(因为显示实现接口后成员为private)。
(7)使用接口的建议
a.面向抽象编程,使用抽象(父类、抽象类、接口)不使用具体。
b.“向上转型”
c.在编程时:
接口→抽象类→父类→具体类(在定义方法参数、返回值、声明变量的时候能用抽象就不要用具体。)
能使用接口就不用抽象类,能使用抽象类就不用类,能用父类就不用子类。
避免定义“体积庞大的接口”、“多功能接口”,会造成“接口污染”。只把相关联的一组成员定义到一个接口中(尽量在接口中少定义成员)。单一职责原则应用在了接口上“接口隔离原则”。
定义多个职责单一的接口(小接口)(组合使用)。(印刷术与活字印刷术)
3、类型转换CAST
(1)隐式类型转换
doublenum=10;//sizeof(double)/sizeof(int)
(2)显示类型转换
int n=(int)num;
(3)把学生转换为人是隐式转换,把人转换为学生则是显式转换(强制转换)
Student s = newStudent(); Person p =s;//隐式类型转换
Student stu =(Student)p;//显示类型转换 、obj as 类型。
只有在内存存储上存在交集的类型之间才能进行隐式转换。
不能用Cast转换string/int,只能用Convert。Convert.ToInt32/Convert.ToString
(4)将任意类型转换成字符串:
ToString()
(5)将字符串转换成“数值类型”(int、float、double):
int.Parse(stringstr);、int.TryParse(string str,out int n);//很常用,推荐。
double.Parse(stringstr);、double.TryParse(string str,out double d);
……
Parse()转换失败报异常,TryParse()转换失败不报异常。
(6)再说as与直接类型转换:(*)
if(p isStudent){ Student stu=(Student)p; }
CLR会进行两次类型检查if(检查一次){ //再检查一次 }
通过GetType(),GetType()不允许重写。
Student stu=pas Student;//推荐,效率高于第一种,如果转换失败返回null,而不会报异常。
(7)类型转换Convert
Convert考虑数据意义的转换。Convert是一个加工、改造的过程。
若要进行其它类型的转换可以使用Convert.ToInt32、Convert.ToString等。Convert可以把object类型转换为其它类型
string str= null;
int num= 0;
num = Convert.ToInt32(str);
Console.Write(num +“\r\n”);
num= Int32.Parse(str);
Console.Write(num +“\r\n”);
Int32.TryParse(str, out num);
Console.Write(num +”\r\n”);
当遇到类型转换的时候不知道该怎么转,可以去Convert中找找。
4、异常处理
(1)什么是异常?
程序运行时发生的错误。(错误的出现并不总是程序员人的原因,有时应用程序会因为最终用户或运行代码的环境改变而发生错误。比如:1.连接数据库时数据库服务器停电了;2.操作文件时文件没了、权限不足等;3.计算器用户输入的被除数是0;4.使用对象时对象为null;等等。)
.net为我们把“发现错误(try)”的代码与“处理错误(catch)”的代码分离开来。
(2)异常处理的一般代码模式:
try{ …可能发生异常的代码…}catch{ …对异常的处理… }finally{ …无论是否发生异常、是否捕获异常都会执行的代码… }
try块:可能出问题的代码。当遇到异常时,后续代码不执行。
catch块:对异常的处理。记录日志(log4net),继续向上抛出等操作。(只有发生了异常,才会执行。)
finally块:代码清理、资源释放等。无论是否发生异常都会执行。
(3)异常处理代码的其他几种形式:
try → 多个catch → 一个finally
try→(1个或多个catch),多个catch的顺序问题。可以没有finally。
try→finally(只能有一个),没有catch也可以。
(4)注意点:
发生异常后,try块中,异常代码后的代码不会执行。
finally块中的代码,无论是否发生异常都会执行。
finally中不能写return语句。
try中有return语句,finally也会执行
即便没有catch(或者没有找到合适的catch块),finally中的代码也会执行,但finally之后的代码则不会。(见备注1.)
(5)Exception ex异常也是对象,封装了异常发生时的一些信息。
(6)Exception类主要属性:Message、StackTrace、InnerException(*)
(7)扔出自己的异常,扔:throw,抓住:catch
(8)建议:通过逻辑判断(if-else)减少异常发生的可能性!尽量避免使用“异常处理”。
(9)在多级方法嵌套调用的时候,如果发生了异常,则会终止所有相关方法的调用,并且释放相关的资源。
5、函数返回值(函数参数前的修饰符)
(1)params可变参数 无论有几个参数,必须出现在参数列表的最后。可以为可变参数直接传递一个对应类型的数组。
(2)ref仅仅是一个地址,引用传递,可以把值传递强制改为引用传递
(3)out让函数可以输出多个值
1.在方法中必须为out参数赋值
2.out参数的变量在传递之前不需要赋值,即使赋值了也不能在方法中使用。(赋值没意义)
(4)ref
参数在传递之前必须赋值
在方法中可以不为ref参数赋值,可以直接使用。
(5)ref应用场景内部对外部的值进行改变,out则是内部为外部变量赋值,out一般用在函数有多个返回值的场所。
6、方法重载
(1)方法名称相同
(2)方法签名不同
方法名
参数类型、个数、(顺序)
参数的修饰符(ref、out、params)
不包含方法返回值。
7、Equals、==、ReferenceEquals方法
(1)为什么字符串的Equals和别的不一样?
string的Equals方法判断的是字符串的内容是否相同(重写了Object中的Equals方法。而object中的Equals方法是判断对象的地址是否相同
(2)查看String类的==运算符,内部调用的也是Equals
(3)判断两个对象是否相同要用:object. object.ReferenceEquals();
8、常用类库String
(1)字符串的一些特性:
a.不可变性(ToUpper演示)
b.字符串暂存池(拘留池)(针对字符串常量)
内部维护一个哈希表key为字符串,value是地址。每次为一个新变量赋值都会找key中是否有,如果有则直接把value中的地址赋值给新变量
演示”abc”与控制台输入的”abc”与”a”+”b”+”c”与三个变量abc相加是否为同一个对象,以此说明只针对常量。
c.字符串留用(Intern,针对变量常量,见备注1)。
对于动态字符串本身在哈希表中没有,通过这种Intern可以添加到该哈希表中,目的为了提高性能。
String.Intern(xx),Intern方法使用暂存池来搜索与str 值相等的字符串。如果存在这样的字符串,则返回暂存池中它的引用。如果不存在,则向暂存池添加对str的引用,然后返回该引用。
String.IsInterned(xx),此方法在暂存池中查找str。如果已经将str放入暂存池中,则返回对此实例的引用;否则返回nullNothingnullptrnull引用
(2)String字符串,字符串可以看成字符数组,不可变特性(通过for循环,修改string中的元素,失败!)。
a.属性
Length //获得字符串中字符的个数。”aA我你他”→5
b.方法
IsNullOrEmpty() 静态方法,判断为null或者为””(静态方法)
ToCharArray() 将string转换为char[], new string(char[])
ToLower() 小写,必须接收返回值。(因为:字符串的不可变);
ToUpper() 大写。
Equals() 比较两个字符串是否相同。 忽略大小写的比较,StringComparation.
IndexOf() 如果没有找到对应的数据,返回-1.//面试题:统计一个字符串中,”天安门”出现的次数。
LastIndexOf() 如果没有找到对应的数据,返回-1
Substring() //2个重载,截取字符串。
Split() //分割字符串。
Join() 静态方法
Format () 静态方法
Replace()
9、常用类库StringBuilder
(1)StringBuilder高效的字符串操作
当大量进行字符串操作的时候,比如,很多次的字符串的拼接操作。
String 对象是不可变的。每次使用System.String类中的一个方法时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的String对象相关的系统开销可能会非常大。如果要修改字符串而不创建新的对象,则可以使用System.Text. StringBuilder类。例如,当在一个循环中将许多字符串连接在一起时,使用StringBuilder类可以提升性能。
(2)StringBuilder != String//将StringBuilder转换为String.用ToString();
(3)StringBuilder仅仅是拼接字符串的工具,大多数情况下还需要把StringBuilder转换为String.
StringBuilder sb= new StringBuilder();
sb.Append();//追加字符串
sb.ToString();//把StringBuilder转换为字符串。
sb.Insert();
sb.Replace();
10、垃圾回收(*)
(1)垃圾回收的目的:提高内存利用率。
(2)垃圾回收器,只回收托管堆中的内存资源,不回收其他资源(数据库连接、文件句柄、网络端口等)。
(3)什么样的对象才会被回收?
没有变量引用的对象。没有变量引用的对象,表示可以被回收了(null),断了线的风筝,再也回不来了。
大学食堂(自己收盘子)、大排档(不需要程序员自己收盘子)
(4)什么时间回收?
不确定,当程序需要新内存的时候开始执行回收。
GC.Collect();//手动调用垃圾回收器。不建议使用,垃圾回收时会暂停一下(非常短暂)让程序自动去GC。
(5)垃圾回收器中“代”的概念:
共3代:第0代、第1代、第2代。
各代的回收频率:第0代最高,其次第1代,再次第2代。也就是说越老的对象生存几率越大。
(6).net中垃圾回收机制:mark-and-compact(标记和压缩),一开始假设所有对象都是垃圾。
(7)除了内存资源外的其他资源怎么办?~或者Dispose()
11、.net中的集合
(1)集合命名空间:
usingSystem.Collections;(非泛型集合)
usingSystem.Collections.Generic;(泛型集合)
(2)常用集合
“类似数组”集合:ArrayList、List
“键值对”集合(“哈希表”集合):Hashtable、Dictionary