1. 创建对象
终于讲到对象的创建啦。其实你之前肯定见过,只不过这次我们详细聊聊。其实创建对象真的很简单。
要创建对象,你得用 new 操作符。一般来说,你会创建一个类的新实例(对象),然后直接把它赋值给同类型的变量。
类名 变量名 = new 类名(参数);
这里 类名 是对象的类型,变量名 是变量的名字(通常小写,比如 cat),= 右边就是创建这个类的新对象。
例子:
| 代码 | 说明 |
|---|---|
|
创建一个 object 类型的对象 |
|
创建一个 Cat 类型的对象 |
|
创建一个带参数的 StreamReader 对象 |
程序员经常会把变量名和类名写得一样,只不过变量名首字母小写。新手看到这种代码可能会有点懵:
| 代码 |
|---|
|
|
|
其实这种写法完全没问题——就是声明一个变量,然后直接用同类型的对象初始化。
左边是变量声明,右边是用 new 在内存里创建新对象。就这么简单!
2. 构造函数
你应该经常看到,创建对象的时候会传一些参数。有的对象要参数,有的又不用。这到底是怎么回事?
其实也很简单:每个类都有一个(或多个)专门用来处理创建对象时参数的方法。这种方法叫 构造函数。单个方法就叫 构造函数。
构造函数和普通方法还是挺好区分的。它有两个特点:
- 构造函数的名字和类名一样(而且首字母大写)
- 构造函数没有返回值类型(连 void 都不写)。
一般长这样:
修饰符 类名(参数)
{
构造函数代码
}
构造函数声明例子:
public class Point
{
public int X;
public int Y;
public Point(int x, int y) // 构造函数
{
X = x;
Y = y;
}
}
用法:
Point p = new Point(5, 10); // 调用构造函数
用 new Point(5, 10) 创建对象时,会调用带两个参数的构造函数,初始化 X 和 Y 字段。
有时候构造函数的参数名和字段名一样——这样写方便。名字冲突时用 关键字 this 解决:
public Point(int x, int y)
{
this.x = x; // this.x 是对象字段,x 是构造函数参数
this.y = y;
}
3. 构造函数的调用
当你写 new 类名(参数) 时,C# 会:
- 在内存里创建新对象,
- 调用合适的构造函数,把你的参数传进去。
以猫类为例:
public class Cat
{
public string Name;
public int Age;
public Cat(string name, int age)
{
Name = name;
Age = age;
}
}
用法:
Cat cat = new Cat("瓦斯卡", 2); // 可以:会调用带两个参数的构造函数
Cat cat2 = new Cat("瓦斯卡"); // 不行:没有这种构造函数——编译报错
Cat cat3 = new Cat(); // 不行:没有无参数构造函数——编译报错
如果类只有带参数的构造函数,那你创建对象时必须给它传参数。
4. 多个构造函数
不过你也可以在类里写多个构造函数。构造函数数量和参数都不限。创建对象时,编译器会自动选参数最合适的那个构造函数。
例子:
public class Cat
{
public const int UNKNOWN = -1;
public string Name;
public int Age;
public Cat(string name, int age)
{
Name = name;
Age = age;
}
public Cat(string name)
{
Name = name;
Age = UNKNOWN; // 年龄未知时的默认值
}
}
用法:
Cat cat1 = new Cat("瓦斯卡", 2); // 用第一个构造函数
Cat cat2 = new Cat("穆尔卡"); // 用第二个构造函数
Cat cat3 = new Cat(); // 报错!没有无参数构造函数
我们考虑到有时候猫的年龄可能不知道。为这种情况加了个常量 UNKNOWN,还有只带名字参数的构造函数。
注意我们在两个构造函数里都初始化了所有字段。缺的参数就用 UNKNOWN 常量补上。
5. 默认构造函数
如果你的类里一个构造函数都没写,编译器会自动加一个无参数的空构造函数:
public ClassName()
{
}
但如果你自己写了至少一个构造函数——默认的空构造函数就不会自动加了。
例子1:
public class Cat
{
public string Name;
public int Age;
}
// 可以:
Cat cat = new Cat(); // 会调用默认的无参数构造函数
例子2:
public class Cat
{
public string Name;
public int Age;
public Cat(string name) { Name = name; }
}
// 不可以:
Cat cat = new Cat(); // 报错:没有无参数构造函数了!
6. 变量初始化
在C#里,类变量可以在声明时直接初始化,也可以在构造函数里初始化。
例子:
public class Cat
{
public string Name;
public int Age = -1; // 初始值
public Cat(string name, int age)
{
Name = name;
Age = age; // 覆盖初始值
}
public Cat()
{
Name = "无名";
// Age 还是 -1(初始值)
}
}
说明:
- 用 new Cat("瓦斯卡", 2) 创建时,Age 先是 -1,然后构造函数把它改成 2。
- 用 new Cat() 创建时,Age 还是 -1。
7. 变量初始化顺序
新对象的数据初始化顺序如下:
- 类字段先拿到默认值(0 对于 int,null 对于引用类型等等)。
- 然后执行你在字段声明时写的初始化器,按代码里出现的顺序来。
- 最后才执行构造函数里的代码。
变量是在构造函数之前、按声明顺序初始化的。所以字段之间有依赖关系时要小心。
错误例子
非静态字段不能用其他非静态变量初始化:
public class Solution
{
public int a = 1;
public int b = a; // 不能用 a
public int c = a + b; // 不能用 a 和 b
}
// 这种代码会编译报错!
正确例子
对于静态变量 就没问题:
public class Solution
{
public static int a = 1; // 明确设置 a = 1
public static int b = a + 2; // b = 1 + 2 ⇒ 3
public static int c = a + b + 3; // c = 1 + 3 + 3 ⇒ 7
public Solution()
{
Console.WriteLine($"a = {a}, b = {b}, c = {c}");
// 输出:a = 1, b = 3, c = 7 —— 就是你想要的
}
}
正确例子2
静态字段甚至可以这样写:
public class Solution
{
public static int a = b + c + 1; // b 和 c 还都是 0
public static int b = a + c + 2; // a == 1, c 还是 0
public static int c = a + b + 3; // a == 1, b == 3
}
GO TO FULL VERSION