来自于静态方法和实例方法的联想翩翩

来自:架构师修行之路(微信号:jiagoushixiuxing),作者:菜v菜

这两周没有妹子来找我问问题,有点小伤感,所以耽误更新了。哈哈,别当真,因为菜菜这两周周末都有事(你可以认为去公司加班了),实在是没有精力,忘各位见谅!!

以下为菜菜自己观点,不代表任何妹子的观点,请轻喷

◆◆
面向对象
◆◆


作为一个久经考验并得到业界肯定的编程思想莫过于面向对象编程思想了。

面向对象(Object Oriented,OO)是软件开发方法。面向对象的概念和应用已超越了程序设计和软件开发,扩展到如数据库系统、交互式界面、应用结构、应用平台、分布式系统、网络管理结构、CAD技术、人工智能等领域。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。


谈到面向对象思想,首先你得有一个对象才可以。所以计算机天才在语言角度发挥抽象能力,在编程中把对象抽象创建了出来,典型的代表作就是java/c# 中的类(class)。把每一个class的类型看做现实世界中的一类对象,然后根据class 可以创建出来多个class的实例,把这些实例看做是面向的具体对象。


为了完美的支持面向对象,大多数语言都支持了特性:封装,继承,多态。这也是诸多蛋疼的面试题中的常见题型。


◆◆
应用场景
◆◆


引入实例化方法概念是面向对象概念出现以后的事情了,区分静态方法和实例化方法不能单单从性能上去理解,创建c++,java,c#这样面向对象语言的大师引入实例化方法一定不是要解决什么性能、内存的问题,而是为了让开发更加模式化、面向对象化。这样说的话,静态方法和实例化方式的区分是为了解决模式的问题。


说的白话一点,到底是使用实例方法还是静态方法取决于业务的场景,当你的业务中每个对象都有自己的状态,或者行为,这些状态和行为是只属于当前对象的,那你的行为可以设计成实例方法。

举一个很简单的例子:一个游戏的项目中,每个玩家(player)都有自己的状态,比如玩家有一个行为:跳跃,不同的玩家跳的距离可能不同,所以这个跳跃的行为体现到代码上就是一个player类型实例的方法。


至于静态方法,一般的定义成类型的行为和状态。因为类型是所有实例共享的,所以通常用作全局共享用途。实际项目中会发现有很多的helper类里边都是静态方法,因为这些方法和具体对象,和具体对象的行为状态没有任何关系。因为和具体实例没有连接,所以这类型的静态方法几乎都是线程安全的。

举个很简单的例子:项目中有很多加密的方法,这些方法的作用就是给一个参数,返回一个结果,没有任何自己的状态,所以这些方法被设计成静态方法。


在多数项目中,实例方法的使用量要大于静态方法,为什么呢?因为在多数系统中充斥着各种对象的设计,各种XX设计模式的使用,而这些最终都使用了面向对象的思想。举一个最简单的mvc例子,无论是java中还是c#的 mvc框架,controller中的方法都是实例方法,因为每个http请求都有自己的状态,像header头信息,body信息等,这些状态是属于当前http请求的,所以这些controller必须是实例方法才行。


几乎现代所有的流行编程语言都提供了类型实例的继承和多态,统统都是为了更好的服务面向对象这个理念。为什么不提供类型的继承和多态呢?小伙伴们可以留言!

◆◆
常见问题
◆◆


静态方法是类型的方法,实例方法是每个实例的方法(每个语言形式不太一样):

class Bird
    {

        //静态方法
        static bool IsAnimal()
        
{
            return true;
        }
        //实例方法
        bool IsCanFly()
        
{
            return true;
        }

    }


静态方法比实例方法快?

菜菜认为这是错误的。一个方法的代码被加载到内存中,然后被cpu去执行,执行的速度快慢和是不是静态方法没有任何关系。但是有一个特殊的场景,那就是GC。实例化太多对象在java/c#这类带有GC的编程语言中会引发垃圾回收操作,当垃圾回收进行的时候会挂起所有的线程,所以在这个短暂的时间里,程序会卡顿。

静态方法常驻内存?

在一个类型第一次被使用的时候,会把静态方法和静态变量载入内存,直到进程被销毁。说道常驻内存,也算是一种误解,正确的说法是只有在被使用之后才会加载进入内存。当然在一些语言中可以手动卸载当前类型。

静态方法没有线程安全问题

菜菜认为是错的。有没有线程安全问题不是是不是静态所决定的,一个类型也可以有自己的状态和行为,只不过在一个进程中只有一份而已。当一个类型中的状态被多个线程修改的时候,就会有资源竞争问题,就会有线程安全问题。当一个类型的状态只有读的情况下,可以认为读这个方法是线程安全的。 自己运行一下以下程序的结果

class Program
    {

        static void Main(string[] args)
        
{
            for (int i = 0; i < 20; i++)
            {
                Thread t = new Thread(() => {
                    for (int i2 = 0; i2 < 100000; i2++)
                    {
                        Add();
                    }

                });
                t.Start();
            }
            //为了模拟程序一直运行
            while (true)
            {
                Console.WriteLine($"Num的值:"+Num);
                Thread.Sleep(1000);
            }

            Console.Read();
        }

        public static int Num;
        public static void Add()
        
{
            Num= Num+1;
        }
    }


至于实例方法的线程安全问题,原理类似。有没有线程安全问题取决于状态有没有被多个线程并发修改,有没有资源竞争,和是否静态完全没关系。

THE END

推荐↓↓↓
Java编程
上一篇:Spring中如何使用设计模式 下一篇:整理了 15 道 Spring Boot 高频面试题,看完当面霸!