面向对象-封装
文章目录
- 1、类和对象
- 1、什么是类
- 2、类的声明
- 3、类声明语法
- 4、类声明实例
- 5、对象(类)
- 6、实例化对象语法
- 7、实例化对象
- 2、成员变量和访问修饰符
- 1、成员变量
- 2、访问修饰符
- 3、成员变量的使用和初始值
- 3、成员方法
- 1、成员方法声明
- 2、成员方法的使用
- 4、构造函数和析构函数
- 1、构造函数
- 2、构造函数特殊写法
- 3、析构函数
- 4、垃圾回收机制
- 5、成员属性
- 1、成员属性的基本概念
- 2、成员属性的基本语法
- 3、成员属性的使用
- 4、成员属性中 get 、set 前可以加访问修饰符
- 5、get和set可以只有一个
- 6、自动属性
- 6、索引器
- 1、索引器基本概念
- 2、索引器语法
- 3、索引器的使用
- 4、索引器可以写逻辑
- 5、索引器可以重载
- 思考 封装增删改查
- 7、静态成员
- 1、静态成员概念
- 2、遇见的静态成员
- 3、自定义静态成员
- 4、静态成员的使用
- 5、为什么可以直接使用
- 6、静态函数中不能使用非静态成员
- 7、非静态函数可以使用静态成员
- 8、静态成员对于我们的作用
- 9、常量和静态常量
- 思考 单例模式思想
- 8、静态类和静态构造函数
- 1、静态类
- 2、静态构造函数
- 思考 写一个静态的工具类
- 9、拓展方法
- 1、拓展方法的基本概念
- 2、基本语法
- 3、示例
- 4、为自定义的类型拓展方法
- 10、运算符重载
- 1、基本概念
- 2、基本语法
- 3、示例
- 4、可重载和不可重载的运算符
- 思考 重载运算符实现Vector3类(x,y,z)
- 11、内部类和分部类
1、类和对象
1、什么是类
具有相同特征、相同行为的一类事物的抽象
类是对象的模板
可以通过类创建出对象
2、类的声明
在namespace中
3、类声明语法
class 类名{
特征-成员变量
行为-成员方法
保护特征-成员属性
构造函数和析构函数
索引器
运算符重载
静态成员
}
4、类声明实例
class Person{
}
5、对象(类)
对象是类创建的
类创建的过程称为实例化对象
类是引用类型,结构体是值类型
6、实例化对象语法
类名 变量名;
类名 变量名 = null;
类名 变量名 = new 类名();
7、实例化对象
Person p; Person P2 = null; //没有分配堆内存
Person p3 = new Person(); //分配了堆内存
Person p4 = new Person(); //p3和p4没有关系
2、成员变量和访问修饰符
1、成员变量
1、声明在类语句块中
2、用来描述对象的特征
3、可以是任意变量类型
4、数量不做限制
5、是否赋值根据需求来定
enmu E_SexType{Man,Woman,}
struct Position{}
class Pet{}
class Person{
//特征-成员变量
public string name;
public int age;
public E_SexType sex;
public Person girlFriend; //类可以声明和自己一样的变量类型,这样的成员变量不能实例化
public Person[] friend;
public Position pos;
public Pet pet;
}
2、访问修饰符
public 共有的 外部访问
private 内部访问,默认
protected 内部和子类访问
3、成员变量的使用和初始值
值类型: 数字默认值为0 bool类型 false
引用类型: null
Console.WriteLine(default(int));
3、成员方法
1、成员方法声明
概念
成员方法用来表现对象行为
1、声明在类语句块中
2、是用来描述对象的行为
3、规则和函数声明规则相同
4、收到访问修饰符规则影响
5、返回值参数不做限制
6、方法数量不做限制
class Person{
public string name;
public int age;
public Person[] friends;
//添加新朋友的方法
public void AddFriend(Person p){
if(friends == null){
friends = new Person[] {p};
}
else{
//新建一个数组
Person[] newFriends = new Person[friends.Length + 1];
for(int i = 0;i < friends.Length; i++){
newFriends[i] = friends[i];
}
//添加新朋友
newFfriends[newFriends.Length - 1] = p;
//地址重定向
friends = newFriends;
}
}
public void Speak(string str){
Console.WriteLine("{0}说{1}",name,str);
}
public bool IsAdult(){
return age >= 18;
}
}
2、成员方法的使用
Person p = new Person();
p.name = "魔君";
p.age = 18;
p.Speak("什么");
if(p.IsAdult){
p.Speak("成年了");
}
//添加新朋友
Person p2 = new Person();
p2.name = "大舅哥";
p2.age = 20;
p.AddFriend(p2);
for(int i =0;i<p.friends.Length; i++){
Console.WriteLine(p.friends[i].name);
}
4、构造函数和析构函数
1、构造函数
概念
在实列化对象时,会调用用于初始化的函数
若不写,默认存在一个无参构造函数
构造函数的写法
1、没有返回值
2、函数名必须和类名相同
3、构造函数可以重载
4、this代表当前调用该函数的对象
class Person{
public string name;
public int age;
//类中可以声明无参构造函数,结构体不可以
public Person(){
name = "魔君";
age = 18;
}
//有参构造会顶替掉默认的无参构造
public Person(int age, string name){
this.age = age; //this代表当前成员变量
this.name = name;
}
}
Person p = new Person(18,"魔君");
Console.WriteLine(p.age);
2、构造函数特殊写法
可以通过this 重用构造函数代码
访问修饰符 构造函数名(参数列表):this(参数1,参数2...)
public Person(){}
public Person(int age, string name):this(){ //先调用无参方法,复用代码
Console.WriteLine("Person两个参数构造函数调用");
}
public Person():this("魔君"){
Console.WriteLine("Person两个参数构造函数调用");
}
public Person(int age, string name):this(age + 10){
Console.WriteLine("Person两个参数构造函数调用");
}
3、析构函数
概念
当引用类型的堆内存被回收时,会调用该函数
基本语法
~类名(){}
~Person(){}
4、垃圾回收机制
垃圾回收,GC(Garbage Collector)
垃圾回收过程时在遍历堆(Heap)上动态分配的所有对象
垃圾就是没有被任何变量,对象引用的内容
垃圾回收算法
引用计数(Reference Counting)
标记清楚(Mark Sweep)
标记整理(Mark Compact)
复制集合(Copy Collection)
GC只负责堆(Heap)内存的垃圾回收,引用类型通过GC回收
栈(Stack)上的内存是由系统自动管理的,值类型会随生命周期自动释放
C#中GC大概原理
0代内存 1代内存 2代内存
代是垃圾回收机制使用的一种算法(分代算法)
新分配的对象都会被配置在第0代内存中
每次分配都可能会进行垃圾回收以释放内存(0代内存满时)
在一次内存回收过程开始时,垃圾回收器会认为堆中全是垃圾:
1、标记对象 从根(静态字段、方法参数)开始检查引用对象,标记后为可达对象,未标记为不可达对象
2、搬迁对象压缩堆(挂起执行托管代码线程),释放未标记的对象,搬迁可达对象,修改引用地址
大对象认为是第二代内存,目的是减少性能损耗,提高性能
不会对大对象进行搬迁压缩 85000字节(83kb)以上为大对象
//手动GC方法,用于Loading过场景时调用
GC.Collect();
5、成员属性
1、成员属性的基本概念
1、用于保护成员变量
2、为成员属性的获取和赋值添加逻辑处理
3.解决访问修饰符的局限性
2、成员属性的基本语法
访问修饰符 属性类型 属性名{get{}set{}}
class Person{
private string name;
private int age;
private int money;
private bool sex;
public string Name{
get{
return name; //返回通过属性获取的内容
}
set{
//value 关键字用于表示外部传入的值
name = value;
}
}
}
3、成员属性的使用
Person p = new Person();
p.Name = "老哥";
Console.WriteLine(p.Name);
4、成员属性中 get 、set 前可以加访问修饰符
1、不加修饰符 默认为属性声明时的访问权限
2、加的访问修饰符要低于属性的访问权限
3、不能让get和set的访问权限都低于属性的权限
public int Money{
get{
//解密处理
return money - 5;
}
set{
//加密处理
money = value + 5;
}
}
5、get和set可以只有一个
只有一个时,不加访问修饰符
6、自动属性
作用:外部能得不能改的特征
如果类中有一个特征是只希望外部能得到但不能修改,没有什么特殊处理的情况,可以直接使用自动属性
public float Height{
get;
private set;
}
6、索引器
1、索引器基本概念
让对象可以像数组一样通过索引访问其中元素,使程序更直观,更容易编写
2、索引器语法
访问修饰符 返回值 this[参数类型 参数名, 参数类型 参数名]
{
内部写法和规则和属性相同
get{}set{}
}
class Person{
private string name;
private int age;
private Person[] friends;
public Person this[int index]{
get{
return fridents[index];
}
set{
friends[index] = value;
}
}
}
3、索引器的使用
Person p = new Person();
p[0] = new Person();
Console.WriteLine(p[0]);
4、索引器可以写逻辑
class Person{
private string name;
private int age;
private Person[] friends;
public Person this[int index]{
get{
if(fridens == null || friends.Length - 1 < index){
return null;
}
return fridents[index];
}
set{
if(friends == null){
friends = new Person[] {value};
}
else if(index > friends.Length - 1){
friends[friends.Length - 1] = value;
}
friends[index] = value;
}
}
}
5、索引器可以重载
p[0,0] = 10;
class Person{
private string name;
private int age;
private Person[] friends;
private int[,] array;
public int this[int i, int j]{
get{
return array[i,j];
}
set{
array[i,j] = value;
}
}
public string this[string str]{
get{
switch(str){
case "name":
return this.name;
case "age":
return age.ToString();
}
return "";
}
}
public Person this[int index]{get{}set{}}
}
思考 封装增删改查
//定义一个整型数组类,该类中有一个整型数组变量,为它封装增删改查方法
IntArray array = new IntArray();
array.Add(10);
array.Add(20);
array.Add(30);
array.Add(40);
array.Add(50);
//array.RemoveAt(1);
array.Remove(2);
Console.WriteLine(array[3]);
Console.WriteLine(array.Length);
class IntArray
{
private int[] array;
//房间容量
private int capacity;
//当前房间数
private int length;
public IntArray()
{
capacity = 5;
length = 0;
array = new int[capacity];
}
//增
public void Add(int value)
{
//是否扩容
if(length < capacity)
{
array[length] = value;
length++;
}
else // 扩容,换新数组
{
capacity *= 2;
//新房间
int[] tempArray =new int[capacity];
//旧换新
for (int i = 0; i < array.Length; i++)
{
tempArray[i] = array[i];
}
array = tempArray; //老房子地址指向新房子地址
//添加内容
array[length] = value;
length++;
}
}
//删
public void Remove(int value)
{
for (int i = 0; length > i; i++)
{
if (array[i] == value)
{
RemoveAt(i);
return;
}
}
Console.WriteLine("没有在数组中找到{0}",value);
}
public void RemoveAt(int index)
{
if (index > length - 1)
{
Console.WriteLine("当前数组长度只有{0}", length);
return;
}
for (int i = index; i < length - 1; i++)
{
array[i] = array[i + 1]; //前移数组元素
}
length--;
}
//改查
public int this[int index]
{
get
{
if(index>=length || index < 0)
{
Console.WriteLine("越界");
return 0;
}
return array[index];
}
set
{
if (index >= length || index < 0)
{
Console.WriteLine("越界");
}
array[index] = value;
}
}
public int Length
{
get
{
return length;
}
}
}
7、静态成员
1、静态成员概念
静态关键字 static
用static修饰的 成员变量、方法、属性等,称为静态成员
静态成员的特点:直接用类名.静态成员使用
2、遇见的静态成员
Console是静态成员
3、自定义静态成员
class Test{
//静态成员变量
public static float PI = 3.14f;
//成员变量
public int testInt = 100;
//静态成员方法
public static float CalCircle(float r){
return PI * r * r;
}
//成员方法
public void TestTun(){
Console.WriteLine("123");
}
}
4、静态成员的使用
Console.WriteLine(Test.PI);
Console.WriteLine(Test.CalCircle(2));
5、为什么可以直接使用
静态成员的特点
程序开始运行时,就会分配内存空间。所以静态成员和程序同生共死,静态成员具有唯一性和全局性
6、静态函数中不能使用非静态成员
成员变量只能将对象实例化后才能使用,不能直接使用 非静态成员
7、非静态函数可以使用静态成员
静态生命周期长
8、静态成员对于我们的作用
静态变量:
1、常用唯一变量的声明
2、方便别人获取对象声明
静态方法:
常用唯一的方法声明
9、常量和静态常量
const 可以理解为特殊的static
相同点:
都可以不通过实例化直接使用
不同点:
1、const必须初始化,不能修改; static没有此规则
2、const只能修饰变量; static可以修饰其他
3、const一定写在访问修饰符后; static可以写前面
思考 单例模式思想
//一个类对象,在整个应用程序的生命周期中,有且仅有一个该对象存在
//不能在外部实例化,直接通过该类类名就能够得到唯一的对象
Console.WriteLine(Test.T.testInt);
class Test
{
private static Test t = new Test();
public int testInt = 10;
public static Test T
{
get
{
return t;
}
}
private Test()
{
}
}
8、静态类和静态构造函数
1、静态类
概念
用static修饰的类
特点
只能包含静态成员,不能被实例化
作用
1、将常用的静态成员写在静态类中,方便使用
2、静态类不能被实例化,更能体现工具类的唯一性
2、静态构造函数
概念
在构造函数上加static修饰
特点
1、静态类和普通类都可以
2、不能使用访问修饰符
3、不能有参数
4、只会调用一次
作用
在静态构造函数中初始化 静态变量
使用
1、静态类中的静态构造函数
2、普通类中的静态构造函数
思考 写一个静态的工具类
//写一个用于数学计算的静态类
//该类中提供计算圆面积,周长,矩形面积,矩形周长,取一个数的绝对值
Console.WriteLine(MathTool.CalcCircularArea(2));
Console.WriteLine(MathTool.CalcCircularLength(2));
Console.WriteLine(MathTool.CalcRectArea(2,3));
Console.WriteLine(MathTool.CalcRectLength(2,3));
Console.WriteLine(MathTool.GetABS(-22));
static class MathTool
{
public static float PI = 3.14f;
/// <summary>
/// 计算圆面积
/// </summary>
/// <param name="r">半径</param>
/// <returns>周长</returns>
public static float CalcCircularArea(float r)
{
return PI * r * r;
}
public static float CalcCircularLength(float r)
{
return 2 * PI * r;
}
public static float CalcRectArea(float w,float h)
{
return w * h;
}
public static float CalcRectLength(float w, float h)
{
return 2 * (w + h);
}
public static float GetABS(float value)
{
if (value < 0)
{
return -value;
}
return value;
}
}
9、拓展方法
1、拓展方法的基本概念
概念
为现有非静态变量类型添加新方法
作用
1、提升程序拓展性
2、不需要再对象中重新写方法
3、不需要继承来添加方法
4、为别人封装的类型写额外的方法
特点
1、一定是写在静态类中
2、一定是一个静态函数
3、第一个参数为拓展目标
4、第一个参数用this修饰
2、基本语法
访问修饰符 static 返回值(this 拓展类名 参数名,参数类型 参数名)
3、示例
int i = 10;
i.SperkValue();
static class Tools{
//为int拓展了一个成员方法
//成员方法是需要实例化对象后才能使用
//value代表使用该方法的实例化对象
public static void SpeakValue(this int value){
//拓展方法的逻辑
Console.WriteLine("为int拓展的方法"+value);
}
}
4、为自定义的类型拓展方法
Test t = new Test();
t.Fun3();
static class Tools{
public static void Fun3(this Test t){
Console.WriteLine("为Test写的拓展方法");
}
}
class Test{
public int i = 20;
public void Fun1(){
Console.WriteLine("123");
}
public void Fun2(){
Console.WriteLine("456");
}
}
10、运算符重载
1、基本概念
概念
让自定义类和结构体能够使用运算符
关键字
operator
特点
1、一定是一个公共的静态方法
2、返回值写在operator前
3、逻辑处理自定义
作用
让自定义类和结构体对象可以进行运算
注意
1、条件运算符需要成对实现
2、一个符号可以多个重载,注意参数
3、不能使用ref 和 out
2、基本语法
public static 返回类型 operator 运算符(参数列表)
3、示例
Point p = new Point();
p.x = 1;
p.y = 1;
Point p2 = new Point();
p2.x = 2;
p2.y = 2;
Point p3 = p + p2;
Point p4 = p3 + 2;
class Point{
public int x;
public int y;
public static Point operator +(Point p1, Point p2){
Point p = new Point();
p.x = p1.x + p2.x;
p.y = p1.y + p2.y;
return p;
}
public static Point operator +(Point p1, int value){
Point p = new Point();
p.x = p1.x + value;
p.y = p1.y + value;
return p;
}
}
4、可重载和不可重载的运算符
1、可重载的运算符
算数运算符 //都可以
public static Point operator -(Point p1, Point p2){return null};
public static Point operator *(Point p1, Point p2){return null};
public static Point operator /(Point p1, Point p2){return null};
public static Point operator %(Point p1, Point p2){return null};
public static Point operator ++(Point p1){return null};
public static Point operator --(Point p1){return null};
逻辑运算符 //逻辑非可以!
public static bool operator !(Point p1){return false};
位运算符 //都可以
public static Point operator |(Point p1, Point p2){return null};
public static Point operator &(Point p1, Point p2){return null};
public static Point operator ^(Point p1, Point p2){return null};
public static Point operator ~(Point p1){return null};
public static Point operator <<(Point p1, Point p2){return null};
public static Point operator >>(Point p1, Point p2){return null};
条件运算符 //成对出现
public static bool operator >(Point p1, Point p2){return false};
public static bool operator <(Point p1, Point p2){return false};
public static bool operator >=(Point p1, Point p2){return false};
public static bool operator <=(Point p1, Point p2){return false};
public static bool operator ==(Point p1, Point p2){return false};
public static bool operator !=(Point p1, Point p2){return false};
2、不可重载的运算符
逻辑与&& 逻辑或||
索引符[]
强转运算符()
特殊运算符
三目运算符 赋值符号= 点.
思考 重载运算符实现Vector3类(x,y,z)
//定义一个Vector3类(x,y,z)通过重载运算符实现以下运算
//(x1, y1, z1) + (x2, y2, z2) = (x1 + x2, y1 + y2, z1 + z2)
//(x1, y1, z1) - (x2, y2, z2) = (x1 - x2, y1 - y2, z1 - z2)
//(x1, y1, z1) * num = (x1 * num, y1 * num, z1 * num)
Vector3 v1 = new Vector3(1,1,1);
Vector3 v2 = new Vector3(2,2,2);
Vector3 v3 = v1 + v2;
Console.WriteLine("Vector3({0},{1},{2})", v3.x,v3.y,v3.z);
Vector3 v4 = v1 - v2;
Console.WriteLine("Vector3({0},{1},{2})", v4.x, v4.y, v4.z);
v4 *= 3;
Console.WriteLine("Vector3({0},{1},{2})", v4.x, v4.y, v4.z);
class Vector3
{
public int x; public int y; public int z;
public Vector3(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public static Vector3 operator +(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
}
public static Vector3 operator -(Vector3 v1, Vector3 v2)
{
return new Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
}
public static Vector3 operator *(Vector3 v,int num)
{
return new Vector3(v.x * num, v.y * num, v.z * num);
}
}
11、内部类和分部类
1、内部类
概念
在类中再声明一个类
特点
使用时要用包括者点出自己
作用
亲密关系的体现
注意
访问修饰符作用很大
2、分部类
概念
把一个类分成几部分声明
关键字
partial
作用
分部表述一个类
增加程序的拓展性
注意
分部类可以写在多个脚本文件中
分部类的访问修饰符要一致
分部类中不能有重复成员
3、分部方法
概念
将方法的声明和实现分离
特点
1、不能加访问修饰符,默认私有
2、只能在分部类中声明
3、返回值只能是void
4、可以有参数但不用out关键字