12.1 类与对象
我们在代码中描述一个对象,是通过描述这个对象的属性和方法,对象必须是看得见摸得着的。
例如:
灯:拥有属性和方法
属性:外形:圆的;功耗:500w;颜色:白色;牌子:xx
方法:发光
电风扇:拥有属性和方法
属性:外形:三个叶片;颜色:白色;品牌:xx
方法:转动、扇风
我们把这些具有相同属性和相同方法的对象进行进一步的封装,抽象出来“类”这个概念。
类就是一张图纸,确定了对象具有的属性和方法,对象是根据类创建出来的。
类就是一个盖大楼的图纸,对象就是盖出来的大楼。
12.2 类的语法
[public] class〈类名〉
{
字段(Field);
属性(property);
方法(Method);
}
属性与字段是对同一个元素不同的称呼。字段是从数据库角度的名字,也可以称为属性,属性是从面向对象角度的名字。
写好了一个类后,我们需要使用关键字new创建这个类的对象,我们称创建这个类的过程叫做实例化,this:表示当前这个类的对象。类是不占内存的,而对象是占内存的。
12.3 属性
1)简介:
属性是字段的自然扩展。从命名上看,字段更偏向于实例对象在内存中的布局。属性更偏向于反映现实世界对象的特征。属性的作用就是保护字段、对字段的赋值和取值进行限定,属性的本质就是两个方法,一个叫get一个叫set。
建议:永远使用属性(而不是字段)来暴露数据,即字段永远都是private或protected的
2)语法:
public 返回类型 标识符
{
get{ retrun 输出值;} 访问器为属性获取值
set{ 赋值 = value;} 访问器为属性赋值
}
既有get也有set我们称之为可读可写属性
只有get没有set我们称之为只读属性
没有get只有set我们称之为只写属性,只写属性很少出现,常用于限制对敏感数据的访问
封装属性的快捷键:Ctrl+R+E
在进行设值(赋值)限定的时候,参数用的是value,在进行取值(输出值)限定的时候参数用的是字段。
示例:
private string name;
public string Name
{
get{return name;}
set{name=value;}
}
3)new关键词:
Person zs Person = new Person();
new帮我们做3件事:
① 在内存中开辟一块空间
② 在开辟的空间中创建对象
③ 调用对象的构造函数进行初始化对象
4)this关键词:
代表当前类的对象。
在类当中显示的调用本类的构造函数:this。
5)属性练习:在同一命名空间下,新建Person类,判断用户赋值的姓名、性别、年龄,如果性别赋值的不是男/女,默认为中,年龄如果小于0或者大于100,默认为0。
//首先在Person类下封装属性,写方法:
public class Person
{
private string _name;
public string Name
{
//当你输出属性的值得时候 会执行get方法
get { return _name; }
//当你给属性赋值的时候 首先会执行set方法
set { _name = value; }
}
private int _age;
public int Age
{
get { return _age; }
set
{
if (value < 0 || value > 100)
{
value = 0;
}
_age = value;
}
}
private char _gender;
public char Gender
{
get
{
if (_gender != '男' && _gender != '女')
{
return _gender = '中';
}
return _gender;
}
set { _gender = value; }
}
public void Say()
{
Console.WriteLine("我叫{0},我今年{1}岁了,我是{2}生,我 可以说话", this.Name, this.Age, this.Gender);
}
}
//再在Main函数实例化Person对象,并赋值:
class Program
{
public static void Main(String[] args)
{
Person p = new Person();
p.Name = "张三";
p.Age = 20;
p.Gender = '女';
p.Say();
Console.ReadKey();
}
}
运行结果:
我叫张三,我今年20岁了,我是女生,我可以说话
12.4 构造函数
1)构造函数简介:
首先它是一个方法(函数),因为具有“构造”的作用,所以称之为构造函数(构造方法)。构造函数就是与类具有相同名称的成员函数,通常使用类中的构造函数来初始化类的成员属性,不包含返回类型。
作用:在创建实例对象时自动调用,可以对实例对象进行初始化。
为何要初始化?
我们一个类可以实例化出很多实例对象,每个对象都需要定一下属性。
通过下面的方式进行赋值:
对象1.属性1 = 值1;
对象1.属性2 = 值2;
对象2.属性1 = 值3;
对象2.属性2 = 值4;
当实例化对象很多时会存在大量的重复代码
因此,我们用构造方法对对象进行初始化,在刚开始实例化对象的时候就对其进行赋值操作。
如下:
类名 对象1 = new 类名(值1,值2)
类名 对象2 = new 类名(值3,值4)
简洁而优美
2)构造函数的语法要求:
public 类名([ 参数 ])
{
构造函数代码;
}
注意:
① 构造函数必须用public修饰符,不能用private,public:公开的公共的,在哪儿都能访问。private:私有的,只能在当前类内部进行访问,除了这个类就访问不到了。
② 构造函数是创建对象的特殊函数,函数名和类名一样,没有返回值,连void都不用写。
③ 构造函数可以有参数,new对象的时候传递函数参数即可,当对象被创建的时候(new),构造函数被执行。
④ 构造函数没有返回值,所以不用在函数名前面加void、int、string等返回值类型。
⑤ 构造函数不能被继承,也不能被重写。但子类可以通过base()调用父类的构造函数。
⑥ 构造函数可以重载:我们可以不定义构造函数,这时候系统用自带的空的构造函数。当你写了一个新的构造函数之后,不管是不是无参数的,那个默认的无参数的构造函数都被干掉了,当一个类中有两个以上的构造函数时,可以看做是构造函数的重载。
如下为空构造函数:
public 类名()
{
此处啥也不写
}
我们在实例化对象的时候有两种方式:
① 调用无参数的构造函数实例化对象
People p1 = new People();
② 调用有参数的构造函数实例化对象
People p1 = new People("张三",20,"学生");
3)构造函数练习1:
写一个ticket类,有一个距离属性(本属性只读, 在构造方法中赋值), 不能为负数,有一个价格属性,价格属性只读,并且根据距离distance计算价格price(1元/公里) :
0 - 100公里 票价不打折
101 - 200公里 总额打9.5折
201 - 300公里 总额打9折
300公里以上 总额打8折
//首先写ticket类:
public class Ticket
{
private double distance;
public double Distance
{
get { return distance; }
}
//构造函数
public Ticket(double distance)
{
if (distance < 0)
{
distance = 0;
}
this.distance = distance;
}
private double price;
public double Price
{
get
{
if (distance > 0 && distance <= 100)
{
price = distance * 1.0;
return price;
}
else if (distance >= 101 && distance < 200)
{
price = distance * 0.95;
return price;
}
else if (distance >= 201 && distance < 300)
{
price = distance * 0.9;
return price;
}
else
{
price = distance * 0.8;
return price;
}
}
}
public void ShowTicket()
{
Console.WriteLine("{0}公里需要{1}元", Distance, Price);
}
}
//再在Main函数中调用有参数的构造函数实例化对象:
class Program
{
public static void Main(String[] args)
{
Ticket t = new Ticket(358);
t.ShowTicket();
Console.ReadKey();
}
}
输出结果:
358公里需要286.4元
4)构造函数练习2:编写一个Student类,其中包括4个数据成员:姓名、性别、年龄、所在学校,以及若干成员函数。同时编写主函数使用这个类,实现对学生数据的赋值和输出。
要求:使用成员函数实现对数据的输出(显示学生的相关信息);使用构造函数实现对数据的输入(可以重载多个构造函数并使用)。
//首先在Student类中编写:
public class Student
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string gender;
public string Gender
{
get { return gender; }
set
{
if (value != "男" && value != "女")
{
value = "中";
}
gender = value;
}
}
private int age;
public int Age//设为属性
{
get { return age; }//得到 return返回值
set //设置判断的条件,规定该属性的正确要求
{
if (value < 0 || value > 100)
{
value = 18;
}
age = value;
}
}
private string school;
public string School
{
get { return school; }
set { school = value; }
}
public void ShowStudent()
{
Console.WriteLine("该学生姓名为:{0},性别为{1}生,年龄为{2}岁,学校为{3}", Name, gender, Age, School);
}
public Student(string name, string gender, int age, string school)
{
this.Name = name;
this.Gender = gender;
this.Age = age;
this.School = school;
}
public Student(string name, string gender)
{
this.Name = name;
this.gender = gender;
}
}
//再在Main函数中实例化新的对象:
class Program
{
public static void Main(String[] args)
{
Student s1 = new Student("小丽", "女", 22, "南昌大学");
s1.ShowStudent();
Student s2 = new Student("张三","未知");
s2.ShowStudent();
Console.ReadKey();
}
}
输出结果:
该学生姓名为:小丽,性别为中生,年龄为22岁,学校为南昌大学
该学生姓名为:张三,性别为未知生,年龄为0岁,学校为
12.5 析构函数
1)析构函数简介:
可以定义在垃圾回收程序最终销毁对象之前调用的方法,该方法称为析构函数。当程序结束时,析构函数才执行,帮助我们释放资源,不写析构函数GC(Garbage Collection垃圾回收器)会自动清理释放资源。
什么意思呢?我们平常用的比较多的都是构造函数,因为我们每次new一个对象,程序就会自动调用对象中的构造函数,从而给对象动态的开辟一块内存。但是有没有想过,内存没用完,怎么办,得有回收机制,去回收空闲的内存。
C#有自己的垃圾回收机制。这个的“垃圾”包含空闲内存,不需要程序员手动去释放空间,人家是自动的,但是有一点不好的是!他这个自动是随机的,不是你每次运行程序他就必须回收,他是看心情。
析构函数是在你对象被进行销毁之前被执行的一个函数,也就是在垃圾回收完成之前调用的一个函数,在释放内存之前,你可以写在析构函数中,写你想实现的东西。
2)析构函数语法:
~类名()
{
代码体;
}
注意:析构函数不能有任何参数;析构函数无返回值,无访问修饰符; 析构函数由系统调用;析构函数可不写;程序退出时会调用析构函数。
释放资源GC(Garbage Collection)垃圾回收器。