主頁 > 後端開發 > Mybatis使用的9種設計模式,這些你都知道嗎?真是太有用了

Mybatis使用的9種設計模式,這些你都知道嗎?真是太有用了

2021-01-05 06:30:41 後端開發

  • 1、Builder模式

  • 2、工廠模式

  • 3、單例模式

  • 4、代理模式

  • 5、組合模式

  • 6、模板方法模式

  • 7、配接器模式

  • 8、裝飾者模式

  • 9、迭代器模式

雖然我們都知道有26個設計模式,但是大多停留在概念層面,真實開發中很少遇到,Mybatis原始碼中使用了大量的設計模式,閱讀原始碼并觀察設計模式在其中的應用,能夠更深入的理解設計模式,

Mybatis至少遇到了以下的設計模式的使用:

  1. Builder模式,例如SqlSessionFactoryBuilder、XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder、CacheBuilder;

  2. 工廠模式,例如SqlSessionFactory、ObjectFactory、MapperProxyFactory;

  3. 單例模式,例如ErrorContext和LogFactory;

  4. 代理模式,Mybatis實作的核心,比如MapperProxy、ConnectionLogger,用的jdk的動態代理;還有executor.loader包使用了cglib或者javassist達到延遲加載的效果;

  5. 組合模式,例如SqlNode和各個子類ChooseSqlNode等;

  6. 模板方法模式,例如BaseExecutor和SimpleExecutor,還有BaseTypeHandler和所有的子類例如IntegerTypeHandler;

  7. 配接器模式,例如Log的Mybatis介面和它對jdbc、log4j等各種日志框架的適配實作;

  8. 裝飾者模式,例如Cache包中的cache.decorators子包中等各個裝飾者的實作;

  9. 迭代器模式,例如迭代器模式PropertyTokenizer;

接下來挨個模式進行解讀,先介紹模式自身的知識,然后解讀在Mybatis中怎樣應用了該模式,

1、Builder模式

Builder模式的定義是“將一個復雜物件的構建與它的表示分離,使得同樣的構建程序可以創建不同的表示,”,它屬于創建類模式,一般來說,如果一個物件的構建比較復雜,超出了建構式所能包含的范圍,就可以使用工廠模式和Builder模式,相對于工廠模式會產出一個完整的產品,Builder應用于更加復雜的物件的構建,甚至只會構建產品的一個部分,

在Mybatis環境的初始化程序中,SqlSessionFactoryBuilder會呼叫XMLConfigBuilder讀取所有的MybatisMapConfig.xml和所有的*Mapper.xml檔案,構建Mybatis運行的核心物件Configuration物件,然后將該Configuration物件作為引數構建一個SqlSessionFactory物件,

其中XMLConfigBuilder在構建Configuration物件時,也會呼叫XMLMapperBuilder用于讀取*Mapper檔案,而XMLMapperBuilder會使用XMLStatementBuilder來讀取和build所有的SQL陳述句,

在這個程序中,有一個相似的特點,就是這些Builder會讀取檔案或者配置,然后做大量的XpathParser決議、配置或語法的決議、反射生成物件、存入結果快取等步驟,這么多的作業都不是一個建構式所能包括的,因此大量采用了Builder模式來解決,

對于builder的具體類,方法都大都用build*開頭,比如SqlSessionFactoryBuilder為例,它包含以下方法:

即根據不同的輸入引數來構建SqlSessionFactory這個工廠物件,

2、工廠模式

在Mybatis中比如SqlSessionFactory使用的是工廠模式,該工廠沒有那么復雜的邏輯,是一個簡單工廠模式,

簡單工廠模式(Simple Factory Pattern):又稱為靜態工廠方法(Static Factory Method)模式,它屬于類創建型模式,在簡單工廠模式中,可以根據引數的不同回傳不同類的實體,簡單工廠模式專門定義一個類來負責創建其他類的實體,被創建的實體通常都具有共同的父類,

SqlSession可以認為是一個Mybatis作業的核心的介面,通過這個介面可以執行執行SQL陳述句、獲取Mappers、管理事務,類似于連接MySQL的Connection物件,

可以看到,該Factory的openSession方法多載了很多個,分別支持autoCommit、Executor、Transaction等引數的輸入,來構建核心的SqlSession物件,

在DefaultSqlSessionFactory的默認工廠實作里,有一個方法可以看出工廠怎么產出一個產品:

 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
   boolean autoCommit) {
  Transaction tx = null;
  try {
   final Environment environment = configuration.getEnvironment();
   final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
   tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
   final Executor executor = configuration.newExecutor(tx, execType);
   return new DefaultSqlSession(configuration, executor, autoCommit);
  } catch (Exception e) {
   closeTransaction(tx); // may have fetched a connection so lets call
         // close()
   throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  } finally {
   ErrorContext.instance().reset();
  }
 }

這是一個openSession呼叫的底層方法,該方法先從configuration讀取對應的環境配置,然后初始化TransactionFactory獲得一個Transaction物件,然后通過Transaction獲取一個Executor物件,最后通過configuration、Executor、是否autoCommit三個引數構建了SqlSession,

在這里其實也可以看到端倪,SqlSession的執行,其實是委托給對應的Executor來進行的,

而對于LogFactory,它的實作代碼:

public final class LogFactory {
 private static Constructor<? extends Log> logConstructor;

 private LogFactory() {
  // disable construction
 }

 public static Log getLog(Class<?> aClass) {
  return getLog(aClass.getName());
 }

這里有個特別的地方,是Log變數的的型別是Constructor<? extends Log>,也就是說該工廠生產的不只是一個產品,而是具有Log公共介面的一系列產品,比如Log4jImpl、Slf4jImpl等很多具體的Log,

3、單例模式

單例模式(Singleton Pattern):單例模式確保某一個類只有一個實體,而且自行實體化并向整個系統提供這個實體,這個類稱為單例類,它提供全域訪問的方法,

單例模式的要點有三個:一是某個類只能有一個實體;二是它必須自行創建這個實體;三是它必須自行向整個系統提供這個實體,單例模式是一種物件創建型模式,單例模式又名單件模式或單態模式,

在Mybatis中有兩個地方用到單例模式,ErrorContext和LogFactory,其中ErrorContext是用在每個執行緒范圍內的單例,用于記錄該執行緒的執行環境錯誤資訊,而LogFactory則是提供給整個Mybatis使用的日志工廠,用于獲得針對專案配置好的日志物件,

ErrorContext的單例實作代碼:

public class ErrorContext {

 private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

 private ErrorContext() {
 }

 public static ErrorContext instance() {
  ErrorContext context = LOCAL.get();
  if (context == null) {
   context = new ErrorContext();
   LOCAL.set(context);
  }
  return context;
 }

建構式是private修飾,具有一個static的區域instance變數和一個獲取instance變數的方法,在獲取實體的方法中,先判斷是否為空如果是的話就先創建,然后回傳構造好的物件,

只是這里有個有趣的地方是,LOCAL的靜態實體變數使用了ThreadLocal修飾,也就是說它屬于每個執行緒各自的資料,而在instance()方法中,先獲取本執行緒的該實體,如果沒有就創建該執行緒獨有的ErrorContext,

4、代理模式

代理模式可以認為是Mybatis的核心使用的模式,正是由于這個模式,我們只需要撰寫Mapper.java介面,不需要實作,由Mybatis后臺幫我們完成具體SQL的執行,

代理模式(Proxy Pattern) :給某一個物件提供一個代 理,并由代理物件控制對原物件的參考,代理模式的英 文叫做Proxy或Surrogate,它是一種物件結構型模式,

代理模式包含如下角色:

  • Subject: 抽象主題角色

  • Proxy: 代理主題角色

  • RealSubject: 真實主題角色

這里有兩個步驟,第一個是提前創建一個Proxy,第二個是使用的時候會自動請求Proxy,然后由Proxy來執行具體事務;

當我們使用Configuration的getMapper方法時,會呼叫mapperRegistry.getMapper方法,而該方法又會呼叫mapperProxyFactory.newInstance(sqlSession)來生成一個具體的代理:

public class MapperProxyFactory<T> {

 private final Class<T> mapperInterface;
 private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

 public MapperProxyFactory(Class<T> mapperInterface) {
  this.mapperInterface = mapperInterface;
 }

 public Class<T> getMapperInterface() {
  return mapperInterface;
 }

 public Map<Method, MapperMethod> getMethodCache() {
  return methodCache;
 }

 @SuppressWarnings("unchecked")
 protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
    mapperProxy);
 }

 public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
 }

}

在這里,先通過T newInstance(SqlSession sqlSession)方法會得到一個MapperProxy物件,然后呼叫T newInstance(MapperProxymapperProxy)生成代理物件然后回傳,

而查看MapperProxy的代碼,可以看到如下內容:

public class MapperProxy<T> implements InvocationHandler, Serializable {

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
   if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
   } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
   }
  } catch (Throwable t) {
   throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
 }

非常典型的,該MapperProxy類實作了InvocationHandler介面,并且實作了該介面的invoke方法,

通過這種方式,我們只需要撰寫Mapper.java介面類,當真正執行一個Mapper介面的時候,就會轉發給MapperProxy.invoke方法,而該方法則會呼叫后續的sqlSession.cud>executor.execute>prepareStatement等一系列方法,完成SQL的執行和回傳,

5、組合模式

組合模式組合多個物件形成樹形結構以表示“整體-部分”的結構層次,

組合模式對單個物件(葉子物件)和組合物件(組合物件)具有一致性,它將物件組織到樹結構中,可以用來描述整體與部分的關系,同時它也模糊了簡單元素(葉子物件)和復雜元素(容器物件)的概念,使得客戶能夠像處理簡單元素一樣來處理復雜元素,從而使客戶程式能夠與復雜元素的內部結構解耦,

在使用組合模式中需要注意一點也是組合模式最關鍵的地方:葉子物件和組合物件實作相同的介面,這就是組合模式能夠將葉子節點和物件節點進行一致處理的原因,

Mybatis支持動態SQL的強大功能,比如下面的這個SQL:

<update id="update" parameterType="org.format.dynamicproxy.mybatis.bean.User">
    UPDATE users
    <trim prefix="SET" prefixOverrides=",">
        <if test="name != null and name != ''">
            name = #{name}
        </if>
        <if test="age != null and age != ''">
            , age = #{age}
        </if>
        <if test="birthday != null and birthday != ''">
            , birthday = #{birthday}
        </if>
    </trim>
    where id = ${id}
</update>

在這里面使用到了trim、if等動態元素,可以根據條件來生成不同情況下的SQL;

在DynamicSqlSource.getBoundSql方法里,呼叫了rootSqlNode.apply(context)方法,apply方法是所有的動態節點都實作的介面:

public interface SqlNode {
 boolean apply(DynamicContext context);
}

對于實作該SqlSource介面的所有節點,就是整個組合模式樹的各個節點:

組合模式的簡單之處在于,所有的子節點都是同一類節點,可以遞回的向下執行,比如對于TextSqlNode,因為它是最底層的葉子節點,所以直接將對應的內容append到SQL陳述句中:

 @Override
 public boolean apply(DynamicContext context) {
  GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));
  context.appendSql(parser.parse(text));
  return true;
 }

但是對于IfSqlNode,就需要先做判斷,如果判斷通過,仍然會呼叫子元素的SqlNode,即contents.apply方法,實作遞回的決議,

 @Override
 public boolean apply(DynamicContext context) {
  if (evaluator.evaluateBoolean(test, context.getBindings())) {
   contents.apply(context);
   return true;
  }
  return false;
 }

6、模板方法模式

模板方法模式是所有模式中最為常見的幾個模式之一,是基于繼承的代碼復用的基本技術,

模板方法模式需要開發抽象類和具體子類的設計師之間的協作,一個設計師負責給出一個演算法的輪廓和骨架,另一些設計師則負責給出這個演算法的各個邏輯步驟,代表這些具體邏輯步驟的方法稱做基本方法(primitive method);而將這些基本方法匯總起來的方法叫做模板方法(template method),這個設計模式的名字就是從此而來,

模板類定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟,

在Mybatis中,sqlSession的SQL執行,都是委托給Executor實作的,Executor包含以下結構:

其中的BaseExecutor就采用了模板方法模式,它實作了大部分的SQL執行邏輯,然后把以下幾個方法交給子類定制化完成:

 protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

 protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds,
   ResultHandler resultHandler, BoundSql boundSql) throws SQLException;

該模板方法類有幾個子類的具體實作,使用了不同的策略:

  • 簡單SimpleExecutor:每執行一次update或select,就開啟一個Statement物件,用完立刻關閉Statement物件,(可以是Statement或PrepareStatement物件)

  • 重用ReuseExecutor:執行update或select,以sql作為key查找Statement物件,存在就使用,不存在就創建,用完后,不關閉Statement物件,而是放置于Map<String, Statement>內,供下一次使用,(可以是Statement或PrepareStatement物件)

  • 批量BatchExecutor:執行update(沒有select,JDBC批處理不支持select),將所有sql都添加到批處理中(addBatch()),等待統一執行(executeBatch()),它快取了多個Statement物件,每個Statement物件都是addBatch()完畢后,等待逐一執行executeBatch()批處理的;BatchExecutor相當于維護了多個桶,每個桶里都裝了很多屬于自己的SQL,就像蘋果藍里裝了很多蘋果,番茄藍里裝了很多番茄,最后,再統一倒進倉庫,(可以是Statement或PrepareStatement物件)

比如在SimpleExecutor中這樣實作update方法:

 @Override
 public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
   Configuration configuration = ms.getConfiguration();
   StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null,
     null);
   stmt = prepareStatement(handler, ms.getStatementLog());
   return handler.update(stmt);
  } finally {
   closeStatement(stmt);
  }
 }

7、配接器模式

配接器模式(Adapter Pattern) :將一個介面轉換成客戶希望的另一個介面,配接器模式使介面不兼容的那些類可以一起作業,其別名為包裝器(Wrapper),配接器模式既可以作為類結構型模式,也可以作為物件結構型模式,

在Mybatsi的logging包中,有一個Log介面:

public interface Log {

 boolean isDebugEnabled();

 boolean isTraceEnabled();

 void error(String s, Throwable e);

 void error(String s);

 void debug(String s);

 void trace(String s);

 void warn(String s);

}

該介面定義了Mybatis直接使用的日志方法,而Log介面具體由誰來實作呢?Mybatis提供了多種日志框架的實作,這些實作都匹配這個Log介面所定義的介面方法,最終實作了所有外部日志框架到Mybatis日志包的適配:

比如對于Log4jImpl的實作來說,該實作持有了org.apache.log4j.Logger的實體,然后所有的日志方法,均委托該實體來實作,

public class Log4jImpl implements Log {

 private static final String FQCN = Log4jImpl.class.getName();

 private Logger log;

 public Log4jImpl(String clazz) {
  log = Logger.getLogger(clazz);
 }

 @Override
 public boolean isDebugEnabled() {
  return log.isDebugEnabled();
 }

 @Override
 public boolean isTraceEnabled() {
  return log.isTraceEnabled();
 }

 @Override
 public void error(String s, Throwable e) {
  log.log(FQCN, Level.ERROR, s, e);
 }

 @Override
 public void error(String s) {
  log.log(FQCN, Level.ERROR, s, null);
 }

 @Override
 public void debug(String s) {
  log.log(FQCN, Level.DEBUG, s, null);
 }

 @Override
 public void trace(String s) {
  log.log(FQCN, Level.TRACE, s, null);
 }

 @Override
 public void warn(String s) {
  log.log(FQCN, Level.WARN, s, null);
 }

}

8、裝飾者模式

裝飾模式(Decorator Pattern) :動態地給一個物件增加一些額外的職責(Responsibility),就增加物件功能來說,裝飾模式比生成子類實作更為靈活,其別名也可以稱為包裝器(Wrapper),與配接器模式的別名相同,但它們適用于不同的場合,根據翻譯的不同,裝飾模式也有人稱之為“油漆工模式”,它是一種物件結構型模式,

 

在mybatis中,快取的功能由根介面Cache(org.apache.ibatis.cache.Cache)定義,整個體系采用裝飾器設計模式,資料存盤和快取的基本功能由PerpetualCache(org.apache.ibatis.cache.impl.PerpetualCache)永久快取實作,然后通過一系列的裝飾器來對PerpetualCache永久快取進行快取策略等方便的控制,如下圖:

用于裝飾PerpetualCache的標準裝飾器共有8個(全部在org.apache.ibatis.cache.decorators包中):

  1. FifoCache:先進先出演算法,快取回收策略

  2. LoggingCache:輸出快取命中的日志資訊

  3. LruCache:最近最少使用演算法,快取回收策略

  4. ScheduledCache:調度快取,負責定時清空快取

  5. SerializedCache:快取序列化和反序列化存盤

  6. SoftCache:基于軟參考實作的快取管理策略

  7. SynchronizedCache:同步的快取裝飾器,用于防止多執行緒并發訪問

  8. WeakCache:基于弱參考實作的快取管理策略

另外,還有一個特殊的裝飾器TransactionalCache:事務性的快取

正如大多數持久層框架一樣,mybatis快取同樣分為一級快取和二級快取

  • 一級快取,又叫本地快取,是PerpetualCache型別的永久快取,保存在執行器中(BaseExecutor),而執行器又在SqlSession(DefaultSqlSession)中,所以一級快取的生命周期與SqlSession是相同的,

  • 二級快取,又叫自定義快取,實作了Cache介面的類都可以作為二級快取,所以可配置如encache等的第三方快取,二級快取以namespace名稱空間為其唯一標識,被保存在Configuration核心配置物件中,

二級快取物件的默認型別為PerpetualCache,如果配置的快取是默認型別,則mybatis會根據配置自動追加一系列裝飾器,

Cache物件之間的參考順序為:

SynchronizedCache–>LoggingCache–>SerializedCache–>ScheduledCache–>LruCache–>PerpetualCache

9、迭代器模式

迭代器(Iterator)模式,又叫做游標(Cursor)模式,GOF給出的定義為:提供一種方法訪問一個容器(container)物件中各個元素,而又不需暴露該物件的內部細節,

Java的Iterator就是迭代器模式的介面,只要實作了該介面,就相當于應用了迭代器模式:

比如Mybatis的PropertyTokenizer是property包中的重量級類,該類會被reflection包中其他的類頻繁的參考到,這個類實作了Iterator介面,在使用時經常被用到的是Iterator介面中的hasNext這個函式,

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
 private String name;
 private String indexedName;
 private String index;
 private String children;

 public PropertyTokenizer(String fullname) {
  int delim = fullname.indexOf('.');
  if (delim > -1) {
   name = fullname.substring(0, delim);
   children = fullname.substring(delim + 1);
  } else {
   name = fullname;
   children = null;
  }
  indexedName = name;
  delim = name.indexOf('[');
  if (delim > -1) {
   index = name.substring(delim + 1, name.length() - 1);
   name = name.substring(0, delim);
  }
 }

 public String getName() {
  return name;
 }

 public String getIndex() {
  return index;
 }

 public String getIndexedName() {
  return indexedName;
 }

 public String getChildren() {
  return children;
 }

 @Override
 public boolean hasNext() {
  return children != null;
 }

 @Override
 public PropertyTokenizer next() {
  return new PropertyTokenizer(children);
 }

 @Override
 public void remove() {
  throw new UnsupportedOperationException(
    "Remove is not supported, as it has no meaning in the context of properties.");
 }
}

可以看到,這個類傳入一個字串到建構式,然后提供了iterator方法對決議后的子串進行遍歷,是一個很常用的方法類,

 

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

標籤:其他

上一篇:ConcurrentHashMap 并發之美

下一篇:使用Modbus4J進行RTU模式串口通信

標籤雲
其他(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