类和对象
特征(属性和字段):
字段:
1)在类中字段也可以叫全局变量
2)字段是定义在类中方法之外的 局部变量是定义在方法内部的
3)字段的格式 访问修饰符 数据类型 字段名
4)字段是有默认值的 值类型默认值为0 引用类型默认值为null 局部变量没有默认值
5)在一个类中 不能重复定义字段
6)字段包含 静态字段和非静态字段
7)实例字段 随着new对象的创建而创建
8)静态字段 在类中加载的时候创建 且创建一次
字段的访问修饰符 学过的修饰符字段都能使用
readonly:
public readonly int _id1=99; //初始化非必须赋值 (只能在初始化和构造函数中赋值) 只读
public static readonly int _id3=199; //可以设置为静态字段
const:
//用const修饰的都是静态字段
public const int _id2=100; //初始化必须赋值 只读 const 常量 如果用于字段 则包含static(已经包含了static,不能再添加static)
new:
1)开辟内存空间为对应类的对象
2)初始化构造函数
2)属性和字段的关系
特点:
1.属性(Property) 是类(class)、结构(structure)和接口(interface)的命名成员
2.属性其实外界访问私有字段的入口 属性本质就是方法 一个属性可以分别有一个set 和get 方法组成。(或者称为访问器)
3.字段是变量 属性是方法
4.属性可以为私有字段进行扩展
5.属性就是对字段的封装,set给某字段写入数据,get读取某字段的数据
3)属性和字段的对比
【1】字段,又称为“成员变量” 第一、目的:字段是为类的内部方法,或者方法之间传递数据使用,强调对内使用。 第二、修饰:字段一般用private修饰,很少用public。 第三、读写:字段可以读写均可,也可以用readonly限制为只读,但是不能添加业务逻辑。
【2】属性 第一、目的:属性是对外提供数据访问、本身不保存数据,强调对外使用。 第二、修饰:属性都是public,从来不用private。 第三、读写:属性可以轻松的实现单独读写控制,并且可以添加任意需要的业务逻辑。
1)属性的基本格式:
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
2)增加业务判断逻辑
public string Code
{
get
{
return code;
}
set
{
code = value;
}
}
3)分别设置只读只写字段
//只读
private int _test =10;
public int Test
{
get {
return _test;
}
}
//只写
private int _test =10;
public int Test
{
set {
_test=value;
}
}
//语法糖
public int Test { get; set; } 可读可写
//属性直接赋值
public string StrNumber { get; set; } = "属性直接初始化";
//属性表达式
public string StrNumber2 { get => "属性只读表达式"; }
public string StrNumber3 => "属性表达式";
4)简化对象初始化:
//简化前
People people1 = new People();
people1.Age=12;
//简化后
People people1 = new People() { Age = 12 }
5)构造函数
分类:构造函数分为实例构造函数、私有构造函数和静态构造函数
1)实例构造函数
//1)它主要用于为对象分配储存空间,对数据成员进行初始化 //2)类默认自带构造函数 如果需要扩展内容 也可以重新定义构造函数 //3)实例化构造函数的名字必须和类名相同 //4)实例构造函数没有返回类型,它可以带参数,也可以不带参数 //5)构造方法的访问修饰符通常是public类型的,这样在其他类中都可以创建该类的对象 //6)构造方法实在创建类的对象时被调用的. //构造方法是在创建类的对象时被调用的。通常会将一些对类中成员初始化的操作放到构造方法中去完成 //7)构造函数可以重载,从而提供初始化类对象的不同方法()
2)私有构造函数
私有构造函数可用于阻止创建类的实例。如果类中的所有方法都是静态的,可考虑使整个类成为静态的。
##### 4)简化对象初始化:
```c#
//简化前
People people1 = new People();
people1.Age=12;
//简化后
People people1 = new People() { Age = 12 }
```
##### 5)构造函数
**分类**:构造函数分为实例构造函数、私有构造函数和静态构造函数
###### 1)实例构造函数
```c#
//1)它主要用于为对象分配储存空间,对数据成员进行初始化
//2)类默认自带构造函数 如果需要扩展内容 也可以重新定义构造函数
//3)实例化构造函数的名字必须和类名相同
//4)实例构造函数没有返回类型,它可以带参数,也可以不带参数
//5)构造方法的访问修饰符通常是public类型的,这样在其他类中都可以创建该类的对象
//6)构造方法实在创建类的对象时被调用的.
//构造方法是在创建类的对象时被调用的。通常会将一些对类中成员初始化的操作放到构造方法中去完成
//7)构造函数可以重载,从而提供初始化类对象的不同方法()
```
###### 2)私有构造函数
私有构造函数可用于阻止创建类的实例。如果类中的所有方法都是静态的,可考虑使整个类成为静态的。
```c#
main方法中的调用
//MyClass1类
//MyClass1 myClass13 = new MyClass1(); 私有构造函数不能直接在外部实例 这一步会报错
MyClass1 aa= MyClass1.MyClassMethod1(); //通过调用静态方法来调用在 MyClass1类中的方法 MyClassMethod1
Console.WriteLine(aa.Name);
public class MyClass1{
private static int _age;
private static string _name;
public string Name
{
get { return "嗡嗡嗡"; }
}
private MyClass1()
{
_age = 10;
_name = "张三";
}
public static MyClass1 MyClassMethod1()
{
MyClass1 p1 = new MyClass1();
return p1;
}
}
}
```
###### 3) 静态构造函数
```c#
/* 1、静态构造函数既没有访问修饰符,也没有参数。
2、创建第一个类实例或任何静态成员被引用时,自动调用静态构造函数来初始化类
3、一个类只能有一个静态构造函数,如果没定义 系统默认有 反之只能定义无参静态构造函数
不会主动调用静态构造函数 实例化无参构造函数、调用静态成员等都会自动调用静态构造函数
4、无参数的构造函数可以与静态构造函数共存。尽管参数列表相同,但一个属于类,一个属于实例,所以不会冲突
5、如果没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数
6、在类创建时 系统自动调用一次静态构造函数
*/
main方法中:
//静态构造函数
MyClass3 myClass3 = new MyClass3();//静态类构造函数不会主动调用,会随着实例化构造函数时一起被调用
MyClass3._age = 20;//也会随着访问静态成员时被调用
Console.ReadKey();
public class MyClass3
{
static MyClass3()
{
Console.WriteLine("MyClass3静态构造函数");
}
public MyClass3()
{
Console.WriteLine("MyClass3实例构造函数");
}
public MyClass3(int a)
{
}
}
```
##### 6)析构函数
###### 1)析构函数
垃圾回收程序最终销毁对象之前调用的方法,该方法称为析构函数
析构函数的声明类似构造函数,除了在析构函数开头要使用 ‘~’
析构函数,没有返回值,且不带任何参数。
析构函数的功能是用来释放一个对象的。在对象删除前,用它来做一些清理工作,比如一些和类型相关的数据,它与构造函数的功能正好相反。
###### 2)什么样的对象才会被回收
```c#
/*
1、GC只会回收 堆 的内存 ,而 值类型在 栈 中的使用完后马上就会给释放,并不需要 GC 进行处理
2、堆中的没有被引用或者null的对象才会被回收,会调用析构函数通知程序对象将要释放(不会立即回收,会在程序的某个时间段执行)
3、静态的成员 会在程序结束后销毁。
*/
```
###### 3) //强制开启回收 立即回收对象 调用析构函数
```c#
//GC.Collect();
```
##### 7)面向对象的特性
###### 1)封装
将类的某些信息隐藏在类内部(private),不允许外部程序直接访问,
而是通过该类提供的公共属性(public)来实现对隐藏信息的操作和访问
**好处:**
```c#
1、隐藏类的实现细节
2、只能通过规定属性访问数据
3、方便加入控制语句
4、方便修改实现
封装在c#中具体表现形式 :方法的封装 私有字段的封装(属性) 类的定义
```
###### 2)继承
```
1.一个类继承另一个类 其中被继承的类(父类 超类 基类) 继承的类(子类 派生类)
2.继承关系建立后 子类拥有父类的属性和方法 反之父类不继承子类属性和方法。
3. 继承的作用:解决类与类直接 属性或者方法重复的问题
4 .C#中类与类之间是单继承关系 利用接口实现多继承(后续讲)
5. 继承的格式 A类(子类) : B类(父类)
6. 创建子类对象时,系统默认先调用父类构造方法,然后再调用子类构造方法
7.当父类有有参构造方法时,如果子类有构造方法(无参或有参),
要求父类必须有一个无参构造方法。
8.转型:子类可以赋值给父类,但是父类不能赋值给子类
9.覆盖:在子类中写一个和基类一样名字(参数不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字
10.Object 是所有引用类型的基类
```
```C#
main方法中:
//父类类型的变量保存子类类型的对象可以直接使用
SuperClass1 superClass1 = new SubClass1();
superClass1._age = 10;
//子类的变量 保存父类的对象 在编译时 会报错
//SubClass1 subClass1 = (SubClass1) new SuperClass1();
//subClass1._age = 5;
public class SuperClass1 {//父类
public int _age;
}
public class SubClass1:SuperClass1 { }//子类
```
```c#
//当父类有有参构造方法时,如果子类有构造方法(无参或有参),
//要求父类必须有一个无参构造方法。
public class Person
{
public Person() {//例一:
Console.WriteLine("Person无参构造函数");
}
public Person(int a) {
Console.WriteLine("Person构造函数");
}
}
public class Woman:Person {
/* public Woman(int b) //此时会报错 必须要在父类中加一个无参的构造函数 如例一:
{
}*/
public Woman(int a):base(a)//因为子类构造函数会默认调用父类的无参构造函数 所以用base来改变成调用父类有参的构造函数
{
}
}
```
###### 8)静态字段用变量访问
3) 静态构造函数
/* 1、静态构造函数既没有访问修饰符,也没有参数。 2、创建第一个类实例或任何静态成员被引用时,自动调用静态构造函数来初始化类 3、一个类只能有一个静态构造函数,如果没定义 系统默认有 反之只能定义无参静态构造函数 不会主动调用静态构造函数 实例化无参构造函数、调用静态成员等都会自动调用静态构造函数 4、无参数的构造函数可以与静态构造函数共存。尽管参数列表相同,但一个属于类,一个属于实例,所以不会冲突 5、如果没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数 6、在类创建时 系统自动调用一次静态构造函数 */
main方法中:
//静态构造函数
MyClass3 myClass3 = new MyClass3();//静态类构造函数不会主动调用,会随着实例化构造函数时一起被调用
MyClass3._age = 20;//也会随着访问静态成员时被调用
Console.ReadKey();
public class MyClass3
{
static MyClass3()
{
Console.WriteLine("MyClass3静态构造函数");
}
public MyClass3()
{
Console.WriteLine("MyClass3实例构造函数");
}
public MyClass3(int a)
{
}
}
6)析构函数
1)析构函数
垃圾回收程序最终销毁对象之前调用的方法,该方法称为析构函数 析构函数的声明类似构造函数,除了在析构函数开头要使用 ‘~’ 析构函数,没有返回值,且不带任何参数。 析构函数的功能是用来释放一个对象的。在对象删除前,用它来做一些清理工作,比如一些和类型相关的数据,它与构造函数的功能正好相反。
2)什么样的对象才会被回收
/* 1、GC只会回收 堆 的内存 ,而 值类型在 栈 中的使用完后马上就会给释放,并不需要 GC 进行处理 2、堆中的没有被引用或者null的对象才会被回收,会调用析构函数通知程序对象将要释放(不会立即回收,会在程序的某个时间段执行) 3、静态的成员 会在程序结束后销毁。 */
3) //强制开启回收 立即回收对象 调用析构函数
//GC.Collect();
7)面向对象的特性
1)封装
将类的某些信息隐藏在类内部(private),不允许外部程序直接访问, 而是通过该类提供的公共属性(public)来实现对隐藏信息的操作和访问
好处:
1、隐藏类的实现细节 2、只能通过规定属性访问数据 3、方便加入控制语句 4、方便修改实现 封装在c#中具体表现形式 :方法的封装 私有字段的封装(属性) 类的定义
2)继承
1.一个类继承另一个类 其中被继承的类(父类 超类 基类) 继承的类(子类 派生类) 2.继承关系建立后 子类拥有父类的属性和方法 反之父类不继承子类属性和方法。 3. 继承的作用:解决类与类直接 属性或者方法重复的问题 4 .C#中类与类之间是单继承关系 利用接口实现多继承(后续讲) 5. 继承的格式 A类(子类) : B类(父类) 6. 创建子类对象时,系统默认先调用父类构造方法,然后再调用子类构造方法 7.当父类有有参构造方法时,如果子类有构造方法(无参或有参), 要求父类必须有一个无参构造方法。 8.转型:子类可以赋值给父类,但是父类不能赋值给子类 9.覆盖:在子类中写一个和基类一样名字(参数不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字 10.Object 是所有引用类型的基类
main方法中:
//父类类型的变量保存子类类型的对象可以直接使用
SuperClass1 superClass1 = new SubClass1();
superClass1._age = 10;
//子类的变量 保存父类的对象 在编译时 会报错
//SubClass1 subClass1 = (SubClass1) new SuperClass1();
//subClass1._age = 5;
public class SuperClass1 {//父类
public int _age;
}
public class SubClass1:SuperClass1 { }//子类
//当父类有有参构造方法时,如果子类有构造方法(无参或有参),
//要求父类必须有一个无参构造方法。
public class Person
{
public Person() {//例一:
Console.WriteLine("Person无参构造函数");
}
public Person(int a) {
Console.WriteLine("Person构造函数");
}
}
public class Woman:Person {
/* public Woman(int b) //此时会报错 必须要在父类中加一个无参的构造函数 如例一:
{
}*/
public Woman(int a):base(a)//因为子类构造函数会默认调用父类的无参构造函数 所以用base来改变成调用父类有参的构造函数
{
}
}
3)多态
含义 : 即一个接口,多个功能,同一种操作作用于不同的对象,可以有不同的解释,产生不同的执行结果 体现多态的功能有 :重写 重载 抽象类 接口
1)覆盖:
在子类中写一个和基类一样名字(参数名不同也算)的非虚函数,会让基类中的函数被隐藏,编译后会提示要求使用New关键字 使用base 可以调用父类覆盖的方法
public class MyClass3:Object //如果类没有明确的继承关系 默认继承Object类
//.object是所有类的基类
{
public void MyClass2Mothod(int a)
{
Console.WriteLine("22222222");
}
}
public class MyClass4: MyClass3
{
/*
* 覆盖:在子类中写一个和基类一样名字(参数名不同也算)的非虚函数,
* 会让基类中的函数被隐藏,编译后会提示要求使用New关键字 使用base 可以调用父类覆盖的方法
*/
public void Test2()
{
//base关键字 代表父类的对象
// base.Test2();
//代表当前类的对象
// this.Test2();
Console.WriteLine("Test4");
}
}
2)重写
重写 必然发生在基类和派生类中,其类函数用virtual修饰,派生类用override修饰
重写的目的:父类的方法功能 不能满足于子类需求时
public class MyClass1 {
public void MyClass1Mothod() {
Console.WriteLine("MyClass1Mothod");
}
//父类允许子类覆盖叫重写 virtual允许子类覆盖 这种方法叫虚方法
public virtual void MyClass1Mothod1()
{
Console.WriteLine("MyClass1Mothod1");
}
}
public class MyClass2: MyClass1
{
public void MyClass1Mothod()//重写的父类方法没有用virtual修饰和和override则是覆盖
{
Console.WriteLine("MyClass1Mothod覆盖");
}
//子类重写父类的方法 使用override
public override void MyClass1Mothod1()
{
base.MyClass1Mothod1 ();
Console.WriteLine("MyClass1Mothod1重写");
}
}
3)重载
重载,函数名相同,参数类型或者顺序不同构成重载,与返回类型无关,并且在继承关系中也可实现重载
public class MyClass1 {
public void MyClass1Mothod(int a,int b)
{
Console.WriteLine("MyClass1Mothod int a int b");
}
public static void MyClass1Mothod(string a)//静态方法
{
}
public int MyClass1Mothod(string a, int b)//返回值
{
return 1;
}
}
public class MyClass2 :MyClass1{
public void MyClass1Mothod(string a, int b, bool c)//也可实现重载
{
}
}
8)静态字段用变量访问
/*(1)子类继承的字段,并不属于子类,仍然属于父类。
(2)在子类中修改了父类的静态字段,则父类的静态字段就确实被改了。
(3)在子类中可以用同名的静态字段覆盖父类的静态字段,但是不能使用virtual和override关键字。不过,vs中在子类中会给出警告提示,但确实可以正确运行。
(4)在子类的实例方法中可以直接操作父类的静态字段,而无需用“父类类名.字段名”的调用方法。但是,当子类中具有同名的静态字段覆盖了父类的静态字段时,如果确实需要调用父类的静态字段,则需要采用“父类类名.字段名”的调用方式。
(5)无论是子类或父类,都不能用实例对象来调用静态字段。例如,AClass a=new AClass(); a.staticField=...;这会导致编译错误。因此,只能用类名来调用静态字段,因为静态字段只属于类,而不属于实例对象。*/
main方法:
public class People {
//静态字段
public static int PeopleNumber;
public static void IncrementII()
{
PeopleNumber++;
}
}
public class Man :People
{
public void ChangeValue()
{
People.PeopleNumber=22;
IncrementII();
}
}
9)抽象类
1.关键字:abstract
2.不能被实例化 不能创建对象 3.抽象类可以包括抽象方法,或者普通方法 4.抽象方法和属性只能被定义 ,在子类中实现 5.继承抽象类的非抽象类,必须包含全部已继承的抽象方法和访问器的实现 6.通过包含使用 override 修饰符的属性声明,可在派生类中重写抽象类 方法和属性
7.抽象类可以继承抽象类,但要在普通类中实现功能
public abstract class MyClass {
//普通方法
public void Eat() {
Console.WriteLine("吃饱了");
}
//普通的属性
public int Age { get; set; }
//抽象方法
public abstract void Drink();
//抽象属性
public abstract int Bge { get; set; }
}
public class MyClass1:MyClass{
//实现抽象类drink方法
public override void Drink() {
Console.WriteLine("实现抽象类 Drink方法");
}
private int _bge;
//实现抽象类Bge属性
public override int Bge { get { return _bge; } set { _bge = value; } }
}
10)接口
1)含义:
接口定义:所有类继承接口时应遵循的语法合同 (接口定义那些功能 继承的类就要实现功能)
作用:用接口可以使程序更加清晰和条理化
1.接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的
2.接口只包含了成员的声明(方法 属性声明) 3.不允许声明成员上的修饰符,即使是pubilc都不行,因为接口成员总是公有的 4.不能声明为虚拟和静态的成员 5.接口中定义的成员 需要类继承 来进行实现 类的继承关系 要在接口继承之前 6.接口也可以继承接口 7..一个类 可以继承多个接口 (接口就是c#实现多继承的方式)
2)接口和抽象类 区别和相同点
相同点: 不能实例对象 都可以被继承 都可以给继承类提供成员模板 不同点: 接口只能有成员定义 不能定义静态和虚拟的成员 抽象类 也可以包含成员的实现 接口可以实现多继承 抽象类只能单继承
//接口继承接口
interface IMyInterFace3
{
int Age3 { get; set; }
void IMyinterfaceMothod3();
}
interface IMyInterFace1 {
int Age1 { get; set; }
void IMyinterfaceMothod1();
}
//接口继承接口
interface IMyInterFace2:IMyInterFace1,IMyInterFace3
{
int Age2 { get; set; }
void IMyinterfaceMothod2();
}
3)接口名作为方法的参数和返回值
class Program
{
static void Main(string[] args)
{
#region 接口作为引用参数的形参
//创建Animal类的实例化对象
//Animal p = new Animal();
//因为接口不能实例化 所以只能作为Cat的实例化对象的类型
//Interface o = new Cat();//因为有继承关系,所以写成这样
//这样就可以通过Cat中重写的接口方法来调用接口的方法
//p.usejump(o);
#endregion
#region 接口作为方法的返回值
Animal p = new Animal();
Interface o = p.usejump();
o.jump(1,2);
#endregion
}
}
public interface Interface
{
void jump(int a,int b);//接口里面用抽象方法
}
//接口作为形参
//public class Animal
//{
// public void usejump(Interface a)//相当于创建一个 Interface类型的 对象a
// {
// a.jump(2,3);
// }
//}
//接口作为返回值
public class Animal
{
public Interface usejump()//相当于创建一个 Interface类型的 对象a
{
//Interface p = new Cat();
return new Cat();// 或者p
}
}
public class Cat:Interface //这边继承了接口
{
public void jump(int a,int b) //重写了接口中的方法
{
Console.WriteLine("我是重写{0}的接口中的{1}方法",a,b);
}
}
11、泛型
含义:允许我们延迟编写类或方法中的数据类型, 直到真正使用时确定类型的一种规范
1.可以创建自己的泛型接口、泛型类、泛型方法、泛型集合 ,泛型事件和泛型委托 2.泛型的格式: 结构<泛型的名称T> 在定义泛型时T通常用作变量类型名称。但实际上 T 可以用任何有效名称代替 3.泛型方法在定义是提供泛型的类型 在调用方法时 提供具体的延迟编写的类型 4.泛型无法直接使用运算符 比如 + - < > 等 但是能使用Object中的属性和方法
泛型修饰类:
//泛型:
//概念:使用占位符T来代表某种类型,编译期间决定其具体类型
//格式:class MyGeneric<T>
//使用:MyGeneric<int> mg=new MyGeneric<int>();
class Program
{
static void Main(string[] args)
{
//实现泛型类的类型
//MyClass2<int ,string> w = new MyClass2<int, string>();
//string a = w.MyClass1Mothod();
MyClass4<float> myClass=new MyClass4<float>();
}
}
public class MyClass2<T, D>
{
public T Age { get; set; }
public D MyClass1Mothod()
{
//解决T 泛型的 默认值问题
D a = default(D);
return a;
}
//普通类继承泛型类
public class MyClass3:MyClass2<int ,string> { //实现泛型类的类型实现
}
//泛型类继承泛型类
public class MyClass4<T>:MyClass2<string,int> {//实现泛型类的同时 设置了MyClass类的泛型
public T Name { get; set; }
}
}
泛型修饰方法:
static public void Add<T>( T a, T b)
{
//a+b 不能直接相加会报错
dynamic da = a; //dynamic类型校验推迟到运行时
dynamic db = b;
Console.WriteLine($"二者之后{da+db}");
}
public class MyClass {
//main方法中的
//MyClass myClass = new MyClass();
//int aNumber = myClass.MyClassMothod5<int, string>
public T MyClassMothod5<T,W>(T a, W b) { (10, "123");
return a;
}
}
泛型修饰接口:
interface IMyInterFace<T>{
int IMyInterFaceMothod();
T IMyInterFaceMothod1();
void IMyInterFaceMothod1(T a);
}
interface IMyInterFace2<T,D>
{
T IMyInterFace2Mothod1(D a);
}
interface IMyInterFace3<T, D> :IMyInterFace2<string,int> //泛型接口继承泛型接口
{
T IMyInterFace3Mothod1(D a);
}
interface IMyInterFace1
{
int IMyInterFace1Mothod();
void IMyInterFace1Mothod1();
void IMyInterFace1Mothod1(string a);
}
//普通类可以同时继承泛型接口和普通接口
class MyClass1:IMyInterFace<string>,IMyInterFace1,IMyInterFace3<float,int> {}