1.进行类继承的前提是我们要有两个类,且已经确定好了这两个类谁为父类谁为子类
2.先创建父类然后再创建子类
3.创建子类的时候我们要使用继承语法,使用的方式是在创建子类时的第一条代码的最后面加上冒号 : 和父类的类名
4.继承的本质就是将父类中的代码全都拷贝了一份到子类中,此时子类具有父类的代码,但是父类不具有子类的代码
(换句话说就是子类能够使用父类和自己的成员,但是父类只能够使用父类成员)
5.父类的 public 成员既能够被其子类调用,也能被进行其它类外调用
procted 成员只能够被其子类调用,不能够进行其它类外调用
private 成员只能够在父类中调用,不能够进行任何形式的类外调用
6.上图就是父类型的引用能够指向子类型的在堆区中实例化的对象
7.父类型的引用指向子类对象后,我们通过父类引用 + 点操作符能够访问到的子类对象成员只有子类继承过来的父类成员
8.如果想要既能访问父类成员,又能访问子类成员的话我们必须用子类引用来指向子类对象
(上面那个父类引用中装着子类对象的地址,但是这个地址不能直接传给子类引用,因为这个地址的类型是父类引用类型,如果想传的话我们必须先将父类引用强制类型转换为子类引用后,才能将数据进行传递)
(子类对象的地址在传给父类引用的时候已经进行了隐式类型转换了,但是在在从父类引用传给子类引用的时候没有进行隐式类型转换,而是必须进行显式类型转换后才能进行传递)
父类引用在转换为子类引用的时候需要强制类型转换,但是这个强制类型转换不要直接用括号那种类型,而是要用 :
在父类引用后面加上 as 然后再加上要转换的子类类名(如上图)
这样做的好处是:
如果父类引用指向的是与要转换的子类引用不对应的子类对象的话,在进行类型转换后传数据时会传一个 null 给子类引用,起到报错和检查的作用
静态部分
1.首先提出一个问题,当我们通过类实例化对象的时候,是不是每个对象都会各自拥有一份类中包含的所有字段(成员变量)?
答案是否定的,这要从类中字段(成员变量)的分类开始说起:
一个类中的字段分为两类,这两类字段在实例化对象的时候会有区别:
一种字段是在实例化对象的时候每个对象都会在堆区中开辟空间并自己拥有一份
还有一种字段是在实例化第一个对象的时候单独在堆区为其开辟一个空间,这个空间可以被所有同类的对象访问和操作,也就是说这个类中字段可以被所有的同类对象访问
(PS:空间只会开辟一次,后面再实例化对象的时候就不会再开辟了,这个空间是同类对象的共有空间)
在类中共有字段的语法是:
在正常对象的访问权限后面加上 static 关键字进行修饰 ,就可以将这个字段变为共有字段,又称为静态字段
需要注意的一点是共有字段(静态字段)只能够通过类名 + 点操作符 + 字段名的方式去访问
现在我们回到我们的主题 --- Static
首先static是一个关键字
1.static关键字除了可以修饰成员变量(字段)外,还可以修饰构造函数和成员方法(函数)
2.静态成员变量属于类的意思就是这个成员变量是属于所有通过该类实例化的对象的,是共有物,而实例成员对象则是每个对象在实例化的时候都会拥有的,是属于对象本身的
3.在堆区中为静态成员变量开辟的空间称为静态区(是共享区域),在通过类实例化对象的时候,如果静态区没有被创建则会进行创建,如果已经创建好了的话就不会再重复创建了。
4.静态成员变量直接通过类名访问,而实例成员变量则是通过对象名进行访问
1.出现类名的时候,类本身被加载(类先于对象加载,所以此时还没有对象),此时类中的静态数据成员加载(在堆区中开辟存储它的静态区) --- 类只会被加载一次
2.在堆区中开辟的静态区属于常驻内存,它只有在程序关闭后才会被GC(garbage collection)清除
(构造函数也需要加访问权限,修饰数据成员的关键字都在数据成员最前面,其中访问权限在最前面 ,另外不加访问权限的数据成员会有一个默认的访问权限-- private)
1.静态构造函数不能够有访问修饰符 --- 即静态构造函数不能够有访问权限修饰,只能够有一个static 关键字修饰
2.静态构造函数在出现类名的时候会自动调用,它的作用是初始化静态成员变量(字段)
3.构造函数是用来初始化分配给每个对象的实例成员变量的,它无法初始化静态成员变量,原因是静态成员变量在对象被创建前(出现类的时候)就需要初始化了,而在对象被创建的时候才会调用的构造函数自然无法初始化静态成员变量
1.静态构造函数在类被加载的时候(类名出现的时候)会被调用一次,而且仅会被调用一次,以后都不会再调用了
2.成员变量(无论是静态的还是实例的)的初始化工作都集中在对应的构造函数中进行,而声明工作则是正常在类中实现
3.静态代码块只能够访问静态成员
1.通过对象的引用调用实例方法的时候,都会自动的将引用传给实例方法,让方法能够锁定到自己要操作和处理的是那个对象的实例成员变量
2.传进来的引用都被存储到类中方法中自动创建的 this 指针里了(一般这个this指针的调用都会被隐藏起来),通过this 指针方法就能够明确的找到自己要处理的数据对象所在的内存空间的内存地址了
3.调用静态成员方法和调用实例成员方法的区别就在于:
a. 在类外调用实例成员方法的时候我们都会将调用这个方法的对象的引用传给方法中的this指针
b. 而其与静态成员方法最大的一个区别就是:静态成员方法中没有this 指针,而且我们在类外调用它的时候只能够通过类名进行调用,调用时也不会进行引用传递(对象都没有哪来的引用传递)
这两个区别就决定了我们在静态成员方法中无法处理实例成员变量,在实例成员方法中无法处理实例成员变量
同时我们也无法通过类名调用实例成员方法和通过对象名调用静态成员方法
(1) 让方法处理实例成员变量的时候我们必须先确定是那个实例(对象)的成员变量,这就需要调用方法的实例传一个引用进到方法中用来定位数据成员,如果通过类名调用方法的话就没有引用传入,就无法进行成员定位,自然就报错了
(2)通过对象名调用静态成员方法的话也会调用失败,原因是调用时对象会传引用,但是静态成员方法中没有接收引用的 this 指针,这又会导致出错
问题又来了,为什么我们会需要对象传引用呢?
原因很简单,当我们在实例化了多个对象的时候相当于创建了多份实例成员变量,如果我们想调用实例成员方法去处理某个对象的实例成员变量而只给一个成员变量名的话,问题就出现了
每一个对象拥有的实例成员变量都是同名的,只通过一个变量名方法无法确定到底要处理那一个变量,自然就会导致出错。
这个问题的解决方案就是在对象调用方法的时候传一个引用进来(用this指针来接收),然后通过这个指针(引用)先访问到要处理的成员变量(这一步都被隐藏了),然后再进行处理
为什么静态成员方法处理静态成员变量的时候不需要传引用呢?
首先静态成员方法中就没有接收引用的this指针,其次静态成员变量最为特别的是它只有一份,这一份是所有对象共享的,大家都可以进行访问。正是因为它只有一份没有克隆体,所以方法在找变量的时候不会出现二义性问题
PS:实例代码块既能够访问实例成员变量也能够访问静态成员变量(因为无论是那种变量都能够准确的找到它们的位置)
第三条应该改为静态类不能被继承(不能够当别人的爸爸),普通类中的静态方法,字段和属性都能够被正常继承
静态特性在什么时候适用呢?如上图
1.在通过类实例化一个对象的时候,在堆区中为对象开辟内存空间存储实例成员变量,不需要存储静态成员变量,静态成员变量存在专门为它准备的静态区中
2.静态区中同一个变量被两个对象同时访问时就会出现资源争抢问题,即并发问题,解决这个问题的方法就是加锁,当变量被一个对象访问时将变量锁上,不让其它人访问,访问完后再解锁再继续
3.静态类往往作为工具类来使用,工具类中放的是我们常用的一些方法(函数,在这里会被处理为静态成员函数),这样子做就是为了方便我们使用工具方法和增加代码的可读性,直接通过类名 + 方法(静态方法)名的方式就能够调用我们想用的工具方法,不需要像普通类那样先声明和实例化对象,然后再通过对象调用这么麻烦。
1.这里面存在有两个属性,一个是上面的普通属性Rindex,写这个属性之前我们还需要创建一个对应的成员字段才行(首字母小写)
2.下面那个则是自动属性,创建这个属性的时候编译器就会自动的(隐式的)帮我们创建一个对应的成员字段,同时这个自动属性自己就具有最基础的get和set --- 注意这两个只能够返回字段和接收数据给字段,没有其它的功能,无法像普通的属性那样对get 和 set 进行拓展,无法进行额外的逻辑判断,同时也无法删去 get 和 set 中的任何一个来屏蔽接口
3.关于属性的补充:
没有对应字段的时候属性依然可以被创建,而且能够被正常的使用,因为属性的本质其实就是一个方法集成区,如果是直接调用属性的话那就相当于调用属性中的 get 方法(一定要有return),如果调用属性后给其赋值的话则相当于调用属性中的 set 方法(可以没有return)
没有对应字段时也可以正常的调用方法,而且get 和 set都可以自己选择是保留还是删除,如果删除的话就相当于屏蔽了接口,无法再调用对应的方法
PS:list类中存在一个将对象转换为数组的方法 --- ToArray() --- 通过list类实例化的对象都是对应数组的升级版,拥有更多的功能,使用起来更加的方便。
二维数组中也有GetLength方法,不过和一维数组不同的是,二维数组使用这个方法的时候需要传参,如果传0的话则是获取二维数组的总行数,传的是1的话则获取到的是二维数组的总列数