上一部分讲解了类(如何创建类,类的实例化等)以及一些基于类上的基本操作,比如继承,多态,封装,重载和重写(覆盖),这一部分,我们将细分化的介绍类的更多表现形式。
一、静态类
就像我们上一部分说的那样,我们在类内使用static声明的变量,称为类的静态成员变量,这样的函数称为类的静态成员函数。
为什么我们要用静态类呢?一般当这个类很频繁被调用,定义成静态的就不用每次给他实例化了,直接用类去调用可以了,像工具类一般都定义成静态的,这样不用每次实例化了,直接用类名去调用(普通类调用时,必须先要给他new 下,才能去调用)
特点:(1)不能被实例化、不能被继承
(2)不能包含实例构造函数,但是可以包含一个静态构造函数,且静态构造函数不可被调用
1:静态修饰符
const:在类内声明的const常量,在类外访问时,只能通过类名进行访问(不可通过实例化类名)
MyClass myClass= new Myclass();
int a = MyClass.mVaule 而不可为myClass.mVaule
在类内声明的const常量,只能在声明时进行初始化,不允许任何其他地方对其初始化
public const int mVaule1=10;
在某种程度,被const修饰的常量,不可变值
readonly:在类内声明的readonly,不可与const共同修饰一个数据类型
public readonly const int mVaule; //这样的声明方式不允许
readonly修饰的类型,可以被类的实例进行访问,但不可修改值。
readonly的初始化,只能发生在构造函数或声明中。
2:静态构造函数
①静态构造函数,不需要增加访问修饰符
②静态构造函数,无论多少实例,都只被调用一次,而且都只是被系统自动调用一次。
public class Class3;
{
static Class4()
{
Debug.Log("Father");
}
}
public class Class4: Class3
{
public Class3()
{
Debug.Log("Son");
}
}
public calss ClassTest:MonoBehaviour
{
void Start()
{
Class3 iClass1 =new Class3();
Class3 iClass2 =new Class3();
}
}
如上代码,父类声明了静态构造函数,在输出时,只输出一次Father,而输出两Son。
3:静态类
①:静态类不允许有实例构造函数,只允许存在一个静态构造函数
②:不能被实例化
③:静态类内部成员,必须是静态成员,函数也必须是静态成员函数
public static class Myclass
{
public const int mVaule=10; //可以添加静态修饰符
public static int mVaule; //成员必须是静态
public static void Show(){} //函数必须是静态
}
④:静态类无法被继承
二、抽象类
可以在类前加static,使之成为抽象类,它一般是为子类提供设计思想,配合多态用于代码的架构设计。
特点:
不能被实例化
抽象类可以继承抽象类
支持构造函数,静态构造函数只执行一次;其他构造函数会根据实例不同,分别再次调用
允许virtual虚函数
抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类,但抽象函数不允许有函数体,且必须显示覆盖父类的该方法。
public abstract class MyClass6
{
public MyClass6()
{
Debug.Log("MyClass6默认构造函数");
}
static MyClass6()
{
Debug.Log("MyClass6静态构造函数"); //静态构造函数只执行一次
}
public void Show1() //普通构造函数会根据实例多次调用
{
Debug.Log("普通Show1");
}
public virtual void Show2() //可以使用virtual
{
Debug.Log("Myclass6 virtual Show2");
}
public abstract void Show3(); //定义抽象方法,不允许有函数体,且在实现时候必须使用覆盖
}
public class MyClass7:MyClass6 //抽象类可以继承
{
public MyClass7()
{
Debug.Log("MyClass7");
}
public override void Show2() //Show2的重写
{
Debug.Log("MyClass7 override Show2");
}
public override void Show3() //Show3抽象过了,想实现也必须重写
{
}
}
public class Abstract : MonoBehaviour
{
void Start()
{
Myclass7 myClass7=new Myclass7();
Myclass7 myClass8=new Myclass7();
}
如上的输出结果,则只输出一次静态构造函数
三、密封类(接口)
密封类一般用于防止重写某些类或接口影响功能的稳定,在class前加sealed,变为密封类。
特点:不能被继承,但可以继承别的类或者接口
抽象abstruct和sealed不能共存
密封类内的成员函数,不得声明成sealed
public sealed class MyClass8
{
}
public class MyClass9:MyClass8 //不允许被继承
{
}
public class Myclass9
{
public sealed class Myclass10:Myclass9 //允许继承其他类
{
}
}
四、泛型类&泛型方法
我们在处理一组功能相同,仅类型不同的任务的时候,可以在类名后添加<T1,T2,T3..>,将变成一个泛型类,泛型T1,T2,T3,可以通过where关键字来限定类型。
特点:
在声明时可以不指定具体的类型,但是new实例化时必须指定T类型
可指定泛型类型约束
如果子类也是泛型的,那么继承的时候可以不指定具体类型
1:泛型类的效率
#需求:在类内定义一个数组,让这个类具备设置数据和访问数据的能力
①:不使用泛型类
我们必须分别创建两个类,分别用来存储int类型和string类型的数组
public class MyClass //创建一个类用来存储int类型的数组
{
private int[]m_array; //声明了私有Int类型的数组m_array
public MyClass(int size) //构造函数,设置容量
{
m_array=new int[size];
}
public void Set(int index,int value) //设置数据的方法,int类型的下标和数据
{
m_array[index]=value;
}
public int Get(int index) //设置了访问数据的方法
{
return m_array[index]; //返回Index
}
public class MyClass1 //创建一个类用来存储string类型的数组
{
private string[]m_array; //声明了私有string类型的数组m_array
public MyClass1(string size) //构造函数,设置容量
{
m_array=new string[size];
}
public void Set(int index,string value) //设置数据的方法,string类型的下标和数据
{
m_array[index]=value;
}
public string Get(int index) //设置了访问数据的方法
{
return m_array[index]; //返回Index
}
}
public class Tclass
void Start()
{
MyClass myClass= new MyClass(5); //实例化后设置容量
myClass.Set(0,1); //设置数据
myClass.Set(1,2);
int a=myClass.Get(0);
int a=myClass.Get(1);
Debug.LogFormat("第(0)号位,值:{1}",0,a);
Debug.LogFormat("第(1)号位,值:{1}",1,b);
}
②使用泛型
public class MyClass<T> //创建一个泛型类,表示方法如图
{
private T[]m_array; //声明了私有T类型的数组m_array
public MyClass(int size) //构造函数,设置容量
{
m_array=new T[size]; //构造函数也使用T类型
}
public void Set(int index,T value) //设置数据的方法,T类型的下标和数据
{
m_array[index]=value;
}
public T Get(int index) //设置了访问数据的方法
{
return m_array[index]; //返回Index
}
public class Tclass
void Start
{ //在原本T的位置可以改成string,这样就可以存储string类型的数据了
MyClass<string> myClass = new MyClass<string>(5);
myClass.Set(0,"1");
myClass.Set(1,"2");
string a =myClass.Get(0);
string b =myClass.Get(1);
Debug.LogFormat("第(0)号位,值:{1}",0,a):
Debug.LogFormat("第(1)号位,值:{1}",0,b):
}
2:存储类的数据类型
①:不使用where限定
public calss MyClassType //创建需要被存储的类
{
public int a;
public MyClassType(int value) //创建类的成员
{
this.a=value;
}
}
public class MyClass<T>
{
private T[]m_array;
public MyClass(int size)
{
m_array=new T[size];
}
public void Set(int index,T value)
{
m_array[index]=value;
}
public T Get(int index)
{
return m_array[index];
}
void Start()
{
MyClass<MyClassType> myClass =new MyClass<MyClassType>(5); //在实例化的时候也需要改变类型
myClass.Set(0,new MyClassType(1));
myClass.Set(1,new MyClassType(2));
MyClassType a=myClass.Get(0);
MyClassType b=myClass.Get(1);
Debug.LogFormate("第(0)号位,值:{1},",0,a,a);
Debug.LogFormate("第(1)号位,值:{1},",1,b,a);
}
②使用where限定
public calss MyClassType //创建需要被存储的类
{
public int a;
public MyClassType(int value) //创建类的成员
{
this.a=value;
}
}
public class MyClass<T> where T:MyClassType //使用where来限定T的类型
{
private T[]m_array;
public MyClass(int size)
{
m_array=new T[size];
}
public void Set(int index,T value)
{
m_array[index]=value;
}
public int Get(int index) //此处直接修改被存储类的类内定义的成员类型
{
return m_array[index].a;
}
void Start()
{
MyClass<MyClassType> myClass =new MyClass<MyClassType>(5); //在实例化的时候也需要改变类型
myClass.Set(0,new MyClassType(1));
myClass.Set(1,new MyClassType(2));
int a=myClass.Get(0); //直接使用int访问即可
int b=myClass.Get(1);
Debug.LogFormate("第(0)号位,值:{1},",0,a);
Debug.LogFormate("第(1)号位,值:{1},",1,b);
}
NET含有以下五种泛型约束:
1:where T:class|T 必须是一个类
2:where T:struct|T 必须是一个结构类型
3:where T:new()|T 必须要有一个无参数的构造函数
4:where T:NameOfBaseClass |T 必须继承名为NameOfBaseClass的类
5:where T:NameOfInterface |T 必须实现名为NameOfInterface的接口
3:泛型方法
public void Show<X>(X A) //这里的X代表使用泛型的类型,A代表你创建的类型的名称
{
Debug.Log("A="+A);
}
void Start()
{
Son son= new Son();
son.Show<string>("string"); //在外部调用的时候会自动显示需要的类型,后方括号内可写名称
}
4:泛型在继承中的使用,子类给父类限定方法
public class BaseParent<T,X> //父类,赋值类型来自子类
{
}
public class Son<T,X>:BaseParent<T,X> //在定义类的时候不指定类型
{
}
public class Son:BaseParent<int,string> //在定义类的时候指定父类的类型
{
}
public class Son<T,X,Y,A>:BaseParent<T,X> //父类延迟赋值的同时,给自己增加了类型Y,A
{
}
五、接口
接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因
此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
可以使用interface+name,就形成了一个接口。
下面这位博主讲的更为详细。
JAVA基础——接口(全网最详细教程)_刘扬俊的博客-CSDN博客_java接口
特点:
接口只声明接口函数,不包含实现
接口函数访问修饰符,必须是public,默认也是public
接口成员函数的定义时派生类的责任,接口提供了派生类应遵循的标准接口
接口不可被实例化
接口可以继承其他接口,接口允许多重继承
1:接口的具体代码实现
public interface BaseInterface1 //接口的引入,interface +name
{
void ShowWindow(); //接口可声明函数
void HideWindow();
!!! void Show1() //接口不可包含函数体
{
!!! }
!!! private void Show3(); //不能将函数的声明改为私有的
}
public interface BaseInterface2
{
void PlaySound();
void CloseSound();
}
public interface MyInterface:BaseInterface1,BaseInterface2
//接口允许单一继承,也允许多重继承
{
}
public class MyClass16
{
}
public class Tinterface : MonoBehaviour
{
void Start()
{
!!! BaseInterface inter =new BaseInterface(); //接口不可以被实例化
}
2:接口和抽象类的区别和共同点
①:区别
接口不允许声明变量;而抽象类可以
public interface Myinterface
{
public int a; 不可!!!
}
public abstract calss MyAbstract
{
public int b;
}
接口不允许有构造函数(普通和静态),而抽象类可以
public interface Myinterface
{
Myinterface();
static Myinterface(); 不可!!!
}
public abstract calss MyAbstract
{
MyAbstract();
static MyAbstract();
}
接口不允许函数实现,而抽象类可以
接口允许多重继承,而抽象类不可以多重继承
{接口默认的访问修饰符是public,不允许改为private
抽象类默认的访问修饰符是private,函数前面若是abstract,那访问修饰符也不能是private,但是非abstract声明的函数允许private,protected. }
②:共同点
两者都不允许实例化
函数都支持只声明,不实现
他们的派生类都必须去实现接口或抽象类的方法
public interface MyInterface
{
void Show1();
}
public abstract MyAbstract
{
void Show2();
}
public class MyClass:Myinterface
{
public void ShowInterface() //实现interface中的方法
{}
}
public class MyClass:MyAbstract
{
public override void ShowAbstract() //实现abstract中的方法
{}
}
六、结构体Struct
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合,也叫结构。它使得一个单一变量可以存储各种类型的相关数据,我们使用Struct+name结构来声明一个结构体。
特点:
结构体可以带方法,字段,索引,属性,运算符,方法和事件
结构体可以定义构造函数,但不能定义析构函数,无参构造函数(自动定义,且不可改变)
结构体不能继承其他的结构体或类,但可以实现一个或多个接口
结构体不能作为其他结构或类的基础结构
结构成员不能指定为abstract、virtual、protected
结构体不能实例化
1:struct和class的异同
①相同点:
都支持静态构造函数、自定义函数
struct MyStruct
{
Static MyStruct(){}
public void Show(){}
}
class MyClass
{
Static MyClass(){}
public void Show(){}
}
结构体和类对于const修饰的变量的使用方式是一样的
访问函数时,结构体和类都必须进行初始化才能访问
MyClass myClass = new MyClass();
myClass.Show();
MyStruct myStruct = new MyStruct();
myStruct.Show();
②不同点:
结构体不允许定义无参构造函数,只允许定义有参构造函数,但类都可以
struct MyStruct
{
public MyStruct(int value)
{}
public MyStruct() !!!不可
{}
}
结构体不允许定义析构函数,类可以
结构体不允许声明为virtual,但类可以
struct MyStruct
{
public virtual void Show2(){} !!!不可
}
结构体不允许声明为abstratct ,但类可以
结构体不允许声明protected,但类可以
结构体声明的全局普通变量(不带修饰符),不能在声明时直接赋值,只能在构造函数里赋值,但类哪里都可以
struct MyStruct
{
public int a;
public MyStruct(int value){a=value}
}
class MyClass
{
public int a=2000;
}
结构体声明的全局readonly变量,只能在构造函数里赋值,而类都可以
struct MyStruct
public readonly string str;
public MyStruct(int value) //只能在有参构造函数赋值
{
a=value;
str="20";
}
class MyClass
public readonly string str="20"; //直接赋值
或
public read only string str;
public MyClass(int value){str="20"} //构造函数赋值
结构体之间不能相互继承,但是类与类可以
结构体访问成员变量,给变量显示赋值可直接访问,而类必须实例化后才可访问
结构体如果不通过new初始化,是不可以直接访问内部变量(const除外)
MyStruct myStruct;
myStruct.a=2000;
int b =myStruct.a;
MyClass myClass = new MyClass();
int a =myClass.a;
结构体的new并不会在堆上分配内存,仅是调用结构体的构造函数初始化而已
类的new会在堆上分配内存,而且也会调用类的构造函数进行初始化