黑马C#笔记04:继承,里氏转换,Protected,ArrayList,Hashtable(var,foreach),泛型集合,装箱拆箱,Dictionary,文件操作(编码),文件流,

 

亲情奉献全套精品.Net基础视频教程(1-9)2168:36 亲情奉献全套精品.Net基础视频教程(1-9)

-------------------------------------

https://www.bilibili.com/video/av8915750?p=115

继承



访问修饰符
public:所有皆可访问.
internal:在同一程序集的可访问
protected:保护的,仅在当前类或派生类可访问.
private:私有的,仅在当前类或结构可访问
类的访问修饰符可以为 public或internal. 默认为 internal
 
类成员的访问修饰符可以为 public, protected internal, protected, internal, private,  private protected. 
默认为private 


子类成员无法继承父类的private成员.它可以继承父类的public, internal, protected成员
问:子类有没有继承父类的构造函数?
答:子类并没有继承父类的构造函数.但是子类会默认的调用父类的无参数的构造函数(因为要)先创建父类对象,让子类可以使用父类中的成员.如果父类还有父类,依此类推先创建父类的父类对象...
所以,如果在父类中重写了1个有参数的构造函数之后,那么(系统默认生成的)无参数的就被干掉了,子类就调用不到了,所以子类会报错.
解决方法:1.在父类中的写一个无参数的构造函数.(不推荐)
解决方法:2.在子类中显示的调用父类的构造函数(改变默认调用),使用关键字:base()

例如下例-子类使用base关键字显式调用父类有参数构造函数,因此父类中无须添加无参数构造函数(附类图):

namespace App010_继承 {
    public class Program {
        static void Main(string[] args) {
            人类 某甲 = new 人类("张三", '男', 66);
            某甲.打招呼();

            学生 学生甲 = new 学生("张三丰", '男', 18, 10086);
            学生甲.打招呼();

            教师 教师甲 = new 教师("张果老", '男', 500, 10000);
            教师甲.打招呼();
            Console.ReadKey();
        }
    }
    public class 人类 {
        private string _姓名;
        private char _性别;
        private int _年龄;
        public string 姓名 { get => _姓名; set => _姓名 = value; }
        public char 性别 { get => _性别; set => _性别 = value; }
        public int 年龄 { get => _年龄; set => _年龄 = value; }
        //public 人类() {//必须有无参数构造函数,因为该类的子类new时会调用父类(即本类)的无参数构造函数
        //除非-->该类的子类使用关键字base显式的调用父类(即本类)有参数构造函数
        //}
        public 人类(string 姓名, char 性别, int 年龄) {
            this.姓名 = 姓名;
            this.性别 = 性别;
            this.年龄 = 年龄;
        }
        public void 打招呼() {
            Console.WriteLine("你好,我叫{0},性别{1},今年{2}岁,我属于[{3}]类.很高兴认识你!",
                姓名, 性别, 年龄, this.GetType());
        }
    }
    public class 学生 : 人类 {
        private int _学号;
        public int 学号 { get => _学号; set => _学号 = value; }
        //使用base显式调用父类有参数构造函数,则不需要父类的无参数构造函数
        public 学生(string 姓名, char 性别, int 年龄, int 学号) : base(姓名, 性别, 年龄) {
            this.学号 = 学号;
        }
        public new void 打招呼() {//使用new关键字隐藏基类同名方法
            Console.WriteLine("你好,我叫{0},性别{1},今年{2}岁,我的学号{3},我属于[{4}]类.很高兴认识你!",
                姓名, 性别, 年龄, 学号, this.GetType());
        }
    }
    public class 教师 : 人类 {
        private int _薪水;
        public int 薪水 { get => _薪水; set => _薪水 = value; }
        //使用base显式调用父类有参数构造函数,则不需要父类的无参数构造函数
        public 教师(string 姓名, char 性别, int 年龄, int 薪水) : base(姓名, 性别, 年龄) {
            this.薪水 = 薪水;
        }
        public new void 打招呼() {//使用new关键字隐藏基类同名方法
            Console.WriteLine("你好,我叫{0},性别{1},今年{2}岁,我的薪水每月{3}元.我属于[{4}]类.很高兴认识你!",
                姓名, 性别, 年龄, 薪水, this.GetType());
        }
    }
}

输出结果为:

你好,我叫张三,性别男,今年66岁,我属于[App010_继承.人类]类.很高兴认识你!
你好,我叫张三丰,性别男,今年18岁,我的学号10086,我属于[App010_继承.学生]类.很高兴认识你!
你好,我叫张果老,性别男,今年500岁,我的薪水每月10000元.我属于[App010_继承.教师]类.很高兴认识你!

new关键字

在子类中使用new关键字隐藏从父类继承的同名成员.

以上例为例,学生.打招呼()方法前加关键字new隐藏父类同名方法"人类.打招呼()":

        public void 打招呼() {
            Console.WriteLine("你好,我叫{0},性别{1},今年{2}岁,我属于[{3}]类.很高兴认识你!",
                姓名, 性别, 年龄, this.GetType());
        }

上面是父类-人类的同名方法,下面是子类-学生类的同名方法: 

        public new void 打招呼() {//使用new关键字隐藏基类同名方法
            Console.WriteLine("你好,我叫{0},性别{1},今年{2}岁,我的学号{3},我属于[{4}]类.很高兴认识你!",
                姓名, 性别, 年龄, 学号, this.GetType());
        }

里氏转换

看下例:

再简单举例如下:

类型转换-使用is,as关键字

转换类型前使用is判断:

使用as转换类型:(转换成功返回该对象,转换失败则返回null)

Person p=new Student();

Student s= p as Student;//转换成功则返回该对象,转换失败返回null

Protected-仅当前类和派生类允许访问

ArrayList集合-元素类型不限


使用ArrayList集合可不拘泥于元素类型,即元素什么类型都可以.

Add方法:添加单个元素

现在我们往ArrayList里添加各种类型的元素,既可以 添加简单类型,也可以添加对象和数组,如下:

上面的代码不符合我们心中的预期(打印输出list集合各元素的内容).
将for循环代码修改如下,判断当前list元素是对象还是简单类型,根据类型执行相对应的处理代码.
现在可以正确的显示list集合各元素的内容(值)了:

代码:

namespace App011_ArrayList {
    class Program {
        static void Main(string[] args) {
            ArrayList list = new ArrayList();
            //数组:类型单一,长度固定不变
            //ArrayList集合:类型随便 长度可以改变

            list.Add(1);
            list.Add(3.14);
            list.Add(true);
            list.Add("甲乙丙丁");
            list.Add('男');
            list.Add(5000m);
            list.Add(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
            list.Add(new Person());
            for (int i = 0; i < list.Count; i++) {
                if (list[i] is Person) {//如果元素是Person对象
                    ((Person)list[i]).SayHi();
                    Console.WriteLine(" <==类型 " + list[i].GetType());
                }else if(list[i] is int[]){ //如果元素是int数组
                    for (int j = 0; j < ((int[])list[i]).Length; j++) {
                        Console.WriteLine(((int[])list[i])[j]);
                    }
                }else{    
                    Console.WriteLine(list[i] + " <==类型 " + list[i].GetType());
                }
            }
            Console.ReadKey();
        }
    }
    class Person{
        public void SayHi() {
            Console.Write("我是Person类的对象,我和你SayHi!.");
        }
    }
}

AddRange()方法-添加集合

使用AddRange()方法可以添加集合

示例如下:

ArrayList其他常用方法

ArrayList集合长度

练习1:

练习2:

HashTable-键值对集合

简单使用Hashtable的例子,使用foreach循环遍历输出-注意只能使用foreach循环:

练习1-简体转换繁体

如下所示1个简体字数组,1个繁体字数组,数量相同,简繁字在两个数组中一一对应.
用户输入一句话,是汉字则转换为繁体字.如果繁体字数组中没有包含对应繁体字,则显示原字:


运行效果如下:

var关键字

从 Visual C# 3.0 开始,在方法范围中声明的变量可以具有隐式类型 var。 隐式类型的本地变量是强类型变量(就好像您已经声明该类型一样),但由编译器确定类型。 下面的两个 i 声明在功能上是等效的:

var i = 10; // implicitly typed
int i = 10; //explicitly typed

var的缺点-必须赋初值,不能直接声明而不赋值:

for和foreach循环对比

泛型集合

泛型集合List和ArrayList差不多,方法也大致一样.只不过List泛型集合定义时指定了类型,使用它的时候则只能添加该类型的元素.

我们在读取List集合元素的时候,不需要像ArrayList读取元素时候强行转换类型那么麻烦了.




下面是泛型集合的简单示例:

装箱和拆箱

装箱:将值类型转为引用类型

拆箱:将引用类型转为值类型

*两种类型是否发生装箱拆箱,要看它们是否存在继承关系.如果有继承关系,那么有可能发生拆装箱.
拆箱装箱简单示例:

Dictionary字典集合

Dictionary简单示例:

文件

Path类-常用方法

File类-注意不能操作大文件!





File类常见方法:

File.WriteAllBytes(String, Byte[]) 方法-创建一个新文件,在其中写入指定的字节数组,然后关闭该文件。 如果目标文件已存在,则覆盖该文件。

            //写入文件-二进制方式
            string str = "关关雎鸠 在河之洲";
            byte[] buffer = Encoding.UTF8.GetBytes(str);
            File.WriteAllBytes(@"C:\Users\heros\Desktop\1.txt", buffer);

File.ReadAllBytes(String) 方法-打开一个二进制文件,将文件的内容读入一个字节数组,然后关闭该文件.

            //读取文件-二进制方式
            byte[] buffer = File.ReadAllBytes(@"C:\Users\heros\Desktop\1.txt");
            string str = Encoding.GetEncoding("UTF-8").GetString(buffer);
            Console.WriteLine(str);


编码历史和乱码原因


再示例一下ReadAllBytes和WriteAllBytes方法:


在中文windows平台上,使用Default编码(默认应该是GB2312/GBK/GB18030)保存文件,读取的时候则只能中文操作系统(或显式的设置正确的编码)才能正确显示.

ReadAllLines方法-把文本文件读入字符串数组,每个元素是每行字符串:

ReadAllText方法-把整个文本文件读入1个字符串中:

WriteAllLines方法-把字符串数组覆盖写入1个文本文件:

WriteAllText方法-把字符串覆盖写入文本文件:

文件添加方法
AppendAllText方法-给文件添加字符串
AppendAllLines方法-给文件添加字符串数组

文件流

File类是一次性把文件读入内存,对大文件来说是个严重负荷.所以我们使用文件流.
文件流是一部分一部分操作文件的.

FileStream流:操作字节.

StreamReader和StreamWriter:操作字符(文本文件).

FileStream-操作字节

以下为FileStream读取数据简单示例:

以下是FileStream写入数据的简单示例:

以下是使用FileStream复制文件的简单示例:

namespace App016_文件流 {
    class Program {
        static void Main(string[] args) {
            #region FileStream读取数据简单示例
            FileStream-用来操作字节的
            创建FileStream对象
            //FileStream fsRead = new FileStream(@"C:\Users\heros\Desktop\1.txt",
            //    FileMode.OpenOrCreate,FileAccess.Read);

            byte[] buffer = new byte[fsRead.Length];//设置缓冲字节为文件实际长度
            //byte[] buffer = new byte[1024*1024*5];//设置缓冲字节为5MB
            Read方法()的3个参数:缓冲字节数组,起始读取索引(*注意*第1个字节的索引是0不是1),读取字节数(长度)
            返回值:返回实际读取的长度.例如本例一次读取5MB字节,如果读取文件只有1KB(不够5MB),则返回1KB.
            //int r = fsRead.Read(buffer, 0,buffer.Length);//读取缓冲字节数组从0号索引开始的所有字节

            //string str = Encoding.UTF8.GetString(buffer,0,r);//从头(0号索引起)解码r长度的字节数组
            //fsRead.Close();//关闭文件流
            //fsRead.Dispose();//释放内存资源

            //Console.WriteLine("本文件字节长度=" + r);
            //Console.WriteLine("本文件字符长度={0};内容=\n{1}",str.Length,str);
            #endregion

            #region FileStream写入数据简单示例
            //using ( FileStream fsWrite = new FileStream(@"C:\Users\heros\Desktop\2.txt",
            //    FileMode.OpenOrCreate,FileAccess.Write))  
            //{
            //    string str = "南山经之首曰鹊山。其首曰招瑶之山,临于西海之上。";
            //    byte[] buffer = Encoding.UTF8.GetBytes(str);
            //    fsWrite.Write(buffer,0,buffer.Length);
            //    //这种方式写入的字符串会从头覆盖原文件(因为文件指针默认从头开始).
            //}
            #endregion

            #region FileStream的文件Copy简单示例
            string source = @"C:\Users\heros\Desktop\1.flv";
            string target = @"C:\Users\heros\Desktop\new1.flv";

            CopyFile(source,target);
            Console.WriteLine("复制完成.");
            #endregion
            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) {
                        //r为返回值:返回本次读取的实际字节数
                        int r = fsRead.Read(buffer, 0, buffer.Length);
                        //如果r返回0,也就意味什么都没有读到,文件已经读完了
                        if (r==0) {
                            break;//跳出while循环
                        }
                        fsWrite.Write(buffer,0,r);//写入实际读取的长度
                        //(长度不能为buffer.length,因为最后1次读取长度不足buffer.length)
                    }
                }
            }
        }
    }
}

StreamReader和StreamWriter:操作字符(文本文件)

StreamReader和StreamWriter简单示例:


            #region StreamReader和StreamWriter简单示例
            //使用StreamReader读取文件
            using (StreamReader sr=new StreamReader(@"C:\Users\heros\Desktop\1.txt",
                Encoding.UTF8)) 
            {
                while (!sr.EndOfStream) {//使用while循环读取所有行
                    Console.WriteLine(sr.ReadLine());
                }
                //ReadToEnd方法从文件当前位置读取到结尾所有部分
                //Console.WriteLine(sr.ReadToEnd());
            }
            //使用StreamWriter写入文件,第二个参数bool设置是否追加到文件。true追加,false或省略则覆盖
            using (StreamWriter sw=new StreamWriter(@"C:\Users\heros\Desktop\2.txt",
                true)) 
            {
                sw.Write("StreamWriter写入文件测试~~");
            }
            #endregion

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值