Day06
交错数组
- 元素为数组的数组,每个元素都是一个新的一位数组。
- 语法:
-- 定义
数据类型[[]数组名= new 数据类型[元素总数][];
stringlil] array = new string[3][1];
--赋值
数组名[索引]= new 数据类型[子元素数];
array[0]= new string[2];
--读写元素
数组名[元素索引][子元素索引]
输出一个交错数组
static void Main(string[] args)
{
//[行,列]
int[,] arr = null; // null;
arr = new int[5, 3];
arr[0, 2] = 100;
//交错数组 参数数组
int[][] array02;//null
//创建具有4个元素的交错数组
array02 = new int[4][];
//创建一维数组 赋值给 交错数组的第一个元素
array02[0] = new int[3];
array02[1] = new int[5];
array02[2] = new int[4];
array02[3] = new int[1];
// 将数据1赋值给交错数组的第一个元素 的 第一个元素
array02[0][0] = 1;
array02[1][2] = 2;
array02[1][4] = 3;
array02[2][2] = 4;
array02[2][3] = 5;
//遍历数组里面所有数
/*foreach (int[] array in array02)
{
foreach (int element in array)
{
Console.WriteLine(element);
}
}*/
//数据类型 变量名
//int a;
//int[] arr;
//array02.Length 交错数组元素数(理解为 :行数)
for (int r = 0; r < array02.Length; r++)
{
for (int c = 0; c < array02[r].Length; c++)
{
Console.Write(array02[r][c] + "\t");
}
Console.WriteLine();
}
}
参数数组——params
- 在方法形参中通过关键字params 定义。
- 方法调用者可以传递数组,也可以传递一组数据类型相同的变量,甚至可以不传递参数。
- 注意:
参数数组必须在形参列表中的最后一位。只能在一维数组上使用params 关键字。 - WriteLine中使用占位符,就是通过参数数组实现的。
使用参数数组定义整数相加的方法
- 当类型确定个数不确定的情形使用参数数组。
- params 参数数组
- 对于方法内部而言:就是个普通数组
对于方法外部(调用者)而言:可以传递数组,传递一组数据类型相同的变量集合,可以不传递参数。 - 作用 :简化调用者调用方法的代码、
static void Main2()
{
int result01 = Add(new int[] {
1, 34, 43, 54, 78 });
int result02 = Add(1, 34, 43, 54 ,78);
//Console.WriteLine("{0}{1}{2}"), new object[] { 1, 2, 3, 4, 5 }
Console.WriteLine("{0}{1}{2}", 1, 2, 3, 4, 5);
}
private static int Add(params int[] arr)
{
int sum = 0;
foreach (var item in arr)
{
sum += item;
}
return sum;
}
数据类型
- 值类型:存数据本身。int bool char(比较小的)
- 引用类型:存引用(内存地址)。string Array(int[])(比较大的)——如果是引用类型不需要返回值,传递的是地址
- 方法内部声明的变量都在栈上(不管是值类型还是引用类型)
引用传进来,修改堆中数组对象。
private static void OrderBy(int[] array)//不需要返回值的
private static int OrderBy(int[] array)//不需要返回值的
//array = newArray; 替换引用 方法外不会受到影响
//array[o]= newArray[o]; 通过引用修改堆中数组对象(如果有修改堆中的数据的代码改void就不用添加代码了)
- 方法执行在栈中 ,所以在方法中声明的变量都在栈中
- 因为值类型直接存储数据,所以数据存储在栈中
arr改的是栈里面的数据
arr[]改的是堆里面的数据
局部变量
- 定义在方法内部的变量。
- 特 点 :
-没有默认值,必须自行设定初始值,否则不能使用。
-方法被调用时,存在栈中,方法调用结束时从栈中清除。
值类型和引用类型(局部变量)
- 值类型:
声明在栈中,数据存储在栈中。 - 引用类型:
声明在栈中,数据存储在堆中,栈中存储该数据的引用。
分配图
垃圾回收器
- GC(Garbage Collection)是CLR 中一种针对托管堆自动回收释放内存的服务。
- GC线程从栈中的引用开始跟踪,从而判定哪些内存是正在 使用的,若GC 无法跟踪到某一块堆内存,那么就认为这块内存不再使用了,即为可回收的。
成员变量
- 定义在类中方法外的变量
- 特 点 :
– 具有默认值。
– 所在类被实例化后,存在堆中,对象被回收时,成员变量从堆中清除。
– 可以与局部变量重名。
值类型与引用类型(成员变量)
- 值类型:
声明在堆中,数据存储在堆中。 - 引用类型:
声明在堆中,数据存储在堆的另一块空间。
分配图
应用
(1)
static void Main()
{
//a在栈中 1在栈中
int a = 1;
int b = a;
a = 2;
Console.WriteLine(b);//?
//arr在栈中存储数组对象的引用(内存地址) 1在堆中
int[] arr = new int[] {
1 };
int[] arr2 = arr;
arr[0] = 2;//修改的是堆中的数据
Console.WriteLine(arr2[0]);//?
}
(2)
int[] arr = new int[] {
1 };
int[] arr2 = arr;
arr[0] = 2;//修改的是堆中的数据
arr = new int[] {
2 };//修改的是栈中存储的应用
Console.WriteLine(arr2[0]);//?
(3)
string s1 = "男";
string s2 = s1;
s1 = "女";//修改的是栈中存储的引用
//s1[0] =女
//堆中的文字 不能修改
Console.WriteLine(s2);
(4)
static void Main()
{
int a = 1;
int[] arr = new int[] {
1 };
Fun1(a, arr);//实参将 数据1. 数据引用 赋值给形参
Console.WriteLine(a);//?
Console.WriteLine(arr[O]);//?
}
private static void Fun1(int a, int[] arr)
{
a = 2;
arr[0] = 2;
}
(5)
int num01 = 1, num02 = 1;
bool r1 = num01 == num02;//?true 因为值类型存储的是数据,所以比较的是数据
int[] arr01 = new int[] {
1 }, arr02 = new int[] {
1 };
bool r2 = arr01 == arr02;//?false 因为引用类型存储的是数据的引用,所以比较的是存储的引用
bool r3 = arr01[0] == arr02[0];//这种比较的才是数据
值参数
- 值参数: 按值传递 - 传递实参变量存储的内容**
- 作用:传递数据(信息)
private static void Fun2(int a,int[] arr)
{
a = 2;
//arr[0] = 2;
//arr = new int[] { 2 };
}
引用参数
- 使用ref关键字修饰。
- 引用参数 : 按引用传递 – 传递实参变量自身的内存地址(调用方法时复制实参变量在栈中的引用。)
- 作用:改变数据
- 语法:
…… (ref数据类型参数名[,ref 数据类型 参数名])
private static void Fun3(ref int a)
{
//方法内部修改引用参数 实质上就是在修改实参变量
a = 2;
}
输出参数
- 使用out关键字修饰
- 输出参数: 按引用传递 传递实参变量自身的内存地址
- 语 法 :
… …(out 数据类型参数名[,out 数据类型 参数名])
private static void Fun4(out int a)
{
a = 2;
}
引用参数(ref)和输出参数(out)的区别
- 区别1: 方法内部必须为输出参数赋值
- 区别2 : 输出参数传递之前可以不赋值
(ref要求实参必须在传递前进行赋值,
out 要求形参离开方法前必须赋值。) - 实际上就是直接在实参的内存上修改,不像值传递将实参的值拷贝到另外的内存地址中才修改。
TryParse
- TryParse方法,返回2个结果
- 通 过bool表达式配合TryParse使用,防止类型转换失败。
static void Main()
{
int num01 = 100, num02 = 200;
Swop(ref num01, ref num02);
int area;
int perimeter;
CalculateRect(15, 20, out area, out perimeter);
//int number = int.Parse("250+");
int result;
//TryParse方法,返回2个结果
//out : 转换后的结果
//返回值 : 是否可以转换
//将string类型转化成int类型,若转化成功,将值赋给num1,并返回true;若转化失败,返回false。
bool re = int.TryParse("250+", out result);
}
练习1:定义 两个整数交换的方法
private static void Swop(ref int one, ref int two)
{
int temp = one;
one = two;
two = temp;
}
练习2 : 根据矩形长、宽计算面积(长* 宽)与周长( (长+宽)*2
private static void CalculateRect(int lenght, int width, out int area, out int perimeter)
{
area = lenght * width;
perimeter = (lenght + width) * 2;
}
拆装箱
装箱操作(box):“比较”消耗性能(最)
- 值类型隐式转换为 object 类型或由此值类型实现的任何接口类型的过程。
- 内部机制:
1.在堆中开辟内存空间
2.将值类型的数据复制到堆中
3.返回堆中新分配对象的地址
int a = 1;
object o = a;
//比这种
/*int a = 1;
int b = a;*/
拆箱操作(unbox):“比较”消耗性能
- 从object 类型到值类型或从接口类型到实现该接口的值类
型的显式转换。 - 内部机制:
1.判断给定类型是否是装箱时的类型
2.返回已装箱实例中属于原值类型字段的地址
int b = (int)o;
字符串和int直接相加就是一次装箱操作
//1
int num = 100;
string str = num.ToString();//未装箱
//2
string str02 = "" + num;
//上面那句话内部执行代码;string str02 = string.Concat(.,num) //int==>object
形参object类型,实参传递值类型,则装箱
可以通过 重载、泛型 避免。
注意
- 拆箱后的类型必须与装箱时的类型相同。
- 伴随拆箱的字段复制步骤不属于拆箱过程。
- 装箱和拆箱不是互逆的过程,装箱的性能开销远大于拆箱。
string
- 字符串池
字符串常量在创建前,首先在字符串池中查找是否存在相同文本。如果存在,则直接返回该对象引用;如果不存在,则开辟空间存储。
目的:提高内存利用率。
string s1 = "八戒";
string s2 = "八戒";//同一个数据地址,同一个字符串
string s3 = new string(new char[] {
'八', '戒' });
string s4 = new string(new char[] {
'八', '戒' });
bool r1 = object.ReferenceEquals(s3, s4);//不同数据地址,不同字符串
- 字符串的不可变性
字符串常量一旦进入内存,就不得再次改变。因为如果在 原位置改变会使其他对象内存被破坏,导致内存泄漏。当遇到 字符串变量引用新值时,会在内存中新建一个字符串,将该字符串地址交由该变量引用。
重新开辟空间 存储 新字符串,再替换栈中引用
s1 = "悟空";
Console.WriteLine(s1);
//将文本“八戒”改为“悟空"//?NO
//而是重新开辟空间 存储 新字符串,再替换栈中引用
每次修改,都是重新开辟新的空间存储数据,替换栈中引用
- 例子:尽量避免这种情况
string strNumber = "";
for (int i = 0; i < 10; i++)
{
//第一次循环:""+"0"(空加0)
//"0"+"1"每次拼接产生新的对象 替换引用(原有数据成为一个垃圾)
strNumber = strNumber + i.ToString();
}
Console.WriteLine(strNumber);//? 0123456789
可变字符串:StringBuilder
(结果跟上面的一样)
- 一次开辟可以容纳10个字符大小的空间
- 优点:可以在原有空间修改字符串,避免产生垃圾(但是不是在原有的上面扩,而是新开辟了一个空间将所有东西拷贝进去再)
- 适用性:频繁对字符串操作(增加 替换 移除)
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 10; i++)
{
builder.Append(i);
}
string result = builder.ToString();
每日一练:
- 单词反转 How are you ==》 you are How
- 字符反转 How are you ==》 uoy era woH
- 查找指定字符串中不重复出现的文字(重复的文字保留1个)