当前位置: 首页 > news >正文

(22)C#传智:复习,多态虚方法抽象类接口,静态类,String与StringBuilder,集合泛型List与Dictionary,文件类,结构与类的区别

    
    深入静态构造函数


一、继续面向对象的复习


    1、面向对象
        一种分析问题的方法(增强了程序的可扩展性)
    
        面向对象的三大特性:封装,继承,多态.
        
        封装:类与对象本身就是封装的体现。
        
            1)属性封装了字段;
            2)方法的多个参数封装成一个对象;
            
            3)将一堆代码封装到了一个方法中;
            4)装饰一些功能封装到了几个类中;
            
            5)将一些具胡相同功能的代码封闭到了一个程序集中(exe,dll).
                将一坨程序封装起来到一个程序集当中。
        
        继承:类与类之间的关系。
            好处:代码重用
    
    2、LSP里氏替换原则
        (Liskov Substitution Principle LSP)面向对象设计的基本原则之一。
        任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类
        可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能
        够在基类的基础上增加新的行为。
        里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。
        而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的
        具体步骤的规范。
        
        
        
    3、类
        类的单根性、传递性、继承时函数的问题。
        
        所有类都直接或间接继承自object.
        
        继承中的访问修饰符问题.    
            1)private
            2)public
            3)protected
            4)internal
            5)protected internal
    
    
        不加修饰符时:
        
            1)类中默认是private,类本身不加修饰符默认是internal
            2)结构体默认是public
            
            3)抽象类,虚方法,必须显式public,否则子类无法重写
            4)接口禁止写修饰符,它默认只能是public,否则类中无法实现
        
        注意:以修改类的修饰符只能是public和internal
    
        子类的访问权限不能高于父类。会造成父类成员暴露。
        提示:vs2022中,尽管默认类的是interal,若不写时,vs2022在保存时会自动添加
            修改符internal。同时类中成员也会自动添加private

            class Father //默认是interal
            {
                void Show()//默认是private
                {
                }
            }

            public class Son : Father
            {//错误,public高于internal
            }


        上面代码错误,因为父类原意是限制在当前程序集interal内部使用,结果子类玩嗨
    了,用了public,意即在任何地方都可以用,由于继承原因无形中,就容易把父类成员暴
    露在当前程序集之外了。所以,子类的访问权限是不能高于父类的。 
    
        
        
        类是模子,确定对象将会拥有的特征(属性)和行为(方法)
        
        对象
        是一个你能看得到,摸得着的具体实体--万物皆对象
        
        类是模具,创建对象的模具,抽象的.
        1)类是一种数据类型,用户自定义的数据类型
        2)类组成:字段,属性,方法,构造函数等
        
        对象是具体的,是类的具体实例。对象具有属性(特征)和方法(行为).
        
        类中包含了数据(用字段表示)和行为(用方法,函数,功能表示,访求为一块具有名称
                代码)
    
        字段:存储数据
        属性:对字段起辅助作用。如清洗数据,读写权限限制,提供更好安全性与灵活性
        构造函数:初始化对象(new)
    
    
    
    4、特别的关键字
    
        base: 调用父类的成员(如构造函数),在子类中调用父类的重名方法
        this:1当前类的对象,2调用自己的构造函数
        new: 1创建对象,2特别指明子类重载,隐藏父类成员

            public class Precipitation
            {
                public void ShowAmount()
                {
                }
            }

            public class Test : Precipitation
            {
                public new void ShowAmount()
                {//有意隐藏子类时方法重载时,加new
                }
            }


        virtual 标记一个方法是虚方法。
        abstract 抽象类(方法)
                虚方法或抽象类可以是方法或属性。但不能是字段.因字段没有方法体.
        override 重写
        interface 接口
        partial   部分类
        sealed     密封类(禁止向下继承,但本身可以继承别人 )
        return    1返回值;2立即结束本次方法
        break      跳出当前循环 
        continue   结束本次循环,返回循环条件进行判断
        static   静态的
        struct   结构体
        enum   枚举
        const   常量


二、练习:外部设备(抽象方法 )


     模拟移动硬盘,U盘,MP3等移动存储设备,插到电脑上进行读写数据。
     实现目的,让电脑具有可扩展性,无论什么都可以插入进行读写。
     
     步骤一:统一一个父类抽象方法,下面是不同类型的子类如 mp3
     步骤二:子类统一赋值到父类,作为统一的入口,
     步骤三: 电脑的读写,根据统一的父类进行转换对应的子类进行读写(关键)
            两种方式:
            1)将父类传给两个函数;
            2)写一个属性来存储插到电脑上的移动存储设备。父类类型的属性进行读写;

        internal class Program
        {
            private static void Main(string[] args)
            {
                MobileDisk m = new MobileDisk();
                MP3 mp3 = new MP3();
                Udisk u = new Udisk();
                Cpu cpu = new Cpu();
                cpu.Read(m);//父类统一入口,不同子类进入
                cpu.Write(mp3);
                cpu.Read(u);
                Console.ReadKey();
            }
        }

        public class Cpu
        {
            //public Storage S { get; set; } 设置属性,在方法中使用
            public void Read(Storage s)
            {
                s.Read();
            }

            public void Write(Storage s)
            {
                s.Write();
            }
        }

        public abstract class Storage
        {
            public abstract void Read();

            public abstract void Write();
        }

        public class MobileDisk : Storage
        {
            public override void Read()
            {
                Console.WriteLine("移动硬盘读");
            }

            public override void Write()
            {
                Console.WriteLine("移动硬盘写");
            }
        }

        public class Udisk : Storage
        {
            public override void Read()
            {
                Console.WriteLine("优盘读");
            }

            public override void Write()
            {
                Console.WriteLine("优盘写");
            }
        }

        public class MP3 : Storage
        {
            public override void Read()
            {
                Console.WriteLine("MP3读");
            }

            public override void Write()
            {
                Console.WriteLine("MP3写");
            }

            public void PlayMusic()
            {
                Console.WriteLine("MP3播放音乐");
            }
        }


三、虚方法与抽象方法


    1、虚方法注意的地方
    
        1)父类中如果有方法需要让子类重写,则可以将该方法标记为Virtual.
        2)虚方法在父类中必须有实现,哪怕是空实现。
        3)虚方法子类可以重写(override),也可不重写.
    
    
        一般类的方法重写与虚方法的重写,区别:
        
        1)相同处:两者都是对父类方法的重写,都会隐藏父类的同名方法。
        2)不同处:
            a)父类对象由子类赋值而来时,虚方法调用的是子类的方法;
                而一般类这时这时调用的是父类本身的方法。传递不到子类去.
            b)虚方法必须是virtual-override配套使用。(override只能用在虚方法与抽象类中)
                一般类直接写同名方法,或同名方法前面加new(特别标明隐藏父类同名方法)

        internal class Program
        {
            private static void Main(string[] args)
            {
                Fruit f = new Fruit();
                f.Show();//水果
                Apple a = new Apple();
                a.Show();//苹果
                Orange o = new Orange();
                o.Show();//橙子
                Banana b = new Banana();
                b.Show();//香蕉

                f = new Apple();
                f.Show();//苹果,虚方法,调用到子类的重写
                f = new Orange();
                f.Show();//水果
                f = new Banana();
                f.Show();//水果
                Console.ReadKey();
            }
        }

        public class Fruit
        {
            public virtual void Show()
            {
                Console.WriteLine("水果");
            }

            //public void Show()
            //{
            //    Console.WriteLine("水果");
            //}
        }

        public class Apple : Fruit
        {
            public override void Show()
            {
                Console.WriteLine("苹果");
            }
        }

        public class Orange : Fruit
        {
            public void Show()//未标明new,会警告但不报错
            {
                Console.WriteLine("橙子");
            }
        }

        public class Banana : Fruit
        {
            public new void Show()
            {
                Console.WriteLine("香蕉");
            }
        }


    
    
    
    2、抽象方法注意的地方:
    
        1)需要用abstract关键字标记。
        2)抽象方法不能有任何方法实现。
        
        3)抽象成员必须包含在抽象类中。
        4)由于抽象成员没有任何实现,所以子类必须将抽象成员重写。
        
        5)抽象类不能实例化。
            抽象类的作用:抽象类的作用就是为了让子类继承,让子类重写。
        6)抽象类中可以包括抽象成员,也可以包括有具体代码的成员。
            比如抽象父类中可以用字段与属性,但它自己不能用,只能通过继承使用.
            因此,这时的字段或属性,只能是public或protected。如果是private的话,
            抽象类本身又不能实例化,也就无法访问这个字段或属性,同样它的子类
            也无法访问private,这样这个字段或属性就成了“死人”,任何人无法访问。
            
            此时,可以在抽象父类的public/protected属性或方法中去访问这个private
            的字段,它也可以“活”过来。从而间接在子类中访问使用.
            
        7)抽象方法不能用static修饰。
            static是属性类的,具体说就是抽象父类的,静态在实例化前就可以使用了。
            但是抽象方法有方法体么?静态后也无法使用。
            而且静态方法一般不用于重写,已经是静态了(类中直接唯一调用使用),
            重写有意义么?
            抽象类与虚方法就是继承后实例化子类对象中使用,静态后,无法实现重写。
            静态一般不与override,virtual联合使用。
            
            提示:
            C#静态方法属性类,类实例化前就可以使用了。
            因为静态方法在类实例化前,就已经在内存中分配了固定地址位置,方便类直接
            调用,所以它对于类来说静态成员的地址是固定的;而非静态成员只能在类实例
            化后,才能分配内存,这个内存地址动态的,所在在实例化前无法确定非静态成
            员的内存地址,也就无法使用它。
            
        8)抽象方法有构造函数,但这构造函数没什么作用,直接忽略。
            因为抽象类是不可以被直接构造的,不能实例化,所以它的构造函数没有意义。
            尽管C#允许为抽象类添加构造函数,不过一点都没用处。所以不推荐给抽象类
            写构造函数,因为这样做会破坏设计原则。

        internal class Program
        {
            private static void Main(string[] args)
            {
                Shape c = new Circle("red", 33);
                Console.WriteLine(c._color);
                Console.WriteLine(c.Area());
                Console.ReadKey();
            }
        }

        public abstract class Shape
        {
            public string _color;//publlic

            public Shape()//加上不会报错
            {
            }

            public Shape(string color)//这个加上也不会报错
            {
                this._color = color;//这里加this,也不会报错.
            }

            public abstract double Area();
        }

        public class Circle : Shape
        {
            private double _radius;

            public Circle(string color, double radius)
            {
                this._radius = radius;
                this._color = color;//这里写到抽象父类继承过来的_color
            }

            public override double Area()
            {
                return Math.PI * _radius * _radius;
            }
        }


    
    特别注意:(下面过程,可在三个构造中设置断点,观察流程走向)
    
        先将上面三个构造Shape(),shape(string)以及子类的Circle(string,double),还
        有主函数中的语句全部注释掉,着重看抽象类构造函数与其子类构造函数的关系。
        1)三个构造函数,全部注释掉。不会报错,因都有隐藏的无参构造函数。
        2)只激活Shape(string),报错。因为子类中没有构造函数来调用父类唯一的这个
            构造函数.
            
        3)只激活Shape()构造函数,不会报错。因为它就相当于隐式的无参构造函数。
        4)激活shape()与shape(string),不会报错。因为无参构造函数shape()不会造成
            子类隐式构造函数的错误。也就“掩盖”有参构造函数shape(string)的错误,
            因为它没有谁来调用,它是一个“死掉”的构造函数.
            
        5)只激活Circle(string,double)构造函数时,不会报错。因为调用它时,会自动
            调用抽象父类的隐式无参构造函数,不影响构造,所以无错误。
        6)激活Circle(string,double)与无参shape(),这个显式无参shape()起到了隐式
            无参函数的作用,与上面5)相同,也没错误。
            
        7)全部激活,同样因为显式无参shape()的作用,不会产生错误.
        8)只激活circle(string,double)与shape(string),会报错。
            因为circle构造时会调用父类构造,父类只有有参构造函数shape(string),
            这个有参构造shape(string)无法接收到string类型参数。同时,因为它有存
            在,父类隐式的无参函数又被覆盖,起不了作用。也就说,父类若要构造,只
            能是调用有参shape(string).但这条路堵死了,无法有string过来。所以
            报错。
            
            此时也可用base传递参数到父类有参构造,也不会报错,代码如下:

        public Circle(string color, double radius):base(color)
        {
            this._radius = radius;
            this._color = color;//这里写到抽象父类继承过来的_color
        }


            


四、接口


    1、什么是接口?
        接口是一种规范,一种能力。如鸟会飞,飞机能会,但鸟与飞机无从父子关系,也
        无法提炼出共同的父类。
        
        此时接口就应运而生,用它来体现不同类之间,共同拥有的规则或能力,比如鸟与
        飞机都会飞的能力。而且这种规则与能力是多种的,所以一个或多个类可以拥有多
        个接口(也即多个能力)
    
    
    2、什么时候应该使用接口???
        不同类间无从属关系,也无法提炼共同父类,但都有共同的规则或能力时。
    
    
    3、接口的目的是什么?
        多态。此时,接口就类似充当了虚拟类的作用,它可以由继承的类来赋值。

    internal class Program
    {
        private static void Main(string[] args)
        {
            Bird b = new Maque();
            b.Fly();//鸟,非虚方法或抽象类,无法实现子类中的方法
            Maque mq = new Maque();
            mq.Fly();//麻雀,调用自身方法fly
            IFlyable ifly = new Maque();
            ifly.Fly();//接口, 显式调用接口方法fly

            Console.ReadKey();
        }
    }

    public interface IFlyable
    {
        void Fly();
    }

    public class Bird
    {
        public void Fly()
        {
            Console.WriteLine("鸟在飞");
        }
    }

    public class QQ : Bird
    {
        public new void Fly()
        {
            Console.WriteLine("QQ不能飞");
        }
    }

    public class Maque : Bird, IFlyable
    {
        public new void Fly()//重写父类
        {
            Console.WriteLine("麻雀本身能飞");
        }

        void IFlyable.Fly()//显式实现,不能添加public
        {
            Console.WriteLine("接口鸟能飞");
        }
    }

    public class Plane : IFlyable
    {
        public void Fly()
        {
            Console.WriteLine("飞机能飞");
        }
    }


    
    
        
    4、细节
        1)接口中只能包含方法(属性,事件,索引器,都是方法),不能有字段。
        2)接口中的成员都不能有任何实现。
        
        3)接口不能被实例化。(静态与抽象类也不能实例化)
        4)接口中的成员不能有任何访问修饰符(默认public,同理见6)条)
        
        5)实现接口的子类必须将接口中的所有成员全都实现。(同抽象类)
        6)子类实现接口的方法时,不需要任何关键字,直接实现即可。
            public也不允许。而虚方法virtual-override,抽象类abstract-override
            
        7)接口存在的意义就是为多态
    
    5、显式与隐式实现接口
        只有一个成员与接口相同,且不特别指明,则该成员是隐式实现接口。
        需要特别指明与接口相同的成员,以 接口名.方法名()实现的,是显示声明。
        
        接口中:
        1)隐式声明的接口成员,用接口可以访问,也可以用类的对象访问。
        2)显式声明的接口成员,只能用接口访问,不能用类的对象访问。
        
    
    5、什么时候要添加引用和引入命名空间?
        在一个项目中要使用另一个项目中的类的时候,可以添加引用,方便类的重用.
        当前类中无法使用某些类时,应加入这些类的命名空间,以便当前类使用它们。
    
    6、小结
    
        接口存在的意义:多态
        
        多态的意义:程序可扩展性。最终->节省成本,提高效率
        
        接口解决了类的多继承问题,避免的体积庞大的问题.
        
        接口之间可以实现多继承。
        
        显式实现接口.
        
        多态的作用:把不同的子类对象都当作父类来看。这样可以屏蔽子类对象之间的
            的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。


五、静态类
    
    练习1:Console,Convert,Math使用时是否需要实例化?
            它们是静态方法,是属性类的。也就是说程序一运行,该类的静态方法在类
            还没有实例化前,就分配了固定的内存地址,已经存在,而且它在程序结束
            后才消亡。实例化之前,它们就可以直接根据内存地址使用了。所以实例化
            对它们没有任何意义。
            
    练习2:声明一个静态字段都能在哪里使用?
            静态类为什么不能实例化也不能继承?(抽象方法不能static)
        
        可以利用静态字段的特征(高于实例化),每实例化一次,静态字段就记数一次,这
        整个程序实例化了多少次,静态字段都可以记录,无论实例化对象已经消亡。
        所以静态字段方便做全局性的统计。
        
        基于练习1的回答,静态类已经在实例化前就分配了固定的内存地址,实例化则是
        再次动态分配内存地址。两者冲突。


六、面向对象练习



    技巧:vs2022C#
        1)类中输入字段后,按Ctrl+R,再Ctrl+E,会根据当前的字段自动生成属性。
        2)在类中空行处,按Alt+Enter,会有提示,选择自动生成构造函数,根据弹出
            的字段,选择后,自动生成构造函数。
        3)如果是虚函数或抽象类,在子类名后继承的父类输入完成后,按Alt+Shift+F10
            选择实现抽象类,抽象方法,等,将自动生成代码。
            一般的类,在子类名首行输入完成后,按Alt+Shift+F10,选择构造函数,将
            自动生成子类的构造函数。
    
    作业一:
        定义父亲类Father(姓lastName,财产property,血型bloodType),
        儿子Son类(玩游戏PlaygGame方法),女儿类Daughter类(跳舞Dance方法),
        调用父类构造函数(:base())给子类字段赋值

    internal class Program
    {
        private static void Main(string[] args)
        {
            Son s = new Son("张无忌", 8888m, "AB");
            s.SelfInfo();
            s.PlayGame();
            Daughter d = new Daughter("紫微", 6666m, "O");
            d.SelfInfo();
            d.Dance();
            Console.ReadKey();
        }
    }

    public class Father
    {
        private string _lastName;
        private decimal _property;
        private string _bloodType;
        public string LastName { get => _lastName; set => _lastName = value; }
        public decimal Property { get => _property; set => _property = value; }
        public string BloodType { get => _bloodType; set => _bloodType = value; }

        public Father(string lastName, decimal property, string bloodType)
        {
            this._lastName = lastName;
            this._property = property;
            this._bloodType = bloodType;
        }

        public void SelfInfo()
        {
            Console.WriteLine($"我叫{LastName},拥有{Property}元,是{BloodType}型血");
        }
    }

    public class Son : Father
    {
        public Son(string lastName, decimal property, string bloodType) : base(lastName, property, bloodType)
        {
        }

        public void PlayGame()
        {
            Console.WriteLine("儿子玩游戏");
        }
    }

    public class Daughter : Father
    {
        public Daughter(string lastName, decimal property, string bloodType) : base(lastName, property, bloodType)
        {
        }

        public void Dance()
        {
            Console.WriteLine("女儿会跳舞");
        }
    }


    
    
    作业二:
        定义汽车类Vehicle,属性(brand品牌,color颜色),方法:run。
        子类卡车(Truck),属性weight载重,方法拉货。
        轿车(Car),属性passenger载客数量,方法载客.

    internal class Program
    {
        private static void Main(string[] args)
        {
            Vehicle v = new Vehicle("五菱杀手", "white");
            v.Run();
            Druck d = new Druck("五菱杀手", "white", 2.5);
            d.LaHuo();
            Car c = new Car("哈佛5", "black", 4);
            c.CarryPassenger();
            Console.ReadKey();
        }
    }

    public class Vehicle //交通工具
    {
        private string _band;
        private string _color;

        public string Band { get => _band; set => _band = value; }
        public string Color { get => _color; set => _color = value; }

        public Vehicle(string band, string color)
        {
            this._band = band;
            this._color = color;
        }

        public void Run()
        {
            Console.WriteLine("能够行驶");
        }
    }

    public class Druck : Vehicle
    {
        private double _weight;

        public double Weight { get => _weight; set => _weight = value; }

        public Druck(string brand, string color, double weight) : base(brand, color)
        {
            this._weight = weight;
        }

        public void LaHuo()
        {
            Console.WriteLine($"当前卡车拉货{_weight}吨");
        }
    }

    public class Car : Vehicle
    {
        private int _passenger;

        public int Passenger { get => _passenger; set => _passenger = value; }

        public Car(string brand, string color, int passenger) : base(brand, color)
        {
            this._passenger = passenger;
        }

        public void CarryPassenger()
        {
            Console.WriteLine($"当前载客{_passenger}");
        }
    }
        


    
    
    
    作业三:
        员工类,部门经理类(部门经理也是员工,所以要继承自员工类。员工有上班打卡
        的方法,程序员来自员工不用打卡。用类来模拟。多态
  

 internal class Program
    {
        private static void Main(string[] args)
        {
            Employee e1 = new Employee();
            Employee e2 = new Manager();
            Employee e3 = new Programmer();
            e1.ClockIn();
            e2.ClockIn();
            e3.ClockIn();
            Console.ReadKey();
        }
    }

    public class Employee
    {
        public virtual void ClockIn()
        {
            Console.WriteLine("员工九点打卡");
        }
    }

    public class Manager : Employee
    {
        public override void ClockIn()
        {
            Console.WriteLine("经理11点打卡");
        }
    }

    public class Programmer : Employee
    {
        public override void ClockIn()
        {
            Console.WriteLine("程序员不用打卡"); ;
        }
    }


        
        
        
        
        
    作业四:
        动物Animal都有吃Eat和叫Bark的方法,狗Dog和猫Cat叫的方法不一样。
        父类中没有默认的实现,所以考虑用抽象方法

    internal class Program
    {
        private static void Main(string[] args)
        {
            Animal a = new Dog();
            a.Eat();
            a.Bark();
            Animal a1 = new Cat();
            a1.Eat();
            a1.Bark();
            Console.ReadKey();
        }
    }

    public abstract class Animal
    {
        public abstract void Eat();

        public abstract void Bark();
    }

    public class Dog : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("旺旺旺");
        }

        public override void Eat()
        {
            Console.WriteLine("狗狗快吃"); ;
        }
    }

    public class Cat : Animal
    {
        public override void Bark()
        {
            Console.WriteLine("喵喵喵");
        }

        public override void Eat()
        {
            Console.WriteLine("猫咪慢吃"); ;
        }
    }
        


        
        
    技巧:
        在上一行,按Shift+Enter,会新开一空行,如果上句是方法,会自动开{},鼠标
             自动移到下面空行中.
             
             
    作业五:
        计算形状Shape(圆Cirle,矩形Square,正方形Rectangle)的面积,周长.

    internal class Program
    {
        private static void Main(string[] args)
        {
            Shape s = new Circle(5);
            Console.WriteLine(Math.Round(s.GetArea(), 2) + "--" + s.GetPremeter());
            Shape s1 = new Square(3);
            Console.WriteLine(s1.GetArea() + "--" + s1.GetPremeter());
            Shape s2 = new Rectangle(3, 4);
            Console.WriteLine(s2.GetArea() + "--" + s2.GetPremeter());
            Console.ReadKey();
        }
    }

    public abstract class Shape
    {
        public abstract double GetArea();

        public abstract double GetPremeter();
    }

    public class Circle : Shape
    {
        private double _r;

        public Circle(double r)
        {
            _r = r;
        }

        public override double GetArea()
        {
            return Math.PI * _r * _r;
        }

        public override double GetPremeter()
        {
            return 2 * Math.PI * _r;
        }
    }

    public class Square : Shape
    {
        private double _side;

        public Square(double side)
        {
            _side = side;
        }

        public override double GetArea()
        {
            return _side * _side;
        }

        public override double GetPremeter()
        {
            return 4 * _side;
        }
    }

    public class Rectangle : Shape
    {
        private double _sideA;
        private double _sideB;

        public Rectangle(double sideA, double sideB)
        {
            _sideA = sideA;
            _sideB = sideB;
        }

        public override double GetArea()
        {
            return _sideA * _sideB;
        }

        public override double GetPremeter()
        {
            return 2 * (_sideB + _sideA);
        }
    }
        


        
        
    作业六:鸟-麻雀sparrow,鸵鸟ostrich,企鹅penguuin,鹦鹉parrot.
        鸟能飞,鸵鸟、企鹅不能飞...你怎么构造面向对象。
    注意:隐式接口,接口对象可以调用,类的对象也可以调用。
          显式接口,只能是接口对象可以调用,类的对象不能调用。

    internal class Program
    {
        private static void Main(string[] args)
        {
            IFlyable ifly = new Parrot();
            ifly.Fly();//接口只有一个成员,所以只能访问fly
            Parrot p = new Parrot();
            p.HaveWing();//来自父类bird
            p.SayHello();//来自类本身
            p.Fly();//来自类本身    隐式接口成员p.Fly既可以由接口访问,也可以由类对象访问。
            Sparrow s = new Sparrow();
            s.HaveWing();//来自父类bird
            //s.Fly();//错误,显式Fly来自接口,类对象不能直接访问,只能通过接口访问
            Console.ReadKey();
        }
    }

    public interface IFlyable
    {
        void Fly();
    }

    public class Bird
    {
        public void HaveWing()
        {
            Console.WriteLine("鸟类有两翅膀");
        }
    }

    public class Sparrow : Bird, IFlyable
    {
        void IFlyable.Fly()//显式
        {
            Console.WriteLine("接口麻雀能飞");
        }
    }

    public class Parrot : Bird, IFlyable
    {
        public void SayHello()
        {
            Console.WriteLine("鹦鹉能说话");
        }

        public void Fly()//隐式
        {
            Console.WriteLine("类中鹦鹉能飞");
        }
    }

    public class Penguuin : Bird
    {
    }

    public class Ostrich : Bird
    {
    }
          


    
        
    作业七:
        橡皮rubber鸭子,木wood鸭子,真实的鸭子realduck. 
        三个鸭子都会游泳,而橡皮鸭子和真实鸭子都会叫,只是叫声不一样,橡皮鸭子
        “唧唧”叫,真实的鸭子“嘎嘎”叫,木鸭子不会叫。

     internal class Program    
    {
        private static void Main(string[] args)
        {
            ReadDuck r = new ReadDuck();
            WoodDuck w = new WoodDuck();
            RubberDuck ru = new RubberDuck();

            IBarkable ib = r;
            Duck d = w;
            ib.Bark();
            d.Swim();
            Console.ReadKey();
        }
    }

    public interface IBarkable
    {
        void Bark();
    }

    public class Duck
    {
        public virtual void Swim()
        {
            Console.WriteLine("鸭子能游泳");
        }
    }

    public class ReadDuck : Duck, IBarkable
    {
        public void Bark()
        {
            Console.WriteLine("嘎嘎嘎"); ;
        }

        public override void Swim()
        {
            Console.WriteLine("真实鸭子能游泳");
        }
    }

    public class RubberDuck : Duck, IBarkable
    {
        public void Bark()
        {
            Console.WriteLine("唧唧唧"); ;
        }

        public override void Swim()
        {
            Console.WriteLine("橡皮鸭子能游泳");
        }
    }

    public class WoodDuck : Duck
    {
        public override void Swim()
        {
            Console.WriteLine("木头鸭子能游泳");
        }
    }
    


七、String字符串


    学习.Net就是学习它的无数个类库怎么用。
    
    string,字符串可以看成字符的只读数组.
    
    不可变特性(通过for循环,修改String中的元素,失败)
    
    GC 垃圾回收器
        程序结束时或一定时期内,GC会扫描内存,清理资源。但有些资源GC是无法处理的
        比如,流Filestrea等
    
    
    属性:
        Length: 长度
        
    方法:
        IsNullOrEmpty()  静态方法 ,判断是否为null或""
        ToCharArray()  string转换为char[]
        ToLower()  小写.必须接收返回值(因为字符串的不可变)
        ToUpper()  大写
        Equals()   比较两字串是否相同。忽略大小写的比较StringComparation.区别==
        Contains()  是否包含
        IndexOf()   如何没找到,返回-1
        LastIndex()   从后向前找
        Substring()    2个重载,截取字串
        Split()   分割字串
        Join()    静态方法
        Replace()  
        Trim()
        
        
    练习一:接收用户输入的字符串,将字符串反向输出。例:abc->cba

    Console.WriteLine("请输入任意字符串:");
    string s = Console.ReadLine();
    char[] c = s.ToCharArray();
    //Array.Reverse(c);

    for (int i = 0; i < c.Length / 2; i++)
    {
        char temp = c[i];
        c[i] = c[c.Length - 1 - i];
        c[c.Length - 1 - i] = temp;
    }

    s = new string(c);
    Console.WriteLine(s);
    


    
    练习二:接收用户输入的一句英文,将其中的单词反向输入。例如I love you->you love I

    Console.WriteLine("输入一句英文:");
    string s = Console.ReadLine();
    string[] s1 = s.Split(new char[] { ' ', '.', ',' }, StringSplitOptions.RemoveEmptyEntries);
    //Array.Reverse(s1);

    for (int i = 0; i < s1.Length / 2; i++)
    {
        string temp = s1[i];
        s1[i] = s1[s1.Length - 1 - i];
        s1[s1.Length - 1 - i] = temp;
    }

    s = string.Join(" ", s1);
    Console.WriteLine(s);
    


    
    练习三: “2012年12月21日”,从日期字符串中把年月日分别取出来,打印到控制台。

    string s = "2012年12月21日";
    string[] s1 = s.Split(new char[] { '年', '月', '日' }, StringSplitOptions.RemoveEmptyEntries);
    Console.WriteLine(string.Join("-", s1));


    
    
    练习五:123-456---789-----123-2,把类似的字符串中重复的符号去掉。
            即得到123-456-789-123-2

    string s = "123-456---789-----123-2";
    string[] s1 = s.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
    s = string.Join("-", s1);
    Console.WriteLine(s);


        
        
    练习六:从文件路径中提取出文件名(包含后缀),比如从c:\a\b.txt中提取以b.txt这个
            文件名。(引申:以后还可用正则表达式进行提取)

    string p = @"E:\a\b\1.txt";
    Console.WriteLine(Path.GetFileName(p));


    
    
    练习七:求员工工资文件中,员工的最高工资,最低工资,平均工资。
            张三|1000
            李四|900
            王五|1100
            赵六|600

    string[] s = File.ReadAllLines(@"E:\1.txt");
    int max = int.MinValue, min = int.MaxValue, sum = 0, count = 0;
    for (int i = 0; i < s.Length; i++)
    {
        if (!string.IsNullOrEmpty(s[i]))
        {
            string temp = s[i].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries)[1];
            int temp1 = Convert.ToInt32(temp);
            if (max < temp1)
            {
                max = temp1;
            }
            if (min > temp1)
            {
                min = temp1;
            }
            sum += temp1;
            count++;
        }
    }
    Console.WriteLine("{0},{1},{2}", max, min, sum / count);


        


八、StringBuilder高效字串操作


    当大量进行字串操作的时候,例如:很多次的字串的拼接操作。
    若用string,但string对象是不可变的。每次使用system.string类中的一个方法时,都
    要在内在中创建一个新的字串对象,这就需要为该新对象分配新的空间。在需要对字串
    执行重复修改的情况下,与创建新的string对象相关的系统开销可能会非常大。
    
    如果要修改字串而不创建新的对象,则可以使用System.Text.StringBuilder类。
    例如,当在一个循环中许多字符串连接在一起时,使用StringBuilder可以提升性能。
    
    StringBuilder!=string
    将StringBuilder转换为string时,用ToSring()
    
    StringBuilder仅仅是拼接字符串的工具,大多数情况下,还需要把StringBuilder转换
    到string。
    
    样例:
    StringBuilder sb=new StringBuilder();
    sb.Append();//追加
    sb.ToString();//转为字串
    sb.Insert();//插入   索引的前面
    sb.Replace();//替换
    
    案例:使用程序拼接html中的table并显示。
        在winform中添加webbrowser,用StringBuilder拼成WebBrowser.documentText
        这样就创建了一个网页。
        
    在winform中添加一个webbrowser,再添加一个button,在button中添加代码。

	private void button1_Click(object sender, EventArgs e)
	{
		StringBuilder sb = new StringBuilder();
		sb.Append("<html>");
		sb.Append("<head>");
		sb.Append("</head>");

		sb.Append("<body>");
		sb.Append("<p>下面是一个表格</p>");
		sb.Append("<table border='1px'>");

		sb.Append("<tr>");
		sb.Append("<td>星期一</td>");
		sb.Append("<td>星期二</td>");
		sb.Append("<td>星期三</td>");
		sb.Append("</tr>");

		sb.Append("<tr>");
		sb.Append("<td>星期一</td>");
		sb.Append("<td>星期二</td>");
		sb.Append("<td>星期三</td>");
		sb.Append("</tr>");

		sb.Append("<tr>");
		sb.Append("<td>星期一</td>");
		sb.Append("<td>星期二</td>");
		sb.Append("<td>星期三</td>");
		sb.Append("</tr>");

		sb.Append("</table>");
		sb.Append("</body>");
		sb.Append("</html>");

		webBrowser1.DocumentText = sb.ToString();
	}
	

    注意:还可以添加css样式,在head部分添加
    


九、集合类


    集合常用操作:添加,删除,遍历
    命名空间:System.Collections
    
    1、ArrayList可变长度数组,使用类似于数组
        属性:
        Capacity  容量,可容纳元素的个数,翻倍增长。
        Count     集合中实际元素的个数
        
        方法:
        Add(),AddRange(ICollection c),Remove(),RemoveAt(),Clear()
        Contain(),ToArray(),Sort(),Reverse(),
    
    2、HashTable 键值对集合,类似于字典,HashTable在查找元素的时候,速度很快。
        Add(object key,object value)
        hash["key"]
        hash["key"]=修改
        ContainsKey("key")
        Remove("key")
        
        遍历
        hash.keys
        hash.Values/DictionaryEntry
        
    
    3、装箱与拆箱
    
        装箱:将值类型转换成引用类型;(栈中)
        拆箱:将引用类型转换成值类型。(堆中)
        
        值类型:bool,int,double,float,char,struct,enum decimal,byte,
        引用类型:interface,object,string,数组,集合,自定义类
    
        发生拆装箱的两种类型必须具有继承关系。
    
    案例:两个ArrayList集合{"a","b","c","d","e"}和{"d","e","f","g","h"},把这两个
            集合去除重复项合并成一个.

        private static void Main(string[] args)
        {
            ArrayList al1 = new ArrayList() { "a", "b", "c", "d", "d" };
            ArrayList al2 = new ArrayList() { "d", "e", "f", "g", "h" };
            foreach (string value in al2)
            {
                if (!al1.Contains(value))
                {
                    al1.Add(value);
                }
            }
            foreach (string value in al1)
            {
                Console.Write(value + ",");
            }
            Console.ReadKey();
        }


            
    
    案例:随机生成10个1-10之间的数放到ArrayList中,要求这10个数不能重复,并且都
            是偶数

        private static void Main(string[] args)
        {
            Random r = new Random();
            ArrayList al = new ArrayList();

            for (int i = 0; i < 10; i++)
            {
                while (true)
                {
                    int n = r.Next(1, 11);
                    if (al.Contains(n))
                    {
                        continue;
                    }
                    else
                    {
                        al.Add(n);
                        break;
                    }
                }
            }
            foreach (var item in al)
            {
                Console.Write(item.ToString() + " ");
            }
            Console.ReadKey();
        }


    
    
    练习:有一个字符串是用空格分隔的一系列整数,写一个程序把其中的整数做如下重新
        排列打印出来:奇数在左侧,偶数在右侧。
        例如:2 7 8 3 22 9 5 11-->7 3 9 11 2 8 22
        private static void Main(string[] args)
        {
            string s = "2 7 8 3 22 9 5 11";
            string[] s1 = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            ArrayList al = new ArrayList();
            for (int i = 0; i < s1.Length; i++)
            {
                int n = Convert.ToInt32(s1[i]);
                if (n % 2 == 0)
                {
                    al.Add(n);//偶数在后
                }
                else
                {
                    al.Insert(0, n);//奇数在前
                }
            }
            Console.WriteLine(string.Join(" ", al.ToArray()));
            Console.ReadKey();
        }
        
        
    
    注意:不要把Random的实例化放入循环体内,会降低执行效率。因为某次中new的时候
            种子是一样的,取的是当前时间。在循环体内,循环一次new一次,会浪费资源
            去取种子形成迭代算法。
            在取随机数时,只需要new一次就可以了。(除非有特殊要求)
    
    
    
    
    4、List<T>泛型
        
        类似书橱,将不同类型进行归类整理。
        System.Collections.Generic
        
        List<T>类似于ArrayList的升级版,方法有:
            Sort()
            Max()
            MIn()
            Sum()
        Dictionary<k,v>类似于Hashtable升级版
        
        T,K,V就像一把锁,锁住集合只能存储某种特定的类型。
        
        泛型集合可以进行foreach遍历.
        因为它实现了IEnumerable<T>具有GetEnumerator()方法
        list.Add()
        list.AddRange()
        list.Insert()
        list.InsertRange()
        list.Remove()
        list.RemoveAt()
        list.RemoveRange()
        list.RemoveAll()
        
        list.Contain()
    
    5、Dictionary字典
        
        Dictionary<int,string> dic = new Dictionary<int,string>();
        dic.Add()
        dic.ContainsKey()
        dic[key]=value
        KeyValuePair 
        
        
    6、泛型集合练习:
    
    初始化器:
        1)集合:List<int> ints = new List<int>() { 1, 2, 3, 4};
        2)对象: Person p=new Person(){Name="张三",Age=11,Gender='男'};
        3)数组:int[] ints = { 1, 2, 3, 4 };
                int[] ints = new int[]{ 1, 2, 3, 4};

                
        internal class Program
        {
            static void Main(string[] args)
            {
                Person p=new Person();
                Person p1 = new Person("张三");//错误,无对应构造函数
                Person p2 = new Person() { Name="张三" };//正确
                Person p4 = new Person { Name = "张三" };//正确,调用无参构造
            }
        }
        internal class Person
        {
            public string Name { get; set; }
        }


    
        对于上面例子,创建一个构造函数看看:

        internal class Program
        {
            private static void Main(string[] args)
            {
                Person p1 = new Person("张三");//正确,无对应构造函数
                Person p3 = new Person("李四") { Name = "张三" };//正确
                Person p4 = new Person { Name = "张三" };//错误,没有无参构造函数
                Console.WriteLine("{0},{1}", p1.Name, p3.Name);//张三,张三
                Console.ReadKey();
            }
        }

        internal class Person
        {
            public Person(string name)
            {
                Name = name;
            }

            public string Name { get; set; }
        }


        注意:上面初始化时,初始化器起作用。
            若要子类使用构造函数,还是用原来的构造方法即括号()赋值.
            {}用于快速初始化
    
    
    
    案例:把分拣奇偶的程序用泛型实现.

        private static void Main(string[] args)
        {
            string s = "2 7 8 3 22 9 5 11";
            List<int> ints = new List<int>();
            List<int> ints1 = new List<int>();
            List<int> ints2 = new List<int>();
            string[] s1 = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < s1.Length; i++)
            {
                int temp = Convert.ToInt32(s1[i]);
                if (temp % 2 == 0)
                {
                    ints1.Add(temp);//偶
                }
                else
                {
                    ints2.Add(temp);
                }
            }
            ints.AddRange(ints2);
            ints.AddRange(ints1);
            foreach (int i in ints)
            {
                Console.Write(i + " ");
            }
            Console.ReadKey();
        }


    
    练习1:将int数组中的奇数放到一个新int数组中返回.

        private static void Main(string[] args)
        {
            //练习1:将int数组中的奇数放到一个新int数组中返回.
            int[] ints = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            List<int> list = new List<int>();
            for (int i = 0; i < ints.Length; i++)
            {
                if (ints[i] % 2 != 0)
                {
                    list.Add(ints[i]);
                }
            }
            int[] ints1 = list.ToArray();
            Console.WriteLine(string.Join(",", ints1));
            Console.ReadKey();
        }


    
    练习2:从一个整数的List<int>中取出最大数(找出最大值).别用max方法

        private static void Main(string[] args)
        {
            //练习2:从一个整数的List<int>中取出最大数(找出最大值).别用max方法
            List<int> ints = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
            //Console.WriteLine(ints.Max());
            int max = 0;
            for (int i = 0; i < ints.Count; i++)
            {
                if (ints[i] > ints[max])
                {
                    max = i;
                }
            }
            Console.WriteLine(ints[max]);
            Console.ReadKey();
        }


    
    练习3:把123转换为:壹贰参。

        private static void Main(string[] args)
        {
            //练习3:把123转换为:壹贰参。
            Dictionary<int, string> map = new Dictionary<int, string>();
            //Dictionary<int, string> map1 = new Dictionary<int, string>() { { 1, "壹" },{ 2, "贰" },{ 3, "参" } };
            map[1] = "壹";
            map[2] = "贰";
            map.Add(3, "参");
            map[4] = "肆"; map[5] = "伍"; map[6] = "陆"; map[7] = "柒";
            map[8] = "捌"; map[9] = "玖"; map[0] = "零";

            foreach (KeyValuePair<int, string> pair in map)
            {
                Console.Write($"{pair.Key}:{pair.Value},");
            }
            Console.WriteLine();
            foreach (int key in map.Keys)
            {
                Console.Write($"{key},");
            }
            Console.WriteLine();
            foreach (string value in map.Values)
            {
                Console.Write($"{value},");
            }
            Console.WriteLine();

            int n = 234;
            int b = n / 100;
            int s = n / 10 % 10;
            int g = n % 10;
            Console.WriteLine($"{map[b]}佰{map[s]}拾{map[g]}元");
            Console.ReadKey();
        }


    
    练习4:计算字符串中每种字符出现的次数(面试题)。

        "Welcome to Chinaworld",不区分大小写,打印"W 2","e 2"...
        提示:Dictionary<char,int>,char有很多静态方法.char.Isletter()
        private static void Main(string[] args)
        {
            //练习4:计算字符串中每种字符出现的次数(面试题)。
            //"Welcome to Chinaworld",不区分大小写,打印"W 2","e 2"...
            string s = "Welcome to Chinaworld";
            Dictionary<char, int> dic = new Dictionary<char, int>();
            for (int i = 0; i < s.Length; i++)
            {
                char c = char.ToLower(s[i]);
                if (c == ' ') continue;
                if (dic.ContainsKey(c))
                {
                    dic[c] += 1;
                }
                else
                {
                    dic.Add(c, 1);
                }
            }
            foreach (KeyValuePair<char, int> kvp in dic)
            {
                Console.WriteLine($"{kvp.Key} {kvp.Value},");
            }
            Console.ReadKey();
        }


        
        
    案例:简繁体转换.Dictionary.
    


十、文件操作常用相关类


    File  静态类。操作文件,对文件整体操作:拷贝,删除,剪切等
    
    Directory  静态类。操作目录(文件夹)
    
    Path   对文件或目录的路径进行操作。实际上操作的是--字符串
    
    Stream 抽象类。文件流
        FileStream  文件流。MemoryStream内存流,NetworkStream网络流。
        SteamReader 快速读取文本文件
        StreamWriter 快速写入文本文件
        
    
    1、静态与非静态的区别
            静态                                实例(非静态)
        static关键字                            无static
        使用类名调用                            使用实例调用
        静态方法中可访问静态成员                实例中可直接访问静态成员
        静态方法中不可直接访问实例成员            实例中可直接访问实例成员
        调用前初始化                            实例化对象时初始化
    
    1)静态类有构造函数,且该构造函数只能是静态的。
    2)静态构造函数前不能再有修饰符,例如public
    2)静态类的构造函数在调用前就执行了,
    3)静态构造函数在程序中只运行一次。

        internal class Program
        {
            private static void Main(string[] args)
            {
                Student.Test();
                Student.Test();
                Student.Test();
                Console.ReadKey();
            }
        }

        public static class Student
        {
            static Student()//静态构造函数前不能有修饰符
            {
                Console.WriteLine("静态类的静态构造函数");
            }

            public static void Test()
            {
                Console.WriteLine("静态类中的静态方法");
            }
        }


        
        结果:
            静态类的静态构造函数
            静态类中的静态方法
            静态类中的静态方法
            静态类中的静态方法    
    

    internal class Program
    {
        private static void Main(string[] args)
        {
            Teacher t = new Teacher();//一般类的静态构造函数,调用前执行
            t.Hello();  //一般类中的一般方法
            Teacher.Test(); //一般类中的静态方法
            Teacher t2 = new Teacher(); //无内容,因为静态构造函数只执行一次.
            Console.ReadKey();
        }
    }

    public class Teacher
    {
        static Teacher()//静态构造函数前不能有修饰符
        {
            Console.WriteLine("一般类的静态构造函数");
        }

        public static void Test()
        {
            Console.WriteLine("一般类中的静态方法");
        }

        public void Hello()
        {
            Console.WriteLine("一般类中的一般方法");
        }
    }


    
    结果:
        一般类的静态构造函数
        一般类中的一般构造函数
        一般类中的一般方法
        一般类中的静态方法
        一般类中的一般构造函数    
    说明:程序执行时,在初始化实例前,静态的构造函数优先,先执行静态构造函数。
            在实例化时执行一般构造函数。
            后面按顺序执行,因为静态构造函数只执行一次,所以后面的不再出现静态构造.
    
    
    2、结构与类的区别
    
        1)类型上:结构是值类型,类是引用类型;
        2)声明语法上:struct,class
        3)结构也可以声明字段,属性,构造函数,但是:
            结构的构造函数第一次赋值只能是字段,不能用属性。且有参构造必须写全字段。
            不能选择性字段给构造函数,写全情况下重载也不行。
            
        4)对于类new三件事:堆上开空间,存储对象,调用构造函数。
            结构的new只做了一件事:调用结构的构造函数。(是在栈上开空间)
    
        5)结构与类都有构造函数。
            都有无参构造函数。
            类中写了新构造函数后,原无参构造函数消失。
            结构写了新构造函数(必须全字段,且只能是字段),原无参构造函数不消失。
                也即结构最多只能有两个构造函数。
            注意:结构的无参构造函数只能是隐式的,不能显式。
            除非是静态的构造函数,它可以显示,但它只属于类,在实例化前就执行了。
    
    
        结构与类在什么情况下使用:
        1)如果仅单纯存储数据,推荐使用struct。因为它在栈上,节省空间。
        2)如果是OOP思想开发程序,推荐使用class。因为结构不具备面向对象特征.

    internal class Program
    {
        private static void Main(string[] args)
        {
            Dog d = new Dog();
            d.Test();
            Dog.Hello();
            Console.ReadKey();
        }
    }

    public struct Dog
    {
        private string _name;
        private int _age;

        public string Name { get => _name; set => _name = value; }
        public int Age { get => _age; set => _age = value; }

        static Dog()//属性类
        {
            Console.WriteLine("结构的静态构造函数。");
        }

        public Dog(string name, int age)
        {
            // Name = name;//错误。写在下句之后正确,相当于初始化再赋值
            _name = name;//正确
            _age = age;
            Console.WriteLine("结构的有参构造函数");
        }

        public void Test()
        {
            Console.WriteLine("结构方法");
        }

        public static void Hello()
        {
            Console.WriteLine("结构中的静态方法");
        }
    }


    
    说明:结构中有三个构造函数,但静态构造是属性类的,在实例化前就执行了,且在程序中
            只执行一次。
            例中结构中有无参隐式构造函数,它不允许显式写出来。
        
        结果:
            结构的静态构造函数。
            结构方法
            结构中的静态方法
            结构的有参构造函数    
    
    

相关文章:

  • JDK线程池中到底该设置多少线程数才比较合适
  • 数据库补充笔记2
  • 使用 pushd 和 popd 实现快速切换目录
  • JSONP 教程
  • Oracle数据库dump文件的导入与导出及创建表空间
  • 淘宝十年资深架构师吐血总结淘宝的数据库架构设计和采用的技术手段。
  • 南大通用数据库-Gbase-8a-学习-32-gccli客户端
  • Linux的scp、rsync、以及集群分发脚本、ssh配置免密登录
  • 【计算机视觉 | 目标检测】锚点预匹配(Anchor pre-matching)的理解
  • 智联物联分享之物联网协议MQTT简述,MQTT协议特点
  • Echarts立体柱状图
  • SpringBoot定时任务@Scheduled注解详解
  • Gen-LaneNet论文精读总结
  • Spring Cloud Alibaba全家桶——微服务网关Gateway组件
  • 基于微信PC端小程序抓包方法
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 2018一半小结一波
  • CentOS6 编译安装 redis-3.2.3
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • ES6系统学习----从Apollo Client看解构赋值
  • Java,console输出实时的转向GUI textbox
  • leetcode讲解--894. All Possible Full Binary Trees
  • October CMS - 快速入门 9 Images And Galleries
  • Redis 懒删除(lazy free)简史
  • springMvc学习笔记(2)
  • Spring核心 Bean的高级装配
  • Vue 2.3、2.4 知识点小结
  • Zsh 开发指南(第十四篇 文件读写)
  • 阿里云购买磁盘后挂载
  • 前端每日实战:61# 视频演示如何用纯 CSS 创作一只咖啡壶
  • 区块链共识机制优缺点对比都是什么
  • 算法---两个栈实现一个队列
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • gunicorn工作原理
  • Semaphore
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​决定德拉瓦州地区版图的关键历史事件
  • #LLM入门|Prompt#1.7_文本拓展_Expanding
  • #Z0458. 树的中心2
  • #预处理和函数的对比以及条件编译
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (剑指Offer)面试题34:丑数
  • (六)软件测试分工
  • (学习日记)2024.04.10:UCOSIII第三十八节:事件实验
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转)创业的注意事项
  • (转)甲方乙方——赵民谈找工作
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .net 使用ajax控件后如何调用前端脚本
  • .net(C#)中String.Format如何使用
  • .NET开发人员必知的八个网站
  • .NET上SQLite的连接
  • @Autowired和@Resource装配
  • @基于大模型的旅游路线推荐方案
  • @软考考生,这份软考高分攻略你须知道