用程序来抽象现实世界,(万物皆对象)来编程实现功能。
三大特性:封装、继承、多态。
声明位置:namespace
中
样式:class 类名{}
命名:帕斯卡命名法(首字母大写)
实例化对象:根据类来新建一个对象。Person p=new Person();
enum E_SexType
{
Man,
Woman
}
struct Position{}//位置结构体
class Pet{}//宠物类
//类中的成员变量
class Person
{
public string name="TonyChang";//区别于结构体--可以默认赋初始值
public int age=21;
public E_SexType sex;
public Person bestFriend;//区别于结构体---类中可以有同类的成员类型(本质是因为类属于引用类型,但不可以实例化,防止反复new,陷入死循环)
public Position pos;
public Pet pet;
}
成员类型的默认值:
值类型的:数字的为0,bool
类型的false
引用类型:null
查看(int类型)默认值:default(int)
补充:class属于引用类型,其中的值类型也放置在堆中。
成员方法只有在实例化之后才可以使用调用。具体的一个对象的行为(方法),必须具体的对象调用。
//成员方法
class Person
{
public string name;
public int age;
public void Speak()
{
Console.WriteLine("你好!");
}
}
//成员方法的使用
Person p=new Person;
p.Speak();
默认有一个无参构造函数,而类中可以允许自己声明无参构造函数,而结构体不行。
一旦有自定义的构造函数,默认的无参构造函数则失效!
构造函数:
class Person
{
public string name;
public int age;
//构造函数
public Person()
{
name="TonyChang";
age=21;
}
//此时先调用age参数的构造函数 然后再调用两个参数的构造函数
public Person(string name,int age):this(age)
{
this.name=name;
this.age=age;
}
public Person(string name)
{
this.name=name;
}
public Person(int age)
{
this.age=age;
}
}
特殊的构造函数,在调用该函数之前先调用this的无参构造函数。
public Person(int age):this()
{
this.age=age;
}
析构函数:
由于C#中有自动的垃圾回收机制,一般不使用析构函数。
析构函数是当垃圾真正被回收时候才会调用。
~Person(){}
//析构函数
用于保护成员变量,为成员属性的获取和赋值添加逻辑处理。
//成员属性 帕斯卡命名法
class Person
{
private string name;
public string Name
{
get{
return name;
}
set{
name=value;
}
}
private int age;
public int Age
{
//不可以获得年龄(或者删除set设置则可表明也无法获取age)
private get=>age;
//可以设置年龄
set
{
age=value;
}
}
//额外:
//自动成员属性 (对于没有特殊需要的成员)
public float Height
{
get;
set;
}
}
可以让对象像数组一样通过索引来访问其中的元素。
注意:结构体中也支持索引器。
//索引器
class Person
{
private string name;
private int age;
private Person[] friends;
private int[,] arry;
//索引器
public Person this[int index]
{
get
{
return friends[index];
}
set
{
//此处可以写一些控制逻辑
friends[index]=value;
}
}
//索引器的重载
public int this[int i,int j]
{
get
{
return array[i,j];
}
set
{
array[i,j]=value;
}
}
}
//使用
Person p=new Person();
p[0]=new Person();//可以像数组一样进行访问
static
修饰的成员变量/方法为静态成员。
静态成员归属为类,即不用初始化就可以使用的类的成员。
静态成员函数中不可以使用非静态成员,非静态成员函数中可以使用静态成员函数。
本质:在程序运行开始时,检查到静态成员,会再特定的区域为其开辟内存空间来存储。所以说静态成员与程序共生死。因为生命周期不同,所以静态成员函数中不可以使用非静态成员。
使用:全局性,稳定不变的常量。例如固定的数值 Π,重力加速度g等包括固定的计算方法,可以供全局成员来访问使用。但是静态过多会占用内存,引发GC。
相同点:都可以通过类名点来使用
不同点:
const
必须初始化,不能修改,而static可以const
只能修饰变量,而static可以修饰很多const
一定是写在访问修饰符的后面,static则无此要求用 static修饰的类为静态类,往往来作为工具类。例如System中的Console类。只能包含静态成员,不能实例化。
静态构造函数 :在静态构造函数中初始化静态成员变量。
//静态构造函数
static class Test
{
public static int testInt=100;
public static float testFloat=20.5f;
static Test()
{
//静态构造函数
Console.WriteLine("自动调用了!");
}
}
class NormalTest
{
public static int i=5;
//首次使用静态成员时候 自动调用一次
//静态成员函数
static NormalTest()
{
Console.WriteLine("静态构造函数");
}
public NormalTest()
{
Console.WriteLine("非静态成员函数");
}
}
拓展方法为现有的非静态变量类型添加新方法。
作用:
特点:
//拓展方法
static class expendTool
{
//为int添加拓展方法
public static void SpeakValue(this int value)
{
Console.WriteLine("这是int的拓展方法,int的数值为{0}",value);
}
//为string拓展方法
public static void SpeakStringInfo(this string str,string name,string info)
{
Console.WriteLine("这是string的拓展方法,string的数值为{0},该拓展方法由{1}编写,拓展内容为{2}",str,name,info);
}
}
class Program
{
static void Main(string[] args)
{
int i = 5;
i.SpeakValue();
string ss = "原始字符串";
ss.SpeakStringInfo("TonyChang", "附加字符串");
}
}
注意:如果拓展方法名称与自身现有的方法名称相同,则只会调用自身的方法,不会调用拓展的方法。
关键字 operator
特点:1. 一定是一个公共的静态成员方法
作用:可以使自定义的数据类型来完成后相同意义的运算。
注意:
//运算符重载
class Point
{
public int x;
public int y;
public Point(int x,int y)
{
this.x=x;
this.y=y;
}
//重载+运算符
//参数列表中必须要有自己的类别出现
public static Point operator +(Point p1,Point p2)
{
Point sum=new Point();
sum.x=p1.x+p2.x;
sum.y=p1.y+p2.y;
return sum;
}
}
class Program
{
static void Main(string[] args)
{
Point p1=new Point(1,1);
Point p2=new Point(2,2);
Point p3=P1+p2;
Console.WriteLine(p3.x);
}
}
补充:大部运算符可以重载,逻辑运算符中只可以允许重载 逻辑非!
不能重载的运算符有:
&& || 索引符[] 强制转换符号() 特殊运算符 点. 三目运算符?:
//内部类
class Person
{
public class Body
{
class Arm
{
}
}
}
class Program
{
static void Main(string[] args)
{
Person.Body body=new Person.Body();
}
}
//分布类
//分布类可以分布在不同的脚本文件中
//分布类的访问修饰符要一致
partial class Student
{
public string name;
public bool sex;
partial void Speak();
}
partial class Student
{
public int age;
public string stuId;
partial void Speak()
{
Console.WriteLine("分布方法的具体实现");
}
}
垃圾回收,英文简称GC(Garbage Collector)
垃圾回收过程:遍历堆(Heap)上的动态分配的所有对象
通过识别它们是否被引用来确定其是否是垃圾。垃圾是指没有引用所指引的对象、变量,需要被回收释放掉占用的内存空间。
垃圾回收算法:
引用计数、标记清除、标记整理、复制集合。
注意:垃圾回收只回收heap堆上的 栈中的内存由系统管理
回收机制: 三代内存
0代内存 1代内存 2代内存
每一代内存满掉之后便会清理垃圾
高代连锁:1代清理会连带0代清理,2代清理连带0代和1代
清理完垃圾之后,非垃圾内容搬迁到下一代中(0代将非垃圾转移到1代内存,
1代内存将非垃圾转移到2代内存)所以2代内存存储的较为老的对象实例,还包括大的对象
一般是85kb以上的对象
0代1代的读取速度要高于1代,分配内存位置优先0代>1代>2代
手动GC:
GC.Collect();
一般在场景加载时候进行GC。
继承者(子类)继承父类(基类、超类)的特性,同时也可以有自己独特的方法性质。
只能单继承。子类只能由一个父类。
继承特性:单根性、传递性。
//继承
//老师类
class Teacher
{
//姓名
public string name;
//职工号
protected int number;
//介绍名字
public void SpeakName()
{
number = 10;
Console.WriteLine(name);
}
}
//教学老师继承老师类
class TeachingTeacher : Teacher
{
//科目
public string subject;
//介绍科目
public void SpeakSubject()
{
number = 11;
Console.WriteLine(subject + "老师");
}
}
//语文老师继承教学老师类
class ChineseTeacher:TeachingTeacher
{
public void Skill()
{
Console.WriteLine("一行白鹭上青天");
}
}
class Program
{
static void Main(string[] args)
{
TeachingTeacher tt = new TeachingTeacher();
tt.name = "汪老师";
//tt.number = 1;
tt.SpeakName();
tt.subject = "Unity";
tt.SpeakSubject();
ChineseTeacher ct = new ChineseTeacher();
ct.name = "张老师";
//ct.number = 2;
ct.subject = "语文";
ct.SpeakName();
ct.SpeakSubject();
ct.Skill();
}
}
父类容器装在子类对象。(任何父类出现的地方,子类都可以替代)
class GameObject
{
}
class Player:GameObject
{
public void PlayerAtk()
{
Console.WriteLine("玩家攻击");
}
}
class Monster:GameObject
{
public void MonsterAtk()
{
Console.WriteLine("怪物攻击");
}
}
class Boss:GameObject
{
public void BossAtk()
{
Console.WriteLine("Boss攻击");
}
}
class Program
{
static void Main(string[] args)
{
//里氏替换原则
Gameobjet player=new Player();
Gameobjet monster=new Monster();
Gameobjet boss=new Boss();
//is 和 as
if(player is Player)
{
(player as Player).PlayerAtk();
}
}
}
is是判断一个对象是否为指定类型对象,返回值为true则为真,不是则为false
as用来将一个对象转换为指定类型对象,返回值为指定类型对象,若转换失败则返回null
子类构造函数调用之前,先执行父类的构造函数。(爷爷-->父亲-->子类)
所以要保证父类的构造函数(尤其为无参构造函数)
//继承中的构造函数
class Father
{
//父类的无参构造函数很重要!
public Father()
{
}
public Father(int i)
{
Console.WriteLine("Father的有参构造");
}
}
class Son:Father
{
public Son(int i):base(i)
{
//构造函数
}
}
object 是所有类型的基类,
作用:
装箱:
用object存值类型。本该在栈中数值转移到堆上
拆箱
将object转换为值类型,将堆上的值类型转移到栈上(配合 is和as 使用)
优点:统一对象类型(里氏替换原则),方便对不同类型对象数值的管理
缺点:消耗性能,
//装箱拆箱
int i=5;
object obj=i;//装箱
i=(int)obj;//拆箱
使用sealed关键字修饰的类,不可以被派生。(结扎了!)
V: virtual(虚函数)
O: override(重写)
B: base(父类)
让继承同一父类的子类们在执行相同方法有不同的表现与状态。
就是说,继承是一脉相承父类的品质,而多态是由自己的个性,尽管做的和父辈的事情相同。
解决的问题:
class Father
{
public void SpeakName()
{
Console.WriteLine("Father的方法");
}
}
class Son:Father
{
public new void SpeakName()
{
Console.WriteLine("Son的方法");
}
}
class Program
{
static void Main(string[] args)
{
#region 解决的问题
Father f = new Son();
f.SpeakName();//调用的是父亲的方法
(f as Son).SpeakName();//调用的是儿子的方法
#endregion
}
}
使用多态来保证(继承类)一个类方法的独立性
class GameObject
{
public string name;
public GameObject(string name)
{
this.name = name;
}
//虚函数 可以被子类重写
public virtual void Atk()
{
Console.WriteLine("游戏对象进行攻击");
}
}
class Player:GameObject
{
public Player(string name):base(name)
{
}
//重写虚函数
public override void Atk()
{
//base的作用
//代表父类 可以通过base来保留父类的行为
base.Atk();
Console.WriteLine("玩家对象进行攻击");
}
}
抽象类不可以被实例化
abstract class Thing{
public string name;
}
抽象方法:没有方法体的纯虚方法,继承的子类必须实现纯虚方法。(子类必须重写该方法,子类的子类不必强制实行,但也可以继续重写。)
抽象方法与virtual(虚函数)方法区别:
abstract class Fruits
{
public string name;
//抽象方法 是一定不能有函数体的
public abstract void Bad();
public virtual void Test()
{
//可以选择是否写逻辑
}
}
class Apple : Fruits
{
public override void Bad()
{
}
//虚方法是可以由我们子类选择性来实现的
//抽象方法必须要实现
}
概念:接口是行为的抽象规范
关键字:interface
声明规范:
使用规范:
特点:
接口是抽象行为的”基类“
//接口的声明
//命名规范 I+帕斯卡命名法
interface IFly
{
void Fly();//方法
string Name//属性
{
get;
set;
}
int this[int index]//索引器
{
get;
set;
}
event Action doSomthing;//事件委托
}
接口的使用---类的继承
//接口的使用
class Animal
{
}
class Person:Animal,IFly
{
//实现接口方法也可以加virtual来实现
public virtual void Fly()
{
}
public string Name
{
set;
get;
}
public int this[int index]
{
get
{
return 0;
}
set
{
}
}
public event Action doSomething;
}
接口的使用---接口的继承
接口继承基类接口之后,不需要实现接口中的内容(抽象继承抽象,还是抽象)
等到最后类来具体实现
//接口继承接口
interface IWalk
{
void Walk();
}
interface IMove:IFly,IMove
{
}
//必须实现所有相关的
//继承来的抽象内容(接口,接口的父接口中的成员)
class Test:IMove
{
public int this[int index] {
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
public string Name {
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
public event Action doSomthing;
public void Fly()
{
throw new NotImplementedException();
}
public void Walk()
{
throw new NotImplementedException();
}
}
显示实现接口
//接口的使用--当作容器 父类装子类
interface IAtk
{
void Atk();
}
interface ISuperAtk
{
void Atk();
}
//显示实现接口
class Player : IAtk, ISuperAtk
{
//遇到相同方法名字
//显示实现接口 就是用 接口名.行为名 去实现
void IAtk.Atk()
{
}
void ISuperAtk.Atk()
{
}
public void Atk()
{
}
}
class Progarm
{
static void Main(string[] args)
{
IFly f = new Person();
//里氏替换原则
IMove im = new Test();
IFly ifly = new Test();
IWalk iw = new Test();
IAtk ia = new Player();
ISuperAtk isa = new Player();
ia.Atk();
isa.Atk();
Player p = new Player();
(p as IAtk).Atk();//IAtk的
(p as ISuperAtk).Atk();//ISuperAtk的Atk
p.Atk();//自己的Atk
}
}
*密封方法(了解)
sealed 修饰的重写方法,子类不会被重写方法
namespace
string 本质是char[]数组 可以有
string ss="Tony",char[0]='T'
字符串的拼接
正向查找字符的位置 IndexOf()
反向查找字符串的位置 LastIndexOf()
移除指定位置后的字符 Remove(index)//注意接受返回值
字符串的替换
大小写转换
字符串的截取
字符串的切割 str.Split(',');按照,切割
字符串频繁拼接使用StringBuilder,不会再次频繁的创建新的对象,减少垃圾产生。
容量问题:初始时候本身有一定容量,在容量允许范围内,直接存储。
超过容量之后,会以2倍大小扩容。相对于string每一次更改便会新建对象,可减少垃圾产生。
//StringBuilder
StringBuilder str=new StringBuilder("My Name is Tony");
//获取容量
str.Capacity;
//增加
str.Append("Chang");
str.AppendFormat("{0}{1}",123,456);
//插入
str.Insert(0,"Hello");
//删除
str.Remove(0,10);
//清空
str.Clear();
//查
str[1];
//替换
str.Replace("Name"."name");
//重新赋值
str.Clear();
str.Append("Hello World");
//equals
if(str.Equals("123456"))
{
}
String 还是StringBuilder?
String的方法种类较多,使用更加方便和灵活,但性能上不如StringBuilder,不如StringBuilder产生垃圾少
需要频繁修改的字符串选用StringBuilder更好些。
如何优化内存?
如何选择结构体和类:
相同点:
区别:
如何选择抽象类与接口
表示对象的选用抽象类,表示行为拓展的用接口。不同对象的相同行为,我们可以抽象出行为,用接口来实现。
总目标:高内聚、低耦合
减少类内部对其它类的调用,减少模块与模块之间的交互复杂度。
算法学习笔记,记录容易忘记的知识点和难题。详解时空复杂度、50道常见面试笔试题,包括数组、单链表、栈、队列、字符串、哈希表、二叉树、递归、迭代、分治类型题目,均带思路与C++题解