文章目录
🚴大家好!我是近视的脚踏实地,此篇文章纯学习笔记。
唯有行动 才能解除你所有的不安
1、概述
1.1、问题引入
如果某个类型的所有对象,都具有一个相同的属性值,那么这个属性值就没有必要在所有对象中,都存储一份,因为比较浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象,那应该如何去解决这个问题?
当在定义类的时候,类中都会有相应的属性和方法。而属性和方法都是创建本类对象调用的。当在调用对象某个方法时,这个方法没有访问到对象的特有数据(属性–成员变量)时,创建这个对象就有点多余。可以不创建对象,方法又调用不了,所以就会有这么一个需求,能不能不创建对象也能调用方法。
不使用static关键字
Student.class 👇:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StaticLearnDemo.Model
{
public class Student
{
private int id { get; set; }
private string name { get; set; }
private int age { get; set; }
//假设每个班上每个学生都是国籍都是中国
//当一个类的所有对象中某个属性值都相同时,为了节省空间就可以把这个属性修饰成static静态的
public string country = "中国";
public Student()
{
}
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
}
}
}
TestStudentBll.class 👇:
using StaticLearnDemo.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StaticLearnDemo
{
public class TestStudentBll
{
public static void Main(string[] args)
{
Student stu1 = new Student(1, "张三", 20);
Student stu2 = new Student(2, "李四", 21);
}
}
}
此时的内存图如下👇:
可以看到这样做之后,每次创建一个对象,堆中每个对象的id,name,age都不一样,但是country ="中国"存的都是一样,当然可能觉得中国在类中直接赋值了,但是即使没有直接赋值,后边初始化对象时,给country 赋值还是赋值中国(不考虑留学生),这样就会重复存储,浪费空间。所以就想想能不能把country = "中国"单独存放在一个位置,然后这些对象指向它的地址就好了,这样就只需要存一份
使用static关键字
此时的内存图如下👇:
Main()方法执行时,第一次使用Student类,会把Student这个类加载到方法区 ,然后肯定会有id、name、age这三个属性,但是此时是没有值的,有也是默认值;同时还会有把sayHello() 和 study()方法加载到方法区。那么在方法区中还有一块区域叫做静态区,静态的变量,静态的方法都是存在静态区的。
当实例化student对象时,会在堆给id、name、age三个属性赋值,然后country则是直接指向静态区的country = “中国”,这样就实现了多个不同的student对象,共享一个country = "中国"变量,节省了内存空间
1.2、static 作用范围简介
static 关键字可以用于成员变量、方法、代码块和类。
static 修饰的变量称为静态变量
static 修饰的方法称为静态方法
static 修饰的初始化代码块,称为静态初始代码块
static 修饰类,静态内部类
2、静态变量
2.1、静态变量的调用
定义:被static修饰的变量就叫静态变量
解释:静态变量,在方法区中的静态区中只存一份,所有对象共享,且静态变量不属于任何一个对象,只能通过类名去访问
错误👇:
正确👇:
2.2、静态变量的特点
静态的解释:静态的、静止的。静态变量不会随着对象的变化而变化
加载时机:
随着类的加载而加载
静态变量随着类的加载进入方法区,就直接在静态区给开辟了存储静态变量的内存空间
静态变量优先于对象而存在(静态变量)
代码层面:需要使用类名来访问
2.3、静态变量和非静态变量(成员变量)的区别
1、概念上,所属不同:
- 非静态变量属于对象
- 静态变量属于类,类变量
2、内存空间不同,存储位置不同
- 非静态变量属于对象,所以存储在堆内存中
- 静态变量属于类,存储在方法区的静态区中
3、内存时间不同,生命周期不同
- 非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失
- 静态变量属于类,所以生命周期和类相同,随着类的加载(创建对象、类名访问静态变量、类名访问静态方法、反射的方式、加载子类、运行某个测试类)而存在,随着类的消失(内存管理)而消失
静态变量在内存中的存活时间更长,只要类一加载,静态变量就会分配内存了。而成员变量是对象创建分配了堆区的空间以后,才会给成员变量分配内存。
而内存回收时,也是对象先会被回收,当类的所有对象都被卸载后,方法区中分配的内存才会被卸载
所以静态变量出现更早,销毁更晚,存活时间更长,所以说静态变量真的节省空间吗?虽然节省了一些内存的分配,但是占的时间更长了
所以开发中很少会使用静态变量,除非一个类中所有对象的变量值都是同一个值的时候,才适合用静态变量
4、访问方式不同
-
非静态变量只能使用对象名访问
-
静态变量只能使用类名访问(Java既可以使用对象访问,也可以通过类名访问)
3、静态方法
3.1、概述
当static修饰成员方法时,该方法称为类方法或者静态方法,静态方法在使用时可以直接使用类名来调用,不需要创建类的对象
格式👇:
修饰符 static 返回类型 方法名(参数列表){
//执行语句
}
3.2、静态方法中的注意事项
1、静态方法中不能访问类的非静态成员(成员变量、成员方法)
错误1(静态方法中使用非静态成员)👇:
错误2(静态方法中使用非静态方法)👇:
原因:因为静态方法是属于类的,可以通过类名直接调用,而非静态成员(成员变量、成员方法)是属于对象的。生命周期比类短,当通过类名直接调用静态方法时,可能此时对象并没有初始化,静态方法中有调用非静态字段,就不知道这个非静态字段是属于哪一个对象,因为可能对象都没实例化、非静态成语都没有分配内存空间
2、静态方法中不能使用this关键字
3、非静态方法中可以访问静态成员(静态变量和静态方法)
原因:因为静态成员的生命周期比非静态成员的生命长,且静态成员内存分配比非静态成员早,所以非静态成员存在时,静态成员肯定是已经存在了的,所以非静态成员可以访问静态成员
3.3、使用静态变量和静态方法的时机
静态方法和静态变量调用起来似乎非常简单,那为何不把所有方法都设置为static静态的呢?,当然不行,因为静态变量生命周期太长了,一直会占用内存。所以使用静态变量和静态方法要看时机
1、静态变量:当一个类的所有对象中的某个属性的值都相同时,应该把成员变量修饰成静态变量来节省内存空间
2、静态方法:一般工具类中的方法都是静态方法,即当某个类不需要创建对象就能使用,这样类的就是工具类。(类中根本没有成员变量,这样方法,一般这样的类就是工具类)
比如Math这个类就是工具类👇:
4、静态代码块
静态代码块中的代码在类加载时只会执行一次,调用是发生在构造方法执行之前
static Student()
{
Console.WriteLine("静态代码块执行.....");
}
/// <summary>
/// 有参构造
/// </summary>
/// <param name="id"></param>
/// <param name="name"></param>
/// <param name="age"></param>
public Student(int id, string name, int age)
{
this.id = id;
this.name = name;
this.age = age;
Console.WriteLine("构造方法执行.......");
}
public class TestStudentBll
{
public static void Main(string[] args)
{
Student stu1 = new Student(1, "张三", 20);
Console.WriteLine(stu1.name + " 实例化成功");
Student stu2 = new Student(2, "李四", 21);
Console.WriteLine(stu2.name + " 实例化成功");
Student stu3 = new Student(3, "王五", 21);
Console.WriteLine(stu3.name + " 实例化成功");
Console.ReadKey();
}
}
5、静态类
静态类是一种声明为 static 类型的,且仅包含静态成员的类。不能使用 new 关键字创建静态类的实例。静态类在加载包含该类的程序或命名空间时由 .NET Framework 公共语言运行库 (CLR) 自动加载。 当类中没有依赖对象标识的数据或行为时,就可以使用静态类。
使用静态类来包含不与特定对象关联的方法。例如,创建一组不操作实例数据并且不与代码中的特定对象关联的方法是很常见的要求。您应该使用静态类来包含那些方法。
静态类的主要特点如下:
- 仅包含静态成员。
- 不能被实例化。
- 它们是密封的。
- 它们不能包含实例构造函数。
因此创建静态类与创建仅包含静态成员和私有构造函数的类大致一样。私有构造函数阻止类被实例化。
静态类的优点:
- 写在栈中,安全高速稳定,而且在执行的时候,十分优先
- 编译器能够执行检查以确保不致偶然地添加实例成员
- 编译器将保证不会创建此类的实例
尽量少用静态类。大多数情况下,创建类的实例是一个非常非常廉价的操作,因此速度不是问题。