面试中的Singleton

Java - 面试题 - Singleton Java - 面试题 - Singleton 文章目录Java - 面试题 - Singleton一、什么是Singleton?二、要点三、几种常见形式四、代码五、小结 一、什么是Singleton? Singleton:在Java中即指单例设计模式,它是软件开发中最常用的设计模式之一。 单:唯一 例:实例 单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式。 例如:代表JVM运行环境的Runtime类 二、要点 一是某个类只能有一个实例; 构造器私有化 二是它必须自行创建这个 阅读详情

转自:http://www.cnblogs.com/loveis715/archive/2012/07/18/2598409.html

引子

  “请写一个Singleton。”面试官微笑着和我说。

  “这可真简单。”我心里想着,并在白板上写下了下面的Singleton实现:

复制代码
 1 class Singleton
 2 {
 3 public:
 4     static Singleton& Instance()
 5     {
 6         static Singleton singleton;
 7         return singleton;
 8     }
 9 
10 private:
11     Singleton() { };
12 };
复制代码

  “那请你讲解一下该实现的各组成。”面试官的脸上仍然带着微笑。

  “首先要说的就是Singleton的构造函数。由于Singleton限制其类型实例有且只能有一个,因此我们应通过将构造函数设置为非公有来保证其不会被用户代码随意创建。而在类型实例访问函数中,我们通过局部静态变量达到实例仅有一个的要求。另外,通过该静态变量,我们可以将该实例的创建延迟到实例访问函数被调用时才执行,以提高程序的启动速度。”

保护

  “说得不错,而且更可贵的是你能注意到对构造函数进行保护。毕竟中间件代码需要非常严谨才能防止用户代码的误用。那么,除了构造函数以外,我们还需要对哪些组成进行保护?”

  “还需要保护的有拷贝构造函数,析构函数以及赋值运算符。或许,我们还需要考虑取址运算符。这是因为编译器会在需要的时候为这些成员创建一个默认的实现。”

  “那你能详细说一下编译器会在什么情况下创建默认实现,以及创建这些默认实现的原因吗?”面试官继续问道。

  “在这些成员没有被声明的情况下,编译器将使用一系列默认行为:对实例的构造就是分配一部分内存,而不对该部分内存做任何事情;对实例的拷贝也仅仅是将原实例中的内存按位拷贝到新实例中;而赋值运算符也是对类型实例所拥有的各信息进行拷贝。而在某些情况下,这些默认行为不再满足条件,那么编译器将尝试根据已有信息创建这些成员的默认实现。这些影响因素可以分为几种:类型所提供的相应成员,类型中的虚函数以及类型的虚基类。”

  “就以构造函数为例,如果当前类型的成员或基类提供了由用户定义的构造函数,那么仅进行内存拷贝可能已经不是正确的行为。这是因为该成员的构造函数可能包含了成员初始化,成员函数调用等众多执行逻辑。此时编译器就需要为这个类型生成一个默认构造函数,以执行对成员或基类构造函数的调用。另外,如果一个类型声明了一个虚函数,那么编译器仍需要生成一个构造函数,以初始化指向该虚函数表的指针。如果一个类型的各个派生类中拥有一个虚基类,那么编译器同样需要生成构造函数,以初始化该虚基类的位置。这些情况同样需要在拷贝构造函数中考虑:如果一个类型的成员变量拥有一个拷贝构造函数,或者其基类拥有一个拷贝构造函数,位拷贝就不再满足要求了,因为拷贝构造函数内可能执行了某些并不是位拷贝的逻辑。同时如果一个类型声明了虚函数,拷贝构造函数需要根据目标类型初始化虚函数表指针。如基类实例经过拷贝后,其虚函数表指针不应指向派生类的虚函数表。同理,如果一个类型的各个派生类中拥有一个虚派生,那么编译器也应为其生成拷贝构造函数,以正确设置各个虚基类的偏移。”

  “当然,析构函数的情况则略为简单一些:只需要调用其成员的析构函数以及基类的析构函数即可,而不需要再考虑对虚基类偏移的设置及虚函数表指针的设置。”

  “在这些默认实现中,类型实例的各个原生类型成员并没有得到初始化的机会。但是这一般被认为是软件开发人员的责任,而不是编译器的责任。”说完这些,我长出一口气,心里也暗自庆幸曾经研究过该部分内容。

  “你刚才提到需要考虑保护取址运算符,是吗?我想知道。”

  “好的。首先要声明的是,几乎所有的人都会认为对取址运算符的重载是邪恶的。甚至说,boost为了防止该行为所产生的错误更是提供了addressof()函数。而另一方面,我们需要讨论用户为什么要用取址运算符。Singleton所返回的常常是一个引用,对引用进行取址将得到相应类型的指针。而从语法上来说,引用和指针的最大区别在于是否可以被delete关键字删除以及是否可以为NULL。但是Singleton返回一个引用也就表示其生存期由非用户代码所管理。因此使用取址运算符获得指针后又用delete关键字删除Singleton所返回的实例明显是一个用户错误。综上所述,通过将取址运算符设置为私有没有多少意义。”

重用

  “好的,现在我们换个话题。如果我现在有几个类型都需要实现为Singleton,那我应怎样使用你所编写的这段代码呢?”

  刚刚还在洋洋自得的我恍然大悟:这个Singleton实现是无法重用的。没办法,只好一边想一边说:“一般来说,较为流行的重用方法一共有三种:组合、派生以及模板。首先可以想到的是,对Singleton的重用仅仅是对Instance()函数的重用,因此通过从Singleton派生以继承该函数的实现是一个很好的选择。而Instance()函数如果能根据实际类型更改返回类型则更好了。因此奇异递归模板(CRTP,The Curiously Recurring Template Pattern)模式则是一个非常好的选择。”于是我在白板上飞快地写下了下面的代码:

复制代码
 1 template <typename T>
 2 class Singleton
 3 {
 4 public:
 5     static T& Instance()
 6     {
 7         static T s_Instance;
 8         return s_Instance;
 9     }
10 
11 protected:
12     Singleton(void) {}
13     ~Singleton(void) {}
14 
15 private:
16     Singleton(const Singleton& rhs) {}
17     Singleton& operator = (const Singleton& rhs) {}
18 };
复制代码

  同时我也在白板上写下了对该Singleton实现进行重用的方法:

1 class SingletonInstance : public Singleton<SingletonInstance>…

  “在需要重用该Singleton实现时,我们仅仅需要从Singleton派生并将Singleton的泛型参数设置为该类型即可。”

生存期管理

  “我看你在实现中使用了静态变量,那你是否能介绍一下上面Singleton实现中有关生存期的一些特征吗?毕竟生存期管理也是编程中的一个重要话题。”面试官提出了下一个问题。

  “嗯,让我想一想。我认为对Singleton的生存期特性的讨论需要分为两个方面:Singleton内使用的静态变量的生存期以及Singleton外在用户代码中所表现的生存期。Singleton内使用的静态变量是一个局部静态变量,因此只有在Singleton的Instance()函数被调用时其才会被创建,从而拥有了延迟初始化(Lazy)的效果,提高了程序的启动性能。同时该实例将生存至程序执行完毕。而就Singleton的用户代码而言,其生存期贯穿于整个程序生命周期,从程序启动开始直到程序执行完毕。当然,Singleton在生存期上的一个缺陷就是创建和析构时的不确定性。由于Singleton实例会在Instance()函数被访问时被创建,因此在某处新添加的一处对Singleton的访问将可能导致Singleton的生存期发生变化。如果其依赖于其它组成,如另一个Singleton,那么对它们的生存期进行管理将成为一个灾难。甚至可以说,还不如不用Singleton,而使用明确的实例生存期管理。”

  “很好,你能提到程序初始化及关闭时单件的构造及析构顺序的不确定可能导致致命的错误这一情况。可以说,这是通过局部静态变量实现Singleton的一个重要缺点。而对于你所提到的多个Singleton之间相互关联所导致的生存期管理问题,你是否有解决该问题的方法呢?”

  我突然间意识到自己给自己出了一个难题:“有,我们可以将Singleton的实现更改为使用全局静态变量,并将这些全局静态变量在文件中按照特定顺序排序即可。”

  “但是这样的话,静态变量将使用eager initialization的方式完成初始化,可能会对性能影响较大。其实,我想听你说的是,对于具有关联的两个Singleton,对它们进行使用的代码常常局限在同一区域内。该问题的一个解决方法常常是将对它们进行使用的管理逻辑实现为Singleton,而在内部逻辑中对它们进行明确的生存期管理。但不用担心,因为这个答案也过于经验之谈。那么下一个问题,你既然提到了全局静态变量能解决这个问题,那是否可以讲解一下全局静态变量的生命周期是怎样的呢?”

  “编译器会在程序的main()函数执行之前插入一段代码,用来初始化全局变量。当然,静态变量也包含在内。该过程被称为静态初始化。”

  “嗯,很好。使用全局静态变量实现Singleton的确会对性能造成一定影响。但是你是否注意到它也有一定的优点呢?”

  见我许久没有回答,面试官主动帮我解了围:“是线程安全性。由于在静态初始化时用户代码还没有来得及执行,因此其常常处于单线程环境下,从而保证了Singleton真的只有一个实例。当然,这并不是一个好的解决方法。所以,我们来谈谈Singleton的多线程实现吧。”

多线程

  “首先请你写一个线程安全的Singleton实现。”

  我拿起笔,在白板上写下早已烂熟于心的多线程安全实现:

复制代码
 1 template <typename T>
 2 class Singleton
 3 {
 4 public:
 5     static T& Instance()
 6     {
 7         if (m_pInstance == NULL)
 8         {
 9             Lock lock;
10             if (m_pInstance == NULL)
11             {
12                 m_pInstance = new T();
13                 atexit(Destroy);
14             }
15             return *m_pInstance;
16         }
17         return *m_pInstance;
18     }
19 
20 protected:
21     Singleton(void) {}
22     ~Singleton(void) {}
23 
24 private:
25     Singleton(const Singleton& rhs) {}
26     Singleton& operator = (const Singleton& rhs) {}
27 
28     void Destroy()
29     {
30         if (m_pInstance != NULL)
31             delete m_pInstance;
32         m_pInstance = NULL;
33     }
34 
35     static T* volatile m_pInstance;
36 };
37 
38 template <typename T>
39 T* Singleton<T>::m_pInstance = NULL;
复制代码

  “写得很精彩。那你是否能逐行讲解一下你写的这个Singleton实现呢?”

  “好的。首先,我使用了一个指针记录创建的Singleton实例,而不再是局部静态变量。这是因为局部静态变量可能在多线程环境下出现问题。”

  “我想插一句话,为什么局部静态变量会在多线程环境下出现问题?”

  “这是由局部静态变量的实际实现所决定的。为了能满足局部静态变量只被初始化一次的需求,很多编译器会通过一个全局的标志位记录该静态变量是否已经被初始化的信息。那么,对静态变量进行初始化的伪码就变成下面这个样子:”。

1 bool flag = false;
2 if (!flag)
3 {
4     flag = true;
5     staticVar = initStatic();
6 }

  “那么在第一个线程执行完对flag的检查并进入if分支后,第二个线程将可能被启动,从而也进入if分支。这样,两个线程都将执行对静态变量的初始化。因此在这里,我使用了指针,并在对指针进行赋值之前使用锁保证在同一时间内只能有一个线程对指针进行初始化。同时基于性能的考虑,我们需要在每次访问实例之前检查指针是否已经经过初始化,以避免每次对Singleton的访问都需要请求对锁的控制权。”

  “同时,”我咽了口口水继续说,“因为new运算符的调用分为分配内存、调用构造函数以及为指针赋值三步,就像下面的构造函数调用:”

1 SingletonInstance pInstance = new SingletonInstance();

  “这行代码会转化为以下形式:”

1 SingletonInstance pHeap = __new(sizeof(SingletonInstance));
2 pHeap->SingletonInstance::SingletonInstance();
3 SingletonInstance pInstance = pHeap;

  “这样转换是因为在C++标准中规定,如果内存分配失败,或者构造函数没有成功执行, new运算符所返回的将是空。一般情况下,编译器不会轻易调整这三步的执行顺序,但是在满足特定条件时,如构造函数不会抛出异常等,编译器可能出于优化的目的将第一步和第三步合并为同一步:”

1 SingletonInstance pInstance = __new(sizeof(SingletonInstance));
2 pInstance->SingletonInstance::SingletonInstance();

  “这样就可能导致其中一个线程在完成了内存分配后就被切换到另一线程,而另一线程对Singleton的再次访问将由于pInstance已经赋值而越过if分支,从而返回一个不完整的对象。因此,我在这个实现中为静态成员指针添加了volatile关键字。该关键字的实际意义是由其修饰的变量可能会被意想不到地改变,因此每次对其所修饰的变量进行操作都需要从内存中取得它的实际值。它可以用来阻止编译器对指令顺序的调整。只是由于该关键字所提供的禁止重排代码是假定在单线程环境下的,因此并不能禁止多线程环境下的指令重排。”

  “最后来说说我对atexit()关键字的使用。在通过new关键字创建类型实例的时候,我们同时通过atexit()函数注册了释放该实例的函数,从而保证了这些实例能够在程序退出前正确地析构。该函数的特性也能保证后被创建的实例首先被析构。其实,对静态类型实例进行析构的过程与前面所提到的在main()函数执行之前插入静态初始化逻辑相对应。”

引用还是指针

  “既然你在实现中使用了指针,为什么仍然在Instance()函数中返回引用呢?”面试官又抛出了新的问题。

  “这是因为Singleton返回的实例的生存期是由Singleton本身所决定的,而不是用户代码。我们知道,指针和引用在语法上的最大区别就是指针可以为NULL,并可以通过delete运算符删除指针所指的实例,而引用则不可以。由该语法区别引申出的语义区别之一就是这些实例的生存期意义:通过引用所返回的实例,生存期由非用户代码管理,而通过指针返回的实例,其可能在某个时间点没有被创建,或是可以被删除的。但是这两条Singleton都不满足,因此在这里,我使用指针,而不是引用。”

  “指针和引用除了你提到的这些之外,还有其它的区别吗?”

  “有的。指针和引用的区别主要存在于几个方面。从低层次向高层次上来说,分为编译器实现上的,语法上的以及语义上的区别。就编译器的实现来说,声明一个引用并没有为引用分配内存,而仅仅是为该变量赋予了一个别名。而声明一个指针则分配了内存。这种实现上的差异就导致了语法上的众多区别:对引用进行更改将导致其原本指向的实例被赋值,而对指针进行更改将导致其指向另一个实例;引用将永远指向一个类型实例,从而导致其不能为NULL,并由于该限制而导致了众多语法上的区别,如dynamic_cast对引用和指针在无法成功进行转化时的行为不一致。而就语义而言,前面所提到的生存期语义是一个区别,同时一个返回引用的函数常常保证其返回结果有效。一般来说,语义区别的根源常常是语法上的区别,因此上面的语义区别仅仅是列举了一些例子,而真正语义上的差别常常需要考虑它们的语境。”

  “你在前面说到了你的多线程内部实现使用了指针,而返回类型是引用。在编写过程中,你是否考虑了实例构造不成功的情况,如new运算符运行失败?”

  “是的。在和其它人进行讨论的过程中,大家对于这种问题有各自的理解。首先,对一个实例的构造将可能在两处抛出异常:new运算符的执行以及构造函数抛出的异常。对于new运算符,我想说的是几点。对于某些操作系统,例如Windows,其常常使用虚拟地址,因此其运行常常不受物理内存实际大小的限制。而对于构造函数中抛出的异常,我们有两种策略可以选择:在构造函数内对异常进行处理,以及在构造函数之外对异常进行处理。在构造函数内对异常进行处理可以保证类型实例处于一个有效的状态,但一般不是我们想要的实例状态。这样一个实例会导致后面对它的使用更为繁琐,例如需要更多的处理逻辑或再次导致程序执行异常。反过来,在构造函数之外对异常进行处理常常是更好的选择,因为软件开发人员可以根据产生异常时所构造的实例的状态将一定范围内的各个变量更改为合法的状态。举例来说,我们在一个函数中尝试创建一对相互关联的类型实例,那么在一个实例的构造函数抛出了异常时,我们不应该在构造函数里对该实例的状态进行维护,因为前一个实例的构造是按照后一个实例会正常创建来进行的。相对来说,放弃后一个实例,并将前一个实例删除是一个比较好的选择。”

  我在白板上比划了一下,继续说到:“我们知道,异常有两个非常明显的缺陷:效率,以及对代码的污染。在太小的粒度中使用异常,就会导致异常使用次数的增加,对于效率以及代码的整洁型都是伤害。同样地,对拷贝构造函数等组成常常需要使用类似的原则。”

  “反过来说,Singleton的使用也可以保持着这种原则。Singleton仅仅是一个包装好的全局实例,对其的创建如果一旦不成功,在较高层次上保持正常状态同样是一个较好的选择。”

Anti-Patten

  “既然你提到了Singleton仅仅是一个包装好的全局变量,那你能说说它和全局变量的相同与不同么?”

  “单件可以说是全局变量的替代品。其拥有全局变量的众多特点:全局可见且贯穿应用程序的整个生命周期。除此之外,单件模式还拥有一些全局变量所不具有的性质:同一类型的对象实例只能有一个,同时适当的实现还拥有延迟初始化(Lazy)的功能,可以避免耗时的全局变量初始化所导致的启动速度不佳等问题。要说明的是,Singleton的最主要目的并不是作为一个全局变量使用,而是保证类型实例有且仅有一个。它所具有的全局访问特性仅仅是它的一个副作用。但正是这个副作用使它更类似于包装好的全局变量,从而允许各部分代码对其直接进行操作。软件开发人员需要通过仔细地阅读各部分对其进行操作的代码才能了解其真正的使用方式,而不能通过接口得到组件依赖性等信息。如果Singleton记录了程序的运行状态,那么该状态将是一个全局状态。各个组件对其进行操作的调用时序将变得十分重要,从而使各个组件之间存在着一种隐式的依赖。”

  “从语法上来讲,首先Singleton模式实际上将类型功能与类型实例个数限制的代码混合在了一起,违反了SRP。其次Singleton模式在Instance()函数中将创建一个确定的类型,从而禁止了通过多态提供另一种实现的可能。”

  “但是从系统的角度来讲,对Singleton的使用则是无法避免的:假设一个系统拥有成百上千个服务,那么对它们的传递将会成为系统的一个灾难。从微软所提供的众多类库上来看,其常常提供一种方式获得服务的函数,如GetService()等。另外一个可以减轻Singleton模式所带来不良影响的方法则是为Singleton模式提供无状态或状态关联很小的实现。”

  “也就是说,Singleton本身并不是一个非常差的模式,对其使用的关键在于何时使用它并正确的使用它。”

  面试官抬起手腕看了看时间:“好了,时间已经到了。你的C++功底已经很好了。我相信,我们会在不久的将来成为同事。”

笔者注:这本是Writing Patterns Line by Line的一篇文章,但最后想想,写模式的人太多了,我还是省省吧。。。

下一篇回归WPF,环境刚好。可能中间穿插些别的内容,比如HTML5,JS,安全等等。

头一次写小品文,不知道效果是不是好。因为这种文章的特点是知识点分散,而且隐藏在文章的每一句话中。。。好处就是写起来轻松,呵呵。。。

转载请注明原文地址:http://www.cnblogs.com/loveis715/archive/2012/07/18/2598409.html

商业转载请事先与我联系:silverfox715@sina.com,我只会要求添加作者名称以及博客首页链接。

 

TPS563201DDCR电路翻车实录:从原理图到PCB,我踩过的5个坑及补救方法 本文详细记录了使用TPS563201DDCR电源芯片设计电路时遇到的5个常见问题及解决方案,包括EN引脚配置、BST电容选型、反馈网络设计、PCB布局优化以及散热处理。通过实际案例和数据分析,帮助工程师避免类似错误,提升电源模块的可靠性和效率。 阅读详情

相关推荐

整理笔记——二极管

二极管是一种由半导体材料制成的一种具有单向导电性能的电子元器件,二极管的核心是PN结。加在二极管两端的电压和通关的电流被成为,二极管的伏安特性曲线​​​二极管的正向特性:起初正向电压较小时,正向电流几乎没有,称之为死区,此时二极管处于截止状态。当电压差超过截止电压(0.5V或者0.7V),电流随着电压上升,开始增加缓慢,随后急剧增大,二极管的电阻变小进入导通状态,此时电压变化较小。二极管的反向特性:起初反向电流很小,不随电压二变化,此时成为反向饱和电流;当方向电压达到二极管的反向击穿电压。

#include 811

单例模式详解

单例模式详解:懒汉式+饿汉式,双重检测锁+内存屏障防止指令重排+模板类

一点星光 467

微电网系列: 位移因数DPF&&功率因数PF&&过激&&欠激

文中对比了位移因数(DPF,仅基波相位)与功率因数(PF,含谐波影响)的差异,指出PF=DPF×DF(畸变因数)。过激/欠激状态对应无功功率控制:过激(容性,电流超前,抬升电压)与欠激(感性,电流滞后,抑制电压)。PCS(如光伏逆变器)需逆向调节电网无功状态,过激时发出容性无功,欠激时吸收感性无功,以维持电压。

云纳星辰怀自在 1169

Singleton单例模式

参考自:http://www.cnblogs.com/rush/archive/2011/10/30/2229565.html

htkeepmoving 716

CentOS7上装不了最新Chrome?别折腾依赖了,试试这个老版本安装法(附103.0.5060.24 beta下载地址)

本文提供了在CentOS7上安装兼容版Chrome的详细指南,解决了因GLIBC版本限制导致的最新版Chrome无法安装的问题。推荐使用Chrome 103.0.5060.24 beta版本,并附有下载地址和完整安装步骤,包括禁用自动更新和配套工具安装,确保系统稳定性和功能完整性。

weixin_29289821的博客 566

C语言 函数

一、什么是函数 二、函数的相关语法 三、函数编码规范

m0_57105760的博客 1890

饿汉式和懒汉式实现单例模式

单例模式 采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法 饿汉式实现单例模式 class SingleTon { private String name; // get,set方法 public String getName() { return name; } public void setName(String name) { this.name = name;

weixin_45717294的博客 792

【常见开源库的二次开发】基于openssl的加密与解密——openssl认识与配置(一)

OpenSSL 是一个开源的软件库,它提供了一系列加密工具和协议,主要用于实现安全通信,如在网络上的数据传输。它支持多种加密算法,包括对称加密、非对称加密、散列函数、伪随机数生成器、数字签名、密钥交换以及证书管理等。

嵌入式技术开发 4160

c++: 单例模式(Singleton)的最优写法

目的 本例简介C++中单例模式的最优写法。 实现 基础写法 下面的代码是C++单例的基础写法,在静态函数Singleton::getInstance()中定义了Singleton的静态变量对象,并返回此对象的引用。 由于C++函数的静态变量唯一性,可以确保例子中s对象的唯一性,线程同步,以及静态对象间的依赖关系等问题。 #include <iostream> class Singleton { public: static Singleton &getInstance() {

chenxiemin的专栏 1万+

Android性能分析深度指南:Perfetto工具全面解析

本文深入探讨Android性能优化工具Perfetto的应用与实践。作为Google推出的新一代追踪系统,Perfetto整合了CPU调度、内存分配、电池状态等多维度分析功能,通过高性能数据处理和可视化界面帮助开发者定位性能瓶颈。文章系统介绍了Perfetto的架构原理、配置方法和核心功能,包括CPU性能分析、内存泄漏检测等关键技术。通过电商应用卡顿优化的实战案例,展示了如何利用数学建模(如帧时间公式$T_{\text{render}}\leq16.67\text{ms}$)指导性能调优。

郑伟强dev的专栏 144

大数据预测分析:精准把握市场趋势

传统市场预测多依赖经验判断(比如“去年卖了1000件,今年加20%”)或小数据统计(比如用近6个月的销量做线性回归)。而大数据预测分析是:利用海量结构化+非结构化数据(销售、用户、社交媒体、天气等),通过机器学习/深度学习模型,挖掘数据中的关联关系与趋势规律,实现对未来市场走势的精准预测。更全面:覆盖内部(企业数据)+外部(行业、环境数据)的全维度数据;更精准:用算法替代人工,避免主观偏差;更实时:支持分钟级/小时级的实时预测(比如直播带货的销量预测)。错误做法。

AI智能探索者的博客 1024

面试中的Singleton【转】

面试中的Singleton引子  “请写一个Singleton。”面试官微笑着和我说。  “这可真简单。”我心里想着,并在白板上写下了下面的Singleton实现

小鱼的专栏 383

使用Google Earth Engine下载JRC全球水体数据

通过Google Earth Engine平台和提供的源代码,研究人员和水资源管理者可以方便地获取和利用这一重要的水资源监测数据集,从而开展相关的研究和决策支持工作。要使用Google Earth Engine下载JRC全球水体数据,首先需要在Google Earth Engine平台上进行注册和登录。JRC全球水体数据是由欧洲联合研究中心开发的高分辨率全球水体分类数据集,覆盖了整个地球的陆地和水域。下载完成后,你可以使用各种遥感数据处理软件来打开和分析这些数据,以满足不同的研究和应用需求。

LrlDebug的博客 1157

Java面试-Spring Bean 的作用域:singleton、prototype、request、session

《Java面试200问》系列解析Spring Bean的5种作用域:singleton(默认单例)、prototype(每次创建新实例)、request(请求级)、session(会话级)和application(应用级)。文章通过代码示例和对比表格,详细说明各作用域特点、适用场景及注意事项,如singleton适合无状态服务,prototype适合有状态对象,request/session需配合代理模式使用。还提供了作用域选择指南、常见面试题解析和实际开发中的避坑技巧,帮助开发者深入理解Spring Be

千淘万漉虽辛苦,吹尽狂沙始到金 2万+

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

面试中的 Singleton

原文出处: loveis715    引子 “请写一个Singleton。”面试官微笑着和我说。 “这可真简单。”我心里想着,并在白板上写下了下面的Singleton实现: 123456789101112class Singleton{public:    sta

u012662731的博客 300

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

面试之Singleton

单例设计模式,即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式。直接实例化饿汉式 枚举类型 静态代码块 线程不安全 线程安全 静态内部类

weixin_44778973的博客 132

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

上一篇: sql语句中int转varchar
下一篇: web.config 配置文件节点说明
ArvinStudy
博客等级 码龄15年 83粉丝 193原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值