c#中的面向对象基础知识总结
面向对象
1、面向过程----->面向对象
1. 面向过程:面向的是完成这件事儿的过程,强调的是完成这件事儿的动作。
面向对象:意在写出一个通用的代码,屏蔽差异。
我们在代码中描述一个对象,通过描述这个对象的属性和方法,对象必须是看得见摸得着的。
我们把这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来类这个概念。
类就是个模子,确定了对象应该具有的属性和方法。
对象是根据类创建出来的。
类就是一个盖大楼的图纸对象就是盖出来的大楼。
2、类
语法:
[public] class 类名
{
字段;
属性;
方法;
}
写好了一个类之后,我们需要创建这个类的对象,
那么,我们管创建这个类的对象过程称之为类的实例化。
使用关键字 new.
this:表示当前这个类的对象。
类是不占内存的,而对象是占内存的。
3、属性
属性的作用就是保护字段、对字段的赋值和取值进行限定。
属性的本质就是两个方法,一个叫get()一个叫set()。
既有get()也有set()我们诚之为可读可写属性。
只有get()没有set()我们称之为只读属性
没有get()只有set()我们称之为只写属性
Field:字段
Method:方法
Property:属性
4、对象的初始化
当我们创建好一个类的对象后,需要给这个对象的每个属性去赋值。我们管这个过程称之为对象的初始化。
5、静态和非静态的区别
1)、在非静态类中,既可以有实例成员,也可以有静态成员。
2)、在调用实例成员的时候,需要使用对象名.实例成员;
在调用静态成员的时候,需要使用类名.静态成员名;
总结:静态成员必须使用类名去调用,而实例成员使用对象名调用。
静态函数中,只能访问静态成员,不允许访问实例成员。
实例函数中,既可以使用静态成员,也可以使用实例成员。
静态类中只允许有静态成员,不允许出现实例成员。
使用:
1)、如果你想要你的类当做一个"工具类"去使用,这个时候可以考虑将类写成静态的。
2)、静态类在整个项目中资源共享。
只有在程序全部结束之后,静态类才会释放资源。
堆栈静态存储区域
释放资源。GC Garbage Collection垃圾回收器
6、构造函数
作用:帮助我们初始化对象(给对象的每个属性依次的赋值)
构造函数是一个特殊的方法:
1)、构造函数没有返回值,连void也不能写。
2)、构造函数的名称必须跟类名一样。
创建对象的时候会执行构造函数
构造函数是可以有重载的。
类当中会有一个默认的无参数的构造函数,当你写一个新的构造函数之后,不管是有参数的还是无参数的,那个默认的无参数的构造函数都被干掉了。
7、new关键字
Person zsPerson=new Person();
new帮助我们做了3件事儿:
1)、在内存中开辟一块空间
2)、在开辟的空间中创建对象
3)、调用对象的构造函数进行初始化对象
8、this关键字
1)、代表当前类的对象
2)、在类当中显示的调用本类的构造函数 :this
2、命名空间
1.可以认为类是属于命名空间的。
如果在当前项目中没有这个类的命名空间,需要我们手动的导入这个类所在的
命名空间。
1)、用鼠标去点
2)、alt+shift+F10
3)、记住命名空间,手动的去引用
2、在一个项目中引用另一个项目的类
1)、添加引用
2)、引用命名空间
3、值类型和引用类型
区别:
1、值类型和引用类型在内存上存储的地方不一样。
2、在传递值类型和传递引用类型的时候,传递的方式不一样。
值类型我们称之为值传递,引用类型我们称之为引用传递。
我们学的值类型和引用类型:
值类型:int、double、bool、char、decimal、struct、enum
引用类型:string 数组自定义类集合 object 接口
存储:
值类型的值是存储在内存的栈当中。值类型在复制的时候,传递的是这个值得本身。
引用类型的值是存储在内存的堆中。引用类型在复制的时候,传递的是对这个对象的引用。
string s1 = "张三";
string s2 = s1;
s2 = "李四";
Console.WriteLine(s1);
Console.WriteLine(s2);
Console.ReadKey();
输出的:张三,李四因为字符串的不可变性,每一次重新开辟空间
4、字符串
1、字符串的不可变性
当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存储新值。
当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。
2、我们可以讲字符串看做是char类型的一个只读数组。
ToCharArray();将字符串转换为char数组
new string(char[] chs):能够将char数组转换为字符串
3、字符串提供的各种方法
1)、Length:获得当前字符串中字符的个数
2)、ToUpper():将字符转换成大写形式
3)、ToLower():将字符串转换成小写形式
4)、Equals(lessonTwo,StringComparison.OrdinalIgnoreCase):比较两个字符串,可以忽略大小写
5)、Split():分割字符串,返回字符串类型的数组。
6)、Substring():解决字符串。在截取的时候包含要截取的那个位置。
7)、IndexOf():判断某个字符串在字符串中第一次出现的位置,如果没有返回-1、值类型和引用类型在内存上存储的地方不一样。
8)、LastIndexOf():判断某个字符串在字符串中最后一次出现的位置,如果没有同样返回-1
9)、StartsWith():判断以....开始
10)、EndsWith():判断以...结束
11)、Replace():将字符串中某个字符串替换成一个新的字符串
12)、Contains():判断某个字符串是否包含指定的字符串
13)、Trim():去掉字符串中前后的空格
14)、TrimEnd():去掉字符串中结尾的空格
15)、TrimStart():去掉字符串中前面的空格
16)、string.IsNullOrEmpty():判断一个字符串是否为空或者为null
17)、string.Join():将数组按照指定的字符串连接,返回一个字符串。
5、继承
1.我们可能会在一些类中,写一些重复的成员,我们可以将这些重复的成员,
单独的封装到一个类中,作为这些类的父类。
Student、Teacher、Driver 子类派生类
Person 父类基类
子类继承了父类,那么子类从父类那里继承过来了什么?
首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。
问题:子类有没有继承父类的构造函数?
答:子类并没有继承父类的构造函数,但是。子类会默认的调用父类无参数的构造函数,
创建父类对象,让子类可以使用父类中的成员。
所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的就被干掉了,
子类就调用不到了,所以子类会报错。
解决办法:
1)、在父类中重新写一个无参数的构造函数。
2)、在子类中显示的调用父类的构造函数,使用关键字:base()
2、继承的特性
1、继承的单根性:一个子类只能有一个父类。
2、继承的传递性
3、查看类图
4、object是所有类的基类。
5、new关键字
1)、创建对象
2)、隐藏从父类那里继承过来的同名成员。
隐藏的后果就是子类调用不到父类的成员。
6、里氏转换
1.
1)、子类可以赋值给父类
2)、如果父类中装的是子类对象,那么可以讲这个父类强转为子类对象。
2、
子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。
3、
is:表示类型转换,如果能够转换成功,则返回一个true,否则返回一个false
as:表示类型转换,如果能够转换则返回对应的对象,否则返回一个null
7、集合
1.ArrayList方法
1)引用System.Collection命名空间
2)添加数据:Add()、AddRange()---添加数组用add
使用Add可以添加数据,并且与数组一样可以使用“下标”(索引)访问数据
使用下标访问的数据是object类型的,必要时需要进行转换,必须满足里氏转换原则.为什么要把Add参数设为object的呢?为了通用
3)AddRange将数组或集合当中的数据批量的一个一个的加进来
数组或集合中有多少个数据,新集合中就添加了多少个数据,类型等一一对应,但Add同样可以将数组或集合作为数据加入,但此时加入将数组或集合作为一个项添加过去,只有找到该项,才能通过“下标”访问到其中的数据
4)
插入数据:Insert()
使用语法:
集合名.Insert(位置, 值);
位置的确定,使用插入好的数据来数,第二个数据就是要插入的数据内容
Remove方法
arrayList.Remove(数据)
删掉集合中,与“数据”相同的数据
当集合中不存在这个数据时,直接忽略
方法执行从左至右删掉第一个出现的数据,其后数据不考虑
RemoveAt方法
删掉指定索引出的数据,其后数据依次向前移动
当索引超出范围后,异常
Clear()
判断是否包含:Contains()
Sort等可以处理排序功能(甚至还有Max、Min、AVG等)
获得长度使用Count属性
5)ArrayList集合的长度问题
每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capcity)的时候,
集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。
2、Hastable键值对集合
字典
在键值对集合当中,我们是根据键去找值的。
键值对对象[键]=值;
*****:键值对集合当中,键必须是唯一的,而值是可以重复的
3、foreach循环
foreach(知道实现了Ieumerable接口)
语法:
foreach(集合中单个的类型局部变量名in 集合对象)
{
// 循环体
// 循环体当中“局部变量”表示集合中遍历的数据
}
4.泛型集合List<T>
就是为了专门处理某种类型
ArrayList对应的是 List<类型名>
在尖括号中写什么类型,这个集合就变成了什么类型的集合
添加数据、插入数据、索引访问数据都是这个类型的,不用考虑所有的转化问题
5、装箱、拆箱
装箱:就是将值类型转换为引用类型。
拆箱:将引用类型转换为值类型。
看两种类型是否发生了装箱或者拆箱,要看,这两种类型是否存在继承关系。
6Dictionary <TKey,TValue>
默认提供命名空间,提倡使用
Hashtable对应的是 Dictionary<键的类型, 值的类型>
在尖括号中填入键的类型与值的类型,那么这个集合就变成了一个指定的键值对模型
其使用方式与Hashtable一样
Eg:
Dictionary
namespace _04Dictionary { class Program { static void Main(string[] args) { Dictionary<int, string>dic = new Dictionary<int, string>(); dic.Add(1, "张三"); dic.Add(2, "李四"); dic.Add(3, "王五"); dic[1] = "新来的"; foreach (KeyValuePair<int,string>kv in dic) { Console.WriteLine("{0}---{1}",kv.Key,kv.Value); } //foreach (var item in dic.Keys) //{ // Console.WriteLine("{0}---{1}",item,dic[item]); //} Console.ReadKey(); } } }
8、操作文件
1、编码格式
产生乱码的原因就是因为你保存这个文件的编码格式跟你打开这个文件的编码格式不一样。
2、绝对路径和相对路径
绝对路径:通过给定的这个路径直接能在我的电脑中找到这个文件。
相对路径:文件相对于应用程序的路径。
结论:
我们在开发中应该去尽量的使用相对路径。
3、文本文件
拖到txt文件中,还能看得懂的文件就是文本文件。
- 基本操作:判存、复制、移动、删除
- 基本方法:
– File.Exist();
– File.Copy();
– File.Move();
– File.Delete()
4、文件流FileStream
namespace _06文件流 { class Program { static void Main(string[] args) { //使用FileStream来读取数据 FileStreamfsRead = new FileStream(@"C:\Users\SpringRain\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Read); byte[] buffer = new byte[1024 * 1024 * 5]; //3.8M 5M //返回本次实际读取到的有效字节数 int r = fsRead.Read(buffer, 0, buffer.Length); //将字节数组中每一个元素按照指定的编码格式解码成字符串 string s = Encoding.UTF8.GetString(buffer, 0, r); //关闭流 fsRead.Close(); //释放流所占用的资源 fsRead.Dispose(); Console.WriteLine(s); Console.ReadKey(); //使用FileStream来写入数据,将创建文件流对象的过程写在using当中,会自动的帮助我们释放流所占用的资源。 using (FileStreamfsWrite = new FileStream(@"C:\Users\SpringRain\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Write)) { string str = "看我游牧又把你覆盖掉"; byte[] buffer = Encoding.UTF8.GetBytes(str); fsWrite.Write(buffer, 0, buffer.Length); } Console.WriteLine("写入OK"); Console.ReadKey(); } } } //使用文件流来实现多媒体文件的复制 namespace _07使用文件流来实现多媒体文件的复制 { class Program { static void Main(string[] args) { //思路:就是先将要复制的多媒体文件读取出来,然后再写入到你指定的位置 string source = @"C:\Users\SpringRain\Desktop\1、复习.wmv"; string target = @"C:\Users\SpringRain\Desktop\new.wmv"; CopyFile(source, target); Console.WriteLine("复制成功"); Console.ReadKey(); } public static void CopyFile(string soucre, string target) { //1、我们创建一个负责读取的流 using (FileStreamfsRead = new FileStream(soucre, FileMode.Open, FileAccess.Read)) { //2、创建一个负责写入的流 using (FileStreamfsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write)) { byte[] buffer = new byte[1024 * 1024 * 5]; //因为文件可能会比较大,所以我们在读取的时候应该通过一个循环去读取 while (true) { //返回本次读取实际读取到的字节数 int r = fsRead.Read(buffer, 0, buffer.Length); //如果返回一个0,也就意味什么都没有读取到,读取完了 if (r == 0) { break; } fsWrite.Write(buffer, 0, r); } } } } } } 5.StreamReader和StreamWriter namespace _08StreamReader和StreamWriter { class Program { static void Main(string[] args) { //使用StreamReader来读取一个文本文件 //using (StreamReadersr = new StreamReader(@"C:\Users\SpringRain\Desktop\抽象类特点.txt",Encoding.Default)) //{ // while (!sr.EndOfStream) // { // Console.WriteLine(sr.ReadLine()); // } //} //使用StreamWriter来写入一个文本文件 using (StreamWritersw = new StreamWriter(@"C:\Users\SpringRain\Desktop\newnew.txt",true)) { sw.Write("看我有木有把你覆盖掉"); } Console.WriteLine("OK"); Console.ReadKey(); } } }
9、多态
1、实现多态的手段
1)、虚方法
- 用virtual修饰的方法叫做虚方法
步骤:
1、将父类的方法标记为虚方法,使用关键字 virtual,这个函数可以被子类重新写一个遍。
2、在子类中通过override关键字来重写
2)、抽象类
当父类中的方法不知道如何去实现的时候,可以考虑将父类写成抽象类,将方法写成抽象方法。
1.抽象成员必须标记为abstract,并且不能有任何实现。
2.抽象成员必须在抽象类中。
3.抽象类不能被实例化
4.子类继承抽象类后,必须把父类中的所有抽象成员都重写。
(除非子类也是一个抽象类,则可以不重写)
5.抽象成员的访问修饰符不能是private
6.在抽象类中可以包含实例成员。
并且抽象类的实例成员可以不被子类实现
7.抽象类是有构造函数的。虽然不能被实例化。
8、如果父类的抽象方法中有参数,那么。继承这个抽象父类的子类在重写父类的方法的时候必须传入对应的参数。
9.如果抽象父类的抽象方法中有返回值,那么子类在重写这个抽象方法的时候也必须要传入返回值。
======
注:
如果父类中的方法有默认的实现,并且父类需要被实例化,这时可以考虑将父类定义成一个普通类,用虚方法来实现多态。
如果父类中的方法没有默认实现,父类也不需要被实例化,则可以将该类定义为抽象类。
3)、接口
[public] interface I..able
{
成员;
}
接口是一种规范。
1.只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员
2.为了多态。接口不能被实例化。也就是说,接口不能new(不能创建对象)
3.接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。
(默认为public)
4.接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。
5.接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数。
6.接口与接口之间可以继承,并且可以多继承。
7.接口并不能去继承一个类,而类可以继承接口(接口只能继承于接口,而类既可以继承接口,也可以继承类)
8.实现接口的子类必须实现该接口的全部成员。
9.一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。class MyClass:A,IA{},因为类是单继承的。
11.显示实现接口的目的:解决方法的重名问题
什么时候显示的去实现接口:
当继承的借口中的方法和参数一摸一样的时候,要是用显示的实现接口
当一个抽象类实现接口的时候,需要子类去实现接口。
10、c#中的访问修饰符
public :公开的公共的
private:私有的,只能在当前类的内部访问
protected:受保护的,只能在当前类的内部以及该类的子类中访问。
internal:只能在当前项目中访问。在同一个项目中,internal和public的权限是一样。
protected internal:protected+internal
1)、能够修饰类的访问修饰符只有两个:public、internal。
2)、可访问性不一致。
子类的访问权限不能高于父类的访问权限,会暴漏父类的成员。
11、设计模式
设计这个项目的一种方式。
1.简单工厂设计模式
12、序列化和反序列化
序列化:就是将对象转换为二进制
反序列化:就是将二进制转换为对象
作用:传输数据。
序列化:
1)、将这个类标记为可以被序列化的。[Serializable]
//要将p这个对象传输给对方电脑 //Person p = new Person(); //p.Name = "张三"; //p.Age = 19; //p.Gender = '男'; //using (FileStreamfsWrite = new FileStream(@"C:\Users\SpringRain\Desktop\111.txt", FileMode.OpenOrCreate, FileAccess.Write)) //{ // //开始序列化对象 // BinaryFormatter bf = new BinaryFormatter(); // bf.Serialize(fsWrite, p); //} //Console.WriteLine("序列化成功"); //Console.ReadKey(); //接收对方发送过来的二进制反序列化成对象 Person p; using (FileStreamfsRead = new FileStream(@"C:\Users\SpringRain\Desktop\111.txt", FileMode.OpenOrCreate, FileAccess.Read)) { BinaryFormatter bf = new BinaryFormatter(); p = (Person)bf.Deserialize(fsRead); } Console.WriteLine(p.Name); Console.WriteLine(p.Age); Console.WriteLine(p.Gender); Console.ReadKey(); } } [Serializable] public class Person { private string _name; public string Name { get { return _name; } set { _name = value; } } private char _gender; public char Gender { get { return _gender; } set { _gender = value; } } privateint _age; publicint Age { get { return _age; } set { _age = value; } } } }
13、其他类
1、partial部分类
2、sealed密封类
不能够被其他类继承,但是可以继承于其他类。