主頁 > 後端開發 > 師妹面試被問到Spring如何處理回圈依賴,我花了一晚上給她安排的明明白白

師妹面試被問到Spring如何處理回圈依賴,我花了一晚上給她安排的明明白白

2021-06-15 07:09:51 後端開發

前言

你是不是被這個騷氣的標題吸引進來的,_ 喜歡我的文章的話就給個好評吧,你的肯定是我堅持寫作最大的動力,來吧兄弟們,給我一點動力

Spring如何處理回圈依賴?這是最近較為頻繁被問到的一個面試題,在前面Bean實體化流程中,對屬性注入一文多多少少對回圈依賴有過介紹,這篇文章詳細講一下Spring中的回圈依賴的處理方案,

什么是回圈依賴

依賴指的是Bean與Bean之間的依賴關系,回圈依賴指的是兩個或者多個Bean相互依賴,如:
在這里插入圖片描述

構造器回圈依賴

代碼示例:

public class BeanA {

    private BeanB beanB;

    public BeanA(BeanB beanB){
        this.beanB = beanB;
    }
}

public class BeanB {

    private BeanA beanA;

    public BeanB(BeanA beanA){
        this.beanA = beanA;
    }
}

組態檔

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
        <constructor-arg type="cn.itsource._01_di.BeanB" ref="beanB"  />
 </bean>


 <bean id="beanB" class="cn.itsource._01_di.BeanB"  >
         <constructor-arg type="cn.itsource._01_di.BeanA" ref="beanA" />
 </bean>

Setter回圈依賴

代碼示例

public class BeanA {

    private BeanB beanB;

    public void setBeanB(BeanB beanB){
        this.beanB = beanB;
    }
}

@Data
public class BeanB {

    private BeanA beanA;

    public void setBeanA(BeanA beanA){
        this.beanA = beanA;
    }
}

組態檔

<bean id="beanA" class="cn.itsource._01_di.BeanA" >
    <property name="beanB" ref="beanB" />
</bean>


<bean id="beanB" class="cn.itsource._01_di.BeanB">
    <property name="beanA" ref="beanA" />
</bean>

回圈依賴包括: 構造器注入回圈依賴 set , 注入回圈依賴 和 prototype模式Bean的回圈依賴,Spring只解決了單利Bean的 setter 注入回圈依賴,對于構造器回圈依賴,和 prototype模式的回圈依賴是無法解決的,在創建Bean的時候就會拋出例外 :“BeanCurrentlyInCreationException” ,

回圈依賴控制開關在 AbstractRefreshableApplicationContext 容器工廠類中有定義:

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {

	@Nullable
	private Boolean allowBeanDefinitionOverriding;
	//是否允許回圈依賴
	@Nullable
	private Boolean allowCircularReferences;
	
	//設定回圈依賴
	public void setAllowCircularReferences(boolean allowCircularReferences) {
		this.allowCircularReferences = allowCircularReferences;
	}

默認情況下是允許Bean之間的回圈依賴的,在依賴注入時Spring會嘗試處理回圈依賴,如果將該屬性配置為“false”則關倍訓圈依賴,當在Bean依賴注入的時遇到回圈依賴時拋出例外,可以通過如下方式關閉,但是一般都不這么做

ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
//禁用回圈依賴
applicationContext.setAllowCircularReferences(false);
//重繪容器
applicationContext.refresh();
...

構造器回圈依賴處理

構造器是不允許回圈依賴的,動動你的小腦瓜想一想,比如:A 依賴 B ,B依賴C,C依賴A,在實體化A的時候,構造器需要注入B,然后Spirng會實體化B,此時的A屬于“正在創建”的狀態,當實體化B的時候,發現構造器需要注入C,然后去實體化C,然而實體化C的時候又需要注入A的實體,這樣就造成了一個死回圈,永遠無法先實體化出某一個Bean,所以Spring遇到這里構造器回圈依賴會直接拋出例外,

那么Spring到底是如何做的呢?

  1. 首先Spring會走Bean的實體化流程嘗試創建 A 的實體 ,在創建實體之間先從 “正在創建Bean池” (一個快取Map而已)中去查找A 是否正在創建,如果沒找到,則將 A 放入 “正在創建Bean池”中,然后準備實體化構造器引數 B,

  2. Spring會走Bean的實體化流程嘗試創建 B 的實體 ,在創建實體之間先從 “正在創建Bean池” (一個快取Map而已)中去查找B 是否正在創建,如果沒找到,則將 B 放入 “正在創建Bean池”中,然后準備實體化構造器引數 A,

  3. Spring會走Bean的實體化流程嘗試創建 A 的實體 ,在創建實體之間先從 “正在創建Bean池” (一個快取Map而已)中去查找A 是否正在創建,

  4. 此時:Spring發現 A 正處于“正在創建Bean池”,表示出現構造器回圈依賴,拋出例外:“BeanCurrentlyInCreationException”

DefaultSingletonBeanRegistry#getSingleton

下面我們以 BeanA 構造引數依賴BeanB, BeanB 構造引數依賴BeanA 為例來分析,

當Spring的IOC容器啟動,嘗試對單利的BeanA進行初始化,根據之前的分析我們知道,單利Bean的創建入口是 AbstractBeanFactory#doGetBean 在該方法中會先從單利Bean快取中獲取,如果沒有代碼會走到:DefaultSingletonBeanRegistry#getSingleton(jString beanName, ObjectFactory<?> singletonFactory) 方法中 ,在該方法中會先對把創建的Bean加入 一個名字為 singletonsCurrentlyInCreation 的 ConcurrentHashMap中,意思是該Bean正在創建中,然后呼叫 ObjectFactory.getObject() 實體化Bean , 假設 BeanA 進入了該方法進行實體化:

//正在創建中的Bean
private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		...省略...
		//把該Bean的名字加入 singletonsCurrentlyInCreation 正在創建池 中
		beforeSingletonCreation(beanName);
		boolean newSingleton = false;
		boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
		if (recordSuppressedExceptions) {
			this.suppressedExceptions = new LinkedHashSet<>();
		}
		try {
			//呼叫ObjectFactory創建Bean的實體
			singletonObject = singletonFactory.getObject();
			newSingleton = true;
		}
...省略...


//如果singletonsCurrentlyInCreation中沒該Bean,就把該Bean存盤到singletonsCurrentlyInCreation中,
//如果 singletonsCurrentlyInCreation 中有 該Bean,就報錯回圈依賴例外BeanCurrentlyInCreationException
//也就意味著同一個beanName進入該方法2次就會拋例外
protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

beforeSingletonCreation 方法非常關鍵 ,它會把beanName加入 singletonsCurrentlyInCreation,一個代表“正在創建中的Bean”的ConcurrentHashMap中,

  • 如果singletonsCurrentlyInCreation中沒該beanName,就把該Bean存盤到singletonsCurrentlyInCreation中,
  • 如果 singletonsCurrentlyInCreation 中有 該Bean,就報錯回圈依賴例外BeanCurrentlyInCreationException

【注意】也就意味著同一個beanName進入該方法2次就會拋例外 , 現在BeanA已經加入了singletonsCurrentlyInCreation

AbstractAutowireCapableBeanFactory#autowireConstructor

我們前面分析過 ObjectFactory.getObject實體化Bean的詳細流程,這里我只是大概在復盤一下就行了,因為我們的BeanA的構造器注入了一個BeanB,所以 代碼最侄訓走到AbstractAutowireCapableBeanFactory#autowireConstructor ,通過構造器來實體化BeanA(在屬性注入那一章有講到 ) ,

在autowireConstructor 方法中會通過 ConstructorResolver#resolveConstructorArguments 來決議構造引數,呼叫 BeanDefinitionValueResolver 去把 ref="beanB" 這種字串的參考變成一個實實在在的Bean,即BeanB,所以在 BeanDefinitionValueResolver 屬性值決議器中又會去實體化BeanB,同樣會走到 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “正在創建Bean池”中,然后呼叫ObjectFactory.getObject實體化BeanB,

低于BeanB而已同樣需要通過構造器創建,BeanB構造器引數依賴了BeanA,也就意味著又會呼叫 BeanDefinitionValueResolver 去把 ref=“beanA” 這種字串參考變成容器中的BeanA的Bean實體,然后代碼又會走到 DefaultSingletonBeanRegistry#getSingleton,然后再一次的嘗試把BeanA加入singletonsCurrentlyInCreation “正在創建Bean池”,

此時問題就來了,在最開始創建BeanA的時候它已經加入過一次“正在創建Bean” 池,這會兒實體化BeanB的時候,由于構造器引數依賴了BeanA,導致BeanA又想進入“正在創建Bean” 池 ,此時 Spring拋出回圈依賴例外:

Error creating bean with name ‘beanA’: Requested bean is currently in creation: Is there an unresolvable circular reference?

到這,Spring處理構造器回圈依賴的原始碼分析完畢,

setter回圈依賴處理

setter回圈依賴是可以允許的,Spring是通過提前暴露未實體化完成的Bean的 ObjectFactory 來實作回圈依賴的,這樣做的目的是其他的Bean可以通過 ObjectFactory 參考到該Bean,

實作流程如下:

  1. Spring創建BeanA,通過無參構造實體化,并暴露一個ObjectFactory,用來獲取創建中的BeanA,然后把BeanA添加到“正在創建Bean池”中,然后通過setter注入BeanB
  2. Spring創建BeanB,通過無參構造實體化,并暴露一個ObjectFactory,用來獲取創建中的BeanB,然后把BeanB添加到“正在創建Bean池”中,然后通過setter注入BeanA
  3. 在BeanB通過setter注入BeanA時,由于BeanA 提前暴露了ObjectFactory ,通過它回傳一個提前暴露一個創建中的BeanA,
  4. 然后完成BeanB的依賴注入

AbstractAutowireCapableBeanFactory#doCreateBean

我們以BeanA 通過settter依賴BeanB,BeanB通過setter 依賴BeanA為例來分析一下原始碼,在之前的Bean實體化流程分析程序中我們了解到,Bean的實體化會走AbstractBeanFactory#doGetBean,然后查找單利快取中是否有該Bean ,如果沒有就呼叫 DefaultSingletonBeanRegistry#getSingleton,方法會把BeanA加入 singletonsCurrentlyInCreation “創建中的Bean池”,然后呼叫ObjectFactory.getObject創建Bean.

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 原始碼:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		//快取中獲取Bean,解決了回圈依賴問題
		Object sharedInstance = getSingleton(beanName);
     	...快取中沒有走下面...
		if (mbd.isSingleton()) {
					//走 DefaultSingletonBeanRegistry#getSingleton ,方法會把bean加入“正在創建bean池”
					//然后呼叫ObjectFactory實體化Bean
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}


第一次進來,快取中是沒有BeanA的,所有會走 getSingleton 方法,然后代碼最侄訓走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中 ,

AbstractAutowireCapableBeanFactory#doCreateBean原始碼:

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
		BeanWrapper instanceWrapper = null;
		if (mbd.isSingleton()) {
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
		if (instanceWrapper == null) {
		//實體化Bean
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
		...省略...
		//如果是單利 ,如果是允許回圈依賴,如果 beanName 出于創建中,已經被添加到“創建中的bean池”
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isDebugEnabled()) {
				logger.debug("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
			//把ObjectFactory 添加到 singletonFactories 中,
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}
		
	try {
		//走依賴注入流程
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}


//快取單利Bean的創建工廠,用于解決回圈依賴
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(singletonFactory, "Singleton factory must not be null");
		synchronized (this.singletonObjects) {
			//singletonObjects單利快取中是否包含Bean
			if (!this.singletonObjects.containsKey(beanName)) {
				//提前暴露ObjectFactory,把ObjectFactory放到singletonFactories中,
				//后面解決回圈依賴,獲取Bean實體的時候會用到
				this.singletonFactories.put(beanName, singletonFactory);
				//早期單利bean快取中移除Bean
				this.earlySingletonObjects.remove(beanName);
				//把注冊的Bean加入registeredSingletons中
				this.registeredSingletons.add(beanName);
			}
		}
	}

該方法中把BeanA實體化好之后,會把ObjectFactory存盤到一個 singletonFactories (HashMap)中來提前暴露Bean的創建工廠,用于解決回圈依賴【重要】,然后呼叫 populateBean 走屬性注入流程,

屬性注入會通過BeanDefinition得到bean的依賴屬性,然后呼叫 AbstractAutowireCapableBeanFactory#applyPropertyValues ,把屬性應用到物件上,在applyPropertyValues 方法中最終呼叫 BeanDefinitionValueResolver#resolveValueIfNecessary 決議屬性值,比如:ref=“beanB” 這種字串參考變成 物件實體的參考,

在BeanDefinitionValueResolver決議依賴的屬性值即:BeanB的時候,同樣會觸發BeanB的實體化,代碼會走到AbstractBeanFactory#doGetBean ,然后走方法 DefaultSingletonBeanRegistry#getSingleton 中把BeanB加入 singletonsCurrentlyInCreation “創建中的Bean池”,然后代碼會走到AbstractAutowireCapableBeanFactory#doCreateBean 方法中創建BeanB,

該方法中會先實體化BeanB,接著會把BeanB的ObjectFactory存盤到 singletonFactories (HashMap)中來提前暴露Bean的創建工廠,用于解決回圈依賴,然后呼叫 populateBean 走屬性注入流程,

同樣因為BeanB通過Setter 注入了 A,所以在 populateBean 屬性注入流程中會決議 ref=“beanA” 為容器中的 BeanA 的實體,

然后會走到 AbstractBeanFactory#doGetBean 中獲取BeanA的實體,這個時候流程就不一樣了,我們先看一下 AbstractBeanFactory#doGetBean 中的代碼

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		final String beanName = transformedBeanName(name);
		Object bean;

		// Eagerly check singleton cache for manually registered singletons.
		//從快取中獲取Bean
		Object sharedInstance = getSingleton(beanName);

		...省略...

		//如果快取中沒有Bean,就創建Bean
		if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

在獲取單利Bean的實體的時候是會先去單利Bean的快取中去查看Bean是否已經存在,如果不存在,才會走DefaultSingletonBeanRegistry#getSingleton方法創建Bean,
問題是:此刻單利Bean快取中已經有BeanA了,因為在最開始BeanA已經出于“正在創建Bean池”中了,我們先來看一下是如何從快取獲取Bean的,

DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)原始碼如下:

//allowEarlyReference :是否創建早期應用,主要用來解決回圈依賴
	@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Quick check for existing instance without full singleton lock
		//從Map中 singletonObjects = new ConcurrentHashMap<>(256); 獲取單利Bean

		//【一級快取】singletonObject快取中是否有Bean , 它存盤的是已經實體化好的Bean
		Object singletonObject = this.singletonObjects.get(beanName);

		//如果singletonObjects中沒有Bean,但是Bean出于正在創建池中,即: Set<String> singletonsCurrentlyInCreation中有Bean,
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			
			//【二級快取】從早期單例物件的快取 earlySingletonObjects 中獲取
			singletonObject = this.earlySingletonObjects.get(beanName);
			
			//早期單利物件快取中也沒有,但是允許回圈依賴
			if (singletonObject == null && allowEarlyReference) {
				
				synchronized (this.singletonObjects) {
					// Consistent creation of early reference within full singleton lock
					singletonObject = this.singletonObjects.get(beanName);
					if (singletonObject == null) {
						singletonObject = this.earlySingletonObjects.get(beanName);
						if (singletonObject == null) {
							
							//【三級快取】獲取ObjectFactory , 物件創建工廠,得到Bean創建程序中提前暴露的工廠,
							ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
							if (singletonFactory != null) {
								//通過工廠ObjectFactory 獲取物件實體
								singletonObject = singletonFactory.getObject();
								//把物件存盤到早期快取中
								this.earlySingletonObjects.put(beanName, singletonObject);
								//把ObjectFactory移除
								this.singletonFactories.remove(beanName);
							}
						}
					}
				}
			}
		}
		return singletonObject;
	}

這里就是經典的三級快取解決Spring回圈依賴,你看到了,這里會先從 singletonObjects 單利Bean快取集合中獲取Bean(該快取是實體化完成了的Bean),如果沒有,就從earlySingletonObjects早期物件快取中獲取Bean(該快取中存放的是還未實體化完成的早期Bean),如果還是沒有,就從singletonFactories中得到暴露的ObjectFactory來獲取依賴的Bean,然后放入早期快取中,并把ObjectFactory從singletonFactories中移除,最后回傳Bean的實體,

由于在實體化BeanA的時候已經把BeanA的ObjectFactory添加到了 singletonFactories 快取中,那么這里就會走到 singletonFactory.getObject(); 方法得到BeanA的實體,并且會把BeanA存盤到 earlySingletonObjects早期單利Bean快取中,

BeanA的實體成功回傳,那么BeanB的 setter注入成功,代表BeanB實體化完成,那么BeanA的setter方法注入成功,BeanA實體化完成,

prototype模式的回圈依賴

對于prototype模式下的Bean不允許回圈依賴,因為 這種模式下Bean是不做快取的,所以就沒法暴露ObjectFactory,也就沒辦法實作回圈依賴,

總結

不知道你有沒有看暈,反正我但是在原始碼時的程序是比較辛苦的~~~~(>_<)~~~~ ,這里需要你對前面Bean的實體化流程和屬性注入流程比較熟悉,否則就會暈菜,

這里總結一下:

  1. 構造器回圈依賴是不允許的,主要通過 singletonsCurrentlyInCreation “正在創建Bean池” 把創建中的Bean快取起來,如果回圈依賴,同一個Bean勢必會嘗試進入該快取2次,拋出回圈依賴例外,
  2. setter回圈依賴是可以允許的,Spring是通過提前暴露未實體化完成的Bean的 ObjectFactory 來實作回圈依賴的,這樣做的目的是其他的Bean可以通過 ObjectFactory 參考到該Bean , 在獲取依賴的Bean的時候使用到了三級快取,

下面的面試題你會答了嗎?

  1. Spirng支持那種模式下的回圈依賴(構造器?,setter?, prototype?)
  2. Spring是如何處理構造器注入回圈依賴的?
  3. Spring是如何處理Setter注入回圈依賴的?

喜歡我的文章的話就給個好評吧,你的肯定是我堅持寫作最大的動力,來吧兄弟們,給我一點動力

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/287377.html

標籤:java

上一篇:Java基礎面試題(建議收藏)

下一篇:揭秘游戲服務器,不看后悔!!!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more