面向对象---继承
一、命名空间:可以看作是类的文件夹,用来解决类的重名问题。
1、导入命名空间
1)、Alt+Shift+F10(快捷键);
2)、鼠标放在类名字上,最左面会出现一个小箭头,点击显示可修补程序,然后导入需要的命名空间。
2、引用其他项目的类
1)、添加引用;
2)引用命名空间。
二、值类型和引用类型(变量类型):两者传递值的方式不一样,值类型称为值传递,引用类型称为引用传递。
1、值类型
1)各个值类型:int, double, bool, char, decimal, struct, enum, 可空类型。
2)存储位置:值类型的值存储在内存的栈中
2、引用类型
1)、各个引用类型:string, 类,接口,委托,数组,object。
2)存储位置:引用类型的值存储在内存的堆中。同时,引用类型同样会在栈中开辟空间,存放引用(堆中的值的地址)。
3、两种类型的存储位置分析图
三、字符串的特性
1、字符串的不可变性:当给字符串重新赋值时,原来的值并不会改变,而是开辟新的内存空间存储新值。当程序结束后,GC扫描整个内存时,如果发现有的内存空间没有被指向,则立刻销毁它。
2、字符串可以看作是一个char类型的只读数组,我们可以通过下标去访问字符串中的某个元素。
如果你想改变其中某个元素的值,可以
1)先将字符串转化为char类型的数组
2)将字符数组转化为字符串
string s = "abc"; Console.WriteLine(s[0]);//返回char类型元素 char[] chs = s.ToCharArray(); chs[0] = 'b'; s = new string(chs);
四、继承:把几个类中重复的成员单独拿出来封装成一个类,做为这几个类的父类,然后子类可以继承父类的属性和方法(成员)。用来解决代码冗余问题。
1、父类---》基类,子类---》派生类
2、继承的特性
1)单根性:一个子类只能有一个父类;
2)传递性:派生类从基类中继承特性,同时派生类也可以成为其他类的基类。这样就从一个基类派生出多层类,形成类层次结构。
3、object类是所有类的基类。所有类都直接或间接地继承了object类。
4、注意事项:
1)子类无法访问父类的私有字段;
2)子类没有继承到父类的构造函数。当创建子类对象时,子类会先调用父类默认的无参数构造函数来创建父类对象,让子类可以使用父类中的成员,然后再调用子类的构造函数创建子类对象。
3)如果父类默认无参的构造函数被新写的构造函数覆盖掉,子类调用不到会报错。我们可以用一下解决方法:
a、在父类中重新写一个无参的构造函数;
b、在子类显示调用父类的构造函数,使用关键字base。
namespace Inheritance { public class person { public string Name {get; set; } public int Age { get; set; } public person(string name, int age) { this.Name = name; this.Age = age; } } public class student:person { public student(string name, int age) : base(name, age) { Console.WriteLine("我叫{0},今年{1}岁",name ,age); } static void Main(string[] args) { student s = new student("张三", 18); Console.ReadKey(); } } }
五、里氏转换
1、子类可以赋值给父类。如果有一个地方需要父类作为参数,可以传递一个子类对象进去。
2、如果父类中装的是子类对象,那么这个父类是强制转为子类对象。
3、as运算符和is运算符
1)、as运算符用于兼容的引用类型之间执行类型转换,转换成功返回相应的对象,否则返回false。
2)、is运算符用来检查对象是否跟给定的类型兼容并执行类型转换,转换成功返回true,否则返回false。
例子:
namespace 里氏转换 { class Program { static void Main(string[] args) { person p = new student();//把子类对象传递给父类 if (p is student)//使用is进行类型转换,把父类强制转换成子类对象 { ((student)p).StudentSayHi(); } student s2 = p as student;//使用as进行类型转换,把父类强制转换成子类对象 s2.StudentSayHi(); Console.ReadKey(); } } class person { public void SayHi() { Console.WriteLine("我是人类。"); } } class student : person { public void StudentSayHi() { Console.WriteLine("我是学生。"); } } }
在控制台换行打印出:
我是学生。
我是学生。
六、集合:很多数据的一个集合,长度可以任意改变,类型不限。(在命名空间System.Collections中)
1、ArrayList集合
例子:
namespace Collection { class Program { static void Main(string[] args) { //创建一个集合对象 ArrayList list = new ArrayList(); list.Clear();//移除所有元素 list.Add(1); //添加单个元素 list.Add(2); list.Add(3); list.Add("多余的"); list.Add('c'); list.Add("hello"); list.Add(true); list.AddRange(new int[] { 1, 2, 3, 4}); //添加集合元素 Rose r = new Rose(); list.Add(r);//添加子类对象 //list.AddRange(list);//可以添加集合本身 list.Insert(1, "插入的");//在指定位置中插入单个元素 list.InsertRange(0, new int[] { 1, 2, 3 });//在指定位置插入集合 list.Reverse(); //反转集合的元素 list.Remove("多余的");//写谁就删除谁 list.RemoveAt(0);//根据下标删除元素,删除第一个元素1 list.RemoveRange(0,2);//根据索引删除一定范围的元素,删除第一个到第三个元素 //list.Sort();//升序排列 bool b = list.Contains(1);//判断是否包含元素1,返回布尔类型 for (int i = 0; i < list.Count; i++)//Count---》获取当前集合个数 { //里氏转换 if (list[i] is Rose) { ((Rose)list[i]).roseSayHi(); } Console.WriteLine(list[i]); } Console.WriteLine(b); Console.ReadKey(); } } class Rose { public void roseSayHi() { Console.WriteLine("I'm a Rose~"); } } }
2、Hastable 键值对集合
1)键必须是唯一的,而值可以重复。
2)、在键值对集合中,根据键去找值
3)用foreach来遍历键值对集合中所有的元素。在访问大数据时,foreach的效率要高于for循环。
例子:
namespace HasTable1 { class Program { static void Main(string[] args) { //创建一个新的键值对集合对象 Hashtable ht = new Hashtable(); ht.Add(1, "hello"); //添加新数据 键和键值可以为任何数据类型 ht.Add(false,"world"); ht.Add(2,'!'); ht[3] = "hi,chaara"; //另一种添加数据的方法。如果不存在该键,则添加新数据,如果存在该键,则用新数据覆盖以前的数据 //判断是否存在该键,没有则添加该键以及键值,有的话则提示已经存在该键 if (!ht.ContainsKey(4)) { ht.Add(4, "bye~"); } else { Console.WriteLine("已存在该键!"); } //用foreach遍历该集合的键,并且打印出键跟键值 foreach (var item in ht.Keys) { Console.WriteLine("键是:{0}========键值是:{1}",item, ht[item]); } Console.ReadKey(); } } }
3、List泛型集合
1)、元素类型随集合类型的确定而确定下来。
2)List泛型集合和数组可以互相转换。
例子:
class Program { static void Main(string[] args) { //创建一个泛型集合对象 List<int> list = new List<int>(); list.Add(1);//添加单个元素 list.Add(2); list.AddRange(new int[] { 3, 4, 5 }); //添加集合 list.AddRange(list); //List泛型集合可以转换为数组 int[] nums = list.ToArray(); //数组可以转换为泛型集合 List<int> list2 = nums.ToList(); for (int i = 0; i < list2.Count; i++) { Console.WriteLine(list2[i]); //list[i] 指定索引处 } Console.ReadKey(); } }
4、Dictionary 字典:键和值的类型要确定
例子:
class Program { static void Main(string[] args) { //Dictionary 泛型字典集合 值和键类型确定 Dictionary<int, string> dic = new Dictionary<int, string>(); dic.Add(1,"吟诗"); dic.Add(2,"花粥");//键必须是唯一的 dic[1] = "音阙诗听";//添加键跟值,如果键已存在,则覆盖原来的值 //根据键来遍历 foreach (var item in dic.Keys) { Console.WriteLine("{0}----->{1}",item, dic[item]); } //根据键值对遍历,kv代表键和键值这个整体 foreach (KeyValuePair<int, string> kv in dic) { Console.WriteLine("{0}----->{1}",kv.Key, kv.Value); } Console.ReadKey(); } }
5、集合长度问题
1、count和capity
1)count:表示这个集合中实际包含的元素个数。
2)capity:表示这个集合中可以包含的元素。
2、每次集合中实际包含的元素个数(count)超过了可以包含的元素个数时(capity)时,集合就会向内存多申请开辟多一倍的空间,来保证集合的长度够用。这也就是为什么集合的长度可以不定的原因。
七、Path类:操作文件路径(在命名空间System.IO下)
例子:
class Program { static void Main(string[] args) { string str = @"C:\Users\D\Desktop\test.txt"; Path.GetFileName(str); //获得该路径下的文件名 Path.GetFileNameWithoutExtension(str);//获得该路径下没有扩展名的文件名 Path.GetExtension(str);//获得该路径下文件的扩展名 Path.GetDirectoryName(str);//获得该路径下文件所在的文件夹名称 Path.GetFullPath(str);//获得文件所在的全路径 Path.Combine(@"c:\", "b.txt");//连接两个字符串作为路径 } }
八、File类:操作任意类型的文件。只能操作一些小文件。(在命名空间System.IO下)
1、一般用法
File.Create(@"C:\Users\\Desktop\chaara.txt"); //创建一个新文件 Console.WriteLine("创建完成!"); Console.ReadKey(); //File.Create(@"C:\Users\DuffyH\Desktop\chara.txt"); File.Delete(@"C:\Users\D\Desktop\chara.txt"); //删除一个文件 Console.WriteLine("删除完成!"); Console.ReadKey(); File.Copy(@"C:\Users\D\Desktop\chaara.txt", @"C:\Users\DuffyH\Desktop\chaara2.txt"); //复制一个文件到新文件 Console.WriteLine("复制完成!"); Console.ReadKey(); File.Move(@"C:\Users\D\Desktop\chaara.txt", @"C:\chaara.txt"); //只能在同一个逻辑盘下进行文件转移 Console.WriteLine("移动完成!"); Console.ReadKey();
2、ReadAllBytes:以字节为单位读取文件
//将文件的内容读入一个字符串,将字节数组中的每一个元素都按照指定的编码格式解码成字符串 string path = @"C:\Users\D\Desktop\test.txt"; byte[] b = File.ReadAllBytes(path); string s = Encoding.Default.GetString(b); Console.WriteLine(s); Console.ReadKey();
3、ReadAllLines:以行的方式读取文件
string path = @"C:\Users\D\Desktop\chaara.txt"; string[] contents = File.ReadAllLines(path, Encoding.Default); foreach (string item in contents) { Console.WriteLine(item); } Console.ReadKey();
4、ReadAllText:读取整个文本文件
string path = @"C:\Users\D\Desktop\chaara.txt"; string str = File.ReadAllText(path, Encoding.Default); Console.WriteLine(str); Console.ReadKey();
5、WriteAllLines :以行的方式写入文件
string path = @"C:\Users\D\Desktop\test.txt"; File.WriteAllLines(path, new string[] { "这是一个充实的下午。", "外面的阳光很好~" }); Console.WriteLine("OK!");
6、WriteAllText:创建一个新文件,在其中写入指定的字符串;如果目标文件已存在,则覆盖该文件
string path = @"C:\Users\D\Desktop\test.txt"; File.WriteAllText(path, "山中何事,松花酿酒,春水煎茶。");
7、AppendAllText:对目标文件进行追加写入,不覆盖源文件
string path = @"C:\Users\D\Desktop\test.txt"; File.AppendAllText(path, "这是新内容");
九、装箱和拆箱
1、装箱:将值类型转换成引用类型。
2、拆箱:将引用类型转换成值类型。
3、判断方法:两种类型存在继承关系,才会发生了装箱或者拆箱。
例子:
int n = 10; object o = n;//装箱 int n2=(int)o;//拆箱 string str = "123"; int n = Convert.ToInt32(str);//这里并没有进行拆箱
4、我们应该尽量避免装箱和拆箱,因为这是较为影响性能的操作。
十、FileStream文件流:文件流是操作字节的,所以用它可以操作任意类型的文件。用来操作大文件,因为它是一点点地读取文件,可以减少对内存压力。(在命名空间System.IO下)
1、使用FileStream读取文件
string path = @"C:\Users\D\Desktop\文件流.txt"; //创建一个文件流对象,参数1是文件的路径,参数2是操作文件,参数3是操作文件中的数据 FileStream fsRead = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read); byte[] buffer = new byte[1024 * 1024 * 5]; //参数1为每次读取数据的量,参数2为开始读取数据的位置,参数3为每次最多读取数据的量 int read = fsRead.Read(buffer, 0, buffer.Length); //将字节数组中每一个元素按照指定的编码格式解码成字符串 string s = Encoding.Default.GetString(buffer, 0, read);//参数3表示,虽然读取了5M,但只解码了文件实际的字节 //关闭流 fsRead.Close(); //释放流占用的内存 GC并不会自动释放流占用的空间 fsRead.Dispose(); Console.WriteLine(s);
2、使用FileStream写入数据(将创建文件流对象的过程写在using中,文件流所占用的资源会自动释放)
string path = @"C:\Users\D\Desktop\文件流.txt"; using (FileStream fsWrite = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))//创建一个文件流对象 { string str = "新内容"; byte[] buffer = Encoding.Default.GetBytes(str); fsWrite.Write(buffer, 0, buffer.Length); } Console.WriteLine("写入成功!");
练习:使用文件流来实现多媒体文件的复制
class Program { static void Main(string[] args) { //使用文件流来实现多媒体文件的复制 string source = @"E:\BaiduNetdiskDownload\08使用FileStream实现多媒体文件的复制.avi"; string target = @"C:\Users\DDesktop\new.avi"; CopyFile(source, target); Console.WriteLine("复制成功!"); Console.ReadKey(); } //使用文件流来实现多媒体文件的复制 public static void CopyFile(string source, string target) { using (FileStream fsRead = new FileStream(source, FileMode.Open, FileAccess.Read)) { using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write)) { byte[] buffer = new byte[1024 * 1024 * 5]; while (true) { int read = fsRead.Read(buffer, 0, buffer.Length); if (read == 0) { break; } fsWrite.Write(buffer, 0, buffer.Length); } } } }
十一、stream:(在命名空间System.IO下)
1、StreamReader:以行的形式来读取一个文本文件数据
using (StreamReader sr = new StreamReader(@"C: \Users\D\Desktop\文件流.txt", Encoding.Default)) { //通过循环读取所有行 while (!sr.EndOfStream) { Console.WriteLine(sr.ReadLine()); } }
2、StreamWriter:以行的形式来把数据写入一个文本文件
using (StreamWriter sw = new StreamWriter(@"C: \Users\D\Desktop\文件流1.txt", true)) {//当参数2为true时,不覆盖文本原有数据,为false则覆盖 sw.WriteLine("追着我就跑!"); Console.WriteLine("写入成功!"); }