23-什么是类
24-类的声明和类的访问控制
25-类的继承,类成员访问
26-重写和多态
23-什么是类
面向对象OOP-Object Oriented Programming
封装,继承,多态
类
1.类是一种数据结构-抽象数据结构
类本身是抽象的结果
类是抽象数据和抽象行为的载体
DS-集合数据类型
2.类是一种数据类型
类是1个引用类型,具体到每1个类上,每1个类都是1个自定义类型
类-自定义引用类型
-1.声明变量
-2.创建实例-实例的模板-类的实例化-使用new操作符创建实例
反射-不用new操作符创建实例
3.类代表现实世界的种类
构造器-和类名相同,无返回值
默认构造器
实例构造器-一旦有了实例构造器,编译器就不会再生成默认构造器
实例无参构造器?
实例带参构造器
静态构造器-用来初始化static成员
实例析构器
托管类编程语言-java/c#-Gc垃圾收集器
非托管类编程语言-c/cpp-内存泄漏
反射
dynamic编程
创建对象时
//{}-初始化器-没有显式定义的带参构造器
//()-构造器-显式定义了带参的构造器
namespace cstimothy231
{
class Program
{
static void Main(string[] args)
{
//{}初始化器
Student stu1 = new Student() { ID = 1, Name = "Tom" };
//()构造器
Student stu2 = new Student(2, "Tim");
Console.WriteLine(Student.Amount);//1
}
}
class Student
{
public int ID { get; set; }
public string Name { get; set; }
public static int Amount;
public void Report()
{
Console.WriteLine($"{this.ID},{this.Name}");
}
public Student()
{
}
public Student(int id, string name)
{
this.ID = id;
this.Name = name;
Amount++;
}
static Student()
{
Amount = 0;
}
~Student()
{
Console.WriteLine("bye");
Amount--;
}
}
}
namespace cstimothy232
{
class Program
{
static void Main(string[] args)
{
//反射
Type t1 = typeof(Student);
object o1 = Activator.CreateInstance(t1, 1, "Tom");
Console.WriteLine(o1.GetType().Name);//Student
//方式1-强转
Student stu1 = (Student)o1;
//方式2-as
Student stu2 = o1 as Student;
stu1.Report();
stu2.Report();
//dynamic编程
Type t3 = typeof(Student);
dynamic stu3 = Activator.CreateInstance(t3, 2, "Tim");
stu3.Report();
}
}
class Student
{
public int ID { get; set; }
public string Name { get; set; }
public static int Amount;
public void Report()
{
Console.WriteLine($"{this.ID},{this.Name}");
}
public Student()
{
}
public Student(int id, string name)
{
this.ID = id;
this.Name = name;
Amount++;
}
static Student()
{
Amount = 0;
}
~Student()
{
Console.WriteLine("bye");
Amount--;
}
}
}
24-类的声明和类的访问控制
声明1个类
创建1个 类的实例/对象 -类的实例化
类声明的位置
1.命名空间里
2.命名空间外-global全局命名空间-workless
3.类体内-成员类
类成员
成员类-嵌套类
cpp类的声明和定义是分开的,也可以在一起
Student.h
Student.cpp
Test.cpp
java/c#-声明既定义
错误类型:
编译错误
链接错误-linker链接器
最简单的类声明:
class+identifer类名
{类体}
类名-pascal命名法-单词首字母都大写
特性opt+有效类修饰符组合opt+partail类opt+class+类名+:基类opt
类修饰符
-继承关系
abstarct
sealed
-类的访问控制
1.internal(默认)-internal修饰的类,可以在一个project(assembly)里访问
2.public
-成员类的访问控制
private
public
pr
otected
internal
-静态类
static
public static class 类名
依赖关系
a依赖b,b就不能再依赖a-不能循环引用!
assembly:2种
.exe
.dll类库
using MyLib.MyNamespace;
namespace cstimothy241
{
class Program
{
static void Main(string[] args)
{
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(2, 3));
}
}
}
namespace MyLib.MyNamespace
{
class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
}
}
using MyLib.MyNamespace;
namespace MyLib.MyNamespace2
{
class Test
{
public Calculator Calculator { get; set; }
}
}
25-类的继承,类成员访问
1个类只能有1个基类,但可以有多个基接口
基类-派生类
父类-子类
.net继承体系是单根的
所有继承链的顶端都是Object类
是一个is-a
1个子类的实例也是1个父类的实例,反过来则不成立
用父类类型的变量来引用子类类型的实例
sealed封闭类不能被继承
类修饰符
关于继承的修饰符
abstract-抽象类
sealed-封闭类-不能当做基类被继承
1个类只能有1个基类,但可以有多个基接口
基类-继承,派生
基接口-实现
cpp可以有多个基类,多继承,问题:菱形继承
子类的访问级别不能超越父类的访问级别,可以持平or更低
类的访问级别
internal-默认-限制在程序集
public
ex:
父类是internal,子类是public,编译器报错
父类public,子类internal,OK
继承的本质是子类在父类已有成员的基础上,对父类进行的横向和纵向的扩展。
-子类对父类的成员是全盘继承的-但是父类的实例构造器不能被子类继承
-1个类成员一旦被引入继承链后,就会永远向下传递
cpp/java/c#-静态类型语言-对于类成员只能扩展不能削减
python/javascript-动态类型语言-可以移除类成员
-横向扩展-对类成员个数的扩充
-纵向扩展-对类成员版本的更新(override重写)
c#
托管语言-Gc垃圾收集器
静态语言-继承链上的成员只能扩展不能削减
继承链上的类,创建实例的时候,从基类的构造器开始执行
先调用父类的构造器,再调用子类的构造器
当用子类的类型引用1个子类的实例时,父类的构造器也会被调用
base只能向上访问一层
父类的实例构造器不能被子类继承
类成员的访问级别以类的访问级别为上限
类成员的访问级别:
private-默认-限制在类体内
public
internal-限制在project/assembly
protected-限制在继承链上-跨project/aseembly
internal protected-或的关系-既可以在aseembly访问又可以在继承链上访问
_变量名-private实例字段
面向对象实现风格:
基于类-cpp/java/c#
基于原型-javascript
namespace cstimothy251
{
class Program
{
static void Main(string[] args)
{
Car car1 = new Car();
//两个都是car
//this和base效果相同
car1.ShowName();
}
}
class Vehicle
{
public string Name { get; set; }
public Vehicle()
{
this.Name = "vehicle";
Console.WriteLine("vehicle test");
}
}
class Car : Vehicle
{
public Car()
{
this.Name = "car";
Console.WriteLine("car test");
}
public void ShowName()
{
Console.WriteLine(this.Name);
Console.WriteLine(base.Name);
}
}
}
namespace cstimothy252
{
class Program
{
static void Main(string[] args)
{
Car car1 = new Car("car");
car1.ShowName();
}
}
class Vehicle
{
public string Name { get; set; }
public Vehicle(string name)
{
this.Name = name;
}
}
class Car : Vehicle
{
//public Car():base("n/a")
//{
// this.Name = "car";
//}
public Car(string name) : base(name)
{
//因为在基类构造器里已经把Name的值设置为name参数的值了
//所以我们不必要再在Car的构造器里再设置一遍了,让Car的构造器空着就行
}
public void ShowName()
{
Console.WriteLine(this.Name);
Console.WriteLine(base.Name);
}
}
}
java继承:
26-重写和多态
多态基于重写
横向扩展-通过继承增加类成员个数
纵向扩展-通过override重写,对类成员的版本进行迭代
用父类类型的变量引用子类类型的实例-is-a
多态-函数成员的具体行为由实例决定
override重写
父类virtual-cpp中叫虚函数
子类override
不加virtual和override是hide隐藏
hide-workless-work中hide视为一种错误
重写和隐藏发生的条件:
函数成员,可见,签名一致
函数成员-属性/方法
可见-类成员访问级别为public/protected
函数签名一致-方法名和参数列表相同
hide隐藏
用父类类型的变量引用子类类型的实例
调用的是继承链上最新的被override的版本
//重写->多态
//多态-函数成员的具体行为由实例决定
namespace cstimothy262
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Vehicle();
vehicle1.Run();//vehicle run
Car car1 = new Car();
car1.Run();//car run
Vehicle c = new Car();
c.Run();//car run
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("vehicle run");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("car run");
}
}
}
//隐藏hide
namespace cstimothy263
{
class Program
{
static void Main(string[] args)
{
Vehicle vehicle1 = new Vehicle();
vehicle1.Run();//vehicle run
Car car1 = new Car();
car1.Run();//car run
Vehicle v = new Car();
v.Run();//vehicle run
}
}
class Vehicle
{
public void Run()
{
Console.WriteLine("vehicle run");
}
}
class Car : Vehicle
{
public void Run()
{
Console.WriteLine("car run");
}
}
}
//hide隐藏
namespace cstimothy264
{
class Program
{
static void Main(string[] args)
{
Vehicle v1 = new RaceCar();
v1.Run(); //car run
Car c1 = new RaceCar();
c1.Run();//car run
RaceCar r1 = new RaceCar();
r1.Run();//racecar run
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("vehicle run");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("car run");
}
}
class RaceCar : Car
{
public void Run()
{
Console.WriteLine("racecar run");
}
}
}
//属性重写
namespace cstimothy265
{
class Program
{
static void Main(string[] args)
{
Vehicle v1 = new Car();
v1.Run();
Console.WriteLine(v1.Speed);
}
}
class Vehicle
{
private int _speed;
public virtual int Speed
{
get { return _speed; }
set { _speed = value; }
}
public virtual void Run()
{
Console.WriteLine("vehicle run");
_speed = 100;
}
}
class Car : Vehicle
{
private int _rpm;
public override int Speed {
get { return _rpm / 100; }
set { _rpm = value * 100; }
}
public override void Run()
{
Console.WriteLine("car run");
_rpm = 5000;
}
}
class RaceCar : Car
{
public override void Run()
{
Console.WriteLine("racecar run");
}
}
}
C#强类型语言
C#的变量和实例都是有类型的-可以用父类类型的变量引用子类类型的实例-体现多态
python对象有类型,变量无类型,不能用父类类型变量引用子类类型的实例,看不到多态效果-语言层面不支持,但可以通过库来做到
JS和python一样,对象有类型,变量无类型-存在override但看不到多态效果
TypeScript是基于JS的强类型语言,所以TS变量是有类型的,存在多态
C#
强类型语言-变量和实例都是有类型的
托管类型语言-Gc垃圾收集器
静态类型语言-一个成员一旦被添加到继承链上就不能被移除
java是天然重写,不用加virtual和override,也没有hide
java中的@Override只起到辅助检查重写是否有效的功能
python重写-python不是强类型语言-实例有类型,变量无类型-不能用父类类型的变量引用子类类型的实例-看不到多态效果
using System.Collections.Generic;
using System.Linq;// Enumerable
namespace 261
{
class Program
{
static void Main(string[] args)
{
var values = Enumerable.Range(0, 10).ToArray();
var bst = GetTree(values, 0, values.Length - 1);
DFS(bst);
Console.WriteLine("————————");
BFS(bst);
}
static Node GetTree(int[] values, int li, int hi)
{
if (li > hi)
{
return null;
}
var mi = li + (hi - li) / 2;
var node = new Node(values[mi]);
node.Left = GetTree(values, li, mi - 1);
node.Right = GetTree(values, mi + 1, hi);
return node;
}
static void DFS(Node node)
{
if (node == null)
{
return;
}
DFS(node.Left);
Console.WriteLine(node.Value);
DFS(node.Right);
}
static void BFS(Node root)
{
var q = new Queue<Node>();
q.Enqueue(root);
while (q.Count > 0)
{
var node = q.Dequeue();
Console.WriteLine(node.Value);
if (node.Left != null)
{
q.Enqueue(node.Left);
}
if (node.Right != null)
{
q.Enqueue(node.Right);
}
}
}
}
class Node
{
public int Value { get; set; }
public Node Left { get; set; }
public Node Right { get; set; }
public Node(int value)
{
this.Value = value;
}
}
}